Beispiel #1
0
def test_bootstrap_overlay(config_files):
    with open(config_files, "r") as main_file:
        parser = ConfigurationParser(main_file)
        # the host file shall win
        boostrap_source = SourceEntry(parser.get_bootstrap_repository())
        assert parser.get_bootstrap_architecture() == "i386"
        assert "main" in boostrap_source.comps
        assert boostrap_source.uri == "http://deb.debian.org/debian/"
        # the all file shall provide this key
        expected_key = "https://ftp-master.debian.org/keys/archive-key-8.asc"
        assert parser.get_bootstrap_repository_key() == expected_key
        assert boostrap_source.dist == "jessie"
        assert parser.get_bootstrap_tool() == "debootstrap"
Beispiel #2
0
def test_create_host_folders_successful_create(config_files, monkeypatch):
    with open(config_files, "r") as main_file:
        parser = ConfigurationParser(main_file)

        coordinator = SharedFolderCoordinator(parser)

        patch_os_path(monkeypatch, False, False)

        def fake_mkdir_command(*popenargs, **kwargs):
            if get_command(popenargs) == 'mkdir' and get_sub_command(popenargs) == '-p':
                folder = popenargs[0][-1]
                assert 'edi_marker_valid_folder' in folder or 'edi_marker_work' in folder
                return subprocess.CompletedProcess("fakerun", 0, '')
            else:
                return subprocess.run(*popenargs, **kwargs)

        monkeypatch.setattr(mockablerun, 'run_mockable', fake_mkdir_command)

        coordinator.create_host_folders()  # successful mkdir
Beispiel #3
0
def test_verify_container_mountpoints(config_files, monkeypatch):
    with mocked_executable('lxc', '/here/is/no/lxc'):
        with mocked_lxd_version_check():
            with open(config_files, "r") as main_file:

                def fake_lxc_exec_command(*popenargs, **kwargs):
                    if get_command(popenargs).endswith(
                            'lxc') and get_sub_command(popenargs) == 'exec':
                        return subprocess.CompletedProcess("fakerun", 0, '')
                    else:
                        return subprocess.run(*popenargs, **kwargs)

                monkeypatch.setattr(mockablerun, 'run_mockable',
                                    fake_lxc_exec_command)

                parser = ConfigurationParser(main_file)

                coordinator = SharedFolderCoordinator(parser)
                coordinator.verify_container_mountpoints('fake-container')
Beispiel #4
0
def test_create_host_folders_not_a_folder(config_files, monkeypatch):
    with open(config_files, "r") as main_file:
        parser = ConfigurationParser(main_file)

        coordinator = SharedFolderCoordinator(parser)

        def fake_os_path_isdir(*_):
            return False

        monkeypatch.setattr(os.path, 'isdir', fake_os_path_isdir)

        def fake_os_path_exists(*_):
            return True

        monkeypatch.setattr(os.path, 'exists', fake_os_path_exists)

        with pytest.raises(FatalError) as error:
            coordinator.create_host_folders()  # exists but not a folder

        assert 'valid_folder' in error.value.message
Beispiel #5
0
def test_verify_container_mountpoints_failure(config_files, monkeypatch):
    with open(config_files, "r") as main_file:

        def fake_lxc_exec_command(*popenargs, **kwargs):
            command = popenargs[0]
            if command[0] == 'lxc' and command[1] == 'exec':
                if command[command.index('--') + 1] == 'test':
                    return subprocess.CompletedProcess("failure", 1, 'failure')
                else:
                    return subprocess.CompletedProcess("fakerun", 0, '')
            else:
                return subprocess.run(*popenargs, **kwargs)

        monkeypatch.setattr(mockablerun, 'run_mockable', fake_lxc_exec_command)

        parser = ConfigurationParser(main_file)

        coordinator = SharedFolderCoordinator(parser)
        with pytest.raises(FatalError) as error:
            coordinator.verify_container_mountpoints('fake-container')
        assert 'fake-container' in error.value.message
        assert '/foo/bar/target_mountpoint' in error.value.message
