Exemple #1
0
    def build(self, conf, context, stream):
        image_name = self.image_name
        if image_name is None:
            image_name = conf.image_name

        context.close()
        self.log_context_size(context, conf)

        lines = conf.harpoon.docker_context.build(
              tag = image_name
            , fileobj = context.tmpfile
            , custom_context = True

            , rm = True
            , pull = False
            , stream = True
            )

        for line in lines:
            try:
                stream.feed(line)
            except Failure as error:
                raise FailedImage("Failed to build an image", image=conf.name, msg=error)
            except Unknown as error:
                log.warning("Unknown line\tline=%s", error)

            for part in stream.printable():
                hp.write_to(conf.harpoon.stdout, part)
            conf.harpoon.stdout.flush()

        return stream.cached
Exemple #2
0
    def do_build(self, conf, context, stream, image_name=None, verbose=False):
        if image_name is None:
            image_name = conf.image_name

        context.close()
        for line in conf.harpoon.docker_context.build(fileobj=context.tmpfile,
                                                      custom_context=True,
                                                      tag=image_name,
                                                      stream=True,
                                                      rm=True,
                                                      pull=False):
            try:
                stream.feed(line)
            except Failure as error:
                raise FailedImage("Failed to build an image",
                                  image=conf.name,
                                  msg=error)
            except Unknown as error:
                log.warning("Unknown line\tline=%s", error)

            for part in stream.printable():
                hp.write_to(conf.harpoon.stdout, part)
            conf.harpoon.stdout.flush()

        return stream.cached
Exemple #3
0
    def build(self, conf, context, stream):
        image_name = self.image_name
        if image_name is None:
            image_name = conf.image_name_with_tag

        context.close()
        self.log_context_size(context, conf)

        # Login into the correct registry
        current_tags = list(chain.from_iterable(image["RepoTags"] for image in conf.harpoon.docker_api.images() if image["RepoTags"]))

        for dep in conf.commands.dependent_images:
            if isinstance(dep, six.string_types):
                if ":" not in dep:
                    dep = "{0}:latest".format(dep)

                if dep not in current_tags:
                    conf.login(dep, is_pushing=False)

        cache_from = list(conf.cache_from_names)
        if cache_from:
            log.info("Using cache from the following images\timages={0}".format(", ".join(cache_from)))

        lines = conf.harpoon.docker_api.build(
              tag = image_name
            , fileobj = context.tmpfile
            , custom_context = True
            , cache_from = list(conf.cache_from_names)

            , rm = True
            , pull = False
            )

        for found in lines:
            for line in found.decode().split("\n"):
                if line.strip():
                    try:
                        stream.feed(line.encode())
                    except Failure as error:
                        raise FailedImage("Failed to build an image", image=conf.name, msg=error)
                    except Unknown as error:
                        log.warning("Unknown line\tline=%s", error)

                    for part in stream.printable():
                        hp.write_to(conf.harpoon.stdout, part)
                    conf.harpoon.stdout.flush()

        return stream.cached
Exemple #4
0
    def build(self, conf, context, stream):
        image_name = self.image_name
        if image_name is None:
            image_name = conf.image_name

        context.close()
        self.log_context_size(context, conf)

        # Login into the correct registry
        current_tags = list(
            chain.from_iterable(
                image["RepoTags"]
                for image in conf.harpoon.docker_context.images()))
        parent_image = conf.commands.parent_image
        if ":" not in parent_image:
            parent_image = "{0}:latest".format(parent_image)

        if parent_image not in current_tags:
            conf.login(conf.commands.parent_image, is_pushing=False)

        lines = conf.harpoon.docker_context.build(tag=image_name,
                                                  fileobj=context.tmpfile,
                                                  custom_context=True,
                                                  rm=True,
                                                  pull=False,
                                                  stream=True)

        for found in lines:
            for line in found.decode().split("\n"):
                if line.strip():
                    try:
                        stream.feed(line.encode())
                    except Failure as error:
                        raise FailedImage("Failed to build an image",
                                          image=conf.name,
                                          msg=error)
                    except Unknown as error:
                        log.warning("Unknown line\tline=%s", error)

                    for part in stream.printable():
                        hp.write_to(conf.harpoon.stdout, part)
                    conf.harpoon.stdout.flush()

        return stream.cached
