def test_run(self, fixtures): # Given with patch('ecs_update_monitor.ECSMonitor') as ECSMonitor, \ patch('ecs_update_monitor.ECSEventIterator') as ECSEventIterator: event_iterator = Mock() ECSEventIterator.return_value = event_iterator ecs_monitor = Mock() ECSMonitor.return_value = ecs_monitor cluster = fixtures['cluster'] service = fixtures['service'] taskdef = fixtures['taskdef'] boto_session = Mock() # When run(cluster, service, taskdef, boto_session) # Then ECSEventIterator.assert_called_once_with( cluster, service, taskdef, boto_session ) ECSMonitor.assert_called_once_with(event_iterator) ecs_monitor.wait.assert_called_once()
def test_deployment_completed_for_new_service(self): cluster = 'dummy-cluster' service = 'dummy-environment' taskdef = 'dummy-taskdef' boto_session = MagicMock(spec=Session) mock_ecs_client = Mock() def describe_services_generator(running_counts): for running_count in running_counts: pending_count = 0 yield { 'services': [ { 'clusterArn': 'arn:aws:ecs:eu-1:7:cstr/non-prod', 'deployments': [ { 'desiredCount': 2, 'createdAt': datetime.datetime( 2017, 3, 8, 12, 15, 9, 13000 ), 'id': 'ecs-svc/9223370553143707624', 'runningCount': running_count, 'pendingCount': pending_count, 'status': 'PRIMARY', 'taskDefinition': taskdef, } ], 'loadBalancers': [ { 'containerName': 'app', 'containerPort': 8000, 'targetGroupArn': 'd035fe071cf0069f' } ], 'status': 'ACTIVE', 'taskDefinition': taskdef } ] } mock_ecs_client.describe_services.side_effect = \ describe_services_generator([0, 0, 1, 2, 2, 2, 2, 2]) boto_session.client.return_value = mock_ecs_client events = ECSEventIterator( cluster, service, taskdef, boto_session ) event_list = [e for e in events] assert len(event_list) == 8 assert not event_list[0].done assert event_list[0].running == 0 assert event_list[0].desired == 2 assert event_list[7].done assert event_list[7].running == 2 assert event_list[7].desired == 2
def test_monitor_taskdef_does_not_match(self, deployment_data): cluster = 'dummy-cluster' service = 'dummy-service' deploy_taskdef = deployment_data['deploy_taskdef'] actual_taskdef = deployment_data['actual_taskdef'] assume(deploy_taskdef != actual_taskdef) boto_session = MagicMock(spec=Session) mock_ecs_client = Mock() mock_ecs_client.describe_services.return_value = { 'services': [ { 'clusterArn': 'arn:aws:ecs:eu-1:7:cluster/non-production', 'deployments': [ { 'desiredCount': 2, 'id': 'ecs-svc/9223370553143707624', 'pendingCount': 0, 'runningCount': 2, 'status': 'PRIMARY', 'taskDefinition': actual_taskdef, 'updatedAt': datetime.datetime(2017, 1, 6, 13, 57), 'createdAt': datetime.datetime(2017, 1, 6, 13, 57) } ], 'status': 'ACTIVE', 'taskDefinition': 'dummy-taskdef' } ] } boto_session.client.return_value = mock_ecs_client events = ECSEventIterator( cluster, service, deploy_taskdef, boto_session ) with self.assertRaises(TaskdefDoesNotMatchError) as reason: [e for e in events] assert 'found primary deployment with taskdef {} ' \ 'while waiting for deployment of taskdef {}'.format( actual_taskdef, deploy_taskdef ) in str(reason.exception) with self.assertRaises(TaskdefDoesNotMatchError): next(iter(events))
def test_memoization_on_object_instance(self): cluster = 'dummy-cluster' service = 'dummy-service' taskdef = 'dummy-taskdef' boto_session = MagicMock(spec=Session) mock_ecs_client = Mock() mock_ecs_client.describe_services.return_value = { 'services': [ { 'clusterArn': 'arn:aws:ecs:eu-1:7:cstr/non-prod', 'deployments': [ { 'desiredCount': 2, u'createdAt': datetime.datetime( 2017, 3, 8, 12, 15, 9, 13000, tzinfo=tzlocal() ), 'id': 'ecs-svc/9223370553143707624', 'runningCount': 1, 'pendingCount': 1, 'status': 'PRIMARY', 'taskDefinition': taskdef, } ], 'status': 'ACTIVE', 'taskDefinition': taskdef } ] } boto_session.client.return_value = mock_ecs_client events = ECSEventIterator( cluster, service, taskdef, boto_session ) [e.done for e in islice(events, 1000)] boto_session.client.assert_called_once()
def test_deployment_does_not_complete_within_time(self): cluster = 'dummy-cluster' service = 'dummy-service' taskdef = 'dummy-taskdef' boto_session = MagicMock(spec=Session) mock_ecs_client = Mock() mock_ecs_client.describe_services.return_value = { 'services': [ { 'clusterArn': 'arn:aws:ecs:eu-1:7:cstr/non-prod', 'deployments': [ { 'desiredCount': 2, 'createdAt': datetime.datetime( 2017, 1, 6, 10, 58, 9 ), 'id': 'ecs-svc/9223370553143707624', 'runningCount': 1, 'pendingCount': 1, 'status': 'PRIMARY', 'taskDefinition': taskdef, } ], 'status': 'ACTIVE', 'taskDefinition': taskdef } ] } boto_session.client.return_value = mock_ecs_client events = ECSEventIterator( cluster, service, taskdef, boto_session ) statuses = [e.done for e in islice(events, 1000)] assert not any(statuses)
def test_monitor_exited_when_the_deployment_is_stable( self, deployment_data ): cluster = deployment_data['cluster'] service = deployment_data['service'] taskdef = deployment_data['taskdef'] boto_session = MagicMock(spec=Session) mock_ecs_client = Mock() mock_ecs_client.describe_services.return_value = { 'failures': [], 'services': [ { 'clusterArn': 'arn:aws:ecs:eu-1:7:cluster/non-production', 'createdAt': datetime.datetime(2017, 1, 6, 10, 58, 9), 'deployments': [ { 'createdAt': datetime.datetime(2017, 1, 6, 13, 57), 'desiredCount': 2, 'id': 'ecs-svc/9223370553143707624', 'pendingCount': 0, 'runningCount': 2, 'status': 'PRIMARY', 'taskDefinition': taskdef, 'updatedAt': datetime.datetime(2017, 1, 6, 13, 57) }, ], 'desiredCount': 2, 'events': [ { 'createdAt': datetime.datetime( 2017, 3, 10, 10, 27, 40, 1 ), 'id': '71e1ea54-61bd-4d5f-b6ae-ba0ba4a3c270', 'message': 'has reached a steady state.' }, { 'createdAt': datetime.datetime( 2017, 3, 9, 16, 26, 49, 794 ), 'id': '851fd578-579d-4a23-8764-107f0cf1120c', 'message': 'registered 1 targets' }, { 'createdAt': datetime.datetime( 2017, 3, 9, 16, 26, 37, 48000 ), 'id': '39e46d75-018e-4db4-a62d-1d76b4564132', 'message': 'has started 1 tasks' } ], 'loadBalancers': [ { 'containerName': 'app', 'containerPort': 8000, 'targetGroupArn': 'd035fe071cf0069f' } ], 'pendingCount': 0, 'roleArn': '20170106105800480922659dsq', 'runningCount': 2, 'serviceArn': 'aslive-testtf2469', 'serviceName': 'aslive-testtf2469', 'status': 'ACTIVE', 'taskDefinition': taskdef } ] } boto_session.client.return_value = mock_ecs_client events = ECSEventIterator( cluster, service, taskdef, boto_session ) event_list = [e for e in events] # Then assert len(event_list) == 5 assert event_list[0].messages == [ 'has started 1 tasks', 'registered 1 targets', 'has reached a steady state.' ] assert event_list[4].done assert event_list[4].previous_running == 0
def test_get_ecs_service_events(self): # Given since = datetime.datetime( 2017, 3, 8, 12, 15, 0, 0, tzinfo=tzlocal() ) ecs_service_events_1 = [ { u'createdAt': datetime.datetime( 2017, 3, 8, 12, 15, 9, 13000, tzinfo=tzlocal() ), u'id': u'efbfce1c-c7d0-43be-a9a8-d18ad70e9d8b', u'message': u'(service aslive-grahamlyons-test) has started 2' }, # the event blow should *not* be returned (it's before since) { u'createdAt': datetime.datetime( 2017, 3, 8, 12, 14, 30, 0, tzinfo=tzlocal() ), u'id': u'efbfce1c-c7d0-43be-a9a8-d18ad70e9d8b', u'message': u'old event' } ] ecs_service_events_2 = [ { u'createdAt': datetime.datetime( 2017, 3, 8, 12, 15, 46, 32000, tzinfo=tzlocal() ), u'id': u'03c1bf6b-4054-4828-adc0-edce84d96c99', u'message': u'(service aslive-grahamlyons-test) has reached a' }, { u'createdAt': datetime.datetime( 2017, 3, 8, 12, 15, 21, 649000, tzinfo=tzlocal() ), u'id': u'66364f80-7713-4260-a8d7-39ec8207f4a9', u'message': u'(service aslive-grahamlyons-test) registered 2 ' } ] ecs_event_iterator = ECSEventIterator(ANY, ANY, ANY, ANY) # When events_1 = ecs_event_iterator._get_new_ecs_service_events( { 'services': [ { 'events': ecs_service_events_1[:1] } ] }, since ) events_2 = ecs_event_iterator._get_new_ecs_service_events( { 'services': [ { 'events': ecs_service_events_1[:1] + ecs_service_events_2 } ] }, since ) # Then assert events_1 == ecs_service_events_1[:1] assert events_2 == list(reversed(ecs_service_events_2)) assert events_1 + events_2 == \ ecs_service_events_1[:1] + list(reversed(ecs_service_events_2))
def test_deployment_completed_after_previous_instances_stopped(self): cluster = 'dummy-cluster' service = 'dummy-service' taskdef = 'dummy-taskdef' boto_session = MagicMock(spec=Session) mock_ecs_client = Mock() def describe_services_generator(initial_running_count): for running_count in reversed(range(initial_running_count + 1)): yield { 'services': [ { 'clusterArn': 'arn:aws:ecs:eu-1:7:cstr/non-prod', 'deployments': [ { 'desiredCount': initial_running_count, 'createdAt': datetime.datetime( 2017, 1, 6, 10, 58, 9 ), 'id': 'ecs-svc/9223370553143707624', 'runningCount': initial_running_count, 'pendingCount': 0, 'status': 'PRIMARY', 'taskDefinition': taskdef, }, { 'desiredCount': initial_running_count, 'createdAt': datetime.datetime( 2017, 1, 6, 10, 58, 9 ), 'id': 'ecs-svc/9223370553143707624', 'pendingCount': 0, 'runningCount': running_count, 'status': 'ACTIVE', 'taskDefinition': taskdef, } ], 'loadBalancers': [ { 'containerName': 'app', 'containerPort': 8000, 'targetGroupArn': 'd035fe071cf0069f' } ], 'status': 'ACTIVE', 'taskDefinition': taskdef } ] } mock_ecs_client.describe_services.side_effect = \ describe_services_generator(2) boto_session.client.return_value = mock_ecs_client events = ECSEventIterator( cluster, service, taskdef, boto_session ) event_list = [e for e in events] assert len(event_list) == 3 assert not event_list[0].done assert event_list[0].running == 2 assert event_list[0].desired == 2 assert event_list[0].pending == 0 assert event_list[0].previous_running == 2 assert not event_list[1].done assert event_list[1].running == 2 assert event_list[1].desired == 2 assert event_list[1].pending == 0 assert event_list[1].previous_running == 1 assert event_list[2].done assert event_list[2].running == 2 assert event_list[2].desired == 2 assert event_list[2].pending == 0 assert event_list[2].previous_running == 0