kiwi.runtime_config

  1# Copyright (c) 2015 SUSE Linux GmbH.  All rights reserved.
  2#
  3# This file is part of kiwi.
  4#
  5# kiwi is free software: you can redistribute it and/or modify
  6# it under the terms of the GNU General Public License as published by
  7# the Free Software Foundation, either version 3 of the License, or
  8# (at your option) any later version.
  9#
 10# kiwi is distributed in the hope that it will be useful,
 11# but WITHOUT ANY WARRANTY; without even the implied warranty of
 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13# GNU General Public License for more details.
 14#
 15# You should have received a copy of the GNU General Public License
 16# along with kiwi.  If not, see <http://www.gnu.org/licenses/>
 17#
 18import os
 19import logging
 20import functools
 21from typing import (
 22    Literal, List, Optional, NamedTuple, Callable, Dict, Any
 23)
 24import yaml
 25
 26# project
 27import kiwi.defaults as defaults
 28
 29from kiwi.defaults import Defaults
 30from kiwi.utils.size import StringToSize
 31from kiwi.utils.checksum import Checksum
 32from kiwi.exceptions import (
 33    KiwiRuntimeConfigFormatError,
 34    KiwiRuntimeConfigFileError
 35)
 36
 37log = logging.getLogger('kiwi')
 38
 39RUNTIME_CONFIG: Optional[Dict[str, Any]] = None
 40
 41
 42class ShasumT(NamedTuple):
 43    suffix: str
 44    digest: Callable
 45
 46
 47class RuntimeConfig:
 48    """
 49    **Implements reading of runtime configuration files:**
 50
 51    1. vendor: /usr/share/kiwi/kiwi.yml + /usr/share/kiwi/kiwi.yml.d/*.yml
 52    2. admin: /etc/kiwi.yml + /etc/kiwi.yml.d/*.yml
 53    3. ~/.config/kiwi/config.yml
 54    4. Check for --config provided from the CLI
 55
 56    The KIWI runtime configuration file is a yaml formatted file
 57    containing information to control the behavior of the tools
 58    used by KIWI on the build host.
 59
 60    :param bool reread: reread runtime config
 61    """
 62    def __init__(self, reread: bool = False):
 63        global RUNTIME_CONFIG
 64
 65        if RUNTIME_CONFIG is None or reread:
 66            custom_config_file = defaults.CUSTOM_RUNTIME_CONFIG_FILE
 67            config_files = []
 68            # 1. vendor
 69            config_file = defaults.USR_RUNTIME_CONFIG_FILE
 70            if os.path.exists(config_file):
 71                config_files.append(config_file)
 72            config_files += self._read_drop_ins_dir(
 73                defaults.USR_RUNTIME_CONFIG_DIR
 74            )
 75            # 2. admin
 76            config_file = defaults.ETC_RUNTIME_CONFIG_FILE
 77            if os.path.exists(config_file):
 78                config_files.append(config_file)
 79            config_files += self._read_drop_ins_dir(
 80                defaults.ETC_RUNTIME_CONFIG_DIR
 81            )
 82            # 3. home
 83            if self._home_path():
 84                config_file = os.sep.join(
 85                    [self._home_path(), '.config', 'kiwi', 'config.yml']
 86                )
 87                if os.path.exists(config_file):
 88                    config_files.append(config_file)
 89            # 4. cmdline
 90            if custom_config_file:
 91                config_file = custom_config_file
 92                if not os.path.isfile(config_file):
 93                    raise KiwiRuntimeConfigFileError(
 94                        f'Custom config file {config_file!r} not found'
 95                    )
 96                config_files.append(config_file)
 97            # read all config files...
 98            RUNTIME_CONFIG = {}
 99            for config_file in config_files:
