Exemplo n.º 1
0
    def test_get_container_data_volumes(self):
        options = [VolumeSpec.parse(v) for v in [
            '/host/volume:/host/volume:ro',
            '/new/volume',
            '/existing/volume',
        ]]

        self.mock_client.inspect_image.return_value = {
            'ContainerConfig': {
                'Volumes': {
                    '/mnt/image/data': {},
                }
            }
        }
        container = Container(self.mock_client, {
            'Image': 'ababab',
            'Volumes': {
                '/host/volume': '/host/volume',
                '/existing/volume': '/var/lib/docker/aaaaaaaa',
                '/removed/volume': '/var/lib/docker/bbbbbbbb',
                '/mnt/image/data': '/var/lib/docker/cccccccc',
            },
        }, has_been_inspected=True)

        expected = [
            VolumeSpec.parse('/var/lib/docker/aaaaaaaa:/existing/volume:rw'),
            VolumeSpec.parse('/var/lib/docker/cccccccc:/mnt/image/data:rw'),
        ]

        volumes = get_container_data_volumes(container, options)
        assert sorted(volumes) == sorted(expected)
Exemplo n.º 2
0
    def test_merge_volume_bindings(self):
        options = [
            VolumeSpec.parse('/host/volume:/host/volume:ro'),
            VolumeSpec.parse('/host/rw/volume:/host/rw/volume'),
            VolumeSpec.parse('/new/volume'),
            VolumeSpec.parse('/existing/volume'),
        ]

        self.mock_client.inspect_image.return_value = {
            'ContainerConfig': {'Volumes': {}}
        }

        previous_container = Container(self.mock_client, {
            'Id': 'cdefab',
            'Image': 'ababab',
            'Mounts': [{
                'Source': '/var/lib/docker/aaaaaaaa',
                'Destination': '/existing/volume',
                'Mode': '',
                'RW': True,
                'Name': 'existingvolume',
            }],
        }, has_been_inspected=True)

        expected = [
            '/host/volume:/host/volume:ro',
            '/host/rw/volume:/host/rw/volume:rw',
            'existingvolume:/existing/volume:rw',
        ]

        binds, affinity = merge_volume_bindings(options, previous_container)
        assert sorted(binds) == sorted(expected)
        assert affinity == {'affinity:container': '=cdefab'}
Exemplo n.º 3
0
    def test_mount_same_host_path_to_two_volumes(self):
        service = Service(
            'web',
            image='busybox',
            volumes=[
                VolumeSpec.parse('/host/path:/data1'),
                VolumeSpec.parse('/host/path:/data2'),
            ],
            client=self.mock_client,
        )

        self.mock_client.inspect_image.return_value = {
            'Id': 'ababab',
            'ContainerConfig': {
                'Volumes': {}
            }
        }

        service._get_container_create_options(
            override_options={},
            number=1,
        )

        self.assertEqual(
            set(self.mock_client.create_host_config.call_args[1]['binds']),
            set([
                '/host/path:/data1:rw',
                '/host/path:/data2:rw',
            ]),
        )
Exemplo n.º 4
0
    def test_merge_volume_bindings(self):
        options = [
            VolumeSpec.parse('/host/volume:/host/volume:ro'),
            VolumeSpec.parse('/host/rw/volume:/host/rw/volume'),
            VolumeSpec.parse('/new/volume'),
            VolumeSpec.parse('/existing/volume'),
        ]

        self.mock_client.inspect_image.return_value = {
            'ContainerConfig': {'Volumes': {}}
        }

        intermediate_container = Container(self.mock_client, {
            'Image': 'ababab',
            'Volumes': {'/existing/volume': '/var/lib/docker/aaaaaaaa'},
        }, has_been_inspected=True)

        expected = [
            '/host/volume:/host/volume:ro',
            '/host/rw/volume:/host/rw/volume:rw',
            '/var/lib/docker/aaaaaaaa:/existing/volume:rw',
        ]

        binds = merge_volume_bindings(options, intermediate_container)
        self.assertEqual(set(binds), set(expected))
