Пример #1
0
    def test_resolve_path(self):
        os.environ['HOSTENV'] = '/tmp'
        os.environ['CONTAINERENV'] = '/host/tmp'

        service_dict = config.load(
            config.ConfigDetails(
                config={
                    'foo': {
                        'build': '.',
                        'volumes': ['$HOSTENV:$CONTAINERENV']
                    }
                },
                working_dir="tests/fixtures/env",
                filename=None,
            ))[0]
        self.assertEqual(set(service_dict['volumes']), set(['/tmp:/host/tmp']))

        service_dict = config.load(
            config.ConfigDetails(
                config={
                    'foo': {
                        'build': '.',
                        'volumes': ['/opt${HOSTENV}:/opt${CONTAINERENV}']
                    }
                },
                working_dir="tests/fixtures/env",
                filename=None,
            ))[0]
        self.assertEqual(set(service_dict['volumes']),
                         set(['/opt/tmp:/opt/host/tmp']))
Пример #2
0
    def test_load_with_multiple_files(self):
        base_file = config.ConfigFile(
            'base.yaml', {
                'web': {
                    'image': 'example/web',
                    'links': ['db'],
                },
                'db': {
                    'image': 'example/db',
                },
            })
        override_file = config.ConfigFile('override.yaml', {
            'web': {
                'build': '/',
                'volumes': ['/home/user/project:/code'],
            },
        })
        details = config.ConfigDetails('.', [base_file, override_file])

        service_dicts = config.load(details)
        expected = [
            {
                'name': 'web',
                'build': '/',
                'links': ['db'],
                'volumes': ['/home/user/project:/code'],
            },
            {
                'name': 'db',
                'image': 'example/db',
            },
        ]
        self.assertEqual(service_sort(service_dicts), service_sort(expected))
Пример #3
0
    def test_project_up_named_volumes_in_binds(self):
        vol_name = '{0:x}'.format(random.getrandbits(32))
        full_vol_name = 'composetest_{0}'.format(vol_name)

        base_file = config.ConfigFile(
            'base.yml',
            {
                'version': V2_0,
                'services': {
                    'simple': {
                        'image': 'busybox:latest',
                        'command': 'top',
                        'volumes': ['{0}:/data'.format(vol_name)]
                    },
                },
                'volumes': {
                    vol_name: {'driver': 'local'}
                }

            })
        config_details = config.ConfigDetails('.', [base_file])
        config_data = config.load(config_details)
        project = Project.from_config(
            name='composetest', config_data=config_data, client=self.client
        )
        service = project.services[0]
        self.assertEqual(service.name, 'simple')
        volumes = service.options.get('volumes')
        self.assertEqual(len(volumes), 1)
        self.assertEqual(volumes[0].external, full_vol_name)
        project.up()
        engine_volumes = self.client.volumes()['Volumes']
        container = service.get_container()
        assert [mount['Name'] for mount in container.get('Mounts')] == [full_vol_name]
        assert next((v for v in engine_volumes if v['Name'] == vol_name), None) is None
Пример #4
0
 def test_config_invalid_service_names(self):
     with self.assertRaises(ConfigurationError):
         for invalid_name in ['?not?allowed', ' ', '', '!', '/', '\xe2']:
             config.load(
                 config.ConfigDetails({invalid_name: {
                     'image': 'busybox'
                 }}, 'working_dir', 'filename.yml'))
Пример #5
0
    def test_project_up_port_mappings_with_multiple_files(self):
        base_file = config.ConfigFile(
            'base.yml', {
                'version': V2_0,
                'services': {
                    'simple': {
                        'image': 'busybox:latest',
                        'command': 'top',
                        'ports': ['1234:1234']
                    },
                },
            })
        override_file = config.ConfigFile('override.yml', {
            'version': V2_0,
            'services': {
                'simple': {
                    'ports': ['1234:1234']
                }
            }
        })
        details = config.ConfigDetails('.', [base_file, override_file])

        config_data = config.load(details)
        project = Project.from_config(name='composetest',
                                      config_data=config_data,
                                      client=self.client)
        project.up()
        containers = project.containers()
        self.assertEqual(len(containers), 1)
Пример #6
0
 def test_config_integer_service_name_raise_validation_error(self):
     expected_error_msg = "Service name: 1 needs to be a string, eg '1'"
     with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
         config.load(
             config.ConfigDetails({1: {
                 'image': 'busybox'
             }}, 'working_dir', 'filename.yml'))
