def test_get_backend_cls(self): res1 = get_backend_cls("Stackdriver") res2 = get_backend_cls("Prometheus") self.assertEqual(res1.__name__, "StackdriverBackend") self.assertEqual(res2.__name__, "PrometheusBackend") with self.assertRaises(SystemExit): get_backend_cls("UndefinedBackend")
def test_get_backend_cls(self): res1 = get_backend_cls("Stackdriver") res2 = get_backend_cls("Prometheus") self.assertEqual(res1.__name__, "StackdriverBackend") self.assertEqual(res1.__module__, "slo_generator.backends.stackdriver") self.assertEqual(res2.__name__, "PrometheusBackend") self.assertEqual(res2.__module__, "slo_generator.backends.prometheus") with self.assertRaises(ModuleNotFoundError): get_backend_cls("UndefinedBackend")
def make_reports(slo_config, error_budget_policy, timestamp, client=None, backend_obj=None, backend_method=None, backend_config=None): """Run SLO reports for each step in the Error Budget config. Args: slo_config (dict): SLO configuration. error_budget_policy (dict): Error Budget policy. timestamp (int): UNIX timestamp. client (obj) (optional): Existing metrics backend client. backend_obj (obj) (optional): Backend object (if unset, will be imported dynamically from slo_config). Use when you want to try new backends that are not implemented in the backends/ folder. backend_method (str) (optional): Backend method (if unset, will be imported dynamically from slo_config). Use when you want to try new backends that are not implemented in the backends/ folder. This must return a tuple of (n_good_events[int], n_bad_events[int]). The method must take the following arguments: - timestamp (int): query timestamp. - window (int): query window duration. backend_config (dict) (optional): Backend config. Yields: list: List of SLO measurement results. """ if backend_method: if backend_obj: backend_method = getattr(backend_obj, backend_method) LOGGER.info("Backend method: %s (from kwargs).", backend_method) else: backend_config = slo_config.get('backend', {}) backend_cls = backend_config.get('class') method = backend_config.get('method') backend_obj = utils.get_backend_cls(backend_cls)(client=client, **backend_config) backend_method = getattr(backend_obj, method) LOGGER.info("Backend method: %s (from SLO config file).", backend_cls + '.' + backend_method.__name__) # Loop through steps defined in error budget policy and make measurements for step in error_budget_policy: good_event_count, bad_event_count = backend_method( timestamp=timestamp, window=step['measurement_window_seconds'], **slo_config['backend']) report = make_measurement(slo_config, step, good_event_count, bad_event_count, timestamp) import pprint pprint.pprint(report) yield report
def run_backend(self, config, client=None, delete=False): """Get appropriate backend method from SLO configuration and run it on current SLO config and Error Budget Policy step. Args: config (dict): SLO configuration. client (obj, optional): Backend client initiated beforehand. delete (bool, optional): Set to True if we're running a delete action. Returns: obj: Backend data. """ info = self.__get_info() # Grab backend class and method dynamically. cfg = config.get('backend', {}) cls = cfg.get('class') method = cfg.get('method') excluded_keys = ['class', 'method', 'measurement'] backend_cfg = {k: v for k, v in cfg.items() if k not in excluded_keys} instance = utils.get_backend_cls(cls)(client=client, **backend_cfg) method = getattr(instance, method) LOGGER.debug(f'{info} | ' f'Using backend {cls}.{method.__name__} (from ' f'SLO config file).') # Delete mode activation. if delete and hasattr(instance, 'delete'): method = instance.delete LOGGER.warning(f'{info} | Delete mode enabled.') # Run backend method and return results. data = method(self.timestamp, self.window, config) LOGGER.debug(f'{info} | Backend response: {data}') return data
def test_get_backend_dynamic_cls(self): res1 = get_backend_cls("pathlib.Path") self.assertEqual(res1.__name__, "Path") self.assertEqual(res1.__module__, "pathlib") with self.assertRaises(ModuleNotFoundError): get_exporter_cls("foo.bar.DoesNotExist")