Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dbed7c1

Browse files
authored
Merge pull request #3390 from kayrus/list-zone-share
dns: implement shared zones list
2 parents 6f0a5f6 + ef385c0 commit dbed7c1

File tree

7 files changed

+310
-9
lines changed

7 files changed

+310
-9
lines changed

internal/acceptance/openstack/dns/v2/dns.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,41 @@ func DeleteTransferRequest(t *testing.T, client *gophercloud.ServiceClient, tr *
186186
t.Logf("Deleted zone transfer request: %s", tr.ID)
187187
}
188188

189+
// CreateShare will create a zone share. An error will be returned if the
190+
// zone share was unable to be created.
191+
func CreateShare(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zone, targetProjectID string) (*zones.ZoneShare, error) {
192+
t.Logf("Attempting to share zone %s with project %s", zone.ID, targetProjectID)
193+
194+
createOpts := zones.ShareZoneOpts{
195+
TargetProjectID: targetProjectID,
196+
}
197+
198+
share, err := zones.Share(context.TODO(), client, zone.ID, createOpts).Extract()
199+
if err != nil {
200+
return share, err
201+
}
202+
203+
t.Logf("Created share for zone: %s", zone.ID)
204+
205+
th.AssertEquals(t, share.ZoneID, zone.ID)
206+
th.AssertEquals(t, share.TargetProjectID, targetProjectID)
207+
208+
return share, nil
209+
}
210+
211+
// UnshareZone will unshare a zone. An error will be returned if the
212+
// zone unshare was unable to be created.
213+
func UnshareZone(t *testing.T, client *gophercloud.ServiceClient, share *zones.ZoneShare) {
214+
t.Logf("Attempting to unshare zone %s with project %s", share.ZoneID, share.TargetProjectID)
215+
216+
err := zones.Unshare(context.TODO(), client, share.ZoneID, share.ID).ExtractErr()
217+
if err != nil {
218+
t.Fatalf("Unable to unshare zone %s: %v", share.ZoneID, err)
219+
}
220+
221+
t.Logf("Unshared zone: %s", share.ZoneID)
222+
}
223+
189224
// DeleteRecordSet will delete a specified record set. A fatal error will occur if
190225
// the record set failed to be deleted. This works best when used as a deferred
191226
// function.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//go:build acceptance || dns || zone_shares
2+
3+
package v2
4+
5+
import (
6+
"context"
7+
"testing"
8+
9+
"github.com/gophercloud/gophercloud/v2/internal/acceptance/clients"
10+
identity "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/identity/v3"
11+
"github.com/gophercloud/gophercloud/v2/internal/acceptance/tools"
12+
"github.com/gophercloud/gophercloud/v2/openstack/dns/v2/zones"
13+
th "github.com/gophercloud/gophercloud/v2/testhelper"
14+
)
15+
16+
func TestShareCRD(t *testing.T) {
17+
// Create new project
18+
identityClient, err := clients.NewIdentityV3Client()
19+
th.AssertNoErr(t, err)
20+
21+
project, err := identity.CreateProject(t, identityClient, nil)
22+
th.AssertNoErr(t, err)
23+
defer identity.DeleteProject(t, identityClient, project.ID)
24+
25+
// Create new Zone
26+
client, err := clients.NewDNSV2Client()
27+
th.AssertNoErr(t, err)
28+
29+
zone, err := CreateZone(t, client)
30+
th.AssertNoErr(t, err)
31+
defer DeleteZone(t, client, zone)
32+
33+
// Create a zone share to new tenant
34+
share, err := CreateShare(t, client, zone, project.ID)
35+
th.AssertNoErr(t, err)
36+
tools.PrintResource(t, share)
37+
defer UnshareZone(t, client, share)
38+
39+
// Get the share
40+
getShare, err := zones.GetShare(context.TODO(), client, share.ZoneID, share.ID).Extract()
41+
th.AssertNoErr(t, err)
42+
tools.PrintResource(t, getShare)
43+
th.AssertDeepEquals(t, *share, *getShare)
44+
45+
// List shares
46+
allPages, err := zones.ListShares(client, share.ZoneID, nil).AllPages(context.TODO())
47+
th.AssertNoErr(t, err)
48+
49+
allShares, err := zones.ExtractZoneShares(allPages)
50+
th.AssertNoErr(t, err)
51+
tools.PrintResource(t, allShares)
52+
53+
foundShare := -1
54+
for i, s := range allShares {
55+
tools.PrintResource(t, &s)
56+
if share.ID == s.ID {
57+
foundShare = i
58+
break
59+
}
60+
}
61+
if foundShare == -1 {
62+
t.Fatalf("Share %s not found in list", share.ID)
63+
}
64+
65+
th.AssertDeepEquals(t, *share, allShares[foundShare])
66+
}