Exemple #5
0
    def stage_run_intervention(self, conf, just_do_it=False):
        """Start an intervention!"""
        if not conf.harpoon.interactive or conf.harpoon.no_intervention:
            return

        if just_do_it:
            answer = 'y'
        else:
            hp.write_to(conf.harpoon.stdout, "!!!!\n")
            hp.write_to(conf.harpoon.stdout, "Failed to run the container!\n")
            hp.write_to(conf.harpoon.stdout, "Do you want commit the container in it's current state and /bin/bash into it to debug?\n")
            conf.harpoon.stdout.flush()
            answer = input("[y]: ")
        if not answer or answer.lower().startswith("y"):
            with self.commit_and_run(conf.container_id, conf, command="/bin/bash"):
                pass
Exemple #6
0
    def intervention(self, commit, conf):
        """Ask the user if they want to commit this container and run /bin/bash in it"""
        if not conf.harpoon.interactive or conf.harpoon.no_intervention:
            yield
            return

        hp.write_to(conf.harpoon.stdout, "!!!!\n")
        hp.write_to(conf.harpoon.stdout, "It would appear building the image failed\n")
        hp.write_to(conf.harpoon.stdout, "Do you want to run /bin/bash where the build to help debug why it failed?\n")
        conf.harpoon.stdout.flush()
        answer = input("[y]: ")
        if answer and not answer.lower().startswith("y"):
            yield
            return

        with self.commit_and_run(commit, conf, command="/bin/bash"):
            yield
Exemple #7
0
    def intervention(self, commit, conf):
        """Ask the user if they want to commit this container and run sh in it"""
        if not conf.harpoon.interactive or conf.harpoon.no_intervention:
            yield
            return

        hp.write_to(conf.harpoon.stdout, "!!!!\n")
        hp.write_to(conf.harpoon.stdout,
                    "It would appear building the image failed\n")
        hp.write_to(
            conf.harpoon.stdout,
            "Do you want to run {0} where the build to help debug why it failed?\n"
            .format(conf.resolved_shell))
        conf.harpoon.stdout.flush()
        answer = input("[y]: ")
        if answer and not answer.lower().startswith("y"):
            yield
            return

        with self.commit_and_run(commit, conf, command=conf.resolved_shell):
            yield
Exemple #8
0
    def stage_run_intervention(self, conf, just_do_it=False):
        """Start an intervention!"""
        if not conf.harpoon.interactive or conf.harpoon.no_intervention:
            return

        if just_do_it:
            answer = 'y'
        else:
            hp.write_to(conf.harpoon.stdout, "!!!!\n")
            hp.write_to(conf.harpoon.stdout, "Failed to run the container!\n")
            hp.write_to(
                conf.harpoon.stdout,
                "Do you want commit the container in it's current state and {0} into it to debug?\n"
                .format(conf.resolved_shell))
            conf.harpoon.stdout.flush()
            answer = input("[y]: ")
        if not answer or answer.lower().startswith("y"):
            with self.commit_and_run(conf.container_id,
                                     conf,
                                     command=conf.resolved_shell):
                pass
