示例#1
0
    def is_enabled(self,
                   context: dict = None,
                   default_value: bool = False) -> bool:  # pylint: disable=unused-argument
        """
        Checks if feature is enabled.

        :param context: Context information
        :param default_value: Deprecated!  Users should use the fallback_function on the main is_enabled() method.
        :return:
        """
        flag_value = False

        if self.enabled:
            try:
                if self.strategies:
                    strategy_result = any(x.execute(context) for x in self.strategies)
                else:
                    # If no strategies are present, should default to true. This isn't possible via UI.
                    strategy_result = True

                flag_value = strategy_result
            except Exception as strategy_except:
                LOGGER.warning("Error checking feature flag: %s", strategy_except)

        self.increment_stats(flag_value)

        LOGGER.info("Feature toggle status for feature %s: %s", self.name, flag_value)

        return flag_value
示例#2
0
    def is_enabled(self,
                   context: dict = None,
                   default_value: bool = False) -> bool:
        """
        Checks if feature is enabled.

        :param context: Context information
        :param default_value: Optional, but allows for override.
        :return:
        """
        flag_value = default_value

        if self.enabled:
            try:
                if self.strategies:
                    strategy_result = any(
                        [x.execute(context) for x in self.strategies])
                else:
                    # If no strategies are present, should default to true.  This isn't possible via UI.
                    strategy_result = True

                flag_value = flag_value or strategy_result
            except Exception as strategy_except:
                LOGGER.warning("Error checking feature flag: %s",
                               strategy_except)

        self.increment_stats(flag_value)

        LOGGER.info(
            f"Feature toggle status for feature {self.name} with context {context} : {flag_value}"
        )

        return flag_value
    def is_enabled(self,
                   context: dict = None,
                   default_value: bool = False) -> bool:
        """
        Checks if feature is enabled.

        :param context: Context information
        :param default_value: Optional, but allows for override.
        :return:
        """
        flag_value = default_value

        if self.enabled:
            try:
                for strategy in self.strategies:
                    flag_value = flag_value or strategy(context)
            except Exception as strategy_except:
                LOGGER.warning("Error checking feature flag: %s",
                               strategy_except)

        self.increment_stats(flag_value)

        LOGGER.info("Feature toggle status for feature %s: %s", self.name,
                    flag_value)

        return flag_value
示例#4
0
def get_feature_toggles(url: str,
                        app_name: str,
                        instance_id: str,
                        custom_headers: dict,
                        custom_options: dict,
                        project: str = None) -> dict:
    """
    Retrieves feature flags from unleash central server.

    Notes:
    * If unsuccessful (i.e. not HTTP status code 200), exception will be caught and logged.
      This is to allow "safe" error handling if unleash server goes down.

    :param url:
    :param app_name:
    :param instance_id:
    :param custom_headers:
    :param custom_options:
    :param project:
    :return: Feature flags if successful, empty dict if not.
    """
    try:
        LOGGER.info("Getting feature flag.")

        headers = {
            "UNLEASH-APPNAME": app_name,
            "UNLEASH-INSTANCEID": instance_id
        }

        base_url = f"{url}{FEATURES_URL}"
        base_params = {}

        if project:
            base_params = {'project': project}

        resp = requests.get(base_url,
                            headers={
                                **custom_headers,
                                **headers
                            },
                            params=base_params,
                            timeout=REQUEST_TIMEOUT,
                            **custom_options)

        if resp.status_code != 200:
            log_resp_info(resp)
            LOGGER.warning(
                "Unleash Client feature fetch failed due to unexpected HTTP status code."
            )
            raise Exception("Unleash Client feature fetch failed!")

        return resp.json()
    except Exception as exc:
        LOGGER.exception(
            "Unleash Client feature fetch failed due to exception: %s", exc)

    return {}
示例#5
0
    def apply(self, context: dict = None) -> bool:
        """
        Returns true/false depending on constraint provisioning and context.

        :param context: Context information
        :return:
        """
        constraint_check = False

        try:
            context_value = get_identifier(self.context_name, context)

            # Set currentTime if not specified
            if self.context_name == "currentTime" and not context_value:
                context_value = datetime.now()

            if context_value is not None:
                if self.operator in [
                        ConstraintOperators.IN, ConstraintOperators.NOT_IN
                ]:
                    constraint_check = self.check_list_operators(
                        context_value=context_value)
                elif self.operator in [
                        ConstraintOperators.STR_CONTAINS,
                        ConstraintOperators.STR_ENDS_WITH,
                        ConstraintOperators.STR_STARTS_WITH
                ]:
                    constraint_check = self.check_string_operators(
                        context_value=context_value)
                elif self.operator in [
                        ConstraintOperators.NUM_EQ, ConstraintOperators.NUM_GT,
                        ConstraintOperators.NUM_GTE,
                        ConstraintOperators.NUM_LT, ConstraintOperators.NUM_LTE
                ]:
                    constraint_check = self.check_numeric_operators(
                        context_value=context_value)
                elif self.operator in [
                        ConstraintOperators.DATE_AFTER,
                        ConstraintOperators.DATE_BEFORE
                ]:
                    constraint_check = self.check_date_operators(
                        context_value=context_value)
                elif self.operator in [
                        ConstraintOperators.SEMVER_EQ,
                        ConstraintOperators.SEMVER_GT,
                        ConstraintOperators.SEMVER_LT
                ]:
                    constraint_check = self.check_semver_operators(
                        context_value=context_value)

        except Exception as excep:  # pylint: disable=broad-except
            LOGGER.info("Could not evaluate context %s!  Error: %s",
                        self.context_name, excep)

        return not constraint_check if self.inverted else constraint_check
