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_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.")
Exemple #5
0
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
Exemple #6
0
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))
Exemple #11
0
    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])))
Exemple #12
0
    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])))
Exemple #13
0
    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
Exemple #14
0
    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
Exemple #15
0
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
Exemple #16
0
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