def determine_image(self, image, overview, configuration, needs_image=True): """Complain if we don't have an image""" images = configuration["images"] available = None available = images.keys() if needs_image: if not image: info = {} if available: info["available"] = list(available) raise BadOption( "Please use --image to specify an image to run /bin/bash in", **info) if image not in images: raise BadOption("No such image", wanted=image, available=list(images.keys())) return images
def tag(collector, image, artifact, **kwargs): """Tag an image!""" if artifact in (None, "", NotSpecified): raise BadOption("Please specify a tag using the artifact option") if image.image_index in (None, "", NotSpecified): raise BadOption("Please specify an image with an image_index option") tag = image.image_name if image.tag is NotSpecified: tag = "{0}:latest".format(tag) images = image.harpoon.docker_context.images() current_tags = chain.from_iterable(image_conf["RepoTags"] for image_conf in images) if tag not in current_tags: raise BadOption( "Please build or pull the image down to your local cache before tagging it" ) for image_conf in images: if tag in image_conf["RepoTags"]: image_id = image_conf["Id"] break log.info("Tagging {0} ({1}) as {2}".format(image_id, image.image_name, artifact)) image.harpoon.docker_context.tag(image_id, repository=image.image_name, tag=artifact, force=True) image.tag = artifact Syncer().push(image)
def find_image(self, image, configuration): """Complain if we don't have an image""" images = configuration["images"] available = list(images.keys()) if not image: info = {} if available: info["available"] = available raise BadOption("Please use --image to specify an image", **info) if image not in images: raise BadOption("No such image", wanted=image, available=available)
def parse_args(self, argv=None): """Split the args into <args> -- <extra_args> and run <args> through our argparse.ArgumentParser""" if argv is None: argv = sys.argv[1:] argv = list(argv) args = [] extras = None default_task = NotSpecified default_image = NotSpecified if argv: if not argv[0].startswith("-"): default_task = argv[0] argv.pop(0) if argv and not argv[0].startswith("-"): default_image = argv[0] argv.pop(0) while argv: nxt = argv.pop(0) if extras is not None: extras.append(nxt) elif nxt == "--": extras = [] else: args.append(nxt) other_args = "" if extras: other_args = " ".join(extras) parser = self.make_parser(default_task=default_task, default_image=default_image) args = parser.parse_args(args) if default_task is not NotSpecified and args.harpoon_chosen_task != default_task: raise BadOption( "Please don't specify task as a positional argument and as a --task option", positional=default_task, kwarg=args.task) if default_image is not NotSpecified and args.harpoon_chosen_image != default_image: raise BadOption( "Please don't specify image as a positional argument and as a --image option", positional=default_image, kwargs=args.image) return args, other_args
def validate(self, meta, val): if ' ' not in val: raise BadOption( "Expected string to have a space (<ACTION> <COMMAND>)", meta=meta, got=val) return val
def pull(overview, configuration, images, image, **kwargs): """Pull an image""" if not image.image_index: raise BadOption( "The chosen image does not have a image_index configuration", wanted=image.name) Syncer().pull(image, ignore_missing=image.harpoon.ignore_missing)
def push(overview, configuration, images, image): """Push an image""" if not image.image_index: raise BadOption( "The chosen image does not have a image_index configuration", wanted=image.name) Builder().make_image(image, images) Syncer().push(image)
def push(collector, image, **kwargs): """Push an image""" if not image.image_index: raise BadOption( "The chosen image does not have a image_index configuration", wanted=image.name) Builder().make_image(image, collector.configuration["images"], pushing=True) Syncer().push(image)
def find_missing_env(self): """Find any missing environment variables""" missing = [] for e in self.env: if e.default_val is None and e.set_val is None: if e.env_name not in os.environ: missing.append(e.env_name) if missing: raise BadOption( "Some environment variables aren't in the current environment", missing=missing)
def pull(collector, image, **kwargs): """Pull an image""" if not image.image_index: raise BadOption( "The chosen image does not have a image_index configuration", wanted=image.name) tag = kwargs["artifact"] if tag is NotSpecified: collector.configuration["harpoon"].tag if tag is not NotSpecified: image.tag = tag log.info("Pulling tag: %s", tag) Syncer().pull(image, ignore_missing=image.harpoon.ignore_missing)
def push(collector, image, **kwargs): """Push an image""" if not image.image_index: raise BadOption( "The chosen image does not have a image_index configuration", wanted=image.name) tag = kwargs["artifact"] if tag is NotSpecified: tag = collector.configuration["harpoon"].tag if tag is not NotSpecified: image.tag = tag Builder().make_image(image, collector.configuration["images"], pushing=True) Syncer().push(image)
def the_context(self, content, silent_build=False): """Return either a file with the content written to it, or a whole new context tar""" if isinstance(content, six.string_types): with a_temp_file() as fle: fle.write(content.encode('utf-8')) fle.seek(0) yield fle elif "context" in content: with ContextBuilder().make_context(content["context"], silent_build=silent_build) as wrapper: wrapper.close() yield wrapper.tmpfile elif "image" in content: from harpoon.ship.runner import Runner with a_temp_file() as fle: content["conf"].command = "yes" with Runner()._run_container(content["conf"], content["images"], detach=True, delete_anyway=True): try: strm, stat = content["docker_context"].get_archive(content["conf"].container_id, content["path"]) except docker.errors.NotFound: raise BadOption("Trying to get something from an image that don't exist!", path=content["path"], image=content["conf"].image_name) else: log.debug(stat) fo = BytesIO(strm.read()) tf = tarfile.TarFile(fileobj=fo) if tf.firstmember.isdir(): tf2 = tarfile.TarFile(fileobj=fle, mode='w') name = tf.firstmember.name for member in tf.getmembers()[1:]: member.name = member.name[len(name)+1:] if member.issym(): with tempfile.NamedTemporaryFile() as symfle: os.remove(symfle.name) os.symlink(member.linkpath, symfle.name) tf2.addfile(member, fileobj=symfle) elif not member.isdir(): tf2.addfile(member, fileobj=tf.extractfile(member.name)) tf2.close() else: fle.write(tf.extractfile(tf.firstmember.name).read()) tf.close() log.info("Got '{0}' from {1} for context".format(content["path"], content["conf"].container_id)) fle.seek(0) yield fle
def create_container(self, conf, detach, tty): """Create a single container""" name = conf.name image_name = conf.image_name container_name = conf.container_name with conf.assumed_role(): env = dict(e.pair for e in conf.env) links = [link.pair for link in conf.links] binds = conf.volumes.binds command = conf.formatted_command volume_names = conf.volumes.volume_names volumes_from = list(conf.volumes.share_with_names) no_tty_option = conf.no_tty_option ports = [p.container_port.port_pair for p in conf.ports] port_bindings = self.exposed(conf.ports) uncreated = [] for name in binds: if not os.path.exists(name): log.info("Making volume for mounting\tvolume=%s", name) try: os.makedirs(name) except OSError as error: uncreated.append((name, error)) if uncreated: raise BadOption("Failed to create some volumes on the host", uncreated=uncreated) log.info( "Creating container from %s\timage=%s\tcontainer_name=%s\ttty=%s", image_name, name, container_name, tty) if binds: log.info("\tUsing volumes\tvolumes=%s", volume_names) if env: log.info("\tUsing environment\tenv=%s", sorted(env.keys())) if links: log.info("\tLinks: %s", links) if ports: log.info("\tUsing ports\tports=%s", ports) if port_bindings: log.info("\tPort bindings: %s", port_bindings) if volumes_from: log.info("\tVolumes from: %s", volumes_from) host_config = conf.harpoon.docker_context.create_host_config( links=links, binds=binds, volumes_from=volumes_from, port_bindings=port_bindings, devices=conf.devices, lxc_conf=conf.lxc_conf, privileged=conf.privileged, restart_policy=conf.restart_policy, dns=conf.network.dns, dns_search=conf.network.dns_search, extra_hosts=conf.network.extra_hosts, network_mode=conf.network.network_mode, publish_all_ports=conf.network.publish_all_ports, cap_add=conf.cpu.cap_add, cap_drop=conf.cpu.cap_drop, mem_limit=conf.cpu.mem_limit, memswap_limit=conf.cpu.memswap_limit, ulimits=conf.ulimits, read_only=conf.read_only_rootfs, log_config=conf.log_config, security_opt=conf.security_opt, **conf.other_options.host_config) container_id = conf.harpoon.docker_context.create_container( image_name, name=container_name, detach=detach, command=command, volumes=volume_names, environment=env, tty=False if no_tty_option else tty, user=conf.user, ports=ports, stdin_open=tty, dns=conf.network.dns, hostname=conf.network.hostname, domainname=conf.network.domainname, network_disabled=conf.network.disabled, cpuset=conf.cpu.cpuset, cpu_shares=conf.cpu.cpu_shares, host_config=host_config, **conf.other_options.create) if isinstance(container_id, dict): if "errorDetail" in container_id: raise BadImage("Failed to create container", image=name, error=container_id["errorDetail"]) container_id = container_id["Id"] return container_id