Beispiel #6
0
def test_create_host_folders_failed_create(config_files, monkeypatch):
    with open(config_files, "r") as main_file:
        parser = ConfigurationParser(main_file)

        coordinator = SharedFolderCoordinator(parser)

        patch_os_path(monkeypatch, False, False)

        def fake_mkdir_command(*popenargs, **kwargs):
            if get_command(popenargs) == 'mkdir' and get_sub_command(popenargs) == '-p':
                cmd = ['bash', '-c', '>&2 echo -e "no permission" ; exit 1']
                return subprocess.run(cmd, **kwargs)
            else:
                return subprocess.run(*popenargs, **kwargs)

        monkeypatch.setattr(mockablerun, 'run_mockable', fake_mkdir_command)

        with pytest.raises(FatalError) as error:
            coordinator.create_host_folders()  # failed mkdir

        assert 'edi_marker_valid_folder' in error.value.message
        assert 'no permission' in error.value.message
Beispiel #7
0
def test_no_shared_folders_for_distributable_image(config_files, monkeypatch):
    with mocked_executable('lxc'):
        with mocked_lxd_version_check():
            with open(config_files, "r") as main_file:
                with command_context({'edi_create_distributable_image': True}):
                    parser = ConfigurationParser(main_file)

                    coordinator = SharedFolderCoordinator(parser)

                    patch_os_path(monkeypatch, False, False)

                    def fake_run(*popenargs, **kwargs):
                        # We should not run anything!
                        assert False

                    monkeypatch.setattr(mockablerun, 'run_mockable', fake_run)

                    coordinator.create_host_folders()
                    coordinator.verify_container_mountpoints('does-not-exist')
                    assert coordinator.get_mountpoints() == []
                    assert coordinator.get_pre_config_profiles() == []
                    assert coordinator.get_post_config_profiles() == []
Beispiel #8
0
def test_verify_container_mountpoints_failure(config_files, monkeypatch):
    with mocked_executable('lxc', '/here/is/no/lxc'):
        with mocked_lxd_version_check():
            with open(config_files, "r") as main_file:
                def fake_lxc_exec_command(*popenargs, **kwargs):
                    if get_command(popenargs).endswith('lxc') and get_sub_command(popenargs) == 'exec':
                        if get_command_parameter(popenargs, '--') == 'test':
                            return subprocess.CompletedProcess("failure", 1, 'failure')
                        else:
                            return subprocess.CompletedProcess("fakerun", 0, '')
                    else:
                        return subprocess.run(*popenargs, **kwargs)

                monkeypatch.setattr(mockablerun, 'run_mockable', fake_lxc_exec_command)

                parser = ConfigurationParser(main_file)

                coordinator = SharedFolderCoordinator(parser)
                with pytest.raises(FatalError) as error:
                    coordinator.verify_container_mountpoints('fake-container')
                assert 'fake-container' in error.value.message
                assert '/foo/bar/target_mountpoint' in error.value.message
Beispiel #9
0
def test_empty_overlay_file(empty_overlay_config_file):
    with open(empty_overlay_config_file, "r") as main_file:
        parser = ConfigurationParser(main_file)
        assert parser.get_compression() == 'gz'
Beispiel #10
0
def test_global_configuration_overlay(config_files):
    with open(config_files, "r") as main_file:
        parser = ConfigurationParser(main_file)
        assert parser.get_compression() == "gz"
        assert parser.get_lxc_stop_timeout() == 130
Beispiel #11
0
def test_project_name(config_files, config_name):
    with open(config_files, "r") as main_file:
        parser = ConfigurationParser(main_file)
        assert parser.get_configuration_name() == config_name
Beispiel #12
0
 def _setup_parser(self, config_file):
     self.config = ConfigurationParser(config_file)
