Skip to content

Commit 9c81722

Browse files
jtopjianjrperritt
authored andcommitted
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
1 parent 57a8169 commit 9c81722

File tree

7 files changed

+152
-0
lines changed

7 files changed

+152
-0
lines changed

acceptance/openstack/compute/v2/compute.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,31 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient,
268268
return newServer, nil
269269
}
270270

271+
// CreatePrivateFlavor will create a private flavor with a random name.
272+
// An error will be returned if the flavor could not be created.
273+
func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) {
274+
flavorName := tools.RandomString("flavor_", 5)
275+
t.Logf("Attempting to create flavor %s", flavorName)
276+
277+
isPublic := false
278+
createOpts := flavors.CreateOpts{
279+
Name: flavorName,
280+
RAM: 1,
281+
VCPUs: 1,
282+
Disk: gophercloud.IntToPointer(1),
283+
IsPublic: &isPublic,
284+
}
285+
286+
flavor, err := flavors.Create(client, createOpts).Extract()
287+
if err != nil {
288+
return nil, err
289+
}
290+
291+
t.Logf("Successfully created flavor %s", flavor.ID)
292+
293+
return flavor, nil
294+
}
295+
271296
// CreateSecurityGroup will create a security group with a random name.
272297
// An error will be returned if one was failed to be created.
273298
func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (secgroups.SecurityGroup, error) {

acceptance/openstack/compute/v2/flavors_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,30 @@ func TestFlavorCreateDelete(t *testing.T) {
8787

8888
tools.PrintResource(t, flavor)
8989
}
90+
91+
func TestFlavorAccessesList(t *testing.T) {
92+
client, err := clients.NewComputeV2Client()
93+
if err != nil {
94+
t.Fatalf("Unable to create a compute client: %v", err)
95+
}
96+
97+
flavor, err := CreatePrivateFlavor(t, client)
98+
if err != nil {
99+
t.Fatalf("Unable to create flavor: %v", err)
100+
}
101+
defer DeleteFlavor(t, client, flavor)
102+
103+
allPages, err := flavors.ListAccesses(client, flavor.ID).AllPages()
104+
if err != nil {
105+
t.Fatalf("Unable to list flavor accesses: %v", err)
106+
}
107+
108+
allAccesses, err := flavors.ExtractAccesses(allPages)
109+
if err != nil {
110+
t.Fatalf("Unable to extract accesses: %v", err)
111+
}
112+
113+
for _, access := range allAccesses {
114+
tools.PrintResource(t, access)
115+
}
116+
}

openstack/compute/v2/flavors/doc.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,23 @@ Example to Create a Flavor
4141
if err != nil {
4242
panic(err)
4343
}
44+
45+
Example to List Flavor Access
46+
47+
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
48+
49+
allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages()
50+
if err != nil {
51+
panic(err)
52+
}
53+
54+
allAccesses, err := flavors.ExtractAccesses(allPages)
55+
if err != nil {
56+
panic(err)
57+
}
58+
59+
for _, access := range allAccesses {
60+
fmt.Printf("%+v", access)
61+
}
4462
*/
4563
package flavors

openstack/compute/v2/flavors/requests.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,15 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
153153
return
154154
}
155155

156+
// ListAccesses retrieves the tenants which have access to a flavor.
157+
func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager {
158+
url := accessURL(client, id)
159+
160+
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
161+
return AccessPage{pagination.SinglePageBase(r)}
162+
})
163+
}
164+
156165
// IDFromName is a convienience function that returns a flavor's ID given its
157166
// name.
158167
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {

openstack/compute/v2/flavors/results.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ type commonResult struct {
1212
gophercloud.Result
1313
}
1414

15+
// CreateResult is the response of a Get operations. Call its Extract method to
16+
// interpret it as a Flavor.
1517
type CreateResult struct {
1618
commonResult
1719
}
@@ -131,3 +133,32 @@ func ExtractFlavors(r pagination.Page) ([]Flavor, error) {
131133
err := (r.(FlavorPage)).ExtractInto(&s)
132134
return s.Flavors, err
133135
}
136+
137+
// AccessPage contains a single page of all FlavorAccess entries for a flavor.
138+
type AccessPage struct {
139+
pagination.SinglePageBase
140+
}
141+
142+
// IsEmpty indicates whether an AccessPage is empty.
143+
func (page AccessPage) IsEmpty() (bool, error) {
144+
v, err := ExtractAccesses(page)
145+
return len(v) == 0, err
146+
}
147+
148+
// ExtractAccesses interprets a page of results as a slice of FlavorAccess.
149+
func ExtractAccesses(r pagination.Page) ([]FlavorAccess, error) {
150+
var s struct {
151+
FlavorAccesses []FlavorAccess `json:"flavor_access"`
152+
}
153+
err := (r.(AccessPage)).ExtractInto(&s)
154+
return s.FlavorAccesses, err
155+
}
156+
157+
// FlavorAccess represents an ACL of tenant access to a specific Flavor.
158+
type FlavorAccess struct {
159+
// FlavorID is the unique ID of the flavor.
160+
FlavorID string `json:"flavor_id"`
161+
162+
// TenantID is the unique ID of the tenant.
163+
TenantID string `json:"tenant_id"`
164+
}

openstack/compute/v2/flavors/testing/requests_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,41 @@ func TestDeleteFlavor(t *testing.T) {
212212
res := flavors.Delete(fake.ServiceClient(), "12345678")
213213
th.AssertNoErr(t, res.Err)
214214
}
215+
216+
func TestFlavorAccessesList(t *testing.T) {
217+
th.SetupHTTP()
218+
defer th.TeardownHTTP()
219+
220+
th.Mux.HandleFunc("/flavors/12345678/os-flavor-access", func(w http.ResponseWriter, r *http.Request) {
221+
th.TestMethod(t, r, "GET")
222+
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
223+
w.Header().Add("Content-Type", "application/json")
224+
fmt.Fprintf(w, `
225+
{
226+
"flavor_access": [
227+
{
228+
"flavor_id": "12345678",
229+
"tenant_id": "2f954bcf047c4ee9b09a37d49ae6db54"
230+
}
231+
]
232+
}
233+
`)
234+
})
235+
236+
expected := []flavors.FlavorAccess{
237+
flavors.FlavorAccess{
238+
FlavorID: "12345678",
239+
TenantID: "2f954bcf047c4ee9b09a37d49ae6db54",
240+
},
241+
}
242+
243+
allPages, err := flavors.ListAccesses(fake.ServiceClient(), "12345678").AllPages()
244+
th.AssertNoErr(t, err)
245+
246+
actual, err := flavors.ExtractAccesses(allPages)
247+
th.AssertNoErr(t, err)
248+
249+
if !reflect.DeepEqual(expected, actual) {
250+
t.Errorf("Expected %#v, but was %#v", expected, actual)
251+
}
252+
}

openstack/compute/v2/flavors/urls.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ func createURL(client *gophercloud.ServiceClient) string {
1919
func deleteURL(client *gophercloud.ServiceClient, id string) string {
2020
return client.ServiceURL("flavors", id)
2121
}
22+
23+
func accessURL(client *gophercloud.ServiceClient, id string) string {
24+
return client.ServiceURL("flavors", id, "os-flavor-access")
25+
}

0 commit comments

Comments
 (0)