100                log.info(
101                    f'Reading runtime config file: {config_file!r}'
102                )
103                with open(config_file, 'r') as config:
104                    RUNTIME_CONFIG.update(yaml.safe_load(config) or {})
105
106    def get_credentials_verification_metadata_signing_key_file(self) -> str:
107        """
108        Return verification metadata signing key file, used for
109        signature creation of rootfs verification metadata:
110
111        credentials:
112          - verification_metadata_signing_key_file: ...
113
114        There is no default value for this setting available
115
116        :return: file path name or ''
117
118        :rtype: str
119        """
120        signing_key_file = self._get_attribute(
121            element='credentials',
122            attribute='verification_metadata_signing_key_file'
123        )
124        return signing_key_file if signing_key_file else ''
125
126    def get_obs_download_server_url(self) -> str:
127        """
128        Return URL of buildservice download server in:
129
130        obs:
131          - download_url: ...
132
133        if no configuration exists the downloadserver from
134        the Defaults class is returned
135
136        :return: URL type data
137
138        :rtype: str
139        """
140        obs_download_server_url = self._get_attribute(
141            element='obs', attribute='download_url'
142        )
143        return obs_download_server_url if obs_download_server_url else \
144            Defaults.get_obs_download_server_url()
145
146    def get_obs_api_server_url(self) -> str:
147        """
148        Return URL of buildservice API server in:
149
150        obs:
151          - api_url: ...
152
153        if no configuration exists the API server from
154        the Defaults class is returned
155
156        :return: URL type data
157
158        :rtype: str
159        """
160        obs_api_server_url = self._get_attribute(
161            element='obs', attribute='api_url'
162        )
163        return obs_api_server_url if obs_api_server_url else \
164            Defaults.get_obs_api_server_url()
165
166    def get_obs_api_credentials(self) -> List[str]:
167        """
168        Return OBS API credentials if configured:
169
170        obs:
171          - user:
172              - user_name: user_credentials
173
174        :return: List of Dicts with credentials per user
175
176        :rtype: list
177        """
178        obs_users = self._get_attribute(element='obs', attribute='user') or []
179        return obs_users
180
181    def is_obs_public(self) -> bool:
182        """
183        Check if the buildservice configuration is public or private in:
184
185        obs:
186          - public: true|false
187
188        if no configuration exists we assume to be public
189
190        :return: True or False
191
192        :rtype: bool
193        """
194        obs_public = self._get_attribute(element='obs', attribute='public')
195        if obs_public is None:
196            # if the privacy attribute is not set we assume to be public
197            obs_public = True
198        return bool(obs_public)
199
200    def get_package_changes(self, default: bool = True) -> bool:
201        """
202        Return boolean value to express if the image build and bundle
203        should contain a .changes file. The .changes file contains
204        the package changelog information from all packages installed
205        into the image.
206
207        bundle:
208          - has_package_changes: true|false
209
210        By default the creation is switched on.
211        When building in the Open Build Service the default is
212        switched off because obs provides a .report file containing
213        the same information.
214
215        :param bool default: Default value
216
217        :return: True or False
218
219        :rtype: bool
220        """
221        bundle_package_changes = self._get_attribute(
222            element='bundle', attribute='has_package_changes'
223        )
224        if bundle_package_changes is None:
225            if Defaults.is_buildservice_worker():
226                bundle_package_changes = False
227            else:
228                bundle_package_changes = default
229        return bool(bundle_package_changes)
230
231    def get_bundle_compression(self, default: bool = True) -> bool:
232        """
233        Return boolean value to express if the image bundle should
234        contain XZ compressed image results or not.
235
236        bundle:
237          - compress: true|false
238
239        If compression of image build results is activated the size
240        of the bundle is smaller and the download speed increases.
241        However the image must be uncompressed before use
242
243        If no compression is explicitly configured, the provided
244        default value applies
245
246        :param bool default: Default value
247
248        :return: True or False
249
250        :rtype: bool
251        """
252        bundle_compress = self._get_attribute(
253            element='bundle', attribute='compress'
254        )
255        if bundle_compress is None:
256            bundle_compress = default
257        return bool(bundle_compress)
258
259    def get_checksum_handler(
260        self,
261        source_filename: str,
262        target_filename: Optional[str] = None,
263        default: str = '256',
264        bundle_lookup: bool = False
265    ) -> ShasumT:
266        """
267        Return a ShasumT with information about the configured
268        shasum suffix name and the digest(Checksum) callable. The
269        following configuration setting allows to configure the
270        size of the checksum:
271
272        shasum:
273          - size: 256
274
275        bundle:
276          - shasum_size: "256"
277
278        Instructs kiwi to use the provided shasum size. Supported
279        values are 256 (default) and 512. In case of an unsupported
280        value the default is used. A value from the bundle section
281        takes precedence over the global shasum size specified
282        in the shasum section for creating bundle results. If no
283        information for bundle results is specified, the global
284        shasum size or the default applies.
285
286        :param str source_filename: filename to calculate checksum for
287        :param str target_filename: filename to write checksum to
288        :param str default: default size set to 256
289
290        :rtype: ShasumT
291        """
292        supported_shasums = {
293            '256': ShasumT(
294                suffix='.sha256',
295                digest=functools.partial(
296                    Checksum(source_filename).sha256, target_filename
297                )
298            ),
299            '512': ShasumT(
300                suffix='.sha512',
301                digest=functools.partial(
302                    Checksum(source_filename).sha512, target_filename
303                )
304            )
305        }
306        shasum_size = self._get_attribute(
307            element='shasum', attribute='size'
308        )
309        if bundle_lookup:
310            bundle_shasum_size = self._get_attribute(
311                element='bundle', attribute='shasum_size'
312            )
313            if bundle_shasum_size:
314                shasum_size = bundle_shasum_size
315        if supported_shasums.get(shasum_size):
316            return supported_shasums[shasum_size]
317        return supported_shasums['256']
318
319    def get_xz_options(self) -> Optional[List[str]]:
320        """
321        Return list of XZ compression options in:
322
323        xz:
324          - options: ...
325
326        if no configuration exists None is returned
327
328        :return:
329            Contains list of options
330
331            .. code:: python
332
333                ['--option=value']
334
335        :rtype: list
336        """
337        xz_options = self._get_attribute(element='xz', attribute='options')
338        return xz_options.split() if xz_options else None
339
340    def get_container_compression(self) -> bool:
341        """
342        Return compression for container images
343
344        container:
345          - compress: xz|none|true|false
346
347        if no or invalid configuration data is provided, the default
348        compression from the Defaults class is returned
349
350        :return: True or False
351
352        :rtype: bool
353        """
354        container_compression = self._get_attribute(
355            element='container', attribute='compress'
356        )
357        if container_compression is None:
358            return Defaults.get_container_compression()
359        elif 'xz' == container_compression or container_compression is True:
360            return True
361        elif 'none' == container_compression or container_compression is False:
362            return False
363        else:
364            log.warning(
365                'Skipping invalid container compression: {0}'.format(
366                    container_compression
367                )
368            )
369            return Defaults.get_container_compression()
370
371    def get_iso_tool_category(self) -> str:
372        """
373        Return tool category which should be used to build iso images
374
375        iso:
376          - tool_category: xorriso
377
378        if no or invalid configuration exists the default tool category
379        from the Defaults class is returned
380
381        :return: A name
382
383        :rtype: str
384        """
385        iso_tool_category = self._get_attribute(
386            element='iso', attribute='tool_category'
387        )
388        if not iso_tool_category:
389            return Defaults.get_iso_tool_category()
390        elif 'xorriso' in iso_tool_category:
391            return iso_tool_category
392        else:
393            log.warning(
394                'Skipping invalid iso tool category: {0}'.format(
395                    iso_tool_category
396                )
397            )
398            return Defaults.get_iso_tool_category()
399
400    def get_iso_media_tag_tool(self) -> Literal['checkmedia', 'isomd5sum']:
401        """
402        Return media tag tool used to checksum iso images
403
404        iso:
405          - media_tag_tool: checkmedia
406
407        if no or invalid configuration exists the default media tagger
408        from the Defaults class is returned
409
410        :return: A name
411
412        :rtype: str
413        """
414        iso_media_tag_tool = self._get_attribute(
415            element='iso', attribute='media_tag_tool'
416        )
417        if not iso_media_tag_tool:
418            return Defaults.get_iso_media_tag_tool()
419        elif 'checkmedia' in iso_media_tag_tool:
420            return iso_media_tag_tool
421        elif 'isomd5sum' in iso_media_tag_tool:
422            return iso_media_tag_tool
423        else:
424            log.warning(
425                'Skipping invalid iso media tag tool: {0}'.format(
426                    iso_media_tag_tool
427                )
428            )
429            return Defaults.get_iso_media_tag_tool()
430
431    def get_oci_archive_tool(self) -> str:
432        """
433        Return OCI archive tool which should be used on creation of
434        container archives for OCI compliant images, e.g docker
435
436        oci:
437          - archive_tool: umoci
438
439        if no configuration exists the default tool from the
440        Defaults class is returned
441
442        :return: A name
443
444        :rtype: str
445        """
446        oci_archive_tool = self._get_attribute(
447            element='oci', attribute='archive_tool'
448        )
449        return oci_archive_tool or Defaults.get_oci_archive_tool()
450
451    def get_mapper_tool(self) -> str:
452        """
453        Return partition mapper tool
454
455        mapper:
456          - part_mapper: partx
457
458        if no configuration exists the default tool from the
459        Defaults class is returned
460
461        :return: A name
462
463        :rtype: str
464        """
465        part_mapper_tool = self._get_attribute(
466            element='mapper', attribute='part_mapper'
467        )
468        return part_mapper_tool or Defaults.get_part_mapper_tool()
469
470    def get_max_size_constraint(self) -> Optional[int]:
471        """
472        Returns the maximum allowed size of the built image. The value is
473        returned in bytes and it is specified in build_constraints element
474        with the max_size attribute. The value can be specified in bytes or
475        it can be specified with m=MB or g=GB.
476
477        build_constraints:
478          - max_size: 700m
479
480        if no configuration exists None is returned
481
482        :return: byte value or None
483
484        :rtype: int
485        """
486        max_size = self._get_attribute(
487            element='build_constraints', attribute='max_size'
488        )
489        return StringToSize.to_bytes(max_size) if max_size else None
490
491    def get_disabled_runtime_checks(self) -> List[str]:
492        """
493        Returns disabled runtime checks. Checks can be disabled with:
494
495        runtime_checks:
496            - disable: check_container_tool_chain_installed
497
498        if the provided string does not match any RuntimeChecker method it is
499        just ignored.
500        """
501        disabled_checks = self._get_attribute(
502            element='runtime_checks', attribute='disable'
503        ) or []
504        for check in disabled_checks:
505            log.warning(f'Runtime check: {check}: disabled')
506        return disabled_checks
507
508    def _get_attribute(self, element: str, attribute: str):
509        if RUNTIME_CONFIG:
510            try:
511                if element in RUNTIME_CONFIG:
512                    for attribute_dict in RUNTIME_CONFIG[element]:
513                        if attribute in attribute_dict:
514                            return attribute_dict[attribute]
515            except Exception as issue:
516                raise KiwiRuntimeConfigFormatError(
517                    f'{type(issue).__name__}: {issue}'
518                )
519
520    def _home_path(self) -> str:
521        return os.environ.get('HOME') or ''
522
523    def _read_drop_ins_dir(self, config_dir: str) -> List[str]:
524        config_files = []
525        if os.path.isdir(config_dir):
526            for config_file in sorted(os.listdir(config_dir)):
527                if config_file.endswith('.yml'):
528                    config_file_path = os.path.normpath(
529                        os.sep.join([config_dir, config_file])
530                    )
531                    config_files.append(config_file_path)
532        return config_files
log = <Logger kiwi (DEBUG)>
RUNTIME_CONFIG: Optional[Dict[str, Any]] = None
class ShasumT(typing.NamedTuple):
43class ShasumT(NamedTuple):
44    suffix: str
45    digest: Callable

