Exemple #1
0
    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)
Exemple #2
0
    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)
Exemple #3
0
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)))
Exemple #4
0
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)