예제 #1
0
파일: tasks.py 프로젝트: KnpLabs/kitipy
def test_generate_git_tgz(kctx: kitipy.Context, keep: bool):
    """(Re) Generate tests/git-archive.tgz.
    
    This command has been implemented and used to generate the original
    tests/git-archive.tgz file, which is used to test git-related helper
    functions.
    """
    tempdir = kctx.executor.mkdtemp()
    commands = ['cd ' + tempdir, 'git init']
    commits = (
        ('foo', 'v0.1'),
        ('bar', 'v0.2'),
        ('baz', 'v0.3'),
        ('pi', 'v0.4'),
        ('yolo', 'v0.5'),
    )

    for commit, tag in commits:
        commands.append('touch ' + commit)
        commands.append('git add ' + commit)
        commands.append('git commit -m "%s"' % (commit))
        commands.append('git tag %s HEAD' % (tag))

    basedir = os.path.dirname(os.path.abspath(__file__))
    tgz_path = os.path.join(basedir, 'tests', 'tasks', 'testdata',
                            'git-repo.tgz')
    commands.append('tar zcf %s .' % (tgz_path))

    try:
        kctx.run(' && '.join(commands))
    finally:
        if not keep:
            kctx.run('rm -rf %s' % (tempdir))
예제 #2
0
파일: ecs.py 프로젝트: KnpLabs/kitipy
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))
예제 #3
0
def run_playbook(kctx: Context,
                 inventory: str,
                 playbook: str,
                 hosts: Optional[Tuple[str]] = None,
                 tags: Optional[Tuple[str]] = None,
                 ask_become_pass: bool = False):
    """Run a given Ansible playbook using ``ansible-playbook``.

    Args:
        kctx (kitipy.Context):
            Context to use to run the playbook.
        inventory (str):
            Path to Ansible host inventory.
        playbook (str):
            Path to the Ansible playbook to run.
        hosts (Optional[Tuple[str]]):
            List of targeted hosts. Use None to target all hosts (default value).
        tags (Optional[Tuple[str]]):
            List of targeted tags. Use None to apply all the tags (default value).
        ask_become_pass (bool):
            Whether ``--ask-become-pass`` should be added to the ``ansible-playbook``
            command.
    """
    cmd = 'ansible-playbook -i %s' % (inventory)
    if hosts is not None and len(hosts) > 0:
        cmd += ' -l ' + ','.join(hosts)
    if tags is not None and len(tags) > 0:
        cmd += ' -t ' + ','.join(tags)
    if ask_become_pass:
        cmd += ' --ask-become-pass'

    cmd += ' ' + playbook

    kctx.local(cmd)
예제 #4
0
파일: tasks.py 프로젝트: KnpLabs/kitipy
def test_unit(kctx: kitipy.Context, report: bool, coverage: bool):
    # Be sure the SSH container used for tests purpose is up and running.
    # @TODO: add a common way to kitipy to wait for a port to be open
    kctx.invoke(kitipy.docker.tasks.up)

    expected_services = len(kctx.stack.config['services'])
    # @TODO: this won't work as is with Swarm, find how to generalize that sort of tests
    tester = lambda kctx: expected_services == kctx.stack.count_running_services(
    )
    kitipy.wait_for(tester,
                    interval=1,
                    max_checks=5,
                    label="Waiting for services start up...")

    # Host key might change if docker-compose down is used between two test run,
    # thus we start by removing any existing host key.
    kctx.local("ssh-keygen -R '[127.0.0.1]:2022' 1>/dev/null 2>&1")
    kctx.local("ssh-keygen -R '[127.0.0.1]:2023' 1>/dev/null 2>&1")
    kctx.local("ssh-keygen -R testhost 1>/dev/null 2>&1")

    # Ensure the private key has the right chmod or the task might fail.
    os.chmod("tests/.ssh/id_rsa", 0o0600)

    # Ensure first that we're actually able to connect to SSH hosts, or
    # tests will fail anyway.
    kctx.local('ssh -F tests/.ssh/config testhost /bin/true 1>/dev/null 2>&1')
    kctx.local('ssh -F tests/.ssh/config jumphost /bin/true 1>/dev/null 2>&1')
    kctx.local(
        'ssh -F tests/.ssh/config testhost-via-jumphost /bin/true 1>/dev/null 2>&1'
    )

    report_name = 'unit.xml' if report else None
    pytest(kctx, report_name, coverage, 'tests/unit/ -vv')
예제 #5
0
파일: tasks.py 프로젝트: KnpLabs/kitipy
def pytest(kctx: kitipy.Context, report_name: Optional[str], coverage: bool,
           cmd: str, **args):
    env = os.environ.copy()
    env['PYTHONPATH'] = os.getcwd()
    args.setdefault('env', env)

    basecmd = 'pytest'
    if report_name:
        if not kctx.path_exists('.test-results'):
            os.mkdir('.test-results')
        basecmd += ' --junitxml=.test-results/%s' % (report_name)
    if coverage:
        basecmd += ' --cov=kitipy/'

    kctx.local('%s %s' % (basecmd, cmd), **args)
