class TestRuntimeConfig(object):
    def setup(self):
        with patch.dict('os.environ', {'HOME': '../data'}):
            self.runtime_config = RuntimeConfig()

    @raises(KiwiRuntimeConfigFormatError)
    def test_invalid_yaml_format(self):
        self.runtime_config.config_data = {'xz': None}
        self.runtime_config.get_xz_options()

    def test_get_xz_options(self):
        assert self.runtime_config.get_xz_options() == ['-a', '-b', 'xxx']

    def test_is_obs_public(self):
        assert self.runtime_config.is_obs_public() is True

    def test_is_obs_public_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.is_obs_public() is True

    def test_get_obs_download_server_url(self):
        assert self.runtime_config.get_obs_download_server_url() == \
            'http://example.com'

    def test_get_obs_download_server_url_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_obs_download_server_url() == \
                Defaults.get_obs_download_server_url()
Exemple #2
0
    def test_config_sections_from_home_base_config(self, mock_Cli):
        cli = Mock()
        cli.get_global_args.return_value = {}
        mock_Cli.return_value = cli
        with patch.dict('os.environ', {'HOME': '../data/kiwi_config/ok'}):
            runtime_config = RuntimeConfig(reread=True)

        assert runtime_config.get_xz_options() == ['-a', '-b', 'xxx']
        assert runtime_config.is_obs_public() is True
        assert runtime_config.get_bundle_compression() is True
        assert runtime_config.get_obs_download_server_url() == \
            'http://example.com'
        assert runtime_config.get_obs_api_server_url() == \
            'https://api.example.com'
        assert runtime_config.get_container_compression() is None
        assert runtime_config.get_iso_tool_category() == 'cdrtools'
        assert runtime_config.get_oci_archive_tool() == 'umoci'
        assert runtime_config.get_package_changes() is True
        assert runtime_config.get_disabled_runtime_checks() == [
            'check_dracut_module_for_oem_install_in_package_list',
            'check_container_tool_chain_installed'
        ]
        assert runtime_config.get_obs_api_credentials() == [{
            'user_name':
            'user_credentials'
        }]
Exemple #3
0
    def test_config_sections_defaults(self, mock_is_buildservice_worker):
        mock_is_buildservice_worker.return_value = True
        with patch.dict('os.environ', {'HOME': '../data/kiwi_config/defaults'}):
            runtime_config = RuntimeConfig(reread=True)

        assert runtime_config.get_bundle_compression(default=True) is True
        assert runtime_config.get_bundle_compression(default=False) is False
        assert runtime_config.is_obs_public() is True
        assert runtime_config.get_obs_download_server_url() == \
            Defaults.get_obs_download_server_url()
        assert runtime_config.get_obs_api_server_url() == \
            Defaults.get_obs_api_server_url()
        assert runtime_config.get_container_compression() is True
        assert runtime_config.get_iso_tool_category() == 'xorriso'
        assert runtime_config.get_oci_archive_tool() == 'umoci'
        assert runtime_config.get_package_changes() is False
Exemple #4
0
class TestRuntimeConfig(object):
    def setup(self):
        with patch.dict('os.environ', {'HOME': '../data'}):
            self.runtime_config = RuntimeConfig()

    @raises(KiwiRuntimeConfigFormatError)
    def test_invalid_yaml_format(self):
        self.runtime_config.config_data = {'xz': None}
        self.runtime_config.get_xz_options()

    def test_get_xz_options(self):
        assert self.runtime_config.get_xz_options() == ['-a', '-b', 'xxx']

    def test_is_obs_public(self):
        assert self.runtime_config.is_obs_public() is True

    def test_is_obs_public_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.is_obs_public() is True

    def test_get_obs_download_server_url(self):
        assert self.runtime_config.get_obs_download_server_url() == \
            'http://example.com'

    def test_get_obs_download_server_url_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_obs_download_server_url() == \
                Defaults.get_obs_download_server_url()

    def test_get_container_compression(self):
        assert self.runtime_config.get_container_compression() is None

    def test_get_container_compression_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_container_compression() == 'xz'

    @patch.object(RuntimeConfig, '_get_attribute')
    @patch('kiwi.logger.log.warning')
    def test_get_container_compression_invalid(self, mock_warning,
                                               mock_get_attribute):
        mock_get_attribute.return_value = 'foo'
        assert self.runtime_config.get_container_compression() == 'xz'
        mock_warning.assert_called_once_with(
            'Skipping invalid container compression: foo')

    @patch.object(RuntimeConfig, '_get_attribute')
    def test_get_container_compression_xz(self, mock_get_attribute):
        mock_get_attribute.return_value = 'xz'
        assert self.runtime_config.get_container_compression() == 'xz'

    def test_get_iso_tool_category(self):
        assert self.runtime_config.get_iso_tool_category() == 'cdrtools'

    def test_get_iso_tool_category_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_iso_tool_category() == 'xorriso'

    @patch.object(RuntimeConfig, '_get_attribute')
    @patch('kiwi.logger.log.warning')
    def test_get_iso_tool_category_invalid(self, mock_warning,
                                           mock_get_attribute):
        mock_get_attribute.return_value = 'foo'
        assert self.runtime_config.get_iso_tool_category() == 'xorriso'
        mock_warning.assert_called_once_with(
            'Skipping invalid iso tool category: foo')