def fetch_and_load_features(url, app_name, instance_id, custom_headers, cache,
                            features, strategy_mapping):
    feature_provisioning = get_feature_toggles(url, app_name, instance_id,
                                               custom_headers)

    if feature_provisioning:
        cache[FEATURES_URL] = feature_provisioning
        cache.sync()
    else:
        LOGGER.info("Unable to get feature flag toggles, using cached values.")

    load_features(cache, features, strategy_mapping)
示例#7
0
def fetch_and_load_features(url: str, app_name: str, instance_id: str,
                            custom_headers: dict, cache: FileCache,
                            strategies: dict) -> None:
    feature_provisioning = get_feature_toggles(url, app_name, instance_id,
                                               custom_headers)

    if feature_provisioning:
        cache[FEATURES_URL] = feature_provisioning
        cache.sync()
    else:
        LOGGER.info("Unable to get feature flag toggles, using cached values.")

    load_features(cache, strategies)
    def apply(self, context=None):
        """
        Returns true/false depending on constraint provisioning and context.

        :param context: Context information
        :return:
        """
        constraint_check = False

        try:
            value = get_identifier(self.context_name, context)

            if value:
                if self.operator.upper() == "IN":
                    constraint_check = value in self.values
                elif self.operator.upper() == "NOT_IN":
                    constraint_check = value not in self.values
        except Exception as excep:  #pylint: disable=W0703
            LOGGER.info("Could not evaluate context %s!  Error: %s", self.context_name, excep)

        return constraint_check
示例#9
0
def get_feature_toggles(url: str, app_name: str, instance_id: str,
                        custom_headers: dict) -> dict:
    """
    Retrieves feature flags from unleash central server.

    Notes:
    * If unsuccessful (i.e. not HTTP status code 200), exception will be caught and logged.
      This is to allow "safe" error handling if unleash server goes down.

    :param url:
    :param app_name:
    :param instance_id:
    :param custom_headers:
    :return: Feature flags if successful, empty dict if not.
    """
    try:
        LOGGER.info("Getting feature flag.")

        headers = {
            "UNLEASH-APPNAME": app_name,
            "UNLEASH-INSTANCEID": instance_id
        }

        resp = requests.get(url + FEATURES_URL,
                            headers={
                                **custom_headers,
                                **headers
                            },
                            timeout=REQUEST_TIMEOUT)

        if resp.status_code != 200:
            LOGGER.warning("unleash feature fetch failed!")
            raise Exception("unleash feature fetch failed!")

        return json.loads(resp.content)
    except Exception:
        LOGGER.exception("Unleash feature fetch failed!")

    return {}
示例#10
0
def register_client(url: str, app_name: str, instance_id: str,
                    metrics_interval: int, custom_headers: dict,
                    custom_options: dict, supported_strategies: dict) -> bool:
    """
    Attempts to register client with unleash server.

    Notes:
    * If unsuccessful (i.e. not HTTP status code 202), exception will be caught and logged.
      This is to allow "safe" error handling if unleash server goes down.

    :param url:
    :param app_name:
    :param instance_id:
    :param metrics_interval:
    :param custom_headers:
    :param custom_options:
    :param supported_strategies:
    :return: true if registration successful, false if registration unsuccessful or exception.
    """
    registation_request = {
        "appName": app_name,
        "instanceId": instance_id,
        "sdkVersion": "{}:{}".format(SDK_NAME, SDK_VERSION),
        "strategies": [*supported_strategies],
        "started": datetime.now(timezone.utc).isoformat(),
        "interval": metrics_interval
    }

    try:
        LOGGER.info("Registering unleash client with unleash @ %s", url)
        LOGGER.info("Registration request information: %s",
                    registation_request)

        resp = requests.post(url + REGISTER_URL,
                             data=json.dumps(registation_request),
                             headers={
                                 **custom_headers,
                                 **APPLICATION_HEADERS
                             },
                             timeout=REQUEST_TIMEOUT,
                             **custom_options)

        if resp.status_code != 202:
            log_resp_info(resp)
            LOGGER.warning(
                "Unleash Client registration failed due to unexpected HTTP status code."
            )
            return False

        LOGGER.info("Unleash Client successfully registered!")

        return True
    except Exception:
        LOGGER.exception(
            "Unleash Client registration failed due to exception: %s",
            Exception)

    return False
