Exemple #1
0
    def entitlements(self):
        """Return a dictionary of entitlements keyed by entitlement name.

        Return an empty dict if no entitlements are present.
        """
        if self._entitlements:
            return self._entitlements
        machine_token = self.machine_token
        if not machine_token:
            return {}

        self._entitlements = {}
        contractInfo = machine_token['machineTokenInfo']['contractInfo']
        ent_by_name = dict(
            (e['type'], e) for e in contractInfo['resourceEntitlements'])
        for entitlement_name, ent_value in ent_by_name.items():
            entitlement_cfg = {}
            if ent_value.get('entitled'):
                entitlement_cfg = self.read_cache('machine-access-%s' %
                                                  entitlement_name,
                                                  silent=True)
            if not entitlement_cfg:
                # Fallback to machine-token info on unentitled
                entitlement_cfg = {'entitlement': ent_value}
            util.apply_series_overrides(entitlement_cfg)
            self._entitlements[entitlement_name] = entitlement_cfg
        return self._entitlements
Exemple #2
0
    def entitlements(self):
        """Return a dictionary of entitlements keyed by entitlement name.

        Return an empty dict if no entitlements are present.
        """
        if self._entitlements:
            return self._entitlements
        machine_token = self.machine_token
        if not machine_token:
            return {}

        self._entitlements = {}
        contractInfo = machine_token["machineTokenInfo"]["contractInfo"]
        tokens_by_name = dict((e["type"], e["token"])
                              for e in machine_token.get("resourceTokens", []))
        ent_by_name = dict(
            (e["type"], e) for e in contractInfo["resourceEntitlements"])
        for entitlement_name, ent_value in ent_by_name.items():
            entitlement_cfg = {"entitlement": ent_value}
            if entitlement_name in tokens_by_name:
                entitlement_cfg["resourceToken"] = tokens_by_name[
                    entitlement_name]
            util.apply_series_overrides(entitlement_cfg)
            self._entitlements[entitlement_name] = entitlement_cfg
        return self._entitlements
Exemple #3
0
 def test_error_on_non_entitlement_dict(self):
     """Raise a runtime error when seeing invalid dict type."""
     with pytest.raises(RuntimeError) as exc:
         util.apply_series_overrides({"some": "dict"})
     error = ('Expected entitlement access dict. Missing "entitlement" key:'
              " {'some': 'dict'}")
     assert error == str(exc.value)
    def entitlements(self):
        """Return a dictionary of entitlements keyed by entitlement name.

        Return an empty dict if no entitlements are present.
        """
        if self._entitlements:
            return self._entitlements
        machine_token = self.machine_token
        if not machine_token:
            return {}

        self._entitlements = {}
        contractInfo = machine_token["machineTokenInfo"]["contractInfo"]
        ent_by_name = dict(
            (e["type"], e) for e in contractInfo["resourceEntitlements"]
        )
        for entitlement_name, ent_value in ent_by_name.items():
            entitlement_cfg = {}
            if ent_value.get("entitled"):
                entitlement_cfg = self.read_cache(
                    "machine-access-{}".format(entitlement_name), silent=True
                )
            if not entitlement_cfg:
                # Fallback to machine-token info on unentitled
                entitlement_cfg = {"entitlement": ent_value}
            util.apply_series_overrides(entitlement_cfg)
            self._entitlements[entitlement_name] = entitlement_cfg
        return self._entitlements
Exemple #5
0
def process_entitlement_delta(orig_access, new_access, allow_enable=False):
    """Process a entitlement access dictionary deltas if they exist.

    :param orig_access: Dict with original entitlement access details before
        contract refresh deltas
    :param orig_access: Dict with updated entitlement access details after
        contract refresh
    :param allow_enable: Boolean set True to perform enable operation on
        enableByDefault delta. When False log message about ignored default.
    """
    from uaclient.entitlements import ENTITLEMENT_CLASS_BY_NAME

    util.apply_series_overrides(new_access)
    deltas = util.get_dict_deltas(orig_access, new_access)
    if deltas:
        name = orig_access.get('entitlement', {}).get('type')
        if not name:
            name = deltas.get('entitlement', {}).get('type')
        if not name:
            raise RuntimeError(
                'Could not determine contract delta service type %s %s' %
                (orig_access, new_access))
        try:
            ent_cls = ENTITLEMENT_CLASS_BY_NAME[name]
        except KeyError:
            logging.debug(
                'Skipping entitlement deltas for "%s". No such class', name)
            return deltas
        entitlement = ent_cls()
        entitlement.process_contract_deltas(orig_access,
                                            deltas,
                                            allow_enable=allow_enable)
    return deltas
