def test_failure_for_incorrect_urlpath(self):
        labels = self.good_container_dict["Labels"]
        labels[SIMPHONY_NS_RUNINFO.urlpath] = (
            labels[SIMPHONY_NS_RUNINFO.urlpath] + '/')

        with self.assertRaises(ValueError):
            Container.from_docker_dict(self.good_container_dict)
Example #2
0
    def test_failure_for_incorrect_urlpath(self):
        labels = self.good_container_dict["Labels"]
        labels[SIMPHONY_NS_RUNINFO.urlpath] = (
            labels[SIMPHONY_NS_RUNINFO.urlpath] + '/')

        with self.assertRaises(ValueError):
            Container.from_docker_dict(self.good_container_dict)
Example #3
0
    def test_from_docker_dict_inspect_container(self):
        client = VirtualDockerClient.with_containers()
        actual = Container.from_docker_dict(
            client.inspect_container(
                'd2b56bffb5655cb7668b685b80116041a20ee8662ebfa5b5cb68cfc423d9dc30'
            ))  # noqa

        expected = Container(
            docker_id=
            'd2b56bffb5655cb7668b685b80116041a20ee8662ebfa5b5cb68cfc423d9dc30',  # noqa
            name=
            "/myrealm-johndoe-5b34ce60d95742fa828cdced12b4c342-ascvbefsda",  # noqa
            image_name='simphonyproject/simphony-mayavi:0.6.0',
            image_id=
            'sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',  # noqa
            user="******",
            ip='0.0.0.0',
            port=666,
            url_id="20dcb84cdbea4b1899447246789093d0",
            mapping_id="5b34ce60d95742fa828cdced12b4c342",
            realm="myrealm",
            urlpath=
            "/user/johndoe/containers/20dcb84cdbea4b1899447246789093d0"  # noqa
        )

        assert_containers_equal(self, actual, expected)
    def test_no_realm(self):
        labels = self.good_container_dict["Labels"]
        labels[SIMPHONY_NS_RUNINFO.realm] = ""

        container = Container.from_docker_dict(self.good_container_dict)

        self.assertEqual(container.realm, "")
Example #5
0
    def test_no_realm(self):
        labels = self.good_container_dict["Labels"]
        labels[SIMPHONY_NS_RUNINFO.realm] = ""

        container = Container.from_docker_dict(self.good_container_dict)

        self.assertEqual(container.realm, "")
Example #6
0
    def test_find_from_mapping_id(self):
        """ Test containers_for_mapping_id returns a list of Container """
        result = yield self.manager.find_containers(
            user_name="johndoe", mapping_id="5b34ce60d95742fa828cdced12b4c342")

        expected = Container(
            docker_id=
            'd2b56bffb5655cb7668b685b80116041a20ee8662ebfa5b5cb68cfc423d9dc30',  # noqa
            mapping_id="5b34ce60d95742fa828cdced12b4c342",
            name=
            "/myrealm-johndoe-5b34ce60d95742fa828cdced12b4c342-ascvbefsda",  # noqa
            image_name='simphonyproject/simphony-mayavi:0.6.0',  # noqa
            user="******",
            image_id=
            "sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",  # noqa
            ip='127.0.0.1',
            port=666,
            url_id='20dcb84cdbea4b1899447246789093d0',
            realm="myrealm",
            urlpath=
            "/user/johndoe/containers/20dcb84cdbea4b1899447246789093d0"  # noqa
        )

        self.assertEqual(len(result), 1)
        utils.assert_containers_equal(self, result[0], expected)
    def test_retrieve(self):
        _, data = self.get("/api/v1/applications/one/", httpstatus.OK)

        self.assertEqual(
            data, {
                'mapping_id': "one",
                'image': {
                    'configurables': [],
                    'description': '',
                    'type': '',
                    'icon_128': '',
                    'name': 'boo',
                    'policy': {
                        "allow_home": True,
                        "volume_mode": 'ro',
                        "volume_source": "foo",
                        "volume_target": "bar",
                    },
                    'ui_name': 'foo_ui'
                }
            })

        self._app.container_manager.find_containers = \
            mock_coro_factory(return_value=[Container(
                name="container",
                image_name="xxx",
                url_id="yyy")])

        _, data = self.get("/api/v1/applications/one/", httpstatus.OK)

        self.assertEqual(
            data, {
                'mapping_id': "one",
                'container': {
                    'image_name': 'xxx',
                    'name': 'container',
                    'url_id': 'yyy'
                },
                'image': {
                    'description': '',
                    'icon_128': '',
                    'type': '',
                    'name': 'boo',
                    'ui_name': 'foo_ui',
                    'policy': {
                        "allow_home": True,
                        "volume_mode": 'ro',
                        "volume_source": "foo",
                        "volume_target": "bar",
                    },
                    'configurables': [],
                }
            })

        self.get("/api/v1/applications/three/", httpstatus.NOT_FOUND)

        # Check the not found case if the image is not present
        self._app.container_manager.image = mock_coro_factory(None)

        self.get("/api/v1/applications/one/", httpstatus.NOT_FOUND)
    def test_from_docker_dict_without_public_port(self):
        container_dict = self.good_container_dict
        del container_dict["Ports"][0]["PublicPort"]

        # Container without public port
        actual = Container.from_docker_dict(container_dict)
        expected = self.expected
        expected.port = 80
        assert_containers_equal(self, actual, expected)
