class TestPackageManagerTemplateAptGet: def setup(self): self.apt = PackageManagerTemplateAptGet() def test_get_host_template(self): assert self.apt.get_host_template().substitute( apt_shared_base='/var/cache/kiwi/apt-get', unauthenticated='true' ) def test_get_image_template(self): assert self.apt.get_image_template().substitute( unauthenticated='true' ) def test_get_host_template_with_exclude_docs(self): assert self.apt.get_host_template(exclude_docs=True).substitute( apt_shared_base='/var/cache/kiwi/apt-get', unauthenticated='true' ) def test_get_image_template_with_exclude_docs(self): assert self.apt.get_image_template(exclude_docs=True).substitute( unauthenticated='true' )
def post_init(self, custom_args=None): """ Post initialization method Store custom apt-get arguments and create runtime configuration and environment :param list custom_args: apt-get arguments """ self.custom_args = custom_args self.exclude_docs = False self.signing_keys = [] if not custom_args: self.custom_args = [] # extract custom arguments used for apt config only if 'exclude_docs' in self.custom_args: self.custom_args.remove('exclude_docs') self.exclude_docs = True if 'check_signatures' in self.custom_args: self.custom_args.remove('check_signatures') self.unauthenticated = 'false' else: self.unauthenticated = 'true' self.distribution = None self.distribution_path = None self.debootstrap_repo_set = False self.repo_names = [] self.components = [] # apt-get support is based on creating a sources file which # contains path names to the repo and its cache. In order to # allow a persistent use of the files in and outside of a chroot # call an active bind mount from RootBind::mount_shared_directory # is expected and required self.manager_base = self.shared_location + '/apt-get' self.shared_apt_get_dir = { 'sources-dir': self.manager_base + '/sources.list.d', 'preferences-dir': self.manager_base + '/preferences.d' } self.keyring = '{}/trusted.gpg'.format(self.manager_base) self.runtime_apt_get_config_file = NamedTemporaryFile( dir=self.root_dir) self.apt_get_args = [ '-q', '-c', self.runtime_apt_get_config_file.name, '-y' ] + self.custom_args self.command_env = self._create_apt_get_runtime_environment() # config file for apt-get tool self.apt_conf = PackageManagerTemplateAptGet() self._write_runtime_config()
class TestPackageManagerTemplateAptGet(object): def setup(self): self.apt = PackageManagerTemplateAptGet() def test_get_host_template(self): assert self.apt.get_host_template().substitute( apt_shared_base='/var/cache/kiwi/apt-get') def test_get_image_template(self): assert self.apt.get_image_template().substitute()
class TestPackageManagerTemplateAptGet(object): def setup(self): self.apt = PackageManagerTemplateAptGet() def test_get_host_template(self): assert self.apt.get_host_template().substitute( apt_shared_base='/var/cache/kiwi/apt-get' ) def test_get_image_template(self): assert self.apt.get_image_template().substitute()
def post_init(self, custom_args=None): """ Post initialization method Store custom apt-get arguments and create runtime configuration and environment Attributes * :attr:`shared_apt_get_dir` shared directory between image root and build system root * :attr:`runtime_apt_get_config_file` apt-get runtime config file name * :attr:`apt_get_args` apt-get caller arguments * :attr:`command_env` customized os.environ for apt-get * :attr:`manager_base` shared location for apt-get repodata between host and image :param list custom_args: apt-get arguments """ self.custom_args = custom_args self.exclude_docs = False self.signing_keys = [] if not custom_args: self.custom_args = [] # extract custom arguments used for apt config only if 'exclude_docs' in self.custom_args: self.custom_args.remove('exclude_docs') self.exclude_docs = True if 'check_signatures' in self.custom_args: self.custom_args.remove('check_signatures') self.unauthenticated = 'false' else: self.unauthenticated = 'true' self.distribution = None self.distribution_path = None self.repo_names = [] # apt-get support is based on creating a sources file which # contains path names to the repo and its cache. In order to # allow a persistent use of the files in and outside of a chroot # call an active bind mount from RootBind::mount_shared_directory # is expected and required self.manager_base = self.shared_location + '/apt-get' self.shared_apt_get_dir = { 'sources-dir': self.manager_base + '/sources.list.d', 'preferences-dir': self.manager_base + '/preferences.d' } self.runtime_apt_get_config_file = NamedTemporaryFile( dir=self.root_dir ) self.apt_get_args = [ '-q', '-c', self.runtime_apt_get_config_file.name, '-y' ] + self.custom_args self.command_env = self._create_apt_get_runtime_environment() # config file for apt-get tool self.apt_conf = PackageManagerTemplateAptGet() self._write_runtime_config()
class RepositoryApt(RepositoryBase): """ Implements repository handling for apt-get package manager """ def post_init(self, custom_args=None): """ Post initialization method Store custom apt-get arguments and create runtime configuration and environment Attributes * :attr:`shared_apt_get_dir` shared directory between image root and build system root * :attr:`runtime_apt_get_config_file` apt-get runtime config file name * :attr:`apt_get_args` apt-get caller arguments * :attr:`command_env` customized os.environ for apt-get * :attr:`manager_base` shared location for apt-get repodata between host and image :param list custom_args: apt-get arguments """ self.custom_args = custom_args self.exclude_docs = False self.signing_keys = [] if not custom_args: self.custom_args = [] # extract custom arguments used for apt config only if 'exclude_docs' in self.custom_args: self.custom_args.remove('exclude_docs') self.exclude_docs = True if 'check_signatures' in self.custom_args: self.custom_args.remove('check_signatures') self.unauthenticated = 'false' else: self.unauthenticated = 'true' self.distribution = None self.distribution_path = None self.repo_names = [] # apt-get support is based on creating a sources file which # contains path names to the repo and its cache. In order to # allow a persistent use of the files in and outside of a chroot # call an active bind mount from RootBind::mount_shared_directory # is expected and required self.manager_base = self.shared_location + '/apt-get' self.shared_apt_get_dir = { 'sources-dir': self.manager_base + '/sources.list.d', 'preferences-dir': self.manager_base + '/preferences.d' } self.runtime_apt_get_config_file = NamedTemporaryFile( dir=self.root_dir ) self.apt_get_args = [ '-q', '-c', self.runtime_apt_get_config_file.name, '-y' ] + self.custom_args self.command_env = self._create_apt_get_runtime_environment() # config file for apt-get tool self.apt_conf = PackageManagerTemplateAptGet() self._write_runtime_config() def use_default_location(self): """ Setup apt-get repository operations to store all data in the default places """ self.manager_base = os.sep.join([self.root_dir, 'etc/apt']) self.shared_apt_get_dir['sources-dir'] = \ os.sep.join([self.manager_base, 'sources.list.d']) self.shared_apt_get_dir['preferences-dir'] = \ os.sep.join([self.root_dir, 'preferences.d']) self._write_runtime_config(system_default=True) def runtime_config(self): """ apt-get runtime configuration and environment """ return { 'apt_get_args': self.apt_get_args, 'command_env': self.command_env, 'distribution': self.distribution, 'distribution_path': self.distribution_path } def add_repo( self, name, uri, repo_type='deb', prio=None, dist=None, components=None, user=None, secret=None, credentials_file=None, repo_gpgcheck=None, pkg_gpgcheck=None ): """ Add apt_get repository :param string name: repository base file name :param string uri: repository URI :param repo_type: unused :param int prio: unused :param dist: distribution name for non flat deb repos :param components: distribution categories :param user: unused :param secret: unused :param credentials_file: unused :param bool repo_gpgcheck: enable repository signature validation :param pkg_gpgcheck: unused """ list_file = '/'.join( [self.shared_apt_get_dir['sources-dir'], name + '.list'] ) pref_file = '/'.join( [self.shared_apt_get_dir['preferences-dir'], name + '.pref'] ) self.repo_names.append(name + '.list') if os.path.exists(uri): # apt-get requires local paths to take the file: type uri = 'file:/' + uri if not components: components = 'main' with open(list_file, 'w') as repo: if repo_gpgcheck is None: repo_line = 'deb {0}'.format(uri) else: repo_line = 'deb [trusted={0}] {1}'.format( 'no' if repo_gpgcheck else 'yes', uri ) if not dist: # create a debian flat repository setup. We consider the # repository metadata to exist on the toplevel of the # specified uri. This applies to the way the open build # service creates debian repositories and should be # done in the same way for other repositories when used # with kiwi repo_line += ' ./\n' else: # create a debian distributon repository setup for the # specified distributon name and components self.distribution = dist self.distribution_path = uri repo_line += ' {0} {1}\n'.format(dist, components) repo.write(repo_line) if prio: uri_parsed = urlparse(uri.replace('file://', 'file:/')) with open(pref_file, 'w') as pref: pref.write('Package: *{0}'.format(os.linesep)) if not uri_parsed.hostname: pref.write( 'Pin: origin ""{0}'.format(os.linesep) ) else: pref.write( 'Pin: origin "{0}"{1}'.format( uri_parsed.hostname, os.linesep ) ) pref.write( 'Pin-Priority: {0}{1}'.format(prio, os.linesep) ) def import_trusted_keys(self, signing_keys): """ Keeps trusted keys so that later on they can be imported into the image by the PackageManager instance. :param list signing_keys: list of the key files to import """ for key in signing_keys: self.signing_keys.append(key) def delete_repo(self, name): """ Delete apt-get repository :param string name: repository base file name """ Path.wipe( self.shared_apt_get_dir['sources-dir'] + '/' + name + '.list' ) def delete_all_repos(self): """ Delete all apt-get repositories """ Path.wipe(self.shared_apt_get_dir['sources-dir']) Path.create(self.shared_apt_get_dir['sources-dir']) def delete_repo_cache(self, name): """ Delete apt-get repository cache Apt stores the package cache in a collection of binary files and deb archives. As of now I couldn't came across a solution which allows for deleting only the cache data for a specific repository. Thus the repo cache cleanup affects all cache data :param string name: unused """ for cache_file in ['archives', 'pkgcache.bin', 'srcpkgcache.bin']: Path.wipe(os.sep.join([self.manager_base, cache_file])) def cleanup_unused_repos(self): """ Delete unused apt_get repositories Repository configurations which are not used for this build must be removed otherwise they are taken into account for the package installations """ repos_dir = self.shared_apt_get_dir['sources-dir'] repo_files = list(os.walk(repos_dir))[0][2] for repo_file in repo_files: if repo_file not in self.repo_names: Path.wipe(repos_dir + '/' + repo_file) def _create_apt_get_runtime_environment(self): for apt_get_dir in list(self.shared_apt_get_dir.values()): Path.create(apt_get_dir) return dict( os.environ, LANG='C', DEBIAN_FRONTEND='noninteractive' ) def _write_runtime_config(self, system_default=False): parameters = { 'apt_shared_base': self.manager_base, 'unauthenticated': self.unauthenticated } if not system_default: template = self.apt_conf.get_host_template(self.exclude_docs) apt_conf_data = template.substitute(parameters) else: template = self.apt_conf.get_image_template(self.exclude_docs) apt_conf_data = template.substitute(parameters) with open(self.runtime_apt_get_config_file.name, 'w') as config: config.write(apt_conf_data)
class RepositoryApt(RepositoryBase): """ **Implements repository handling for apt-get package manager** :param str shared_apt_get_dir: shared directory between image root and build system root :param str runtime_apt_get_config_file: apt-get runtime config file name :param list apt_get_args: apt-get caller arguments :param dict command_env: customized os.environ for apt-get :param manager_base: shared location for apt-get repodata between host and image """ def post_init(self, custom_args: List = []) -> None: """ Post initialization method Store custom apt-get arguments and create runtime configuration and environment :param list custom_args: apt-get arguments """ self.custom_args = custom_args self.exclude_docs = False self.signing_keys: List = [] # extract custom arguments used for apt config only if 'exclude_docs' in self.custom_args: self.custom_args.remove('exclude_docs') self.exclude_docs = True if 'check_signatures' in self.custom_args: self.custom_args.remove('check_signatures') self.unauthenticated = 'false' else: self.unauthenticated = 'true' self.distribution: str = '' self.distribution_path: str = '' self.debootstrap_repo_set = False self.repo_names: List = [] self.components: List = [] # apt-get support is based on creating a sources file which # contains path names to the repo and its cache. In order to # allow a persistent use of the files in and outside of a chroot # call an active bind mount from RootBind::mount_shared_directory # is expected and required self.manager_base = self.shared_location + '/apt-get' self.shared_apt_get_dir = { 'sources-dir': self.manager_base + '/sources.list.d', 'preferences-dir': self.manager_base + '/preferences.d' } self.keyring = '{}/trusted.gpg'.format(self.manager_base) self.runtime_apt_get_config_file = Temporary( path=self.root_dir).new_file() self.apt_get_args = [ '-q', '-c', self.runtime_apt_get_config_file.name, '-y' ] + self.custom_args self.command_env = self._create_apt_get_runtime_environment() # config file for apt-get tool self.apt_conf = PackageManagerTemplateAptGet() self._write_runtime_config() def setup_package_database_configuration(self) -> None: """ Setup package database configuration No special database configuration required for apt """ pass def use_default_location(self) -> None: """ Setup apt-get repository operations to store all data in the default places """ self.manager_base = os.sep.join([self.root_dir, 'etc/apt']) self.shared_apt_get_dir['sources-dir'] = \ os.sep.join([self.manager_base, 'sources.list.d']) self.shared_apt_get_dir['preferences-dir'] = \ os.sep.join([self.manager_base, 'preferences.d']) self._write_runtime_config(system_default=True) def runtime_config(self) -> Dict: """ apt-get runtime configuration and environment """ return { 'apt_get_args': self.apt_get_args, 'command_env': self.command_env, 'distribution': self.distribution, 'distribution_path': self.distribution_path } def add_repo(self, name: str, uri: str, repo_type: str = 'deb', prio: int = None, dist: str = None, components: str = None, user: str = None, secret: str = None, credentials_file: str = None, repo_gpgcheck: bool = None, pkg_gpgcheck: bool = None, sourcetype: str = None, use_for_bootstrap: bool = False, customization_script: str = None) -> None: """ Add apt_get repository :param str name: repository base file name :param str uri: repository URI :param str repo_type: unused :param int prio: unused :param str dist: distribution name for non flat deb repos :param str components: distribution categories :param str user: unused :param str secret: unused :param str credentials_file: unused :param bool repo_gpgcheck: enable repository signature validation :param bool pkg_gpgcheck: unused :param str sourcetype: unused :param bool use_for_bootstrap: use this repository for the debootstrap call :param str customization_script: custom script called after the repo file was created """ sources_file = '/'.join( [self.shared_apt_get_dir['sources-dir'], name + '.sources']) pref_file = '/'.join( [self.shared_apt_get_dir['preferences-dir'], name + '.pref']) self.repo_names.append(name + '.sources') if os.path.exists(uri): # apt-get requires local paths to take the file: type uri = 'file:/' + uri uri = uri.replace('file://', 'file:/') if not components: components = 'main' self._add_components(components) with open(sources_file, 'w') as repo: repo_details = 'Types: deb' + os.linesep repo_details += 'URIs: ' + uri + os.linesep if not dist: # create a debian flat repository setup. We consider the # repository metadata to exist on the toplevel of the # specified uri. This applies to the way the open build # service creates debian repositories and should be # done in the same way for other repositories when used # with kiwi repo_details += 'Suites: ./' + os.linesep else: # create a debian distributon repository setup for the # specified distributon name and components if not self.debootstrap_repo_set: self.distribution = dist self.distribution_path = uri self.debootstrap_repo_set = use_for_bootstrap repo_details += 'Suites: ' + dist + os.linesep repo_details += 'Components: ' + components + os.linesep if repo_gpgcheck is False: repo_details += 'trusted: yes' + os.linesep repo_details += 'check-valid-until: no' + os.linesep repo.write(repo_details) if customization_script: self.run_repo_customize(customization_script, sources_file) if prio: uri_parsed = urlparse(uri.replace('file://', 'file:/')) with open(pref_file, 'w') as pref: pref.write('Package: *{0}'.format(os.linesep)) if not uri_parsed.hostname: pref.write('Pin: origin ""{0}'.format(os.linesep)) else: pref.write('Pin: origin "{0}"{1}'.format( uri_parsed.hostname, os.linesep)) pref.write('Pin-Priority: {0}{1}'.format(prio, os.linesep)) if customization_script: self.run_repo_customize(customization_script, pref_file) def import_trusted_keys(self, signing_keys: List) -> None: """ Creates a new keyring including provided keys :param list signing_keys: list of the key files to import """ keybox = '{}/trusted-keybox.gpg'.format(self.manager_base) gpg_args = [ 'gpg', '--no-options', '--no-default-keyring', '--no-auto-check-trustdb', '--trust-model', 'always', '--keyring', keybox ] if os.path.exists(self.keyring): os.unlink(self.keyring) if os.path.exists(keybox): os.unlink(keybox) for key in signing_keys: Command.run(gpg_args + ['--import', '--ignore-time-conflict', key]) if os.path.exists(keybox): Command.run(gpg_args + ['--export', '--yes', '--output', self.keyring]) os.unlink(keybox) log.info('Custom keyring for APT created: {}'.format(self.keyring)) log.warning('The keyring is only available at build time. ' 'It will not be part of the resulting image') def delete_repo(self, name: str) -> None: """ Delete apt-get repository :param str name: repository base file name """ Path.wipe(self.shared_apt_get_dir['sources-dir'] + '/' + name + '.sources') def delete_all_repos(self) -> None: """ Delete all apt-get repositories """ Path.wipe(self.shared_apt_get_dir['sources-dir']) Path.create(self.shared_apt_get_dir['sources-dir']) def delete_repo_cache(self, name: str) -> None: """ Delete apt-get repository cache Apt stores the package cache in a collection of binary files and deb archives. As of now I couldn't came across a solution which allows for deleting only the cache data for a specific repository. Thus the repo cache cleanup affects all cache data :param str name: unused """ for cache_file in ['archives', 'pkgcache.bin', 'srcpkgcache.bin']: Path.wipe(os.sep.join([self.manager_base, cache_file])) def cleanup_unused_repos(self) -> None: """ Delete unused apt_get repositories Repository configurations which are not used for this build must be removed otherwise they are taken into account for the package installations """ repos_dir = self.shared_apt_get_dir['sources-dir'] repo_files = list(os.walk(repos_dir))[0][2] for repo_file in repo_files: if repo_file not in self.repo_names: Path.wipe(repos_dir + '/' + repo_file) def _add_components(self, components: str) -> None: for component in components.split(): if component not in self.components: self.components.append(component) def _create_apt_get_runtime_environment(self) -> Dict: for apt_get_dir in list(self.shared_apt_get_dir.values()): Path.create(apt_get_dir) return dict(os.environ, LANG='C', DEBIAN_FRONTEND='noninteractive') def _write_runtime_config(self, system_default: bool = False) -> None: parameters = { 'apt_shared_base': self.manager_base, 'unauthenticated': self.unauthenticated } if not system_default: template = self.apt_conf.get_host_template(self.exclude_docs) apt_conf_data = template.substitute(parameters) else: template = self.apt_conf.get_image_template(self.exclude_docs) apt_conf_data = template.substitute(parameters) with open(self.runtime_apt_get_config_file.name, 'w') as config: config.write(apt_conf_data)
def setup(self): self.apt = PackageManagerTemplateAptGet()