Exemple #5
0
 def test_is_obs_public_default(self):
     with patch.dict('os.environ', {'HOME': './'}):
         runtime_config = RuntimeConfig()
         assert runtime_config.is_obs_public() is True
Exemple #6
0
class TestRuntimeConfig(object):
    def setup(self):
        with patch.dict('os.environ', {'HOME': '../data'}):
            self.runtime_config = RuntimeConfig()

    @patch('os.path.exists')
    @patch('yaml.load')
    def test_reading_system_wide_config_file(self, mock_yaml, mock_exists):
        exists_call_results = [True, False]

        def os_path_exists(config):
            return exists_call_results.pop()

        mock_exists.side_effect = os_path_exists
        with patch_open as mock_open:
            self.runtime_config = RuntimeConfig()
            mock_open.assert_called_once_with('/etc/kiwi.yml', 'r')

    @raises(KiwiRuntimeConfigFormatError)
    def test_invalid_yaml_format(self):
        self.runtime_config.config_data = {'xz': None}
        self.runtime_config.get_xz_options()

    def test_get_xz_options(self):
        assert self.runtime_config.get_xz_options() == ['-a', '-b', 'xxx']

    def test_is_obs_public(self):
        assert self.runtime_config.is_obs_public() is True

    def test_is_obs_public_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.is_obs_public() is True

    def test_get_obs_download_server_url(self):
        assert self.runtime_config.get_obs_download_server_url() == \
            'http://example.com'

    def test_get_obs_download_server_url_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_obs_download_server_url() == \
                Defaults.get_obs_download_server_url()

    def test_get_container_compression(self):
        assert self.runtime_config.get_container_compression() is None

    def test_get_container_compression_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_container_compression() == 'xz'

    @patch.object(RuntimeConfig, '_get_attribute')
    @patch('kiwi.logger.log.warning')
    def test_get_container_compression_invalid(
        self, mock_warning, mock_get_attribute
    ):
        mock_get_attribute.return_value = 'foo'
        assert self.runtime_config.get_container_compression() == 'xz'
        mock_warning.assert_called_once_with(
            'Skipping invalid container compression: foo'
        )

    @patch.object(RuntimeConfig, '_get_attribute')
    def test_get_container_compression_xz(self, mock_get_attribute):
        mock_get_attribute.return_value = 'xz'
        assert self.runtime_config.get_container_compression() == 'xz'

    def test_get_iso_tool_category(self):
        assert self.runtime_config.get_iso_tool_category() == 'cdrtools'

    def test_get_iso_tool_category_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_iso_tool_category() == 'xorriso'

    @patch.object(RuntimeConfig, '_get_attribute')
    @patch('kiwi.logger.log.warning')
    def test_get_iso_tool_category_invalid(
        self, mock_warning, mock_get_attribute
    ):
        mock_get_attribute.return_value = 'foo'
        assert self.runtime_config.get_iso_tool_category() == 'xorriso'
        mock_warning.assert_called_once_with(
            'Skipping invalid iso tool category: foo'
        )

    def test_get_oci_archive_tool(self):
        assert self.runtime_config.get_oci_archive_tool() == 'umoci'

    def test_get_oci_archive_tool_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_oci_archive_tool() == 'umoci'