Example #9
0
    def test_from_docker_dict_without_public_port(self):
        container_dict = self.good_container_dict
        del container_dict["Ports"][0]["PublicPort"]

        # Container without public port
        actual = Container.from_docker_dict(container_dict)
        expected = self.expected
        expected.port = 80
        assert_containers_equal(self, actual, expected)
Example #10
0
    def setUp(self):
        self.good_container_dict = {
            'Command':
            '/startup.sh',
            'Created':
            1466756760,
            'HostConfig': {
                'NetworkMode': 'default'
            },
            'Id':
            'b55a25bdda5273a4a835dbf7843937daff2f124cd6e39e6546bb0f9e6a84a76c',  # noqa
            'Image':
            'empty-ubuntu:latest',
            'ImageID':
            'sha256:14f98aa95d388cabbc4aa44b4b547b729c64673f51fc3321dccdf42fee20f01a',  # noqa
            'Labels': {
                SIMPHONY_NS.ui_name:
                'Empty Ubuntu',
                SIMPHONY_NS_RUNINFO.user:
                '******',
                SIMPHONY_NS_RUNINFO.url_id:
                "8e2fe66d5de74db9bbab50c0d2f92b33",  # noqa
                SIMPHONY_NS_RUNINFO.realm:
                "myrealm",
                SIMPHONY_NS_RUNINFO.mapping_id:
                "492b7c27bb2041278ae851be1c551f4b",  # noqa
                SIMPHONY_NS_RUNINFO.urlpath:
                "/user/johndoe/containers/8e2fe66d5de74db9bbab50c0d2f92b33"
            },  # noqa
            'Names': ['/myrealm-johndoe-empty-ubuntu_3Alatest'],
            'Ports': [{
                'IP': '0.0.0.0',
                'PrivatePort': 8888,
                'PublicPort': 32823,
                'Type': 'tcp'
            }],
            'State':
            'running',
            'Status':
            'Up 56 minutes'
        }

        self.expected = Container(
            docker_id=
            'b55a25bdda5273a4a835dbf7843937daff2f124cd6e39e6546bb0f9e6a84a76c',  # noqa
            name='/myrealm-johndoe-empty-ubuntu_3Alatest',
            image_name='empty-ubuntu:latest',
            image_id=
            'sha256:14f98aa95d388cabbc4aa44b4b547b729c64673f51fc3321dccdf42fee20f01a',  # noqa
            user="******",
            ip='0.0.0.0',
            port=32823,
            mapping_id="492b7c27bb2041278ae851be1c551f4b",
            url_id="8e2fe66d5de74db9bbab50c0d2f92b33",
            urlpath=
            "/user/johndoe/containers/8e2fe66d5de74db9bbab50c0d2f92b33",  # noqa
            realm="myrealm")
    def _stop_and_remove_container(self, container_id):
        """Idempotent removal of a container by id.
        If the container is there, it will be removed. If it's not
        there, the unexpected conditions will be logged.

        Note: The container is only stopped if it belongs to the same
        realm.
        """
        self.log.info("Stopping container {}".format(container_id))

        container_info = yield self._get_container_info(container_id)

        if container_info is None:
            self.log.error('Could not find requested container {} '
                           'during removal'.format(container_id))
            return

        container = Container.from_docker_dict(container_info)
        if container.realm != self.realm:
            self.log.error(
                'Container {} belongs to realm {} '
                'instead of {}. Refusing to stop.'.format(
                    container_id,
                    container.realm,
                    self.realm)
            )
            return

        # Technically, we have a race condition here, but it's pretty much
        # impossible to solve, and would only affect us if the container
        # id is identical.

        # Stop the container
        try:
            yield self._docker_client.stop(container_id)
        except APIError:
            self.log.exception(
                "Container '{}' could not be stopped.".format(
                    container_id,
                )
            )
        else:
            self.log.info("Container '{}' is stopped.".format(container_id))

        # Remove the container from docker
        try:
            yield self._docker_client.remove_container(container_id)
        except NotFound:
            self.log.error('Could not find requested container {} '
                           'during removal'.format(container_id))
        except APIError:
            self.log.exception(
                "Removal failed for container '{}'.".format(container_id)
            )
        else:
            self.log.info("Container '{}' is removed.".format(container_id))
    def test_from_docker_dict_with_public_port(self):
        """Test convertion from "docker ps" to Container with public port"""
        # With public port
        container_dict = self.good_container_dict

        # Container with public port
        actual = Container.from_docker_dict(container_dict)
        expected = self.expected

        assert_containers_equal(self, actual, expected)
