Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[run] images, tag, role and exclusive env/scecret #83

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions ecs_deploy/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ def deploy(cluster, service, tag, image, command, env, secret, role, execution_r
success_message='Deployment successful',
failure_message='Deployment failed',
timeout=timeout,
deregister=deregister,
previous_task_definition=td,
task_definition_to_deregister=deregister and td,
ignore_warnings=ignore_warnings,
sleep_time=sleep_time
)
Expand Down Expand Up @@ -297,9 +296,12 @@ def scale(cluster, service, desired_count, access_key_id, secret_access_key, reg
@click.argument('cluster')
@click.argument('task')
@click.argument('count', required=False, default=1)
@click.option('-t', '--tag', help='Changes the tag for ALL container images')
@click.option('-i', '--image', type=(str, str), multiple=True, help='Overwrites the image for a container: <container> <image>')
@click.option('-c', '--command', type=(str, str), multiple=True, help='Overwrites the command in a container: <container> <command>')
@click.option('-e', '--env', type=(str, str, str), multiple=True, help='Adds or changes an environment variable: <container> <name> <value>')
@click.option('-s', '--secret', type=(str, str, str), multiple=True, help='Adds or changes a secret environment variable from the AWS Parameter Store (Not available for Fargate): <container> <name> <parameter name>')
@click.option('-r', '--role', type=str, help='Sets the task\'s role ARN: <task role ARN>')
@click.option('--launchtype', type=click.Choice([LAUNCH_TYPE_EC2, LAUNCH_TYPE_FARGATE]), default=LAUNCH_TYPE_EC2, help='ECS Launch type (default: EC2)')
@click.option('--subnet', type=str, multiple=True, help='A subnet ID to launch the task within. Required for launch type FARGATE (multiple values possible)')
@click.option('--securitygroup', type=str, multiple=True, help='A security group ID to launch the task within. Required for launch type FARGATE (multiple values possible)')
Expand All @@ -309,7 +311,10 @@ def scale(cluster, service, desired_count, access_key_id, secret_access_key, reg
@click.option('--secret-access-key', help='AWS secret access key')
@click.option('--profile', help='AWS configuration profile name')
@click.option('--diff/--no-diff', default=True, help='Print what values were changed in the task definition')
def run(cluster, task, count, command, env, secret, launchtype, subnet, securitygroup, public_ip, region, access_key_id, secret_access_key, profile, diff):
@click.option('--exclusive-env', is_flag=True, default=False, help='Set the given environment variables exclusively and remove all other pre-existing env variables from all containers')
@click.option('--exclusive-secrets', is_flag=True, default=False, help='Set the given secrets exclusively and remove all other pre-existing secrets from all containers')
@click.option('--deregister/--no-deregister', default=True, help='Deregister or keep the old task definition (default: --deregister)')
def run(cluster, task, count, tag, image, command, env, secret, role, launchtype, subnet, securitygroup, public_ip, region, access_key_id, secret_access_key, profile, diff, exclusive_env, exclusive_secrets, deregister):
"""
Run a one-off task.

Expand All @@ -318,18 +323,24 @@ def run(cluster, task, count, command, env, secret, launchtype, subnet, security
TASK is the name of your task definition (e.g. 'my-task') within ECS.
COUNT is the number of tasks your service should run.
"""
should_create_task_definition = image or tag or role
try:
client = get_client(access_key_id, secret_access_key, region, profile)
action = RunAction(client, cluster)

td = action.get_task_definition(task)
td = td_old = action.get_task_definition(task)
td.set_images(tag, **{key: value for (key, value) in image})
td.set_commands(**{key: value for (key, value) in command})
td.set_environment(env)
td.set_secrets(secret)
td.set_environment(env, exclusive_env)
td.set_secrets(secret, exclusive_secrets)
td.set_role_arn(role)

if diff:
print_diff(td, 'Using task definition: %s' % task)

if should_create_task_definition:
td = create_task_definition(action, td)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you create a new task definition here, I think it should be deregistered, after the task had been successfully executed. Otherwise you end up with n active task definitions in ECS. What do you think? As in the deploy action, this could be controllable via a cli parameter


action.run(td, count, 'ECS Deploy', launchtype, subnet, securitygroup, public_ip)

click.secho(
Expand All @@ -344,6 +355,9 @@ def run(cluster, task, count, command, env, secret, launchtype, subnet, security
click.secho('- %s' % started_task['taskArn'], fg='green')
click.secho(' ')

if should_create_task_definition and deregister:
deregister_task_definition(action, td_old)

except EcsError as e:
click.secho('%s\n' % str(e), fg='red', err=True)
exit(1)
Expand Down Expand Up @@ -433,9 +447,8 @@ def wait_for_finish(action, timeout, title, success_message, failure_message,
click.secho('\n%s\n' % success_message, fg='green')


def deploy_task_definition(deployment, task_definition, title, success_message,
failure_message, timeout, deregister,
previous_task_definition, ignore_warnings, sleep_time):
def deploy_task_definition(deployment, task_definition, title, success_message, failure_message, timeout,
ignore_warnings, sleep_time, task_definition_to_deregister=None):
click.secho('Updating service')
deployment.deploy(task_definition)

Expand All @@ -456,8 +469,8 @@ def deploy_task_definition(deployment, task_definition, title, success_message,
sleep_time=sleep_time
)

if deregister:
deregister_task_definition(deployment, previous_task_definition)
if task_definition_to_deregister:
deregister_task_definition(deployment, task_definition_to_deregister)


def get_task_definition(action, task):
Expand Down Expand Up @@ -501,8 +514,7 @@ def rollback_task_definition(deployment, old, new, timeout=600, sleep_time=1):
success_message='Rollback successful',
failure_message='Rollback failed. Please check ECS Console',
timeout=timeout,
deregister=True,
previous_task_definition=new,
task_definition_to_deregister=new,
ignore_warnings=False,
sleep_time=sleep_time
)
Expand Down
239 changes: 234 additions & 5 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,32 +623,110 @@ def test_run_task(get_client, runner):
assert result.exit_code == 0

assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u'Successfully deregistered revision: 2' not in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_task_with_command(get_client, runner):
def test_run_with_role_arn_deregister_old_task_definiion(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-c', 'webserver', 'date'))
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task:1', '2', '-r', 'arn:new:role'))
assert result.exit_code == 0
assert not result.exception
assert u"Using task definition: test-task" in result.output
assert u'Changed role_arn to: "arn:new:role" (was: "arn:test:role:1")' in result.output
assert u"Creating new task definition revision" in result.output
assert u'Successfully created revision: 2' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u'Successfully deregistered revision: 1' in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_with_role_arn_keep_old_task_definiion(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task:1', '2', '-r', 'arn:new:role', '--no-deregister'))
assert result.exit_code == 0
assert not result.exception
assert u"Using task definition: test-task" in result.output
assert u'Changed role_arn to: "arn:new:role" (was: "arn:test:role:1")' in result.output
assert u"Creating new task definition revision" in result.output
assert u'Successfully created revision: 2' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u'Successfully deregistered revision: 1' not in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_new_tag(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-t', 'latest'))
assert result.exit_code == 0
assert not result.exception
assert u"Using task definition: test-task" in result.output
assert u"Creating new task definition revision" in result.output
assert u'Changed image of container "webserver" to: "webserver:latest" (was: "webserver:123")' in result.output
assert u'Changed image of container "application" to: "application:latest" (was: "application:123")' in result.output
assert u'Successfully created revision: 2' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_one_new_image(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-i', 'application', 'application:latest'))
assert result.exit_code == 0
assert not result.exception
assert u"Using task definition: test-task" in result.output
assert u'Changed command of container "webserver" to: "date" (was: "run")' in result.output
assert u"Creating new task definition revision" in result.output
assert u'Changed image of container "application" to: "application:latest" (was: "application:123")' in result.output
assert u'Successfully created revision: 2' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_task_with_environment_var(get_client, runner):
def test_run_two_new_images(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'application', 'foo', 'bar'))
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-i', 'application', 'application:latest',
'-i', 'webserver', 'webserver:latest'))
assert result.exit_code == 0
assert not result.exception
assert u"Using task definition: test-task" in result.output
assert u"Creating new task definition revision" in result.output
assert u'Changed image of container "webserver" to: "webserver:latest" (was: "webserver:123")' in result.output
assert u'Changed image of container "application" to: "application:latest" (was: "application:123")' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_one_new_command(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-c', 'application', 'date'))
assert result.exit_code == 0
assert not result.exception
assert u"Using task definition: test-task" in result.output
assert u'Changed command of container "application" to: "date" (was: "run")' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_one_new_environment_variable(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'application', 'foo', 'bar'))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "foo" of container "application" to: "bar"' in result.output
Expand All @@ -657,6 +735,157 @@ def test_run_task_with_environment_var(get_client, runner):
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_change_environment_variable_empty_string(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'application', 'foo', ''))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "foo" of container "application" to: ""' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_new_empty_environment_variable(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'application', 'new', ''))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "new" of container "application" to: ""' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_empty_environment_variable_again(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'webserver', 'empty', ''))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" not in result.output
assert u'Changed environment' not in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_previously_empty_environment_variable_with_value(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'webserver', 'empty', 'not-empty'))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "empty" of container "webserver" to: "not-empty"' in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_exclusive_environment(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'webserver', 'new-env', 'new-value', '--exclusive-env'))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" in result.output
assert u'Changed environment "new-env" of container "webserver" to: "new-value"' in result.output

assert u'Removed environment "foo" of container "webserver"' in result.output
assert u'Removed environment "lorem" of container "webserver"' in result.output

assert u'Removed secret' not in result.output

assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_exclusive_secret(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-s', 'webserver', 'new-secret', 'new-place', '--exclusive-secrets'))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" in result.output
assert u'Changed secret "new-secret" of container "webserver" to: "new-place"' in result.output

assert u'Removed secret "baz" of container "webserver"' in result.output
assert u'Removed secret "dolor" of container "webserver"' in result.output

assert u'Removed environment' not in result.output

assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_one_new_secret_variable(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2',
'-s', 'application', 'baz', 'qux',
'-s', 'webserver', 'baz', 'quux'))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" in result.output
assert u'Changed secret "baz" of container "application" to: "qux"' in result.output
assert u'Changed secret "baz" of container "webserver" to: "quux"' in result.output
assert u'Changed secret "dolor" of container "webserver" to: "sit"' not in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_without_changing_environment_value(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-e', 'webserver', 'foo', 'bar'))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" not in result.output
assert u'Changed environment' not in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_without_changing_secrets_value(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
result = runner.invoke(cli.run, (CLUSTER_NAME, 'test-task', '2', '-s', 'webserver', 'baz', 'qux'))

assert result.exit_code == 0
assert not result.exception

assert u"Using task definition: test-task" not in result.output
assert u'Changed secrets' not in result.output
assert u"Successfully started 2 instances of task: test-task:2" in result.output
assert u"- arn:foo:bar" in result.output
assert u"- arn:lorem:ipsum" in result.output


@patch('ecs_deploy.cli.get_client')
def test_run_task_without_diff(get_client, runner):
get_client.return_value = EcsTestClient('acces_key', 'secret_key')
Expand Down