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

Release #644

Merged
merged 13 commits into from
Sep 10, 2019
23 changes: 22 additions & 1 deletion api/backend/ecs/load_balancer_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (e *ECSLoadBalancerManager) DeleteLoadBalancer(loadBalancerID string) error
return err
}

// wait a couple minutes for the elb and it's network interfaces to delete
// wait a couple minutes for the elb and its network interfaces to delete
// todo: using waiters seems pretty verbose - should find a way to clean this up
if securityGroup != nil {
check := func() (bool, error) {
Expand Down Expand Up @@ -216,6 +216,7 @@ func (e *ECSLoadBalancerManager) populateModel(description *elb.LoadBalancerDesc
URL: stringOrEmpty(description.DNSName),
HealthCheck: healthCheck,
IdleTimeout: int(aws.Int64Value(lbAttributes.ConnectionSettings.IdleTimeout)),
CrossZone: bool(aws.BoolValue(lbAttributes.CrossZoneLoadBalancing.Enabled)),
}

return model
Expand Down Expand Up @@ -257,6 +258,7 @@ func (e *ECSLoadBalancerManager) CreateLoadBalancer(
ports []models.Port,
healthCheck models.HealthCheck,
idleTimeout int,
crossZone bool,
) (*models.LoadBalancer, error) {
// we generate a hashed id for load balancers since aws does not enforce unique load balancer names
loadBalancerID := id.GenerateHashedEntityID(loadBalancerName)
Expand All @@ -277,6 +279,11 @@ func (e *ECSLoadBalancerManager) CreateLoadBalancer(
return nil, err
}

// Then set the load balancer's cross-zone load balancing
if err := e.setCrossZone(ecsLoadBalancerID, crossZone); err != nil {
return nil, err
}

model := &models.LoadBalancer{
LoadBalancerID: ecsLoadBalancerID.L0LoadBalancerID(),
LoadBalancerName: loadBalancerName,
Expand All @@ -285,6 +292,7 @@ func (e *ECSLoadBalancerManager) CreateLoadBalancer(
Ports: ports,
HealthCheck: healthCheck,
IdleTimeout: idleTimeout,
CrossZone: crossZone,
}

return model, nil
Expand Down Expand Up @@ -375,6 +383,15 @@ func (e *ECSLoadBalancerManager) UpdateLoadBalancerIdleTimeout(loadBalancerID st
return e.GetLoadBalancer(loadBalancerID)
}

func (e *ECSLoadBalancerManager) UpdateLoadBalancerCrossZone(loadBalancerID string, crossZone bool) (*models.LoadBalancer, error) {
ecsLoadBalancerID := id.L0LoadBalancerID(loadBalancerID).ECSLoadBalancerID()
if err := e.setCrossZone(ecsLoadBalancerID, crossZone); err != nil {
return nil, err
}

return e.GetLoadBalancer(loadBalancerID)
}

func (e *ECSLoadBalancerManager) updateHealthCheck(ecsLoadBalancerID id.ECSLoadBalancerID, healthCheck models.HealthCheck) error {
elbHealthCheck := elb.NewHealthCheck(
healthCheck.Target,
Expand All @@ -391,6 +408,10 @@ func (e *ECSLoadBalancerManager) setIdleTimeout(ecsLoadBalancerID id.ECSLoadBala
return e.ELB.SetIdleTimeout(ecsLoadBalancerID.String(), idleTimeout)
}

func (e *ECSLoadBalancerManager) setCrossZone(ecsLoadBalancerID id.ECSLoadBalancerID, crossZone bool) error {
return e.ELB.SetCrossZone(ecsLoadBalancerID.String(), crossZone)
}

func (e *ECSLoadBalancerManager) UpdateLoadBalancerPorts(loadBalancerID string, ports []models.Port) (*models.LoadBalancer, error) {
model, err := e.GetLoadBalancer(loadBalancerID)
if err != nil {
Expand Down
82 changes: 77 additions & 5 deletions api/backend/ecs/load_balancer_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,15 @@ func TestCreateLoadBalancer(t *testing.T) {
SetIdleTimeout(loadBalancerID.String(), 60).
Return(nil)

mockLB.ELB.EXPECT().
SetCrossZone(loadBalancerID.String(), true).
Return(nil)

return mockLB.LoadBalancer()
},
Run: func(reporter *testutils.Reporter, target interface{}) {
manager := target.(*ECSLoadBalancerManager)
manager.CreateLoadBalancer("lb_name", "envid", true, nil, models.HealthCheck{}, 60)
manager.CreateLoadBalancer("lb_name", "envid", true, nil, models.HealthCheck{}, 60, true)
},
},
{
Expand Down Expand Up @@ -286,11 +290,15 @@ func TestCreateLoadBalancer(t *testing.T) {
SetIdleTimeout(gomock.Any(), gomock.Any()).
Return(nil)

mockLB.ELB.EXPECT().
SetCrossZone(gomock.Any(), gomock.Any()).
Return(nil)

return mockLB.LoadBalancer()
},
Run: func(reporter *testutils.Reporter, target interface{}) {
manager := target.(*ECSLoadBalancerManager)
manager.CreateLoadBalancer("lb_name", "envid", false, nil, models.HealthCheck{}, 60)
manager.CreateLoadBalancer("lb_name", "envid", false, nil, models.HealthCheck{}, 60, true)
},
},
{
Expand Down Expand Up @@ -332,11 +340,14 @@ func TestCreateLoadBalancer(t *testing.T) {
mockLB.ELB.EXPECT().
SetIdleTimeout(gomock.Any(), gomock.Any())

mockLB.ELB.EXPECT().
SetCrossZone(gomock.Any(), gomock.Any())

return mockLB.LoadBalancer()
},
Run: func(reporter *testutils.Reporter, target interface{}) {
manager := target.(*ECSLoadBalancerManager)
manager.CreateLoadBalancer("lb_name", "envid", true, nil, models.HealthCheck{}, 60)
manager.CreateLoadBalancer("lb_name", "envid", true, nil, models.HealthCheck{}, 60, true)
},
},
{
Expand Down Expand Up @@ -377,6 +388,9 @@ func TestCreateLoadBalancer(t *testing.T) {
mockLB.ELB.EXPECT().
SetIdleTimeout(gomock.Any(), gomock.Any())

mockLB.ELB.EXPECT().
SetCrossZone(gomock.Any(), gomock.Any())

return mockLB.LoadBalancer()
},
Run: func(reporter *testutils.Reporter, target interface{}) {
Expand All @@ -385,7 +399,7 @@ func TestCreateLoadBalancer(t *testing.T) {
loadBalancerID := id.L0LoadBalancerID("lbid")
environmentID := id.L0EnvironmentID("envid")

model, err := manager.CreateLoadBalancer("lb_name", environmentID.String(), true, nil, models.HealthCheck{}, 60)
model, err := manager.CreateLoadBalancer("lb_name", environmentID.String(), true, nil, models.HealthCheck{}, 60, true)
if err != nil {
reporter.Fatal(err)
}
Expand Down Expand Up @@ -447,12 +461,70 @@ func TestCreateLoadBalancer(t *testing.T) {
g.Set(i+1, fmt.Errorf("some error"))

manager := setup(g).(*ECSLoadBalancerManager)
if _, err := manager.CreateLoadBalancer("", "", true, nil, models.HealthCheck{}, 60); err == nil {
if _, err := manager.CreateLoadBalancer("", "", true, nil, models.HealthCheck{}, 60, true); err == nil {
reporter.Errorf("Error on variation %d, Error was nil!", i)
}
}
},
},
{
Name: "Should set cross-zone accordingly",
Setup: func(reporter *testutils.Reporter, ctrl *gomock.Controller) interface{} {
mockLB := NewMockECSLoadBalancerManager(ctrl)

mockLB.IAM.EXPECT().
CreateRole(gomock.Any(), gomock.Any())

mockLB.IAM.EXPECT().
PutRolePolicy(gomock.Any(), gomock.Any())

mockLB.IAM.EXPECT().
GetAccountId().
Return("100", nil)

mockLB.EC2.EXPECT().
DescribeSecurityGroup(gomock.Any()).
Return(sgList, nil).
AnyTimes()

// getSubnetsAndAvailZones
mockLB.EC2.EXPECT().
DescribeSubnet(gomock.Any()).
Return(makeSubnet("a"), nil)

mockLB.EC2.EXPECT().
DescribeSubnet(gomock.Any()).
Return(makeSubnet("b"), nil)

mockLB.ELB.EXPECT().
CreateLoadBalancer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())

mockLB.ELB.EXPECT().
ConfigureHealthCheck(gomock.Any(), gomock.Any())

mockLB.ELB.EXPECT().
SetIdleTimeout(gomock.Any(), gomock.Any())

mockLB.ELB.EXPECT().
SetCrossZone(gomock.Any(), false)

return mockLB.LoadBalancer()
},
Run: func(reporter *testutils.Reporter, target interface{}) {
manager := target.(*ECSLoadBalancerManager)

loadBalancerID := id.L0LoadBalancerID("lbid")
environmentID := id.L0EnvironmentID("envid")

model, err := manager.CreateLoadBalancer("lb_name", environmentID.String(), true, nil, models.HealthCheck{}, 60, false)
if err != nil {
reporter.Fatal(err)
}

reporter.AssertEqual(model.LoadBalancerID, loadBalancerID.String())
reporter.AssertEqual(model.EnvironmentID, environmentID.String())
},
},
}

testutils.RunTests(t, testCases)
Expand Down
3 changes: 2 additions & 1 deletion api/backend/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ type Backend interface {
ListLoadBalancers() ([]*models.LoadBalancer, error)
GetLoadBalancer(id string) (*models.LoadBalancer, error)
DeleteLoadBalancer(id string) error
CreateLoadBalancer(loadBalancerName, environmentID string, isPublic bool, ports []models.Port, healthCheck models.HealthCheck, idleTimeout int) (*models.LoadBalancer, error)
CreateLoadBalancer(loadBalancerName, environmentID string, isPublic bool, ports []models.Port, healthCheck models.HealthCheck, idleTimeout int, crossZone bool) (*models.LoadBalancer, error)
UpdateLoadBalancerPorts(loadBalancerID string, ports []models.Port) (*models.LoadBalancer, error)
UpdateLoadBalancerHealthCheck(loadBalancerID string, healthCheck models.HealthCheck) (*models.LoadBalancer, error)
UpdateLoadBalancerIdleTimeout(loadBalancerID string, idleTimeout int) (*models.LoadBalancer, error)
UpdateLoadBalancerCrossZone(loadBalancerID string, crossZone bool) (*models.LoadBalancer, error)
}
21 changes: 17 additions & 4 deletions api/backend/mock_backend/mock_backend.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions api/handlers/load_balancer_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ func (l *LoadBalancerHandler) Routes() *restful.WebService {
Doc("Update load balancer idle timeout").
Writes(models.LoadBalancer{}))

service.Route(service.PUT("{id}/crosszone").
Filter(basicAuthenticate).
To(l.UpdateLoadBalancerCrossZone).
Reads(models.UpdateLoadBalancerCrossZoneRequest{}).
Param(id).
Doc("Update load balancer cross-zone load balancing").
Writes(models.LoadBalancer{}))

return service
}

Expand Down Expand Up @@ -215,3 +223,26 @@ func (l *LoadBalancerHandler) UpdateLoadBalancerIdleTimeout(request *restful.Req

response.WriteAsJson(loadBalancer)
}

func (l *LoadBalancerHandler) UpdateLoadBalancerCrossZone(request *restful.Request, response *restful.Response) {
id := request.PathParameter("id")
if id == "" {
err := fmt.Errorf("Parameter 'id' is required")
BadRequest(response, errors.MissingParameter, err)
return
}

var req models.UpdateLoadBalancerCrossZoneRequest
if err := request.ReadEntity(&req); err != nil {
BadRequest(response, errors.InvalidJSON, err)
return
}

loadBalancer, err := l.LoadBalancerLogic.UpdateLoadBalancerCrossZone(id, req.CrossZone)
if err != nil {
ReturnError(response, err)
return
}

response.WriteAsJson(loadBalancer)
}
29 changes: 29 additions & 0 deletions api/handlers/load_balancer_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,32 @@ func TestUpdateLoadBalancerIdleTimeout(t *testing.T) {

RunHandlerTestCases(t, testCases)
}

func TestUpdateLoadBalancerCrossZone(t *testing.T) {
request := models.UpdateLoadBalancerCrossZoneRequest{CrossZone: true}

testCases := []HandlerTestCase{
{
Name: "Should call UpdateLoadBalancerCrossZone with correct params",
Request: &TestRequest{
Parameters: map[string]string{"id": "some_id"},
Body: request,
},
Setup: func(ctrl *gomock.Controller) interface{} {
mockLogic := mock_logic.NewMockLoadBalancerLogic(ctrl)
mockJob := mock_logic.NewMockJobLogic(ctrl)

mockLogic.EXPECT().
UpdateLoadBalancerCrossZone("some_id", request.CrossZone)

return NewLoadBalancerHandler(mockLogic, mockJob)
},
Run: func(reporter *testutils.Reporter, target interface{}, req *restful.Request, resp *restful.Response, read Readf) {
handler := target.(*LoadBalancerHandler)
handler.UpdateLoadBalancerCrossZone(req, resp)
},
},
}

RunHandlerTestCases(t, testCases)
}
15 changes: 15 additions & 0 deletions api/logic/load_balancer_logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type LoadBalancerLogic interface {
UpdateLoadBalancerPorts(loadBalancerID string, ports []models.Port) (*models.LoadBalancer, error)
UpdateLoadBalancerHealthCheck(loadBalancerID string, healthCheck models.HealthCheck) (*models.LoadBalancer, error)
UpdateLoadBalancerIdleTimeout(loadBalancerID string, idleTimeout int) (*models.LoadBalancer, error)
UpdateLoadBalancerCrossZone(loadBalancerID string, crossZone bool) (*models.LoadBalancer, error)
}

type L0LoadBalancerLogic struct {
Expand Down Expand Up @@ -101,6 +102,7 @@ func (l *L0LoadBalancerLogic) CreateLoadBalancer(req models.CreateLoadBalancerRe
req.Ports,
req.HealthCheck,
req.IdleTimeout,
req.CrossZone,
)

if err != nil {
Expand Down Expand Up @@ -163,6 +165,19 @@ func (l *L0LoadBalancerLogic) UpdateLoadBalancerIdleTimeout(loadBalancerID strin
return loadBalancer, nil
}

func (l *L0LoadBalancerLogic) UpdateLoadBalancerCrossZone(loadBalancerID string, crossZone bool) (*models.LoadBalancer, error) {
loadBalancer, err := l.Backend.UpdateLoadBalancerCrossZone(loadBalancerID, crossZone)
if err != nil {
return nil, err
}

if err := l.populateModel(loadBalancer); err != nil {
return nil, err
}

return loadBalancer, nil
}

func (l *L0LoadBalancerLogic) doesLoadBalancerTagExist(environmentID, name string) (bool, error) {
tags, err := l.TagStore.SelectByType("load_balancer")
if err != nil {
Expand Down
Loading