Beispiel #13
0
class EdiCommand(metaclass=CommandFactory):

    def clean(self, config_file):
        pass

    def _get_sibling_commands(self):
        assert len(type(self).__bases__) == 1
        commands = []
        parent_command = type(self).__bases__[0]._get_command_name()
        for _, command in get_sub_commands(parent_command).items():
            if command != self.__class__:
                commands.append(command)
        return commands

    def _clean_siblings_and_sub_commands(self, config_file):
        for command in self._get_sibling_commands():
            command().clean(config_file)
            command().clean_sub_commands(config_file)

    def clean_sub_commands(self, config_file):
        command = self._get_sub_command("clean")
        if command:
            command().clean(config_file)

    def _setup_parser(self, config_file):
        self.config = ConfigurationParser(config_file)

    @classmethod
    def _get_command_name(cls):
        return compose_command_name(cls)

    @classmethod
    def _get_short_command_name(cls):
        return cls.__name__.lower()

    @classmethod
    def _get_command_file_name_prefix(cls):
        return cls._get_command_name().replace(".", "_")

    @classmethod
    def _add_sub_commands(cls, parser):
        title = "{} commands".format(cls._get_short_command_name())
        subparsers = parser.add_subparsers(title=title,
                                           dest="sub_command_name")

        for _, command in get_sub_commands(cls._get_command_name()).items():
            command.advertise(subparsers)

    def _run_sub_command_cli(self, cli_args):
        self._get_sub_command(cli_args.sub_command_name)().run_cli(cli_args)

    def _get_sub_command(self, command):
        sub_command = "{}.{}".format(self._get_command_name(),
                                     command)
        return get_command(sub_command)

    @staticmethod
    def _require_config_file(parser):
        parser.add_argument('config_file',
                            type=argparse.FileType('r', encoding='UTF-8'))

    @staticmethod
    def _offer_introspection_options(parser):
        group = parser.add_mutually_exclusive_group()
        group.add_argument('--dictionary', action="store_true",
                           help='dump the load time dictionary instead of running the command')
        group.add_argument('--config', action="store_true",
                           help='dump the merged configuration instead of running the command')
        group.add_argument('--plugins', action="store_true",
                           help=('dump the active plugins including their dictionaries instead of '
                                 'running the command'))

    def _get_introspection_method(self, cli_args, plugin_sections):
        if cli_args.dictionary:
            return self._dump_load_time_dictionary
        elif cli_args.config:
            return self._dump_config
        elif cli_args.plugins:
            return partial(self._dump_plugins, plugin_sections)
        else:
            return None

    def _dump_load_time_dictionary(self):
        return self.config.dump_load_time_dictionary()

    def _dump_config(self):
        return self.config.dump()

    def _dump_plugins(self, sections):
        return self.config.dump_plugins(sections)

    def _require_sudo(self):
        if os.getuid() != 0:
            raise FatalError(("The subcommand '{0}' requires superuser "
                              "privileges.\n"
                              "Use 'sudo edi ...'."
                              ).format(self._get_short_command_name()))

    def _pack_image(self, tempdir, datadir, name="result"):
        # advanced options such as numeric-owner are not supported by
        # python tarfile library - therefore we use the tar command line tool
        tempresult = "{0}.tar.{1}".format(name,
                                          self.config.get_compression())
        archive_path = os.path.join(tempdir, tempresult)

        cmd = []
        cmd.append("tar")
        cmd.append("--numeric-owner")
        cmd.extend(["-C", datadir])
        cmd.extend(["-acf", archive_path])
        cmd.extend(os.listdir(datadir))
        run(cmd, sudo=True, log_threshold=logging.INFO)
        return archive_path

    def _unpack_image(self, image, tempdir, subfolder="rootfs"):
        target_folder = os.path.join(tempdir, subfolder)
        os.makedirs(target_folder, exist_ok=True)

        cmd = []
        cmd.append("tar")
        cmd.append("--numeric-owner")
        cmd.extend(["-C", target_folder])
        cmd.extend(["-axf", image])
        run(cmd, sudo=True, log_threshold=logging.INFO)
        return target_folder
