コード例 #1
0
    def get_image_registry_url(self, image_name):
        """Helper function for obtain registry url of image from it's name

        :param image_name: str, short name of an image, example:
            - conu:0.5.0
        :return: str, image registry url, example:
            - 172.30.1.1:5000/myproject/conu:0.5.0
        """
        c = self._oc_command(["get", "is", image_name,
                              "--output=jsonpath=\'{ .status.dockerImageRepository }\'"])
        try:
            internal_registry_name = run_cmd(c, return_output=True)
        except subprocess.CalledProcessError as ex:
            raise ConuException("oc get is failed: %s" % ex)

        logger.info("Image registry url: %s", internal_registry_name)

        return internal_registry_name.replace("'", "").replace('"', '')
コード例 #2
0
ファイル: image.py プロジェクト: lslebodn/conu
 def _wait_for_machine_finish(self, name):
     """
     Interna method
     wait until machine is really destroyed, machine does not exist.
     :param name: str machine name
     :return: True or exception
     """
     # TODO: rewrite it using probes module in utils
     for foo in range(constants.DEFAULT_RETRYTIMEOUT):
         time.sleep(constants.DEFAULT_SLEEP)
         out = run_cmd(
             ["machinectl", "--no-pager", "status", name],
             ignore_status=True, return_output=True)
         if out != 0:
             return True
     raise ConuException(
         "Unable to stop machine %s within %d" %
         (name, constants.DEFAULT_RETRYTIMEOUT))
コード例 #3
0
 def create_from_tuple(cls, volume):
     """
     Create instance from tuple.
     :param volume: tuple in one one of the following forms: target | source,target | source,target,mode
     :return: instance of Volume
     """
     if isinstance(volume, six.string_types):
         return Volume(target=volume)
     elif len(volume) == 2:
         return Volume(source=volume[0], target=volume[1])
     elif len(volume) == 3:
         return Volume(source=volume[0], target=volume[1], mode=volume[2])
     else:
         logger.debug(
             "Cannot create volume instance from {}."
             "It has to be tuple of form target x source,target x source,target,mode."
             .format(volume))
         raise ConuException("Cannot create volume instance.")
コード例 #4
0
ファイル: image.py プロジェクト: mateimicu/conu
    def pull(self):
        """
        Pull this image from registry. Raises an exception if the image is not found in
        the registry.

        :return: None
        """
        for json_s in self.d.pull(repository=self.name, tag=self.tag, stream=True):
            logger.debug(json_s)
            json_e = json.loads(json_s)
            status = graceful_get(json_e, "status")
            if status:
                logger.info(status)
            else:
                error = graceful_get(json_e, "error")
                logger.error(status)
                raise ConuException("There was an error while pulling the image %s: %s",
                                    self.name, error)
コード例 #5
0
    def pull(self):
        """
        Pull this image from registry. Raises an exception if the image is not found in
        the registry.

        :return: None
        """
        for json_e in self.d.pull(repository=self.name, tag=self.tag, stream=True, decode=True):
            logger.debug(json_e)
            status = graceful_get(json_e, "status")
            if status:
                logger.info(status)
            else:
                error = graceful_get(json_e, "error")
                logger.error(status)
                raise ConuException("There was an error while pulling the image %s: %s",
                                    self.name, error)
        self.using_transport(SkopeoTransport.DOCKER_DAEMON)
コード例 #6
0
    def create_app_from_template(self, image_name, name, template,
                                 name_in_template, other_images,
                                 oc_new_app_args, project):
        """
        Helper function to create app from template
        :param image_name: image to be used as builder image
        :param name: name of app from template
        :param template: str, url or local path to a template to use
        :param name_in_template: dict, {repository:tag} image name used in the template
        :param other_images: list of dict, some templates need other image to be pushed into the
               OpenShift registry, specify them in this parameter as list of dict [{<image>:<tag>}],
               where "<image>" is image name with tag and "<tag>" is a tag under which the image
               should be available in the OpenShift registry.
        :param oc_new_app_args: additional parameters for the `oc new-app`
        :param project: project where app should be created
        :return: None
        """
        self.project = project

        # push images to registry
        repository, tag = list(name_in_template.items())[0]
        self.import_image(repository + ":" + tag, image_name)

        other_images = other_images or []

        for o in other_images:
            image, tag = list(o.items())[0]
            self.import_image(
                tag.split(':')[0] + ":" + tag.split(':')[1], image)

        c = self._oc_command(["new-app"] + [template] + oc_new_app_args +
                             ["-n"] + [project] + ["--name=%s" % name])

        logger.info("Creating new app in project %s", project)

        try:
            # ignore status because sometimes oc new-app can fail when image
            # is already pushed in register
            o = run_cmd(c, return_output=True, ignore_status=True)
            logger.debug(o)
        except subprocess.CalledProcessError as ex:
            raise ConuException("oc new-app failed: %s" % ex)

        return name