class TestRuntimeConfig:
    def setup(self):
        with patch.dict('os.environ', {'HOME': '../data'}):
            self.runtime_config = RuntimeConfig()

        # pretend that none of the runtime config files exist, even if they do
        # (e.g. the system wide config file in /etc/kiwi.yml)
        # => this will give us the defaults
        with patch('os.path.exists', return_value=False):
            self.default_runtime_config = RuntimeConfig()

    @patch('os.path.exists')
    @patch('yaml.safe_load')
    def test_reading_system_wide_config_file(self, mock_yaml, mock_exists):
        exists_call_results = [True, False]

        def os_path_exists(config):
            return exists_call_results.pop()

        mock_exists.side_effect = os_path_exists
        with patch('builtins.open') as m_open:
            self.runtime_config = RuntimeConfig()
            m_open.assert_called_once_with('/etc/kiwi.yml', 'r')

    def test_invalid_yaml_format(self):
        self.runtime_config.config_data = {'xz': None}
        with raises(KiwiRuntimeConfigFormatError):
            self.runtime_config.get_xz_options()

    def test_get_xz_options(self):
        assert self.runtime_config.get_xz_options() == ['-a', '-b', 'xxx']

    def test_is_obs_public(self):
        assert self.runtime_config.is_obs_public() is True

    def test_get_bundle_compression(self):
        assert self.runtime_config.get_bundle_compression() is True

    def test_get_bundle_compression_default(self):
        assert self.default_runtime_config.get_bundle_compression(
            default=True) is True
        assert self.default_runtime_config.get_bundle_compression(
            default=False) is False

    def test_is_obs_public_default(self):
        assert self.default_runtime_config.is_obs_public() is True

    def test_get_obs_download_server_url(self):
        assert self.runtime_config.get_obs_download_server_url() == \
            'http://example.com'

    def test_get_obs_download_server_url_default(self):
        assert self.default_runtime_config.get_obs_download_server_url() == \
            Defaults.get_obs_download_server_url()

    def test_get_container_compression(self):
        assert self.runtime_config.get_container_compression() is None

    def test_get_container_compression_default(self):
        assert self.default_runtime_config.get_container_compression() == 'xz'

    @patch.object(RuntimeConfig, '_get_attribute')
    @patch('kiwi.logger.log.warning')
    def test_get_container_compression_invalid(self, mock_warning,
                                               mock_get_attribute):
        mock_get_attribute.return_value = 'foo'
        assert self.runtime_config.get_container_compression() == 'xz'
        mock_warning.assert_called_once_with(
            'Skipping invalid container compression: foo')

    @patch.object(RuntimeConfig, '_get_attribute')
    def test_get_container_compression_xz(self, mock_get_attribute):
        mock_get_attribute.return_value = 'xz'
        assert self.runtime_config.get_container_compression() == 'xz'

    def test_get_iso_tool_category(self):
        assert self.runtime_config.get_iso_tool_category() == 'cdrtools'

    def test_get_iso_tool_category_default(self):
        assert self.default_runtime_config.get_iso_tool_category() == 'xorriso'

    @patch.object(RuntimeConfig, '_get_attribute')
    @patch('kiwi.logger.log.warning')
    def test_get_iso_tool_category_invalid(self, mock_warning,
                                           mock_get_attribute):
        mock_get_attribute.return_value = 'foo'
        assert self.runtime_config.get_iso_tool_category() == 'xorriso'
        mock_warning.assert_called_once_with(
            'Skipping invalid iso tool category: foo')

    def test_get_oci_archive_tool(self):
        assert self.runtime_config.get_oci_archive_tool() == 'umoci'

    def test_get_oci_archive_tool_default(self):
        assert self.default_runtime_config.get_oci_archive_tool() == 'umoci'

    def test_get_disabled_runtime_checks(self):
        assert self.runtime_config.get_disabled_runtime_checks() == [
            'check_dracut_module_for_oem_install_in_package_list',
            'check_container_tool_chain_installed'
        ]
