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