Beispiel #14
0
class EdiCommand(metaclass=CommandFactory):
    def __init__(self):
        self.clean_depth = 0
        self.config = None

    def clean(self, config_file):
        pass

    def _get_sibling_commands(self):
        assert len(type(self).__bases__) == 1
        commands = []
        parent_command = type(self).__bases__[0]._get_command_name()
        for _, command in get_sub_commands(parent_command).items():
            if command != self.__class__:
                commands.append(command)
        return commands

    def _clean_siblings_and_sub_commands(self, config_file):
        for command in self._get_sibling_commands():
            command().clean(config_file)
            command().clean_sub_commands(config_file)

    def clean_sub_commands(self, config_file):
        command = self._get_sub_command("clean")
        if command:
            command().clean(config_file)

    def _setup_parser(self, config_file):
        self.config = ConfigurationParser(config_file)

    @classmethod
    def _get_command_name(cls):
        return compose_command_name(cls)

    @classmethod
    def _get_short_command_name(cls):
        return cls.__name__.lower()

    @classmethod
    def _get_command_file_name_prefix(cls):
        return cls._get_command_name().replace(".", "_")

    @classmethod
    def _add_sub_commands(cls, parser):
        title = "{} commands".format(cls._get_short_command_name())
        subparsers = parser.add_subparsers(title=title,
                                           dest="sub_command_name")

        for _, command in get_sub_commands(cls._get_command_name()).items():
            command.advertise(subparsers)

    def _run_sub_command_cli(self, cli_args):
        if not cli_args.sub_command_name:
            raise FatalError("Missing subcommand. Use 'edi --help' for help.")
        self._get_sub_command(cli_args.sub_command_name)().run_cli(cli_args)

    def _get_sub_command(self, command):
        sub_command = "{}.{}".format(self._get_command_name(), command)
        return get_command(sub_command)

    @staticmethod
    def _require_config_file(parser):
        parser.add_argument('config_file',
                            type=argparse.FileType('r', encoding='UTF-8'))

    @staticmethod
    def _offer_options(parser, introspection=False, clean=False):
        group = parser.add_mutually_exclusive_group()
        if introspection:
            group.add_argument(
                '--dictionary',
                action="store_true",
                help=
                'dump the load time dictionary instead of running the command')
            group.add_argument(
                '--config',
                action="store_true",
                help=
                'dump the merged configuration instead of running the command')
            group.add_argument(
                '--plugins',
                action="store_true",
                help=
                ('dump the active plugins including their dictionaries instead of '
                 'running the command'))
        if clean:
            group.add_argument(
                '--clean',
                action="store_true",
                help='clean the artifacts that got produced by this command')
            group.add_argument(
                '--recursive-clean',
                type=int,
                metavar='N',
                help=
                'clean the artifacts that got produced by this and the preceding N commands'
            )

    @staticmethod
    def _unpack_cli_args(cli_args):
        return [cli_args.config_file]

    def _get_run_method(self, cli_args):
        if hasattr(cli_args, 'dictionary') and cli_args.dictionary:
            return partial(self._print, self._get_load_time_dictionary)
        elif hasattr(cli_args, 'config') and cli_args.config:
            return partial(self._print, self._get_config)
        elif hasattr(cli_args, 'plugins') and cli_args.plugins:
            return partial(
                self._print,
                partial(self.dry_run, *self._unpack_cli_args(cli_args)))
        elif hasattr(cli_args, 'clean') and cli_args.clean:
            return partial(self.clean_recursive,
                           *self._unpack_cli_args(cli_args), 0)
        elif hasattr(
                cli_args,
                'recursive_clean') and cli_args.recursive_clean is not None:
            return partial(self.clean_recursive,
                           *self._unpack_cli_args(cli_args),
                           cli_args.recursive_clean)
        else:
            return partial(self.run, *self._unpack_cli_args(cli_args))

    def run(self, *args, **kwargs):
        raise FatalError('''Missing 'run' implementation for '{}'.'''.format(
            self._get_command_name()))

    def dry_run(self, *args, **kwargs):
        raise FatalError(
            '''Missing 'dry_run' implementation for '{}'.'''.format(
                self._get_command_name()))

    def clean_recursive(self, *args, **kwargs):
        raise FatalError(
            '''Missing 'clean_recursive' implementation for '{}'.'''.format(
                self._get_command_name()))

    def _get_load_time_dictionary(self):
        return self.config.get_load_time_dictionary()

    def _get_config(self):
        return self.config.get_config()

    def _get_plugins(self, sections):
        return self.config.get_plugins(sections)

    @staticmethod
    def _dump(introspection_result):
        return yaml.dump(introspection_result,
                         default_flow_style=False,
                         width=1000)

    def _print(self, method):
        print(self._dump(method()))

    def _require_sudo(self):
        if os.getuid() != 0:
            raise FatalError(
                ("The subcommand '{0}' requires superuser "
                 "privileges.\n"
                 "Use 'sudo edi ...'.").format(self._get_short_command_name()))

    def _pack_image(self, tempdir, datadir, name="result"):
        # advanced options such as numeric-owner are not supported by
        # python tarfile library - therefore we use the tar command line tool
        tempresult = "{0}.tar.{1}".format(name, self.config.get_compression())
        archive_path = os.path.join(tempdir, tempresult)

        cmd = []
        cmd.append("tar")
        cmd.append("--numeric-owner")
        cmd.extend(["-C", datadir])
        cmd.extend(["-acf", archive_path])
        cmd.extend(os.listdir(datadir))
        run(cmd, sudo=True, log_threshold=logging.INFO)
        return archive_path

    def _unpack_image(self, image, tempdir, subfolder="rootfs"):
        target_folder = os.path.join(tempdir, subfolder)
        os.makedirs(target_folder, exist_ok=True)

        cmd = []
        cmd.append("tar")
        cmd.append("--numeric-owner")
        cmd.extend(["-C", target_folder])
        cmd.extend(["-axf", image])
        run(cmd, sudo=True, log_threshold=logging.INFO)
        return target_folder
