-
Notifications
You must be signed in to change notification settings - Fork 303
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package godo | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
const resourceV6Type = "ReservedIPv6" | ||
const reservedIPV6sBasePath = "v2/reserved_ipv6" | ||
|
||
// ReservedIPV6sService is an interface for interfacing with the reserved IPV6s | ||
// endpoints of the Digital Ocean API. | ||
type ReservedIPV6sService interface { | ||
List(context.Context, *ListOptions) ([]ReservedIPV6, *Response, error) | ||
Get(context.Context, string) (*ReservedIPV6, *Response, error) | ||
Reserve(context.Context, *ReservedIPV6ReserveRequest) (*ReservedIPV6, *Response, error) | ||
Release(context.Context, string) (*Response, error) | ||
} | ||
|
||
// ReservedIPV6sServiceOp handles communication with the reserved IPs related methods of the | ||
// DigitalOcean API. | ||
type ReservedIPV6sServiceOp struct { | ||
client *Client | ||
} | ||
|
||
var _ ReservedIPV6sService = &ReservedIPV6sServiceOp{} | ||
|
||
// ReservedIPV6 represents a Digital Ocean reserved IP. | ||
type ReservedIPV6 struct { | ||
RegionSlug string `json:"region_slug"` | ||
IP string `json:"ip"` | ||
ReservedAt time.Time `json:"reserved_at"` | ||
Droplet *Droplet `json:"droplet,omitempty"` | ||
} | ||
|
||
func (f ReservedIPV6) String() string { | ||
return Stringify(f) | ||
} | ||
|
||
// URN returns the reserved IP in a valid DO API URN form. | ||
func (f ReservedIPV6) URN() string { | ||
return ToURN(resourceV6Type, f.IP) | ||
} | ||
|
||
type reservedIPV6sRoot struct { | ||
ReservedIPs []ReservedIPV6 `json:"reserved_ips"` | ||
Links *Links `json:"links"` | ||
Meta *Meta `json:"meta"` | ||
} | ||
|
||
type reservedIPV6Root struct { | ||
ReservedIP *ReservedIPV6 `json:"reserved_ip"` | ||
Links *Links `json:"links,omitempty"` | ||
} | ||
|
||
// ReservedIPV6ReserveRequest represents a request to reserve a reserved IP. | ||
type ReservedIPV6ReserveRequest struct { | ||
Region string `json:"region_slug,omitempty"` | ||
} | ||
|
||
// List all reserved IPV6s. | ||
func (r *ReservedIPV6sServiceOp) List(ctx context.Context, opt *ListOptions) ([]ReservedIPV6, *Response, error) { | ||
path := reservedIPV6sBasePath | ||
path, err := addOptions(path, opt) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
req, err := r.client.NewRequest(ctx, http.MethodGet, path, nil) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
root := new(reservedIPV6sRoot) | ||
resp, err := r.client.Do(ctx, req, root) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
if l := root.Links; l != nil { | ||
resp.Links = l | ||
} | ||
if m := root.Meta; m != nil { | ||
resp.Meta = m | ||
} | ||
|
||
return root.ReservedIPs, resp, err | ||
} | ||
|
||
// Get an individual reserved IPv6. | ||
func (r *ReservedIPV6sServiceOp) Get(ctx context.Context, ip string) (*ReservedIPV6, *Response, error) { | ||
path := fmt.Sprintf("%s/%s", reservedIPV6sBasePath, ip) | ||
|
||
req, err := r.client.NewRequest(ctx, http.MethodGet, path, nil) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
root := new(ReservedIPV6) | ||
resp, err := r.client.Do(ctx, req, root) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return root, resp, err | ||
} | ||
|
||
// Reserve a new IPv6 | ||
func (r *ReservedIPV6sServiceOp) Reserve(ctx context.Context, reserveRequest *ReservedIPV6ReserveRequest) (*ReservedIPV6, *Response, error) { | ||
path := reservedIPV6sBasePath | ||
|
||
req, err := r.client.NewRequest(ctx, http.MethodPost, path, reserveRequest) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
root := new(ReservedIPV6) | ||
resp, err := r.client.Do(ctx, req, root) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return root, resp, err | ||
} | ||
|
||
// Release a reserved IPv6. | ||
func (r *ReservedIPV6sServiceOp) Release(ctx context.Context, ip string) (*Response, error) { | ||
path := fmt.Sprintf("%s/%s", reservedIPV6sBasePath, ip) | ||
|
||
req, err := r.client.NewRequest(ctx, http.MethodDelete, path, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
resp, err := r.client.Do(ctx, req, nil) | ||
|
||
return resp, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package godo | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"reflect" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestReservedIPV6s_Reserve(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
reserveRequest := &ReservedIPV6ReserveRequest{ | ||
Region: "nyc3", | ||
} | ||
nowTime := time.Now() | ||
|
||
mux.HandleFunc("/v2/reserved_ipv6", func(w http.ResponseWriter, r *http.Request) { | ||
v := new(ReservedIPV6ReserveRequest) | ||
err := json.NewDecoder(r.Body).Decode(v) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
testMethod(t, r, http.MethodPost) | ||
if !reflect.DeepEqual(v, reserveRequest) { | ||
t.Errorf("Request body = %+v, expected %+v", v, reserveRequest) | ||
} | ||
|
||
fmt.Fprint(w, `{"ip":"2604:a880:800:14::42c3:d000","region_slug":"nyc3","reserved_at":"`+nowTime.Format(time.RFC3339Nano)+`"}`) | ||
}) | ||
|
||
reservedIP, _, err := client.ReservedIPV6s.Reserve(ctx, reserveRequest) | ||
if err != nil { | ||
t.Errorf("ReservedIPV6s.Create returned error: %v", err) | ||
} | ||
|
||
expected := &ReservedIPV6{RegionSlug: "nyc3", IP: "2604:a880:800:14::42c3:d000", ReservedAt: nowTime} | ||
|
||
if !equalReserveIPv6Objects(reservedIP, expected) { | ||
t.Errorf("ReservedIPs.Create returned %+v, expected %+v", reservedIP, expected) | ||
} | ||
} | ||
|
||
func equalReserveIPv6Objects(a, b *ReservedIPV6) bool { | ||
return a.IP == b.IP && | ||
a.RegionSlug == b.RegionSlug && | ||
a.ReservedAt.Equal(b.ReservedAt) && | ||
reflect.DeepEqual(a.Droplet, b.Droplet) | ||
} | ||
|
||
func TestReservedIPV6s_List(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/v2/reserved_ipv6", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, http.MethodGet) | ||
fmt.Fprint(w, `{"reserved_ips": [ | ||
{"region_slug":"nyc3","droplet":{"id":1},"ip":"2604:a880:800:14::42c3:d000"}, | ||
{"region_slug":"nyc3","droplet":{"id":2},"ip":"2604:a880:800:14::42c3:d001"} | ||
], | ||
"meta":{"total":2} | ||
}`) | ||
}) | ||
|
||
reservedIPs, resp, err := client.ReservedIPV6s.List(ctx, nil) | ||
if err != nil { | ||
t.Errorf("ReservedIPs.List returned error: %v", err) | ||
} | ||
|
||
expectedReservedIPs := []ReservedIPV6{ | ||
{RegionSlug: "nyc3", Droplet: &Droplet{ID: 1}, IP: "2604:a880:800:14::42c3:d000"}, | ||
{RegionSlug: "nyc3", Droplet: &Droplet{ID: 2}, IP: "2604:a880:800:14::42c3:d001"}, | ||
} | ||
if !reflect.DeepEqual(reservedIPs, expectedReservedIPs) { | ||
t.Errorf("ReservedIPV6s.List returned reserved IPs %+v, expected %+v", reservedIPs, expectedReservedIPs) | ||
} | ||
|
||
expectedMeta := &Meta{ | ||
Total: 2, | ||
} | ||
if !reflect.DeepEqual(resp.Meta, expectedMeta) { | ||
t.Errorf("ReservedIPs.List returned meta %+v, expected %+v", resp.Meta, expectedMeta) | ||
} | ||
} | ||
|
||
func TestReservedIPV6s_ListReservedIPsMultiplePages(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/v2/reserved_ipv6", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, http.MethodGet) | ||
fmt.Fprint(w, `{"reserved_ips": [ | ||
{"region_slug":"nyc3","droplet":{"id":1},"ip":"2604:a880:800:14::42c3:d001"}, | ||
{"region":{"slug":"nyc3"},"droplet":{"id":2},"ip":"2604:a880:800:14::42c3:d002"}], | ||
"links":{"pages":{"next":"http://example.com/v2/reserved_ipv6/?page=2"}}} | ||
`) | ||
}) | ||
|
||
_, resp, err := client.ReservedIPV6s.List(ctx, nil) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
checkCurrentPage(t, resp, 1) | ||
} | ||
|
||
func TestReservedIPV6s_Get(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
nowTime := time.Now() | ||
mux.HandleFunc("/v2/reserved_ipv6/2604:a880:800:14::42c3:d001", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, http.MethodGet) | ||
fmt.Fprint(w, `{"region_slug":"nyc3","droplet":{"id":1},"ip":"2604:a880:800:14::42c3:d001", "reserved_at":"`+nowTime.Format(time.RFC3339Nano)+`"}`) | ||
}) | ||
|
||
reservedIP, _, err := client.ReservedIPV6s.Get(ctx, "2604:a880:800:14::42c3:d001") | ||
if err != nil { | ||
t.Errorf("ReservedIPV6s.Get returned error: %v", err) | ||
} | ||
|
||
expected := &ReservedIPV6{RegionSlug: "nyc3", Droplet: &Droplet{ID: 1}, IP: "2604:a880:800:14::42c3:d001", ReservedAt: nowTime} | ||
if !equalReserveIPv6Objects(reservedIP, expected) { | ||
t.Errorf("ReservedIPV6s.Get returned %+v, expected %+v", reservedIP, expected) | ||
} | ||
} | ||
|
||
func TestReservedIPV6s_Release(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/v2/reserved_ipv6/2604:a880:800:14::42c3:d001", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, http.MethodDelete) | ||
}) | ||
|
||
_, err := client.ReservedIPV6s.Release(ctx, "2604:a880:800:14::42c3:d001") | ||
if err != nil { | ||
t.Errorf("ReservedIPV6s.Release returned error: %v", err) | ||
} | ||
} |