コード例 #7
0
ファイル: image.py プロジェクト: pingf/conu
    def get_metadata(self, refresh=True):
        """
        return cached metadata by default

        :param refresh: bool, update the metadata with up to date content
        :return: dict
        """
        if refresh or not self._metadata:
            ident = self._id or self.get_full_name()
            if not ident:
                raise ConuException(
                    "This image does not have a valid identifier.")
            self._metadata = convert_kv_to_dict(
                run_cmd([
                    "machinectl", "--no-pager", "--output=export",
                    "show-image", ident
                ],
                        return_output=True))
        return self._metadata
コード例 #8
0
ファイル: skopeo.py プロジェクト: pombredanne/reto-ejercicio2
def transport_param(image):
    """ Parse DockerImage info into skopeo parameter

    :param image: DockerImage
    :return: string. skopeo parameter specifying image
    """
    transports = {SkopeoTransport.CONTAINERS_STORAGE: "containers-storage:",
                  SkopeoTransport.DIRECTORY: "dir:",
                  SkopeoTransport.DOCKER: "docker://",
                  SkopeoTransport.DOCKER_ARCHIVE: "docker-archive",
                  SkopeoTransport.DOCKER_DAEMON: "docker-daemon:",
                  SkopeoTransport.OCI: "oci:",
                  SkopeoTransport.OSTREE: "ostree:"}

    transport = image.transport
    tag = image.tag
    repository = image.name
    path = image.path

    if not transport:
        transport = SkopeoTransport.DOCKER
    command = transports[transport]

    path_required = [SkopeoTransport.DIRECTORY, SkopeoTransport.DOCKER_ARCHIVE, SkopeoTransport.OCI]
    if transport in path_required and path is None:
        raise ValueError(transports[transport] + " path is required to be specified")

    if transport == SkopeoTransport.DIRECTORY:
        return command + path
    if transport == SkopeoTransport.DOCKER_ARCHIVE:
        command += path
        if repository is None:
            return command
        command += ":"
    if transport in [SkopeoTransport.CONTAINERS_STORAGE, SkopeoTransport.DOCKER,
                     SkopeoTransport.DOCKER_ARCHIVE, transport.DOCKER_DAEMON]:
        return command + repository + ":" + tag
    if transport == SkopeoTransport.OCI:
        return command + path + ":" + tag
    if transport == SkopeoTransport.OSTREE:
        return command + repository + ("@" + path if path else "")

    raise ConuException("This transport is not supported")
コード例 #9
0
    def extend(self, source, new_image_name, s2i_args=None):
        """
        extend this s2i-enabled image using provided source, raises ConuException if
        `s2i build` fails

        :param source: str, source used to extend the image, can be path or url
        :param new_image_name: str, name of the new, extended image
        :param s2i_args: list of str, additional options and arguments provided to `s2i build`
        :return: S2Image instance
        """
        s2i_args = s2i_args or []
        c = self._s2i_command(["build"] + s2i_args + [source, self.get_full_name()])
        if new_image_name:
            c.append(new_image_name)
        try:
            run_cmd(c)
        except subprocess.CalledProcessError as ex:
            raise ConuException("s2i build failed: %s" % ex)
        return S2IDockerImage(new_image_name)
コード例 #10
0
    def wait_for_service(self, app_name, port, expected_output=None, timeout=100):
        """Block until service is not ready to accept requests,
        raises an exc ProbeTimeout if timeout is reached

        :param app_name: str, name of the app
        :param port: str or int, port of the service
        :param expected_output: If not None method will check output returned from request
               and try to find matching string.
        :param timeout: int or float (seconds), time to wait for pod to run
        :return: None
        """
        logger.info('Waiting for service to get ready')
        try:

            Probe(timeout=timeout, pause=10, fnc=self.request_service, app_name=app_name,
                  port=port, expected_output=expected_output, expected_retval=True).run()
        except ProbeTimeout:
            logger.warning("Timeout: Request to service unsuccessful.")
            raise ConuException("Timeout: Request to service unsuccessful.")
