Skip to content

Commit

Permalink
Add support for Load Balancer DNS PTRs (#182)
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Kämmerling <lukas.kaemmerling@hetzner-cloud.de>
  • Loading branch information
LKaemmerling committed Aug 17, 2021
1 parent ebf9f25 commit 0656bf9
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 8 deletions.
32 changes: 30 additions & 2 deletions hcloud/load_balancer.go
Expand Up @@ -42,12 +42,14 @@ type LoadBalancerPublicNet struct {

// LoadBalancerPublicNetIPv4 represents a Load Balancer's public IPv4 address.
type LoadBalancerPublicNetIPv4 struct {
IP net.IP
IP net.IP
DNSPtr string
}

// LoadBalancerPublicNetIPv6 represents a Load Balancer's public IPv6 address.
type LoadBalancerPublicNetIPv6 struct {
IP net.IP
IP net.IP
DNSPtr string
}

// LoadBalancerPrivateNet represents a Load Balancer's private network.
Expand Down Expand Up @@ -1024,3 +1026,29 @@ func (c *LoadBalancerClient) GetMetrics(
}
return ms, resp, nil
}

// 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
}

respBody := schema.LoadBalancerActionChangeDNSPtrResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
}
69 changes: 69 additions & 0 deletions hcloud/load_balancer_test.go
Expand Up @@ -1249,3 +1249,72 @@ 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)
}
})
}
6 changes: 4 additions & 2 deletions hcloud/schema.go
Expand Up @@ -433,10 +433,12 @@ func LoadBalancerFromSchema(s schema.LoadBalancer) *LoadBalancer {
PublicNet: LoadBalancerPublicNet{
Enabled: s.PublicNet.Enabled,
IPv4: LoadBalancerPublicNetIPv4{
IP: net.ParseIP(s.PublicNet.IPv4.IP),
IP: net.ParseIP(s.PublicNet.IPv4.IP),
DNSPtr: s.PublicNet.IPv4.DNSPtr,
},
IPv6: LoadBalancerPublicNetIPv6{
IP: net.ParseIP(s.PublicNet.IPv6.IP),
IP: net.ParseIP(s.PublicNet.IPv6.IP),
DNSPtr: s.PublicNet.IPv6.DNSPtr,
},
},
Location: LocationFromSchema(s.Location),
Expand Down
19 changes: 17 additions & 2 deletions hcloud/schema/load_balancer.go
Expand Up @@ -27,11 +27,13 @@ type LoadBalancerPublicNet struct {
}

type LoadBalancerPublicNetIPv4 struct {
IP string `json:"ip"`
IP string `json:"ip"`
DNSPtr string `json:"dns_ptr"`
}

type LoadBalancerPublicNetIPv6 struct {
IP string `json:"ip"`
IP string `json:"ip"`
DNSPtr string `json:"dns_ptr"`
}

type LoadBalancerPrivateNet struct {
Expand Down Expand Up @@ -401,3 +403,16 @@ type LoadBalancerGetMetricsResponse struct {
type LoadBalancerTimeSeriesVals struct {
Values []interface{} `json:"values"`
}

// LoadBalancerActionChangeDNSPtrRequest defines the schema for the request to
// change a Load Balancer reverse DNS pointer.
type LoadBalancerActionChangeDNSPtrRequest struct {
IP string `json:"ip"`
DNSPtr *string `json:"dns_ptr"`
}

// LoadBalancerActionChangeDNSPtrResponse defines the schema of the response when
// creating a change_dns_ptr Floating IP action.
type LoadBalancerActionChangeDNSPtrResponse struct {
Action Action `json:"action"`
}
12 changes: 10 additions & 2 deletions hcloud/schema_test.go
Expand Up @@ -1292,10 +1292,12 @@ func TestLoadBalancerFromSchema(t *testing.T) {
"name": "Web Frontend",
"public_net": {
"ipv4": {
"ip": "131.232.99.1"
"ip": "131.232.99.1",
"dns_ptr": "example.org"
},
"ipv6": {
"ip": "2001:db8::1"
"ip": "2001:db8::1",
"dns_ptr": "example.com"
}
},
"private_net": [
Expand Down Expand Up @@ -1424,9 +1426,15 @@ func TestLoadBalancerFromSchema(t *testing.T) {
if loadBalancer.PublicNet.IPv4.IP.String() != "131.232.99.1" {
t.Errorf("unexpected IPv4: %v", loadBalancer.PublicNet.IPv4.IP)
}
if loadBalancer.PublicNet.IPv4.DNSPtr != "example.org" {
t.Errorf("unexpected IPv4.DNSPtr: %v", loadBalancer.PublicNet.IPv4.DNSPtr)
}
if loadBalancer.PublicNet.IPv6.IP.String() != "2001:db8::1" {
t.Errorf("unexpected IPv6: %v", loadBalancer.PublicNet.IPv6)
}
if loadBalancer.PublicNet.IPv6.DNSPtr != "example.com" {
t.Errorf("unexpected IPv6.DNSPtr: %v", loadBalancer.PublicNet.IPv6.DNSPtr)
}
if len(loadBalancer.PrivateNet) != 1 {
t.Errorf("unexpected length of PrivateNet: %v", len(loadBalancer.PrivateNet))
} else {
Expand Down

0 comments on commit 0656bf9

Please sign in to comment.