ecsDeploy

ecsDeploy.py

Deploy a new task definition to an existing ECS Cluster/Service

NOTE 1: This is NOT blue/green. Look at ecsBlueGreenDeploy.py NOTE 2: This will check if the clusterArn/serviceArn are NOT aws arns and instead use them as ssm parameter names to grab the values

tag: String (Optional) - Will use release.get_version() as default clusterArn: String (Optional) Will use ECS_CLUSTER_ARN from os.environ as default serviceArn: String (Optional) Will use ECS_SERVICE_ARN from os.environ as default

  1#!/usr/bin/env python3
  2"""
  3ecsDeploy.py
  4
  5Deploy a new task definition to an existing ECS Cluster/Service
  6
  7NOTE 1: This is NOT blue/green. Look at `ecsBlueGreenDeploy.py`
  8NOTE 2: This will check if the clusterArn/serviceArn are NOT aws arns and instead
  9        use them as ssm parameter names to grab the values
 10
 11tag: String (Optional) - Will use `release.get_version()` as default
 12clusterArn: String (Optional) Will use ECS_CLUSTER_ARN from os.environ as default
 13serviceArn: String (Optional) Will use ECS_SERVICE_ARN from os.environ as default
 14"""
 15from utils import aws, release
 16import argparse
 17import os
 18import sys
 19
 20if __name__ == "__main__":
 21    print("ecsDeploy.__main__(): BEGIN")
 22
 23    #
 24    # ArgParse
 25    #
 26    parser = argparse.ArgumentParser(description='Deploy a new task definition to an existing ECS Cluster/Service')
 27
 28    parser.add_argument(
 29        '-t',
 30        '--tag',
 31        action='store',
 32        type=str,
 33        required=False,
 34        default=release.get_version(),
 35        help='container tag: Defaults to aws.get_version()')
 36
 37    parser.add_argument(
 38        '-C',
 39        '--cluster',
 40        action='store',
 41        type=str,
 42        required=False,
 43        default=os.environ.get('ECS_CLUSTER_ARN', None),
 44        help='Cluster ARN to deploy container into. Defaults to ENV ECS_CLUSTER_ARN')
 45
 46    parser.add_argument(
 47        '-s',
 48        '--service',
 49        action='store',
 50        type=str,
 51        required=False,
 52        default=os.environ.get('ECS_SERVICE_ARN', None),
 53        help='Service ARN to deploy container into. Defaults to ENV ECS_SERVICE_ARN')
 54
 55    args = parser.parse_args()
 56
 57    _TAG = args.tag
 58    _CLUSTER_ARN = args.cluster
 59    _SERVICE_ARN = args.service
 60
 61    if _TAG is None or _CLUSTER_ARN is None or _SERVICE_ARN is None:
 62        sys.exit("ecsDeploy(): Tage, Cluster or Service is None. Please ensure arguments or environment variables are set.")
 63
 64    """
 65    If the _CLUSTER_ARN or _SERVICE_ARN are not in ARN format, consider them SSM Param names
 66    and use them to grab the ARNS out of SSM Params
 67    """
 68    _CLUSTER = _CLUSTER_ARN if ':' in _CLUSTER_ARN else aws.ssm_get_parameter(name=_CLUSTER_ARN)
 69    _SERVICE = _SERVICE_ARN if ':' in _SERVICE_ARN else aws.ssm_get_parameter(name=_SERVICE_ARN)
 70
 71    """
 72    go get the entire task definition for a service by name (might need the cluster too)
 73    """
 74    print("ecsDeploy(): Looking up latest task definition for cluster/service")
 75    current_task_definition_arn = aws.ecs_get_latest_task_definition_arn(cluster=_CLUSTER, service=_SERVICE)
 76    print("ecsDeploy(): Storing the entire current task definition for rollback")
 77    current_task_definition = aws.ecs_get_task_definition_from_arn(task_def_arn=current_task_definition_arn)
 78
 79    """
 80    This iterates through all current_task_definition.containers
 81    then for each container -> iterate through container.secrets
 82    for each secret -> find one where the key is "VERSION"
 83    if found, return the value which will be an ssm param arn
 84    """
 85    version_ssm_param_arn = aws.ecs_get_version_param_name_from_task_def(task_def=current_task_definition)
 86
 87    """
 88    Get the currently deployed version number
 89    """
 90    old_version = aws.ssm_get_parameter(name=version_ssm_param_arn)
 91
 92    """
 93    set the new version provided by the caller
 94    """
 95    new_version = _TAG
 96
 97    """
 98    This iterates over the containers again to set
 99    the new image in the container where
100    we simply get the old image and replace the :{tag}
101    before: docker.devops.rekor.io/blue/api:12345
102    after: docker.devops.rekor.io/blue/api:$newVersion
103    """
104    new_task_definition = aws.ecs_set_new_image_in_task_def(task_def=current_task_definition, version=new_version)
105
106    """
107    Go register the next task def
108    there should now be a new version of the task def
109    """
110    new_task_definition_arn = aws.ecs_register_task_definition_revision(task_def=new_task_definition)
111
112    """
113    update the ssm param with the new tag.
114    This function should fail gracefully as not all appilcations use an SSM param
115    to store its version. Scout uses the git commit hash for version awareness.
116    """
117    aws.ssm_put_parameter(name=version_ssm_param_arn, value=_TAG)
118
119    """
120    deploy new task def to the service
121    """
122    aws.ecs_deploy_new_task_definition(cluster=_CLUSTER, service=_SERVICE, task_def_arn=new_task_definition_arn)
123
124    deploy_status = aws.ecs_wait_services_stable(cluster=_CLUSTER, service=_SERVICE)
125    if not deploy_status:
126        print("ecsDeploy(): Deploy FAILED! Rolling back to original task def!")
127        # Roll back procedures by rolling back the version param and setting the service back to the original task def
128        aws.ssm_put_parameter(name=version_ssm_param_arn, value=old_version)
129        aws.ecs_deploy_new_task_definition(cluster=_CLUSTER, service=_SERVICE, task_def_arn=current_task_definition_arn)
130        deploy_status = aws.ecs_wait_services_stable(cluster=_CLUSTER, service=_SERVICE)
131        if not deploy_status:
132            sys.exit("ecsDeploy(): Rolling back to original task def failed!")
133
134        aws.ecs_deregister_task_def(task_def=new_task_definition_arn)
135        sys.exit("ecsDeploy(): Deploy Failed! Rolled back to original task def.")
136
137    print("ecsDeploy(): Deploy Successful")
138
139    sys.exit(0)