Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rdns client #183

Merged
merged 4 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions hcloud/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type Client struct {
SSHKey SSHKeyClient
Volume VolumeClient
PlacementGroup PlacementGroupClient
RDNS RDNSClient
}

// A ClientOption is used to configure a Client.
Expand Down Expand Up @@ -166,6 +167,7 @@ func NewClient(options ...ClientOption) *Client {
client.Certificate = CertificateClient{client: client}
client.Firewall = FirewallClient{client: client}
client.PlacementGroup = PlacementGroupClient{client: client}
client.RDNS = RDNSClient{client: client}

return client
}
Expand Down
21 changes: 20 additions & 1 deletion hcloud/error.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package hcloud

import "fmt"
import (
"fmt"
"net"
)

// ErrorCode represents an error code returned from the API.
type ErrorCode string
Expand Down Expand Up @@ -103,3 +106,19 @@ func IsError(err error, code ErrorCode) bool {
apiErr, ok := err.(Error)
return ok && apiErr.Code == code
}

type InvalidIPError struct {
IP string
}

func (e InvalidIPError) Error() string {
return fmt.Sprintf("could not parse ip address %s", e.IP)
}

type DNSNotFoundError struct {
IP net.IP
}

func (e DNSNotFoundError) Error() string {
return fmt.Sprintf("dns for ip %s not found", e.IP.String())
}
62 changes: 42 additions & 20 deletions hcloud/floating_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type FloatingIP struct {
}

// DNSPtrForIP returns the reverse DNS pointer of the IP address.
// Deprecated: Use GetDNSPtrForIP instead
func (f *FloatingIP) DNSPtrForIP(ip net.IP) string {
return f.DNSPtr[ip.String()]
}
Expand All @@ -50,6 +51,43 @@ const (
FloatingIPTypeIPv6 FloatingIPType = "ipv6"
)

// changeDNSPtr changes or resets the reverse DNS pointer for a IP address.
// Pass a nil ptr to reset the reverse DNS pointer to its default value.
func (f *FloatingIP) changeDNSPtr(ctx context.Context, client *Client, ip net.IP, ptr *string) (*Action, *Response, error) {
reqBody := schema.FloatingIPActionChangeDNSPtrRequest{
IP: ip.String(),
DNSPtr: ptr,
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}

path := fmt.Sprintf("/floating_ips/%d/actions/change_dns_ptr", f.ID)
req, err := client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}

respBody := schema.FloatingIPActionChangeDNSPtrResponse{}
resp, err := client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
}

// GetDNSPtrForIP searches for the dns assigned to the given IP address.
// It returns an error if there is no dns set for the given IP address.
func (f *FloatingIP) GetDNSPtrForIP(ip net.IP) (string, error) {
dns, ok := f.DNSPtr[ip.String()]
if !ok {
return "", DNSNotFoundError{ip}
}

return dns, nil
}

// FloatingIPClient is a client for the Floating IP API.
type FloatingIPClient struct {
client *Client
Expand Down Expand Up @@ -325,27 +363,11 @@ func (c *FloatingIPClient) Unassign(ctx context.Context, floatingIP *FloatingIP)
// 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,
netIP := net.ParseIP(ip)
if netIP == nil {
return nil, nil, InvalidIPError{ip}
}
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
return floatingIP.changeDNSPtr(ctx, c.client, net.ParseIP(ip), ptr)
}

// FloatingIPChangeProtectionOpts specifies options for changing the resource protection level of a Floating IP.
Expand Down
69 changes: 0 additions & 69 deletions hcloud/floating_ip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,75 +535,6 @@ func TestFloatingIPClientUnassign(t *testing.T) {
}
}

func TestFloatingIPClientChangeDNSPtr(t *testing.T) {
var (
ctx = context.Background()
floatingIP = &FloatingIP{ID: 1}
)

t.Run("set", func(t *testing.T) {
env := newTestEnv()
defer env.Teardown()

env.Mux.HandleFunc("/floating_ips/1/actions/change_dns_ptr", func(w http.ResponseWriter, r *http.Request) {
var reqBody schema.FloatingIPActionChangeDNSPtrRequest
if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
t.Fatal(err)
}
if reqBody.IP != "127.0.0.1" {
t.Errorf("unexpected IP: %v", reqBody.IP)
}
if reqBody.DNSPtr == nil || *reqBody.DNSPtr != "example.com" {
t.Errorf("unexpected DNS ptr: %v", reqBody.DNSPtr)
}
json.NewEncoder(w).Encode(schema.FloatingIPActionChangeDNSPtrResponse{
Action: schema.Action{
ID: 1,
},
})
})

action, _, err := env.Client.FloatingIP.ChangeDNSPtr(ctx, floatingIP, "127.0.0.1", String("example.com"))
if err != nil {
t.Fatal(err)
}
if action.ID != 1 {
t.Errorf("unexpected action ID: %d", action.ID)
}
})

t.Run("reset", func(t *testing.T) {
env := newTestEnv()
defer env.Teardown()

env.Mux.HandleFunc("/floating_ips/1/actions/change_dns_ptr", func(w http.ResponseWriter, r *http.Request) {
var reqBody schema.FloatingIPActionChangeDNSPtrRequest
if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
t.Fatal(err)
}
if reqBody.IP != "127.0.0.1" {
t.Errorf("unexpected IP: %v", reqBody.IP)
}
if reqBody.DNSPtr != nil {
t.Errorf("unexpected DNS ptr: %v", reqBody.DNSPtr)
}
json.NewEncoder(w).Encode(schema.FloatingIPActionChangeDNSPtrResponse{
Action: schema.Action{
ID: 1,
},
})
})

action, _, err := env.Client.FloatingIP.ChangeDNSPtr(ctx, floatingIP, "127.0.0.1", nil)
if err != nil {
t.Fatal(err)
}
if action.ID != 1 {
t.Errorf("unexpected action ID: %d", action.ID)
}
})
}