Пример #7
0
    def test_load(self):
        service_dicts = config.load(
            config.ConfigDetails(
                {
                    'foo': {
                        'image': 'busybox'
                    },
                    'bar': {
                        'image': 'busybox',
                        'environment': ['FOO=1']
                    },
                }, 'tests/fixtures/extends', 'common.yml'))

        self.assertEqual(
            service_sort(service_dicts),
            service_sort([{
                'name': 'bar',
                'image': 'busybox',
                'environment': {
                    'FOO': '1'
                },
            }, {
                'name': 'foo',
                'image': 'busybox',
            }]))
Пример #8
0
 def test_invalid_list_of_strings_format(self):
     expected_error_msg = "'command' contains an invalid type, valid types are string or array"
     with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
         config.load(
             config.ConfigDetails({'web': {
                 'build': '.',
                 'command': [1]
             }}, 'tests/fixtures/extends', 'filename.yml'))
Пример #9
0
 def test_nonexistent_path(self):
     with self.assertRaises(ConfigurationError):
         config.load(
             config.ConfigDetails({
                 'foo': {
                     'build': 'nonexistent.path'
                 },
             }, 'working_dir', 'filename.yml'))
Пример #10
0
 def test_config_valid_service_names(self):
     for valid_name in [
             '_', '-', '.__.', '_what-up.', 'what_.up----', 'whatup'
     ]:
         config.load(
             config.ConfigDetails({valid_name: {
                 'image': 'busybox'
             }}, 'tests/fixtures/extends', 'common.yml'))
Пример #11
0
 def make_project(self, cfg):
     details = config.ConfigDetails(
         'working_dir',
         [config.ConfigFile(None, cfg)])
     return Project.from_config(
         name='composetest',
         client=self.client,
         config_data=config.load(details))
Пример #12
0
 def test_memswap_can_be_a_string(self):
     service_dict = config.load(
         config.ConfigDetails(
             {'foo': {'image': 'busybox', 'mem_limit': "1G", 'memswap_limit': "512M"}},
             'tests/fixtures/extends',
             'common.yml'
         )
     )
     self.assertEqual(service_dict[0]['memswap_limit'], "512M")
Пример #13
0
 def test_validation_with_correct_memswap_values(self):
     service_dict = config.load(
         config.ConfigDetails(
             {'foo': {'image': 'busybox', 'mem_limit': 1000000, 'memswap_limit': 2000000}},
             'tests/fixtures/extends',
             'common.yml'
         )
     )
     self.assertEqual(service_dict[0]['memswap_limit'], 2000000)
Пример #14
0
 def test_load_throws_error_when_not_dict(self):
     with self.assertRaises(ConfigurationError):
         config.load(
             config.ConfigDetails(
                 {'web': 'busybox:latest'},
                 'working_dir',
                 'filename.yml'
             )
         )
Пример #15
0
    def test_load_with_multiple_files_and_empty_base(self):
        base_file = config.ConfigFile('base.yaml', None)
        override_file = config.ConfigFile(
            'override.yaml',
            {'web': {'image': 'example/web'}})
        details = config.ConfigDetails('.', [base_file, override_file])

        with pytest.raises(ConfigurationError) as exc:
            config.load(details)
        assert 'Top level object needs to be a dictionary' in exc.exconly()
Пример #16
0
 def test_volume_binding_with_environment_variable(self):
     os.environ['VOLUME_PATH'] = '/host/path'
     d = config.load(
         config.ConfigDetails(
             config={'foo': {'build': '.', 'volumes': ['${VOLUME_PATH}:/container/path']}},
             working_dir='.',
             filename=None,
         )
     )[0]
     self.assertEqual(d['volumes'], ['/host/path:/container/path'])
Пример #17
0
 def test_valid_config_oneof_string_or_list(self):
     entrypoint_values = [["sh"], "sh"]
     for entrypoint in entrypoint_values:
         service = config.load(
             config.ConfigDetails(
                 {'web': {
                     'image': 'busybox',
                     'entrypoint': entrypoint
                 }}, 'working_dir', 'filename.yml'))
         self.assertEqual(service[0]['entrypoint'], entrypoint)
Пример #18
0
 def test_invalid_config_not_a_dictionary(self):
     expected_error_msg = "Top level object needs to be a dictionary."
     with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
         config.load(
             config.ConfigDetails(
                 ['foo', 'lol'],
                 'tests/fixtures/extends',
                 'filename.yml'
             )
         )