コード例 #11
0
ファイル: image.py プロジェクト: pombredanne/reto-ejercicio2
    def copy(self,
             repository=None,
             tag=None,
             source_transport=None,
             target_transport=SkopeoTransport.DOCKER,
             source_path=None,
             target_path=None,
             logs=True):
        """ Copy this image

        :param repository to be copied to
        :param tag
        :param source_transport Transport
        :param target_transport Transport
        :param source_path needed to specify for dir, docker-archive or oci transport
        :param target_path needed to specify for dir, docker-archive or oci transport
        :param logs enable/disable logs
        :return: the new DockerImage
        """
        if not repository:
            repository = self.name
        if not tag:
            tag = self.tag if self.tag else "latest"
        if target_transport == SkopeoTransport.OSTREE and tag and logs:
            logging.warning("tag was ignored")
        target = (DockerImage(
            repository, tag,
            pull_policy=DockerImagePullPolicy.NEVER).using_transport(
                target_transport, target_path))
        self.using_transport(source_transport, source_path)

        try:
            run_cmd([
                "skopeo", "copy",
                transport_param(self),
                transport_param(target)
            ])
        except subprocess.CalledProcessError:
            raise ConuException("There was an error while copying repository",
                                self.name)

        return target
コード例 #12
0
    def get_metadata(self, refresh=True):
        """
        return cached metadata by default

        :param refresh: bool, returns up to date metadata if set to True
        :return: dict
        """
        if refresh or not self._metadata:
            ident = self._id or self.name
            if not ident:
                raise ConuException(
                    "This container does not have a valid identifier.")
            out = run_cmd(["machinectl", "--no-pager", "show", ident],
                          return_output=True,
                          ignore_status=True)
            if "Could not get path to machine" in out:
                self._metadata = {}
            else:
                self._metadata = convert_kv_to_dict(out)
        return self._metadata
コード例 #13
0
    def start_build(self, build, args=None):
        """
        Start new build, raise exception if build failed
        :param build: str, name of the build
        :param args: list of str, another args of 'oc start-build' commands
        :return: None
        """

        args = args or []

        c = self._oc_command(["start-build"] + [build] + args)

        logger.info("Executing build %s", build)
        logger.info("Build command: %s", " ".join(c))

        try:
            Probe(timeout=-1, pause=5, count=2, expected_exceptions=subprocess.CalledProcessError,
                  expected_retval=None, fnc=run_cmd, cmd=c).run()
        except ProbeTimeout as e:
            raise ConuException("Cannot start build of application: %s" % e)
コード例 #14
0
    def run_via_binary_in_foreground(self,
                                     run_command_instance=None,
                                     popen_params=None,
                                     container_name=None):
        """
        Create a container using this image and run it in foreground;
        this method is useful to test real user scenarios when users invoke containers using
        binary and pass input into the container via STDIN. Please bear in mind that conu doesn't
        know the ID of the container when created like this, so it's highly recommended to name
        your container. You are also responsible for checking whether the container exited
        successfully via:

            container.popen_instance.returncode

        Please consult the documentation for subprocess python module for best practices on
        how you should work with instance of Popen

        :param run_command_instance: instance of DockerRunBuilder
        :param popen_params: dict, keyword arguments passed to Popen constructor
        :param container_name: str, pretty container identifier
        :return: instance of DockerContainer
        """
        logger.info("run container via binary in foreground")
        run_command_instance = run_command_instance or DockerRunBuilder()
        if not isinstance(run_command_instance, DockerRunBuilder):
            raise ConuException(
                "run_command_instance needs to be an instance of DockerRunBuilder"
            )
        popen_params = popen_params or {}
        run_command_instance.image_name = self.get_id()
        if container_name:
            run_command_instance.options += ["--name", container_name]
        logger.debug("command = %s", str(run_command_instance))
        popen_instance = subprocess.Popen(run_command_instance.build(),
                                          **popen_params)
        container_id = None
        return DockerContainer(self,
                               container_id,
                               popen_instance=popen_instance,
                               name=container_name)
