def deploy_new_version(region, cluster_name, ecs_service_name, deploy_version_tag, service_name, sample_env_file_path, env_name, color='white', complete_image_uri=None): env_config = build_config(env_name, service_name, sample_env_file_path) client = EcsClient(None, None, region) deployment = DeployAction(client, cluster_name, ecs_service_name) if deployment.service.desired_count == 0: desired_count = 1 else: desired_count = deployment.service.desired_count deployment.service.set_desired_count(desired_count) task_definition = deployment.get_current_task_definition( deployment.service) if complete_image_uri is not None: container_name = task_definition['containerDefinitions'][0]['name'] task_definition.set_images(deploy_version_tag, **{container_name: complete_image_uri}) else: task_definition.set_images(deploy_version_tag) for container in task_definition.containers: task_definition.apply_container_environment(container, env_config) print_task_diff(ecs_service_name, task_definition.diff, color) new_task_definition = deployment.update_task_definition(task_definition) response = deploy_and_wait(deployment, new_task_definition, color) if response: log_bold(ecs_service_name + " Deployed successfully.") else: log_err(ecs_service_name + " Deployment failed.") return response
def _fetch_current_desired_count(self): stack_name = get_service_stack_name(self.env, self.application_name) self.desired_counts = {} try: stack = region_service.get_client_for( 'cloudformation', self.env ).describe_stacks(StackName=stack_name)['Stacks'][0] ecs_service_outputs = filter( lambda x: x['OutputKey'].endswith('EcsServiceName'), stack['Outputs'] ) ecs_service_names = [] for service_name in ecs_service_outputs: ecs_service_names.append({ "key": service_name['OutputKey'], "value": service_name['OutputValue'] }) ecs_client = EcsClient(None, None, self.region) for service_name in ecs_service_names: deployment = DeployAction( ecs_client, self.cluster_name, service_name["value"] ) actual_service_name = service_name["key"]. \ replace("EcsServiceName", "") self.desired_counts[actual_service_name] = deployment. \ service.desired_count log("Existing service counts: " + str(self.desired_counts)) except Exception: log_bold("Could not find existing services.")
def deploy_new_version(cluster_name, ecs_service_name, ecs_service_logical_name, deployment_identifier, service_name, sample_env_file_path, timeout_seconds, env_name, secrets_name, service_configuration, region, ecr_image_uri, color='white'): client = EcsClient(None, None, region) task_definition = create_new_task_definition( color=color, ecr_image_uri=ecr_image_uri, ecs_service_name=ecs_service_name, env_name=env_name, sample_env_file_path=sample_env_file_path, secrets_name=secrets_name, service_name=service_name, client=client, cluster_name=cluster_name, deployment_identifier=deployment_identifier, ecs_service_logical_name=ecs_service_logical_name, service_configuration=service_configuration, region=region, ) deploy_task_definition(client, task_definition, cluster_name, ecs_service_name, color, timeout_seconds, 'Deploy')
def test_get_task_definition_by_deployment_identifier_with_next_token( self, mock_session): cluster_name = "cluster-1" service_name = MagicMock() service_name.task_definition.return_value.family.return_value = "prodServiceAFamily" mock_boto_client = MagicMock() mock_session.return_value.client.return_value = mock_boto_client client = EcsClient() mock_boto_client.list_task_definitions.side_effect = [ { 'taskDefinitionArns': ['arn1', 'arn2'], 'nextToken': 'token1' }, { 'taskDefinitionArns': ['arn3', 'arn3'] }, ] def mock_describe_task_definition(taskDefinition, include): if taskDefinition == 'arn3': return { 'taskDefinition': { 'taskDefinitionArn': taskDefinition, 'family': 'tdFamily' }, 'tags': [ { 'key': 'deployment_identifier', 'value': 'id-0' }, ] } else: return { 'taskDefinition': { 'taskDefinitionArn': taskDefinition, 'family': 'tdFamily' }, 'tags': {} } mock_boto_client.describe_task_definition.side_effect = mock_describe_task_definition action = EcsAction(client, cluster_name, service_name) actual_td = action.get_task_definition_by_deployment_identifier( service=service_name, deployment_identifier="id-0") self.assertEqual('arn3', actual_td.arn) self.assertEqual({'deployment_identifier': 'id-0'}, actual_td.tags) mock_boto_client.list_task_definitions.assert_has_calls([ call(familyPrefix='tdFamily', status='ACTIVE', sort='DESC'), call(familyPrefix='tdFamily', status='ACTIVE', sort='DESC', nextToken='token1') ])
def revert_deployment(cluster_name, ecs_service_name, color, timeout_seconds, deployment_identifier, region, **kwargs): client = EcsClient(None, None, region) deployment = DeployAction(client, cluster_name, ecs_service_name) previous_task_defn = deployment.get_task_definition_by_deployment_identifier( deployment.service, deployment_identifier) deploy_task_definition(client, previous_task_defn, cluster_name, ecs_service_name, color, timeout_seconds, 'Revert')
def fetch_current_desired_count(self): desired_counts = {} try: deployment_ecs_client = EcsClient( None, None, get_region_for_environment(self.environment)) for logical_service_name, service_config in self.service_info.items( ): deployment = DeployAction(deployment_ecs_client, self.cluster_name, service_config["ecs_service_name"]) desired_counts[ logical_service_name] = deployment.service.desired_count log("Existing service counts: " + str(desired_counts)) except Exception: pass return desired_counts
def fetch_current_desired_count(self): desired_counts = {} try: deployment_ecs_client = EcsClient( None, None, get_region_for_environment(self.environment)) for logical_service_name, service_config in self.service_info.items( ): deployment = DeployAction(deployment_ecs_client, self.cluster_name, service_config["ecs_service_name"]) if deployment.service: desired_counts[ logical_service_name] = deployment.service.desired_count log("Existing service counts: " + str(desired_counts)) except Exception as e: raise UnrecoverableException( "Could not find existing services. {}".format(e)) return desired_counts
def run(self): log_warning("Deploying to {self.region}".format(**locals())) self.init_stack_info() if not os.path.exists(self.env_sample_file): raise UnrecoverableException('env.sample not found. Exiting.') log_intent("name: " + self.name + " | environment: " + self.environment + " | version: " + str(self.version)) log_bold("Checking image in ECR") self.upload_artefacts() log_bold("Initiating deployment\n") ecs_client = EcsClient(None, None, self.region) jobs = [] for index, service_name in enumerate(self.ecs_service_names): log_bold("Starting to deploy " + service_name) color = DEPLOYMENT_COLORS[index % 3] image_url = self.ecr_image_uri image_url += (':' + self.version) process = multiprocessing.Process( target=deployer.deploy_new_version, args=( ecs_client, self.cluster_name, service_name, self.version, self.name, self.env_sample_file, self.environment, color, image_url ) ) jobs.append(process) process.start() exit_codes = [] while True: sleep(1) exit_codes = [proc.exitcode for proc in jobs] if None not in exit_codes: break if any(exit_codes) != 0: raise UnrecoverableException("Deployment failed")
def run(self): log_warning("Deploying to {self.region}".format(**locals())) if not os.path.exists(self.env_sample_file): raise UnrecoverableException('env.sample not found. Exiting.') log_intent("name: " + self.name + " | environment: " + self.environment + " | version: " + str(self.version) + " | deployment_identifier: " + self.deployment_identifier) log_bold("Checking image in ECR") self.ecr.upload_artefacts() log_bold("Initiating deployment\n") ecs_client = EcsClient(None, None, self.region) image_url = self.ecr.image_uri target = deployer.deploy_new_version kwargs = dict(client=ecs_client, cluster_name=self.cluster_name, service_name=self.name, sample_env_file_path=self.env_sample_file, timeout_seconds=self.timeout_seconds, env_name=self.environment, ecr_image_uri=image_url, deployment_identifier=self.deployment_identifier, ) self.run_job_for_all_services("Deploy", target, kwargs)
def test_get_task_definition_by_deployment_identifier_with_no_matches( self, mock_session): cluster_name = "cluster-1" service_name = MagicMock() service_name.task_definition.return_value.family.return_value = "stgServiceAFamily" mock_boto_client = MagicMock() mock_session.return_value.client.return_value = mock_boto_client client = EcsClient() mock_boto_client.list_task_definitions.side_effect = [ { 'taskDefinitionArns': ['arn1', 'arn2'], 'nextToken': 'token1' }, { 'taskDefinitionArns': ['arn3', 'arn3'] }, ] def mock_describe_task_definition(taskDefinition, include): return { 'taskDefinition': { 'taskDefinitionArn': taskDefinition, 'family': 'tdFamily' }, 'tags': {} } mock_boto_client.describe_task_definition.side_effect = mock_describe_task_definition action = EcsAction(client, cluster_name, service_name) with self.assertRaises(UnrecoverableException) as error: action.get_task_definition_by_deployment_identifier( service=service_name, deployment_identifier="id-0") self.assertEqual( "task definition does not exist for deployment_identifier: id-0", error.exception.value)
def revert(self): target = deployer.revert_deployment ecs_client = EcsClient(None, None, self.region) kwargs = dict(client=ecs_client, cluster_name=self.cluster_name, timeout_seconds=self.timeout_seconds, deployment_identifier=self.deployment_identifier) self.run_job_for_all_services("Revert", target, kwargs)