Exemple #9
0
    def stop_container(self,
                       conf,
                       fail_on_bad_exit=False,
                       fail_reason=None,
                       tag=None,
                       remove_volumes=False):
        """Stop some container"""
        stopped = False
        container_id = conf.container_id
        if not container_id:
            return

        container_name = conf.container_name
        stopped, exit_code = self.is_stopped(conf, container_id)

        if stopped:
            if exit_code != 0 and fail_on_bad_exit:
                if not conf.harpoon.interactive:
                    print_logs = True
                else:
                    hp.write_to(conf.harpoon.stdout, "!!!!\n")
                    hp.write_to(
                        conf.harpoon.stdout,
                        "Container had already exited with a non zero exit code\tcontainer_name={0}\tcontainer_id={1}\texit_code={2}\n"
                        .format(container_name, container_id, exit_code))
                    hp.write_to(
                        conf.harpoon.stdout,
                        "Do you want to see the logs from this container?\n")
                    conf.harpoon.stdout.flush()
                    answer = input("[y]: ")
                    print_logs = not answer or answer.lower().startswith("y")

                if print_logs:
                    hp.write_to(
                        conf.harpoon.stdout,
                        "=================== Logs for failed container {0} ({1})\n"
                        .format(container_id, container_name))
                    for line in conf.harpoon.docker_context.logs(
                            container_id).split("\n"):
                        hp.write_to(conf.harpoon.stdout, "{0}\n".format(line))
                    hp.write_to(
                        conf.harpoon.stdout,
                        "------------------- End logs for failed container\n")
                fail_reason = fail_reason or "Failed to run container"
                raise BadImage(fail_reason,
                               container_id=container_id,
                               container_name=container_name)
        else:
            try:
                log.info("Killing container %s:%s", container_name,
                         container_id)
                conf.harpoon.docker_context.kill(container_id, 9)
            except DockerAPIError:
                pass
            self.wait_till_stopped(
                conf,
                container_id,
                timeout=10,
                message=
                "waiting for container to die\tcontainer_name={0}\tcontainer_id={1}"
                .format(container_name, container_id))

        if tag:
            log.info("Tagging a container\tcontainer_id=%s\ttag=%s",
                     container_id, tag)
            new_id = conf.harpoon.docker_context.commit(container_id)["Id"]
            conf["committed"] = new_id
            if tag is not True:
                the_tag = "latest" if conf.tag is NotSpecified else conf.tag
                conf.harpoon.docker_context.tag(new_id,
                                                repository=tag,
                                                tag=the_tag,
                                                force=True)

        mounts = []
        if remove_volumes:
            inspection = conf.harpoon.docker_context.inspect_container(
                container_id)
            if "Mounts" in inspection:
                for m in inspection["Mounts"]:
                    if "Name" in m:
                        mounts.append(m['Name'])
            else:
                log.warning("Your docker can't inspect and delete volumes :(")

        if not conf.harpoon.no_cleanup:
            log.info("Removing container %s:%s", container_name, container_id)
            for _ in until(
                    timeout=10,
                    action=
                    "removing container\tcontainer_name={0}\tcontainer_id={1}".
                    format(container_name, container_id)):
                try:
                    conf.harpoon.docker_context.remove_container(container_id)
                    break
                except socket.timeout:
                    break
                except (ValueError, DockerAPIError) as error:
                    log.warning(
                        "Failed to remove container\tcontainer_id=%s\terror=%s",
                        container_id, error)

        for mount in mounts:
            try:
                log.info("Cleaning up volume {0}".format(mount))
                conf.harpoon.docker_context.remove_volume(mount)
            except DockerAPIError as error:
                log.warning("Failed to cleanup volume\tvolume=%s\terror=%s",
                            mount, error)