コード例 #15
0
    def create_new_app_from_source(self,
                                   image,
                                   project,
                                   source=None,
                                   oc_new_app_args=None):
        """
        Deploy app using source-to-image in OpenShift cluster using 'oc new-app'
        :param image: image to be used as builder image
        :param project: project where app should be created
        :param source: source used to extend the image, can be path or url
        :param oc_new_app_args: additional parameters for the `oc new-app`
        :return: str, name of the app
        """

        # app name is generated randomly
        name = 'app-{random_string}'.format(random_string=random_str(5))

        oc_new_app_args = oc_new_app_args or []

        new_image = push_to_registry(image,
                                     image.name.split('/')[-1], image.tag,
                                     project)

        c = self._oc_command(["new-app"] + [new_image.name + "~" + source] +
                             oc_new_app_args + ["-n"] + [project] +
                             ["--name=%s" % name])

        logger.info("Creating new app in project %s", project)

        try:
            o = run_cmd(c, return_output=True)
            logger.debug(o)
        except subprocess.CalledProcessError as ex:
            raise ConuException("oc new-app failed: %s" % ex)

        # build from local source
        if os.path.isdir(source):
            self.start_build(name, ["-n", project, "--from-dir=%s" % source])

        return name
コード例 #16
0
    def import_image(self, imported_image_name, image_name):
        """Import image using `oc import-image` command.

        :param imported_image_name: str, short name of an image in internal registry, example:
            - hello-openshift:latest
        :param image_name: full repository name, example:
            - docker.io/openshift/hello-openshift:latest
        :return: str, short name in internal registry
        """

        c = self._oc_command(["import-image", imported_image_name,
                              "--from=%s" % image_name, "--confirm"])

        logger.info("Importing image from: %s, as: %s", image_name, imported_image_name)

        try:
            o = run_cmd(c, return_output=True, ignore_status=True)
            logger.debug(o)
        except subprocess.CalledProcessError as ex:
            raise ConuException("oc import-image failed: %s" % ex)

        return imported_image_name
コード例 #17
0
ファイル: image.py プロジェクト: pombredanne/reto-ejercicio2
    def using_transport(self, transport=None, path=None, logs=True):
        """ change used transport

        :param transport: from where will be this image copied
        :param path in filesystem
        :param logs enable/disable
        :return: self
        """
        if not transport:
            return self

        if self.transport == transport and self.path == path:
            return self

        path_required = [
            SkopeoTransport.DIRECTORY, SkopeoTransport.DOCKER_ARCHIVE,
            SkopeoTransport.OCI
        ]

        if transport in path_required:
            if not path and logs:
                logging.debug("path not provided, temporary path was used")
            self.path = self.mount(path).mount_point
        elif transport == SkopeoTransport.OSTREE:
            if path and not os.path.isabs(path):
                raise ConuException("Path '", path,
                                    "' for OSTree transport is not absolute")
            if not path and logs:
                logging.debug(
                    "path not provided, default /ostree/repo path was used")
            self.path = path
        else:
            if path and logs:
                logging.warning("path %s was ignored!", path)
            self.path = None

        self.transport = transport
        return self
コード例 #18
0
    def _internal_reschedule(callback,
                             retry=3,
                             sleep_time=constants.DEFAULT_SLEEP):
        """
        workaround method for internal_run_container method
        It sometimes fails because of Dbus or whatever, so try to start it moretimes

        :param callback: callback method list
        :param retry: how many times try to invoke command
        :param sleep_time: how long wait before subprocess.poll() to find if it failed
        :return: subprocess object
        """
        for foo in range(retry):
            container_process = callback[0](callback[1], *callback[2],
                                            **callback[3])
            time.sleep(sleep_time)
            container_process.poll()
            rcode = container_process.returncode
            if rcode is None:
                return container_process
        raise ConuException(
            "Unable to start nspawn container - process failed for {}-times".
            format(retry))
コード例 #19
0
    def _run_container(self, run_command_instance, callback):
        """ this is internal method """
        tmpfile = os.path.join(get_backend_tmpdir(), random_tmp_filename())
        # the cid file must not exist
        run_command_instance.options += ["--cidfile=%s" % tmpfile]
        logger.debug("docker command: %s" % run_command_instance)
        response = callback()

        def get_cont_id():
            if not os.path.exists(tmpfile):
                return False
            with open(tmpfile, 'r') as fd:
                content = fd.read()
            return bool(content)

        Probe(timeout=2, count=10, pause=0.1, fnc=get_cont_id).run()

        with open(tmpfile, 'r') as fd:
            container_id = fd.read()

        if not container_id:
            raise ConuException("We could not get container's ID, it probably was not created")
        return container_id, response