ShasumT(suffix, digest)

ShasumT(suffix: str, digest: Callable)

Create new instance of ShasumT(suffix, digest)

suffix: str

Alias for field number 0

digest: Callable

Alias for field number 1

class RuntimeConfig:
 48class RuntimeConfig:
 49    """
 50    **Implements reading of runtime configuration files:**
 51
 52    1. vendor: /usr/share/kiwi/kiwi.yml + /usr/share/kiwi/kiwi.yml.d/*.yml
 53    2. admin: /etc/kiwi.yml + /etc/kiwi.yml.d/*.yml
 54    3. ~/.config/kiwi/config.yml
 55    4. Check for --config provided from the CLI
 56
 57    The KIWI runtime configuration file is a yaml formatted file
 58    containing information to control the behavior of the tools
 59    used by KIWI on the build host.
 60
 61    :param bool reread: reread runtime config
 62    """
 63    def __init__(self, reread: bool = False):
 64        global RUNTIME_CONFIG
 65
 66        if RUNTIME_CONFIG is None or reread:
 67            custom_config_file = defaults.CUSTOM_RUNTIME_CONFIG_FILE
 68            config_files = []
 69            # 1. vendor
 70            config_file = defaults.USR_RUNTIME_CONFIG_FILE
 71            if os.path.exists(config_file):
 72                config_files.append(config_file)
 73            config_files += self._read_drop_ins_dir(
 74                defaults.USR_RUNTIME_CONFIG_DIR
 75            )
 76            # 2. admin
 77            config_file = defaults.ETC_RUNTIME_CONFIG_FILE
 78            if os.path.exists(config_file):
 79                config_files.append(config_file)
 80            config_files += self._read_drop_ins_dir(
 81                defaults.ETC_RUNTIME_CONFIG_DIR
 82            )
 83            # 3. home
 84            if self._home_path():
 85                config_file = os.sep.join(
 86                    [self._home_path(), '.config', 'kiwi', 'config.yml']
 87                )
 88                if os.path.exists(config_file):
 89                    config_files.append(config_file)
 90            # 4. cmdline
 91            if custom_config_file:
 92                config_file = custom_config_file
 93                if not os.path.isfile(config_file):
 94                    raise KiwiRuntimeConfigFileError(
 95                        f'Custom config file {config_file!r} not found'
 96                    )
 97                config_files.append(config_file)
 98            # read all config files...
 99            RUNTIME_CONFIG = {}
