Skip to content

Commit 7b1b877

Browse files
authored
Compute v2: Flavor Access Add (#687)
* Compute v2: Flavor Access Add * updating acceptance test to use a real project
1 parent cf81d92 commit 7b1b877

6 files changed

Lines changed: 155 additions & 0 deletions

File tree

acceptance/openstack/compute/v2/flavors_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"github.com/gophercloud/gophercloud/acceptance/clients"
99
"github.com/gophercloud/gophercloud/acceptance/tools"
1010
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
11+
12+
identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3"
1113
)
1214

1315
func TestFlavorsList(t *testing.T) {
@@ -114,3 +116,40 @@ func TestFlavorAccessesList(t *testing.T) {
114116
tools.PrintResource(t, access)
115117
}
116118
}
119+
120+
func TestFlavorAccessCRUD(t *testing.T) {
121+
client, err := clients.NewComputeV2Client()
122+
if err != nil {
123+
t.Fatalf("Unable to create a compute client: %v", err)
124+
}
125+
126+
identityClient, err := clients.NewIdentityV3Client()
127+
if err != nil {
128+
t.Fatal("Unable to create identity client: %v", err)
129+
}
130+
131+
project, err := identity.CreateProject(t, identityClient, nil)
132+
if err != nil {
133+
t.Fatal("Unable to create project: %v", err)
134+
}
135+
defer identity.DeleteProject(t, identityClient, project.ID)
136+
137+
flavor, err := CreatePrivateFlavor(t, client)
138+
if err != nil {
139+
t.Fatalf("Unable to create flavor: %v", err)
140+
}
141+
defer DeleteFlavor(t, client, flavor)
142+
143+
addAccessOpts := flavors.AddAccessOpts{
144+
Tenant: project.ID,
145+
}
146+
147+
accessList, err := flavors.AddAccess(client, flavor.ID, addAccessOpts).Extract()
148+
if err != nil {
149+
t.Fatalf("Unable to add access to flavor: %v", err)
150+
}
151+
152+
for _, access := range accessList {
153+
tools.PrintResource(t, access)
154+
}
155+
}

openstack/compute/v2/flavors/doc.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,18 @@ Example to List Flavor Access
5959
for _, access := range allAccesses {
6060
fmt.Printf("%+v", access)
6161
}
62+
63+
Example to Grant Access to a Flavor
64+
65+
flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b"
66+
67+
accessOpts := flavors.AddAccessOpts{
68+
Tenant: "15153a0979884b59b0592248ef947921",
69+
}
70+
71+
accessList, err := flavors.AddAccess(computeClient, flavor.ID, accessOpts).Extract()
72+
if err != nil {
73+
panic(err)
74+
}
6275
*/
6376
package flavors

openstack/compute/v2/flavors/requests.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,36 @@ func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager
162162
})
163163
}
164164

165+
// AddAccessOptsBuilder allows extensions to add additional parameters to the
166+
// AddAccess requests.
167+
type AddAccessOptsBuilder interface {
168+
ToAddAccessMap() (map[string]interface{}, error)
169+
}
170+
171+
// AddAccessOpts represents options for adding access to a flavor.
172+
type AddAccessOpts struct {
173+
// Tenant is the project/tenant ID to grant access.
174+
Tenant string `json:"tenant"`
175+
}
176+
177+
// ToAddAccessMap constructs a request body from AddAccessOpts.
178+
func (opts AddAccessOpts) ToAddAccessMap() (map[string]interface{}, error) {
179+
return gophercloud.BuildRequestBody(opts, "addTenantAccess")
180+
}
181+
182+
// AddAccess grants a tenant/project access to a flavor.
183+
func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) {
184+
b, err := opts.ToAddAccessMap()
185+
if err != nil {
186+
r.Err = err
187+
return
188+
}
189+
_, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
190+
OkCodes: []int{200},
191+
})
192+
return
193+
}
194+
165195
// IDFromName is a convienience function that returns a flavor's ID given its
166196
// name.
167197
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {

openstack/compute/v2/flavors/results.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,26 @@ func ExtractAccesses(r pagination.Page) ([]FlavorAccess, error) {
154154
return s.FlavorAccesses, err
155155
}
156156

157+
type accessResult struct {
158+
gophercloud.Result
159+
}
160+
161+
// AddAccessResult is the response of an AddAccess operations. Call its
162+
// Extract method to interpret it as a slice of FlavorAccess.
163+
type AddAccessResult struct {
164+
accessResult
165+
}
166+
167+
// Extract provides access to the result of an access create or delete.
168+
// The result will be all accesses that the flavor has.
169+
func (r accessResult) Extract() ([]FlavorAccess, error) {
170+
var s struct {
171+
FlavorAccesses []FlavorAccess `json:"flavor_access"`
172+
}
173+
err := r.ExtractInto(&s)
174+
return s.FlavorAccesses, err
175+
}
176+
157177
// FlavorAccess represents an ACL of tenant access to a specific Flavor.
158178
type FlavorAccess struct {
159179
// FlavorID is the unique ID of the flavor.

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,52 @@ func TestFlavorAccessesList(t *testing.T) {
250250
t.Errorf("Expected %#v, but was %#v", expected, actual)
251251
}
252252
}
253+
254+
func TestFlavorAccessAdd(t *testing.T) {
255+
th.SetupHTTP()
256+
defer th.TeardownHTTP()
257+
258+
th.Mux.HandleFunc("/flavors/12345678/action", func(w http.ResponseWriter, r *http.Request) {
259+
th.TestMethod(t, r, "POST")
260+
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
261+
th.TestHeader(t, r, "accept", "application/json")
262+
th.TestJSONRequest(t, r, `
263+
{
264+
"addTenantAccess": {
265+
"tenant": "2f954bcf047c4ee9b09a37d49ae6db54"
266+
}
267+
}
268+
`)
269+
270+
w.Header().Add("Content-Type", "application/json")
271+
w.WriteHeader(http.StatusOK)
272+
fmt.Fprintf(w, `
273+
{
274+
"flavor_access": [
275+
{
276+
"flavor_id": "12345678",
277+
"tenant_id": "2f954bcf047c4ee9b09a37d49ae6db54"
278+
}
279+
]
280+
}
281+
`)
282+
})
283+
284+
expected := []flavors.FlavorAccess{
285+
flavors.FlavorAccess{
286+
FlavorID: "12345678",
287+
TenantID: "2f954bcf047c4ee9b09a37d49ae6db54",
288+
},
289+
}
290+
291+
addAccessOpts := flavors.AddAccessOpts{
292+
Tenant: "2f954bcf047c4ee9b09a37d49ae6db54",
293+
}
294+
295+
actual, err := flavors.AddAccess(fake.ServiceClient(), "12345678", addAccessOpts).Extract()
296+
th.AssertNoErr(t, err)
297+
298+
if !reflect.DeepEqual(expected, actual) {
299+
t.Errorf("Expected %#v, but was %#v", expected, actual)
300+
}
301+
}

openstack/compute/v2/flavors/urls.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,7 @@ func deleteURL(client *gophercloud.ServiceClient, id string) string {
2323
func accessURL(client *gophercloud.ServiceClient, id string) string {
2424
return client.ServiceURL("flavors", id, "os-flavor-access")
2525
}
26+
27+
func accessActionURL(client *gophercloud.ServiceClient, id string) string {
28+
return client.ServiceURL("flavors", id, "action")
29+
}

0 commit comments

Comments
 (0)