コード例 #20
0
    def __init__(self,
                 repository,
                 tag=None,
                 pull_policy=ImagePullPolicy.IF_NOT_PRESENT,
                 location=None):
        """
        :param repository: str, expected name of this image
        :param tag: not used, nspawn doesn't utilize the concept of tags
        :param pull_policy: enum, strategy to apply for pulling the image
        :param location: str, location from which we can obtain the image, it can be local (a
                path) or remote (URL)
        """
        self.system_requirements()
        self.container_process = None

        super(NspawnImage, self).__init__(repository, tag=None)
        if not isinstance(pull_policy, ImagePullPolicy):
            raise ConuException(
                "'pull_policy' is not an instance of ImagePullPolicy")
        self.pull_policy = pull_policy
        self.location = location
        self.local_location = os.path.join(
            CONU_IMAGES_STORE,
            self.name + ("_" + self.tag if self.tag else ""))
        # TODO: move this code to API __init__, will be same for various
        # backends or maybe add there some callback method
        if self.pull_policy == ImagePullPolicy.ALWAYS:
            logger.debug("pull policy set to 'always', pulling the image")
            self.pull()
        elif self.pull_policy == ImagePullPolicy.IF_NOT_PRESENT and not self.is_present(
        ):
            logger.debug(
                "pull policy set to 'if_not_present' and image is not present, "
                "pulling the image")
            self.pull()
        elif self.pull_policy == ImagePullPolicy.NEVER:
            logger.debug("pull policy set to 'never'")
コード例 #21
0
    def request_service(self, app_name, port, expected_output=None):
        """
        Make request on service of app. If there is connection error function return False.
        :param app_name: str, name of the app
        :param expected_output: str, If not None method will check output returned from request
               and try to find matching string.
        :param port: str or int, port of the service
        :return: bool, True if connection was established False if there was connection error
        """

        # get ip of service
        ip = [
            service.get_ip()
            for service in self.list_services(namespace=self.project)
            if service.name == app_name
        ][0]

        # make http request to obtain output
        if expected_output is not None:
            try:
                output = self.http_request(host=ip, port=port)
                if expected_output not in output.text:
                    raise ConuException(
                        "Connection to service established, but didn't match expected output"
                    )
                else:
                    logger.info(
                        "Connection to service established and return expected output!"
                    )
                    return True
            except ConnectionError as e:
                logger.info("Connection to service failed %s!", e)
                return False
        elif check_port(port, host=ip):  # check if port is open
            return True

        return False
コード例 #22
0
    def has_pkgs_signed_with(self, allowed_keys):
        """
        Check signature of packages installed in image.
        Raises exception when

        * rpm binary is not installed in image
        * parsing of rpm fails
        * there are packages in image that are not signed with one of allowed keys

        :param allowed_keys: list of allowed keys
        :return: bool
        """

        if not allowed_keys or not isinstance(allowed_keys, list):
            raise ConuException("allowed_keys must be a list")
        command = ['rpm', '-qa', '--qf', '%{name} %{SIGPGP:pgpsig}\n']
        cont = self.run_via_binary(command=command)
        try:
            out = cont.logs_unicode()[:-1].split('\n')
            check_signatures(out, allowed_keys)
        finally:
            cont.stop()
            cont.delete()
        return True
コード例 #23
0
    def run_in_pod(self, namespace="default"):
        """
        run image inside Kubernetes Pod
        :param namespace: str, name of namespace where pod will be created
        :return: Pod instance
        """

        core_api = get_core_api()

        image_data = self.get_metadata()

        pod = Pod.create(image_data)

        try:
            pod_instance = core_api.create_namespaced_pod(namespace=namespace, body=pod)
        except ApiException as e:
            raise ConuException("Exception when calling CoreV1Api->create_namespaced_pod: %s\n" % e)

        logger.info(
            "Starting Pod %s in namespace %s" % (pod_instance.metadata.name, namespace))

        return Pod(name=pod_instance.metadata.name,
                   namespace=pod_instance.metadata.namespace,
                   spec=pod_instance.spec)
コード例 #24
0
    def _wait_for_machine_booted(name, suffictinet_texts=None):
        """
        Internal method
        wait until machine is ready, in common case means there is running systemd-logind

        :param name: str with machine name
        :param suffictinet_texts: alternative text to check in output
        :return: True or exception
        """
        # TODO: rewrite it using probes module in utils
        suffictinet_texts = suffictinet_texts or ["systemd-logind"]
        # optionally use: "Unit: machine"
        for foo in range(constants.DEFAULT_RETRYTIMEOUT):
            time.sleep(constants.DEFAULT_SLEEP)
            out = run_cmd(["machinectl", "--no-pager", "status", name],
                          ignore_status=True,
                          return_output=True)
            for restr in suffictinet_texts:
                if restr in out:
                    time.sleep(constants.DEFAULT_SLEEP)
                    return True
        raise ConuException(
            "Unable to start machine %s within %d (machinectl status command dos not contain %s)"
            % (name, constants.DEFAULT_RETRYTIMEOUT, suffictinet_texts))