Exemple #10
0
    def wait_for_deps(self, conf, images):
        """Wait for all our dependencies"""
        from harpoon.option_spec.image_objs import WaitCondition
        ctxt = conf.harpoon.docker_context_maker()

        waited = set()
        last_attempt = {}
        dependencies = set(dep for dep, _ in conf.dependency_images())

        # Wait conditions come from dependency_options first
        # Or if none specified there, they come from the image itself
        wait_conditions = {}
        for dependency in dependencies:
            if conf.dependency_options is not NotSpecified and dependency in conf.dependency_options and conf.dependency_options[
                    dependency].wait_condition is not NotSpecified:
                wait_conditions[dependency] = conf.dependency_options[
                    dependency].wait_condition
            elif images[dependency].wait_condition is not NotSpecified:
                wait_conditions[dependency] = images[dependency].wait_condition

        if not wait_conditions:
            return

        start = time.time()
        while True:
            this_round = []
            for dependency in dependencies:
                if dependency in waited:
                    continue

                image = images[dependency]
                if dependency in wait_conditions:
                    done = self.wait_for_dep(ctxt, image,
                                             wait_conditions[dependency],
                                             start,
                                             last_attempt.get(dependency))
                    this_round.append(done)
                    if done is True:
                        waited.add(dependency)
                    elif done is False:
                        last_attempt[dependency] = time.time()
                    elif done is WaitCondition.Timedout:
                        log.warning(
                            "Stopping dependency because it timedout waiting\tcontainer_id=%s",
                            image.container_id)
                        self.stop_container(image)
                else:
                    waited.add(dependency)

            if set(this_round) != set([WaitCondition.KeepWaiting]):
                if dependencies - waited == set():
                    log.info("Finished waiting for dependencies")
                    break
                else:
                    log.info("Still waiting for dependencies\twaiting_on=%s",
                             list(dependencies - waited))

                couldnt_wait = set()
                container_ids = {}
                for dependency in dependencies:
                    if dependency in waited:
                        continue

                    image = images[dependency]
                    if image.container_id is None:
                        stopped = True
                        if dependency not in container_ids:
                            available = sorted([
                                i for i in available if "/{0}".format(
                                    image.container_name) in i["Names"]
                            ],
                                               key=lambda i: i["Created"])
                            if available:
                                container_ids[dependency] = available[0]["Id"]
                    else:
                        if dependency not in container_ids:
                            container_ids[dependency] = image.container_id
                        stopped, _ = self.is_stopped(image, image.container_id)

                    if stopped:
                        couldnt_wait.add(dependency)

                if couldnt_wait:
                    for container in couldnt_wait:
                        if container not in images or container not in container_ids:
                            continue
                        image = images[container]
                        container_id = container_ids[container]
                        container_name = image.container_name
                        hp.write_to(
                            conf.harpoon.stdout,
                            "=================== Logs for failed container {0} ({1})\n"
                            .format(container_id, container_name))
                        for line in conf.harpoon.docker_context.logs(
                                container_id).split("\n"):
                            hp.write_to(conf.harpoon.stdout,
                                        "{0}\n".format(line))
                        hp.write_to(
                            conf.harpoon.stdout,
                            "------------------- End logs for failed container\n"
                        )
                    raise BadImage(
                        "One or more of the dependencies stopped running whilst waiting for other dependencies",
                        stopped=list(couldnt_wait))

            time.sleep(0.1)
Exemple #11
0
    def wait_for_deps(self, conf, images):
        """Wait for all our dependencies"""
        from harpoon.option_spec.image_objs import WaitCondition
        ctxt = conf.harpoon.docker_context_maker()

        waited = set()
        last_attempt = {}
        dependencies = set(dep for dep, _ in conf.dependency_images())

        # Wait conditions come from dependency_options first
        # Or if none specified there, they come from the image itself
        wait_conditions = {}
        for dependency in dependencies:
            if conf.dependency_options is not NotSpecified and dependency in conf.dependency_options and conf.dependency_options[dependency].wait_condition is not NotSpecified:
                wait_conditions[dependency] = conf.dependency_options[dependency].wait_condition
            elif images[dependency].wait_condition is not NotSpecified:
                wait_conditions[dependency] = images[dependency].wait_condition

        if not wait_conditions:
            return

        start = time.time()
        while True:
            this_round = []
            for dependency in dependencies:
                if dependency in waited:
                    continue

                image = images[dependency]
                if dependency in wait_conditions:
                    done = self.wait_for_dep(ctxt, image, wait_conditions[dependency], start, last_attempt.get(dependency))
                    this_round.append(done)
                    if done is True:
                        waited.add(dependency)
                    elif done is False:
                        last_attempt[dependency] = time.time()
                    elif done is WaitCondition.Timedout:
                        log.warning("Stopping dependency because it timedout waiting\tcontainer_id=%s", image.container_id)
                        self.stop_container(image)
                else:
                    waited.add(dependency)

            if set(this_round) != set([WaitCondition.KeepWaiting]):
                if dependencies - waited == set():
                    log.info("Finished waiting for dependencies")
                    break
                else:
                    log.info("Still waiting for dependencies\twaiting_on=%s", list(dependencies-waited))

                couldnt_wait = set()
                container_ids = {}
                for dependency in dependencies:
                    if dependency in waited:
                        continue

                    image = images[dependency]
                    if image.container_id is None:
                        stopped = True
                        if dependency not in container_ids:
                            available = sorted([i for i in available if "/{0}".format(image.container_name) in i["Names"]], key=lambda i: i["Created"])
                            if available:
                                container_ids[dependency] = available[0]["Id"]
                    else:
                        if dependency not in container_ids:
                            container_ids[dependency] = image.container_id
                        stopped, _ = self.is_stopped(image, image.container_id)

                    if stopped:
                        couldnt_wait.add(dependency)

                if couldnt_wait:
                    for container in couldnt_wait:
                        if container not in images or container not in container_ids:
                            continue
                        image = images[container]
                        container_id = container_ids[container]
                        container_name = image.container_name
                        hp.write_to(conf.harpoon.stdout, "=================== Logs for failed container {0} ({1})\n".format(container_id, container_name))
                        for line in conf.harpoon.docker_context.logs(container_id).split("\n"):
                            hp.write_to(conf.harpoon.stdout, "{0}\n".format(line))
                        hp.write_to(conf.harpoon.stdout, "------------------- End logs for failed container\n")
                    raise BadImage("One or more of the dependencies stopped running whilst waiting for other dependencies", stopped=list(couldnt_wait))

            time.sleep(0.1)