Exemple #8
0
class Uri:
    """
    **Normalize url types available in a kiwi configuration into
    standard mime types**

    :param str repo_type: repository type name. Only needed if the uri
        is not enough to determine the repository type
        e.g for yast2 vs. rpm-md obs repositories
    :param str uri: URI, repository location, file
    :param list mount_stack: list of mounted locations
    :param dict remote_uri_types: dictionary of remote uri type names
    :param dict local_uri_type: dictionary of local uri type names
    """
    def __init__(self, uri, repo_type=None):
        self.runtime_config = RuntimeConfig()
        self.repo_type = repo_type
        self.uri = uri if not uri.startswith(os.sep) else ''.join(
            [Defaults.get_default_uri_type(), uri]
        )
        self.mount_stack = []

        self.remote_uri_types = {
            'http': True,
            'https': True,
            'ftp': True,
            'obs': True
        }
        self.local_uri_type = {
            'iso': True,
            'dir': True,
            'file': True,
            'obsrepositories': True
        }

    def translate(self, check_build_environment=True):
        """
        Translate repository location according to their URI type

        Depending on the URI type the provided location needs to
        be adapted e.g loop mounted in case of an ISO or updated
        by the service URL in case of an open buildservice project
        name

        :raises KiwiUriStyleUnknown: if the uri scheme can't be detected, is
            unknown or it is inconsistent with the build environment
        :param bool check_build_environment: specify if the uri translation
            should depend on the environment the build is called in. As of
            today this only effects the translation result if the image
            build happens inside of the Open Build Service

        :rtype: str
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            raise KiwiUriStyleUnknown(
                'URI scheme not detected {uri}'.format(uri=self.uri)
            )
        elif uri.scheme == 'obs':
            if check_build_environment and Defaults.is_buildservice_worker():
                return self._buildservice_path(
                    name=''.join([uri.netloc, uri.path]).replace(':/', ':'),
                    fragment=uri.fragment,
                    urischeme=uri.scheme
                )
            else:
                return self._obs_project_download_link(
                    ''.join([uri.netloc, uri.path]).replace(':/', ':')
                )
        elif uri.scheme == 'obsrepositories':
            if not Defaults.is_buildservice_worker():
                raise KiwiUriStyleUnknown(
                    'Only the buildservice can use the {0} schema'.format(
                        uri.scheme
                    )
                )
            return self._buildservice_path(
                name=''.join([uri.netloc, uri.path]).replace(':/', ':'),
                fragment=uri.fragment,
                urischeme=uri.scheme
            )
        elif uri.scheme == 'dir':
            return self._local_path(uri.path)
        elif uri.scheme == 'file':
            return self._local_path(uri.path)
        elif uri.scheme == 'iso':
            return self._iso_mount_path(uri.path)
        elif uri.scheme.startswith('http') or uri.scheme == 'ftp':
            if self._get_credentials_uri() or not uri.query:
                return ''.join(
                    [uri.scheme, '://', uri.netloc, uri.path]
                )
            else:
                return ''.join(
                    [uri.scheme, '://', uri.netloc, uri.path, '?', uri.query]
                )
        else:
            raise KiwiUriStyleUnknown(
                'URI schema %s not supported' % self.uri
            )

    def credentials_file_name(self):
        """
        Filename to store repository credentials

        :return: credentials file name

        :rtype: str
        """
        uri = self._get_credentials_uri()
        # initialize query with default credentials file name.
        # The information will be overwritten if the uri contains
        # a parameter query with a credentials parameter
        query = {'credentials': 'kiwiRepoCredentials'}

        if uri:
            query = dict(params.split('=') for params in uri.query.split('&'))

        return query['credentials']

    def alias(self):
        """
        Create hexdigest from URI as alias

        If the repository definition from the XML description does
        not provide an alias, kiwi creates one for you. However it's
        better to assign a human readable alias in the XML
        configuration

        :return: alias name as hexdigest

        :rtype: str
        """
        return hashlib.md5(self.uri.encode()).hexdigest()

    def is_remote(self):
        """
        Check if URI is a remote or local location

        :return: True or False

        :rtype: bool
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            raise KiwiUriStyleUnknown(
                'URI scheme not detected %s' % self.uri
            )
        if uri.scheme == 'obs' and Defaults.is_buildservice_worker():
            return False
        elif uri.scheme in self.remote_uri_types:
            return True
        elif uri.scheme in self.local_uri_type:
            return False
        else:
            raise KiwiUriTypeUnknown(
                'URI type %s unknown' % uri.scheme
            )

    def is_public(self):
        """
        Check if URI is considered to be publicly reachable

        :return: True or False

        :rtype: bool
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            # unknown uri schema is considered not public
            return False
        elif uri.scheme == 'obs':
            # obs is public but only if the configured download_server is public
            return self.runtime_config.is_obs_public()
        elif uri.scheme in self.remote_uri_types:
            # listed in remote uri types, thus public
            return True
        else:
            # unknown uri type considered not public
            return False

    def get_fragment(self):
        """
        Returns the fragment part of the URI.

        :return: fragment part of the URI if any, empty string otherwise

        :rtype: str
        """
        uri = urlparse(self.uri)
        return uri.fragment

    def _get_credentials_uri(self):
        uri = urlparse(self.uri)
        if uri.query and uri.query.startswith('credentials='):
            return uri

    def _iso_mount_path(self, path):
        # The prefix name 'kiwi_iso_mount' has a meaning here because the
        # zypper repository manager looks up iso mount paths by its repo
        # source name
        iso_mount_path = mkdtemp(prefix='kiwi_iso_mount.')
        iso_mount = MountManager(
            device=path, mountpoint=iso_mount_path
        )
        self.mount_stack.append(iso_mount)
        iso_mount.mount()
        return iso_mount.mountpoint

    def _local_path(self, path):
        return os.path.normpath(path)

    def _obs_project_download_link(self, name):
        name_parts = name.split(os.sep)
        repository = name_parts.pop()
        project = os.sep.join(name_parts)
        try:
            download_link = os.sep.join(
                [
                    self.runtime_config.get_obs_download_server_url(),
                    project.replace(':', ':/'), repository
                ]
            )
            if not Defaults.is_buildservice_worker():
                request = requests.get(download_link)
                request.raise_for_status()
                return request.url
            else:
                log.warning(
                    'Using {0} without location verification due to build '
                    'in isolated environment'.format(download_link)
                )
                return download_link
        except Exception as e:
            raise KiwiUriOpenError(
                '{0}: {1}'.format(type(e).__name__, format(e))
            )

    def _buildservice_path(self, name, urischeme, fragment=None):
        """
        Special to openSUSE buildservice. If the buildservice builds
        the image it arranges the repos for each build in a special
        environment, the so called build worker.
        """
        bs_source_dir = '/usr/src/packages/SOURCES'
        if self.repo_type == 'container':
            if urischeme == 'obsrepositories':
                local_path = os.sep.join(
                    [bs_source_dir, 'containers/_obsrepositories', name]
                )
            else:
                local_path = os.sep.join(
                    [bs_source_dir, 'containers', name]
                )
            if fragment:
                local_path = ''.join([local_path, '#', fragment])
        else:
            local_path = os.sep.join(
                [bs_source_dir, 'repos', name]
            )
        return self._local_path(local_path)

    def __del__(self):
        for mount in reversed(self.mount_stack):
            if mount.is_mounted():
                if mount.umount():
                    Path.wipe(mount.mountpoint)
Exemple #9
0
class Uri:
    """
    **Normalize and manage URI types**
    """
    def __init__(self,
                 uri: str,
                 repo_type: str = 'rpm-md',
                 source_type: str = ''):
        """
        Manage kiwi source URIs and allow transformation into
        standard URLs

        :param str uri: URI, remote, local or metalink repository location
            The resource type as part of the URI can be set to one of:
            * http:
            * https:
            * ftp:
            * obs:
            * dir:
            * file:
            * obsrepositories:
            * this:
            The special this:// type resolve to the image description
            directory. The code to resolve this is not part of the Uri
            class because it has no state information about the image
            description directory. Therefore the resolving of the this://
            path happens on construction of an XMLState object as
            part of the resolve_this_path() method. The method resolves
            the path into a native dir:// URI which can be properly
            handled here.
        :param str repo_type:
            repository type name, defaults to 'rpm-md' and is only
            effectively used when building inside of the open
            build service which maps local repositories to a
            specific environment
        :param str source_type:
            specify source type if the provided URI is a service.
            Currently only the metalink source type is handled
        """
        self.runtime_config = RuntimeConfig()

        if source_type == 'metalink':
            uri = self._resolve_metalink_uri(uri)

        self.repo_type = repo_type
        self.uri = uri if not uri.startswith(os.sep) else ''.join(
            [Defaults.get_default_uri_type(), uri])

        self.remote_uri_types = {
            'http': True,
            'https': True,
            'ftp': True,
            'obs': True
        }
        self.local_uri_type = {
            'dir': True,
            'file': True,
            'obsrepositories': True
        }

    def translate(self, check_build_environment: bool = True) -> str:
        """
        Translate repository location according to their URI type

        Depending on the URI type the provided location needs to
        be adapted e.g updated by the service URL in case of an
        open buildservice project name

        :raises KiwiUriStyleUnknown: if the uri scheme can't be detected, is
            unknown or it is inconsistent with the build environment
        :param bool check_build_environment: specify if the uri translation
            should depend on the environment the build is called in. As of
            today this only effects the translation result if the image
            build happens inside of the Open Build Service

        :return: translated repository location

        :rtype: str
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            raise KiwiUriStyleUnknown(
                'URI scheme not detected {uri}'.format(uri=self.uri))
        elif uri.scheme == 'obs':
            if check_build_environment and Defaults.is_buildservice_worker():
                return self._buildservice_path(name=''.join(
                    [uri.netloc, uri.path]).replace(':/', ':'),
                                               fragment=uri.fragment,
                                               urischeme=uri.scheme)
            else:
                return self._obs_project_download_link(''.join(
                    [uri.netloc, uri.path]).replace(':/', ':'))
        elif uri.scheme == 'obsrepositories':
            if not Defaults.is_buildservice_worker():
                raise KiwiUriStyleUnknown(
                    'Only the buildservice can use the {0} schema'.format(
                        uri.scheme))
            return self._buildservice_path(name=''.join([uri.netloc, uri.path
                                                         ]).replace(':/', ':'),
                                           fragment=uri.fragment,
                                           urischeme=uri.scheme)
        elif uri.scheme == 'dir':
            return self._local_path(uri.path)
        elif uri.scheme == 'file':
            return self._local_path(uri.path)
        elif uri.scheme.startswith('http') or uri.scheme == 'ftp':
            if self._get_credentials_uri() or not uri.query:
                return ''.join([uri.scheme, '://', uri.netloc, uri.path])
            else:
                return ''.join(
                    [uri.scheme, '://', uri.netloc, uri.path, '?', uri.query])
        else:
            raise KiwiUriStyleUnknown('URI schema %s not supported' % self.uri)

    def credentials_file_name(self) -> str:
        """
        Filename to store repository credentials

        :return: credentials file name

        :rtype: str
        """
        uri = self._get_credentials_uri()
        # initialize query with default credentials file name.
        # The information will be overwritten if the uri contains
        # a parameter query with a credentials parameter
        query = {'credentials': 'kiwiRepoCredentials'}

        if uri:
            query = dict(params.split('=')
                         for params in uri.query.split('&'))  # type: ignore

        return query['credentials']

    def alias(self) -> str:
        """
        Create hexdigest from URI as alias

        If the repository definition from the XML description does
        not provide an alias, kiwi creates one for you. However it's
        better to assign a human readable alias in the XML
        configuration

        :return: alias name as hexdigest

        :rtype: str
        """
        return hashlib.md5(self.uri.encode()).hexdigest()

    def is_remote(self) -> bool:
        """
        Check if URI is a remote or local location

        :return: True|False

        :rtype: bool
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            raise KiwiUriStyleUnknown('URI scheme not detected %s' % self.uri)
        if uri.scheme == 'obs' and Defaults.is_buildservice_worker():
            return False
        elif uri.scheme in self.remote_uri_types:
            return True
        elif uri.scheme in self.local_uri_type:
            return False
        else:
            raise KiwiUriTypeUnknown('URI type %s unknown' % uri.scheme)

    def is_public(self) -> bool:
        """
        Check if URI is considered to be publicly reachable

        :return: True|False

        :rtype: bool
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            # unknown uri schema is considered not public
            return False
        elif uri.scheme == 'obs':
            # obs is public but only if the configured download_server is public
            return self.runtime_config.is_obs_public()
        elif uri.scheme in self.remote_uri_types:
            # listed in remote uri types, thus public
            return True
        else:
            # unknown uri type considered not public
            return False

    def get_fragment(self) -> str:
        """
        Returns the fragment part of the URI.

        :return: fragment part of the URI if any, empty string otherwise

        :rtype: str
        """
        uri = urlparse(self.uri)
        return uri.fragment

    def _get_credentials_uri(self) -> Optional[ParseResult]:
        uri = urlparse(self.uri)
        credentials_uri = None
        if uri.query and uri.query.startswith('credentials='):
            credentials_uri = uri
        return credentials_uri

    def _local_path(self, path: str) -> str:
        return os.path.abspath(os.path.normpath(path))

    def _obs_project_download_link(self, name: str) -> str:
        name_parts = name.split(os.sep)
        repository = name_parts.pop()
        project = os.sep.join(name_parts)
        download_link = None
        try:
            download_link = os.sep.join([
                self.runtime_config.get_obs_download_server_url(),
                project.replace(':', ':/'), repository
            ])
            if not Defaults.is_buildservice_worker():
                request = requests.get(download_link)
                request.raise_for_status()
                return request.url
            else:
                log.warning(
                    'Using {0} without location verification due to build '
                    'in isolated environment'.format(download_link))
                return download_link
        except Exception as issue:
            raise KiwiUriOpenError(f'{download_link}: {issue}')

    def _buildservice_path(self,
                           name: str,
                           urischeme: str,
                           fragment: str = '') -> str:
        """
        Special to openSUSE buildservice. If the buildservice builds
        the image it arranges the repos for each build in a special
        environment, the so called build worker.
        """
        bs_source_dir = '/usr/src/packages/SOURCES'
        if self.repo_type == 'container':
            if urischeme == 'obsrepositories':
                local_path = os.sep.join(
                    [bs_source_dir, 'containers/_obsrepositories', name])
            else:
                local_path = os.sep.join([bs_source_dir, 'containers', name])
            if fragment:
                local_path = ''.join([local_path, '#', fragment])
        else:
            local_path = os.sep.join([bs_source_dir, 'repos', name])
        return self._local_path(local_path)

    def _resolve_metalink_uri(self, uri: str) -> str:
        selected_repo_source = uri
        namespace_map = dict(metalink="http://www.metalinker.org/")
        expression = '//metalink:file[@name="repomd.xml"]/metalink:resources/*'
        try:
            metalink_location = urlopen(Request(uri))
            xml = etree.parse(metalink_location)
            url_list = xml.getroot().xpath(expression,
                                           namespaces=namespace_map)
            source_dict = {}
            for url in url_list:
                if url.get('protocol') == 'https':
                    source_dict[url.text] = int(url.get('preference'))
            start_preference = 0
            for url in sorted(source_dict.keys()):
                preference = source_dict[url]
                if preference > start_preference:
                    selected_repo_source = url
                    start_preference = preference
        except Exception as issue:
            raise KiwiUriOpenError(f'Failed to resolve metalink URI: {issue}')
        selected_repo_source = selected_repo_source.replace(
            'repodata/repomd.xml', '')
        return selected_repo_source
