Exemplo n.º 1
0
def build(app_name: str, repository: str, app_config: dict) -> str:
    """Build the docker image of the last version of the app"""
    image_tag = git.get_last_tag(repository)

    Logger.info(
        {"tag": f"{app_name}@{image_tag}"}, "Application name and version",
    )

    if has_docker_image(app_name, image_tag):
        Logger.info({}, "Docker image already built (skipped)")
        return image_tag

    commit_hash = git.get_commit_hash_from_tag(repository, image_tag)
    build_variables = app_config.get("docker", {}).get("build", {}).get("variables", {})
    build_variables["COMMIT_HASH"] = commit_hash

    # Application build environment variables:
    builds_args = []
    for key, value in build_variables.items():
        builds_args.append(f"--build-arg {key}={value}")

    builds_args_str = " ".join(builds_args)

    command = f"docker build --tag {app_name}:{image_tag} {builds_args_str} {repository}"

    Logger.debug({"command": command}, "Docker build command")

    try:
        io.execute(command)
    except Exception as err:
        Logger.error({"err": err}, "Error while building Docker image")
        raise err

    return image_tag
Exemplo n.º 2
0
def branch(repository_dir: str, branch_name: str) -> None:
    """Checkout a branch of a repository"""
    Logger.debug({"path": repository_dir}, "[git#branch] Repository path")

    exists = is_branch_existing(repository_dir, branch_name)

    io.execute(f'git checkout{"" if exists else " -b"} {branch_name}',
               repository_dir)
Exemplo n.º 3
0
    def test_execute_should_raise_if_failure(self, subprocess_run_mock):
        subprocess_run_mock.return_value.returncode = 1
        subprocess_run_mock.return_value.stderr.decode.return_value = "An error message\n"

        with self.assertRaises(RuntimeError) as context:
            io.execute("a command with --arg1 arg-value")

        self.assertEqual(str(context.exception), "An error message")
Exemplo n.º 4
0
def test_execute(mocker):
    mocker.patch.object(subprocess, "run")

    io.execute("a command with --arg1 arg-value")

    subprocess.run.assert_called_with(
        ["a", "command", "with", "--arg1", "arg-value"],
        check=True,
        cwd=None,
        stdout=-1)
Exemplo n.º 5
0
def branch(repository_dir, branch_name):
    """Checkout a branch of a repository"""
    Logger.debug({"path": repository_dir}, "[git#branch] Repository path")

    stdout = io.execute(f"git branch --list {branch_name}", repository_dir)

    exists = len(stdout) > 0

    io.execute(f'git checkout{"" if exists else " -b"} {branch_name}',
               repository_dir)
Exemplo n.º 6
0
def update_repository(repository_dir, git_url, revision="origin/master"):
    """Get the latest version of a revision or clone the repository"""
    should_clone = True

    if io.exists(repository_dir):
        # If the target directory already exists
        remote_url = None

        try:
            remote_url = get_remote_url(repository_dir)
        except Exception:  # pylint: disable=broad-except
            # If the directory is not a git repository, `get_remote_url` will throw
            pass

        if remote_url == git_url:
            # If the remotes are the same, clean and fetch
            io.execute("git clean -dfx", repository_dir)
            io.execute("git fetch --all", repository_dir)

            # No need to clone
            should_clone = False
        else:
            # If the remotes mismatch, remove the old one
            io.remove(repository_dir)

    if should_clone:
        io.execute(f"git clone {git_url} {repository_dir}")
    io.execute(f"git reset --hard {revision}", repository_dir)
Exemplo n.º 7
0
def change_environment(environment: str,
                       config_path=Configuration.get_config_path()):
    """Change the environment (branch) of the configuration"""
    io.execute("git stash", config_path)
    io.execute("git fetch origin", config_path)
    io.execute(f"git checkout {environment}", config_path)
    io.execute(f"git reset --hard origin/{environment}", config_path)