openstack/dns/v2/zones/requests.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,48 @@ func Delete(ctx context.Context, client *gophercloud.ServiceClient, zoneID strin
179179
return
180180
}
181181

182+
// ListSharesOptsBuilder allows extensions to add additional parameters to the List
183+
// request.
184+
type ListSharesOptsBuilder interface {
185+
ToZoneListSharesHeadersMap() (map[string]string, error)
186+
}
187+
188+
// ListSharesOpts is a structure that holds parameters for listing zone shares.
189+
type ListSharesOpts struct {
190+
AllProjects bool `h:"X-Auth-All-Projects"`
191+
}
192+
193+
// ToZoneListSharesHeadersMap formats a ListSharesOpts into header parameters.
194+
func (opts ListSharesOpts) ToZoneListSharesHeadersMap() (map[string]string, error) {
195+
return gophercloud.BuildHeaders(opts)
196+
}
197+
198+
// ListShares implements a zone list shares request.
199+
func ListShares(client *gophercloud.ServiceClient, zoneID string, opts ListSharesOptsBuilder) pagination.Pager {
200+
var h map[string]string
201+
var err error
202+
203+
if opts != nil {
204+
h, err = opts.ToZoneListSharesHeadersMap()
205+
if err != nil {
206+
return pagination.Pager{Err: err}
207+
}
208+
}
209+
210+
pager := pagination.NewPager(client, sharesBaseURL(client, zoneID), func(r pagination.PageResult) pagination.Page {
211+
return ZoneSharePage{pagination.LinkedPageBase{PageResult: r}}
212+
})
213+
pager.Headers = h
214+
return pager
215+
}
216+
217+
// GetShare returns information about a shared zone, given its ID.
218+
func GetShare(ctx context.Context, client *gophercloud.ServiceClient, zoneID, shareID string) (r ZoneShareResult) {
219+
resp, err := client.Get(ctx, shareURL(client, zoneID, shareID), &r.Body, nil)
220+
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
221+
return
222+
}
223+
182224
// request body for sharing a zone.
183225
type ShareOptsBuilder interface {
184226
ToShareMap() (map[string]interface{}, error)
@@ -198,14 +240,14 @@ func (opts ShareZoneOpts) ToShareMap() (map[string]interface{}, error) {
198240
}
199241

200242
// Share shares a zone with another project.
201-
func Share(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, opts ShareOptsBuilder) (r gophercloud.ErrResult) {
243+
func Share(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, opts ShareOptsBuilder) (r ZoneShareResult) {
202244
body, err := gophercloud.BuildRequestBody(opts, "")
203245
if err != nil {
204246
r.Err = err
205247
return
206248
}
207249

208-
resp, err := client.Post(ctx, zoneShareURL(client, zoneID), body, nil, &gophercloud.RequestOpts{
250+
resp, err := client.Post(ctx, sharesBaseURL(client, zoneID), body, &r.Body, &gophercloud.RequestOpts{
209251
OkCodes: []int{201},
210252
})
211253
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
@@ -214,7 +256,7 @@ func Share(ctx context.Context, client *gophercloud.ServiceClient, zoneID string
214256

215257
// Unshare removes a share for a zone.
216258
func Unshare(ctx context.Context, client *gophercloud.ServiceClient, zoneID, shareID string) (r gophercloud.ErrResult) {
217-
resp, err := client.Delete(ctx, zoneUnshareURL(client, zoneID, shareID), &gophercloud.RequestOpts{
259+
resp, err := client.Delete(ctx, shareURL(client, zoneID, shareID), &gophercloud.RequestOpts{
218260
OkCodes: []int{204},
219261
})
220262
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)

openstack/dns/v2/zones/results.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,81 @@ func (r *Zone) UnmarshalJSON(b []byte) error {
173173

174174
return err
175175
}
176+
177+
// ZoneShare represents a shared zone.
178+
type ZoneShare struct {
179+
// ID uniquely identifies this zone share.
180+
ID string `json:"id"`
181+
182+
// ZoneID is the ID of the zone being shared.
183+
ZoneID string `json:"zone_id"`
184+
185+
// ProjectID is the ID of the project with which the zone is shared.
186+
ProjectID string `json:"project_id"`
187+
188+
// TargetProjectID is the ID of the project with which the zone is shared.
189+
TargetProjectID string `json:"target_project_id"`
190+
191+
// CreatedAt is the date when the zone share was created.
192+
CreatedAt time.Time `json:"-"`
193+
194+
// UpdatedAt is the date when the zone share was last updated.
195+
UpdatedAt time.Time `json:"-"`
196+
}
197+
198+
func (r *ZoneShare) UnmarshalJSON(b []byte) error {
199+
type tmp ZoneShare
200+
var s struct {
201+
tmp
202+
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
203+
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
204+
}
205+
206+
err := json.Unmarshal(b, &s)
207+
if err != nil {
208+
return err
209+
}
210+
*r = ZoneShare(s.tmp)
211+
212+
r.CreatedAt = time.Time(s.CreatedAt)
213+
r.UpdatedAt = time.Time(s.UpdatedAt)
214+
215+
return nil
216+
}
217+
218+
// ZoneShareResult is the result of a GetZoneShare request.
219+
type ZoneShareResult struct {
220+
gophercloud.Result
221+
}
222+
223+
// Extract interprets a GetResult, CreateResult or UpdateResult as a Zone.
224+
// An error is returned if the original call or the extraction failed.
225+
func (r ZoneShareResult) Extract() (*ZoneShare, error) {
226+
var s *ZoneShare
227+
err := r.ExtractInto(&s)
228+
return s, err
229+
}
230+
231+
// ZoneSharePage is a single page of ZoneShare results.
232+
type ZoneSharePage struct {
233+
pagination.LinkedPageBase
234+
}
235+
236+
// IsEmpty returns true if the page contains no results.
237+
func (r ZoneSharePage) IsEmpty() (bool, error) {
238+
if r.StatusCode == 204 {
239+
return true, nil
240+
}
241+
242+
s, err := ExtractZoneShares(r)
243+
return len(s) == 0, err
244+
}
245+
246+
// ExtractZoneShares extracts a slice of ZoneShares from a List result.
247+
func ExtractZoneShares(r pagination.Page) ([]ZoneShare, error) {
248+
var s struct {
249+
ZoneShares []ZoneShare `json:"shared_zones"`
250+
}
251+
err := (r.(ZoneSharePage)).ExtractInto(&s)
252+
return s.ZoneShares, err
253+
}

openstack/dns/v2/zones/testing/fixtures_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,57 @@ func HandleDeleteSuccessfully(t *testing.T, fakeServer th.FakeServer) {
300300
fmt.Fprint(w, DeleteZoneResponse)
301301
})
302302
}
303+
304+
// ShareZoneResponse is a sample response to share a zone.
305+
const ShareZoneResponse = `
306+
{
307+
"id": "fd40b017-bf97-461c-8d30-d4e922b28edd",
308+
"zone_id": "a3365b47-ee93-43ad-9a60-2b2ca96b1898",
309+
"project_id": "16ade46c85a1435bb86d9138d37da57e",
310+
"target_project_id": "232e37df46af42089710e2ae39111c2f",
311+
"created_at": "2022-11-30T22:20:27.000000",
312+
"updated_at": null,
313+
"links": {
314+
"self": "http://127.0.0.1:60053/v2/zones/a3365b47-ee93-43ad-9a60-2b2ca96b1898/shares/fd40b017-bf97-461c-8d30-d4e922b28edd",
315+
"zone": "http://127.0.0.1:60053/v2/zones/a3365b47-ee93-43ad-9a60-2b2ca96b1898"
316+
}
317+
}
318+
`
319+
320+
// ShareZoneCreatedAt is the expected created at time for the shared zone
321+
var ShareZoneCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2022-11-30T22:20:27.000000")
322+
323+
// ShareZone is the expected shared zone
324+
var ShareZone = zones.ZoneShare{
325+
ID: "fd40b017-bf97-461c-8d30-d4e922b28edd",
326+
ZoneID: "a3365b47-ee93-43ad-9a60-2b2ca96b1898",
327+
ProjectID: "16ade46c85a1435bb86d9138d37da57e",
328+
TargetProjectID: "232e37df46af42089710e2ae39111c2f",
329+
CreatedAt: ShareZoneCreatedAt,
330+
}
331+
332+
// ListSharesResponse is a sample response to list zone shares.
333+
const ListSharesResponse = `
334+
{
335+
"shared_zones": [
336+
{
337+
"id": "fd40b017-bf97-461c-8d30-d4e922b28edd",
338+
"zone_id": "a3365b47-ee93-43ad-9a60-2b2ca96b1898",
339+
"project_id": "16ade46c85a1435bb86d9138d37da57e",
340+
"target_project_id": "232e37df46af42089710e2ae39111c2f",
341+
"created_at": "2022-11-30T22:20:27.000000",
342+
"updated_at": null,
343+
"links": {
344+
"self": "http://127.0.0.1:60053/v2/zones/a3365b47-ee93-43ad-9a60-2b2ca96b1898/shares/fd40b017-bf97-461c-8d30-d4e922b28edd",
345+
"zone": "http://127.0.0.1:60053/v2/zones/a3365b47-ee93-43ad-9a60-2b2ca96b1898"
346+
}
347+
}
348+
],
349+
"links": {
350+
"self": "http://127.0.0.1:60053/v2/zones/a3365b47-ee93-43ad-9a60-2b2ca96b1898/shares"
351+
}
352+
}
353+
`
354+
355+
// ListZoneShares is the expected list of shared zones
356+
var ListZoneShares = []zones.ZoneShare{ShareZone}

openstack/dns/v2/zones/testing/requests_test.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package testing
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"io"
78
"net/http"
89
"testing"
@@ -127,11 +128,14 @@ func TestShare(t *testing.T) {
127128
th.CheckDeepEquals(t, expectedBody, reqBody)
128129

129130
w.WriteHeader(http.StatusCreated)
131+
w.Header().Add("Content-Type", "application/json")
132+
fmt.Fprint(w, ShareZoneResponse)
130133
})
131134

132135
opts := zones.ShareZoneOpts{TargetProjectID: "project-id"}
133-
err := zones.Share(context.TODO(), client.ServiceClient(fakeServer), "zone-id", opts).ExtractErr()
136+
zone, err := zones.Share(context.TODO(), client.ServiceClient(fakeServer), "zone-id", opts).Extract()
134137
th.AssertNoErr(t, err)
138+
th.CheckDeepEquals(t, ShareZone, *zone)
135139
}
136140

137141
func TestUnshare(t *testing.T) {
@@ -146,3 +150,25 @@ func TestUnshare(t *testing.T) {
146150
err := zones.Unshare(context.TODO(), client.ServiceClient(fakeServer), "zone-id", "share-id").ExtractErr()
147151
th.AssertNoErr(t, err)
148152
}
153+
154+
func TestListShares(t *testing.T) {
155+
fakeServer := th.SetupHTTP()
156+
defer fakeServer.Teardown()
157+
158+
fakeServer.Mux.HandleFunc("/zones/zone-id/shares", func(w http.ResponseWriter, r *http.Request) {
159+
th.AssertEquals(t, r.Method, "GET")
160+
th.AssertEquals(t, "true", r.Header.Get("X-Auth-All-Projects"))
161+
162+
w.Header().Add("Content-Type", "application/json")
163+
fmt.Fprint(w, ListSharesResponse)
164+
})
165+
166+
opts := zones.ListSharesOpts{
167+
AllProjects: true,
168+
}
169+
pages, err := zones.ListShares(client.ServiceClient(fakeServer), "zone-id", opts).AllPages(context.TODO())
170+
th.AssertNoErr(t, err)
171+
actual, err := zones.ExtractZoneShares(pages)
172+
th.AssertNoErr(t, err)
173+
th.CheckDeepEquals(t, ListZoneShares, actual)
174+
}

openstack/dns/v2/zones/urls.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ func zoneURL(c *gophercloud.ServiceClient, zoneID string) string {
1212
return c.ServiceURL("zones", zoneID)
1313
}
1414

15-
// zoneShareURL returns the URL for sharing a zone.
16-
func zoneShareURL(c *gophercloud.ServiceClient, zoneID string) string {
15+
// sharesBaseURL returns the URL for shared zones.
16+
func sharesBaseURL(c *gophercloud.ServiceClient, zoneID string) string {
1717
return c.ServiceURL("zones", zoneID, "shares")
1818
}
1919

20-
// zoneUnshareURL returns the URL for unsharing a zone.
21-
func zoneUnshareURL(c *gophercloud.ServiceClient, zoneID, shareID string) string {
22-
return c.ServiceURL("zones", zoneID, "shares", shareID)
20+
// shareURL returns the URL for a shared zone.
21+
func shareURL(c *gophercloud.ServiceClient, zoneID, sharedZoneID string) string {
22+
return c.ServiceURL("zones", zoneID, "shares", sharedZoneID)
2323
}

0 commit comments

Comments
 (0)