Exemplo n.º 5
0
    def test_get_container_data_volumes(self):
        options = [VolumeSpec.parse(v) for v in [
            '/host/volume:/host/volume:ro',
            '/new/volume',
            '/existing/volume',
            'named:/named/vol',
            '/dev/tmpfs'
        ]]

        self.mock_client.inspect_image.return_value = {
            'ContainerConfig': {
                'Volumes': {
                    '/mnt/image/data': {},
                }
            }
        }
        container = Container(self.mock_client, {
            'Image': 'ababab',
            'Mounts': [
                {
                    'Source': '/host/volume',
                    'Destination': '/host/volume',
                    'Mode': '',
                    'RW': True,
                    'Name': 'hostvolume',
                }, {
                    'Source': '/var/lib/docker/aaaaaaaa',
                    'Destination': '/existing/volume',
                    'Mode': '',
                    'RW': True,
                    'Name': 'existingvolume',
                }, {
                    'Source': '/var/lib/docker/bbbbbbbb',
                    'Destination': '/removed/volume',
                    'Mode': '',
                    'RW': True,
                    'Name': 'removedvolume',
                }, {
                    'Source': '/var/lib/docker/cccccccc',
                    'Destination': '/mnt/image/data',
                    'Mode': '',
                    'RW': True,
                    'Name': 'imagedata',
                },
            ]
        }, has_been_inspected=True)

        expected = [
            VolumeSpec.parse('existingvolume:/existing/volume:rw'),
            VolumeSpec.parse('imagedata:/mnt/image/data:rw'),
        ]

        volumes = get_container_data_volumes(container, options, ['/dev/tmpfs'])
        assert sorted(volumes) == sorted(expected)
Exemplo n.º 6
0
def get_named_volumes(services):
    volume_specs = [
        VolumeSpec.parse(volume) for service in services.values()
        for volume in service.get('volumes', [])
    ]
    names = {spec.external for spec in volume_specs if spec.is_named_volume}
    return {name: {'external': True} for name in names}
Exemplo n.º 7
0
    def execute_command(self,
                        service_name,
                        command,
                        volumes=[],
                        one_off=False):
        if one_off:
            compose_project = self.__get_compose_project()
            service = compose_project.get_service(service_name)
            compose_project.initialize()
            container = service.create_container(
                one_off=True,
                command=command,
                volumes=[VolumeSpec.parse(volume) for volume in volumes])
            container.start()

            for log in container.attach(stream=True, logs=True):
                sys.stdout.write(log)
                sys.stdout.flush()

            compose_project.client.remove_container(container.id,
                                                    force=True,
                                                    v=True)
        else:
            os.system('docker-compose --file="%s" exec -T %s %s' %
                      (self.compose_file, service_name, command))
Exemplo n.º 8
0
    def test_duplicate_volume_trailing_slash(self):
        """
        When an image specifies a volume, and the Compose file specifies a host path
        but adds a trailing slash, make sure that we don't create duplicate binds.
        """
        host_path = '/tmp/data'
        container_path = '/data'
        volumes = [VolumeSpec.parse('{}:{}/'.format(host_path, container_path))]

        tmp_container = self.client.create_container(
            'busybox', 'true',
            volumes={container_path: {}},
            labels={'com.docker.compose.test_image': 'true'},
        )
        image = self.client.commit(tmp_container)['Id']

        service = self.create_service('db', image=image, volumes=volumes)
        old_container = create_and_start_container(service)

        self.assertEqual(
            old_container.get('Config.Volumes'),
            {container_path: {}},
        )

        service = self.create_service('db', image=image, volumes=volumes)
        new_container = service.recreate_container(old_container)

        self.assertEqual(
            new_container.get('Config.Volumes'),
            {container_path: {}},
        )

        self.assertEqual(service.containers(stopped=False), [new_container])
Exemplo n.º 9
0
 def test_parse_volume_windows_absolute_path(self):
     windows_path = "c:\\Users\\me\\Documents\\shiny\\config:\\opt\\shiny\\config:ro"
     assert VolumeSpec.parse(windows_path) == (
         "/c/Users/me/Documents/shiny/config",
         "/opt/shiny/config",
         "ro"
     )
Exemplo n.º 10
0
    def test_duplicate_volume_trailing_slash(self):
        """
        When an image specifies a volume, and the Compose file specifies a host path
        but adds a trailing slash, make sure that we don't create duplicate binds.
        """
        host_path = "/tmp/data"
        container_path = "/data"
        volumes = [VolumeSpec.parse("{}:{}/".format(host_path, container_path))]

        tmp_container = self.client.create_container(
            "busybox", "true", volumes={container_path: {}}, labels={"com.docker.compose.test_image": "true"}
        )
        image = self.client.commit(tmp_container)["Id"]

        service = self.create_service("db", image=image, volumes=volumes)
        old_container = create_and_start_container(service)

        self.assertEqual(old_container.get("Config.Volumes"), {container_path: {}})

        service = self.create_service("db", image=image, volumes=volumes)
        new_container = service.recreate_container(old_container)

        self.assertEqual(new_container.get("Config.Volumes"), {container_path: {}})

        self.assertEqual(service.containers(stopped=False), [new_container])