Exemplo n.º 8
0
def tag(repository_dir: str,
        tag_name: str,
        tag_message: str = "NESTOR_AUTO_TAG") -> str:
    """Add a tag to the repository"""
    commit_hash = get_last_commit_hash(repository_dir)

    final_tag = f"{tag_name}-sha-{commit_hash}"

    if not semver.VersionInfo.isvalid(final_tag):
        raise RuntimeError(f'Invalid version tag: "{final_tag}".')

    io.execute(f"git tag -a {final_tag} {commit_hash} -m '{tag_message}'",
               repository_dir)

    return final_tag
Exemplo n.º 9
0
def push(app_name: str, image_tag: str, app_config: dict) -> None:
    """Push an image to the configured docker registry"""
    if not has_docker_image(app_name, image_tag):
        raise RuntimeError("Docker image not available")

    # This will need to be done a bit differently to work with other registries (GCP)
    # -> the config schema currently expects
    #   {docker: {registries: {[name: string]: {id: string, organization: string}[]}}}
    registry = app_config["docker"]["registries"]["docker.com"][0]

    # Create the tag
    image = get_registry_image_tag(app_name, image_tag, registry)

    io.execute(f"docker tag {app_name}:{image_tag} {image}")

    io.execute(f"docker push {image}")
Exemplo n.º 10
0
def tag(repository_dir, app_name, tag_name):
    """Add a tag to the repository"""
    app_config = config.get_app_config(app_name)

    # Move to the corresponding environment branch
    branch(repository_dir, app_config.get("workflow")[0])

    # Extract the last commit hash:
    commit_hash = get_last_commit_hash(repository_dir)

    final_tag = f"{tag_name}-sha-{commit_hash}"

    if not semver.VersionInfo.isvalid(final_tag):
        raise RuntimeError(f'Invalid version tag: "{final_tag}".')

    io.execute(f"git tag -a {final_tag} {commit_hash}", repository_dir)

    return final_tag
Exemplo n.º 11
0
def build(app_name, context):
    """Build the docker image of the last version of the app
    Example:
        > app_name = "my-app"
        > context = {"repository": "/path_to/my-app-doc", "commit_hash": "a2b3c4"}
        > build(app_name, context)
    """
    repository = context["repository"]
    image_tag = git.get_last_tag(repository)

    Logger.info(
        {"tag": f"{app_name}@{image_tag}"},
        "Application name and version",
    )

    if has_docker_image(app_name, image_tag):
        Logger.info({}, "Docker image already built (skipped)")
        return image_tag

    app_config = config.get_app_config(app_name)
    commit_hash = context["commit_hash"]
    build_variables = app_config["build"]["variables"]

    # Application build environment variables:
    builds_args = []
    for key, value in build_variables.items():
        builds_args.append(f"--build-arg {key}={value}")

    builds_args.append(f"--build-arg COMMIT_HASH={commit_hash}")
    builds_args_str = " ".join(builds_args)

    command = f"docker build --tag {app_name}:{image_tag} {builds_args_str} {repository}"

    Logger.debug({"command": command}, "Docker build command")

    try:
        io.execute(command)
    except Exception as err:
        Logger.error({"err": err}, "Error while building Docker image")
        raise err

    return image_tag
Exemplo n.º 12
0
def push(app_name, repository):
    """Push an image to the configured docker registry"""

    # This will need to be done a bit differently to work with GCP registry

    app_config = config.get_app_config(app_name)

    image_tag = git.get_last_tag(repository)

    if not has_docker_image(app_name, image_tag):
        raise RuntimeError("Docker image not available")

    registry = app_config["docker"]["registry"]

    # Create the tag
    image = get_registry_image_tag(app_name, image_tag, registry)

    io.execute(f"docker tag {app_name}:{image_tag} {image}")

    io.execute(f"docker push {image}")
