/* * Copyright 2008-2010 Xebia and the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package fr.xebia.workshop.infrastructureascode; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import org.apache.commons.lang.ObjectUtils; import org.junit.Test; import org.mockito.ArgumentMatcher; import com.amazonaws.services.ec2.AmazonEC2; import com.amazonaws.services.ec2.model.DescribeInstancesRequest; import com.amazonaws.services.ec2.model.DescribeInstancesResult; import com.amazonaws.services.ec2.model.Instance; import com.amazonaws.services.ec2.model.Placement; import com.amazonaws.services.ec2.model.Reservation; import com.amazonaws.services.elasticloadbalancing.AmazonElasticLoadBalancing; import com.amazonaws.services.elasticloadbalancing.model.AppCookieStickinessPolicy; import com.amazonaws.services.elasticloadbalancing.model.ConfigureHealthCheckRequest; import com.amazonaws.services.elasticloadbalancing.model.CreateLBCookieStickinessPolicyRequest; import com.amazonaws.services.elasticloadbalancing.model.CreateLoadBalancerRequest; import com.amazonaws.services.elasticloadbalancing.model.DeleteLoadBalancerPolicyRequest; import com.amazonaws.services.elasticloadbalancing.model.DeregisterInstancesFromLoadBalancerRequest; import com.amazonaws.services.elasticloadbalancing.model.DescribeLoadBalancersRequest; import com.amazonaws.services.elasticloadbalancing.model.DescribeLoadBalancersResult; import com.amazonaws.services.elasticloadbalancing.model.DisableAvailabilityZonesForLoadBalancerRequest; import com.amazonaws.services.elasticloadbalancing.model.EnableAvailabilityZonesForLoadBalancerRequest; import com.amazonaws.services.elasticloadbalancing.model.HealthCheck; import com.amazonaws.services.elasticloadbalancing.model.LBCookieStickinessPolicy; import com.amazonaws.services.elasticloadbalancing.model.Listener; import com.amazonaws.services.elasticloadbalancing.model.ListenerDescription; import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerDescription; import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerNotFoundException; import com.amazonaws.services.elasticloadbalancing.model.Policies; import com.amazonaws.services.elasticloadbalancing.model.RegisterInstancesWithLoadBalancerRequest; import com.amazonaws.services.elasticloadbalancing.model.SetLoadBalancerPoliciesOfListenerRequest; import com.amazonaws.services.rds.AmazonRDS; import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import fr.xebia.workshop.infrastructureascode.AmazonAwsPetclinicInfrastructureEnforcer; public class AmazonAwsPetclinicInfrastructureEnforcerTest { AmazonEC2 ec2 = mock(AmazonEC2.class); AmazonElasticLoadBalancing elb = mock(AmazonElasticLoadBalancing.class); AmazonRDS rds = mock(AmazonRDS.class); @Test public void add_one_instance_and_one_availability_zone() { DescribeInstancesResult describeInstanceResult = buildDescribeInstancesResult(); when(ec2.describeInstances((DescribeInstancesRequest) any())).thenReturn(describeInstanceResult); LoadBalancerDescription lbDescription = buildExpectedLoadBalancerDescription(); lbDescription.setAvailabilityZones(Lists.newArrayList("eu-west-1b")); lbDescription.setInstances(Lists.newArrayList(new com.amazonaws.services.elasticloadbalancing.model.Instance("i-1"))); when(elb.describeLoadBalancers((DescribeLoadBalancersRequest) any())) // .thenReturn(new DescribeLoadBalancersResult().withLoadBalancerDescriptions(lbDescription)); AmazonAwsPetclinicInfrastructureEnforcer infraEnforcer = new AmazonAwsPetclinicInfrastructureEnforcer(ec2, elb, rds); infraEnforcer.createOrUpdateElasticLoadBalancer("/myapp/healsthcheck.jsp", "myapp"); verify(elb, atLeastOnce()).describeLoadBalancers((DescribeLoadBalancersRequest) any()); verify(elb, times(1)).enableAvailabilityZonesForLoadBalancer( argThat(new ArgumentMatcher<EnableAvailabilityZonesForLoadBalancerRequest>() { @Override public boolean matches(Object argument) { EnableAvailabilityZonesForLoadBalancerRequest req = (EnableAvailabilityZonesForLoadBalancerRequest) argument; if (!ObjectUtils.equals("eu-west-1c", Iterables.getOnlyElement(req.getAvailabilityZones()))) { return false; } return true; } })); verify(elb, times(1)).registerInstancesWithLoadBalancer(argThat(new ArgumentMatcher<RegisterInstancesWithLoadBalancerRequest>() { @Override public boolean matches(Object argument) { RegisterInstancesWithLoadBalancerRequest req = (RegisterInstancesWithLoadBalancerRequest) argument; if (!ObjectUtils.equals("i-2", Iterables.getOnlyElement(req.getInstances()).getInstanceId())) { return false; } return true; } })); verifyNoMoreInteractions(elb); } @Test public void add_stickiness_policy() { DescribeInstancesResult describeInstanceResult = buildDescribeInstancesResult(); when(ec2.describeInstances((DescribeInstancesRequest) any())).thenReturn(describeInstanceResult); LoadBalancerDescription lbDescription = buildExpectedLoadBalancerDescription(); lbDescription.setPolicies(new Policies()); lbDescription.getListenerDescriptions().get(0).getPolicyNames().clear(); when(elb.describeLoadBalancers((DescribeLoadBalancersRequest) any())) // .thenReturn(new DescribeLoadBalancersResult().withLoadBalancerDescriptions(lbDescription)); AmazonAwsPetclinicInfrastructureEnforcer infraEnforcer = new AmazonAwsPetclinicInfrastructureEnforcer(ec2, elb, rds); infraEnforcer.createOrUpdateElasticLoadBalancer("/myapp/healsthcheck.jsp", "myapp"); verify(elb, atLeastOnce()).describeLoadBalancers((DescribeLoadBalancersRequest) any()); verify(elb, times(1)).createLBCookieStickinessPolicy(argThat(buildCreateLBCookieStickinessPolicyRequestMatcher())); verify(elb, times(1)).setLoadBalancerPoliciesOfListener(argThat(buildSetLoadBalancerPoliciesOfListenerRequestMatcher())); verifyNoMoreInteractions(elb); } ArgumentMatcher<ConfigureHealthCheckRequest> buildConfigureHealthCheckRequestMatcher() { ArgumentMatcher<ConfigureHealthCheckRequest> configureHealthCheckMatcher = new ArgumentMatcher<ConfigureHealthCheckRequest>() { @Override public boolean matches(Object argument) { ConfigureHealthCheckRequest req = (ConfigureHealthCheckRequest) argument; HealthCheck expectedHealthCheck = new HealthCheck() // .withTarget("HTTP:8080/myapp/healsthcheck.jsp") // .withHealthyThreshold(2) // .withUnhealthyThreshold(2) // .withInterval(30) // .withTimeout(2); HealthCheck actualHealthCheck = req.getHealthCheck(); if ( // !ObjectUtils.equals(expectedHealthCheck.getHealthyThreshold(), actualHealthCheck.getHealthyThreshold()) || // !ObjectUtils.equals(expectedHealthCheck.getInterval(), actualHealthCheck.getInterval()) || // !ObjectUtils.equals(expectedHealthCheck.getTarget(), actualHealthCheck.getTarget()) || // !ObjectUtils.equals(expectedHealthCheck.getTimeout(), actualHealthCheck.getTimeout()) || // !ObjectUtils.equals(expectedHealthCheck.getUnhealthyThreshold(), actualHealthCheck.getUnhealthyThreshold())) { return false; } return true; } }; return configureHealthCheckMatcher; } ArgumentMatcher<CreateLBCookieStickinessPolicyRequest> buildCreateLBCookieStickinessPolicyRequestMatcher() { return new ArgumentMatcher<CreateLBCookieStickinessPolicyRequest>() { @Override public boolean matches(Object argument) { CreateLBCookieStickinessPolicyRequest req = (CreateLBCookieStickinessPolicyRequest) argument; LBCookieStickinessPolicy expectedCookieStickinessPolicy = new LBCookieStickinessPolicy("myapp-stickiness-policy", null); if (!ObjectUtils.equals("myapp", req.getLoadBalancerName())) { return false; } if (!ObjectUtils.equals(expectedCookieStickinessPolicy.getPolicyName(), req.getPolicyName())) { return false; } if (!ObjectUtils.equals(expectedCookieStickinessPolicy.getCookieExpirationPeriod(), req.getCookieExpirationPeriod())) { return false; } return true; } }; } ArgumentMatcher<CreateLoadBalancerRequest> buildCreateLoadBalancerRequestMatcher() { ArgumentMatcher<CreateLoadBalancerRequest> createLbMatcher = new ArgumentMatcher<CreateLoadBalancerRequest>() { @Override public boolean matches(Object argument) { CreateLoadBalancerRequest req = (CreateLoadBalancerRequest) argument; ArrayList<String> expectedAvailabilityZones = Lists.newArrayList("eu-west-1b", "eu-west-1c"); if (!ObjectUtils.equals(expectedAvailabilityZones, req.getAvailabilityZones())) { return false; } Listener actualListener = Iterables.getOnlyElement(req.getListeners()); Listener expectedListener = new Listener("HTTP", 80, 8080); if ( // !ObjectUtils.equals(expectedListener.getProtocol(), actualListener.getProtocol()) || // !ObjectUtils.equals(expectedListener.getLoadBalancerPort(), actualListener.getLoadBalancerPort()) || // !ObjectUtils.equals(expectedListener.getInstancePort(), actualListener.getInstancePort())) { return false; } return true; } }; return createLbMatcher; } DescribeInstancesResult buildDescribeInstancesResult() { DescribeInstancesResult describeInstanceResult = new DescribeInstancesResult().withReservations(new Reservation().withInstances( // new Instance().withInstanceId("i-1").withPlacement(new Placement("eu-west-1b")), // new Instance().withInstanceId("i-2").withPlacement(new Placement("eu-west-1c")))); return describeInstanceResult; } LoadBalancerDescription buildExpectedLoadBalancerDescription() { LoadBalancerDescription lbDescription = new LoadBalancerDescription().withAvailabilityZones("eu-west-1b") .withHealthCheck(new HealthCheck("HTTP:8080/myapp/healsthcheck.jsp", 30, 2, 2, 2)) // .withInstances( // new com.amazonaws.services.elasticloadbalancing.model.Instance("i-1"), // new com.amazonaws.services.elasticloadbalancing.model.Instance("i-2")) // .withAvailabilityZones("eu-west-1b", "eu-west-1c") // .withListenerDescriptions( // new ListenerDescription() // .withListener(new Listener("HTTP", 80, 8080)) // .withPolicyNames("myapp-stickiness-policy")) // .withPolicies( // new Policies() // .withLBCookieStickinessPolicies(new LBCookieStickinessPolicy("myapp-stickiness-policy", null))// )// .withLoadBalancerName("myapp"); return lbDescription; } ArgumentMatcher<SetLoadBalancerPoliciesOfListenerRequest> buildSetLoadBalancerPoliciesOfListenerRequestMatcher() { return new ArgumentMatcher<SetLoadBalancerPoliciesOfListenerRequest>() { @Override public boolean matches(Object argument) { SetLoadBalancerPoliciesOfListenerRequest req = (SetLoadBalancerPoliciesOfListenerRequest) argument; if (!ObjectUtils.equals(80, req.getLoadBalancerPort())) { return false; } if (!ObjectUtils.equals(Lists.newArrayList("myapp-stickiness-policy"), req.getPolicyNames())) { return false; } if (!ObjectUtils.equals("myapp", req.getLoadBalancerName())) { return false; } return true; } }; } @Test public void deregister_exceeding_instances() { DescribeInstancesResult describeInstanceResult = buildDescribeInstancesResult(); when(ec2.describeInstances((DescribeInstancesRequest) any())).thenReturn(describeInstanceResult); LoadBalancerDescription lbDescription = buildExpectedLoadBalancerDescription(); lbDescription.setInstances(Arrays.asList( // new com.amazonaws.services.elasticloadbalancing.model.Instance("i-1"), // new com.amazonaws.services.elasticloadbalancing.model.Instance("i-2"), // new com.amazonaws.services.elasticloadbalancing.model.Instance("i-3")) // ); when(elb.describeLoadBalancers((DescribeLoadBalancersRequest) any())) // .thenReturn(new DescribeLoadBalancersResult().withLoadBalancerDescriptions(lbDescription)); AmazonAwsPetclinicInfrastructureEnforcer infraEnforcer = new AmazonAwsPetclinicInfrastructureEnforcer(ec2, elb, rds); infraEnforcer.createOrUpdateElasticLoadBalancer("/myapp/healsthcheck.jsp", "myapp"); verify(elb, atLeastOnce()).describeLoadBalancers((DescribeLoadBalancersRequest) any()); verify(elb, times(1)).deregisterInstancesFromLoadBalancer( argThat(new ArgumentMatcher<DeregisterInstancesFromLoadBalancerRequest>() { @Override public boolean matches(Object argument) { DeregisterInstancesFromLoadBalancerRequest req = (DeregisterInstancesFromLoadBalancerRequest) argument; Collection<String> instanceIdsToUnregister = Collections2.transform(req.getInstances(), AmazonAwsPetclinicInfrastructureEnforcer.ELB_INSTANCE_TO_INSTANCE_ID); if (!Arrays.asList("i-3").equals(Lists.newArrayList(instanceIdsToUnregister))) { return false; } return true; } })); verifyNoMoreInteractions(elb); } @Test public void disable_exceeding_availability_zone() { DescribeInstancesResult describeInstanceResult = buildDescribeInstancesResult(); when(ec2.describeInstances((DescribeInstancesRequest) any())).thenReturn(describeInstanceResult); LoadBalancerDescription lbDescription = buildExpectedLoadBalancerDescription(); lbDescription.setAvailabilityZones(Arrays.asList("eu-west-1a", "eu-west-1b", "eu-west-1c")); when(elb.describeLoadBalancers((DescribeLoadBalancersRequest) any())) // .thenReturn(new DescribeLoadBalancersResult().withLoadBalancerDescriptions(lbDescription)); AmazonAwsPetclinicInfrastructureEnforcer infraEnforcer = new AmazonAwsPetclinicInfrastructureEnforcer(ec2, elb, rds); infraEnforcer.createOrUpdateElasticLoadBalancer("/myapp/healsthcheck.jsp", "myapp"); verify(elb, atLeastOnce()).describeLoadBalancers((DescribeLoadBalancersRequest) any()); verify(elb, times(1)).disableAvailabilityZonesForLoadBalancer( argThat(new ArgumentMatcher<DisableAvailabilityZonesForLoadBalancerRequest>() { @Override public boolean matches(Object argument) { DisableAvailabilityZonesForLoadBalancerRequest req = (DisableAvailabilityZonesForLoadBalancerRequest) argument; if (!Arrays.asList("eu-west-1a").equals(req.getAvailabilityZones())) { return false; } return true; } })); verifyNoMoreInteractions(elb); } @Test public void dont_update_anything_to_up_to_date_load_balancer() { DescribeInstancesResult describeInstanceResult = buildDescribeInstancesResult(); when(ec2.describeInstances((DescribeInstancesRequest) any())).thenReturn(describeInstanceResult); LoadBalancerDescription lbDescription = buildExpectedLoadBalancerDescription(); when(elb.describeLoadBalancers((DescribeLoadBalancersRequest) any())) // .thenReturn(new DescribeLoadBalancersResult().withLoadBalancerDescriptions(lbDescription)); AmazonAwsPetclinicInfrastructureEnforcer infraEnforcer = new AmazonAwsPetclinicInfrastructureEnforcer(ec2, elb, rds); infraEnforcer.createOrUpdateElasticLoadBalancer("/myapp/healsthcheck.jsp", "myapp"); verify(elb, atLeastOnce()).describeLoadBalancers((DescribeLoadBalancersRequest) any()); verifyNoMoreInteractions(elb); } @Test public void elb_create_from_scratch() { DescribeInstancesResult describeInstanceResult = buildDescribeInstancesResult(); when(ec2.describeInstances((DescribeInstancesRequest) any())).thenReturn(describeInstanceResult); LoadBalancerDescription lbDescription = buildExpectedLoadBalancerDescription(); when(elb.describeLoadBalancers((DescribeLoadBalancersRequest) any())) // .thenThrow(new LoadBalancerNotFoundException("elb '" + "myapp" + "' not found")) // .thenReturn(new DescribeLoadBalancersResult().withLoadBalancerDescriptions(lbDescription)); ArgumentMatcher<CreateLoadBalancerRequest> createLbMatcher = buildCreateLoadBalancerRequestMatcher(); ArgumentMatcher<ConfigureHealthCheckRequest> configureHealthCheckMatcher = buildConfigureHealthCheckRequestMatcher(); AmazonAwsPetclinicInfrastructureEnforcer infraEnforcer = new AmazonAwsPetclinicInfrastructureEnforcer(ec2, elb, rds); infraEnforcer.createOrUpdateElasticLoadBalancer("/myapp/healsthcheck.jsp", "myapp"); verify(elb, atLeastOnce()).describeLoadBalancers((DescribeLoadBalancersRequest) any()); verify(elb, times(1)).createLoadBalancer(argThat(createLbMatcher)); verify(elb, times(1)).configureHealthCheck(argThat(configureHealthCheckMatcher)); verify(elb, never()).disableAvailabilityZonesForLoadBalancer((DisableAvailabilityZonesForLoadBalancerRequest) any()); verify(elb, never()).enableAvailabilityZonesForLoadBalancer((EnableAvailabilityZonesForLoadBalancerRequest) any()); verify(elb, times(1)).registerInstancesWithLoadBalancer(argThat(new ArgumentMatcher<RegisterInstancesWithLoadBalancerRequest>() { @Override public boolean matches(Object argument) { RegisterInstancesWithLoadBalancerRequest req = (RegisterInstancesWithLoadBalancerRequest) argument; if (!ObjectUtils.equals(2, req.getInstances().size())) { return false; } return true; } })); verify(elb, times(1)).createLBCookieStickinessPolicy(argThat(buildCreateLBCookieStickinessPolicyRequestMatcher())); verify(elb, times(1)).setLoadBalancerPoliciesOfListener(argThat(buildSetLoadBalancerPoliciesOfListenerRequestMatcher())); verifyNoMoreInteractions(elb); } @Test public void overwrite_unexpected_stickiness_policy() { DescribeInstancesResult describeInstanceResult = buildDescribeInstancesResult(); when(ec2.describeInstances((DescribeInstancesRequest) any())).thenReturn(describeInstanceResult); LoadBalancerDescription lbDescription = buildExpectedLoadBalancerDescription(); lbDescription.setPolicies(new Policies()); lbDescription.getPolicies().setAppCookieStickinessPolicies( Arrays.asList(new AppCookieStickinessPolicy("myapp-jsessionid-policy", "JSESSIONID"))); lbDescription.getListenerDescriptions().get(0).getPolicyNames().clear(); lbDescription.getListenerDescriptions().get(0).getPolicyNames().add("myapp-jsessionid-policy"); when(elb.describeLoadBalancers((DescribeLoadBalancersRequest) any())) // .thenReturn(new DescribeLoadBalancersResult().withLoadBalancerDescriptions(lbDescription)); AmazonAwsPetclinicInfrastructureEnforcer infraEnforcer = new AmazonAwsPetclinicInfrastructureEnforcer(ec2, elb, rds); infraEnforcer.createOrUpdateElasticLoadBalancer("/myapp/healsthcheck.jsp", "myapp"); verify(elb, atLeastOnce()).describeLoadBalancers((DescribeLoadBalancersRequest) any()); verify(elb, times(1)).createLBCookieStickinessPolicy(argThat(buildCreateLBCookieStickinessPolicyRequestMatcher())); verify(elb, times(1)).setLoadBalancerPoliciesOfListener(argThat(buildSetLoadBalancerPoliciesOfListenerRequestMatcher())); verify(elb, times(1)).deleteLoadBalancerPolicy(argThat(new ArgumentMatcher<DeleteLoadBalancerPolicyRequest>() { @Override public boolean matches(Object argument) { DeleteLoadBalancerPolicyRequest req = (DeleteLoadBalancerPolicyRequest) argument; if (!ObjectUtils.equals("myapp-jsessionid-policy", req.getPolicyName())) { return false; } if (!ObjectUtils.equals("myapp", req.getLoadBalancerName())) { return false; } return true; } })); verifyNoMoreInteractions(elb); } }