Exemplo n.º 11
0
    def test_execute_convergence_plan_recreate(self):
        service = self.create_service(
            "db", environment={"FOO": "1"}, volumes=[VolumeSpec.parse("/etc")], entrypoint=["top"], command=["-d", "1"]
        )
        old_container = service.create_container()
        self.assertEqual(old_container.get("Config.Entrypoint"), ["top"])
        self.assertEqual(old_container.get("Config.Cmd"), ["-d", "1"])
        self.assertIn("FOO=1", old_container.get("Config.Env"))
        self.assertEqual(old_container.name, "composetest_db_1")
        service.start_container(old_container)
        old_container.inspect()  # reload volume data
        volume_path = old_container.get_mount("/etc")["Source"]

        num_containers_before = len(self.client.containers(all=True))

        service.options["environment"]["FOO"] = "2"
        new_container, = service.execute_convergence_plan(ConvergencePlan("recreate", [old_container]))

        self.assertEqual(new_container.get("Config.Entrypoint"), ["top"])
        self.assertEqual(new_container.get("Config.Cmd"), ["-d", "1"])
        self.assertIn("FOO=2", new_container.get("Config.Env"))
        self.assertEqual(new_container.name, "composetest_db_1")
        self.assertEqual(new_container.get_mount("/etc")["Source"], volume_path)
        self.assertIn("affinity:container==%s" % old_container.id, new_container.get("Config.Env"))

        self.assertEqual(len(self.client.containers(all=True)), num_containers_before)
        self.assertNotEqual(old_container.id, new_container.id)
        self.assertRaises(APIError, self.client.inspect_container, old_container.id)
Exemplo n.º 12
0
    def test_execute_convergence_plan_recreate(self):
        service = self.create_service('db',
                                      environment={'FOO': '1'},
                                      volumes=[VolumeSpec.parse('/etc')],
                                      entrypoint=['top'],
                                      command=['-d', '1'])
        old_container = service.create_container()
        self.assertEqual(old_container.get('Config.Entrypoint'), ['top'])
        self.assertEqual(old_container.get('Config.Cmd'), ['-d', '1'])
        self.assertIn('FOO=1', old_container.get('Config.Env'))
        self.assertEqual(old_container.name, 'composetest_db_1')
        service.start_container(old_container)
        old_container.inspect()  # reload volume data
        volume_path = old_container.get_mount('/etc')['Source']

        num_containers_before = len(self.client.containers(all=True))

        service.options['environment']['FOO'] = '2'
        new_container, = service.execute_convergence_plan(
            ConvergencePlan('recreate', [old_container]))

        self.assertEqual(new_container.get('Config.Entrypoint'), ['top'])
        self.assertEqual(new_container.get('Config.Cmd'), ['-d', '1'])
        self.assertIn('FOO=2', new_container.get('Config.Env'))
        self.assertEqual(new_container.name, 'composetest_db_1')
        self.assertEqual(
            new_container.get_mount('/etc')['Source'], volume_path)
        self.assertIn('affinity:container==%s' % old_container.id,
                      new_container.get('Config.Env'))

        self.assertEqual(len(self.client.containers(all=True)),
                         num_containers_before)
        self.assertNotEqual(old_container.id, new_container.id)
        self.assertRaises(APIError, self.client.inspect_container,
                          old_container.id)
Exemplo n.º 13
0
    def test_execute_convergence_plan_when_image_volume_masks_config(self):
        service = self.create_service(
            'db',
            build={'context': 'tests/fixtures/dockerfile-with-volume'},
        )

        old_container = create_and_start_container(service)
        self.assertEqual(
            [mount['Destination'] for mount in old_container.get('Mounts')],
            ['/data'])
        volume_path = old_container.get_mount('/data')['Source']

        service.options['volumes'] = [VolumeSpec.parse('/tmp:/data')]

        with mock.patch('compose.service.log') as mock_log:
            new_container, = service.execute_convergence_plan(
                ConvergencePlan('recreate', [old_container]))

        mock_log.warn.assert_called_once_with(mock.ANY)
        _, args, kwargs = mock_log.warn.mock_calls[0]
        self.assertIn(
            "Service \"db\" is using volume \"/data\" from the previous container",
            args[0])

        self.assertEqual(
            [mount['Destination'] for mount in new_container.get('Mounts')],
            ['/data'])
        self.assertEqual(
            new_container.get_mount('/data')['Source'], volume_path)
Exemplo n.º 14
0
    def test_project_up_with_no_recreate_stopped(self):
        web = self.create_service('web')
        db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
        project = Project('composetest', [web, db], self.client)
        project.start()
        self.assertEqual(len(project.containers()), 0)

        project.up(['db'])
        project.kill()

        old_containers = project.containers(stopped=True)

        self.assertEqual(len(old_containers), 1)
        old_db_id = old_containers[0].id
        db_volume_path = old_containers[0].inspect()['Volumes']['/var/db']

        project.up(strategy=ConvergenceStrategy.never)

        new_containers = project.containers(stopped=True)
        self.assertEqual(len(new_containers), 2)
        self.assertEqual([c.is_running for c in new_containers], [True, True])

        db_container = [c for c in new_containers if 'db' in c.name][0]
        self.assertEqual(db_container.id, old_db_id)
        self.assertEqual(db_container.inspect()['Volumes']['/var/db'],
                         db_volume_path)
