def check_install_prerequisites(self, resolved, os_tuple, fix_unsatisfied=False, interactive=True): info_v("Checking install prerequisites of installer '{}'... " "No checks implemented.".format(self.name))
def check_package_manager_updated(self, os_tuple, fix_unsatisfied=False, interactive=True): info_v("Checking if package manager '{}' is updated.". format(self.name)) try: updated = self.is_package_manager_updated() except NotImplementedError: info_v("Check if package manager '{}' is updated not implemented; " "skipping check.".format(self.name)) return if updated: info_v("Package manager '{}' is updated.".format(self.name)) return info_v("Package manager '{}' not updated.".format(self.name)) if fix_unsatisfied: info_v("Trying to update '{}'.".format(self.name)) try: self.update_package_manager(interactive) if self.is_package_manager_updated(): return except NotImplementedError: error("Installer plugin '{}' does not support updating " "itself.".format(self.name)) else: error("Installer plugin '{}' failed to update itself.". format(self.name)) warning("Package manager '{}' seems to be out-of-date. Please " "consider updating for best results.")
def check_package_manager_updated(self, os_tuple, fix_unsatisfied=False, interactive=True): info_v("Checking if package manager '{}' is updated.".format( self.name)) try: updated = self.is_package_manager_updated() except NotImplementedError: info_v("Check if package manager '{}' is updated not implemented; " "skipping check.".format(self.name)) return if updated: info_v("Package manager '{}' is updated.".format(self.name)) return info_v("Package manager '{}' not updated.".format(self.name)) if fix_unsatisfied: info_v("Trying to update '{}'.".format(self.name)) try: self.update_package_manager(interactive) if self.is_package_manager_updated(): return except NotImplementedError: error("Installer plugin '{}' does not support updating " "itself.".format(self.name)) else: error("Installer plugin '{}' failed to update itself.".format( self.name)) warning("Package manager '{}' seems to be out-of-date. Please " "consider updating for best results.")
def load_plugins(kind, base_class, group, disabled=[]): """Load plugins form entry points. Load the plugins of given ``kind`` from entry points ``group``, instantiating objects and ignoring duplicates. The entry points must be valid plugin definitions (see :func:`verify_plugin_definition`). The list of plugins is free of duplicates by plugin class name (not plugin name), whereas the list of ``disabled`` plugins refer to the plugin names instead. :param str kind: kind of plugin (e.g. "installer") :param base_class: (abstract) base class plugins (must implement PluginBase) :param group: entry point group to load plugins from :param disabled: list of plugins to ignore; these are plugin names, not plugin object names :type disabled: `list` of `str` :return: list of the loaded and instantiated plugin classes :rtype: `list` """ plugin_list = [] name_set = set() for entry_point in pkg_resources.iter_entry_points(group=group): definition = entry_point.load() try: verify_plugin_definition(definition, kind, base_class) except InvalidPluginError as e: # TODO: somehow decide when to reraise and when to display error error("Skipping {} plugin. Failed to load from '{}' entry point " "with name '{}':\n{}".format(kind, group, entry_point.name, e)) continue plugin_name = definition["plugin_name"] if plugin_name in disabled: info_v("Skipping disabled {} plugin '{}'.".format( kind, plugin_name)) continue plugin_class = definition[kind] plugin_obj = plugin_class() obj_name = plugin_obj.name try: plugin_class.verify_plugin(plugin_obj) except InvalidPluginError as e: # TODO: somehow decide when to reraise and when to display error error("Skipping {} plugin '{}'. Plugin is invalid:\n{}".format( kind, plugin_name, exc_to_str(e))) continue if obj_name in name_set: # TODO: somehow decide when to reraise and when to display error warning( "Ignoring {0} plugin '{1}' with duplicate name '{1}'".format( kind, definition['plugin_name'], obj_name)) continue info_v("Loaded {0} plugin '{1}' with {0} name '{2}'.".format( kind, plugin_name, obj_name)) name_set.add(obj_name) plugin_list.append(plugin_obj) return plugin_list
def load_plugins(kind, base_class, group, disabled=[]): """Load plugins form entry points. Load the plugins of given ``kind`` from entry points ``group``, instantiating objects and ignoring duplicates. The entry points must be valid plugin definitions (see :func:`verify_plugin_definition`). The list of plugins is free of duplicates by plugin class name (not plugin name), whereas the list of ``disabled`` plugins refer to the plugin names instead. :param str kind: kind of plugin (e.g. "installer") :param base_class: (abstract) base class plugins (must implement PluginBase) :param group: entry point group to load plugins from :param disabled: list of plugins to ignore; these are plugin names, not plugin object names :type disabled: `list` of `str` :return: list of the loaded and instantiated plugin classes :rtype: `list` """ plugin_list = [] name_set = set() for entry_point in pkg_resources.iter_entry_points(group=group): definition = entry_point.load() try: verify_plugin_definition(definition, kind, base_class) except InvalidPluginError as e: # TODO: somehow decide when to reraise and when to display error error("Skipping {} plugin. Failed to load from '{}' entry point " "with name '{}':\n{}".format( kind, group, entry_point.name, e)) continue plugin_name = definition["plugin_name"] if plugin_name in disabled: info_v("Skipping disabled {} plugin '{}'.". format(kind, plugin_name)) continue plugin_class = definition[kind] plugin_obj = plugin_class() obj_name = plugin_obj.name try: plugin_class.verify_plugin(plugin_obj) except InvalidPluginError as e: # TODO: somehow decide when to reraise and when to display error error("Skipping {} plugin '{}'. Plugin is invalid:\n{}".format( kind, plugin_name, exc_to_str(e))) continue if obj_name in name_set: # TODO: somehow decide when to reraise and when to display error warning("Ignoring {0} plugin '{1}' with duplicate name '{1}'". format(kind, definition['plugin_name'], obj_name)) continue info_v("Loaded {0} plugin '{1}' with {0} name '{2}'.". format(kind, plugin_name, obj_name)) name_set.add(obj_name) plugin_list.append(plugin_obj) return plugin_list
def check_general_prerequisites(self, os_tuple, fix_unsatisfied=False, interactive=True): info_v("Checking general prerequisites of installer '{}'...". format(self.name)) self.check_package_manager_installed( os_tuple, fix_unsatisfied, interactive) self.check_package_manager_updated( os_tuple, fix_unsatisfied, interactive)
def check_general_prerequisites(self, os_tuple, fix_unsatisfied=False, interactive=True): info_v("Checking general prerequisites of installer '{}'...".format( self.name)) self.check_package_manager_installed(os_tuple, fix_unsatisfied, interactive) self.check_package_manager_updated(os_tuple, fix_unsatisfied, interactive)
def check_package_manager_installed(self, os_tuple, fix_unsatisfied=False, interactive=True): info_v("Checking if package manager '{}' is installed.". format(self.name)) if self.is_package_manager_installed(): info_v("Package manager '{}' is installed.".format(self.name)) return info_v("Package manager '{}' not installed ('{}' not found).". format(self.name, self.executable_name)) if fix_unsatisfied: info_v("Trying to install '{}'.".format(self.name)) try: self.install_package_manager(os_tuple, interactive) if self.is_package_manager_installed(): return except NotImplementedError: error("Installer plugin '{}' does not support installing " "itself.".format(self.name)) else: error("Installer plugin '{}' failed to install itself.". format(self.name)) raise InstallerPrerequisiteError("Package manager '{}' not installed". format(self.name))
def check_package_manager_installed(self, os_tuple, fix_unsatisfied=False, interactive=True): info_v("Checking if package manager '{}' is installed.".format( self.name)) if self.is_package_manager_installed(): info_v("Package manager '{}' is installed.".format(self.name)) return info_v("Package manager '{}' not installed ('{}' not found).".format( self.name, self.executable_name)) if fix_unsatisfied: info_v("Trying to install '{}'.".format(self.name)) try: self.install_package_manager(os_tuple, interactive) if self.is_package_manager_installed(): return except NotImplementedError: error("Installer plugin '{}' does not support installing " "itself.".format(self.name)) else: error("Installer plugin '{}' failed to install itself.".format( self.name)) raise InstallerPrerequisiteError( "Package manager '{}' not installed".format(self.name))
def setup_installers(self): """For current os, setup configured installers. Installers are set based on the current os, user config and installer plugins. """ os = self.get_os() os_tuple = os.get_tuple() _, os_version = os_tuple self.core_installers = [] self.additional_installers = [] # 1. Go through all installers and set options from config for inst in self.installer_plugins: inst.options = self.config.installer_options.get(inst.name, {}) # 2. setup core installers from config or OS plugin if self.config.core_installers is not None: core_installer_names = self.config.core_installers info_v("setting up core installers from config: '{}'". format(", ".join(core_installer_names))) else: core_installer_names = os.get_core_installers(os_version, os.options) info_v("setting up core installers from os plugin: '{}'". format(", ".join(core_installer_names))) for name in core_installer_names: inst = self.lookup_installer(name) if inst is None: error("ignoring core installer '{}'; according plugin was not " "found".format(name)) else: self.core_installers.append(inst) # 3. Go through all installers and check if they should be used # as additional installers for the current OS. if self.config.use_additional_installers: for inst in self.installer_plugins: if inst not in self.core_installers and \ inst.use_as_additional_installer(os_tuple): self.additional_installers.append(inst) info_v("Using additional installers: '{}'".format( ", ".join([i.name for i in self.additional_installers])))
def setup_installers(self): """For current os, setup configured installers. Installers are set based on the current os, user config and installer plugins. """ os = self.get_os() os_tuple = os.get_tuple() _, os_version = os_tuple self.core_installers = [] self.additional_installers = [] # 1. Go through all installers and set options from config for inst in self.installer_plugins: inst.options = self.config.installer_options.get(inst.name, {}) # 2. setup core installers from config or OS plugin if self.config.core_installers is not None: core_installer_names = self.config.core_installers info_v("setting up core installers from config: '{}'".format( ", ".join(core_installer_names))) else: core_installer_names = os.get_core_installers( os_version, os.options) info_v("setting up core installers from os plugin: '{}'".format( ", ".join(core_installer_names))) for name in core_installer_names: inst = self.lookup_installer(name) if inst is None: error("ignoring core installer '{}'; according plugin was not " "found".format(name)) else: self.core_installers.append(inst) # 3. Go through all installers and check if they should be used # as additional installers for the current OS. if self.config.use_additional_installers: for inst in self.installer_plugins: if inst not in self.core_installers and \ inst.use_as_additional_installer(os_tuple): self.additional_installers.append(inst) info_v("Using additional installers: '{}'".format(", ".join( [i.name for i in self.additional_installers])))
def setup_os(self): """Create `OSSupport` and detect or override OS depending on config. :raises xylem.os_support.UnsupportedOsError: if OS override was invalid and detection failed :raises xylem.os_support.UnsupportedOSVersionError: if override version is not valid for override OS """ self.os_support = OSSupport(self.config.disabled_plugins.os) if self.config.os_override is None: self.os_support.detect_os() info_v("detected OS [%s]" % self.get_os_string()) else: info_v("overriding OS to [%s:%s]" % self.config.os_override) self.os_support.override_os(self.config.os_override) if self.config.os_override[1] is None: info_v("detected OS version [%s]" % self.get_os_string()) self.get_os().options = self.config.os_options
def resolve(xylem_keys, all_keys=False, config=None, database=None, sources_context=None, installer_context=None): """TODO""" # 1. Prepare config and contexts and load database config = ensure_config(config) ic = ensure_installer_context(installer_context, config) del installer_context # don't use further down, use `ic` only if not database: sources_context = ensure_sources_context(sources_context, config) database = RulesDatabase(sources_context) database.load_from_cache() del sources_context # don't use further down, use `database` only # 2. Prepare set of keys to look up if all_keys: lookup_keys = remove_duplicates(xylem_keys + sorted(database.keys(ic))) else: lookup_keys = remove_duplicates(xylem_keys) result = [] errors = [] # 3. Create an inverse install-from mapping # TODO: maybe allow pattern matching here like # `install-from "pip: python-*"` install_from_map = dict() for inst, keys in six.iteritems(config.install_from): for k in keys: if k in install_from_map: error("ignoring 'install from {}' for key '{}'; " "already configured to install from '{}'". format(inst, k, install_from_map[k])) else: install_from_map[k] = inst # 4. Resolve each key for key in lookup_keys: # 4.1. Lookup key in the database try: installer_dict = database.lookup(key, ic) if not installer_dict: errors.append((key, ResolutionError( "could not find rule for xylem key '{}' on '{}'.". format(key, ic.get_os_string())))) continue except LookupError as e: errors.append((key, chain_exception( ResolutionError, "lookup for key '{}' failed".format(key), e))) continue # 4.2. Decide which installer to use if key in install_from_map: inst_name = install_from_map[key] if not ic.lookup_installer(inst_name): errors.append((key, ResolutionError( "explicitly requested to install '{}' from '{}', but that " "installer is not loaded".format(key, inst_name)))) continue if inst_name not in installer_dict: errors.append((key, ResolutionError( "explicitly requested to install '{}' from '{}', but no " "rule for that installer was found; found rules for " "installers: `{}`". format(key, inst_name, to_str(installer_dict.keys()))))) continue info_v("found rule for key '{}' for explicitly requested " "installer '{}'".format(key, inst_name)) rule = installer_dict[inst_name] installer = ic.lookup_installer(inst_name) else: installer = None for inst in ic.core_installers: if inst.name in installer_dict: info_v("found rule for key '{}' for core installer '{}'". format(key, inst.name)) rule = installer_dict[inst.name] installer = inst break if installer is None: for inst in ic.additional_installers: if inst.name in installer_dict: info_v("found rule for key '{}' for additional " "installer '{}'".format(key, inst.name)) rule = installer_dict[inst.name] installer = inst if installer is None: errors.append((key, ResolutionError( "did not find rule for key '{}' for neither core " "installers '{}' nor additional installers '{}'; rules " "found for installers: '{}'". format(key, to_str(ic.core_installer_names), to_str(ic.additional_installer_names), to_str(installer_dict.keys()))))) continue # 4.3. Resolve with determined installer try: resolutions = installer.resolve(rule) except InstallerError as e: errors.append((key, chain_exception( ResolutionError, "failed to resolve with installer '{}'".format(installer.name), e))) else: result.append((key, (installer.name, resolutions))) return result, errors
def install_resolutions(installer_name, resolutions, installer_context, interactive=True, reinstall=False, simulate=False, continue_on_error=False): installer = installer_context.lookup_installer(installer_name) if installer is None: raise XylemInternalError("did not find resolved installer '{}'". format(installer_name)) errors = [] try: commands = installer.get_install_commands(resolutions, interactive=interactive, reinstall=reinstall) except InstallerError as e: # TODO: does InstallerError here make sense? errors.append(chain_exception( InstallError, "installer '{}' failed to compose install commands " "for resolutions {} with options `interactive={}` and " "`reinstall={}`". format(installer_name, resolutions, interactive, reinstall), e)) except Exception as e: raise_from( XylemInternalError, "unexpected error in installer '{}' while " "composing install commands for resolutions {} with options " "`interactive={}` and `reinstall={}`". format(installer_name, resolutions, interactive, reinstall), e) if not commands: info_v("# [%s] no packages to install" % installer_name) return errors # 1. when simulating, only print commands to screen if simulate: print("# [%s] installation commands:" % installer_name) for cmd in commands: info(' ' + ' '.join(cmd)) return errors # 2. else, run each install command set and collect errors for cmd in commands: info(fmt("@!executing command: %s@|" % ' '.join(cmd))) exitcode = subprocess.call(cmd) info_v(fmt("@!command return code: %s@|" % exitcode)) if exitcode != 0: errors.append(InstallError( "command `{}` for installer '{}' failed with return code {}". format(' '.join(cmd), installer, exitcode))) if not continue_on_error: return errors # 3. test installation of each resolution item for item in resolutions: try: if not installer.is_installed(item): errors.append(InstallError( "failed to detect successful installation of '{}' " "resolution `{}`".format(installer_name, item))) except InstallerError as e: # TODO: does this here make sense? errors.append(chain_exception( InstallError, "installer '{}' failed to determine if `{}` " "was successfully installed or not". format(installer_name, item), e)) except Exception as e: raise_from( XylemInternalError, "unexpected error in installer '{}' while " "checking successful installation of `{}`". format(installer_name, item), e) # 4. return list of failures if errors: info_v("# [%s] errors during installation" % installer_name) else: info_v("# [%s] successfully installed" % installer_name) return errors