From 6dfa314396d26c56d15ec5acb567f738d7ad1a7e Mon Sep 17 00:00:00 2001 From: Luke Granger-Brown Date: Sat, 22 Jul 2017 14:37:42 +0100 Subject: [PATCH] Add AsRequest method on LoadBalancer Updating a LoadBalancer requires a LoadBalancerRequest, which is not easily creatable from a LoadBalancer. Serializing the LoadBalancer to JSON and then deserializing it to a LoadBalancerRequest mostly works, except that Region is a string on the Request but not on the LB (since the LB returns the entire Region object), so json.Unmarshal returns an error. An explicit AsRequest method makes it easier for consumers of the API to modify existing LoadBalancer objects, and is designed to avoid modifying attributes on the original LoadBalancer when the LoadBalancerRequest is modified. --- load_balancers.go | 26 +++++++++ load_balancers_test.go | 125 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 141 insertions(+), 10 deletions(-) diff --git a/load_balancers.go b/load_balancers.go index b4b5300..81c0bdf 100644 --- a/load_balancers.go +++ b/load_balancers.go @@ -48,6 +48,32 @@ func (l LoadBalancer) String() string { return Stringify(l) } +// AsRequest creates a LoadBalancerRequest that can be submitted to Update with the current values of the LoadBalancer. +// Modifying the returned LoadBalancerRequest will not modify the original LoadBalancer. +func (l LoadBalancer) AsRequest() *LoadBalancerRequest { + r := LoadBalancerRequest{ + Name: l.Name, + Algorithm: l.Algorithm, + ForwardingRules: append([]ForwardingRule(nil), l.ForwardingRules...), + DropletIDs: append([]int(nil), l.DropletIDs...), + Tag: l.Tag, + RedirectHttpToHttps: l.RedirectHttpToHttps, + HealthCheck: l.HealthCheck, + } + if l.HealthCheck != nil { + r.HealthCheck = &HealthCheck{} + *r.HealthCheck = *l.HealthCheck + } + if l.StickySessions != nil { + r.StickySessions = &StickySessions{} + *r.StickySessions = *l.StickySessions + } + if l.Region != nil { + r.Region = l.Region.Slug + } + return &r +} + // ForwardingRule represents load balancer forwarding rules. type ForwardingRule struct { EntryProtocol string `json:"entry_protocol,omitempty"` diff --git a/load_balancers_test.go b/load_balancers_test.go index 2419317..52e673a 100644 --- a/load_balancers_test.go +++ b/load_balancers_test.go @@ -272,7 +272,7 @@ var lbUpdateJSONResponse = ` } ` -func TestLoadBlanacers_Get(t *testing.T) { +func TestLoadBalancers_Get(t *testing.T) { setup() defer teardown() @@ -333,7 +333,7 @@ func TestLoadBlanacers_Get(t *testing.T) { assert.Equal(t, expected, loadBalancer) } -func TestLoadBlanacers_Create(t *testing.T) { +func TestLoadBalancers_Create(t *testing.T) { setup() defer teardown() @@ -440,7 +440,7 @@ func TestLoadBlanacers_Create(t *testing.T) { assert.Equal(t, expected, loadBalancer) } -func TestLoadBlanacers_Update(t *testing.T) { +func TestLoadBalancers_Update(t *testing.T) { setup() defer teardown() @@ -547,7 +547,7 @@ func TestLoadBlanacers_Update(t *testing.T) { assert.Equal(t, expected, loadBalancer) } -func TestLoadBlanacers_List(t *testing.T) { +func TestLoadBalancers_List(t *testing.T) { setup() defer teardown() @@ -608,7 +608,7 @@ func TestLoadBlanacers_List(t *testing.T) { assert.Equal(t, expected, loadBalancers) } -func TestLoadBlanacers_List_Pagination(t *testing.T) { +func TestLoadBalancers_List_Pagination(t *testing.T) { setup() defer teardown() @@ -630,7 +630,7 @@ func TestLoadBlanacers_List_Pagination(t *testing.T) { assert.Equal(t, "http://localhost:3001/v2/load_balancers?page=3&per_page=1", resp.Links.Pages.Last) } -func TestLoadBlanacers_Delete(t *testing.T) { +func TestLoadBalancers_Delete(t *testing.T) { setup() defer teardown() @@ -648,7 +648,7 @@ func TestLoadBlanacers_Delete(t *testing.T) { } } -func TestLoadBlanacers_AddDroplets(t *testing.T) { +func TestLoadBalancers_AddDroplets(t *testing.T) { setup() defer teardown() @@ -678,7 +678,7 @@ func TestLoadBlanacers_AddDroplets(t *testing.T) { } } -func TestLoadBlanacers_RemoveDroplets(t *testing.T) { +func TestLoadBalancers_RemoveDroplets(t *testing.T) { setup() defer teardown() @@ -708,7 +708,7 @@ func TestLoadBlanacers_RemoveDroplets(t *testing.T) { } } -func TestLoadBlanacers_AddForwardingRules(t *testing.T) { +func TestLoadBalancers_AddForwardingRules(t *testing.T) { setup() defer teardown() @@ -752,7 +752,7 @@ func TestLoadBlanacers_AddForwardingRules(t *testing.T) { } } -func TestLoadBlanacers_RemoveForwardingRules(t *testing.T) { +func TestLoadBalancers_RemoveForwardingRules(t *testing.T) { setup() defer teardown() @@ -794,3 +794,108 @@ func TestLoadBlanacers_RemoveForwardingRules(t *testing.T) { t.Errorf("LoadBalancers.RemoveForwardingRules returned error: %v", err) } } + +func TestLoadBalancers_AsRequest(t *testing.T) { + lb := &LoadBalancer{ + ID: "37e6be88-01ec-4ec7-9bc6-a514d4719057", + Name: "test-loadbalancer", + IP: "10.0.0.1", + Algorithm: "least_connections", + Status: "active", + Created: "2011-06-24T12:00:00Z", + HealthCheck: &HealthCheck{ + Protocol: "http", + Port: 80, + Path: "/ping", + CheckIntervalSeconds: 30, + ResponseTimeoutSeconds: 10, + HealthyThreshold: 3, + UnhealthyThreshold: 3, + }, + StickySessions: &StickySessions{ + Type: "cookies", + CookieName: "nomnom", + CookieTtlSeconds: 32, + }, + Region: &Region{ + Slug: "lon1", + }, + RedirectHttpToHttps: true, + } + lb.DropletIDs = make([]int, 1, 2) + lb.DropletIDs[0] = 12345 + lb.ForwardingRules = make([]ForwardingRule, 1, 2) + lb.ForwardingRules[0] = ForwardingRule{ + EntryProtocol: "http", + EntryPort: 80, + TargetProtocol: "http", + TargetPort: 80, + } + + want := &LoadBalancerRequest{ + Name: "test-loadbalancer", + Algorithm: "least_connections", + Region: "lon1", + ForwardingRules: []ForwardingRule{ForwardingRule{ + EntryProtocol: "http", + EntryPort: 80, + TargetProtocol: "http", + TargetPort: 80, + }}, + HealthCheck: &HealthCheck{ + Protocol: "http", + Port: 80, + Path: "/ping", + CheckIntervalSeconds: 30, + ResponseTimeoutSeconds: 10, + HealthyThreshold: 3, + UnhealthyThreshold: 3, + }, + StickySessions: &StickySessions{ + Type: "cookies", + CookieName: "nomnom", + CookieTtlSeconds: 32, + }, + DropletIDs: []int{12345}, + RedirectHttpToHttps: true, + } + + r := lb.AsRequest() + assert.Equal(t, want, r) + assert.False(t, r.HealthCheck == lb.HealthCheck, "HealthCheck points to same struct") + assert.False(t, r.StickySessions == lb.StickySessions, "StickySessions points to same struct") + + r.DropletIDs = append(r.DropletIDs, 54321) + r.ForwardingRules = append(r.ForwardingRules, ForwardingRule{ + EntryProtocol: "https", + EntryPort: 443, + TargetProtocol: "https", + TargetPort: 443, + TlsPassthrough: true, + }) + + // Check that original LoadBalancer hasn't changed + lb.DropletIDs = append(lb.DropletIDs, 13579) + lb.ForwardingRules = append(lb.ForwardingRules, ForwardingRule{ + EntryProtocol: "tcp", + EntryPort: 587, + TargetProtocol: "tcp", + TargetPort: 587, + }) + assert.Equal(t, []int{12345, 54321}, r.DropletIDs) + assert.Equal(t, []ForwardingRule{ + ForwardingRule{ + EntryProtocol: "http", + EntryPort: 80, + TargetProtocol: "http", + TargetPort: 80, + }, + ForwardingRule{ + EntryProtocol: "https", + EntryPort: 443, + TargetProtocol: "https", + TargetPort: 443, + TlsPassthrough: true, + }, + }, r.ForwardingRules) +}