2018-10-17 06:15:47 -04:00
|
|
|
package hcloud
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2019-08-05 04:10:39 -04:00
|
|
|
"net/url"
|
|
|
|
"time"
|
2018-10-17 06:15:47 -04:00
|
|
|
|
|
|
|
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
|
|
|
)
|
|
|
|
|
|
|
|
// FloatingIP represents a Floating IP in the Hetzner Cloud.
|
|
|
|
type FloatingIP struct {
|
|
|
|
ID int
|
|
|
|
Description string
|
2019-08-05 04:10:39 -04:00
|
|
|
Created time.Time
|
2018-10-17 06:15:47 -04:00
|
|
|
IP net.IP
|
|
|
|
Network *net.IPNet
|
|
|
|
Type FloatingIPType
|
|
|
|
Server *Server
|
|
|
|
DNSPtr map[string]string
|
|
|
|
HomeLocation *Location
|
|
|
|
Blocked bool
|
|
|
|
Protection FloatingIPProtection
|
|
|
|
Labels map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
// DNSPtrForIP returns the reverse DNS pointer of the IP address.
|
|
|
|
func (f *FloatingIP) DNSPtrForIP(ip net.IP) string {
|
|
|
|
return f.DNSPtr[ip.String()]
|
|
|
|
}
|
|
|
|
|
|
|
|
// FloatingIPProtection represents the protection level of a Floating IP.
|
|
|
|
type FloatingIPProtection struct {
|
|
|
|
Delete bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// FloatingIPType represents the type of a Floating IP.
|
|
|
|
type FloatingIPType string
|
|
|
|
|
|
|
|
// Floating IP types.
|
|
|
|
const (
|
|
|
|
FloatingIPTypeIPv4 FloatingIPType = "ipv4"
|
|
|
|
FloatingIPTypeIPv6 FloatingIPType = "ipv6"
|
|
|
|
)
|
|
|
|
|
|
|
|
// FloatingIPClient is a client for the Floating IP API.
|
|
|
|
type FloatingIPClient struct {
|
|
|
|
client *Client
|
|
|
|
}
|
|
|
|
|
2018-11-24 11:56:09 -05:00
|
|
|
// GetByID retrieves a Floating IP by its ID. If the Floating IP does not exist,
|
|
|
|
// nil is returned.
|
2018-10-17 06:15:47 -04:00
|
|
|
func (c *FloatingIPClient) GetByID(ctx context.Context, id int) (*FloatingIP, *Response, error) {
|
|
|
|
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/floating_ips/%d", id), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var body schema.FloatingIPGetResponse
|
|
|
|
resp, err := c.client.Do(req, &body)
|
|
|
|
if err != nil {
|
|
|
|
if IsError(err, ErrorCodeNotFound) {
|
|
|
|
return nil, resp, nil
|
|
|
|
}
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
return FloatingIPFromSchema(body.FloatingIP), resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FloatingIPListOpts specifies options for listing Floating IPs.
|
|
|
|
type FloatingIPListOpts struct {
|
|
|
|
ListOpts
|
|
|
|
}
|
|
|
|
|
2019-08-05 04:10:39 -04:00
|
|
|
func (l FloatingIPListOpts) values() url.Values {
|
|
|
|
return l.ListOpts.values()
|
|
|
|
}
|
|
|
|
|
2018-10-17 06:15:47 -04:00
|
|
|
// List returns a list of Floating IPs for a specific page.
|
|
|
|
func (c *FloatingIPClient) List(ctx context.Context, opts FloatingIPListOpts) ([]*FloatingIP, *Response, error) {
|
2019-08-05 04:10:39 -04:00
|
|
|
path := "/floating_ips?" + opts.values().Encode()
|
2018-10-17 06:15:47 -04:00
|
|
|
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var body schema.FloatingIPListResponse
|
|
|
|
resp, err := c.client.Do(req, &body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
floatingIPs := make([]*FloatingIP, 0, len(body.FloatingIPs))
|
|
|
|
for _, s := range body.FloatingIPs {
|
|
|
|
floatingIPs = append(floatingIPs, FloatingIPFromSchema(s))
|
|
|
|
}
|
|
|
|
return floatingIPs, resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// All returns all Floating IPs.
|
|
|
|
func (c *FloatingIPClient) All(ctx context.Context) ([]*FloatingIP, error) {
|
|
|
|
return c.AllWithOpts(ctx, FloatingIPListOpts{ListOpts{PerPage: 50}})
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllWithOpts returns all Floating IPs for the given options.
|
|
|
|
func (c *FloatingIPClient) AllWithOpts(ctx context.Context, opts FloatingIPListOpts) ([]*FloatingIP, error) {
|
|
|
|
allFloatingIPs := []*FloatingIP{}
|
|
|
|
|
|
|
|
_, err := c.client.all(func(page int) (*Response, error) {
|
|
|
|
opts.Page = page
|
|
|
|
floatingIPs, resp, err := c.List(ctx, opts)
|
|
|
|
if err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
allFloatingIPs = append(allFloatingIPs, floatingIPs...)
|
|
|
|
return resp, nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return allFloatingIPs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FloatingIPCreateOpts specifies options for creating a Floating IP.
|
|
|
|
type FloatingIPCreateOpts struct {
|
|
|
|
Type FloatingIPType
|
|
|
|
HomeLocation *Location
|
|
|
|
Server *Server
|
|
|
|
Description *string
|
|
|
|
Labels map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate checks if options are valid.
|
|
|
|
func (o FloatingIPCreateOpts) Validate() error {
|
|
|
|
switch o.Type {
|
|
|
|
case FloatingIPTypeIPv4, FloatingIPTypeIPv6:
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
return errors.New("missing or invalid type")
|
|
|
|
}
|
|
|
|
if o.HomeLocation == nil && o.Server == nil {
|
|
|
|
return errors.New("one of home location or server is required")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FloatingIPCreateResult is the result of creating a Floating IP.
|
|
|
|
type FloatingIPCreateResult struct {
|
|
|
|
FloatingIP *FloatingIP
|
|
|
|
Action *Action
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create creates a Floating IP.
|
|
|
|
func (c *FloatingIPClient) Create(ctx context.Context, opts FloatingIPCreateOpts) (FloatingIPCreateResult, *Response, error) {
|
|
|
|
if err := opts.Validate(); err != nil {
|
|
|
|
return FloatingIPCreateResult{}, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
reqBody := schema.FloatingIPCreateRequest{
|
|
|
|
Type: string(opts.Type),
|
|
|
|
Description: opts.Description,
|
|
|
|
}
|
|
|
|
if opts.HomeLocation != nil {
|
|
|
|
reqBody.HomeLocation = String(opts.HomeLocation.Name)
|
|
|
|
}
|
|
|
|
if opts.Server != nil {
|
|
|
|
reqBody.Server = Int(opts.Server.ID)
|
|
|
|
}
|
|
|
|
if opts.Labels != nil {
|
|
|
|
reqBody.Labels = &opts.Labels
|
|
|
|
}
|
|
|
|
reqBodyData, err := json.Marshal(reqBody)
|
|
|
|
if err != nil {
|
|
|
|
return FloatingIPCreateResult{}, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := c.client.NewRequest(ctx, "POST", "/floating_ips", bytes.NewReader(reqBodyData))
|
|
|
|
if err != nil {
|
|
|
|
return FloatingIPCreateResult{}, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respBody schema.FloatingIPCreateResponse
|
|
|
|
resp, err := c.client.Do(req, &respBody)
|
|
|
|
if err != nil {
|
|
|
|
return FloatingIPCreateResult{}, resp, err
|
|
|
|
}
|
|
|
|
var action *Action
|
|
|
|
if respBody.Action != nil {
|
|
|
|
action = ActionFromSchema(*respBody.Action)
|
|
|
|
}
|
|
|
|
return FloatingIPCreateResult{
|
|
|
|
FloatingIP: FloatingIPFromSchema(respBody.FloatingIP),
|
|
|
|
Action: action,
|
|
|
|
}, resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete deletes a Floating IP.
|
|
|
|
func (c *FloatingIPClient) Delete(ctx context.Context, floatingIP *FloatingIP) (*Response, error) {
|
|
|
|
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/floating_ips/%d", floatingIP.ID), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return c.client.Do(req, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FloatingIPUpdateOpts specifies options for updating a Floating IP.
|
|
|
|
type FloatingIPUpdateOpts struct {
|
|
|
|
Description string
|
|
|
|
Labels map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update updates a Floating IP.
|
|
|
|
func (c *FloatingIPClient) Update(ctx context.Context, floatingIP *FloatingIP, opts FloatingIPUpdateOpts) (*FloatingIP, *Response, error) {
|
|
|
|
reqBody := schema.FloatingIPUpdateRequest{
|
|
|
|
Description: opts.Description,
|
|
|
|
}
|
|
|
|
if opts.Labels != nil {
|
|
|
|
reqBody.Labels = &opts.Labels
|
|
|
|
}
|
|
|
|
reqBodyData, err := json.Marshal(reqBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/floating_ips/%d", floatingIP.ID)
|
|
|
|
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
respBody := schema.FloatingIPUpdateResponse{}
|
|
|
|
resp, err := c.client.Do(req, &respBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
return FloatingIPFromSchema(respBody.FloatingIP), resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assign assigns a Floating IP to a server.
|
|
|
|
func (c *FloatingIPClient) Assign(ctx context.Context, floatingIP *FloatingIP, server *Server) (*Action, *Response, error) {
|
|
|
|
reqBody := schema.FloatingIPActionAssignRequest{
|
|
|
|
Server: server.ID,
|
|
|
|
}
|
|
|
|
reqBodyData, err := json.Marshal(reqBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/floating_ips/%d/actions/assign", floatingIP.ID)
|
|
|
|
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respBody schema.FloatingIPActionAssignResponse
|
|
|
|
resp, err := c.client.Do(req, &respBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
return ActionFromSchema(respBody.Action), resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unassign unassigns a Floating IP from the currently assigned server.
|
|
|
|
func (c *FloatingIPClient) Unassign(ctx context.Context, floatingIP *FloatingIP) (*Action, *Response, error) {
|
|
|
|
var reqBody schema.FloatingIPActionUnassignRequest
|
|
|
|
reqBodyData, err := json.Marshal(reqBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/floating_ips/%d/actions/unassign", floatingIP.ID)
|
|
|
|
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respBody schema.FloatingIPActionUnassignResponse
|
|
|
|
resp, err := c.client.Do(req, &respBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
return ActionFromSchema(respBody.Action), resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChangeDNSPtr changes or resets the reverse DNS pointer for a Floating IP address.
|
|
|
|
// Pass a nil ptr to reset the reverse DNS pointer to its default value.
|
|
|
|
func (c *FloatingIPClient) ChangeDNSPtr(ctx context.Context, floatingIP *FloatingIP, ip string, ptr *string) (*Action, *Response, error) {
|
|
|
|
reqBody := schema.FloatingIPActionChangeDNSPtrRequest{
|
|
|
|
IP: ip,
|
|
|
|
DNSPtr: ptr,
|
|
|
|
}
|
|
|
|
reqBodyData, err := json.Marshal(reqBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/floating_ips/%d/actions/change_dns_ptr", floatingIP.ID)
|
|
|
|
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
respBody := schema.FloatingIPActionChangeDNSPtrResponse{}
|
|
|
|
resp, err := c.client.Do(req, &respBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
return ActionFromSchema(respBody.Action), resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FloatingIPChangeProtectionOpts specifies options for changing the resource protection level of a Floating IP.
|
|
|
|
type FloatingIPChangeProtectionOpts struct {
|
|
|
|
Delete *bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChangeProtection changes the resource protection level of a Floating IP.
|
|
|
|
func (c *FloatingIPClient) ChangeProtection(ctx context.Context, floatingIP *FloatingIP, opts FloatingIPChangeProtectionOpts) (*Action, *Response, error) {
|
|
|
|
reqBody := schema.FloatingIPActionChangeProtectionRequest{
|
|
|
|
Delete: opts.Delete,
|
|
|
|
}
|
|
|
|
reqBodyData, err := json.Marshal(reqBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/floating_ips/%d/actions/change_protection", floatingIP.ID)
|
|
|
|
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
respBody := schema.FloatingIPActionChangeProtectionResponse{}
|
|
|
|
resp, err := c.client.Do(req, &respBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
return ActionFromSchema(respBody.Action), resp, err
|
|
|
|
}
|