Exemple #6
0
    def test_missing_keys_are_handled(self, _):
        orig_access = {
            "entitlement": {
                "series": {
                    "xenial": {
                        "directives": {
                            "suites": ["xenial"]
                        }
                    }
                }
            }
        }
        expected = {"entitlement": {"directives": {"suites": ["xenial"]}}}

        util.apply_series_overrides(orig_access)

        assert expected == orig_access
def process_entitlement_delta(
    orig_access: "Dict[str, Any]",
    new_access: "Dict[str, Any]",
    allow_enable: bool = False,
    series_overrides: bool = True,
) -> "Dict":
    """Process a entitlement access dictionary deltas if they exist.

    :param orig_access: Dict with original entitlement access details before
        contract refresh deltas
    :param new_access: Dict with updated entitlement access details after
        contract refresh
    :param allow_enable: Boolean set True if allowed to perform the enable
        operation. When False, a message will be logged to inform the user
        about the recommended enabled service.
    :param series_overrides: Boolean set True if series overrides should be
        applied to the new_access dict.

    :raise UserFacingError: on failure to process deltas.
    :return: Dict of processed deltas
    """
    from uaclient.entitlements import ENTITLEMENT_CLASS_BY_NAME

    if series_overrides:
        util.apply_series_overrides(new_access)

    deltas = util.get_dict_deltas(orig_access, new_access)
    if deltas:
        name = orig_access.get("entitlement", {}).get("type")
        if not name:
            name = deltas.get("entitlement", {}).get("type")
        if not name:
            raise RuntimeError(
                "Could not determine contract delta service type {} {}".format(
                    orig_access, new_access))
        try:
            ent_cls = ENTITLEMENT_CLASS_BY_NAME[name]
        except KeyError:
            logging.debug(
                'Skipping entitlement deltas for "%s". No such class', name)
            return deltas
        entitlement = ent_cls(assume_yes=allow_enable)
        entitlement.process_contract_deltas(orig_access,
                                            deltas,
                                            allow_enable=allow_enable)
    return deltas
Exemple #8
0
 def test_mutates_orig_access_dict(self, _):
     """Mutate orig_access dict when called."""
     orig_access = {
         "entitlement": {
             "a": {
                 "a1": "av1",
                 "a2": {
                     "aa2": "aav2"
                 }
             },
             "b": "b1",
             "c": "c1",
             "series": {
                 "trusty": {
                     "a": "t1"
                 },
                 "xenial": {
                     "a": {
                         "a2": {
                             "aa2": "xxv2"
                         }
                     },
                     "b": "bx1"
                 },
             },
         }
     }
     expected = {
         "entitlement": {
             "a": {
                 "a1": "av1",
                 "a2": {
                     "aa2": "xxv2"
                 }
             },
             "b": "bx1",
             "c": "c1",
         }
     }
     util.apply_series_overrides(orig_access)
     assert orig_access == expected
    def process_contract_deltas(
        self,
        orig_access: "Dict[str, Any]",
        deltas: "Dict[str, Any]",
        allow_enable: bool = False,
    ) -> bool:
        """Process any contract access deltas for this entitlement.

        :param orig_access: Dictionary containing the original
            resourceEntitlement access details.
        :param deltas: Dictionary which contains only the changed access keys
        and values.
        :param allow_enable: Boolean set True if allowed to perform the enable
            operation. When False, a message will be logged to inform the user
            about the recommended enabled service.

        :return: True when delta operations are processed; False when noop.
        :raise: UserFacingError when auto-enable fails unexpectedly.
        """
        if not deltas:
            return True  # We processed all deltas that needed processing

        delta_entitlement = deltas.get("entitlement", {})
        delta_directives = delta_entitlement.get("directives", {})
        status_cache = self.cfg.read_cache("status-cache")

        transition_to_unentitled = bool(delta_entitlement == util.DROPPED_KEY)
        if not transition_to_unentitled:
            if delta_entitlement:
                util.apply_series_overrides(deltas)
                delta_entitlement = deltas["entitlement"]
            if orig_access and "entitled" in delta_entitlement:
                transition_to_unentitled = delta_entitlement["entitled"] in (
                    False,
                    util.DROPPED_KEY,
                )
        if transition_to_unentitled:
            if delta_directives and status_cache:
                application_status = self._check_application_status_on_cache()
            else:
                application_status, _ = self.application_status()

            if application_status != status.ApplicationStatus.DISABLED:
                if self.can_disable(silent=True):
                    self.disable()
                    logging.info(
                        "Due to contract refresh, '%s' is now disabled.",
                        self.name,
                    )
                else:
                    logging.warning(
                        "Unable to disable '%s' as recommended during contract"
                        " refresh. Service is still active. See"
                        " `ua status`",
                        self.name,
                    )
            return True

        resourceToken = orig_access.get("resourceToken")
        if not resourceToken:
            resourceToken = deltas.get("resourceToken")
        delta_obligations = delta_entitlement.get("obligations", {})
        can_enable = self.can_enable(silent=True)
        enableByDefault = bool(
            delta_obligations.get("enableByDefault") and resourceToken
        )
        if can_enable and enableByDefault:
            if allow_enable:
                msg = status.MESSAGE_ENABLE_BY_DEFAULT_TMPL.format(
                    name=self.name
                )
                logging.info(msg)
                self.enable()
            else:
                msg = status.MESSAGE_ENABLE_BY_DEFAULT_MANUAL_TMPL.format(
                    name=self.name
                )
                logging.info(msg)
            return True

        return False