100            for config_file in config_files:
101                log.info(
102                    f'Reading runtime config file: {config_file!r}'
103                )
104                with open(config_file, 'r') as config:
105                    RUNTIME_CONFIG.update(yaml.safe_load(config) or {})
106
107    def get_credentials_verification_metadata_signing_key_file(self) -> str:
108        """
109        Return verification metadata signing key file, used for
110        signature creation of rootfs verification metadata:
111
112        credentials:
113          - verification_metadata_signing_key_file: ...
114
115        There is no default value for this setting available
116
117        :return: file path name or ''
118
119        :rtype: str
120        """
121        signing_key_file = self._get_attribute(
122            element='credentials',
123            attribute='verification_metadata_signing_key_file'
124        )
125        return signing_key_file if signing_key_file else ''
126
127    def get_obs_download_server_url(self) -> str:
128        """
129        Return URL of buildservice download server in:
130
131        obs:
132          - download_url: ...
133
134        if no configuration exists the downloadserver from
135        the Defaults class is returned
136
137        :return: URL type data
138
139        :rtype: str
140        """
141        obs_download_server_url = self._get_attribute(
142            element='obs', attribute='download_url'
143        )
144        return obs_download_server_url if obs_download_server_url else \
145            Defaults.get_obs_download_server_url()
146
147    def get_obs_api_server_url(self) -> str:
148        """
149        Return URL of buildservice API server in:
150
151        obs:
152          - api_url: ...
153
154        if no configuration exists the API server from
155        the Defaults class is returned
156
157        :return: URL type data
158
159        :rtype: str
160        """
161        obs_api_server_url = self._get_attribute(
162            element='obs', attribute='api_url'
163        )
164        return obs_api_server_url if obs_api_server_url else \
165            Defaults.get_obs_api_server_url()
166
167    def get_obs_api_credentials(self) -> List[str]:
168        """
169        Return OBS API credentials if configured:
170
171        obs:
172          - user:
173              - user_name: user_credentials
174
175        :return: List of Dicts with credentials per user
176
177        :rtype: list
178        """
179        obs_users = self._get_attribute(element='obs', attribute='user') or []
180        return obs_users
181
182    def is_obs_public(self) -> bool:
183        """
184        Check if the buildservice configuration is public or private in:
185
186        obs:
187          - public: true|false
188
189        if no configuration exists we assume to be public
190
191        :return: True or False
192
193        :rtype: bool
194        """
195        obs_public = self._get_attribute(element='obs', attribute='public')
196        if obs_public is None:
197            # if the privacy attribute is not set we assume to be public
198            obs_public = True
199        return bool(obs_public)
200
201    def get_package_changes(self, default: bool = True) -> bool:
202        """
203        Return boolean value to express if the image build and bundle
204        should contain a .changes file. The .changes file contains
205        the package changelog information from all packages installed
206        into the image.
207
208        bundle:
209          - has_package_changes: true|false
210
211        By default the creation is switched on.
212        When building in the Open Build Service the default is
213        switched off because obs provides a .report file containing
214        the same information.
215
216        :param bool default: Default value
217
218        :return: True or False
219
220        :rtype: bool
221        """
222        bundle_package_changes = self._get_attribute(
223            element='bundle', attribute='has_package_changes'
224        )
225        if bundle_package_changes is None:
226            if Defaults.is_buildservice_worker():
227                bundle_package_changes = False
228            else:
229                bundle_package_changes = default
230        return bool(bundle_package_changes)
231
232    def get_bundle_compression(self, default: bool = True) -> bool:
233        """
234        Return boolean value to express if the image bundle should
235        contain XZ compressed image results or not.
236
237        bundle:
238          - compress: true|false
239
240        If compression of image build results is activated the size
241        of the bundle is smaller and the download speed increases.
242        However the image must be uncompressed before use
243
244        If no compression is explicitly configured, the provided
245        default value applies
246
247        :param bool default: Default value
248
249        :return: True or False
250
251        :rtype: bool
252        """
253        bundle_compress = self._get_attribute(
254            element='bundle', attribute='compress'
255        )
256        if bundle_compress is None:
257            bundle_compress = default
258        return bool(bundle_compress)
259
260    def get_checksum_handler(
261        self,
262        source_filename: str,
263        target_filename: Optional[str] = None,
264        default: str = '256',
265        bundle_lookup: bool = False
266    ) -> ShasumT:
267        """
268        Return a ShasumT with information about the configured
269        shasum suffix name and the digest(Checksum) callable. The
270        following configuration setting allows to configure the
271        size of the checksum:
272
273        shasum:
274          - size: 256
275
276        bundle:
277          - shasum_size: "256"
278
279        Instructs kiwi to use the provided shasum size. Supported
280        values are 256 (default) and 512. In case of an unsupported
281        value the default is used. A value from the bundle section
282        takes precedence over the global shasum size specified
283        in the shasum section for creating bundle results. If no
284        information for bundle results is specified, the global
285        shasum size or the default applies.
286
287        :param str source_filename: filename to calculate checksum for
288        :param str target_filename: filename to write checksum to
289        :param str default: default size set to 256
290
291        :rtype: ShasumT
292        """
293        supported_shasums = {
294            '256': ShasumT(
295                suffix='.sha256',
296                digest=functools.partial(
297                    Checksum(source_filename).sha256, target_filename
298                )
299            ),
300            '512': ShasumT(
301                suffix='.sha512',
302                digest=functools.partial(
303                    Checksum(source_filename).sha512, target_filename
304                )
305            )
306        }
307        shasum_size = self._get_attribute(
308            element='shasum', attribute='size'
309        )
310        if bundle_lookup:
311            bundle_shasum_size = self._get_attribute(
312                element='bundle', attribute='shasum_size'
313            )
314            if bundle_shasum_size:
315                shasum_size = bundle_shasum_size
316        if supported_shasums.get(shasum_size):
317            return supported_shasums[shasum_size]
318        return supported_shasums['256']
319
320    def get_xz_options(self) -> Optional[List[str]]:
321        """
322        Return list of XZ compression options in:
323
324        xz:
325          - options: ...
326
327        if no configuration exists None is returned
328
329        :return:
330            Contains list of options
331
332            .. code:: python
333
334                ['--option=value']
335
336        :rtype: list
337        """
338        xz_options = self._get_attribute(element='xz', attribute='options')
339        return xz_options.split() if xz_options else None
340
341    def get_container_compression(self) -> bool:
342        """
343        Return compression for container images
344
345        container:
346          - compress: xz|none|true|false
347
348        if no or invalid configuration data is provided, the default
349        compression from the Defaults class is returned
350
351        :return: True or False
352
353        :rtype: bool
354        """
355        container_compression = self._get_attribute(
356            element='container', attribute='compress'
357        )
358        if container_compression is None:
359            return Defaults.get_container_compression()
360        elif 'xz' == container_compression or container_compression is True:
361            return True
362        elif 'none' == container_compression or container_compression is False:
363            return False
364        else:
365            log.warning(
366                'Skipping invalid container compression: {0}'.format(
367                    container_compression
368                )
369            )
370            return Defaults.get_container_compression()
371
372    def get_iso_tool_category(self) -> str:
373        """
374        Return tool category which should be used to build iso images
375
376        iso:
377          - tool_category: xorriso
378
379        if no or invalid configuration exists the default tool category
380        from the Defaults class is returned
381
382        :return: A name
383
384        :rtype: str
385        """
386        iso_tool_category = self._get_attribute(
387            element='iso', attribute='tool_category'
388        )
389        if not iso_tool_category:
390            return Defaults.get_iso_tool_category()
391        elif 'xorriso' in iso_tool_category:
392            return iso_tool_category
393        else:
394            log.warning(
395                'Skipping invalid iso tool category: {0}'.format(
396                    iso_tool_category
397                )
398            )
399            return Defaults.get_iso_tool_category()
400
401    def get_iso_media_tag_tool(self) -> Literal['checkmedia', 'isomd5sum']:
402        """
403        Return media tag tool used to checksum iso images
404
405        iso:
406          - media_tag_tool: checkmedia
407
408        if no or invalid configuration exists the default media tagger
409        from the Defaults class is returned
410
411        :return: A name
412
413        :rtype: str
414        """
415        iso_media_tag_tool = self._get_attribute(
416            element='iso', attribute='media_tag_tool'
417        )
418        if not iso_media_tag_tool:
419            return Defaults.get_iso_media_tag_tool()
420        elif 'checkmedia' in iso_media_tag_tool:
421            return iso_media_tag_tool
422        elif 'isomd5sum' in iso_media_tag_tool:
423            return iso_media_tag_tool
424        else:
425            log.warning(
426                'Skipping invalid iso media tag tool: {0}'.format(
427                    iso_media_tag_tool
428                )
429            )
430            return Defaults.get_iso_media_tag_tool()
431
432    def get_oci_archive_tool(self) -> str:
433        """
434        Return OCI archive tool which should be used on creation of
435        container archives for OCI compliant images, e.g docker
436
437        oci:
438          - archive_tool: umoci
439
440        if no configuration exists the default tool from the
441        Defaults class is returned
442
443        :return: A name
444
445        :rtype: str
446        """
447        oci_archive_tool = self._get_attribute(
448            element='oci', attribute='archive_tool'
449        )
450        return oci_archive_tool or Defaults.get_oci_archive_tool()
451
452    def get_mapper_tool(self) -> str:
453        """
454        Return partition mapper tool
455
456        mapper:
457          - part_mapper: partx
458
459        if no configuration exists the default tool from the
460        Defaults class is returned
461
462        :return: A name
463
464        :rtype: str
465        """
466        part_mapper_tool = self._get_attribute(
467            element='mapper', attribute='part_mapper'
468        )
469        return part_mapper_tool or Defaults.get_part_mapper_tool()
470
471    def get_max_size_constraint(self) -> Optional[int]:
472        """
473        Returns the maximum allowed size of the built image. The value is
474        returned in bytes and it is specified in build_constraints element
475        with the max_size attribute. The value can be specified in bytes or
476        it can be specified with m=MB or g=GB.
477
478        build_constraints:
479          - max_size: 700m
480
481        if no configuration exists None is returned
482
483        :return: byte value or None
484
485        :rtype: int
486        """
487        max_size = self._get_attribute(
488            element='build_constraints', attribute='max_size'
489        )
490        return StringToSize.to_bytes(max_size) if max_size else None
491
492    def get_disabled_runtime_checks(self) -> List[str]:
493        """
494        Returns disabled runtime checks. Checks can be disabled with:
495
496        runtime_checks:
497            - disable: check_container_tool_chain_installed
498
499        if the provided string does not match any RuntimeChecker method it is
500        just ignored.
501        """
502        disabled_checks = self._get_attribute(
503            element='runtime_checks', attribute='disable'
504        ) or []
505        for check in disabled_checks:
506            log.warning(f'Runtime check: {check}: disabled')
507        return disabled_checks
508
509    def _get_attribute(self, element: str, attribute: str):
510        if RUNTIME_CONFIG:
511            try:
512                if element in RUNTIME_CONFIG:
513                    for attribute_dict in RUNTIME_CONFIG[element]:
514                        if attribute in attribute_dict:
515                            return attribute_dict[attribute]
516            except Exception as issue:
517                raise KiwiRuntimeConfigFormatError(
518                    f'{type(issue).__name__}: {issue}'
519                )
520
521    def _home_path(self) -> str:
522        return os.environ.get('HOME') or ''
523
524    def _read_drop_ins_dir(self, config_dir: str) -> List[str]:
525        config_files = []
526        if os.path.isdir(config_dir):
527            for config_file in sorted(os.listdir(config_dir)):
528                if config_file.endswith('.yml'):
529                    config_file_path = os.path.normpath(
530                        os.sep.join([config_dir, config_file])
531                    )
532                    config_files.append(config_file_path)
533        return config_files