コード例 #25
0
ファイル: backend.py プロジェクト: pingf/conu
    def __init__(self, logging_level=logging.INFO, logging_kwargs=None, cleanup=None):
        """
        This method serves as a configuration interface for conu.

        :param logging_level: int, control logger verbosity: see logging.{DEBUG,INFO,ERROR}
        :param logging_kwargs: dict, additional keyword arguments for logger set up, for more info
                                see docstring of set_logging function
        :param cleanup: list, list of k8s cleanup policy values, examples:
            - [CleanupPolicy.EVERYTHING]
            - [CleanupPolicy.PODS, CleanupPolicy.SERVICES]
            - [CleanupPolicy.NOTHING]
        """
        super(K8sBackend, self).__init__(
            logging_level=logging_level, logging_kwargs=logging_kwargs)

        self.core_api = get_core_api()
        self.apps_api = get_apps_api()

        self.managed_namespaces = []

        self.cleanup = cleanup or [K8sCleanupPolicy.NOTHING]

        if K8sCleanupPolicy.NOTHING in self.cleanup and len(self.cleanup) != 1:
            raise ConuException("Cleanup policy NOTHING cannot be combined with other values")
コード例 #26
0
ファイル: image.py プロジェクト: r00ta/conu
    def run_via_binary_in_foreground(self,
                                     run_command_instance=None,
                                     command=None,
                                     volumes=None,
                                     additional_opts=None,
                                     popen_params=None,
                                     container_name=None):
        """
        Create a container using this image and run it in foreground;
        this method is useful to test real user scenarios when users invoke containers using
        binary and pass input into the container via STDIN. You are also responsible for:

         * redirecting STDIN when intending to use container.write_to_stdin afterwards by setting
              popen_params={"stdin": subprocess.PIPE} during run_via_binary_in_foreground

         * checking whether the container exited successfully via:
              container.popen_instance.returncode

        Please consult the documentation for subprocess python module for best practices on
        how you should work with instance of Popen

        :param run_command_instance: instance of PodmanRunBuilder
        :param command: list of str, command to run in the container, examples:
            - ["ls", "/"]
            - ["bash", "-c", "ls / | grep bin"]
        :param volumes: tuple or list of tuples in the form:

            * `("/path/to/directory", )`
            * `("/host/path", "/container/path")`
            * `("/host/path", "/container/path", "mode")`
            * `(conu.Directory('/host/path'), "/container/path")` (source can be also
                Directory instance)

        :param additional_opts: list of str, additional options for `docker run`
        :param popen_params: dict, keyword arguments passed to Popen constructor
        :param container_name: str, pretty container identifier
        :return: instance of PodmanContainer
        """
        logger.info("run container via binary in foreground")

        if (command is not None or additional_opts is not None) \
                and run_command_instance is not None:
            raise ConuException(
                "run_command_instance and command parameters cannot be "
                "passed into method at same time")

        if run_command_instance is None:
            command = command or []
            additional_opts = additional_opts or []

            if (isinstance(command, list) or isinstance(command, tuple)
                    and isinstance(additional_opts, list)
                    or isinstance(additional_opts, tuple)):
                run_command_instance = PodmanRunBuilder(
                    command=command, additional_opts=additional_opts)
            else:
                raise ConuException(
                    "command and additional_opts needs to be list of str or None"
                )
        else:
            run_command_instance = run_command_instance or PodmanRunBuilder()
            if not isinstance(run_command_instance, PodmanRunBuilder):
                raise ConuException("run_command_instance needs to be an "
                                    "instance of PodmanRunBuilder")

        popen_params = popen_params or {}

        run_command_instance.image_name = self.get_id()
        if container_name:
            run_command_instance.options += ["--name", container_name]

        if volumes:
            run_command_instance.options += self.get_volume_options(
                volumes=volumes)

        def callback():
            return subprocess.Popen(run_command_instance.build(),
                                    **popen_params)

        container_id, popen_instance = self._run_container(
            run_command_instance, callback)

        actual_name = graceful_get(self._inspect(container_id), "Name")

        if container_name and container_name != actual_name:
            raise ConuException(
                "Unexpected container name value. Expected = " +
                str(container_name) + " Actual = " + str(actual_name))
        if not container_name:
            container_name = actual_name
        return PodmanContainer(self,
                               container_id,
                               popen_instance=popen_instance,
                               name=container_name)