Exemplo n.º 15
0
    def test_duplicate_volume_trailing_slash(self):
        """
        When an image specifies a volume, and the Compose file specifies a host path
        but adds a trailing slash, make sure that we don't create duplicate binds.
        """
        host_path = '/tmp/data'
        container_path = '/data'
        volumes = [VolumeSpec.parse('{}:{}/'.format(host_path, container_path))]

        tmp_container = self.client.create_container(
            'busybox', 'true',
            volumes={container_path: {}},
            labels={'com.docker.compose.test_image': 'true'},
        )
        image = self.client.commit(tmp_container)['Id']

        service = self.create_service('db', image=image, volumes=volumes)
        old_container = create_and_start_container(service)

        self.assertEqual(
            old_container.get('Config.Volumes'),
            {container_path: {}},
        )

        service = self.create_service('db', image=image, volumes=volumes)
        new_container = service.recreate_container(old_container)

        self.assertEqual(
            new_container.get('Config.Volumes'),
            {container_path: {}},
        )

        self.assertEqual(service.containers(stopped=False), [new_container])
Exemplo n.º 16
0
    def test_project_up_with_no_recreate_stopped(self):
        web = self.create_service('web')
        db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
        project = Project('composetest', [web, db], self.client)
        project.start()
        self.assertEqual(len(project.containers()), 0)

        project.up(['db'])
        project.kill()

        old_containers = project.containers(stopped=True)

        self.assertEqual(len(old_containers), 1)
        old_container, = old_containers
        old_db_id = old_container.id
        db_volume_path = old_container.get_mount('/var/db')['Source']

        project.up(strategy=ConvergenceStrategy.never)

        new_containers = project.containers(stopped=True)
        self.assertEqual(len(new_containers), 2)
        self.assertEqual([c.is_running for c in new_containers], [True, True])

        db_container = [c for c in new_containers if 'db' in c.name][0]
        self.assertEqual(db_container.id, old_db_id)
        self.assertEqual(
            db_container.get_mount('/var/db')['Source'],
            db_volume_path)
Exemplo n.º 17
0
    def test_execute_convergence_plan_when_image_volume_masks_config(self):
        service = self.create_service(
            'db',
            build={'context': 'tests/fixtures/dockerfile-with-volume'},
        )

        old_container = create_and_start_container(service)
        self.assertEqual(
            [mount['Destination'] for mount in old_container.get('Mounts')],
            ['/data']
        )
        volume_path = old_container.get_mount('/data')['Source']

        service.options['volumes'] = [VolumeSpec.parse('/tmp:/data')]

        with mock.patch('compose.service.log') as mock_log:
            new_container, = service.execute_convergence_plan(
                ConvergencePlan('recreate', [old_container]))

        mock_log.warn.assert_called_once_with(mock.ANY)
        _, args, kwargs = mock_log.warn.mock_calls[0]
        self.assertIn(
            "Service \"db\" is using volume \"/data\" from the previous container",
            args[0])

        self.assertEqual(
            [mount['Destination'] for mount in new_container.get('Mounts')],
            ['/data']
        )
        self.assertEqual(new_container.get_mount('/data')['Source'], volume_path)
Exemplo n.º 18
0
    def test_different_host_path_in_container_json(self):
        service = Service(
            'web',
            image='busybox',
            volumes=[VolumeSpec.parse('/host/path:/data')],
            client=self.mock_client,
        )

        self.mock_client.inspect_image.return_value = {
            'Id': 'ababab',
            'ContainerConfig': {
                'Volumes': {
                    '/data': {},
                }
            }
        }

        self.mock_client.inspect_container.return_value = {
            'Id': '123123123',
            'Image': 'ababab',
            'Volumes': {
                '/data': '/mnt/sda1/host/path',
            },
        }

        service._get_container_create_options(
            override_options={},
            number=1,
            previous_container=Container(self.mock_client, {'Id': '123123123'}),
        )

        self.assertEqual(
            self.mock_client.create_host_config.call_args[1]['binds'],
            ['/mnt/sda1/host/path:/data:rw'],
        )
Exemplo n.º 19
0
    def setUp(self):
        self.db = self.create_service('db',
                                      volumes=[VolumeSpec.parse('/var/db')],
                                      command='top')
        self.project = Project('composetest', [self.db], self.client)

        container = self.db.create_container()
        self.db.start_container(container)
        self.host_path = container.get_mount('/var/db')['Source']
Exemplo n.º 20
0
    def test_recreate_preserves_volume_with_trailing_slash(self):
        """When the Compose file specifies a trailing slash in the container path, make
        sure we copy the volume over when recreating.
        """
        service = self.create_service('data', volumes=[VolumeSpec.parse('/data/')])
        old_container = create_and_start_container(service)
        volume_path = old_container.get_mount('/data')['Source']

        new_container = service.recreate_container(old_container)
        self.assertEqual(new_container.get_mount('/data')['Source'], volume_path)
