Skip to content

Commit 1f574bb

Browse files
authored
Merge pull request gophercloud#2648 from kayrus/share-transfer
[manila]: implement share transfer API
2 parents 44d55f0 + c779dc2 commit 1f574bb

File tree

10 files changed

+811
-11
lines changed

10 files changed

+811
-11
lines changed

acceptance/clients/conditions.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func IsReleasesAbove(t *testing.T, release string) bool {
147147
func IsReleasesBelow(t *testing.T, release string) bool {
148148
current_branch := getReleaseFromEnv(t)
149149

150-
if current_branch != "master" && current_branch < release {
150+
if current_branch != "master" || current_branch < release {
151151
return true
152152
}
153153
t.Logf("Target release %s is above the current branch %s", release, current_branch)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package v2
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/gophercloud/gophercloud"
8+
"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares"
9+
"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers"
10+
)
11+
12+
func CreateTransferRequest(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, name string) (*sharetransfers.Transfer, error) {
13+
opts := sharetransfers.CreateOpts{
14+
ShareID: share.ID,
15+
Name: name,
16+
}
17+
transfer, err := sharetransfers.Create(client, opts).Extract()
18+
if err != nil {
19+
return nil, fmt.Errorf("failed to create a share transfer request: %s", err)
20+
}
21+
22+
return transfer, nil
23+
}
24+
25+
func AcceptTransfer(t *testing.T, client *gophercloud.ServiceClient, transferRequest *sharetransfers.Transfer) error {
26+
opts := sharetransfers.AcceptOpts{
27+
AuthKey: transferRequest.AuthKey,
28+
ClearAccessRules: true,
29+
}
30+
err := sharetransfers.Accept(client, transferRequest.ID, opts).ExtractErr()
31+
if err != nil {
32+
return fmt.Errorf("failed to accept a share transfer request: %s", err)
33+
}
34+
35+
return nil
36+
}
37+
38+
func DeleteTransferRequest(t *testing.T, client *gophercloud.ServiceClient, transfer *sharetransfers.Transfer) {
39+
err := sharetransfers.Delete(client, transfer.ID).ExtractErr()
40+
if err != nil {
41+
if _, ok := err.(gophercloud.ErrDefault404); ok {
42+
return
43+
}
44+
t.Errorf("Unable to delete share transfer %s: %v", transfer.ID, err)
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//go:build acceptance || share || transfers
2+
// +build acceptance share transfers
3+
4+
package v2
5+
6+
import (
7+
"testing"
8+
9+
"github.com/gophercloud/gophercloud/acceptance/clients"
10+
"github.com/gophercloud/gophercloud/acceptance/tools"
11+
th "github.com/gophercloud/gophercloud/testhelper"
12+
13+
"github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers"
14+
)
15+
16+
// minimal microversion for the share transfers
17+
const shareTransfersMicroversion = "2.77"
18+
19+
func TestTransferRequestCRUD(t *testing.T) {
20+
clients.SkipReleasesBelow(t, "master")
21+
22+
client, err := clients.NewSharedFileSystemV2Client()
23+
if err != nil {
24+
t.Fatalf("Unable to create a shared file system client: %v", err)
25+
}
26+
client.Microversion = shareTransfersMicroversion
27+
28+
share, err := CreateShare(t, client)
29+
if err != nil {
30+
t.Fatalf("Unable to create a share: %v", err)
31+
}
32+
33+
defer DeleteShare(t, client, share)
34+
35+
// Create transfers request to a new tenant
36+
trName := "123"
37+
transferRequest, err := CreateTransferRequest(t, client, share, trName)
38+
th.AssertNoErr(t, err)
39+
defer DeleteTransferRequest(t, client, transferRequest)
40+
41+
// list transfer requests
42+
allTransferRequestsPages, err := sharetransfers.ListDetail(client, nil).AllPages()
43+
th.AssertNoErr(t, err)
44+
45+
allTransferRequests, err := sharetransfers.ExtractTransfers(allTransferRequestsPages)
46+
th.AssertNoErr(t, err)
47+
48+
// finding the transfer request
49+
var foundRequest bool
50+
for _, tr := range allTransferRequests {
51+
tools.PrintResource(t, &tr)
52+
if tr.ResourceID == share.ID && tr.Name == trName && !tr.Accepted {
53+
foundRequest = true
54+
}
55+
}
56+
th.AssertEquals(t, foundRequest, true)
57+
58+
// checking get
59+
tr, err := sharetransfers.Get(client, transferRequest.ID).Extract()
60+
th.AssertNoErr(t, err)
61+
62+
th.AssertEquals(t, transferRequest.ID == tr.ID, true)
63+
64+
// Accept Share Transfer Request
65+
err = AcceptTransfer(t, client, transferRequest)
66+
th.AssertNoErr(t, err)
67+
}

openstack/sharedfilesystems/v2/replicas/requests.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create
5151
}
5252

5353
// ListOpts holds options for listing Share Replicas. This object is passed to the
54-
// replicas.List function.
54+
// replicas.List or replicas.ListDetail functions.
5555
type ListOpts struct {
5656
// The UUID of the share.
5757
ShareID string `q:"share_id"`

openstack/sharedfilesystems/v2/replicas/results.go

+11-9
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ type commonResult struct {
6363
gophercloud.Result
6464
}
6565

66-
// Extract will get the Replica object from the commonResul.t
66+
// Extract will get the Replica object from the commonResult.
6767
func (r commonResult) Extract() (*Replica, error) {
6868
var s struct {
6969
Replica *Replica `json:"share_replica"`
@@ -149,16 +149,18 @@ func (r ReplicaPage) IsEmpty() (bool, error) {
149149
return len(replicas) == 0, err
150150
}
151151

152-
// ExtractReplicas extracts and returns a Replica slice. It is used while
153-
// iterating over a replicas.List call.
152+
// ExtractReplicas extracts and returns Replicas. It is used while iterating
153+
// over a replicas.List or replicas.ListDetail calls.
154154
func ExtractReplicas(r pagination.Page) ([]Replica, error) {
155-
var s struct {
156-
Replicas []Replica `json:"share_replicas"`
157-
}
158-
159-
err := (r.(ReplicaPage)).ExtractInto(&s)
155+
var s []Replica
156+
err := ExtractReplicasInto(r, &s)
157+
return s, err
158+
}
160159

161-
return s.Replicas, err
160+
// ExtractReplicasInto similar to ExtractReplicas but operates on a `list` of
161+
// replicas.
162+
func ExtractReplicasInto(r pagination.Page, v interface{}) error {
163+
return r.(ReplicaPage).Result.ExtractIntoSlicePtr(v, "share_replicas")
162164
}
163165

164166
// DeleteResult contains the response body and error from a Delete request.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package sharetransfers
2+
3+
import (
4+
"github.com/gophercloud/gophercloud"
5+
"github.com/gophercloud/gophercloud/pagination"
6+
)
7+
8+
// CreateOptsBuilder allows extensions to add additional parameters to the
9+
// Create request.
10+
type CreateOptsBuilder interface {
11+
ToTransferCreateMap() (map[string]interface{}, error)
12+
}
13+
14+
// CreateOpts contains options for a Share transfer.
15+
type CreateOpts struct {
16+
// The ID of the share to transfer.
17+
ShareID string `json:"share_id" required:"true"`
18+
19+
// The name of the share transfer.
20+
Name string `json:"name,omitempty"`
21+
}
22+
23+
// ToCreateMap assembles a request body based on the contents of a
24+
// TransferOpts.
25+
func (opts CreateOpts) ToTransferCreateMap() (map[string]interface{}, error) {
26+
return gophercloud.BuildRequestBody(opts, "transfer")
27+
}
28+
29+
// Create will create a share tranfer request based on the values in CreateOpts.
30+
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
31+
b, err := opts.ToTransferCreateMap()
32+
if err != nil {
33+
r.Err = err
34+
return
35+
}
36+
resp, err := client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{
37+
OkCodes: []int{202},
38+
})
39+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
40+
return
41+
}
42+
43+
// AcceptOpts contains options for a Share transfer accept reqeust.
44+
type AcceptOpts struct {
45+
// The auth key of the share transfer to accept.
46+
AuthKey string `json:"auth_key" required:"true"`
47+
48+
// Whether to clear access rules when accept the share.
49+
ClearAccessRules bool `json:"clear_access_rules,omitempty"`
50+
}
51+
52+
// ToAcceptMap assembles a request body based on the contents of a
53+
// AcceptOpts.
54+
func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) {
55+
return gophercloud.BuildRequestBody(opts, "accept")
56+
}
57+
58+
// Accept will accept a share tranfer request based on the values in AcceptOpts.
59+
func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r AcceptResult) {
60+
b, err := opts.ToAcceptMap()
61+
if err != nil {
62+
r.Err = err
63+
return
64+
}
65+
resp, err := client.Post(acceptURL(client, id), b, nil, &gophercloud.RequestOpts{
66+
OkCodes: []int{202},
67+
})
68+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
69+
return
70+
}
71+
72+
// Delete deletes a share transfer.
73+
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
74+
resp, err := client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{
75+
// DELETE requests response with a 200 code, adding it here
76+
OkCodes: []int{200, 202, 204},
77+
})
78+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
79+
return
80+
}
81+
82+
// ListOptsBuilder allows extensions to add additional parameters to the List
83+
// request.
84+
type ListOptsBuilder interface {
85+
ToTransferListQuery() (string, error)
86+
}
87+
88+
// ListOpts holds options for listing Transfers. It is passed to the sharetransfers.List
89+
// or sharetransfers.ListDetail functions.
90+
type ListOpts struct {
91+
// AllTenants will retrieve transfers of all tenants/projects. Admin
92+
// only.
93+
AllTenants bool `q:"all_tenants"`
94+
95+
// The user defined name of the share transfer to filter resources by.
96+
Name string `q:"name"`
97+
98+
// The name pattern that can be used to filter share transfers.
99+
NamePattern string `q:"name~"`
100+
101+
// The key to sort a list of transfers. A valid value is id, name,
102+
// resource_type, resource_id, source_project_id, destination_project_id,
103+
// created_at, expires_at.
104+
SortKey string `q:"sort_key"`
105+
106+
// The direction to sort a list of resources. A valid value is asc, or
107+
// desc.
108+
SortDir string `q:"sort_dir"`
109+
110+
// Requests a page size of items.
111+
Limit int `q:"limit"`
112+
113+
// Used in conjunction with limit to return a slice of items.
114+
Offset int `q:"offset"`
115+
116+
// The ID of the last-seen item.
117+
Marker string `q:"marker"`
118+
}
119+
120+
// ToTransferListQuery formats a ListOpts into a query string.
121+
func (opts ListOpts) ToTransferListQuery() (string, error) {
122+
q, err := gophercloud.BuildQueryString(opts)
123+
return q.String(), err
124+
}
125+
126+
// List returns Transfers optionally limited by the conditions provided in ListOpts.
127+
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
128+
url := listURL(client)
129+
if opts != nil {
130+
query, err := opts.ToTransferListQuery()
131+
if err != nil {
132+
return pagination.Pager{Err: err}
133+
}
134+
url += query
135+
}
136+
137+
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
138+
p := TransferPage{pagination.MarkerPageBase{PageResult: r}}
139+
p.MarkerPageBase.Owner = p
140+
return p
141+
})
142+
}
143+
144+
// List returns Transfers with details optionally limited by the conditions
145+
// provided in ListOpts.
146+
func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
147+
url := listDetailURL(client)
148+
if opts != nil {
149+
query, err := opts.ToTransferListQuery()
150+
if err != nil {
151+
return pagination.Pager{Err: err}
152+
}
153+
url += query
154+
}
155+
156+
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
157+
p := TransferPage{pagination.MarkerPageBase{PageResult: r}}
158+
p.MarkerPageBase.Owner = p
159+
return p
160+
})
161+
}
162+
163+
// Get retrieves the Transfer with the provided ID. To extract the Transfer object
164+
// from the response, call the Extract method on the GetResult.
165+
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
166+
resp, err := client.Get(getURL(client, id), &r.Body, nil)
167+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
168+
return
169+
}

0 commit comments

Comments
 (0)