func TestFloatingIPClientChangeProtection(t *testing.T) {
var (
ctx = context.Background()
Expand Down
2 changes: 1 addition & 1 deletion hcloud/hcloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
package hcloud

// Version is the library's version following Semantic Versioning.
const Version = "1.31.0"
const Version = "1.31.1"
62 changes: 42 additions & 20 deletions hcloud/load_balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,44 @@ type LoadBalancerProtection struct {
Delete bool
}

// changeDNSPtr changes or resets the reverse DNS pointer for a IP address.
// Pass a nil ptr to reset the reverse DNS pointer to its default value.
func (lb *LoadBalancer) changeDNSPtr(ctx context.Context, client *Client, ip net.IP, ptr *string) (*Action, *Response, error) {
reqBody := schema.LoadBalancerActionChangeDNSPtrRequest{
IP: ip.String(),
DNSPtr: ptr,
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}

path := fmt.Sprintf("/load_balancers/%d/actions/change_dns_ptr", lb.ID)
req, err := client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}

respBody := schema.LoadBalancerActionChangeDNSPtrResponse{}
resp, err := client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
}

// GetDNSPtrForIP searches for the dns assigned to the given IP address.
// It returns an error if there is no dns set for the given IP address.
func (lb *LoadBalancer) GetDNSPtrForIP(ip net.IP) (string, error) {
if net.IP.Equal(lb.PublicNet.IPv4.IP, ip) {
return lb.PublicNet.IPv4.DNSPtr, nil
} else if net.IP.Equal(lb.PublicNet.IPv6.IP, ip) {
return lb.PublicNet.IPv6.DNSPtr, nil
}

return "", DNSNotFoundError{ip}
}

// LoadBalancerClient is a client for the Load Balancers API.
type LoadBalancerClient struct {
client *Client
Expand Down Expand Up @@ -1030,25 +1068,9 @@ func (c *LoadBalancerClient) GetMetrics(
// ChangeDNSPtr changes or resets the reverse DNS pointer for a Load Balancer.
// Pass a nil ptr to reset the reverse DNS pointer to its default value.
func (c *LoadBalancerClient) ChangeDNSPtr(ctx context.Context, lb *LoadBalancer, ip string, ptr *string) (*Action, *Response, error) {
reqBody := schema.LoadBalancerActionChangeDNSPtrRequest{
IP: ip,
DNSPtr: ptr,
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}

path := fmt.Sprintf("/load_balancers/%d/actions/change_dns_ptr", lb.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
netIP := net.ParseIP(ip)
if netIP == nil {
return nil, nil, InvalidIPError{ip}
}

respBody := schema.LoadBalancerActionChangeDNSPtrResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
return lb.changeDNSPtr(ctx, c.client, net.ParseIP(ip), ptr)
}
69 changes: 0 additions & 69 deletions hcloud/load_balancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1249,72 +1249,3 @@ func loadBalancerMetricsOptsFromURL(t *testing.T, u *url.URL) LoadBalancerGetMet

return opts
}

func TestLoadBalancerClientChangeDNSPtr(t *testing.T) {
var (
ctx = context.Background()
loadBalancer = &LoadBalancer{ID: 1}
)

t.Run("set", func(t *testing.T) {
env := newTestEnv()
defer env.Teardown()

env.Mux.HandleFunc("/load_balancers/1/actions/change_dns_ptr", func(w http.ResponseWriter, r *http.Request) {
var reqBody schema.LoadBalancerActionChangeDNSPtrRequest
if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
t.Fatal(err)
}
if reqBody.IP != "127.0.0.1" {
t.Errorf("unexpected IP: %v", reqBody.IP)
}
if reqBody.DNSPtr == nil || *reqBody.DNSPtr != "example.com" {
t.Errorf("unexpected DNS ptr: %v", reqBody.DNSPtr)
}
json.NewEncoder(w).Encode(schema.LoadBalancerActionChangeDNSPtrResponse{
Action: schema.Action{
ID: 1,
},
})
})

action, _, err := env.Client.LoadBalancer.ChangeDNSPtr(ctx, loadBalancer, "127.0.0.1", String("example.com"))
if err != nil {
t.Fatal(err)
}
if action.ID != 1 {
t.Errorf("unexpected action ID: %d", action.ID)
}
})

t.Run("reset", func(t *testing.T) {
env := newTestEnv()
defer env.Teardown()

env.Mux.HandleFunc("/load_balancers/1/actions/change_dns_ptr", func(w http.ResponseWriter, r *http.Request) {
var reqBody schema.LoadBalancerActionChangeDNSPtrRequest
if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
t.Fatal(err)
}
if reqBody.IP != "127.0.0.1" {
t.Errorf("unexpected IP: %v", reqBody.IP)
}
if reqBody.DNSPtr != nil {
t.Errorf("unexpected DNS ptr: %v", reqBody.DNSPtr)
}
json.NewEncoder(w).Encode(schema.LoadBalancerActionChangeDNSPtrResponse{
Action: schema.Action{
ID: 1,
},
})
})

action, _, err := env.Client.LoadBalancer.ChangeDNSPtr(ctx, loadBalancer, "127.0.0.1", nil)
if err != nil {
t.Fatal(err)
}
if action.ID != 1 {
t.Errorf("unexpected action ID: %d", action.ID)
}
})
}
Loading