def setUp(self): self.route53 = MagicMock() self.acm = MagicMock() self.iam = MagicMock() self.elb2 = MagicMock() self.disco_elb = DiscoELB(_get_vpc_mock(), route53=self.route53, acm=self.acm, iam=self.iam, elb2=self.elb2) self.acm.get_certificate_arn.return_value = TEST_CERTIFICATE_ARN_ACM self.iam.get_certificate_arn.return_value = TEST_CERTIFICATE_ARN_IAM
def test_get_elb_with_multiple_ports(self): """Test creating an ELB that listens on multiple ports""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb(instance_protocols=['HTTP', 'HTTP'], instance_ports=[80, 80], elb_protocols=['HTTP', 'HTTPS'], elb_ports=[80, 443]) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }, { 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': TEST_CERTIFICATE_ARN_ACM }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def test_get_elb_cert_name_not_found(self): """Test creation an ELB with TLS and a specific cert name that doesn't exist""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) def _get_certificate_arn(name): return None if name == 'foo.com' else TEST_CERTIFICATE_ARN_ACM self.acm.get_certificate_arn.side_effect = _get_certificate_arn self.iam.get_certificate_arn.return_value = None self._create_elb(tls=True, cert_name='foo.com') elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': '' }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def setUp(self): self.route53 = MagicMock() self.acm = MagicMock() self.iam = MagicMock() self.disco_elb = DiscoELB(_get_vpc_mock(), route53=self.route53, acm=self.acm, iam=self.iam) self.acm.get_certificate_arn.return_value = TEST_CERTIFICATE_ARN_ACM self.iam.get_certificate_arn.return_value = TEST_CERTIFICATE_ARN_IAM
def run(): """Parses command line and dispatches the commands""" args = docopt(__doc__) configure_logging(args["--debug"]) config = read_config() env = args.get("--env") or config.get("disco_aws", "default_environment") vpc = DiscoVPC.fetch_environment(environment_name=env) if not vpc: print("Environment does not exist: {}".format(env)) sys.exit(1) if args['list']: format_string = "{0:<50} {1:33} {2}" print(format_string.format("ELB Name", "Availability Zones", "ELB Id"), file=sys.stderr) for elb_info in sorted(DiscoELB(vpc).list_for_display()): print( format_string.format(elb_info['elb_name'], elb_info['availability_zones'], elb_info["elb_id"])) elif args['update']: DiscoAWS(config, env).update_elb(args['--hostclass'])
def test_get_elb_cert_name_not_found(self): """Test creation an ELB with TLS and a specific cert name that doesn't exist""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) def _get_certificate_arn(name): return None if name == 'foo.com' else TEST_CERTIFICATE_ARN_ACM self.acm.get_certificate_arn.side_effect = _get_certificate_arn self.iam.get_certificate_arn.return_value = None self._create_elb(tls=True, cert_name='foo.com') elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': '' }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal' )
def test_get_elb_with_idle_timeout(self): """Test creating an ELB with an idle timeout""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock(wraps=client.modify_load_balancer_attributes) self._create_elb(idle_timeout=100) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={'ConnectionDraining': {'Enabled': False, 'Timeout': 0}, 'ConnectionSettings': {'IdleTimeout': 100}, 'CrossZoneLoadBalancing': {'Enabled': True}} )
def test_get_elb_no_cross_zone_lb(self): """Test creating ELB without cross zone load balancing""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock(wraps=client.modify_load_balancer_attributes) self._create_elb(cross_zone_load_balancing=False) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={ 'ConnectionDraining': {'Enabled': False, 'Timeout': 0}, 'CrossZoneLoadBalancing': {'Enabled': False} } )
def test_get_elb_external(self): """Test creation a publically accessible ELB""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(public=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1'])
def test_tagging_elb(self): """Test tagging an ELB""" client = self.disco_elb.elb_client client.add_tags = MagicMock(wraps=client.add_tags) self._create_elb() client.add_tags.assert_called_once_with( LoadBalancerNames=[DiscoELB.get_elb_id('unittestenv', 'mhcunit')], Tags=[ {'Key': 'environment', 'Value': TEST_ENV_NAME}, {'Key': 'is_testing', 'Value': '0'}, {'Key': 'hostclass', 'Value': TEST_HOSTCLASS}, ] )
def test_get_elb_external(self): """Test creation a publically accessible ELB""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb(public=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1'])
def test_get_elb_with_tcp(self): """Test creation an ELB with TCP""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(instance_protocols=['TCP'], instance_ports=[25], elb_protocols=['TCP'], elb_ports=[25]) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'TCP', 'LoadBalancerPort': 25, 'InstanceProtocol': 'TCP', 'InstancePort': 25 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def test_get_elb_with_tls(self): """Test creation an ELB with TLS""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(tls=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': TEST_CERTIFICATE_ARN_ACM }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def test_get_alarm_config_elb_metric(self): """Test DiscoAlarmsConfig get_alarms for ELB metrics""" disco_alarms_config = DiscoAlarmsConfig(ENVIRONMENT, autoscale=self.autoscale) disco_alarms_config.config = get_mock_config({ 'reporting.AWS/ELB.HealthyHostCount.mhcbanana': { 'threshold_min': '1', 'duration': '60', 'period': '5', 'statistic': 'Minimum', 'custom_metric': 'false', 'level': 'critical' } }) alarm_configs = disco_alarms_config.get_alarms('mhcbanana') self.assertEqual(1, len(alarm_configs)) self.assertEqual({'LoadBalancerName': DiscoELB.get_elb_id('testenv', 'mhcbanana')}, alarm_configs[0].dimensions)
def test_get_elb_with_tls(self): """Test creation an ELB with TLS""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb(tls=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': TEST_CERTIFICATE_ARN_ACM }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def test_get_elb_internal_no_tls(self): """Test creation an internal private ELB""" self.acm.get_certificate_arn.return_value = None self.iam.get_certificate_arn.return_value = None elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb() elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def test_get_elb_no_cross_zone_lb(self): """Test creating ELB without cross zone load balancing""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock( wraps=client.modify_load_balancer_attributes) self._create_elb(cross_zone_load_balancing=False) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={ 'ConnectionDraining': { 'Enabled': False, 'Timeout': 0 }, 'CrossZoneLoadBalancing': { 'Enabled': False } })
def test_get_elb_internal_no_tls(self): """Test creation an internal private ELB""" self.acm.get_certificate_arn.return_value = None self.iam.get_certificate_arn.return_value = None elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb() elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def test_get_elb_with_connection_draining(self): """Test creating ELB with connection draining""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock( wraps=client.modify_load_balancer_attributes) self._create_elb(connection_draining_timeout=100) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={ 'ConnectionDraining': { 'Enabled': True, 'Timeout': 100 }, 'CrossZoneLoadBalancing': { 'Enabled': True } })
def test_get_alarm_config_elb_metric(self): """Test DiscoAlarmsConfig get_alarms for ELB metrics""" disco_alarms_config = DiscoAlarmsConfig(ENVIRONMENT, autoscale=self.autoscale) disco_alarms_config.config = get_mock_config({ 'reporting.AWS/ELB.HealthyHostCount.mhcbanana': { 'threshold_min': '1', 'duration': '60', 'period': '5', 'statistic': 'Minimum', 'custom_metric': 'false', 'level': 'critical' } }) alarm_configs = disco_alarms_config.get_alarms('mhcbanana') self.assertEqual(1, len(alarm_configs)) self.assertEqual( {'LoadBalancerName': DiscoELB.get_elb_id('testenv', 'mhcbanana')}, alarm_configs[0].dimensions)
def run(): """Parses command line and dispatches the commands""" args = docopt(__doc__) configure_logging(args["--debug"]) config = read_config() env = args.get("--env") or config.get("disco_aws", "default_environment") vpc = DiscoVPC.fetch_environment(environment_name=env) if not vpc: print("Environment does not exist: {}".format(env)) sys.exit(1) if args['list']: for elb in sorted(DiscoELB(vpc).list()): print("{0:<20} {1:25}".format(elb['LoadBalancerName'], ','.join(elb['AvailabilityZones']))) elif args['update']: DiscoAWS(config, env).update_elb(args['--hostclass'])
def test_get_elb_with_tcp(self): """Test creation an ELB with TCP""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb(instance_protocols=['TCP'], instance_ports=[25], elb_protocols=['TCP'], elb_ports=[25]) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'TCP', 'LoadBalancerPort': 25, 'InstanceProtocol': 'TCP', 'InstancePort': 25 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def test_get_elb_with_multiple_ports(self): """Test creating an ELB that listens on multiple ports""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(instance_protocols=['HTTP', 'HTTP'], instance_ports=[80, 80], elb_protocols=['HTTP', 'HTTPS'], elb_ports=[80, 443]) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }, { 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': TEST_CERTIFICATE_ARN_ACM }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal')
def test_tagging_elb(self): """Test tagging an ELB""" client = self.disco_elb.elb_client client.add_tags = MagicMock(wraps=client.add_tags) self._create_elb() client.add_tags.assert_called_once_with( LoadBalancerNames=[DiscoELB.get_elb_id('unittestenv', 'mhcunit')], Tags=[ { 'Key': 'environment', 'Value': TEST_ENV_NAME }, { 'Key': 'is_testing', 'Value': '0' }, { 'Key': 'hostclass', 'Value': TEST_HOSTCLASS }, ])
class DiscoELBTests(TestCase): """Test DiscoELB""" def setUp(self): self.disco_elb = DiscoELB(_get_vpc_mock(), route53=MagicMock(), acm=MagicMock(), iam=MagicMock()) self.disco_elb.acm.get_certificate_arn = MagicMock(return_value="arn:aws:acm::123:blah") self.disco_elb.iam.get_certificate_arn = MagicMock(return_value="arn:aws:iam::123:blah") def _create_elb(self, hostclass=None, public=False, tls=False, idle_timeout=None, connection_draining_timeout=None, sticky_app_cookie=None): return self.disco_elb.get_or_create_elb( hostclass=hostclass or TEST_HOSTCLASS, security_groups=['sec-1'], subnets=['sub-1'], hosted_zone_name=TEST_DOMAIN_NAME, health_check_url="/", instance_protocol="HTTP", instance_port=80, elb_protocol="HTTPS" if tls else "HTTP", elb_port=443 if tls else 80, elb_public=public, sticky_app_cookie=sticky_app_cookie, idle_timeout=idle_timeout, connection_draining_timeout=connection_draining_timeout) @mock_elb def test_get_certificate_arn_prefers_acm(self): '''get_certificate_arn() prefers an ACM provided certificate''' self.assertEqual(self.disco_elb.get_certificate_arn("dummy"), "arn:aws:acm::123:blah") @mock_elb def test_get_certificate_arn_fallback_to_iam(self): '''get_certificate_arn() uses an IAM certificate if no ACM cert available''' self.disco_elb.acm.get_certificate_arn = MagicMock(return_value=None) self.assertEqual(self.disco_elb.get_certificate_arn("dummy"), "arn:aws:iam::123:blah") @mock_elb def test_get_cname(self): '''Make sure get_cname returns what we expect''' self.assertEqual(self.disco_elb.get_cname(TEST_HOSTCLASS, TEST_DOMAIN_NAME), "mhcunit-unittestenv.test.example.com") @mock_elb def test_get_elb_with_create(self): """Test creating a ELB""" self._create_elb() self.assertEquals( len(self.disco_elb.elb_client.describe_load_balancers()['LoadBalancerDescriptions']), 1) @mock_elb def test_get_elb_with_update(self): """Updating an ELB doesn't add create a new ELB""" self._create_elb() self._create_elb() self.assertEquals( len(self.disco_elb.elb_client.describe_load_balancers()['LoadBalancerDescriptions']), 1) @mock_elb def test_get_elb_internal(self): """Test creation an internal private ELB""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb() self.disco_elb.elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName='unittestenv-mhcunit', Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': 'arn:aws:acm::123:blah' }], Subnets=['sub-1'], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_internal_no_tls(self): """Test creation an internal private ELB""" self.disco_elb.acm.get_certificate_arn = MagicMock(return_value=None) self.disco_elb.iam.get_certificate_arn = MagicMock(return_value=None) elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb() elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName='unittestenv-mhcunit', Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': '' }], Subnets=['sub-1'], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_external(self): """Test creation a publically accessible ELB""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(public=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName='unittestenv-mhcunit', Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': 'arn:aws:acm::123:blah' }], Subnets=['sub-1'], SecurityGroups=['sec-1']) @mock_elb def test_get_elb_with_tls(self): """Test creation an ELB with TLS""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(tls=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName='unittestenv-mhcunit', Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': 'arn:aws:acm::123:blah' }], Subnets=['sub-1'], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_with_idle_timeout(self): """Test creating an ELB with an idle timeout""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock(wraps=client.modify_load_balancer_attributes) self._create_elb(idle_timeout=100) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName='unittestenv-mhcunit', LoadBalancerAttributes={'ConnectionDraining': {'Enabled': False, 'Timeout': 0}, 'ConnectionSettings': {'IdleTimeout': 100}} ) @mock_elb def test_get_elb_with_connection_draining(self): """Test creating ELB with connection draining""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock(wraps=client.modify_load_balancer_attributes) self._create_elb(connection_draining_timeout=100) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName='unittestenv-mhcunit', LoadBalancerAttributes={'ConnectionDraining': {'Enabled': True, 'Timeout': 100}} ) @mock_elb def test_delete_elb(self): """Test deleting an ELB""" self._create_elb() self.disco_elb.delete_elb(TEST_HOSTCLASS) load_balancers = self.disco_elb.elb_client.describe_load_balancers()['LoadBalancerDescriptions'] self.assertEquals(len(load_balancers), 0) @mock_elb def test_get_existing_elb(self): """Test get_elb for a hostclass""" self._create_elb() self.assertIsNotNone(self.disco_elb.get_elb(TEST_HOSTCLASS)) @mock_elb def test_list(self): """Test getting the list of ELBs""" self._create_elb(hostclass='mhcbar') self._create_elb(hostclass='mhcfoo') self.assertEquals(len(self.disco_elb.list()), 2) @mock_elb def test_elb_delete(self): """Test deletion of ELBs""" self._create_elb(hostclass='mhcbar') self.disco_elb.delete_elb(hostclass='mhcbar') self.assertEquals(len(self.disco_elb.list()), 0) @mock_elb def test_destroy_all_elbs(self): """Test deletion of all ELBs""" self._create_elb(hostclass='mhcbar') self._create_elb(hostclass='mhcfoo') self.disco_elb.destroy_all_elbs() self.assertEquals(len(self.disco_elb.list()), 0)
class DiscoELBTests(TestCase): """Test DiscoELB""" def setUp(self): self.route53 = MagicMock() self.acm = MagicMock() self.iam = MagicMock() self.elb2 = MagicMock() self.disco_elb = DiscoELB(_get_vpc_mock(), route53=self.route53, acm=self.acm, iam=self.iam, elb2=self.elb2) self.acm.get_certificate_arn.return_value = TEST_CERTIFICATE_ARN_ACM self.iam.get_certificate_arn.return_value = TEST_CERTIFICATE_ARN_IAM # pylint: disable=too-many-arguments, R0914 def _create_elb(self, hostclass=TEST_HOSTCLASS, public=False, tls=False, instance_protocols=('HTTP', ), instance_ports=(80, ), elb_protocols=('HTTP', ), elb_ports=(80, ), idle_timeout=None, connection_draining_timeout=None, sticky_app_cookie=None, elb_dns_alias=None, existing_cookie_policy=None, testing=False, cross_zone_load_balancing=True, cert_name=None, health_check_url='/'): sticky_policies = [existing_cookie_policy ] if existing_cookie_policy else [] mock_describe = MagicMock( return_value={'PolicyDescriptions': sticky_policies}) self.disco_elb.elb_client.describe_load_balancer_policies = mock_describe elb_protocols = ['HTTPS'] if tls else elb_protocols elb_ports = [443] if tls else elb_ports return self.disco_elb.get_or_create_elb( hostclass=hostclass or TEST_HOSTCLASS, security_groups=['sec-1'], subnets=[], hosted_zone_name=TEST_DOMAIN_NAME, health_check_url=health_check_url, port_config=DiscoELBPortConfig([ DiscoELBPortMapping(internal_port, internal_protocol, external_port, external_protocol) for (internal_port, internal_protocol), (external_port, external_protocol) in zip(zip(instance_ports, instance_protocols), zip(elb_ports, elb_protocols)) ]), elb_public=public, sticky_app_cookie=sticky_app_cookie, elb_dns_alias=elb_dns_alias, idle_timeout=idle_timeout, connection_draining_timeout=connection_draining_timeout, cert_name=cert_name, tags={ 'environment': TEST_ENV_NAME, 'hostclass': hostclass, 'is_testing': '1' if testing else '0' }, cross_zone_load_balancing=cross_zone_load_balancing) @mock_elb def test_get_certificate_arn_prefers_acm(self): '''get_certificate_arn() prefers an ACM provided certificate''' self.assertEqual(self.disco_elb.get_certificate_arn("dummy"), TEST_CERTIFICATE_ARN_ACM) @mock_elb def test_get_certificate_arn_fallback_to_iam(self): '''get_certificate_arn() uses an IAM certificate if no ACM cert available''' self.acm.get_certificate_arn.return_value = None self.assertEqual(self.disco_elb.get_certificate_arn("dummy"), TEST_CERTIFICATE_ARN_IAM) @mock_elb def test_get_cname(self): '''Make sure get_cname returns what we expect''' self.assertEqual( self.disco_elb.get_cname(TEST_HOSTCLASS, TEST_DOMAIN_NAME), "mhcunit-unittestenv.test.example.com") @mock_elb def test_get_elb_with_create(self): """Test creating a ELB""" self._create_elb() self.assertEqual( len(self.disco_elb.elb_client.describe_load_balancers() ['LoadBalancerDescriptions']), 1) @mock_elb def test_get_elb_with_update(self): """Updating an ELB doesn't add create a new ELB""" self._create_elb() self._create_elb() self.assertEqual( len(self.disco_elb.elb_client.describe_load_balancers() ['LoadBalancerDescriptions']), 1) @mock_elb def test_get_elb_internal(self): """Test creation an internal private ELB""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb() self.disco_elb.elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_internal_no_tls(self): """Test creation an internal private ELB""" self.acm.get_certificate_arn.return_value = None self.iam.get_certificate_arn.return_value = None elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb() elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_external(self): """Test creation a publically accessible ELB""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb(public=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1']) @mock_elb def test_get_elb_with_tls(self): """Test creation an ELB with TLS""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb(tls=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': TEST_CERTIFICATE_ARN_ACM }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_with_tls_and_cert_name(self): """Test creation an ELB with TLS and a specific cert name""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) def _get_certificate_arn(name): return 'arn:aws:acm::foo:com' if name == 'foo.com' else TEST_CERTIFICATE_ARN_ACM self.acm.get_certificate_arn.side_effect = _get_certificate_arn self._create_elb(tls=True, cert_name='foo.com') elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': 'arn:aws:acm::foo:com' }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_cert_name_not_found(self): """Test creation an ELB with TLS and a specific cert name that doesn't exist""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) def _get_certificate_arn(name): return None if name == 'foo.com' else TEST_CERTIFICATE_ARN_ACM self.acm.get_certificate_arn.side_effect = _get_certificate_arn self.iam.get_certificate_arn.return_value = None self._create_elb(tls=True, cert_name='foo.com') elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': '' }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_with_tcp(self): """Test creation an ELB with TCP""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb(instance_protocols=['TCP'], instance_ports=[25], elb_protocols=['TCP'], elb_ports=[25]) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'TCP', 'LoadBalancerPort': 25, 'InstanceProtocol': 'TCP', 'InstancePort': 25 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_with_multiple_ports(self): """Test creating an ELB that listens on multiple ports""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock( wraps=elb_client.create_load_balancer) self._create_elb(instance_protocols=['HTTP', 'HTTP'], instance_ports=[80, 80], elb_protocols=['HTTP', 'HTTPS'], elb_ports=[80, 443]) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }, { 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': TEST_CERTIFICATE_ARN_ACM }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_http_health_check(self): """ Creating an ELB creates a health check for the first HTTP or HTTPS instance listener """ # HTTP first elb_client = self.disco_elb.elb_client elb_client.configure_health_check = MagicMock( wraps=elb_client.configure_health_check) self._create_elb(instance_protocols=['HTTP', 'HTTP'], instance_ports=[80, 80], elb_protocols=['HTTP', 'HTTPS'], elb_ports=[80, 443], health_check_url='/health/check/endpoint/') elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'HTTP:80/health/check/endpoint/', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 }) # HTTPS first elb_client = self.disco_elb.elb_client elb_client.configure_health_check = MagicMock( wraps=elb_client.configure_health_check) self._create_elb(instance_protocols=['HTTPS', 'HTTP'], instance_ports=[443, 80], elb_protocols=['HTTP', 'HTTPS'], elb_ports=[80, 443], health_check_url='/health/check/endpoint/') elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'HTTPS:443/health/check/endpoint/', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 }) # HTTP after TCP elb_client.configure_health_check = MagicMock( wraps=elb_client.configure_health_check) self._create_elb(instance_protocols=['TCP', 'HTTP'], instance_ports=[9001, 4271], elb_protocols=['TCP', 'HTTPS'], elb_ports=[9001, 443], health_check_url='/health/check/endpoint/') elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'HTTP:4271/health/check/endpoint/', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 }) # HTTPS after TCP elb_client.configure_health_check = MagicMock( wraps=elb_client.configure_health_check) self._create_elb(instance_protocols=['TCP', 'HTTPS'], instance_ports=[9001, 4271], elb_protocols=['TCP', 'HTTPS'], elb_ports=[9001, 443], health_check_url='/health/check/endpoint/') elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'HTTPS:4271/health/check/endpoint/', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 }) @mock_elb def test_get_elb_tcp_health_check(self): """ If there are no HTTP(S) instance listeners, create_elb creates a health check for the first listener """ # HTTP first elb_client = self.disco_elb.elb_client elb_client.configure_health_check = MagicMock( wraps=elb_client.configure_health_check) self._create_elb(instance_protocols=['TCP', 'TCP'], instance_ports=[9001, 9002], elb_protocols=['TCP', 'TCP'], elb_ports=[9001, 9002], health_check_url='/health/check/endpoint/') elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'TCP:9001', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 }) @mock_elb def test_get_elb_with_idle_timeout(self): """Test creating an ELB with an idle timeout""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock( wraps=client.modify_load_balancer_attributes) self._create_elb(idle_timeout=100) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={ 'ConnectionDraining': { 'Enabled': False, 'Timeout': 0 }, 'ConnectionSettings': { 'IdleTimeout': 100 }, 'CrossZoneLoadBalancing': { 'Enabled': True } }) @mock_elb def test_get_elb_with_connection_draining(self): """Test creating ELB with connection draining""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock( wraps=client.modify_load_balancer_attributes) self._create_elb(connection_draining_timeout=100) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={ 'ConnectionDraining': { 'Enabled': True, 'Timeout': 100 }, 'CrossZoneLoadBalancing': { 'Enabled': True } }) @mock_elb def test_get_elb_no_cross_zone_lb(self): """Test creating ELB without cross zone load balancing""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock( wraps=client.modify_load_balancer_attributes) self._create_elb(cross_zone_load_balancing=False) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={ 'ConnectionDraining': { 'Enabled': False, 'Timeout': 0 }, 'CrossZoneLoadBalancing': { 'Enabled': False } }) @mock_elb def test_delete_elb(self): """Test deleting an ELB""" self._create_elb() self.disco_elb.delete_elb(TEST_HOSTCLASS) load_balancers = self.disco_elb.elb_client.describe_load_balancers( )['LoadBalancerDescriptions'] self.assertEqual(len(load_balancers), 0) @mock_elb def test_get_existing_elb(self): """Test get_elb for a hostclass""" self._create_elb() self.assertIsNotNone(self.disco_elb.get_elb(TEST_HOSTCLASS)) @mock_elb def test_list(self): """Test getting the list of ELBs""" self._create_elb(hostclass='mhcbar') self._create_elb(hostclass='mhcfoo') self.assertEqual(len(self.disco_elb.list()), 2) @mock_elb def test_elb_delete(self): """Test deletion of ELBs""" self._create_elb(hostclass='mhcbar') self.disco_elb.delete_elb(hostclass='mhcbar') self.assertEqual(len(self.disco_elb.list()), 0) @mock_elb def test_destroy_all_elbs(self): """Test deletion of all ELBs""" self._create_elb(hostclass='mhcbar') self._create_elb(hostclass='mhcfoo') self.disco_elb.destroy_all_elbs() self.assertEqual(len(self.disco_elb.list()), 0) @mock_elb def test_wait_for_instance_health(self): """Test that we can wait for instances attached to an ELB to enter a specific state""" self._create_elb(hostclass='mhcbar') elb_id = self.disco_elb.get_elb_id(TEST_ENV_NAME, 'mhcbar') instances = [{"InstanceId": "i-123123aa"}] self.disco_elb.elb_client.register_instances_with_load_balancer( LoadBalancerName=elb_id, Instances=instances) self.disco_elb.wait_for_instance_health_state(hostclass='mhcbar') @mock_elb def test_tagging_elb(self): """Test tagging an ELB""" client = self.disco_elb.elb_client client.add_tags = MagicMock(wraps=client.add_tags) self._create_elb() client.add_tags.assert_called_once_with( LoadBalancerNames=[DiscoELB.get_elb_id('unittestenv', 'mhcunit')], Tags=[ { 'Key': 'environment', 'Value': TEST_ENV_NAME }, { 'Key': 'is_testing', 'Value': '0' }, { 'Key': 'hostclass', 'Value': TEST_HOSTCLASS }, ]) @mock_elb def test_display_listing(self): """ Test that the tags for an ELB are correctly read for display """ self._create_elb(hostclass='mhcbar') self._create_elb(hostclass='mhcfoo', testing=True) listings = self.disco_elb.list_for_display() elb_names = [listing['elb_name'] for listing in listings] self.assertEqual( set(['unittestenv-mhcbar', 'unittestenv-mhcfoo-test']), set(elb_names)) @mock_elb def test_default_dns_alias(self): """Test that the default DNS alias is set up""" self._create_elb(hostclass='mhcfunky') self.route53.create_record.assert_called_once_with( TEST_DOMAIN_NAME, 'mhcfunky-' + TEST_ENV_NAME + '.' + TEST_DOMAIN_NAME, 'CNAME', MOCK_ELB_ADDRESS) @mock_elb def test_custom_dns_alias(self): """Test that the custom DNS alias is set up""" self._create_elb(hostclass='mhcfunky', elb_dns_alias='thefunk') self.route53.create_record.assert_any_call( TEST_DOMAIN_NAME, 'thefunk' + '.' + TEST_DOMAIN_NAME, 'CNAME', MOCK_ELB_ADDRESS) self.route53.create_record.assert_any_call( TEST_DOMAIN_NAME, 'mhcfunky-' + TEST_ENV_NAME + '.' + TEST_DOMAIN_NAME, 'CNAME', MOCK_ELB_ADDRESS) def test_get_target_group(self): """Test getting a target group""" describe_call = { "TargetGroups": [ { "TargetGroupArn": "mock_target_group" }, ] } self.elb2.describe_target_groups.return_value = describe_call group = self.disco_elb.get_or_create_target_group( environment=TEST_ENV_NAME, hostclass=TEST_HOSTCLASS, vpc_id=TEST_VPC_ID, ) self.assertEqual(group, ["mock_target_group"]) def test_create_tg_without_port_or_health(self): """Test creating a group without port config or health check""" self.elb2.describe_target_groups.side_effect = EC2ResponseError( status="mockstatus", reason="mockreason") self.disco_elb.get_or_create_target_group( environment=TEST_ENV_NAME, hostclass=TEST_HOSTCLASS, vpc_id=TEST_VPC_ID, ) self.elb2.create_target_group.assert_called_with( Name="unittestenv-mhcunit", Protocol='HTTP', Port=80, VpcId=TEST_VPC_ID, HealthCheckProtocol="HTTP", HealthCheckPort="80", HealthCheckPath="/") def test_create_tg_with_health_check(self): """Test creating a group with health check""" self.elb2.describe_target_groups.side_effect = EC2ResponseError( status="mockstatus", reason="mockreason") self.disco_elb.get_or_create_target_group( environment=TEST_ENV_NAME, hostclass=TEST_HOSTCLASS, vpc_id=TEST_VPC_ID, health_check_path="/mockpath") self.elb2.create_target_group.assert_called_with( Name="unittestenv-mhcunit", Protocol='HTTP', Port=80, VpcId=TEST_VPC_ID, HealthCheckProtocol="HTTP", HealthCheckPort="80", HealthCheckPath="/mockpath") def test_create_target_group_with_port_config(self): """Test creating a group using port config""" self.elb2.describe_target_groups.side_effect = EC2ResponseError( status="mockstatus", reason="mockreason") instance_protocols = ('HTTP', ) instance_ports = (80, ) elb_protocols = ('HTTP', ) elb_ports = (80, ) self.disco_elb.get_or_create_target_group( environment=TEST_ENV_NAME, hostclass=TEST_HOSTCLASS, vpc_id=TEST_VPC_ID, port_config=DiscoELBPortConfig([ DiscoELBPortMapping(internal_port, internal_protocol, external_port, external_protocol) for (internal_port, internal_protocol), (external_port, external_protocol) in zip(zip(instance_ports, instance_protocols), zip(elb_ports, elb_protocols)) ])) self.elb2.create_target_group.assert_called_with( Name="unittestenv-mhcunit", Protocol='HTTP', Port=80, VpcId=TEST_VPC_ID, HealthCheckProtocol="HTTP", HealthCheckPort="80", HealthCheckPath="/") def test_create_target_group_tcp(self): """Test creating a group non http/https""" self.elb2.describe_target_groups.side_effect = EC2ResponseError( status="mockstatus", reason="mockreason") instance_protocols = ('TCP', ) instance_ports = (80, ) elb_protocols = ('TCP', ) elb_ports = (80, ) self.disco_elb.get_or_create_target_group( environment=TEST_ENV_NAME, hostclass=TEST_HOSTCLASS, vpc_id=TEST_VPC_ID, port_config=DiscoELBPortConfig([ DiscoELBPortMapping(internal_port, internal_protocol, external_port, external_protocol) for (internal_port, internal_protocol), (external_port, external_protocol) in zip(zip(instance_ports, instance_protocols), zip(elb_ports, elb_protocols)) ])) self.elb2.create_target_group.assert_called_with( Name="unittestenv-mhcunit", Protocol='TCP', Port=80, VpcId=TEST_VPC_ID, HealthCheckProtocol="TCP", HealthCheckPort="80") def test_create_target_group_ssl(self): """Test creating a group that is SSL""" self.elb2.describe_target_groups.side_effect = EC2ResponseError( status="mockstatus", reason="mockreason") instance_protocols = ('SSL', ) instance_ports = (80, ) elb_protocols = ('SSL', ) elb_ports = (80, ) self.disco_elb.get_or_create_target_group( environment=TEST_ENV_NAME, hostclass=TEST_HOSTCLASS, vpc_id=TEST_VPC_ID, port_config=DiscoELBPortConfig([ DiscoELBPortMapping(internal_port, internal_protocol, external_port, external_protocol) for (internal_port, internal_protocol), (external_port, external_protocol) in zip(zip(instance_ports, instance_protocols), zip(elb_ports, elb_protocols)) ])) self.elb2.create_target_group.assert_called_with( Name="unittestenv-mhcunit", Protocol='TLS', Port=80, VpcId=TEST_VPC_ID, HealthCheckProtocol="TCP", HealthCheckPort="80") def test_tags_transformation(self): """Tests if tags are converted to the right format""" tags = { "application": "fake-app-tag", "environment": "fake-environment" } expected_tags = [{ 'Value': "fake-app-tag", 'Key': "application" }, { 'Value': "fake-environment", 'Key': "environment" }] actual_tags = self.disco_elb.add_tags_to_target_groups( target_groups=["mock_target_group"], tags=tags) self.assertEqual(sorted(actual_tags), sorted(expected_tags)) def test_add_tags_existing_tg(self): """ Tests if add tags is called for existing tg""" describe_call = { "TargetGroups": [ { "TargetGroupArn": "mock_target_group" }, ] } self.elb2.describe_target_groups.return_value = describe_call self.disco_elb.get_or_create_target_group( environment=TEST_ENV_NAME, hostclass=TEST_HOSTCLASS, vpc_id=TEST_VPC_ID, tags={"fake-key": "fake-value"}) self.elb2.add_tags.assert_called_with( ResourceArns=["mock_target_group"], Tags=[{ 'Key': "fake-key", 'Value': "fake-value" }]) def test_add_tags_created_tg(self): """ Tests if add tags is called for new tg""" self.elb2.describe_target_groups.side_effect = EC2ResponseError( status="mockstatus", reason="mockreason") self.elb2.create_target_group.return_value = { 'TargetGroups': [{ 'TargetGroupArn': "fake-tg-arn" }] } self.disco_elb.get_or_create_target_group( environment=TEST_ENV_NAME, hostclass=TEST_HOSTCLASS, vpc_id=TEST_VPC_ID, tags={"fake-key": "fake-value"}) self.elb2.create_target_group.assert_called_with( Name="unittestenv-mhcunit", Protocol='HTTP', Port=80, VpcId=TEST_VPC_ID, HealthCheckProtocol="HTTP", HealthCheckPort="80", HealthCheckPath="/") self.elb2.add_tags.assert_called_with(ResourceArns=["fake-tg-arn"], Tags=[{ 'Key': "fake-key", 'Value': "fake-value" }])
def run(): """Parses command line and dispatches the commands""" config = read_config() args = docopt(__doc__) configure_logging(args["--debug"]) env = args["--environment"] or config.get("disco_aws", "default_environment") force_deployable = None if args["--deployable"] is None else is_truthy( args["--deployable"]) pipeline_definition = [] if args["--pipeline"]: with open(args["--pipeline"], "r") as f: reader = csv.DictReader(f) pipeline_definition = [line for line in reader] aws = DiscoAWS(config, env) if config.has_option('test', 'env'): test_env = config.get('test', 'env') test_aws = DiscoAWS(config, test_env) else: test_aws = aws bake = DiscoBake(config, aws.connection) if args["--ami"] and args["--hostclass"]: image = bake.get_image(args["--ami"]) if args["--hostclass"] != bake.ami_hostclass(image): logger.error('AMI %s does not belong to hostclass %s', args["--ami"], args["--hostclass"]) sys.exit(1) vpc = DiscoVPC.fetch_environment(environment_name=env) deploy = DiscoDeploy(aws, test_aws, bake, DiscoGroup(env), DiscoELB(vpc), DiscoSSM(environment_name=env), pipeline_definition=pipeline_definition, ami=args.get("--ami"), hostclass=args.get("--hostclass"), allow_any_hostclass=args["--allow-any-hostclass"]) if args["test"]: try: deploy.test(dry_run=args["--dry-run"], deployment_strategy=args["--strategy"], ticket_id=args["--ticket"], force_deployable=force_deployable) except RuntimeError as err: logger.error(str(err)) sys.exit(1) elif args["update"]: try: deploy.update(dry_run=args["--dry-run"], deployment_strategy=args["--strategy"], ticket_id=args["--ticket"], force_deployable=force_deployable) except RuntimeError as err: logger.error(str(err)) sys.exit(1) elif args["list"]: missing = "-" if pipeline_definition else "" if args["--tested"]: for (_hostclass, ami) in deploy.get_latest_tested_amis().iteritems(): print("{} {:40} {}".format( ami.id, ami.name.split()[0], deploy.get_integration_test(ami.name.split()[0]) or missing)) elif args["--untested"]: for (_hostclass, ami) in deploy.get_latest_untested_amis().iteritems(): print("{} {:40} {}".format( ami.id, ami.name.split()[0], deploy.get_integration_test(ami.name.split()[0]) or missing)) elif args["--failed"]: for (_hostclass, ami) in deploy.get_latest_failed_amis().iteritems(): print("{} {:40} {}".format( ami.id, ami.name.split()[0], deploy.get_integration_test(ami.name.split()[0]) or missing)) elif args["--testable"]: for ami in deploy.get_test_amis(): print("{} {:40} {}".format( ami.id, ami.name.split()[0], deploy.get_integration_test(ami.name.split()[0]) or missing)) elif args["--updatable"]: for ami in deploy.get_update_amis(): print("{} {:40} {}".format( ami.id, ami.name.split()[0], deploy.get_integration_test(ami.name.split()[0]) or missing)) elif args["--failures"]: failures = deploy.get_failed_amis() for ami in failures: print("{} {:40} {}".format( ami.id, ami.name.split()[0], deploy.get_integration_test(ami.name.split()[0]) or missing)) sys.exit(1 if failures else 0)
def setUp(self): self.disco_elb = DiscoELB(_get_vpc_mock(), route53=MagicMock(), acm=MagicMock(), iam=MagicMock()) self.disco_elb.acm.get_certificate_arn = MagicMock(return_value="arn:aws:acm::123:blah") self.disco_elb.iam.get_certificate_arn = MagicMock(return_value="arn:aws:iam::123:blah")
class DiscoELBTests(TestCase): """Test DiscoELB""" def setUp(self): self.route53 = MagicMock() self.acm = MagicMock() self.iam = MagicMock() self.disco_elb = DiscoELB(_get_vpc_mock(), route53=self.route53, acm=self.acm, iam=self.iam) self.acm.get_certificate_arn.return_value = TEST_CERTIFICATE_ARN_ACM self.iam.get_certificate_arn.return_value = TEST_CERTIFICATE_ARN_IAM # pylint: disable=too-many-arguments, R0914 def _create_elb( self, hostclass=TEST_HOSTCLASS, public=False, tls=False, instance_protocols=('HTTP',), instance_ports=(80,), elb_protocols=('HTTP',), elb_ports=(80,), idle_timeout=None, connection_draining_timeout=None, sticky_app_cookie=None, elb_dns_alias=None, existing_cookie_policy=None, testing=False, cross_zone_load_balancing=True, cert_name=None, health_check_url='/' ): sticky_policies = [existing_cookie_policy] if existing_cookie_policy else [] mock_describe = MagicMock(return_value={'PolicyDescriptions': sticky_policies}) self.disco_elb.elb_client.describe_load_balancer_policies = mock_describe elb_protocols = ['HTTPS'] if tls else elb_protocols elb_ports = [443] if tls else elb_ports return self.disco_elb.get_or_create_elb( hostclass=hostclass or TEST_HOSTCLASS, security_groups=['sec-1'], subnets=[], hosted_zone_name=TEST_DOMAIN_NAME, health_check_url=health_check_url, port_config=DiscoELBPortConfig( [ DiscoELBPortMapping(internal_port, internal_protocol, external_port, external_protocol) for (internal_port, internal_protocol), (external_port, external_protocol) in zip( zip(instance_ports, instance_protocols), zip(elb_ports, elb_protocols) ) ] ), elb_public=public, sticky_app_cookie=sticky_app_cookie, elb_dns_alias=elb_dns_alias, idle_timeout=idle_timeout, connection_draining_timeout=connection_draining_timeout, cert_name=cert_name, tags={ 'environment': TEST_ENV_NAME, 'hostclass': hostclass, 'is_testing': '1' if testing else '0' }, cross_zone_load_balancing=cross_zone_load_balancing ) @mock_elb def test_get_certificate_arn_prefers_acm(self): '''get_certificate_arn() prefers an ACM provided certificate''' self.assertEqual(self.disco_elb.get_certificate_arn("dummy"), TEST_CERTIFICATE_ARN_ACM) @mock_elb def test_get_certificate_arn_fallback_to_iam(self): '''get_certificate_arn() uses an IAM certificate if no ACM cert available''' self.acm.get_certificate_arn.return_value = None self.assertEqual(self.disco_elb.get_certificate_arn("dummy"), TEST_CERTIFICATE_ARN_IAM) @mock_elb def test_get_cname(self): '''Make sure get_cname returns what we expect''' self.assertEqual(self.disco_elb.get_cname(TEST_HOSTCLASS, TEST_DOMAIN_NAME), "mhcunit-unittestenv.test.example.com") @mock_elb def test_get_elb_with_create(self): """Test creating a ELB""" self._create_elb() self.assertEqual( len(self.disco_elb.elb_client.describe_load_balancers()['LoadBalancerDescriptions']), 1) @mock_elb def test_get_elb_with_update(self): """Updating an ELB doesn't add create a new ELB""" self._create_elb() self._create_elb() self.assertEqual( len(self.disco_elb.elb_client.describe_load_balancers()['LoadBalancerDescriptions']), 1) @mock_elb def test_get_elb_internal(self): """Test creation an internal private ELB""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb() self.disco_elb.elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_internal_no_tls(self): """Test creation an internal private ELB""" self.acm.get_certificate_arn.return_value = None self.iam.get_certificate_arn.return_value = None elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb() elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_external(self): """Test creation a publically accessible ELB""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(public=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }], Subnets=[], SecurityGroups=['sec-1']) @mock_elb def test_get_elb_with_tls(self): """Test creation an ELB with TLS""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(tls=True) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': TEST_CERTIFICATE_ARN_ACM }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_with_tls_and_cert_name(self): """Test creation an ELB with TLS and a specific cert name""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) def _get_certificate_arn(name): return 'arn:aws:acm::foo:com' if name == 'foo.com' else TEST_CERTIFICATE_ARN_ACM self.acm.get_certificate_arn.side_effect = _get_certificate_arn self._create_elb(tls=True, cert_name='foo.com') elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': 'arn:aws:acm::foo:com' }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal' ) @mock_elb def test_get_elb_cert_name_not_found(self): """Test creation an ELB with TLS and a specific cert name that doesn't exist""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) def _get_certificate_arn(name): return None if name == 'foo.com' else TEST_CERTIFICATE_ARN_ACM self.acm.get_certificate_arn.side_effect = _get_certificate_arn self.iam.get_certificate_arn.return_value = None self._create_elb(tls=True, cert_name='foo.com') elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': '' }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal' ) @mock_elb def test_get_elb_with_tcp(self): """Test creation an ELB with TCP""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(instance_protocols=['TCP'], instance_ports=[25], elb_protocols=['TCP'], elb_ports=[25]) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'TCP', 'LoadBalancerPort': 25, 'InstanceProtocol': 'TCP', 'InstancePort': 25 }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_with_multiple_ports(self): """Test creating an ELB that listens on multiple ports""" elb_client = self.disco_elb.elb_client elb_client.create_load_balancer = MagicMock(wraps=elb_client.create_load_balancer) self._create_elb(instance_protocols=['HTTP', 'HTTP'], instance_ports=[80, 80], elb_protocols=['HTTP', 'HTTPS'], elb_ports=[80, 443]) elb_client.create_load_balancer.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), Listeners=[{ 'Protocol': 'HTTP', 'LoadBalancerPort': 80, 'InstanceProtocol': 'HTTP', 'InstancePort': 80 }, { 'Protocol': 'HTTPS', 'LoadBalancerPort': 443, 'InstanceProtocol': 'HTTP', 'InstancePort': 80, 'SSLCertificateId': TEST_CERTIFICATE_ARN_ACM }], Subnets=[], SecurityGroups=['sec-1'], Scheme='internal') @mock_elb def test_get_elb_http_health_check(self): """ Creating an ELB creates a health check for the first HTTP or HTTPS instance listener """ # HTTP first elb_client = self.disco_elb.elb_client elb_client.configure_health_check = MagicMock(wraps=elb_client.configure_health_check) self._create_elb( instance_protocols=['HTTP', 'HTTP'], instance_ports=[80, 80], elb_protocols=['HTTP', 'HTTPS'], elb_ports=[80, 443], health_check_url='/health/check/endpoint/' ) elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'HTTP:80/health/check/endpoint/', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 } ) # HTTPS first elb_client = self.disco_elb.elb_client elb_client.configure_health_check = MagicMock(wraps=elb_client.configure_health_check) self._create_elb( instance_protocols=['HTTPS', 'HTTP'], instance_ports=[443, 80], elb_protocols=['HTTP', 'HTTPS'], elb_ports=[80, 443], health_check_url='/health/check/endpoint/' ) elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'HTTPS:443/health/check/endpoint/', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 } ) # HTTP after TCP elb_client.configure_health_check = MagicMock(wraps=elb_client.configure_health_check) self._create_elb( instance_protocols=['TCP', 'HTTP'], instance_ports=[9001, 4271], elb_protocols=['TCP', 'HTTPS'], elb_ports=[9001, 443], health_check_url='/health/check/endpoint/' ) elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'HTTP:4271/health/check/endpoint/', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 } ) # HTTPS after TCP elb_client.configure_health_check = MagicMock(wraps=elb_client.configure_health_check) self._create_elb( instance_protocols=['TCP', 'HTTPS'], instance_ports=[9001, 4271], elb_protocols=['TCP', 'HTTPS'], elb_ports=[9001, 443], health_check_url='/health/check/endpoint/' ) elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'HTTPS:4271/health/check/endpoint/', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 } ) @mock_elb def test_get_elb_tcp_health_check(self): """ If there are no HTTP(S) instance listeners, create_elb creates a health check for the first listener """ # HTTP first elb_client = self.disco_elb.elb_client elb_client.configure_health_check = MagicMock(wraps=elb_client.configure_health_check) self._create_elb( instance_protocols=['TCP', 'TCP'], instance_ports=[9001, 9002], elb_protocols=['TCP', 'TCP'], elb_ports=[9001, 9002], health_check_url='/health/check/endpoint/' ) elb_client.configure_health_check.assert_called_once_with( LoadBalancerName=ANY, HealthCheck={ 'Target': 'TCP:9001', 'Interval': 5, 'Timeout': 4, 'UnhealthyThreshold': 2, 'HealthyThreshold': 2 } ) @mock_elb def test_get_elb_with_idle_timeout(self): """Test creating an ELB with an idle timeout""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock(wraps=client.modify_load_balancer_attributes) self._create_elb(idle_timeout=100) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={'ConnectionDraining': {'Enabled': False, 'Timeout': 0}, 'ConnectionSettings': {'IdleTimeout': 100}, 'CrossZoneLoadBalancing': {'Enabled': True}} ) @mock_elb def test_get_elb_with_connection_draining(self): """Test creating ELB with connection draining""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock(wraps=client.modify_load_balancer_attributes) self._create_elb(connection_draining_timeout=100) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={ 'ConnectionDraining': {'Enabled': True, 'Timeout': 100}, 'CrossZoneLoadBalancing': {'Enabled': True} } ) @mock_elb def test_get_elb_no_cross_zone_lb(self): """Test creating ELB without cross zone load balancing""" client = self.disco_elb.elb_client client.modify_load_balancer_attributes = MagicMock(wraps=client.modify_load_balancer_attributes) self._create_elb(cross_zone_load_balancing=False) client.modify_load_balancer_attributes.assert_called_once_with( LoadBalancerName=DiscoELB.get_elb_id('unittestenv', 'mhcunit'), LoadBalancerAttributes={ 'ConnectionDraining': {'Enabled': False, 'Timeout': 0}, 'CrossZoneLoadBalancing': {'Enabled': False} } ) @mock_elb def test_delete_elb(self): """Test deleting an ELB""" self._create_elb() self.disco_elb.delete_elb(TEST_HOSTCLASS) load_balancers = self.disco_elb.elb_client.describe_load_balancers()['LoadBalancerDescriptions'] self.assertEqual(len(load_balancers), 0) @mock_elb def test_get_existing_elb(self): """Test get_elb for a hostclass""" self._create_elb() self.assertIsNotNone(self.disco_elb.get_elb(TEST_HOSTCLASS)) @mock_elb def test_list(self): """Test getting the list of ELBs""" self._create_elb(hostclass='mhcbar') self._create_elb(hostclass='mhcfoo') self.assertEqual(len(self.disco_elb.list()), 2) @mock_elb def test_elb_delete(self): """Test deletion of ELBs""" self._create_elb(hostclass='mhcbar') self.disco_elb.delete_elb(hostclass='mhcbar') self.assertEqual(len(self.disco_elb.list()), 0) @mock_elb def test_destroy_all_elbs(self): """Test deletion of all ELBs""" self._create_elb(hostclass='mhcbar') self._create_elb(hostclass='mhcfoo') self.disco_elb.destroy_all_elbs() self.assertEqual(len(self.disco_elb.list()), 0) @mock_elb def test_wait_for_instance_health(self): """Test that we can wait for instances attached to an ELB to enter a specific state""" self._create_elb(hostclass='mhcbar') elb_id = self.disco_elb.get_elb_id(TEST_ENV_NAME, 'mhcbar') instances = [{"InstanceId": "i-123123aa"}] self.disco_elb.elb_client.register_instances_with_load_balancer(LoadBalancerName=elb_id, Instances=instances) self.disco_elb.wait_for_instance_health_state(hostclass='mhcbar') @mock_elb def test_tagging_elb(self): """Test tagging an ELB""" client = self.disco_elb.elb_client client.add_tags = MagicMock(wraps=client.add_tags) self._create_elb() client.add_tags.assert_called_once_with( LoadBalancerNames=[DiscoELB.get_elb_id('unittestenv', 'mhcunit')], Tags=[ {'Key': 'environment', 'Value': TEST_ENV_NAME}, {'Key': 'is_testing', 'Value': '0'}, {'Key': 'hostclass', 'Value': TEST_HOSTCLASS}, ] ) @mock_elb def test_display_listing(self): """ Test that the tags for an ELB are correctly read for display """ self._create_elb(hostclass='mhcbar') self._create_elb(hostclass='mhcfoo', testing=True) listings = self.disco_elb.list_for_display() elb_names = [listing['elb_name'] for listing in listings] self.assertEqual(set(['unittestenv-mhcbar', 'unittestenv-mhcfoo-test']), set(elb_names)) @mock_elb def test_default_dns_alias(self): """Test that the default DNS alias is set up""" self._create_elb(hostclass='mhcfunky') self.route53.create_record.assert_called_once_with( TEST_DOMAIN_NAME, 'mhcfunky-' + TEST_ENV_NAME + '.' + TEST_DOMAIN_NAME, 'CNAME', MOCK_ELB_ADDRESS) @mock_elb def test_custom_dns_alias(self): """Test that the custom DNS alias is set up""" self._create_elb(hostclass='mhcfunky', elb_dns_alias='thefunk') self.route53.create_record.assert_any_call( TEST_DOMAIN_NAME, 'thefunk' + '.' + TEST_DOMAIN_NAME, 'CNAME', MOCK_ELB_ADDRESS) self.route53.create_record.assert_any_call( TEST_DOMAIN_NAME, 'mhcfunky-' + TEST_ENV_NAME + '.' + TEST_DOMAIN_NAME, 'CNAME', MOCK_ELB_ADDRESS)