Example #13
0
    def test_from_docker_dict_with_public_port(self):
        """Test convertion from "docker ps" to Container with public port"""
        # With public port
        container_dict = self.good_container_dict

        # Container with public port
        actual = Container.from_docker_dict(container_dict)
        expected = self.expected

        assert_containers_equal(self, actual, expected)
    def test_from_docker_dict_inspect_container(self):
        client = VirtualDockerClient.with_containers()
        actual = Container.from_docker_dict(
            client.inspect_container('d2b56bffb5655cb7668b685b80116041a20ee8662ebfa5b5cb68cfc423d9dc30'))  # noqa

        expected = Container(
            docker_id='d2b56bffb5655cb7668b685b80116041a20ee8662ebfa5b5cb68cfc423d9dc30',  # noqa
            name="/myrealm-johndoe-5b34ce60d95742fa828cdced12b4c342-ascvbefsda",  # noqa
            image_name='simphonyproject/simphony-mayavi:0.6.0',
            image_id='sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',  # noqa
            user="******",
            ip='0.0.0.0',
            port=666,
            url_id="20dcb84cdbea4b1899447246789093d0",
            mapping_id="5b34ce60d95742fa828cdced12b4c342",
            realm="myrealm",
            urlpath="/user/johndoe/containers/20dcb84cdbea4b1899447246789093d0"  # noqa
        )

        assert_containers_equal(self, actual, expected)
    def containers_from_filters(self, filters):
        """Returns the currently running containers for a given filter

        Parameters
        ----------
        filters: dict
            A dictionary of filters as in dockerpy

        Return
        ------
        A list of Container objects, or an empty list if nothing is found.
        """
        containers = []
        infos = yield self._docker_client.containers(filters=filters)
        for info in infos:
            try:
                container = Container.from_docker_dict(info)
            except ValueError:
                self.log.exception("Unable to parse container info.")
                continue

            # override the ip and port obtained by the docker info with the
            # appropriate ip and port, considering that we might be using a
            # separate docker machine
            try:
                ip, port = yield from self._get_ip_and_port(
                    container.docker_id)
            except RuntimeError:
                self.log.exception(
                    "Unable to retrieve ip/port "
                    "for container {}".format(container.docker_id))
                continue

            container.ip = ip
            container.port = port
            containers.append(container)

        return containers
    def test_multiple_ports_data(self):
        docker_dict = {'Id': 'container_id1',
                       'Config': {
                           'Labels': {
                               'eu.simphony-project.docker.ui_name': 'Mayavi 4.4.4',  # noqa
                               'eu.simphony-project.docker.env.x11-depth': '',
                               'eu.simphony-project.docker.type': 'vncapp',
                               'eu.simphony-project.docker.env.x11-height': '',
                               'eu.simphony-project.docker.env.x11-width': '',
                               'eu.simphony-project.docker.description':
                                   'Ubuntu machine with mayavi preinstalled'},
                           'Image': 'image_name1'
                       },
                       'NetworkSettings': {
                           'Ports': {
                               '8889/tcp': [
                                   {'HostPort': '667',
                                    'HostIp': '0.0.0.0'}
                               ],
                               '8888/tcp': [
                                   {'HostPort': '666', 'HostIp': '0.0.0.0'}
                               ]
                           }
                       },
                       'Name': '/container_name1',
                       'State': 'running',
                       'Image': 'image_id1'
                       }

        docker_dict["NetworkSettings"]["Ports"] = {
            '8888/tcp': [{'HostIp': '0.0.0.0', 'HostPort': '666'}],
            '8889/tcp': [{'HostIp': '0.0.0.0', 'HostPort': '667'}]
        }
        with self.assertRaises(ValueError):
            Container.from_docker_dict(docker_dict)

        docker_dict["NetworkSettings"]["Ports"] = {
            '8888/tcp': [
                {'HostIp': '0.0.0.0', 'HostPort': '32782'},
                {'HostIp': '0.0.0.0', 'HostPort': '32783'}
            ]
        }
        with self.assertRaises(ValueError):
            Container.from_docker_dict(docker_dict)

        docker_dict = {
            'Labels': {
                'eu.simphony-project.docker.runinfo.user': '******',
                'eu.simphony-project.docker.env.x11-depth': '',
                'eu.simphony-project.docker.runinfo.mapping_id': 'mapping_id',
                'eu.simphony-project.docker.env.x11-height': '',
                'eu.simphony-project.docker.ui_name': 'Mayavi 4.4.4',
                'eu.simphony-project.docker.runinfo.urlpath': '/user/username/containers/url_id',  # noqa
                'eu.simphony-project.docker.type': 'vncapp',
                'eu.simphony-project.docker.runinfo.realm': 'myrealm',
                'eu.simphony-project.docker.description': 'Ubuntu machine with mayavi preinstalled',  # noqa
                'eu.simphony-project.docker.env.x11-width': '',
                'eu.simphony-project.docker.runinfo.url_id': 'url_id'
            },
            'Names': ['/myrealm-username-mapping_5Fid'],
            'Ports': [{'Type': 'tcp', 'IP': '0.0.0.0', 'PublicPort': 666, 'PrivatePort': 8888}],  # noqa
            'State': 'running',
            'Command': '/sbin/init -D',
            'Image': 'image_name1',
            'Id': 'container_id1', 'ImageID': 'image_id1'
        }

        docker_dict["Ports"] = [
             {
                'IP': '0.0.0.0',
                'PublicIP': 34567,
                'PrivatePort': 22,
                'Type': 'tcp'
             },
             {
                'IP': '0.0.0.0',
                'PublicIP': 34562,
                'PrivatePort': 21,
                'Type': 'tcp'
             }
        ]
        with self.assertRaises(ValueError):
            Container.from_docker_dict(docker_dict)