Пример #19
0
 def test_valid_config_which_allows_two_type_definitions(self):
     expose_values = [["8000"], [8000]]
     for expose in expose_values:
         service = config.load(
             config.ConfigDetails(
                 {'web': {
                     'image': 'busybox',
                     'expose': expose
                 }}, 'working_dir', 'filename.yml'))
         self.assertEqual(service[0]['expose'], expose)
Пример #20
0
 def test_config_image_and_dockerfile_raise_validation_error(self):
     expected_error_msg = "Service 'web' has both an image and alternate Dockerfile."
     with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
         config.load(
             config.ConfigDetails(
                 {'web': {'image': 'busybox', 'dockerfile': 'Dockerfile.alt'}},
                 'working_dir',
                 'filename.yml'
             )
         )
Пример #21
0
    def __get_compose_project(self):
        client = docker_client(Environment())
        config_data = config.load(
            config.ConfigDetails(
                self.home_path,
                [config.ConfigFile.from_filename(self.compose_file)]))

        return DockerComposeProject.from_config(name='metal',
                                                client=client,
                                                config_data=config_data)
Пример #22
0
 def test_config_valid_ports_format_validation(self):
     valid_ports = [["8000", "9000"], ["8000/8050"], ["8000"], [8000],
                    ["49153-49154:3002-3003"]]
     for ports in valid_ports:
         config.load(
             config.ConfigDetails(
                 {'web': {
                     'image': 'busybox',
                     'ports': ports
                 }}, 'working_dir', 'filename.yml'))
Пример #23
0
 def test_extends_validation_empty_dictionary(self):
     with self.assertRaisesRegexp(ConfigurationError, 'service'):
         config.load(
             config.ConfigDetails(
                 {
                     'web': {
                         'image': 'busybox',
                         'extends': {}
                     },
                 }, 'tests/fixtures/extends', 'filename.yml'))
Пример #24
0
 def test_invalid_config_build_and_image_specified(self):
     expected_error_msg = "Service 'foo' has both an image and build path specified."
     with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
         config.load(
             config.ConfigDetails(
                 {
                     'foo': {
                         'image': 'busybox',
                         'build': '.'
                     },
                 }, 'tests/fixtures/extends', 'filename.yml'))
Пример #25
0
 def test_extends_validation_missing_service_key(self):
     with self.assertRaisesRegexp(ConfigurationError, "'service' is a required property"):
         config.load(
             config.ConfigDetails(
                 {
                     'web': {'image': 'busybox', 'extends': {'file': 'common.yml'}},
                 },
                 'tests/fixtures/extends',
                 'filename.yml'
             )
         )
Пример #26
0
 def test_config_invalid_ports_format_validation(self):
     expected_error_msg = "Service 'web' configuration key 'ports' contains an invalid type"
     with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
         for invalid_ports in [{"1": "8000"}, False, 0, "8000", 8000, ["8000", "8000"]]:
             config.load(
                 config.ConfigDetails(
                     {'web': {'image': 'busybox', 'ports': invalid_ports}},
                     'working_dir',
                     'filename.yml'
                 )
             )
Пример #27
0
 def test_config_hint(self):
     expected_error_msg = "(did you mean 'privileged'?)"
     with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
         config.load(
             config.ConfigDetails(
                 {
                     'foo': {
                         'image': 'busybox',
                         'privilige': 'something'
                     },
                 }, 'tests/fixtures/extends', 'filename.yml'))
Пример #28
0
 def test_invalid_config_type_should_be_an_array(self):
     expected_error_msg = "Service 'foo' configuration key 'links' contains an invalid type, it should be an array"
     with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
         config.load(
             config.ConfigDetails(
                 {
                     'foo': {
                         'image': 'busybox',
                         'links': 'an_link'
                     },
                 }, 'tests/fixtures/extends', 'filename.yml'))
Пример #29
0
    def test_invalid_interpolation(self):
        with self.assertRaises(config.ConfigurationError) as cm:
            config.load(
                config.ConfigDetails({'web': {
                    'image': '${'
                }}, 'working_dir', 'filename.yml'))

        self.assertIn('Invalid', cm.exception.msg)
        self.assertIn('for "image" option', cm.exception.msg)
        self.assertIn('in service "web"', cm.exception.msg)
        self.assertIn('"${"', cm.exception.msg)
Пример #30
0
    def test_config_extra_hosts_string_raises_validation_error(self):
        expected_error_msg = "Service 'web' configuration key 'extra_hosts' contains an invalid type"

        with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
            config.load(
                config.ConfigDetails(
                    {
                        'web': {
                            'image': 'busybox',
                            'extra_hosts': 'somehost:162.242.195.82'
                        }
                    }, 'working_dir', 'filename.yml'))