Skip to content

Commit 6469a30

Browse files
Koodtmandre
authored andcommitted
Add new fields for ListOpts volumetypes
Added fields: name, description extra_specs was left out as it would cause an API change.
1 parent f5c55da commit 6469a30

5 files changed

Lines changed: 233 additions & 16 deletions

File tree

internal/acceptance/openstack/blockstorage/v3/blockstorage.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumet
158158

159159
createOpts := volumetypes.CreateOpts{
160160
Name: name,
161-
ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"},
161+
ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name", "RESKEY:availability_zones": "zone", "multiattach": "<is> True"},
162162
Description: description,
163163
}
164164

internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package v3
44

55
import (
66
"context"
7+
"fmt"
78
"testing"
89

910
"github.com/gophercloud/gophercloud/v2/internal/acceptance/clients"
@@ -13,7 +14,7 @@ import (
1314
th "github.com/gophercloud/gophercloud/v2/testhelper"
1415
)
1516

16-
func TestVolumeTypes(t *testing.T) {
17+
func TestVolumeTypesCRUD(t *testing.T) {
1718
clients.RequireAdmin(t)
1819

1920
client, err := clients.NewBlockStorageV3Client()
@@ -39,9 +40,9 @@ func TestVolumeTypes(t *testing.T) {
3940

4041
th.AssertEquals(t, found, true)
4142

43+
name := vt.Name + "-updated"
44+
description := vt.Description + "-updated"
4245
isPublic := false
43-
name := vt.Name + "-UPDATED"
44-
description := ""
4546
updateOpts := volumetypes.UpdateOpts{
4647
Name: &name,
4748
Description: &description,
@@ -55,6 +56,69 @@ func TestVolumeTypes(t *testing.T) {
5556
th.AssertEquals(t, name, newVT.Name)
5657
th.AssertEquals(t, description, newVT.Description)
5758
th.AssertEquals(t, isPublic, newVT.IsPublic)
59+
newVisibility := volumetypes.VisibilityPrivate
60+
61+
for _, tt := range []struct {
62+
name string
63+
opts volumetypes.ListOpts
64+
expectedVolumeTypes int
65+
}{
66+
{
67+
name: "Volume Type Name",
68+
opts: volumetypes.ListOpts{
69+
Name: newVT.Name,
70+
},
71+
expectedVolumeTypes: 1,
72+
},
73+
{
74+
name: "Description",
75+
opts: volumetypes.ListOpts{
76+
Description: "create_from_gophercloud-updated",
77+
},
78+
expectedVolumeTypes: 1,
79+
},
80+
{
81+
name: "Is Public",
82+
opts: volumetypes.ListOpts{
83+
IsPublic: newVisibility,
84+
},
85+
expectedVolumeTypes: 1,
86+
},
87+
} {
88+
t.Run(fmt.Sprintf("List volumetypes by %s", tt.name), func(t *testing.T) {
89+
allPages, err := volumetypes.List(client, tt.opts).AllPages(context.TODO())
90+
th.AssertNoErr(t, err)
91+
92+
allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages)
93+
th.AssertNoErr(t, err)
94+
95+
logVolumeTypes := func() {
96+
for _, volumetype := range allVolumeTypes {
97+
tools.PrintResource(t, volumetype)
98+
}
99+
}
100+
101+
if len(allVolumeTypes) != tt.expectedVolumeTypes {
102+
if len(allVolumeTypes) == 0 {
103+
t.Fatalf("Volume type not found")
104+
}
105+
} else {
106+
if len(allVolumeTypes) > 1 {
107+
logVolumeTypes()
108+
t.Fatalf("Expected %d volume type but got %d", tt.expectedVolumeTypes, len(allVolumeTypes))
109+
}
110+
}
111+
func() {
112+
for _, volumetype := range allVolumeTypes {
113+
if volumetype.ID == newVT.ID {
114+
return
115+
}
116+
}
117+
logVolumeTypes()
118+
t.Fatalf("Returned volume types did not contain expected volume type")
119+
}()
120+
})
121+
}
58122
}
59123

60124
func TestVolumeTypesExtraSpecs(t *testing.T) {

openstack/blockstorage/v3/volumetypes/requests.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ type ListOptsBuilder interface {
7373
// ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List
7474
// function.
7575
type ListOpts struct {
76+
// Name will filter by the specified volume type name.
77+
Name string `q:"name"`
78+
// Description will filter by the specified volume type description.
79+
Description string `q:"description"`
7680
// Specifies whether the query should include public or private Volume Types.
7781
// By default, it queries both types.
7882
IsPublic visibility `q:"is_public"`

openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go

Lines changed: 115 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,121 @@ var UpdatedExtraSpec = map[string]string{
195195
"capabilities": "gpu-2",
196196
}
197197

198+
func HandleListIsPublicParam(t *testing.T, fakeServer th.FakeServer, values map[string]string) {
199+
fakeServer.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
200+
th.TestMethod(t, r, "GET")
201+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
202+
th.TestFormValues(t, r, values)
203+
204+
w.Header().Add("Content-Type", "application/json")
205+
w.WriteHeader(http.StatusOK)
206+
fmt.Fprint(w, `{"volume_types": []}`)
207+
})
208+
}
209+
210+
func HandleListWithNameFilter(t *testing.T, fakeServer th.FakeServer, values map[string]string) {
211+
fakeServer.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
212+
th.TestMethod(t, r, "GET")
213+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
214+
th.TestFormValues(t, r, values)
215+
216+
w.Header().Add("Content-Type", "application/json")
217+
w.WriteHeader(http.StatusOK)
218+
fmt.Fprint(w, `
219+
{
220+
"volume_types": [
221+
{
222+
"name": "test-type",
223+
"qos_specs_id": null,
224+
"os-volume-type-access:is_public": true,
225+
"extra_specs": {
226+
"storage_protocol": "nfs"
227+
},
228+
"is_public": true,
229+
"id": "996af3df-92fd-4814-a0ee-ba5f899aa1ec",
230+
"description": "test"
231+
}
232+
]
233+
}
234+
`)
235+
})
236+
}
237+
238+
func HandleListWithDescriptionFilter(t *testing.T, fakeServer th.FakeServer, values map[string]string) {
239+
fakeServer.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
240+
th.TestMethod(t, r, "GET")
241+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
242+
th.TestFormValues(t, r, values)
243+
244+
w.Header().Add("Content-Type", "application/json")
245+
w.WriteHeader(http.StatusOK)
246+
fmt.Fprint(w, `
247+
{
248+
"volume_types": [
249+
{
250+
"name": "test-type",
251+
"qos_specs_id": null,
252+
"os-volume-type-access:is_public": true,
253+
"extra_specs": {
254+
"multiattach": "<is> True"
255+
},
256+
"is_public": true,
257+
"id": "ab948f0a-13ed-47c8-b9be-cade0beb0706",
258+
"description": "test"
259+
}
260+
]
261+
}
262+
`)
263+
})
264+
}
265+
266+
func HandleListWithExtraSpecsFilter(t *testing.T, fakeServer th.FakeServer, values map[string]string) {
267+
fakeServer.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
268+
th.TestMethod(t, r, "GET")
269+
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
270+
th.TestFormValues(t, r, values)
271+
272+
if err := r.ParseForm(); err != nil {
273+
t.Errorf("Failed to parse request form %v", err)
274+
}
275+
276+
w.Header().Add("Content-Type", "application/json")
277+
w.WriteHeader(http.StatusOK)
278+
279+
extraSpecsFilter := r.Form.Get("extra_specs")
280+
281+
// Determine which volume type to return based on extra_specs filter.
282+
// Note: We only test with a single extra_spec value because testing multiple
283+
// values is not feasible due to the non-deterministic order of map keys in Go.
284+
// The order of keys in the serialized string can vary between runs, making
285+
// it impossible to reliably match the expected string without adding complex
286+
// normalization code that would clutter the test.
287+
if extraSpecsFilter == "{'storage_protocol':'nfs'}" {
288+
// Return only nfs-type
289+
fmt.Fprint(w, `
290+
{
291+
"volume_types": [
292+
{
293+
"name": "nfs-type",
294+
"qos_specs_id": null,
295+
"os-volume-type-access:is_public": true,
296+
"extra_specs": {
297+
"storage_protocol": "nfs"
298+
},
299+
"is_public": true,
300+
"id": "6b0cfee7-48b6-41b7-9d68-0d74cbdc08de",
301+
"description": "NFS storage type"
302+
}
303+
]
304+
}
305+
`)
306+
} else {
307+
// Default: return empty list
308+
fmt.Fprint(w, `{"volume_types": []}`)
309+
}
310+
})
311+
}
312+
198313
func HandleExtraSpecsListSuccessfully(t *testing.T, fakeServer th.FakeServer) {
199314
fakeServer.Mux.HandleFunc("/types/1/extra_specs", func(w http.ResponseWriter, r *http.Request) {
200315
th.TestMethod(t, r, "GET")
@@ -377,15 +492,3 @@ func MockEncryptionGetSpecResponse(t *testing.T, fakeServer th.FakeServer) {
377492
`)
378493
})
379494
}
380-
381-
func HandleListIsPublicParam(t *testing.T, fakeServer th.FakeServer, values map[string]string) {
382-
fakeServer.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) {
383-
th.TestMethod(t, r, "GET")
384-
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
385-
th.TestFormValues(t, r, values)
386-
387-
w.Header().Add("Content-Type", "application/json")
388-
w.WriteHeader(http.StatusOK)
389-
fmt.Fprint(w, `{"volume_types": []}`)
390-
})
391-
}

openstack/blockstorage/v3/volumetypes/testing/requests_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,52 @@ func TestListIsPublicParam(t *testing.T) {
162162
th.AssertNoErr(t, err)
163163
}
164164

165+
func TestListNameParam(t *testing.T) {
166+
fakeServer := th.SetupHTTP()
167+
defer fakeServer.Teardown()
168+
result := make(map[string]string)
169+
HandleListWithNameFilter(t, fakeServer, result)
170+
171+
result["is_public"] = "None"
172+
result["name"] = "test-type"
173+
allPages, err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{
174+
Name: "test-type",
175+
}).AllPages(context.TODO())
176+
th.AssertNoErr(t, err)
177+
actual, err := volumetypes.ExtractVolumeTypes(allPages)
178+
th.AssertNoErr(t, err)
179+
th.AssertEquals(t, 1, len(actual))
180+
th.AssertEquals(t, "test-type", actual[0].Name)
181+
th.AssertEquals(t, "996af3df-92fd-4814-a0ee-ba5f899aa1ec", actual[0].ID)
182+
th.AssertEquals(t, "test", actual[0].Description)
183+
th.AssertEquals(t, true, actual[0].IsPublic)
184+
th.AssertEquals(t, true, actual[0].PublicAccess)
185+
th.AssertEquals(t, "nfs", actual[0].ExtraSpecs["storage_protocol"])
186+
}
187+
188+
func TestListDescriptionParam(t *testing.T) {
189+
fakeServer := th.SetupHTTP()
190+
defer fakeServer.Teardown()
191+
result := make(map[string]string)
192+
HandleListWithDescriptionFilter(t, fakeServer, result)
193+
194+
result["is_public"] = "None"
195+
result["description"] = "test"
196+
allPages, err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{
197+
Description: "test",
198+
}).AllPages(context.TODO())
199+
th.AssertNoErr(t, err)
200+
actual, err := volumetypes.ExtractVolumeTypes(allPages)
201+
th.AssertNoErr(t, err)
202+
th.AssertEquals(t, 1, len(actual))
203+
th.AssertEquals(t, "test", actual[0].Description)
204+
th.AssertEquals(t, "test-type", actual[0].Name)
205+
th.AssertEquals(t, "ab948f0a-13ed-47c8-b9be-cade0beb0706", actual[0].ID)
206+
th.AssertEquals(t, true, actual[0].IsPublic)
207+
th.AssertEquals(t, true, actual[0].PublicAccess)
208+
th.AssertEquals(t, "<is> True", actual[0].ExtraSpecs["multiattach"])
209+
}
210+
165211
func TestVolumeTypeExtraSpecsList(t *testing.T) {
166212
fakeServer := th.SetupHTTP()
167213
defer fakeServer.Teardown()

0 commit comments

Comments
 (0)