Exemplo n.º 1
0
def load_url(url, retry=2, retry_period=1, timeout=10):
    """Load a given url with retries, retry_periods, and timeouts.

    :param str url: URL to load and return contents of
    :param int retry: number of times to retry the url on 503 or timeout
    :param float retry_period: time to wait between retries in seconds
    :param float timeout: timeout for opening the URL in seconds
    :retunrs: loaded data as string
    :rtype: str
    :raises DownloadFailure: if loading fails even after retries
    """
    retry = max(retry, 0)  # negative retry count causes infinite loop
    while True:
        try:
            req = urlopen(url, timeout=timeout)
        except HTTPError as e:
            if e.code == 503 and retry:
                retry -= 1
                time.sleep(retry_period)
            else:
                raise_from(DownloadFailure, "Failed to load url '{0}'.".
                           format(url), e)
        except URLError as e:
            if isinstance(e.reason, socket.timeout) and retry:
                retry -= 1
                time.sleep(retry_period)
            else:
                raise_from(DownloadFailure, "Failed to load url '{0}'.".
                           format(url), e)
        else:
            break
    _, params = cgi.parse_header(req.headers.get('Content-Type', ''))
    encoding = params.get('charset', 'utf-8')
    data = req.read()
    return to_str(data, encoding=encoding)
Exemplo n.º 2
0
def verify_installer_dict(installer_dict, allow_default_installer=True):
    """Verify that an expanded installer dict has valid structure.

    :param dict installer_dict: dictionary mapping installer names to
        installer rules
    :param bool allow_default_installer: indicates if
        'default_installer' installer name is allowed
    :raises ValueError: if installer dict does not have valid structure
    """
    if not isinstance(installer_dict, dict):
        raise ValueError("Expected installer dict of type 'dict', but got "
                         "'{0}'.".format(type(installer_dict)))
    for installer_name, installer_rule in installer_dict.items():
        if not allow_default_installer:
            if installer_name == 'default_installer':
                raise ValueError("Default installer is not allowed here.")
        try:
            verify_installer_name(installer_name)
        except ValueError as e:
            raise_from(ValueError, "Expected installer dict to have installer "
                       "names as keys.", e)
        try:
            verify_installer_rule(installer_rule)
        except ValueError as e:
            raise_from(ValueError, "Expected installer dict to have installer "
                       "rules as values, but got error for installer '{0}.".
                       format(installer_name), e)
Exemplo n.º 3
0
def verify_installer_rule(installer_rule):
    """Verify that an expanded installer rule has valid structure.

    :param dict installer_rule: dictionary describing an installer command
    :raises ValueError: if installer rule does not have valid structure
    """
    if not isinstance(installer_rule, dict):
        raise ValueError("Expected installer rule of type 'dict', but got "
                         "'{0}'.".format(type(installer_rule)))
    for key, value in installer_rule.items():
        if not isinstance(key, text_type):
            raise ValueError("Expected installer rule to have keys of text"
                             " type, but got '{0}'.".format(type(key)))
        # The contents of the installer rule is specific to the
        # according installer plugin, but we check for a few common keys here
        if key == "packages":
            if not isinstance(value, list):
                raise ValueError(
                    "Expected 'packages' entry of installer rule to be of "
                    "type 'list', but got '{0}'".format(type(value)))
        if key == "depends":
            if not isinstance(value, list):
                raise ValueError(
                    "Expected 'depends' entry of installer rule to be of "
                    "type 'list', but got '{0}'".format(type(value)))
            try:
                for xylem_key in value:
                    verify_xylem_key(xylem_key)
            except ValueError as e:
                raise_from(ValueError, "Expected 'depends' entry of installer "
                           "rule to be list of xylem keys.", e)
Exemplo n.º 4
0
    def from_command_line(self, value):
        """Create config value from value of command line parser.

        A value of `UNSET_COMMAND_LINE` should be interpreted as
        'unset'.

        The default implementation parses the input string as yaml and
        then invokes :meth:`from_yaml`.

        Before returning a value, :meth:`verify` should be used to
        verify the validity of the parsed value.

        :param value: value from the command line parser, which is a
            string when :meth:`command_line_multiple()` is false and a
            list of strings otherwise, or `UNSET_COMMAND_LINE` if the
            argument was omitted on the command line.
        :raises ConfigValueError: if ``value`` cannot be parsed properly
            or has invalid type or structure
        """
        if value == UNSET_COMMAND_LINE:
            return self.unset_value()
        try:
            return self.from_yaml(load_yaml(value))
        except YAMLError as e:
            raise_from(
                ConfigValueError, "parsing command line value failed", e)