Exemplo n.º 21
0
    def setUp(self):
        self.db = self.create_service(
            'db',
            volumes=[VolumeSpec.parse('/var/db')],
            command='top')
        self.project = Project('composetest', [self.db], self.client)

        container = self.db.create_container()
        container.start()
        self.host_path = container.get_mount('/var/db')['Source']
Exemplo n.º 22
0
    def test_project_up(self):
        web = self.create_service('web')
        db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
        project = Project('composetest', [web, db], self.client)
        project.start()
        self.assertEqual(len(project.containers()), 0)

        project.up(['db'])
        self.assertEqual(len(project.containers()), 1)
        self.assertEqual(len(db.containers()), 1)
        self.assertEqual(len(web.containers()), 0)
def get_named_volumes(services):
    volume_specs = [
        VolumeSpec.parse(volume)
        for service in services.values()
        for volume in service.get('volumes', [])
    ]
    names = {
        spec.external
        for spec in volume_specs
        if spec.is_named_volume
    }
    return {name: {'external': True} for name in names}
Exemplo n.º 24
0
    def test_create_twice(self):
        web = self.create_service('web')
        db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
        project = Project('composetest', [web, db], self.client)

        project.create(['db', 'web'])
        project.create(['db', 'web'])
        self.assertEqual(len(project.containers()), 0)
        self.assertEqual(len(project.containers(stopped=True)), 2)
        self.assertEqual(len(db.containers()), 0)
        self.assertEqual(len(db.containers(stopped=True)), 1)
        self.assertEqual(len(web.containers()), 0)
        self.assertEqual(len(web.containers(stopped=True)), 1)
Exemplo n.º 25
0
    def test_merge_volume_bindings(self):
        options = [
            VolumeSpec.parse('/host/volume:/host/volume:ro', True),
            VolumeSpec.parse('/host/rw/volume:/host/rw/volume', True),
            VolumeSpec.parse('/new/volume', True),
            VolumeSpec.parse('/existing/volume', True),
        ]

        self.mock_client.inspect_image.return_value = {
            'ContainerConfig': {
                'Volumes': {}
            }
        }

        previous_container = Container(self.mock_client, {
            'Id':
            'cdefab',
            'Image':
            'ababab',
            'Mounts': [{
                'Source': '/var/lib/docker/aaaaaaaa',
                'Destination': '/existing/volume',
                'Mode': '',
                'RW': True,
                'Name': 'existingvolume',
            }],
        },
                                       has_been_inspected=True)

        expected = [
            '/host/volume:/host/volume:ro',
            '/host/rw/volume:/host/rw/volume:rw',
            'existingvolume:/existing/volume:rw',
        ]

        binds, affinity = merge_volume_bindings(options, previous_container)
        assert sorted(binds) == sorted(expected)
        assert affinity == {'affinity:container': '=cdefab'}
Exemplo n.º 26
0
    def test_project_up_starts_links(self):
        console = self.create_service('console')
        db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
        web = self.create_service('web', links=[(db, 'db')])

        project = Project('composetest', [web, db, console], self.client)
        project.start()
        self.assertEqual(len(project.containers()), 0)

        project.up(['web'])
        self.assertEqual(len(project.containers()), 2)
        self.assertEqual(len(web.containers()), 1)
        self.assertEqual(len(db.containers()), 1)
        self.assertEqual(len(console.containers()), 0)
Exemplo n.º 27
0
    def test_create_with_special_volume_mode(self):
        self.mock_client.inspect_image.return_value = {'Id': 'imageid'}

        self.mock_client.create_container.return_value = {'Id': 'containerid'}

        volume = '/tmp:/foo:z'
        Service(
            'web',
            client=self.mock_client,
            image='busybox',
            volumes=[VolumeSpec.parse(volume, True)],
        ).create_container()

        assert self.mock_client.create_container.call_count == 1
        assert self.mock_client.create_host_config.call_args[1]['binds'] == [volume]
Exemplo n.º 28
0
    def test_create_with_special_volume_mode(self):
        self.mock_client.inspect_image.return_value = {'Id': 'imageid'}

        self.mock_client.create_container.return_value = {'Id': 'containerid'}

        volume = '/tmp:/foo:z'
        Service(
            'web',
            client=self.mock_client,
            image='busybox',
            volumes=[VolumeSpec.parse(volume, True)],
        ).create_container()

        assert self.mock_client.create_container.call_count == 1
        assert self.mock_client.create_host_config.call_args[1]['binds'] == [volume]