コード例 #27
0
ファイル: image.py プロジェクト: r00ta/conu
    def run_via_binary(self,
                       run_command_instance=None,
                       command=None,
                       volumes=None,
                       additional_opts=None,
                       **kwargs):
        """
        create a container using this image and run it in background;
        this method is useful to test real user scenarios when users invoke containers using
        binary

        :param run_command_instance: instance of PodmanRunBuilder
        :param command: list of str, command to run in the container, examples:
            - ["ls", "/"]
            - ["bash", "-c", "ls / | grep bin"]
        :param volumes: tuple or list of tuples in the form:

            * `("/path/to/directory", )`
            * `("/host/path", "/container/path")`
            * `("/host/path", "/container/path", "mode")`
            * `(conu.Directory('/host/path'), "/container/path")` (source can be also
                Directory instance)

        :param additional_opts: list of str, additional options for `podman run`
        :return: instance of PodmanContainer
        """

        logger.info("run container via binary in background")

        if (command is not None or additional_opts is not None) \
                and run_command_instance is not None:
            raise ConuException(
                "run_command_instance and command parameters cannot be passed "
                "into method at same time")

        if run_command_instance is None:
            command = command or []
            additional_opts = additional_opts or []

            if (isinstance(command, list) or isinstance(command, tuple)
                    and isinstance(additional_opts, list)
                    or isinstance(additional_opts, tuple)):
                run_command_instance = PodmanRunBuilder(
                    command=command, additional_opts=additional_opts)
            else:
                raise ConuException(
                    "command and additional_opts needs to be list of str or None"
                )
        else:
            run_command_instance = run_command_instance or PodmanRunBuilder()
            if not isinstance(run_command_instance, PodmanRunBuilder):
                raise ConuException(
                    "run_command_instance needs to be an instance of PodmanRunBuilder"
                )

        run_command_instance.image_name = self.get_id()
        run_command_instance.options += ["-d"]

        if volumes:
            run_command_instance.options += self.get_volume_options(
                volumes=volumes)

        def callback():
            try:
                # FIXME: catch std{out,err}, print stdout to logger.debug, stderr to logger.error
                run_cmd(run_command_instance.build())
            except subprocess.CalledProcessError as ex:
                raise ConuException("Container exited with an error: %s" %
                                    ex.returncode)

        container_id, _ = self._run_container(run_command_instance, callback)
        container_name = graceful_get(self._inspect(container_id), "Name")

        return PodmanContainer(self, container_id, name=container_name)
コード例 #28
0
 def callback():
     try:
         # FIXME: catch std{out,err}, print stdout to logger.debug, stderr to logger.error
         run_cmd(run_command_instance.build())
     except subprocess.CalledProcessError as ex:
         raise ConuException("Container exited with an error: %s" % ex.returncode)