Exemple #10
0
class Uri(object):
    """
    **Normalize url types available in a kiwi configuration into
    standard mime types**

    :param str repo_type: repository type name. Only needed if the uri
        is not enough to determine the repository type
        e.g for yast2 vs. rpm-md obs repositories
    :param str uri: URI, repository location, file
    :param list mount_stack: list of mounted locations
    :param dict remote_uri_types: dictionary of remote uri type names
    :param dict local_uri_type: dictionary of local uri type names
    """
    def __init__(self, uri, repo_type=None):
        self.runtime_config = RuntimeConfig()
        self.repo_type = repo_type
        self.uri = uri
        self.mount_stack = []

        self.remote_uri_types = {
            'http': True,
            'https': True,
            'ftp': True,
            'obs': True
        }
        self.local_uri_type = {
            'iso': True,
            'dir': True,
            'file': True,
            'obsrepositories': True
        }

    def translate(self, check_build_environment=True):
        """
        Translate repository location according to their URI type

        Depending on the URI type the provided location needs to
        be adapted e.g loop mounted in case of an ISO or updated
        by the service URL in case of an open buildservice project
        name

        :raises KiwiUriStyleUnknown: if the uri scheme can't be detected, is
            unknown or it is inconsistent with the build environment
        :param bool check_build_environment: specify if the uri translation
            should depend on the environment the build is called in. As of
            today this only effects the translation result if the image
            build happens inside of the Open Build Service

        :rtype: str
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            raise KiwiUriStyleUnknown(
                'URI scheme not detected {uri}'.format(uri=self.uri)
            )
        elif uri.scheme == 'obs':
            if check_build_environment and Defaults.is_buildservice_worker():
                return self._buildservice_path(
                    name=''.join([uri.netloc, uri.path]).replace(':/', ':'),
                    fragment=uri.fragment,
                    urischeme=uri.scheme
                )
            else:
                return self._obs_project_download_link(
                    ''.join([uri.netloc, uri.path]).replace(':/', ':')
                )
        elif uri.scheme == 'obsrepositories':
            if not Defaults.is_buildservice_worker():
                raise KiwiUriStyleUnknown(
                    'Only the buildservice can use the {0} schema'.format(
                        uri.scheme
                    )
                )
            return self._buildservice_path(
                name=''.join([uri.netloc, uri.path]).replace(':/', ':'),
                fragment=uri.fragment,
                urischeme=uri.scheme
            )
        elif uri.scheme == 'dir':
            return self._local_path(uri.path)
        elif uri.scheme == 'file':
            return self._local_path(uri.path)
        elif uri.scheme == 'iso':
            return self._iso_mount_path(uri.path)
        elif uri.scheme.startswith('http') or uri.scheme == 'ftp':
            return ''.join([uri.scheme, '://', uri.netloc, uri.path])
        else:
            raise KiwiUriStyleUnknown(
                'URI schema %s not supported' % self.uri
            )

    def credentials_file_name(self):
        """
        Filename to store repository credentials

        :return: credentials file name

        :rtype: str
        """
        uri = urlparse(self.uri)
        # initialize query with default credentials file name.
        # The information will be overwritten if the uri contains
        # a parameter query with a credentials parameter
        query = {'credentials': 'kiwiRepoCredentials'}

        if uri.query:
            query = dict(params.split('=') for params in uri.query.split('&'))

        return query['credentials']

    def alias(self):
        """
        Create hexdigest from URI as alias

        If the repository definition from the XML description does
        not provide an alias, kiwi creates one for you. However it's
        better to assign a human readable alias in the XML
        configuration

        :return: alias name as hexdigest

        :rtype: str
        """
        return hashlib.md5(self.uri.encode()).hexdigest()

    def is_remote(self):
        """
        Check if URI is a remote or local location

        :return: True or False

        :rtype: bool
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            raise KiwiUriStyleUnknown(
                'URI scheme not detected %s' % self.uri
            )
        if uri.scheme == 'obs' and Defaults.is_buildservice_worker():
            return False
        elif uri.scheme in self.remote_uri_types:
            return True
        elif uri.scheme in self.local_uri_type:
            return False
        else:
            raise KiwiUriTypeUnknown(
                'URI type %s unknown' % uri.scheme
            )

    def is_public(self):
        """
        Check if URI is considered to be publicly reachable

        :return: True or False

        :rtype: bool
        """
        uri = urlparse(self.uri)
        if not uri.scheme:
            # unknown uri schema is considered not public
            return False
        elif uri.scheme == 'obs':
            # obs is public but only if the configured download_server is public
            return self.runtime_config.is_obs_public()
        elif uri.scheme in self.remote_uri_types:
            # listed in remote uri types, thus public
            return True
        else:
            # unknown uri type considered not public
            return False

    def get_fragment(self):
        """
        Returns the fragment part of the URI.

        :return: fragment part of the URI if any, None otherwise

        :rtype: str, None
        """
        uri = urlparse(self.uri)
        return uri.fragment

    def _iso_mount_path(self, path):
        # The prefix name 'kiwi_iso_mount' has a meaning here because the
        # zypper repository manager looks up iso mount paths by its repo
        # source name
        iso_mount_path = mkdtemp(prefix='kiwi_iso_mount.')
        iso_mount = MountManager(
            device=path, mountpoint=iso_mount_path
        )
        self.mount_stack.append(iso_mount)
        iso_mount.mount()
        return iso_mount.mountpoint

    def _local_path(self, path):
        return os.path.normpath(path)

    def _obs_project_download_link(self, name):
        name_parts = name.split(os.sep)
        repository = name_parts.pop()
        project = os.sep.join(name_parts)
        try:
            download_link = os.sep.join(
                [
                    self.runtime_config.get_obs_download_server_url(),
                    project.replace(':', ':/'), repository
                ]
            )
            if not Defaults.is_buildservice_worker():
                request = requests.get(download_link)
                request.raise_for_status()
                return request.url
            else:
                log.warning(
                    'Using {0} without location verification due to build '
                    'in isolated environment'.format(download_link)
                )
                return download_link
        except Exception as e:
            raise KiwiUriOpenError(
                '{0}: {1}'.format(type(e).__name__, format(e))
            )

    def _buildservice_path(self, name, urischeme, fragment=None):
        """
        Special to openSUSE buildservice. If the buildservice builds
        the image it arranges the repos for each build in a special
        environment, the so called build worker.
        """
        bs_source_dir = '/usr/src/packages/SOURCES'
        if self.repo_type == 'container':
            if urischeme == 'obsrepositories':
                local_path = os.sep.join(
                    [bs_source_dir, 'containers/_obsrepositories', name]
                )
            else:
                local_path = os.sep.join(
                    [bs_source_dir, 'containers', name]
                )
            if fragment:
                local_path = ''.join([local_path, '#', fragment])
        else:
            local_path = os.sep.join(
                [bs_source_dir, 'repos', name]
            )
        return self._local_path(local_path)

    def __del__(self):
        for mount in reversed(self.mount_stack):
            if mount.is_mounted():
                if mount.umount():
                    Path.wipe(mount.mountpoint)