Exemplo n.º 29
0
    def test_get_container_create_options_with_different_host_path_in_container_json(
            self):
        service = Service(
            'web',
            image='busybox',
            volumes=[VolumeSpec.parse('/host/path:/data')],
            client=self.mock_client,
        )
        volume_name = 'abcdefff1234'

        self.mock_client.inspect_image.return_value = {
            'Id': 'ababab',
            'ContainerConfig': {
                'Volumes': {
                    '/data': {},
                }
            }
        }

        self.mock_client.inspect_container.return_value = {
            'Id':
            '123123123',
            'Image':
            'ababab',
            'Mounts': [
                {
                    'Destination': '/data',
                    'Source': '/mnt/sda1/host/path',
                    'Mode': '',
                    'RW': True,
                    'Driver': 'local',
                    'Name': volume_name,
                },
            ]
        }

        service._get_container_create_options(
            override_options={},
            number=1,
            previous_container=Container(self.mock_client,
                                         {'Id': '123123123'}),
        )

        assert (self.mock_client.create_host_config.call_args[1]['binds'] == [
            '{}:/data:rw'.format(volume_name)
        ])
Exemplo n.º 30
0
    def test_recreate_preserves_volumes(self):
        web = self.create_service('web')
        db = self.create_service('db', volumes=[VolumeSpec.parse('/etc')])
        project = Project('composetest', [web, db], self.client)
        project.start()
        self.assertEqual(len(project.containers()), 0)

        project.up(['db'])
        self.assertEqual(len(project.containers()), 1)
        old_db_id = project.containers()[0].id
        db_volume_path = project.containers()[0].get('Volumes./etc')

        project.up(strategy=ConvergenceStrategy.always)
        self.assertEqual(len(project.containers()), 2)

        db_container = [c for c in project.containers() if 'db' in c.name][0]
        self.assertNotEqual(db_container.id, old_db_id)
        self.assertEqual(db_container.get('Volumes./etc'), db_volume_path)
Exemplo n.º 31
0
    def test_execute_convergence_plan_when_image_volume_masks_config(self):
        service = self.create_service("db", build={"context": "tests/fixtures/dockerfile-with-volume"})

        old_container = create_and_start_container(service)
        self.assertEqual([mount["Destination"] for mount in old_container.get("Mounts")], ["/data"])
        volume_path = old_container.get_mount("/data")["Source"]

        service.options["volumes"] = [VolumeSpec.parse("/tmp:/data")]

        with mock.patch("compose.service.log") as mock_log:
            new_container, = service.execute_convergence_plan(ConvergencePlan("recreate", [old_container]))

        mock_log.warn.assert_called_once_with(mock.ANY)
        _, args, kwargs = mock_log.warn.mock_calls[0]
        self.assertIn('Service "db" is using volume "/data" from the previous container', args[0])

        self.assertEqual([mount["Destination"] for mount in new_container.get("Mounts")], ["/data"])
        self.assertEqual(new_container.get_mount("/data")["Source"], volume_path)
Exemplo n.º 32
0
    def test_get_container_create_options_with_different_host_path_in_container_json(self):
        service = Service(
            'web',
            image='busybox',
            volumes=[VolumeSpec.parse('/host/path:/data')],
            client=self.mock_client,
        )
        volume_name = 'abcdefff1234'

        self.mock_client.inspect_image.return_value = {
            'Id': 'ababab',
            'ContainerConfig': {
                'Volumes': {
                    '/data': {},
                }
            }
        }

        self.mock_client.inspect_container.return_value = {
            'Id': '123123123',
            'Image': 'ababab',
            'Mounts': [
                {
                    'Destination': '/data',
                    'Source': '/mnt/sda1/host/path',
                    'Mode': '',
                    'RW': True,
                    'Driver': 'local',
                    'Name': volume_name,
                },
            ]
        }

        service._get_container_create_options(
            override_options={},
            number=1,
            previous_container=Container(self.mock_client, {'Id': '123123123'}),
        )

        assert (
            self.mock_client.create_host_config.call_args[1]['binds'] ==
            ['{}:/data:rw'.format(volume_name)]
        )
Exemplo n.º 33
0
    def test_project_up_with_no_recreate_running(self):
        web = self.create_service('web')
        db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
        project = Project('composetest', [web, db], self.client)
        project.start()
        self.assertEqual(len(project.containers()), 0)

        project.up(['db'])
        self.assertEqual(len(project.containers()), 1)
        old_db_id = project.containers()[0].id
        db_volume_path = project.containers()[0].inspect()['Volumes']['/var/db']

        project.up(strategy=ConvergenceStrategy.never)
        self.assertEqual(len(project.containers()), 2)

        db_container = [c for c in project.containers() if 'db' in c.name][0]
        self.assertEqual(db_container.id, old_db_id)
        self.assertEqual(db_container.inspect()['Volumes']['/var/db'],
                         db_volume_path)