Exemplo n.º 5
0
def load_url(url, retry=2, retry_period=1, timeout=10):
    """Load a given url with retries, retry_periods, and timeouts.

    :param str url: URL to load and return contents of
    :param int retry: number of times to retry the url on 503 or timeout
    :param float retry_period: time to wait between retries in seconds
    :param float timeout: timeout for opening the URL in seconds
    :retunrs: loaded data as string
    :rtype: str
    :raises DownloadFailure: if loading fails even after retries
    """
    retry = max(retry, 0)  # negative retry count causes infinite loop
    while True:
        try:
            req = urlopen(url, timeout=timeout)
        except HTTPError as e:
            if e.code == 503 and retry:
                retry -= 1
                time.sleep(retry_period)
            else:
                raise_from(DownloadFailure,
                           "Failed to load url '{0}'.".format(url), e)
        except URLError as e:
            if isinstance(e.reason, socket.timeout) and retry:
                retry -= 1
                time.sleep(retry_period)
            else:
                raise_from(DownloadFailure,
                           "Failed to load url '{0}'.".format(url), e)
        else:
            break
    _, params = cgi.parse_header(req.headers.get('Content-Type', ''))
    encoding = params.get('charset', 'utf-8')
    data = req.read()
    return to_str(data, encoding=encoding)
Exemplo n.º 6
0
 def verify(self, value):
     if not self.is_unset(value):
         if not isinstance(value, list):
             raise ConfigValueError(type_error_msg('list', value))
         try:
             for elt in value:
                 self.element_type.verify(elt)
         except ConfigValueError as e:
             raise_from(ConfigValueError, "invalid list element", e)
     return value
Exemplo n.º 7
0
 def verify(self, value):
     if not self.is_unset(value):
         if not isinstance(value, dict):
             raise ConfigValueError(type_error_msg('dict', value))
         try:
             for k, v in six.iteritems(value):
                 self.key_type.verify(k)
                 self.value_type.verify(v)
         except ConfigValueError as e:
             raise_from(ConfigValueError, "invalid key or value", e)
     return value
Exemplo n.º 8
0
    def verify_plugin(cls, obj):
        """Verify validity of a plugin object.

        :param PluginBase obj: instantiated plugin object
        :raises InvalidPluginError: if ``obj`` is invalid
        """
        # delay import to avoid circular dependency
        from xylem.sources.rules_dict import verify_rules_dict_identifier
        try:
            verify_rules_dict_identifier(obj.name, "plugin name")
        except ValueError as e:
            raise_from(InvalidPluginError, "plugin name invalid", e)
Exemplo n.º 9
0
    def verify_plugin(cls, obj):
        """Verify validity of a plugin object.

        :param PluginBase obj: instantiated plugin object
        :raises InvalidPluginError: if ``obj`` is invalid
        """
        # delay import to avoid circular dependency
        from xylem.sources.rules_dict import verify_rules_dict_identifier
        try:
            verify_rules_dict_identifier(obj.name, "plugin name")
        except ValueError as e:
            raise_from(InvalidPluginError, "plugin name invalid", e)
Exemplo n.º 10
0
def load_config_file_yaml(path):
    """Utility for loading yaml config files.

    Makes sure to always return a dict with strings as keys.  Non-
    existent or empty files result in an empty dictionary.

    :raises ConfigValueError: if config file cannot be opened, decode or
        parsed
    """
    try:
        if os.path.isfile(path):
            with open(path, 'rb') as f:
                binary_data = f.read()
            config = load_yaml(to_str(binary_data))
        else:
            config = None
        return process_config_file_yaml(config)
    except EnvironmentError as e:
        raise_from(ConfigValueError,
                   "failed to read config file `{}`".format(path), e)
    except UnicodeError as e:
        raise_from(ConfigValueError,
                   "failed to decode config file `{}`".format(path), e)
    except YAMLError as e:
        raise_from(ConfigValueError,
                   "failed to parse config file as YAML `{}`".format(path), e)
    except ConfigValueError as e:
        raise_from(ConfigValueError,
                   "config file has invalid structure `{}`".format(path), e)