示例#11
0
    def update_cache(data: Dict[str, Any]) -> None:
        """
        Update cache data

        Args:
            data(dict): Feature toggles Data

        Returns:
            None
        """
        if FeatureToggles.__cache is None:
            raise Exception(
                'To update cache Feature Toggles class needs to be initialised'
            )

        LOGGER.info(f'Updating the cache data: {data}')
        try:
            FeatureToggles.__cache.set(consts.FEATURES_URL, pickle.dumps(data))
        except Exception as err:
            raise Exception(
                f'Exception occured while updating the redis cache: {str(err)}'
            )
        LOGGER.info(f'Cache Updatation is Done')
示例#12
0
def send_metrics(url: str, request_body: dict, custom_headers: dict,
                 custom_options: dict) -> bool:
    """
    Attempts to send metrics to Unleash server

    Notes:
    * If unsuccessful (i.e. not HTTP status code 200), message will be logged

    :param url:
    :param app_name:
    :param instance_id:
    :param metrics_interval:
    :param custom_headers:
    :param custom_options:
    :return: true if registration successful, false if registration unsuccessful or exception.
    """
    try:
        LOGGER.info("Sending messages to with unleash @ %s", url)
        LOGGER.info("unleash metrics information: %s", request_body)

        resp = requests.post(url + METRICS_URL,
                             data=json.dumps(request_body),
                             headers={
                                 **custom_headers,
                                 **APPLICATION_HEADERS
                             },
                             timeout=REQUEST_TIMEOUT,
                             **custom_options)

        if resp.status_code != 202:
            log_resp_info(resp)
            LOGGER.warning("Unleash CLient metrics submission failed.")
            return False

        LOGGER.info("Unleash Client metrics successfully sent!")

        return True
    except Exception:
        LOGGER.exception(
            "Unleash Client metrics submission failed dye to exception: %s",
            Exception)

    return False
def send_metrics(url, request_body, custom_headers):
    """
    Attempts to send metrics to Unleash server

    Notes:
    * If unsuccessful (i.e. not HTTP status code 200), message will be logged

    :param url:
    :param app_name:
    :param instance_id:
    :param metrics_interval:
    :param custom_headers:
    :return: true if registration successful, false if registration unsuccessful or exception.
    """
    try:
        LOGGER.info("Sending messages to with unleash @ %s", url)
        LOGGER.info("unleash metrics information: %s", request_body)

        headers = APPLICATION_HEADERS.copy()
        headers.update(custom_headers)

        resp = requests.post(url + METRICS_URL,
                             data=json.dumps(request_body),
                             headers=headers,
                             timeout=REQUEST_TIMEOUT)

        if resp.status_code != 202:
            LOGGER.warning("unleash metrics submission failed.")
            return False

        LOGGER.info("unleash metrics successfully sent!")

        return True
    except Exception:
        LOGGER.exception("unleash metrics failed to send.")

    return False
示例#14
0
def get_feature_toggles(url: str,
                        app_name: str,
                        instance_id: str,
                        custom_headers: dict,
                        custom_options: dict,
                        project: str = None,
                        cached_etag: str = '') -> Tuple[dict, str]:
    """
    Retrieves feature flags from unleash central server.

    Notes:
    * If unsuccessful (i.e. not HTTP status code 200), exception will be caught and logged.
      This is to allow "safe" error handling if unleash server goes down.

    :param url:
    :param app_name:
    :param instance_id:
    :param custom_headers:
    :param custom_options:
    :param project:
    :param cached_etag:
    :return: (Feature flags, etag) if successful, ({},'') if not
    """
    try:
        LOGGER.info("Getting feature flag.")

        headers = {
            "UNLEASH-APPNAME": app_name,
            "UNLEASH-INSTANCEID": instance_id
        }

        if cached_etag:
            headers['If-None-Match'] = cached_etag

        base_url = f"{url}{FEATURES_URL}"
        base_params = {}

        if project:
            base_params = {'project': project}

        resp = requests.get(base_url,
                            headers={
                                **custom_headers,
                                **headers
                            },
                            params=base_params,
                            timeout=REQUEST_TIMEOUT,
                            **custom_options)

        if resp.status_code not in [200, 304]:
            log_resp_info(resp)
            LOGGER.warning(
                "Unleash Client feature fetch failed due to unexpected HTTP status code."
            )
            raise Exception("Unleash Client feature fetch failed!")

        etag = ''
        if 'etag' in resp.headers.keys():
            etag = resp.headers['etag']

        if resp.status_code == 304:
            return None, etag

        return resp.json(), etag
    except Exception as exc:
        LOGGER.exception(
            "Unleash Client feature fetch failed due to exception: %s", exc)

    return {}, ''