Exemplo n.º 34
0
    def test_execute_convergence_plan_when_containers_are_stopped(self):
        service = self.create_service('db',
                                      environment={'FOO': '1'},
                                      volumes=[VolumeSpec.parse('/var/db')],
                                      entrypoint=['top'],
                                      command=['-d', '1'])
        service.create_container()

        containers = service.containers(stopped=True)
        self.assertEqual(len(containers), 1)
        container, = containers
        self.assertFalse(container.is_running)

        service.execute_convergence_plan(ConvergencePlan('start', [container]))

        containers = service.containers()
        self.assertEqual(len(containers), 1)
        container.inspect()
        self.assertEqual(container, containers[0])
        self.assertTrue(container.is_running)
Exemplo n.º 35
0
    def test_project_up_with_volume_labels(self):
        self.require_api_version('1.23')

        volume_name = 'volume_with_label'

        config_data = config.Config(
            version=V2_0,
            services=[{
                'name':
                'web',
                'image':
                'busybox:latest',
                'volumes': [VolumeSpec.parse('{}:/data'.format(volume_name))]
            }],
            volumes={volume_name: {
                'labels': {
                    'label_key': 'label_val'
                }
            }},
            networks={},
        )

        project = Project.from_config(
            client=self.client,
            name='composetest',
            config_data=config_data,
        )

        project.up()

        volumes = [
            v for v in self.client.volumes().get('Volumes', [])
            if v['Name'].startswith('composetest_')
        ]

        assert [v['Name']
                for v in volumes] == ['composetest_{}'.format(volume_name)]

        assert 'label_key' in volumes[0]['Labels']
        assert volumes[0]['Labels']['label_key'] == 'label_val'
Exemplo n.º 36
0
    def test_execute_convergence_plan_when_containers_are_stopped(self):
        service = self.create_service(
            "db",
            environment={"FOO": "1"},
            volumes=[VolumeSpec.parse("/var/db")],
            entrypoint=["top"],
            command=["-d", "1"],
        )
        service.create_container()

        containers = service.containers(stopped=True)
        self.assertEqual(len(containers), 1)
        container, = containers
        self.assertFalse(container.is_running)

        service.execute_convergence_plan(ConvergencePlan("start", [container]))

        containers = service.containers()
        self.assertEqual(len(containers), 1)
        container.inspect()
        self.assertEqual(container, containers[0])
        self.assertTrue(container.is_running)
Exemplo n.º 37
0
    def test_execute_convergence_plan_when_containers_are_stopped(self):
        service = self.create_service(
            'db',
            environment={'FOO': '1'},
            volumes=[VolumeSpec.parse('/var/db')],
            entrypoint=['top'],
            command=['-d', '1']
        )
        service.create_container()

        containers = service.containers(stopped=True)
        self.assertEqual(len(containers), 1)
        container, = containers
        self.assertFalse(container.is_running)

        service.execute_convergence_plan(ConvergencePlan('start', [container]))

        containers = service.containers()
        self.assertEqual(len(containers), 1)
        container.inspect()
        self.assertEqual(container, containers[0])
        self.assertTrue(container.is_running)
Exemplo n.º 38
0
    def test_project_up_with_volume_labels(self):
        self.require_api_version('1.23')

        volume_name = 'volume_with_label'

        config_data = config.Config(
            version=V2_0,
            services=[{
                'name': 'web',
                'image': 'busybox:latest',
                'volumes': [VolumeSpec.parse('{}:/data'.format(volume_name))]
            }],
            volumes={
                volume_name: {
                    'labels': {
                        'label_key': 'label_val'
                    }
                }
            },
            networks={},
        )

        project = Project.from_config(
            client=self.client,
            name='composetest',
            config_data=config_data,
        )

        project.up()

        volumes = [
            v for v in self.client.volumes().get('Volumes', [])
            if v['Name'].startswith('composetest_')
        ]

        assert [v['Name'] for v in volumes] == ['composetest_{}'.format(volume_name)]

        assert 'label_key' in volumes[0]['Labels']
        assert volumes[0]['Labels']['label_key'] == 'label_val'
Exemplo n.º 39
0
    def test_execute_convergence_plan_recreate_twice(self):
        service = self.create_service('db',
                                      volumes=[VolumeSpec.parse('/etc')],
                                      entrypoint=['top'],
                                      command=['-d', '1'])

        orig_container = service.create_container()
        service.start_container(orig_container)

        orig_container.inspect()  # reload volume data
        volume_path = orig_container.get_mount('/etc')['Source']

        # Do this twice to reproduce the bug
        for _ in range(2):
            new_container, = service.execute_convergence_plan(
                ConvergencePlan('recreate', [orig_container]))

            assert new_container.get_mount('/etc')['Source'] == volume_path
            assert ('affinity:container==%s' % orig_container.id
                    in new_container.get('Config.Env'))

            orig_container = new_container