コード例 #29
0
ファイル: image.py プロジェクト: lslebodn/conu
    def bootstrap(
            repositories, name, packages=None, additional_packages=None,
            tag="latest", prefix=constants.CONU_ARTIFACT_TAG, packager=None):
        """
        bootstrap Image from scratch. It creates new image based on giver dnf repositories and package setts

        :param repositories:list of repositories
        :param packages: list of base packages in case you don't want to use base packages defined in contants
        :param additional_packages: list of additonal packages
        :param tag: tag the output image
        :param prefix: use some prefix for newly cretaed image (internal usage)
        :param packager: use another packages in case you dont want to use defined in constants (dnf)
        :return: NspawnImage instance
        """
        additional_packages = additional_packages or []
        if packages is None:
            packages = constants.CONU_NSPAWN_BASEPACKAGES
        package_set = packages + additional_packages
        if packager is None:
            packager = constants.BOOTSTRAP_PACKAGER
        mounted_dir = mkdtemp()
        if not os.path.exists(mounted_dir):
            os.makedirs(mounted_dir)
        imdescriptor, tempimagefile = mkstemp()
        image_size = constants.BOOTSTRAP_IMAGE_SIZE_IN_MB
        # create no partitions when create own image
        run_cmd(["dd", "if=/dev/zero", "of={}".format(tempimagefile),
                 "bs=1M", "count=1", "seek={}".format(image_size)])
        run_cmd([constants.BOOTSTRAP_FS_UTIL, tempimagefile])
        # TODO: is there possible to use NspawnImageFS class instead of direct
        # mount/umount, image objects does not exist
        run_cmd(["mount", tempimagefile, mounted_dir])
        if os.path.exists(os.path.join(mounted_dir, "usr")):
            raise ConuException("Directory %s already in use" % mounted_dir)
        if not os.path.exists(mounted_dir):
            os.makedirs(mounted_dir)
        repo_params = []
        repo_file_content = ""
        for cnt in range(len(repositories)):
            repo_params += ["--repofrompath",
                            "{0}{1},{2}".format(prefix, cnt, repositories[cnt])]
            repo_file_content += """
[{NAME}{CNT}]
name={NAME}{CNT}
baseurl={REPO}
enabled=1
gpgcheck=0
""".format(NAME=prefix, CNT=cnt, REPO=repositories[cnt])
        packages += set(additional_packages)
        logger.debug("Install system to direcory: %s" % mounted_dir)
        logger.debug("Install packages: %s" % packages)
        logger.debug("Repositories: %s" % repositories)
        packager_addition = [
            "--installroot",
            mounted_dir,
            "--disablerepo",
            "*",
            "--enablerepo",
            prefix + "*"]
        final_command = packager + packager_addition + repo_params + package_set
        try:
            run_cmd(final_command)
        except Exception as e:
            raise ConuException("Unable to install packages via command: {} (original exception {})".format(final_command, e))
        insiderepopath = os.path.join(
            mounted_dir,
            "etc",
            "yum.repos.d",
            "{}.repo".format(prefix))
        if not os.path.exists(os.path.dirname(insiderepopath)):
            os.makedirs(os.path.dirname(insiderepopath))
        with open(insiderepopath, 'w') as f:
            f.write(repo_file_content)
        run_cmd(["umount", mounted_dir])
        # add sleep before umount, to ensure, that kernel finish ops
        time.sleep(constants.DEFAULT_SLEEP)
        nspawnimage = NspawnImage(repository=name, location=tempimagefile, tag=tag)
        os.remove(tempimagefile)
        os.rmdir(mounted_dir)
        return nspawnimage
コード例 #30
0
ファイル: filesystem.py プロジェクト: jischebeck/conu
    def __init__(self,
                 path,
                 mode=None,
                 user_owner=None,
                 group_owner=None,
                 facl_rules=None,
                 selinux_context=None,
                 selinux_user=None,
                 selinux_role=None,
                 selinux_type=None,
                 selinux_range=None):
        """
        For more info on SELinux, please see `$ man chcon`. An exception will be thrown if
        selinux_context is specified and at least one of other SELinux fields.

        :param path: str, path to the directory we will operate on
        :param mode: int, octal representation of permission bits, e.g. 0o0400
        :param user_owner: str or int, uid or username to own the directory
        :param group_owner: str or int, gid or group name to own the directory
        :param facl_rules: list of str, file ACLs to apply, e.g. "u:26:rwx"
        :param selinux_context: str, set directory to this SELinux context (this is the full
                context with all the field, example: "system_u:object_r:unlabeled_t:s0")
        :param selinux_user: str, user in the target security context, e.g. "system_u"
        :param selinux_role: str, role in the target security context, e.g. "object_r"
        :param selinux_type: str, type in the target security context, e.g. "unlabeled_t"
        :param selinux_range: str, range in the target security context, e.g. "s0"
        """
        if selinux_context and any(
            [selinux_user, selinux_role, selinux_type, selinux_range]):
            raise ConuException(
                "Don't specify both selinux_context and some of its fields.")
        if any([
                selinux_context, selinux_user, selinux_role, selinux_type,
                selinux_range
        ]):
            if is_selinux_disabled():
                raise ConuException(
                    "You are trying to apply SELinux labels, but SELinux is "
                    "disabled on this system. Please enable it first.")

        # if set to True, it means the directory is created and set up
        self._initialized = False

        # TODO: if path is None, we could do mkdtemp
        self.path = path
        self.mode = mode
        self.selinux_context = selinux_context
        self.selinux_user = selinux_user
        self.selinux_role = selinux_role
        self.selinux_type = selinux_type
        self.selinux_range = selinux_range
        self.facl_rules = facl_rules

        # os.chown wants int
        if isinstance(user_owner, six.string_types):
            try:
                self.owner = pwd.getpwnam(user_owner)[2]
            except KeyError as ex:
                raise ConuException("User %r not found, error message: %r" %
                                    (user_owner, ex))
        else:
            self.owner = user_owner
        if isinstance(group_owner, six.string_types):
            try:
                self.group = pwd.getpwnam(group_owner)[3]
            except KeyError as ex:
                raise ConuException("Group %r not found, error message: %r" %
                                    (group_owner, ex))
        else:
            self.group = group_owner