Exemple #1
0
def deploy(registry: str, image: str, name: str, namespace: str, branch: str,
           version: str, variants: str, local: bool, dry: bool):
    working_directory = os.getcwd()
    if local:
        # Reset branch when using local.
        branch = None
    if version is None:
        version = head_of(working_directory, branch, local=local)

    if variants is not None:
        variants = preprocess_variants(variants)

    kube = Kube(namespace=namespace,
                deployment_name=image,
                printer=prompt,
                error_printer=error_prompt,
                variants=variants)
    tag = docker_helpers.make_tag(registry, image, version)
    if local and not dry:
        download_requirements()
        docker_helpers.docker_image('build', tag)
        docker_helpers.docker_image('push', tag)

    if not docker_helpers.docker_image_exists(tag):
        error_prompt('Image not found: {}'.format(tag))
        if not dry:
            sys.exit(1)

    kube.info()

    if dry:
        prompt('Dry run finished. Not deploying.')
        return

    kube.apply(tag)
Exemple #2
0
    def test_prompt(self):
        with mock.patch('sys.stdout') as mock_stdout:
            prompt('this is a test')

        mock_stdout.assert_has_calls([
            mock.call.write('\x1b[32m>> '),  # green prompt
            mock.call.write('this is a test'),
            mock.call.write('\n')  # newline added by print()
        ])
Exemple #3
0
def docker_image(op: str, tag: str):
    # The registry part of the tag will be used to determine the push
    # destination domain.
    client = docker.from_env(version='1.24')

    if op == "build":
        prompt('Building image: {}'.format(tag))
        client.images.build(tag=tag, path=os.getcwd())
    elif op == "push":
        prompt('Pushing image: {}'.format(tag))
        client.images.push(tag)
Exemple #4
0
def apply(from_file: str):
    # Load the deployments from file and get the current count of replicas in
    # the target cluster for each of the deployments. Then update the replicas
    # to match the target cluster. Save the file and pass on to kubectl apply.
    with open(from_file) as fd:
        content = fd.read()
    kube_list = yaml.load(content)

    kubectl = Kubectl()
    kubectl.update_replicas(kube_list)

    with open(from_file, mode='w') as fd:
        fd.write(yaml.dump(kube_list, default_flow_style=False))

    try:
        lines = kubectl.apply(from_file)
        for line in lines.split('\n'):
            prompt(line)
    except KubectlCallFailed as e:
        error_prompt(str(e))
Exemple #5
0
def download_requirements(force: bool=False):
    if not os.path.isfile('requirements.txt'):
        return
    # Create temporary directory as download target for requirements then
    # download to this temporary directory and move it into the docker
    # context. The docker context is the current directory that can not be
    # used as initial destination as it itself is part of the requirements.txt
    dest = os.path.join(os.getcwd(), 'pip-cache')

    if os.path.isdir(dest):
        if not force:
            prompt('pip-cache exists. Skipping download of requirements.')
            return

        # Remove the existing pip-cache if any
        prompt('pip-cache exists. Removing for fresh download of '
               'requirements.')
        shutil.rmtree(dest)

    tmp = tempfile.mkdtemp()
    prompt('Downloading requirements.')
    with open('requirements.txt') as f:
        deps = [line for line in f if line.startswith('git+ssh')]
    pip.main(['download', '-q', '--dest', tmp, *deps])
    shutil.move(tmp, dest)
Exemple #6
0
def print_cluster_info(state):
    for item in state['items']:
        name = item['metadata']['name']
        prompt(name)
        for cont in item['spec']['template']['spec']['containers']:
            prompt(cont['image'], 4)
        if item.get('status') is None:
            error_prompt('No replicas running.', 4)
        else:
            prompt(
                (f'replicas: {item["status"]["replicas"]} '
                 f'ready: {item["status"]["readyReplicas"]} '
                 f'updated: {item["status"]["updatedReplicas"]}'), 4)
Exemple #7
0
def head_of(working_directory: str,
            branch: str=None, local: bool=False) -> str:
    repo = git.Repo(working_directory)
    if branch is None:
        try:
            branch = repo.active_branch
        # The type error gets thrown for example on detached HEAD states and
        # during unfinished rebases and cherry-picks.
        except TypeError as e:
            error_prompt('No branch given and current status is inconclusive: '
                         '{}'.format(str(e)))
            sys.exit(1)

    if local:
        return repo.git.rev_parse(repo.head.commit, short=8)

    prompt("Getting remote HEAD of {}".format(branch))

    # Fetch all remotes (usually one?!) to make sure the latest refs are known
    # to git. Save remote refs that match current branch to make sure to avoid
    # ambiguities and bail out if a branch exists in multiple remotes with
    # different HEADs.
    remote_refs = []
    for remote in repo.remotes:
        remote.fetch()
        for ref in remote.refs:
            # Finding the remote tracking branch this way is a simplification
            # already that assumes the remote tracking branches are of the
            # format refs/remotes/foo/bar (indicating that it tracks a branch
            # named bar in a remote named foo), and matches the right-hand-side
            # of a configured fetch refspec. To actually do it correctly
            # involves reading local git config and use `git remote show
            # <remote-name>`. The main issue with that approach is it would
            # involve porcelain commands as there are no plumbing commands
            # available to get the remote tracking branches currently. Long
            # story short: follow the conventions and this script will work.
            if ref.name == '{remote}/{branch}'.format(remote=remote.name,
                                                      branch=branch):
                prompt('Found "{}" at {}'.format(ref.name,
                                                 str(ref.commit)[:8]))
                remote_refs.append(ref)

    # Bail out if no remote tracking branches where found.
    if len(remote_refs) < 1:
        error_prompt('No remote tracking branch matching "{}" found'.format(
            branch))
        sys.exit(1)

    # Iterate over found remote tracking branches and compare commit IDs; bail
    # out if there is more than one and they differ.
    if len(remote_refs) > 1:
        seen = {}
        for ref in remote_refs:
            seen[repo.git.rev_parse(ref.name)] = True
        if seen.keys() != 1:
            error_prompt('Multiple matching remote tracking branches with'
                         ' different commit IDs found. Can not go on. Make'
                         ' sure requested deployments are unambiguous.')
            sys.exit(1)

    # At this point the head commit of the first remote tracking branch can be
    # returned as it is the same as the others if they exist.
    return repo.git.rev_parse(remote_refs[0].commit, short=8)