Exemplo n.º 11
0
def verify_plugin_definition(definition, kind, base_class):
    """Verify plugin definition.

    :param dict definition: definition of plugin as loaded from entry point
    :param str kind: kind of plugin (e.g. "installer")
    :param type base_class: (abstract) base class plugins must derive from
    :raises InvalidPluginError: if plugin definition is invalid
    """
    try:
        verify_plugin_name(definition['plugin_name'])
        verify_plugin_description(definition['description'])
        verify_plugin_class(definition[kind], base_class)
    except (TypeError, KeyError, ValueError) as e:
        raise_from(InvalidPluginError, "The following definition of {0} "
                   "plugin is invalid:\n{1}".format(kind, definition), e)
Exemplo n.º 12
0
def verify_source_description_list(descr_list):
    """Verify that a source description list has valid structure.

    :param list descr_list: list of source descriptions
    :raises ValueError: if structure of source descriptions is invalid
    """
    try:
        for descr in descr_list:
            verify_source_description(descr)
    except TypeError as e:
        raise_from(ValueError, "failed to verify source description list of "
                   "type '{0}'".format(to_str(type(descr_list))), e)
    except ValueError as e:
        raise_from(ValueError, "source description list contains invalid "
                   "source descriptions", e)
Exemplo n.º 13
0
 def parse_single(input):
     try:
         result = load_yaml(input)
     except YAMLError:
         result = None
     if not isinstance(result, dict):
         # special parsing allowing lists without enclosing `{}`
         try:
             result = load_yaml("{" + input + "}")
         except YAMLError as e:
             raise_from(ConfigValueError,
                        "failed to parse `{}` as dict".format(input), e)
     if not isinstance(result, dict):
         raise ConfigValueError(type_error_msg("dict", result))
     return result
Exemplo n.º 14
0
 def options(self, value):
     try:
         self._options = config_from_parsed_yaml(value,
                                                 self.options_description,
                                                 use_defaults=True)
     except ConfigValueError as e:
         raise_from(
             ConfigValueError, "invalid options `{}` for os plugin "
             "'{}'".format(value, self.name), e)
     unused_keys = set(value.keys()) - set(self._options.keys())
     if unused_keys:
         warning("ignoring the following unknown options for os plugin "
                 "'{}': {} -- known options are: {}".format(
                     self.name, to_str(list(unused_keys)),
                     to_str(self._options.keys())))
Exemplo n.º 15
0
def verify_plugin_definition(definition, kind, base_class):
    """Verify plugin definition.

    :param dict definition: definition of plugin as loaded from entry point
    :param str kind: kind of plugin (e.g. "installer")
    :param type base_class: (abstract) base class plugins must derive from
    :raises InvalidPluginError: if plugin definition is invalid
    """
    try:
        verify_plugin_name(definition['plugin_name'])
        verify_plugin_description(definition['description'])
        verify_plugin_class(definition[kind], base_class)
    except (TypeError, KeyError, ValueError) as e:
        raise_from(
            InvalidPluginError, "The following definition of {0} "
            "plugin is invalid:\n{1}".format(kind, definition), e)
Exemplo n.º 16
0
def verify_source_description(descr):
    """Verify that a source description has valid structure.

    :param dict descr_list: source description
    :raises ValueError: if structure of source description is invalid
    """
    if not isinstance(descr, dict):
        raise ValueError("Expected source description to be a dictionary, but "
                         "got '{0}'.".format(to_str(type(descr))))
    keys = descr.keys()
    if not len(keys) == 1:
        raise ValueError("Expected source description to have one entry, but "
                         "got keys '{0}'.".format(keys))
    try:
        verify_spec_name(keys[0])
    except ValueError as e:
        raise_from(ValueError, "source description does not have valid "
                   "spec name '{0}'".format(keys[0], e))
Exemplo n.º 17
0
 def _parse_installer_rule(self, installer_rule):
     """Helper to parse installer rule with the installer_description."""
     try:
         parsed_rule = config_from_parsed_yaml(
             installer_rule,
             self.installer_rule_description,
             use_defaults=True)
     except ConfigValueError as e:
         raise_from(
             InvalidRuleError, "invalid installer rule `{}` for "
             "installer '{}'".format(installer_rule, self.name), e)
     unused_keys = set(installer_rule.keys()) - set(parsed_rule.keys())
     if unused_keys:
         warning("ignoring the following unknown installer rule keys for "
                 "installer '{}' while parsing installer rule with "
                 "packages {}: {}".format(self.name,
                                          to_str(parsed_rule.packages),
                                          to_str(list(unused_keys))))
     return parsed_rule