Beispiel #15
0
def test_config_nodes_presence(config_files):
    with open(config_files, "r") as main_file:
        parser = ConfigurationParser(main_file)
        assert parser.has_bootstrap_node()
        assert parser.get_bootstrap_repository()
        assert parser.get_bootstrap_architecture()
Beispiel #16
0
class EdiCommand(metaclass=CommandFactory):
    def clean(self, config_file):
        pass

    def _get_sibling_commands(self):
        assert len(type(self).__bases__) == 1
        commands = []
        parent_command = type(self).__bases__[0]._get_command_name()
        for _, command in get_sub_commands(parent_command).items():
            if command != self.__class__:
                commands.append(command)
        return commands

    def _clean_siblings_and_sub_commands(self, config_file):
        for command in self._get_sibling_commands():
            command().clean(config_file)
            command().clean_sub_commands(config_file)

    def clean_sub_commands(self, config_file):
        command = self._get_sub_command("clean")
        if command:
            command().clean(config_file)

    def _setup_parser(self, config_file):
        self.config = ConfigurationParser(config_file)

    @classmethod
    def _get_command_name(cls):
        return compose_command_name(cls)

    @classmethod
    def _get_short_command_name(cls):
        return cls.__name__.lower()

    @classmethod
    def _get_command_file_name_prefix(cls):
        return cls._get_command_name().replace(".", "_")

    @classmethod
    def _add_sub_commands(cls, parser):
        title = "{} commands".format(cls._get_short_command_name())
        subparsers = parser.add_subparsers(title=title,
                                           dest="sub_command_name")

        for _, command in get_sub_commands(cls._get_command_name()).items():
            command.advertise(subparsers)

    def _run_sub_command_cli(self, cli_args):
        self._get_sub_command(cli_args.sub_command_name)().run_cli(cli_args)

    def _get_sub_command(self, command):
        sub_command = "{}.{}".format(self._get_command_name(), command)
        return get_command(sub_command)

    @staticmethod
    def _require_config_file(parser):
        parser.add_argument('config_file',
                            type=argparse.FileType('r', encoding='UTF-8'))

    def _require_sudo(self):
        if os.getuid() != 0:
            raise FatalError(
                ("The subcommand '{0}' requires superuser "
                 "privileges.\n"
                 "Use 'sudo edi ...'.").format(self._get_short_command_name()))

    def _pack_image(self, tempdir, datadir, name="result"):
        # advanced options such as numeric-owner are not supported by
        # python tarfile library - therefore we use the tar command line tool
        tempresult = "{0}.tar.{1}".format(name, self.config.get_compression())
        archive_path = os.path.join(tempdir, tempresult)

        cmd = []
        cmd.append("tar")
        cmd.append("--numeric-owner")
        cmd.extend(["-C", datadir])
        cmd.extend(["-acf", archive_path])
        cmd.extend(os.listdir(datadir))
        run(cmd, sudo=True)
        return archive_path

    def _unpack_image(self, image, tempdir, subfolder="rootfs"):
        target_folder = os.path.join(tempdir, subfolder)
        os.makedirs(target_folder, exist_ok=True)

        cmd = []
        cmd.append("tar")
        cmd.append("--numeric-owner")
        cmd.extend(["-C", target_folder])
        cmd.extend(["-axf", image])
        run(cmd, sudo=True)
        return target_folder