1package linodego
2
3import (
4	"context"
5	"encoding/json"
6	"fmt"
7)
8
9// InstanceIPAddressResponse contains the IPv4 and IPv6 details for an Instance
10type InstanceIPAddressResponse struct {
11	IPv4 *InstanceIPv4Response `json:"ipv4"`
12	IPv6 *InstanceIPv6Response `json:"ipv6"`
13}
14
15// InstanceIPv4Response contains the details of all IPv4 addresses associated with an Instance
16type InstanceIPv4Response struct {
17	Public   []*InstanceIP `json:"public"`
18	Private  []*InstanceIP `json:"private"`
19	Shared   []*InstanceIP `json:"shared"`
20	Reserved []*InstanceIP `json:"reserved"`
21}
22
23// InstanceIP represents an Instance IP with additional DNS and networking details
24type InstanceIP struct {
25	Address    string         `json:"address"`
26	Gateway    string         `json:"gateway"`
27	SubnetMask string         `json:"subnet_mask"`
28	Prefix     int            `json:"prefix"`
29	Type       InstanceIPType `json:"type"`
30	Public     bool           `json:"public"`
31	RDNS       string         `json:"rdns"`
32	LinodeID   int            `json:"linode_id"`
33	Region     string         `json:"region"`
34}
35
36// InstanceIPv6Response contains the IPv6 addresses and ranges for an Instance
37type InstanceIPv6Response struct {
38	LinkLocal *InstanceIP  `json:"link_local"`
39	SLAAC     *InstanceIP  `json:"slaac"`
40	Global    []*IPv6Range `json:"global"`
41}
42
43// IPv6Range represents a range of IPv6 addresses routed to a single Linode in a given Region
44type IPv6Range struct {
45	Range  string `json:"range"`
46	Region string `json:"region"`
47	Prefix int    `json:"prefix"`
48}
49
50// InstanceIPType constants start with IPType and include Linode Instance IP Types
51type InstanceIPType string
52
53// InstanceIPType constants represent the IP types an Instance IP may be
54const (
55	IPTypeIPv4      InstanceIPType = "ipv4"
56	IPTypeIPv6      InstanceIPType = "ipv6"
57	IPTypeIPv6Pool  InstanceIPType = "ipv6/pool"
58	IPTypeIPv6Range InstanceIPType = "ipv6/range"
59)
60
61// GetInstanceIPAddresses gets the IPAddresses for a Linode instance
62func (c *Client) GetInstanceIPAddresses(ctx context.Context, linodeID int) (*InstanceIPAddressResponse, error) {
63	e, err := c.InstanceIPs.endpointWithParams(linodeID)
64	if err != nil {
65		return nil, err
66	}
67
68	r, err := coupleAPIErrors(c.R(ctx).SetResult(&InstanceIPAddressResponse{}).Get(e))
69	if err != nil {
70		return nil, err
71	}
72	return r.Result().(*InstanceIPAddressResponse), nil
73}
74
75// GetInstanceIPAddress gets the IPAddress for a Linode instance matching a supplied IP address
76func (c *Client) GetInstanceIPAddress(ctx context.Context, linodeID int, ipaddress string) (*InstanceIP, error) {
77	e, err := c.InstanceIPs.endpointWithParams(linodeID)
78	if err != nil {
79		return nil, err
80	}
81	e = fmt.Sprintf("%s/%s", e, ipaddress)
82	r, err := coupleAPIErrors(c.R(ctx).SetResult(&InstanceIP{}).Get(e))
83	if err != nil {
84		return nil, err
85	}
86	return r.Result().(*InstanceIP), nil
87}
88
89// AddInstanceIPAddress adds a public or private IP to a Linode instance
90func (c *Client) AddInstanceIPAddress(ctx context.Context, linodeID int, public bool) (*InstanceIP, error) {
91	var body string
92	e, err := c.InstanceIPs.endpointWithParams(linodeID)
93	if err != nil {
94		return nil, err
95	}
96
97	req := c.R(ctx).SetResult(&InstanceIP{})
98
99	instanceipRequest := struct {
100		Type   string `json:"type"`
101		Public bool   `json:"public"`
102	}{"ipv4", public}
103
104	if bodyData, err := json.Marshal(instanceipRequest); err == nil {
105		body = string(bodyData)
106	} else {
107		return nil, NewError(err)
108	}
109
110	r, err := coupleAPIErrors(req.
111		SetHeader("Content-Type", "application/json").
112		SetBody(body).
113		Post(e))
114	if err != nil {
115		return nil, err
116	}
117
118	return r.Result().(*InstanceIP), nil
119}
120
121// UpdateInstanceIPAddress updates the IPAddress with the specified instance id and IP address
122func (c *Client) UpdateInstanceIPAddress(ctx context.Context, linodeID int, ipAddress string, updateOpts IPAddressUpdateOptions) (*InstanceIP, error) {
123	var body string
124	e, err := c.InstanceIPs.endpointWithParams(linodeID)
125	if err != nil {
126		return nil, err
127	}
128	e = fmt.Sprintf("%s/%s", e, ipAddress)
129
130	req := c.R(ctx).SetResult(&InstanceIP{})
131
132	if bodyData, err := json.Marshal(updateOpts); err == nil {
133		body = string(bodyData)
134	} else {
135		return nil, NewError(err)
136	}
137
138	r, err := coupleAPIErrors(req.
139		SetBody(body).
140		Put(e))
141	if err != nil {
142		return nil, err
143	}
144	return r.Result().(*InstanceIP), nil
145}
146
147func (c *Client) DeleteInstanceIPAddress(ctx context.Context, linodeID int, ipAddress string) error {
148	e, err := c.InstanceIPs.endpointWithParams(linodeID)
149	if err != nil {
150		return err
151	}
152
153	e = fmt.Sprintf("%s/%s", e, ipAddress)
154	_, err = coupleAPIErrors(c.R(ctx).Delete(e))
155	return err
156}
157