Exemple #12
0
    def stop_container(self, conf, fail_on_bad_exit=False, fail_reason=None, tag=None, remove_volumes=False):
        """Stop some container"""
        stopped = False
        container_id = conf.container_id
        if not container_id:
            return

        container_name = conf.container_name
        stopped, exit_code = self.is_stopped(conf, container_id)

        if stopped:
            if exit_code != 0 and fail_on_bad_exit:
                if not conf.harpoon.interactive:
                    print_logs = True
                else:
                    hp.write_to(conf.harpoon.stdout, "!!!!\n")
                    hp.write_to(conf.harpoon.stdout, "Container had already exited with a non zero exit code\tcontainer_name={0}\tcontainer_id={1}\texit_code={2}\n".format(container_name, container_id, exit_code))
                    hp.write_to(conf.harpoon.stdout, "Do you want to see the logs from this container?\n")
                    conf.harpoon.stdout.flush()
                    answer = input("[y]: ")
                    print_logs = not answer or answer.lower().startswith("y")

                if print_logs:
                    hp.write_to(conf.harpoon.stdout, "=================== Logs for failed container {0} ({1})\n".format(container_id, container_name))
                    for line in conf.harpoon.docker_context.logs(container_id).split("\n"):
                        hp.write_to(conf.harpoon.stdout, "{0}\n".format(line))
                    hp.write_to(conf.harpoon.stdout, "------------------- End logs for failed container\n")
                fail_reason = fail_reason or "Failed to run container"
                raise BadImage(fail_reason, container_id=container_id, container_name=container_name)
        else:
            try:
                log.info("Killing container %s:%s", container_name, container_id)
                conf.harpoon.docker_context.kill(container_id, 9)
            except DockerAPIError:
                pass
            self.wait_till_stopped(conf, container_id, timeout=10, message="waiting for container to die\tcontainer_name={0}\tcontainer_id={1}".format(container_name, container_id))

        if tag:
            log.info("Tagging a container\tcontainer_id=%s\ttag=%s", container_id, tag)
            new_id = conf.harpoon.docker_context.commit(container_id)["Id"]
            conf["committed"] = new_id
            if tag is not True:
                conf.harpoon.docker_context.tag(new_id, repository=tag, tag="latest", force=True)

        mounts = []
        if remove_volumes:
            for m in conf.harpoon.docker_context.inspect_container(container_id)["Mounts"]:
                mounts.append(m['Name'])

        if not conf.harpoon.no_cleanup:
            log.info("Removing container %s:%s", container_name, container_id)
            for _ in until(timeout=10, action="removing container\tcontainer_name={0}\tcontainer_id={1}".format(container_name, container_id)):
                try:
                    conf.harpoon.docker_context.remove_container(container_id)
                    break
                except socket.timeout:
                    break
                except (ValueError, DockerAPIError) as error:
                    log.warning("Failed to remove container\tcontainer_id=%s\terror=%s", container_id, error)

        for mount in mounts:
            try:
                log.info("Cleaning up volume {0}".format(mount))
                conf.harpoon.docker_context.remove_volume(mount)
            except DockerAPIError as error:
                log.warning("Failed to cleanup volume\tvolume=%s\terror=%s", mount, error)