Exemplo n.º 40
0
    def test_execute_convergence_plan_recreate_twice(self):
        service = self.create_service(
            'db',
            volumes=[VolumeSpec.parse('/etc')],
            entrypoint=['top'],
            command=['-d', '1'])

        orig_container = service.create_container()
        service.start_container(orig_container)

        orig_container.inspect()  # reload volume data
        volume_path = orig_container.get_mount('/etc')['Source']

        # Do this twice to reproduce the bug
        for _ in range(2):
            new_container, = service.execute_convergence_plan(
                ConvergencePlan('recreate', [orig_container]))

            assert new_container.get_mount('/etc')['Source'] == volume_path
            assert ('affinity:container==%s' % orig_container.id in
                    new_container.get('Config.Env'))

            orig_container = new_container
Exemplo n.º 41
0
    def test_execute_convergence_plan_recreate(self):
        service = self.create_service(
            'db',
            environment={'FOO': '1'},
            volumes=[VolumeSpec.parse('/etc')],
            entrypoint=['top'],
            command=['-d', '1']
        )
        old_container = service.create_container()
        self.assertEqual(old_container.get('Config.Entrypoint'), ['top'])
        self.assertEqual(old_container.get('Config.Cmd'), ['-d', '1'])
        self.assertIn('FOO=1', old_container.get('Config.Env'))
        self.assertEqual(old_container.name, 'composetest_db_1')
        service.start_container(old_container)
        old_container.inspect()  # reload volume data
        volume_path = old_container.get_mount('/etc')['Source']

        num_containers_before = len(self.client.containers(all=True))

        service.options['environment']['FOO'] = '2'
        new_container, = service.execute_convergence_plan(
            ConvergencePlan('recreate', [old_container]))

        self.assertEqual(new_container.get('Config.Entrypoint'), ['top'])
        self.assertEqual(new_container.get('Config.Cmd'), ['-d', '1'])
        self.assertIn('FOO=2', new_container.get('Config.Env'))
        self.assertEqual(new_container.name, 'composetest_db_1')
        self.assertEqual(new_container.get_mount('/etc')['Source'], volume_path)
        self.assertIn(
            'affinity:container==%s' % old_container.id,
            new_container.get('Config.Env'))

        self.assertEqual(len(self.client.containers(all=True)), num_containers_before)
        self.assertNotEqual(old_container.id, new_container.id)
        self.assertRaises(APIError,
                          self.client.inspect_container,
                          old_container.id)
Exemplo n.º 42
0
 def test_parse_volume_spec_only_one_path(self):
     spec = VolumeSpec.parse('/the/volume')
     assert spec == (None, '/the/volume', 'rw')
Exemplo n.º 43
0
 def test_create_container_with_unspecified_volume(self):
     service = self.create_service('db',
                                   volumes=[VolumeSpec.parse('/var/db')])
     container = service.create_container()
     container.start()
     self.assertIn('/var/db', container.get('Volumes'))
Exemplo n.º 44
0
 def test_parse_volume_windows_absolute_path(self):
     windows_path = "c:\\Users\\me\\Documents\\shiny\\config:\\opt\\shiny\\config:ro"
     assert VolumeSpec.parse(windows_path) == (
         "/c/Users/me/Documents/shiny/config", "/opt/shiny/config", "ro")
Exemplo n.º 45
0
    def test_parse_volume_spec_with_mode(self):
        spec = VolumeSpec.parse('external:interval:ro')
        assert spec == ('external', 'interval', 'ro')

        spec = VolumeSpec.parse('external:interval:z')
        assert spec == ('external', 'interval', 'z')
Exemplo n.º 46
0
 def test_parse_volume_spec_too_many_parts(self):
     with pytest.raises(ConfigurationError) as exc:
         VolumeSpec.parse('one:two:three:four')
     assert 'has incorrect format' in exc.exconly()
Exemplo n.º 47
0
 def test_build_volume_binding(self):
     binding = build_volume_binding(VolumeSpec.parse('/outside:/inside'))
     assert binding == ('/inside', '/outside:/inside:rw')
Exemplo n.º 48
0
 def test_parse_volume_spec_too_many_parts(self):
     with pytest.raises(ConfigurationError) as exc:
         VolumeSpec.parse('one:two:three:four')
     assert 'has incorrect format' in exc.exconly()
Exemplo n.º 49
0
    def test_parse_volume_spec_with_mode(self):
        spec = VolumeSpec.parse('external:interval:ro')
        assert spec == ('external', 'interval', 'ro')

        spec = VolumeSpec.parse('external:interval:z')
        assert spec == ('external', 'interval', 'z')
Exemplo n.º 50
0
 def test_create_container_with_unspecified_volume(self):
     service = self.create_service('db',
                                   volumes=[VolumeSpec.parse('/var/db')])
     container = service.create_container()
     service.start_container(container)
     assert container.get_mount('/var/db')
Exemplo n.º 51
0
 def test_parse_volume_spec_internal_and_external(self):
     spec = VolumeSpec.parse('external:interval')
     assert spec == ('external', 'interval', 'rw')