class TestRuntimeConfig(object):
    def setup(self):
        with patch.dict('os.environ', {'HOME': '../data'}):
            self.runtime_config = RuntimeConfig()

    @raises(KiwiRuntimeConfigFormatError)
    def test_invalid_yaml_format(self):
        self.runtime_config.config_data = {'xz': None}
        self.runtime_config.get_xz_options()

    def test_get_xz_options(self):
        assert self.runtime_config.get_xz_options() == ['-a', '-b', 'xxx']

    def test_is_obs_public(self):
        assert self.runtime_config.is_obs_public() is True

    def test_is_obs_public_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.is_obs_public() is True

    def test_get_obs_download_server_url(self):
        assert self.runtime_config.get_obs_download_server_url() == \
            'http://example.com'

    def test_get_obs_download_server_url_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_obs_download_server_url() == \
                Defaults.get_obs_download_server_url()

    def test_get_container_compression(self):
        assert self.runtime_config.get_container_compression() is None

    def test_get_container_compression_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_container_compression() == 'xz'

    @patch.object(RuntimeConfig, '_get_attribute')
    @patch('kiwi.logger.log.warning')
    def test_get_container_compression_invalid(
        self, mock_warning, mock_get_attribute
    ):
        mock_get_attribute.return_value = 'foo'
        assert self.runtime_config.get_container_compression() == 'xz'
        mock_warning.assert_called_once_with(
            'Skipping invalid container compression: foo'
        )

    @patch.object(RuntimeConfig, '_get_attribute')
    def test_get_container_compression_xz(self, mock_get_attribute):
        mock_get_attribute.return_value = 'xz'
        assert self.runtime_config.get_container_compression() == 'xz'

    def test_get_iso_tool_category(self):
        assert self.runtime_config.get_iso_tool_category() == 'cdrtools'

    def test_get_iso_tool_category_default(self):
        with patch.dict('os.environ', {'HOME': './'}):
            runtime_config = RuntimeConfig()
            assert runtime_config.get_iso_tool_category() == 'xorriso'

    @patch.object(RuntimeConfig, '_get_attribute')
    @patch('kiwi.logger.log.warning')
    def test_get_iso_tool_category_invalid(
        self, mock_warning, mock_get_attribute
    ):
        mock_get_attribute.return_value = 'foo'
        assert self.runtime_config.get_iso_tool_category() == 'xorriso'
        mock_warning.assert_called_once_with(
            'Skipping invalid iso tool category: foo'
        )
 def test_is_obs_public_default(self):
     with patch.dict('os.environ', {'HOME': './'}):
         runtime_config = RuntimeConfig()
         assert runtime_config.is_obs_public() is True