1package godo
2
3import (
4	"context"
5	"fmt"
6	"net/http"
7)
8
9const floatingBasePath = "v2/floating_ips"
10
11// FloatingIPsService is an interface for interfacing with the floating IPs
12// endpoints of the Digital Ocean API.
13// See: https://docs.digitalocean.com/reference/api/api-reference/#tag/Floating-IPs
14type FloatingIPsService interface {
15	List(context.Context, *ListOptions) ([]FloatingIP, *Response, error)
16	Get(context.Context, string) (*FloatingIP, *Response, error)
17	Create(context.Context, *FloatingIPCreateRequest) (*FloatingIP, *Response, error)
18	Delete(context.Context, string) (*Response, error)
19}
20
21// FloatingIPsServiceOp handles communication with the floating IPs related methods of the
22// DigitalOcean API.
23type FloatingIPsServiceOp struct {
24	client *Client
25}
26
27var _ FloatingIPsService = &FloatingIPsServiceOp{}
28
29// FloatingIP represents a Digital Ocean floating IP.
30type FloatingIP struct {
31	Region  *Region  `json:"region"`
32	Droplet *Droplet `json:"droplet"`
33	IP      string   `json:"ip"`
34}
35
36func (f FloatingIP) String() string {
37	return Stringify(f)
38}
39
40// URN returns the floating IP in a valid DO API URN form.
41func (f FloatingIP) URN() string {
42	return ToURN("FloatingIP", f.IP)
43}
44
45type floatingIPsRoot struct {
46	FloatingIPs []FloatingIP `json:"floating_ips"`
47	Links       *Links       `json:"links"`
48	Meta        *Meta        `json:"meta"`
49}
50
51type floatingIPRoot struct {
52	FloatingIP *FloatingIP `json:"floating_ip"`
53	Links      *Links      `json:"links,omitempty"`
54}
55
56// FloatingIPCreateRequest represents a request to create a floating IP.
57// Specify DropletID to assign the floating IP to a Droplet or Region
58// to reserve it to the region.
59type FloatingIPCreateRequest struct {
60	Region    string `json:"region,omitempty"`
61	DropletID int    `json:"droplet_id,omitempty"`
62}
63
64// List all floating IPs.
65func (f *FloatingIPsServiceOp) List(ctx context.Context, opt *ListOptions) ([]FloatingIP, *Response, error) {
66	path := floatingBasePath
67	path, err := addOptions(path, opt)
68	if err != nil {
69		return nil, nil, err
70	}
71
72	req, err := f.client.NewRequest(ctx, http.MethodGet, path, nil)
73	if err != nil {
74		return nil, nil, err
75	}
76
77	root := new(floatingIPsRoot)
78	resp, err := f.client.Do(ctx, req, root)
79	if err != nil {
80		return nil, resp, err
81	}
82	if l := root.Links; l != nil {
83		resp.Links = l
84	}
85	if m := root.Meta; m != nil {
86		resp.Meta = m
87	}
88
89	return root.FloatingIPs, resp, err
90}
91
92// Get an individual floating IP.
93func (f *FloatingIPsServiceOp) Get(ctx context.Context, ip string) (*FloatingIP, *Response, error) {
94	path := fmt.Sprintf("%s/%s", floatingBasePath, ip)
95
96	req, err := f.client.NewRequest(ctx, http.MethodGet, path, nil)
97	if err != nil {
98		return nil, nil, err
99	}
100
101	root := new(floatingIPRoot)
102	resp, err := f.client.Do(ctx, req, root)
103	if err != nil {
104		return nil, resp, err
105	}
106
107	return root.FloatingIP, resp, err
108}
109
110// Create a floating IP. If the DropletID field of the request is not empty,
111// the floating IP will also be assigned to the droplet.
112func (f *FloatingIPsServiceOp) Create(ctx context.Context, createRequest *FloatingIPCreateRequest) (*FloatingIP, *Response, error) {
113	path := floatingBasePath
114
115	req, err := f.client.NewRequest(ctx, http.MethodPost, path, createRequest)
116	if err != nil {
117		return nil, nil, err
118	}
119
120	root := new(floatingIPRoot)
121	resp, err := f.client.Do(ctx, req, root)
122	if err != nil {
123		return nil, resp, err
124	}
125	if l := root.Links; l != nil {
126		resp.Links = l
127	}
128
129	return root.FloatingIP, resp, err
130}
131
132// Delete a floating IP.
133func (f *FloatingIPsServiceOp) Delete(ctx context.Context, ip string) (*Response, error) {
134	path := fmt.Sprintf("%s/%s", floatingBasePath, ip)
135
136	req, err := f.client.NewRequest(ctx, http.MethodDelete, path, nil)
137	if err != nil {
138		return nil, err
139	}
140
141	resp, err := f.client.Do(ctx, req, nil)
142
143	return resp, err
144}
145