Exemplo n.º 13
0
def get_commits_between_tags(repository_dir: str, tag_old: str,
                             tag_new: str) -> list:
    """Get the commits between two tags (ordered with the newest first)."""
    output = io.execute(
        f"git log --oneline --no-decorate refs/tags/{tag_old}..refs/tags/{tag_new}",
        repository_dir)

    commits = []
    for line in output.splitlines():
        commit = {}
        split_idx = line.find(" ")
        commit["hash"] = line[:split_idx]
        commit["message"] = line[split_idx + 1:]
        commits.append(commit)
    return commits
Exemplo n.º 14
0
def fetch_resource_configuration(cluster_name: str, namespace: str,
                                 app_name: str,
                                 resources: List[K8sResourceKind]) -> dict:
    """Fetch a resource's configuration using kubectl."""
    resources_str = ",".join([str(resource) for resource in resources])
    command = ("kubectl "
               f"--context {cluster_name} "
               f"--namespace {namespace} "
               f"get {resources_str} "
               "--output=json "
               f"--selector app={app_name}")
    env = _build_kubectl_env()

    stdout = io.execute(command, env=env)

    return json.loads(stdout)
Exemplo n.º 15
0
    def test_execute(self, subprocess_run_mock):
        subprocess_run_mock.return_value.returncode = 0
        subprocess_run_mock.return_value.stdout.decode.return_value = "some output\n"

        output = io.execute("a command with --arg1 arg-value")

        self.assertEqual(output, "some output")
        subprocess_run_mock.assert_called_with(
            "a command with --arg1 arg-value",
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            cwd=None,
            env=None,
            shell=True,
            check=False,
        )
Exemplo n.º 16
0
def has_docker_image(app_name, tag):
    """Checks if the docker image already exists for a given app and tag"""
    stdout = io.execute(f"docker images {app_name}:{tag} --quiet")
    return len(stdout) != 0
Exemplo n.º 17
0
def get_remote_url(repository_dir, remote_name="origin"):
    """Retrieves the remote url of a repository"""
    return io.execute(f"git remote get-url {remote_name}", repository_dir)
Exemplo n.º 18
0
def test_execute_should_raise_if_failure():
    with pytest.raises(Exception):
        io.execute("a command with --arg1 arg-value")
Exemplo n.º 19
0
def push(repository_dir, branch_name="HEAD"):
    """Push to the remote repository"""
    io.execute(f"git push origin {branch_name} --tags --follow-tags",
               repository_dir)
Exemplo n.º 20
0
def get_last_tag(repository_dir):
    """Retrieves the last tag of a repository (locally)"""
    return io.execute("git describe --always --abbrev=0", repository_dir)
Exemplo n.º 21
0
def apply_config(cluster_name: str, yaml_path: str) -> None:
    """Apply the k8s configuration using kubectl."""
    command = "kubectl " f"--context {cluster_name} " f"apply -f {yaml_path}"
    env = _build_kubectl_env()

    io.execute(command, env=env)
Exemplo n.º 22
0
def get_commit_hash_from_tag(repository_dir: str, tag_name: str) -> str:
    """Returns the commit hash associated to the given tag"""
    return io.execute(f"git rev-list -1 {tag_name}", repository_dir)
Exemplo n.º 23
0
def rebase(repository_dir: str, branch_name: str, *, onto: str = None) -> None:
    """Rebase the current branch on top of the given branch"""
    io.execute(
        f'git rebase{f" --onto {onto}" if onto else ""} {branch_name} --keep-empty',
        repository_dir)
Exemplo n.º 24
0
def get_last_commit_hash(repository_dir: str, reference: str = "HEAD") -> str:
    """Retrieves the last commit hash of a repository (locally)"""
    return io.execute(f"git rev-parse --short {reference}", repository_dir)
Exemplo n.º 25
0
def is_branch_existing(repository_dir: str, branch_name: str) -> bool:
    """Determines if a branch exists on the repository"""
    stdout = io.execute(f"git branch --list {branch_name}", repository_dir)
    return len(stdout) != 0
Exemplo n.º 26
0
def get_last_commit_hash(repository_dir):
    """Retrieves the last commit hash of a repository (locally)"""
    return io.execute("git rev-parse --short HEAD", repository_dir)