def __init__(self, name, config, runtime_config): self.all_config = config self.config = config["container"] if runtime_config: self.runtime_config = runtime_config.get("container", {}) else: self.runtime_config = {} self.image = self.all_config["image"]["name"] self.image_version = str(self.all_config["image"]["version"]) self.name = name self.version = datetime.now().strftime("%Y%m%dT%H%M%S") self.path = os.path.join(BASE_CONTAINER_DIR, self.image, name, self.version) self.architecture = self.all_config["image"].get("architecture") self.architecture_detected = False if not self.architecture: machine = platform.machine() # We can't assume every distro reports i686. if machine in ("i386", "i486", "i586", "i686"): self.architecture = "x86" else: self.architecture = "x86-64" self.architecture_detected = True self.mounts = self.config.get("mounts", []) self.limits = self.config.get("limits") self.slice = self.config.get("slice") self.network = self.config.get("network", {}) self.systemd = SystemdContainer(self.runtime_config)
def __init__(self, name, config, runtime_config): """ Delete a container. :param name: container name. :type name: str :param config: configuration of the container :type config: dict :param runtime_config: runtime configuration :type runtime_config: dict """ super().__init__(name, config, runtime_config) self.path = os.path.join(BASE_CONTAINER_DIR, self.config["image"]["name"], self.name, self.config["container"]["version"]) self.unit_name = "sponson-container-{}.service".format(self.name) self.systemd = SystemdContainer(runtime_config)
class DeleteContainer(Delete): """ Delete containers. """ def __init__(self, name, config, runtime_config): """ Delete a container. :param name: container name. :type name: str :param config: configuration of the container :type config: dict :param runtime_config: runtime configuration :type runtime_config: dict """ super().__init__(name, config, runtime_config) self.path = os.path.join(BASE_CONTAINER_DIR, self.config["image"]["name"], self.name, self.config["container"]["version"]) self.unit_name = "sponson-container-{}.service".format(self.name) self.systemd = SystemdContainer(runtime_config) def _sanity_check(self): """ Container deletion sanity check. """ if self.name and self.systemd.is_running(self.name): raise DeleteError("Container is currently running, " "stop the container service before deleting") def _clean_sponson_conf(self): """ Remove sponson container configuration files. """ os.remove(os.path.join(ETC_CONTAINER_CONF_DIR, "{}.yaml".format(self.name)))
class New(object): """ Create a new container. """ def __init__(self, name, config, runtime_config): self.all_config = config self.config = config["container"] if runtime_config: self.runtime_config = runtime_config.get("container", {}) else: self.runtime_config = {} self.image = self.all_config["image"]["name"] self.image_version = str(self.all_config["image"]["version"]) self.name = name self.version = datetime.now().strftime("%Y%m%dT%H%M%S") self.path = os.path.join(BASE_CONTAINER_DIR, self.image, name, self.version) self.architecture = self.all_config["image"].get("architecture") self.architecture_detected = False if not self.architecture: machine = platform.machine() # We can't assume every distro reports i686. if machine in ("i386", "i486", "i586", "i686"): self.architecture = "x86" else: self.architecture = "x86-64" self.architecture_detected = True self.mounts = self.config.get("mounts", []) self.limits = self.config.get("limits") self.slice = self.config.get("slice") self.network = self.config.get("network", {}) self.systemd = SystemdContainer(self.runtime_config) def start(self): """ Start creating new container. """ logger.info("Starting") logger.info("Sanity check") self._sanity_check() logger.info("Creating image mounts") self._image_mount() logger.info("Creating container mounts") self._mounts() logger.info("Container firstboot") self._firstboot() logger.info("Create container unit file") self._create_unit() logger.info("Saving configuration") self._save_config() logger.info("Finished") def _sanity_check(self): """ Check directories exist and required configuration is set. """ if not os.path.exists(ETC_DIR): os.makedirs(ETC_DIR) if os.path.exists(self.path): raise NewContainerError("Container path already exists") try: os.makedirs(self.path) except PermissionError: raise NewContainerError( "Insufficient permissions to make container") def _check_if_version(self, image): """ Checks if supplied image is versioned. :param image: image with possible version to check :type image: str :return: sanitised/checked image string :rtype: str """ # Check if the image we've been given # is the name or the actual version version = check_if_versioned(image) if version: return version else: raise NewContainerError("Cannot determine version of the image") def _image_mount(self): """ Create image overlay. """ image_path = os.path.join(BASE_IMAGE_DIR, self.image, self.image_version) self.systemd.mount_image(self.name, self.version, self.path, self.image, image_path) def _mounts(self): """ Add mounts to image. """ container = os.path.join(self.name, self.version) for mount in self.mounts: self.systemd.mount_path_inside( mount["src"], self.image, container, mount["dest"], mount.get("type", "overlay"), mount.get("mode", "rw")) def _firstboot(self): """ Run 'firstboot' on the container to setup mahcine-id, hostname etc. """ self.systemd.firstboot(self.name, self.path) def _create_unit(self): """ Creates container unit file. """ privnet = self.network.get("privnet", True) if not self.architecture_detected: architecture = self.architecture else: architecture = None self.systemd.create_unit_file(self.name, self.image, self.path, privnet, architecture, self.slice, self.limits) def _save_config(self): """ Save container configuration to disk, both in the container and on the host. This is to allow the firewall setup to work later, and to record any mounts, slices or other changes. The host copy of the configuration file is always the last build container, to match what is set up in the systemd unit file. """ host_conf_file = os.path.join(ETC_CONTAINER_CONF_DIR, "{}.yaml".format(self.name)) container_conf_file = os.path.join(self.path, host_conf_file.lstrip("/")) conf_data = configfile.merge(self.runtime_config, self.all_config) conf_data["container"]["name"] = self.name conf_data["container"]["version"] = self.version configfile.write_config_file(conf_data, host_conf_file) configfile.write_config_file(conf_data, container_conf_file)