예제 #6
0
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))
예제 #7
0
파일: tasks.py 프로젝트: KnpLabs/kitipy
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)
예제 #8
0
파일: secrets.py 프로젝트: KnpLabs/kitipy
def show(kctx: kitipy.Context, show_values: bool):
    """Show secrets stored by AWS Secrets Manager."""
    # @TODO: kctx.stack should be the raw config dict
    stack = kctx.config['stacks'][kctx.stack.name]
    secrets = stack['secrets_resolver'](kctx)

    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))

    client = sm.new_client()
    for secret_arn in secrets:
        secret = sm.describe_secret_with_current_value(client, secret_arn)
        kctx.echo("=================================")
        kctx.echo("ID: %s" % (secret["ARN"]))
        kctx.echo("Name: %s" % (secret["Name"]))
        kctx.echo("Value: %s\n" %
                  (format_secret_value(secret["SecretString"], show_values)))
예제 #9
0
파일: ecs.py 프로젝트: KnpLabs/kitipy
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)
예제 #10
0
def galaxy_install(kctx: Context,
                   dest: str,
                   file: str = 'galaxy-requirements.yml',
                   force: bool = False):
    """Install Ansible dependencies using ``ansible-galaxy install``.

    Args:
        kctx (kitipy.Context):
            Context to use to run ``ansible-galaxy``.
        dest (str):
            Directory path where dependencies should be installed.
        file (str):
            Requirements file (defaults to ``galaxy-requirements.yml``).
        force (bool):
            Whehter ``--force`` flag should be added to ``ansible-galaxy`` command.
    """
    kctx.run('ansible-galaxy install -r %s -p %s %s' % (
        file,
        dest,
        '--force' if force else '',
    ))
예제 #11
0
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."
        )
예제 #12
0
파일: tasks.py 프로젝트: KnpLabs/kitipy
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))
예제 #13
0
파일: ecs.py 프로젝트: KnpLabs/kitipy
def show_failed_containers(kctx: kitipy.Context,
                           task: mypy_boto3_ecs.type_defs.TaskTypeDef):
    containers = list(filter(lambda c: c["exitCode"] > 0, task["containers"]))

    if len(containers) == 0:
        kctx.echo("Containers with nonzero exit code: (None)")
        return

    kctx.echo("Containers with nonzero exit code:")
    for container in task["containers"]:
        if container["exitCode"] == 0:
            continue

        reason = "exit code: {0}".format(container["exitCode"]) + (
            " - " + container['reason'] if 'reason' in container else '')
        kctx.echo("  * {name}: {reason}".format(name=container["name"],
                                                reason=reason))
예제 #14
0
파일: tasks.py 프로젝트: KnpLabs/kitipy
def docker_tasks(kctx: kitipy.Context):
    if not isinstance(kctx.stack, stack.BaseStack):
        kctx.fail("No valid Docker stack available in kipity Context.")
예제 #15
0
파일: ecs.py 프로젝트: KnpLabs/kitipy
def show_task(kctx: kitipy.Context,
              task: mypy_boto3_ecs.type_defs.TaskTypeDef,
              image_tag: Optional[str] = None):
    kctx.echo("=================================")
    kctx.echo("Task ID: {0}".format(task_id_from_arn(task["taskArn"])))
    kctx.echo("Task definition: {0}".format(
        task_def_from_arn(task["taskDefinitionArn"])))

    if image_tag:
        kctx.echo("Image tag: {0}".format(image_tag))

    kctx.echo("CPU / Memory: {0} / {1}".format(task["cpu"], task["memory"]))
    kctx.echo("Last status / Desired status: {0} / {1}".format(
        task["lastStatus"], task["desiredStatus"]))

    if task["lastStatus"] == "RUNNING":
        kctx.echo("Started at: {0}".format(task["startedAt"].isoformat()))

    if task["lastStatus"] == "STOPPED":
        kctx.echo("Stopped at: {0}".format(task["stoppedAt"].isoformat()))
        kctx.echo("Reason: {0}".format(task["stoppedReason"]))
        show_failed_containers(kctx, task)

    kctx.echo("")  # Put an empty line between each task
예제 #16
0
파일: tasks.py 프로젝트: KnpLabs/kitipy
def lint(kctx: kitipy.Context):
    """Run mypy, a static type checker, to detect type errors."""
    kctx.local('mypy -p kitipy')
예제 #17
0
파일: secrets.py 프로젝트: KnpLabs/kitipy
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)
예제 #18
0
파일: tasks.py 프로젝트: KnpLabs/kitipy
def test_all(kctx: kitipy.Context, report: bool, coverage: bool):
    """Execute all the tests suites."""
    kctx.invoke(test_unit)
    kctx.invoke(test_tasks)