Exemplo n.º 18
0
def filter_uninstalled(resolved, installer_context):
    errors = []
    uninstalled = []
    for installer_name, resolutions in resolved:
        installer = installer_context.lookup_installer(installer_name)
        if installer is None:
            raise XylemInternalError("did not find resolved installer '{}'".
                                     format(installer_name))
        try:
            resolutions = installer.filter_uninstalled(resolutions)
        except InstallerError as e:  # TODO: does this here make sense?
            errors.append(chain_exception(
                InstallError, "installer '{}' failed to determine "
                "uninstalled resolutions out of {}".
                format(installer_name, resolutions), e))
        except Exception as e:
            raise_from(
                XylemInternalError, "unexpected error in installer '{}' "
                "while trying to determine uninstalled resolutions out of {}".
                format(installer_name, resolutions), e)
        # only create a tuple if there is something to do
        if resolutions:
            uninstalled.append((installer_name, resolutions))
    return uninstalled, errors
Exemplo n.º 19
0
def verify_rules_dict(rules_dict, allow_default_installer=True):
    """Verify that an expanded rules dict has valid structure.

    :param dict rules_dict: dictionary mapping xylem keys to os dicts
    :param bool allow_default_installer: indicates if
        'default_installer' installer name is allowed
    :raises ValueError: if rules dict does not have valid structure
    """
    if not isinstance(rules_dict, dict):
        raise ValueError("Expected rules dict of type 'dict', but got '{0}'.".
                         format(type(rules_dict)))
    for xylem_key, os_dict in rules_dict.items():
        try:
            verify_xylem_key(xylem_key)
        except ValueError as e:
            raise_from(ValueError, "Expected rules dict to have xylem keys as "
                       "keys.", e)
        try:
            verify_os_dict(os_dict)
        except ValueError as e:
            raise_from(ValueError, "Expected rules dict to have valid os "
                       "dicts as values, but got error for key '{0}'.".
                       format(xylem_key), e)
            raise
Exemplo n.º 20
0
def verify_version_dict(version_dict, allow_default_installer=True):
    """Verify that an expanded version dict has valid structure.

    :param dict version_dict: dictionary mapping os versions to
        installer dicts
    :param bool allow_default_installer: indicates if
        'default_installer' installer name is allowed
    :raises ValueError: if version dict does not have valid structure
    """
    if not isinstance(version_dict, dict):
        raise ValueError("Expected version dict of type 'dict', but got "
                         "'{0}'.".format(type(version_dict)))
    for os_version, installer_dict in version_dict.items():
        try:
            verify_os_version(os_version)
        except ValueError as e:
            raise_from(ValueError, "Expected version dict to have os versions "
                       "as keys.", e)
        try:
            verify_installer_dict(installer_dict, allow_default_installer)
        except ValueError as e:
            raise_from(ValueError, "Expected version dict to have valid "
                       "installer dicts as values, but got error for version "
                       "'{0}'.".format(os_version), e)
Exemplo n.º 21
0
def verify_os_dict(os_dict, allow_default_installer=True):
    """Verify that an expanded os dict has valid structure.

    :param dict os_dict: dictionary mapping os names to version dicts
    :param bool allow_default_installer: indicates if
        'default_installer' installer name is allowed
    :raises ValueError: if os dict does not have valid structure
    """
    if not isinstance(os_dict, dict):
        raise ValueError("Expected os dict of type 'dict', but got '{0}'.".
                         format(type(os_dict)))
    for os_name, version_dict in os_dict.items():
        try:
            verify_os_name(os_name)
        except ValueError as e:
            raise_from(ValueError, "Expected os dict to have os names as "
                       "keys.", e)
        try:
            def_inst = allow_default_installer and os_name != 'any_os'
            verify_version_dict(version_dict, allow_default_installer=def_inst)
        except ValueError as e:
            raise_from(ValueError, "Expected os dict to have valid version "
                       "dicts as values, but got error for os '{0}'.".
                       format(os_name), e)
Exemplo n.º 22
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