Exemple #10
0
    def process_contract_deltas(self,
                                orig_access: 'Dict[str, Any]',
                                deltas: 'Dict[str, Any]',
                                allow_enable: bool = False) -> bool:
        """Process any contract access deltas for this entitlement.

        :param orig_access: Dictionary containing the original
            resourceEntitlement access details.
        :param deltas: Dictionary which contains only the changed access keys
        and values.
        :param allow_enable: Boolean set True if allowed to perform the enable
            operation. When False, a message will be logged to inform the user
            about the recommended enabled service.

        :return: True when delta operations are processed; False when noop.
        """
        if not deltas:
            return True  # We processed all deltas that needed processing

        delta_entitlement = deltas.get('entitlement', {})
        transition_to_unentitled = bool(delta_entitlement == util.DROPPED_KEY)
        if not transition_to_unentitled:
            if delta_entitlement:
                util.apply_series_overrides(deltas)
                delta_entitlement = deltas['entitlement']
            if orig_access and 'entitled' in delta_entitlement:
                transition_to_unentitled = (delta_entitlement['entitled']
                                            in (False, util.DROPPED_KEY))
        if transition_to_unentitled:
            application_status, _ = self.application_status()
            if application_status != status.ApplicationStatus.DISABLED:
                if self.can_disable(silent=True):
                    self.disable()
                    logging.info(
                        "Due to contract refresh, '%s' is now disabled.",
                        self.name)
                else:
                    logging.warning(
                        "Unable to disable '%s' as recommended during contract"
                        " refresh. Service is still active. See `ua status`" %
                        self.name)
            # Clean up former entitled machine-access-<name> response cache
            # file because uaclient doesn't access machine-access-* routes or
            # responses on unentitled services.
            self.cfg.delete_cache_key('machine-access-%s' % self.name)
            return True

        resourceToken = orig_access.get('resourceToken')
        if not resourceToken:
            resourceToken = deltas.get('resourceToken')
        delta_obligations = delta_entitlement.get('obligations', {})
        can_enable = self.can_enable(silent=True)
        enableByDefault = bool(
            delta_obligations.get('enableByDefault') and resourceToken)
        if can_enable and enableByDefault:
            if allow_enable:
                msg = status.MESSAGE_ENABLE_BY_DEFAULT_TMPL.format(
                    name=self.name)
                logging.info(msg)
                self.enable()
            else:
                msg = status.MESSAGE_ENABLE_BY_DEFAULT_MANUAL_TMPL.format(
                    name=self.name)
                logging.info(msg)
            return True

        return False