Example #17
0
    def test_host_url(self):
        container = Container(ip="123.45.67.89", port=31337)

        self.assertEqual(container.host_url, "http://123.45.67.89:31337")
Example #18
0
    def test_multiple_ports_data(self):
        docker_dict = {
            'Id': 'container_id1',
            'Config': {
                'Labels': {
                    'eu.simphony-project.docker.ui_name':
                    'Mayavi 4.4.4',  # noqa
                    'eu.simphony-project.docker.env.x11-depth':
                    '',
                    'eu.simphony-project.docker.type':
                    'vncapp',
                    'eu.simphony-project.docker.env.x11-height':
                    '',
                    'eu.simphony-project.docker.env.x11-width':
                    '',
                    'eu.simphony-project.docker.description':
                    'Ubuntu machine with mayavi preinstalled'
                },
                'Image': 'image_name1'
            },
            'NetworkSettings': {
                'Ports': {
                    '8889/tcp': [{
                        'HostPort': '667',
                        'HostIp': '0.0.0.0'
                    }],
                    '8888/tcp': [{
                        'HostPort': '666',
                        'HostIp': '0.0.0.0'
                    }]
                }
            },
            'Name': '/container_name1',
            'State': 'running',
            'Image': 'image_id1'
        }

        docker_dict["NetworkSettings"]["Ports"] = {
            '8888/tcp': [{
                'HostIp': '0.0.0.0',
                'HostPort': '666'
            }],
            '8889/tcp': [{
                'HostIp': '0.0.0.0',
                'HostPort': '667'
            }]
        }
        with self.assertRaises(ValueError):
            Container.from_docker_dict(docker_dict)

        docker_dict["NetworkSettings"]["Ports"] = {
            '8888/tcp': [{
                'HostIp': '0.0.0.0',
                'HostPort': '32782'
            }, {
                'HostIp': '0.0.0.0',
                'HostPort': '32783'
            }]
        }
        with self.assertRaises(ValueError):
            Container.from_docker_dict(docker_dict)

        docker_dict = {
            'Labels': {
                'eu.simphony-project.docker.runinfo.user': '******',
                'eu.simphony-project.docker.env.x11-depth': '',
                'eu.simphony-project.docker.runinfo.mapping_id': 'mapping_id',
                'eu.simphony-project.docker.env.x11-height': '',
                'eu.simphony-project.docker.ui_name': 'Mayavi 4.4.4',
                'eu.simphony-project.docker.runinfo.urlpath':
                '/user/username/containers/url_id',  # noqa
                'eu.simphony-project.docker.type': 'vncapp',
                'eu.simphony-project.docker.runinfo.realm': 'myrealm',
                'eu.simphony-project.docker.description':
                'Ubuntu machine with mayavi preinstalled',  # noqa
                'eu.simphony-project.docker.env.x11-width': '',
                'eu.simphony-project.docker.runinfo.url_id': 'url_id'
            },
            'Names': ['/myrealm-username-mapping_5Fid'],
            'Ports': [{
                'Type': 'tcp',
                'IP': '0.0.0.0',
                'PublicPort': 666,
                'PrivatePort': 8888
            }],  # noqa
            'State':
            'running',
            'Command':
            '/sbin/init -D',
            'Image':
            'image_name1',
            'Id':
            'container_id1',
            'ImageID':
            'image_id1'
        }

        docker_dict["Ports"] = [{
            'IP': '0.0.0.0',
            'PublicIP': 34567,
            'PrivatePort': 22,
            'Type': 'tcp'
        }, {
            'IP': '0.0.0.0',
            'PublicIP': 34562,
            'PrivatePort': 21,
            'Type': 'tcp'
        }]
        with self.assertRaises(ValueError):
            Container.from_docker_dict(docker_dict)
    def _start_container(self,
                         user_name,
                         image_name,
                         mapping_id,
                         base_urlpath,
                         volumes,
                         environment):
        """Helper method that performs the physical operation of starting
        the container.

        If successful, returns a Container object.
        If any exception occurs, it logs it and re-raises an exception.
        """

        try:
            image_info = yield self._docker_client.inspect_image(image_name)
            image_id = image_info["Id"]
        except NotFound as e:
            self.log.error('Could not find requested image {}'.format(
                image_name))
            raise e
        except Exception as e:
            self.log.exception("Could not inspect image {}".format(
                image_name
            ))
            raise e

        self.log.info('Got container image: {}'.format(image_name))

        # Check if the container is present.
        container = yield self.find_container(
            user_name=user_name, mapping_id=mapping_id)

        if container is not None:
            # Make sure we stop and remove it if by any chance is already
            # there. This will guarantee a fresh start every time.
            self.log.info('Container for image {} '
                          'already present. Stopping.'.format(image_name))
            yield self.stop_and_remove_container(container.docker_id)

        # Data volume binding to be used with Docker Client
        # volumes = {volume_source: {'bind': volume_target,
        #                            'mode': volume_mode}
        volumes = volumes if volumes else {}

        # Filter away the volume sources that do not exist,
        # otherwise Docker would create non-existing host directory
        # See Docker PR #21666
        filtered_volumes = {source: volumes[source]
                            for source in volumes
                            if os.path.exists(source)}

        volume_targets = [binding['bind']
                          for binding in filtered_volumes.values()]

        # Log the paths that are not being mounted
        if volumes.keys() - filtered_volumes.keys():
            self.log.error('Path(s) does not exist, not mounting:\n%s',
                           '\n'.join(volumes.keys() - filtered_volumes.keys()))

        self.log.info(
            'Mounting these volumes: \n%s',
            '\n'.join('{0} -> {1}'.format(source, target['bind'])
                      for source, target in filtered_volumes.items()))

        container_url_id = _generate_container_url_id()
        container_urlpath = without_end_slash(
            url_path_join(base_urlpath,
                          "containers",
                          container_url_id))
        container_name = _generate_container_name(self.realm,
                                                  user_name,
                                                  mapping_id)
        create_kwargs = dict(
            image=image_name,
            name=container_name,
            environment=_get_container_env(user_name,
                                           container_url_id,
                                           environment,
                                           base_urlpath),
            volumes=volume_targets,
            labels=_get_container_labels(user_name,
                                         mapping_id,
                                         container_url_id,
                                         container_urlpath,
                                         self.realm))

        # build the dictionary of keyword arguments for host_config
        host_config = dict(
            port_bindings={
                self.container_port: None
            },
            binds=filtered_volumes
        )

        self.log.debug("Starting host with config: %s", host_config)
        host_config = yield self._docker_client.create_host_config(
            **host_config)

        # Get the host_config configuration in create_kwargs.
        # If it's not there, create an empty one.
        # Then update it with the current configuration.
        create_kwargs.setdefault('host_config', {}).update(host_config)

        resp = yield self._docker_client.create_container(**create_kwargs)

        container_id = resp['Id']

        self.log.info("Created container '%s' (id: %s) from image %s",
                      container_name, container_id, image_name)

        # start the container
        try:
            yield self._docker_client.start(container_id)
        except Exception as e:
            self.log.exception("Could not start container {}".format(
                container_id))
            yield self.stop_and_remove_container(container_id)
            raise e

        try:
            ip, port = yield from self._get_ip_and_port(container_id)
        except Exception as e:
            self.log.exception(
                "Could not retrieve ip/port information "
                "for container {}".format(container_id))
            yield self.stop_and_remove_container(container_id)
            raise e

        container = Container(
            docker_id=container_id,
            name=container_name,
            image_name=image_name,
            image_id=image_id,
            mapping_id=mapping_id,
            ip=ip,
            port=port,
            url_id=container_url_id,
            urlpath=container_urlpath,
        )

        self.log.info(
            ("Started container '{}' (id: {}). "
             "Exported port reachable at {}:{}").format(
                container_name,
                container_id,
                ip,
                port
            )
        )

        return container