def deploy(kctx: kitipy.Context, version: str): """Deploy a given version to ECS.""" client = kitipy.libs.aws.ecs.new_client() stack = kctx.config["stacks"][kctx.stack.name] cluster_name = kctx.stage["ecs_cluster_name"] service_name = versioned_service_name(stack) service_def = stack["ecs_service_definition"](kctx) task_def = stack["ecs_task_definition"](kctx) task_def["containerDefinitions"] = stack["ecs_container_transformer"]( kctx, version) task_def_tags = task_def.get("tags", []) task_def_tags.append({'key': 'kitipy.image_tag', 'value': version}) task_def["tags"] = task_def_tags try: deployment_id = kitipy.libs.aws.ecs.upsert_service( client, cluster_name, service_name, task_def, service_def) except kitipy.libs.aws.ecs.ServiceDefinitionChangedError as err: kctx.fail( "Could not deploy the API: ECS service definition has " + "changed - {0}. You have to increment the version ".format(err) + "number in the ./tasks.py file before re-running this command.\n") for event in kitipy.libs.aws.ecs.watch_deployment(client, cluster_name, service_name, deployment_id): createdAt = event["createdAt"].isoformat() message = event["message"] kctx.info("[{createdAt}] {message}".format(createdAt=createdAt, message=message))
def ensure_tag_exists(kctx: kitipy.Context, tag: str): """Check if the given Git tag exists on both local copy and remote origin. This is mostly useful to ensure no invalid tag is going to be deployed. Args: kctx (kitipy.Context): Kitipy context. tag (str): Git tag to verify. Raises: ValueError: If the given Git tag does not exist either on local or remote origin. """ res = kctx.local( 'git ls-remote --exit-code --tags origin refs/tags/%s >/dev/null 2>&1' % (tag), check=False) if res.returncode != 0: kctx.fail("The given tag is not available on Git remote origin.") res = kctx.local( 'git ls-remote --exit-code --tags ./. refs/tags/%s >/dev/null 2>&1' % (tag), check=False) if res.returncode != 0: kctx.fail( "The given tag is not available in your local Git repo. Please fetch remote tags before running this task again." )
def edit(kctx: kitipy.Context, secret_name): """Edit secrets stored by AWS Secrets Manager.""" stack = kctx.config['stacks'][kctx.stack.name] secret_arn = stack['secret_arn_resolver'](kctx=kctx, secret_name=secret_name) client = sm.new_client() secret = sm.describe_secret_with_current_value(client, secret_arn) if secret == None: kctx.fail("Secret \"%s\" not found." % (secret_name)) value = click.edit(text=secret['SecretString']) if value == None: kctx.info("Secret value was not changed. Aborting.") raise click.exceptions.Abort() trim_question = ("Your secret value ends with a new line. This is " + "generally abnormal. Would you want to trim it " + "automatically?") if value.endswith("\n") and click.confirm(trim_question, default=True): value = value.rstrip("\n") kctx.echo(("NOTE: Secret values end with %s. This is here to help you " + "see invisible characters (e.g. whitespace, line breaks, " + "etc...).\n") % (secret_delimiter)) kctx.echo("ID: %s" % (secret["ARN"])) kctx.echo("Name: %s" % (secret["Name"])) kctx.echo("Previous value: %s" % (format_secret_value(secret["SecretString"], True))) kctx.echo("New value: %s" % (format_secret_value(value, True))) click.confirm("\nDo you confirm this change?", abort=True) sm.put_secret_value(client, secret["ARN"], value)
def ensure_tag_is_recent(kctx: kitipy.Context, tag: str, last: int = 5): """Check if the given Git tag is recent enough (by default, one of the last five). Args: kctx (kitipy.Context): Kitipy Context. tag (str): Tag to look for. """ res = kctx.local( "git for-each-ref --format='%%(refname:strip=2)' --sort=committerdate 'refs/tags/*' 2>/dev/null | tail -n%d | grep %s >/dev/null 2>&1" % (last, tag), check=False, ) if res.returncode != 0: kctx.fail( 'This tag seems too old: at least %d new tags have been released since %s.' % (last, tag))
def format(kctx: kitipy.Context, show_diff, fix): """Run yapf to detect style divergences and fix them.""" if not show_diff and not fix: kctx.fail( "You can't use both --no-diff and --no-fix at the same time.") confirm_msg = 'Do you want to reformat your code using yapf?' dry_run = lambda: kctx.local('yapf --diff -r kitipy/ tests/ tasks*.py', check=False) apply = lambda: kctx.local('yapf -vv -p -i -r kitipy/ tests/ tasks*.py') kitipy.confirm_and_apply(dry_run, confirm_msg, apply, show_dry_run=show_diff, ask_confirm=fix is None, should_apply=fix if fix is not None else True)
def run(kctx: kitipy.Context, container: str, command: List[str], version: Optional[str]): """Run a given command in a oneoff task.""" client = kitipy.libs.aws.ecs.new_client() stack = kctx.config["stacks"][kctx.stack.name] cluster_name = kctx.stage["ecs_cluster_name"] service_name = versioned_service_name(stack) task_def = stack["ecs_task_definition"](kctx) if version is None: regular_task_def = kitipy.libs.aws.ecs.get_task_definition( client, task_def["family"]) version = next((tag["value"] for tag in regular_task_def["tags"] if tag["key"] == "kitipy.image_tag"), None) if version is None: kctx.fail( "No --version flag was provided and no deployments have been found." ) task_name = "{0}-{1}".format(service_name, "-".join(command)) containers = stack["ecs_container_transformer"](kctx, version) containers = stack["ecs_oneoff_container_transformer"](kctx, containers, container) run_args = stack["ecs_service_definition"](kctx) run_args = { k: v for k, v in run_args.items() if k not in ["desiredCount", "loadBalancers"] } task_def["family"] = task_def["family"] + "-oneoff" task_def["containerDefinitions"] = list(containers) task_arn = kitipy.libs.aws.ecs.run_oneoff_task(client, cluster_name, task_name, task_def, container, command, run_args) task = kitipy.libs.aws.ecs.wait_until_task_stops(client, cluster_name, task_arn) show_task(kctx, task)
def validate_tag(kctx: kitipy.Context, image_ref: str): """Check if the given image reference exists on a remote Docker registry. Args: kctx (kitipy.Context): The current kitipy Context. image_ref (str): A full image reference composed of: the repository to check, the base image name and the image tag. Raises: click.Exception: When the given image tag doesn't exist. """ if len(image_ref) == 0: kctx.fail( "No image tag provided. You can provide it through --tag flag or IMAGE_TAG env var." ) images = (service['image'] for service in kctx.stack.config['services']) for image in images: result = actions.buildx_imagetools_inspect(image_ref, _check=False) if result.returncode != 0: kctx.fail('Image %s not found on remote registry.' % (image_ref))
def docker_tasks(kctx: kitipy.Context): if not isinstance(kctx.stack, stack.BaseStack): kctx.fail("No valid Docker stack available in kipity Context.")