Implements reading of runtime configuration files:

  1. vendor: /usr/share/kiwi/kiwi.yml + /usr/share/kiwi/kiwi.yml.d/*.yml
  2. admin: /etc/kiwi.yml + /etc/kiwi.yml.d/*.yml
  3. ~/.config/kiwi/config.yml
  4. Check for --config provided from the CLI

The KIWI runtime configuration file is a yaml formatted file containing information to control the behavior of the tools used by KIWI on the build host.

Parameters
  • bool reread: reread runtime config
RuntimeConfig(reread: bool = False)
 63    def __init__(self, reread: bool = False):
 64        global RUNTIME_CONFIG
 65
 66        if RUNTIME_CONFIG is None or reread:
 67            custom_config_file = defaults.CUSTOM_RUNTIME_CONFIG_FILE
 68            config_files = []
 69            # 1. vendor
 70            config_file = defaults.USR_RUNTIME_CONFIG_FILE
 71            if os.path.exists(config_file):
 72                config_files.append(config_file)
 73            config_files += self._read_drop_ins_dir(
 74                defaults.USR_RUNTIME_CONFIG_DIR
 75            )
 76            # 2. admin
 77            config_file = defaults.ETC_RUNTIME_CONFIG_FILE
 78            if os.path.exists(config_file):
 79                config_files.append(config_file)
 80            config_files += self._read_drop_ins_dir(
 81                defaults.ETC_RUNTIME_CONFIG_DIR
 82            )
 83            # 3. home
 84            if self._home_path():
 85                config_file = os.sep.join(
 86                    [self._home_path(), '.config', 'kiwi', 'config.yml']
 87                )
 88                if os.path.exists(config_file):
 89                    config_files.append(config_file)
 90            # 4. cmdline
 91            if custom_config_file:
 92                config_file = custom_config_file
 93                if not os.path.isfile(config_file):
 94                    raise KiwiRuntimeConfigFileError(
 95                        f'Custom config file {config_file!r} not found'
 96                    )
 97                config_files.append(config_file)
 98            # read all config files...
 99            RUNTIME_CONFIG = {}
100            for config_file in config_files:
101                log.info(
102                    f'Reading runtime config file: {config_file!r}'
103                )
104                with open(config_file, 'r') as config:
105                    RUNTIME_CONFIG.update(yaml.safe_load(config) or {})
def get_credentials_verification_metadata_signing_key_file(self) -> str:
107    def get_credentials_verification_metadata_signing_key_file(self) -> str:
108        """
109        Return verification metadata signing key file, used for
110        signature creation of rootfs verification metadata:
111
112        credentials:
113          - verification_metadata_signing_key_file: ...
114
115        There is no default value for this setting available
116
117        :return: file path name or ''
118
119        :rtype: str
120        """
121        signing_key_file = self._get_attribute(
122            element='credentials',
123            attribute='verification_metadata_signing_key_file'
124        )
125        return signing_key_file if signing_key_file else ''

Return verification metadata signing key file, used for signature creation of rootfs verification metadata:

credentials:

  • verification_metadata_signing_key_file: ...

There is no default value for this setting available

Returns

file path name or ''

def get_obs_download_server_url(self) -> str:
127    def get_obs_download_server_url(self) -> str:
128        """
129        Return URL of buildservice download server in:
130
131        obs:
132          - download_url: ...
133
134        if no configuration exists the downloadserver from
135        the Defaults class is returned
136
137        :return: URL type data
138
139        :rtype: str
140        """
141        obs_download_server_url = self._get_attribute(
142            element='obs', attribute='download_url'
143        )
144        return obs_download_server_url if obs_download_server_url else \
145            Defaults.get_obs_download_server_url()

Return URL of buildservice download server in:

obs:

  • download_url: ...

if no configuration exists the downloadserver from the Defaults class is returned

Returns

URL type data

def get_obs_api_server_url(self) -> str:
147    def get_obs_api_server_url(self) -> str:
148        """
149        Return URL of buildservice API server in:
150
151        obs:
152          - api_url: ...
153
154        if no configuration exists the API server from
155        the Defaults class is returned
156
157        :return: URL type data
158
159        :rtype: str
160        """
161        obs_api_server_url = self._get_attribute(
162            element='obs', attribute='api_url'
163        )
164        return obs_api_server_url if obs_api_server_url else \
165            Defaults.get_obs_api_server_url()

Return URL of buildservice API server in:

obs:

  • api_url: ...

if no configuration exists the API server from the Defaults class is returned

Returns

URL type data

def get_obs_api_credentials(self) -> List[str]:
167    def get_obs_api_credentials(self) -> List[str]:
168        """
169        Return OBS API credentials if configured:
170
171        obs:
172          - user:
173              - user_name: user_credentials
174
175        :return: List of Dicts with credentials per user
176
177        :rtype: list
178        """
179        obs_users = self._get_attribute(element='obs', attribute='user') or []
180        return obs_users

Return OBS API credentials if configured:

obs:

  • user:
    • user_name: user_credentials
Returns

List of Dicts with credentials per user

def is_obs_public(self) -> bool:
182    def is_obs_public(self) -> bool:
183        """
184        Check if the buildservice configuration is public or private in:
185
186        obs:
187          - public: true|false
188
189        if no configuration exists we assume to be public
190
191        :return: True or False
192
193        :rtype: bool
194        """
195        obs_public = self._get_attribute(element='obs', attribute='public')
196        if obs_public is None:
197            # if the privacy attribute is not set we assume to be public
198            obs_public = True
199        return bool(obs_public)

Check if the buildservice configuration is public or private in:

obs:

  • public: true|false

if no configuration exists we assume to be public

Returns

True or False

def get_package_changes(self, default: bool = True) -> bool:
201    def get_package_changes(self, default: bool = True) -> bool:
202        """
203        Return boolean value to express if the image build and bundle
204        should contain a .changes file. The .changes file contains
205        the package changelog information from all packages installed
206        into the image.
207
208        bundle:
209          - has_package_changes: true|false
210
211        By default the creation is switched on.
212        When building in the Open Build Service the default is
213        switched off because obs provides a .report file containing
214        the same information.
215
216        :param bool default: Default value
217
218        :return: True or False
219
220        :rtype: bool
221        """
222        bundle_package_changes = self._get_attribute(
223            element='bundle', attribute='has_package_changes'
224        )
225        if bundle_package_changes is None:
226            if Defaults.is_buildservice_worker():
227                bundle_package_changes = False
228            else:
229                bundle_package_changes = default
230        return bool(bundle_package_changes)

Return boolean value to express if the image build and bundle should contain a .changes file. The .changes file contains the package changelog information from all packages installed into the image.

bundle:

  • has_package_changes: true|false

By default the creation is switched on. When building in the Open Build Service the default is switched off because obs provides a .report file containing the same information.

Parameters
  • bool default: Default value
Returns

True or False

def get_bundle_compression(self, default: bool = True) -> bool:
232    def get_bundle_compression(self, default: bool = True) -> bool:
233        """
234        Return boolean value to express if the image bundle should
235        contain XZ compressed image results or not.
236
237        bundle:
238          - compress: true|false
239
240        If compression of image build results is activated the size
241        of the bundle is smaller and the download speed increases.
242        However the image must be uncompressed before use
243
244        If no compression is explicitly configured, the provided
245        default value applies
246
247        :param bool default: Default value
248
249        :return: True or False
250
251        :rtype: bool
252        """
253        bundle_compress = self._get_attribute(
254            element='bundle', attribute='compress'
255        )
256        if bundle_compress is None:
257            bundle_compress = default
258        return bool(bundle_compress)

Return boolean value to express if the image bundle should contain XZ compressed image results or not.

bundle:

  • compress: true|false

If compression of image build results is activated the size of the bundle is smaller and the download speed increases. However the image must be uncompressed before use

If no compression is explicitly configured, the provided default value applies

Parameters
  • bool default: Default value
Returns

True or False

def get_checksum_handler( self, source_filename: str, target_filename: Optional[str] = None, default: str = '256', bundle_lookup: bool = False) -> ShasumT:
260    def get_checksum_handler(
261        self,
262        source_filename: str,
263        target_filename: Optional[str] = None,
264        default: str = '256',
265        bundle_lookup: bool = False
266    ) -> ShasumT:
267        """
268        Return a ShasumT with information about the configured
269        shasum suffix name and the digest(Checksum) callable. The
270        following configuration setting allows to configure the
271        size of the checksum:
272
273        shasum:
274          - size: 256
275
276        bundle:
277          - shasum_size: "256"
278
279        Instructs kiwi to use the provided shasum size. Supported
280        values are 256 (default) and 512. In case of an unsupported
281        value the default is used. A value from the bundle section
282        takes precedence over the global shasum size specified
283        in the shasum section for creating bundle results. If no
284        information for bundle results is specified, the global
285        shasum size or the default applies.
286
287        :param str source_filename: filename to calculate checksum for
288        :param str target_filename: filename to write checksum to
289        :param str default: default size set to 256
290
291        :rtype: ShasumT
292        """
293        supported_shasums = {
294            '256': ShasumT(
295                suffix='.sha256',
296                digest=functools.partial(
297                    Checksum(source_filename).sha256, target_filename
298                )
299            ),
300            '512': ShasumT(
301                suffix='.sha512',
302                digest=functools.partial(
303                    Checksum(source_filename).sha512, target_filename
304                )
305            )
306        }
307        shasum_size = self._get_attribute(
308            element='shasum', attribute='size'
309        )
310        if bundle_lookup:
311            bundle_shasum_size = self._get_attribute(
312                element='bundle', attribute='shasum_size'
313            )
314            if bundle_shasum_size:
315                shasum_size = bundle_shasum_size
316        if supported_shasums.get(shasum_size):
317            return supported_shasums[shasum_size]
318        return supported_shasums['256']

Return a ShasumT with information about the configured shasum suffix name and the digest(Checksum) callable. The following configuration setting allows to configure the size of the checksum:

shasum:

  • size: 256

bundle:

  • shasum_size: "256"

Instructs kiwi to use the provided shasum size. Supported values are 256 (default) and 512. In case of an unsupported value the default is used. A value from the bundle section takes precedence over the global shasum size specified in the shasum section for creating bundle results. If no information for bundle results is specified, the global shasum size or the default applies.

Parameters
  • str source_filename: filename to calculate checksum for
  • str target_filename: filename to write checksum to
  • str default: default size set to 256
def get_xz_options(self) -> Optional[List[str]]:
320    def get_xz_options(self) -> Optional[List[str]]:
321        """
322        Return list of XZ compression options in:
323
324        xz:
325          - options: ...
326
327        if no configuration exists None is returned
328
329        :return:
330            Contains list of options
331
332            .. code:: python
333
334                ['--option=value']
335
336        :rtype: list
337        """
338        xz_options = self._get_attribute(element='xz', attribute='options')
339        return xz_options.split() if xz_options else None

Return list of XZ compression options in:

xz:

  • options: ...

if no configuration exists None is returned

Returns
Contains list of options

.. code:: python

    ['--option=value']
def get_container_compression(self) -> bool:
341    def get_container_compression(self) -> bool:
342        """
343        Return compression for container images
344
345        container:
346          - compress: xz|none|true|false
347
348        if no or invalid configuration data is provided, the default
349        compression from the Defaults class is returned
350
351        :return: True or False
352
353        :rtype: bool
354        """
355        container_compression = self._get_attribute(
356            element='container', attribute='compress'
357        )
358        if container_compression is None:
359            return Defaults.get_container_compression()
360        elif 'xz' == container_compression or container_compression is True:
361            return True
362        elif 'none' == container_compression or container_compression is False:
363            return False
364        else:
365            log.warning(
366                'Skipping invalid container compression: {0}'.format(
367                    container_compression
368                )
369            )
370            return Defaults.get_container_compression()

Return compression for container images

container:

  • compress: xz|none|true|false

if no or invalid configuration data is provided, the default compression from the Defaults class is returned

Returns

True or False

def get_iso_tool_category(self) -> str:
372    def get_iso_tool_category(self) -> str:
373        """
374        Return tool category which should be used to build iso images
375
376        iso:
377          - tool_category: xorriso
378
379        if no or invalid configuration exists the default tool category
380        from the Defaults class is returned
381
382        :return: A name
383
384        :rtype: str
385        """
386        iso_tool_category = self._get_attribute(
387            element='iso', attribute='tool_category'
388        )
389        if not iso_tool_category:
390            return Defaults.get_iso_tool_category()
391        elif 'xorriso' in iso_tool_category:
392            return iso_tool_category
393        else:
394            log.warning(
395                'Skipping invalid iso tool category: {0}'.format(
396                    iso_tool_category
397                )
398            )
399            return Defaults.get_iso_tool_category()

Return tool category which should be used to build iso images

iso:

  • tool_category: xorriso

if no or invalid configuration exists the default tool category from the Defaults class is returned

Returns

A name

def get_iso_media_tag_tool(self) -> Literal['checkmedia', 'isomd5sum']:
401    def get_iso_media_tag_tool(self) -> Literal['checkmedia', 'isomd5sum']:
402        """
403        Return media tag tool used to checksum iso images
404
405        iso:
406          - media_tag_tool: checkmedia
407
408        if no or invalid configuration exists the default media tagger
409        from the Defaults class is returned
410
411        :return: A name
412
413        :rtype: str
414        """
415        iso_media_tag_tool = self._get_attribute(
416            element='iso', attribute='media_tag_tool'
417        )
418        if not iso_media_tag_tool:
419            return Defaults.get_iso_media_tag_tool()
420        elif 'checkmedia' in iso_media_tag_tool:
421            return iso_media_tag_tool
422        elif 'isomd5sum' in iso_media_tag_tool:
423            return iso_media_tag_tool
424        else:
425            log.warning(
426                'Skipping invalid iso media tag tool: {0}'.format(
427                    iso_media_tag_tool
428                )
429            )
430            return Defaults.get_iso_media_tag_tool()

Return media tag tool used to checksum iso images

iso:

  • media_tag_tool: checkmedia

if no or invalid configuration exists the default media tagger from the Defaults class is returned

Returns

A name

def get_oci_archive_tool(self) -> str:
432    def get_oci_archive_tool(self) -> str:
433        """
434        Return OCI archive tool which should be used on creation of
435        container archives for OCI compliant images, e.g docker
436
437        oci:
438          - archive_tool: umoci
439
440        if no configuration exists the default tool from the
441        Defaults class is returned
442
443        :return: A name
444
445        :rtype: str
446        """
447        oci_archive_tool = self._get_attribute(
448            element='oci', attribute='archive_tool'
449        )
450        return oci_archive_tool or Defaults.get_oci_archive_tool()

Return OCI archive tool which should be used on creation of container archives for OCI compliant images, e.g docker

oci:

  • archive_tool: umoci

if no configuration exists the default tool from the Defaults class is returned

Returns

A name

def get_mapper_tool(self) -> str:
452    def get_mapper_tool(self) -> str:
453        """
454        Return partition mapper tool
455
456        mapper:
457          - part_mapper: partx
458
459        if no configuration exists the default tool from the
460        Defaults class is returned
461
462        :return: A name
463
464        :rtype: str
465        """
466        part_mapper_tool = self._get_attribute(
467            element='mapper', attribute='part_mapper'
468        )
469        return part_mapper_tool or Defaults.get_part_mapper_tool()

Return partition mapper tool

mapper:

  • part_mapper: partx

if no configuration exists the default tool from the Defaults class is returned

Returns

A name

def get_max_size_constraint(self) -> Optional[int]:
471    def get_max_size_constraint(self) -> Optional[int]:
472        """
473        Returns the maximum allowed size of the built image. The value is
474        returned in bytes and it is specified in build_constraints element
475        with the max_size attribute. The value can be specified in bytes or
476        it can be specified with m=MB or g=GB.
477
478        build_constraints:
479          - max_size: 700m
480
481        if no configuration exists None is returned
482
483        :return: byte value or None
484
485        :rtype: int
486        """
487        max_size = self._get_attribute(
488            element='build_constraints', attribute='max_size'
489        )
490        return StringToSize.to_bytes(max_size) if max_size else None

Returns the maximum allowed size of the built image. The value is returned in bytes and it is specified in build_constraints element with the max_size attribute. The value can be specified in bytes or it can be specified with m=MB or g=GB.

build_constraints:

  • max_size: 700m

if no configuration exists None is returned

Returns

byte value or None

def get_disabled_runtime_checks(self) -> List[str]:
492    def get_disabled_runtime_checks(self) -> List[str]:
493        """
494        Returns disabled runtime checks. Checks can be disabled with:
495
496        runtime_checks:
497            - disable: check_container_tool_chain_installed
498
499        if the provided string does not match any RuntimeChecker method it is
500        just ignored.
501        """
502        disabled_checks = self._get_attribute(
503            element='runtime_checks', attribute='disable'
504        ) or []
505        for check in disabled_checks:
506            log.warning(f'Runtime check: {check}: disabled')
507        return disabled_checks

Returns disabled runtime checks. Checks can be disabled with:

runtime_checks: - disable: check_container_tool_chain_installed

if the provided string does not match any RuntimeChecker method it is just ignored.