def __init__(self, spec_factory, deploy_queue, config, lifecycle): super(CrdWatcher, self).__init__() self._spec_factory = spec_factory self._deploy_queue = deploy_queue self._watcher = Watcher(FiaasApplication) self._lifecycle = lifecycle self.namespace = config.namespace self.enable_deprecated_multi_namespace_support = config.enable_deprecated_multi_namespace_support
def test_handle_reconnect(self, api_watch_list): events = [_event(0, ADDED, 1)] api_watch_list.side_effect = [events, events] watcher = Watcher(WatchListExample) gen = watcher.watch() _assert_event(next(gen), 0, ADDED, 1) watcher._run_forever = False assert list(gen) == []
def test_handle_changes(self, api_watch_list): events = [_event(0, ADDED, 1), _event(0, MODIFIED, 2)] api_watch_list.side_effect = [events] watcher = Watcher(WatchListExample) gen = watcher.watch() _assert_event(next(gen), 0, ADDED, 1) _assert_event(next(gen), 0, MODIFIED, 2) watcher._run_forever = False assert list(gen) == []
def test_multiple_events(self, api_watch_list): number_of_events = 20 events = [_event(i, ADDED, 1) for i in range(number_of_events)] api_watch_list.side_effect = [events] watcher = Watcher(WatchListExample) gen = watcher.watch() for i in range(number_of_events): _assert_event(next(gen), i, ADDED, 1) watcher._run_forever = False assert list(gen) == [] api_watch_list.assert_called_with(namespace=None)
def test_namespace(self, api_watch_list): namespace = "the-namespace" watcher = Watcher(WatchListExample) def stop_iteration(*args, **kwargs): watcher._run_forever = False return [] api_watch_list.side_effect = stop_iteration gen = watcher.watch(namespace=namespace) assert list(gen) == [] api_watch_list.assert_called_with(namespace=namespace)
def test_handle_reconnect(self, api_watch_list): events = [_event(0, ADDED, 1)] api_watch_list.side_effect = [events, events] gen = Watcher(WatchListExample).watch() _assert_event(next(gen), 0, ADDED, 1) with pytest.raises(StopIteration): next(gen)
def test_namespace(self, api_watch_list): namespace = "the-namespace" api_watch_list.side_effect = [] gen = Watcher(WatchListExample).watch(namespace=namespace) with pytest.raises(StopIteration): next(gen) api_watch_list.assert_called_with(namespace=namespace)
def test_complicated(self, api_watch_list): first = [_event(0, ADDED, 1), _event(1, ADDED, 1), _event(2, ADDED, 1)] second = [ _event(0, ADDED, 1), _event(1, ADDED, 2), _event(2, ADDED, 1), _event(0, MODIFIED, 2) ] third = [ _event(0, ADDED, 2), _event(1, DELETED, 2), _event(2, ADDED, 1), _event(2, MODIFIED, 2) ] fourth = [ _event(0, ADDED, 2), _event(0, ADDED, 1, "other"), _event(0, MODIFIED, 2, "other") ] api_watch_list.side_effect = [first, second, third, fourth] watcher = Watcher(WatchListExample) gen = watcher.watch() # First batch _assert_event(next(gen), 0, ADDED, 1) _assert_event(next(gen), 1, ADDED, 1) _assert_event(next(gen), 2, ADDED, 1) # Second batch _assert_event(next(gen), 1, ADDED, 2) _assert_event(next(gen), 0, MODIFIED, 2) # Third batch _assert_event(next(gen), 1, DELETED, 2) _assert_event(next(gen), 2, MODIFIED, 2) # Fourth batch _assert_event(next(gen), 0, ADDED, 1, "other") _assert_event(next(gen), 0, MODIFIED, 2, "other") watcher._run_forever = False assert list(gen) == []
def test_multiple_events(self, api_watch_list): number_of_events = 20 events = [_event(i, ADDED, 1) for i in range(number_of_events)] api_watch_list.side_effect = [events] gen = Watcher(WatchListExample).watch() for i in range(number_of_events): _assert_event(next(gen), i, ADDED, 1) with pytest.raises(StopIteration): next(gen) api_watch_list.assert_called_with(namespace=None)
def test_complicated(self, api_watch_list): first = [_event(0, ADDED, 1), _event(1, ADDED, 1), _event(2, ADDED, 1)] second = [ _event(0, ADDED, 1), _event(1, ADDED, 2), _event(2, ADDED, 1), _event(0, MODIFIED, 2) ] third = [ _event(0, ADDED, 2), _event(1, DELETED, 2), _event(2, ADDED, 1), _event(2, MODIFIED, 2) ] fourth = [ _event(0, ADDED, 2), _event(0, ADDED, 1, "other"), _event(0, MODIFIED, 2, "other") ] api_watch_list.side_effect = [first, second, third, fourth] gen = Watcher(WatchListExample).watch() # First batch _assert_event(next(gen), 0, ADDED, 1) _assert_event(next(gen), 1, ADDED, 1) _assert_event(next(gen), 2, ADDED, 1) # Second batch _assert_event(next(gen), 1, ADDED, 2) _assert_event(next(gen), 0, MODIFIED, 2) # Third batch _assert_event(next(gen), 1, DELETED, 2) _assert_event(next(gen), 2, MODIFIED, 2) # Fourth batch _assert_event(next(gen), 0, ADDED, 1, "other") _assert_event(next(gen), 0, MODIFIED, 2, "other") with pytest.raises(StopIteration): next(gen)
class CrdWatcher(DaemonThread): def __init__(self, spec_factory, deploy_queue, config, lifecycle): super(CrdWatcher, self).__init__() self._spec_factory = spec_factory self._deploy_queue = deploy_queue self._watcher = Watcher(FiaasApplication) self._lifecycle = lifecycle self.namespace = config.namespace self.enable_deprecated_multi_namespace_support = config.enable_deprecated_multi_namespace_support def __call__(self): while True: if self.enable_deprecated_multi_namespace_support: self._watch(namespace=None) else: self._watch(namespace=self.namespace) def _watch(self, namespace): try: for event in self._watcher.watch(namespace=namespace): self._handle_watch_event(event) except NotFound: self.create_custom_resource_definitions() except Exception: LOG.exception( "Error while watching for changes on FiaasApplications") @classmethod def create_custom_resource_definitions(cls): cls._create("Application", "applications", ("app", "fa"), "fiaas.schibsted.io") cls._create("ApplicationStatus", "application-statuses", ("status", "appstatus", "fs"), "fiaas.schibsted.io") @staticmethod def _create(kind, plural, short_names, group): name = "%s.%s" % (plural, group) metadata = ObjectMeta(name=name) names = CustomResourceDefinitionNames(kind=kind, plural=plural, shortNames=short_names) spec = CustomResourceDefinitionSpec(group=group, names=names, version="v1") definition = CustomResourceDefinition.get_or_create(metadata=metadata, spec=spec) definition.save() LOG.info("Created CustomResourceDefinition with name %s", name) def _handle_watch_event(self, event): if event.type in (WatchEvent.ADDED, WatchEvent.MODIFIED): self._deploy(event.object) elif event.type == WatchEvent.DELETED: self._delete(event.object) else: raise ValueError("Unknown WatchEvent type {}".format(event.type)) def _deploy(self, application): LOG.debug("Deploying %s", application.spec.application) try: deployment_id = application.metadata.labels["fiaas/deployment_id"] set_extras(app_name=application.spec.application, namespace=application.metadata.namespace, deployment_id=deployment_id) except (AttributeError, KeyError, TypeError): raise ValueError( "The Application {} is missing the 'fiaas/deployment_id' label" .format(application.spec.application)) try: repository = _repository(application) self._lifecycle.initiate(app_name=application.spec.application, namespace=application.metadata.namespace, deployment_id=deployment_id, repository=repository) app_spec = self._spec_factory( name=application.spec.application, image=application.spec.image, app_config=application.spec.config, teams=[], tags=[], deployment_id=deployment_id, namespace=application.metadata.namespace) set_extras(app_spec) self._deploy_queue.put(DeployerEvent("UPDATE", app_spec)) LOG.debug("Queued deployment for %s", application.spec.application) except (InvalidConfiguration, YAMLError): LOG.exception("Failed to create app spec from fiaas config file") self._lifecycle.failed(app_name=application.spec.application, namespace=application.metadata.namespace, deployment_id=deployment_id, repository=repository) def _delete(self, application): app_spec = self._spec_factory(name=application.spec.application, image=application.spec.image, app_config=application.spec.config, teams=[], tags=[], deployment_id="deletion", namespace=application.metadata.namespace) set_extras(app_spec) self._deploy_queue.put(DeployerEvent("DELETE", app_spec)) LOG.debug("Queued delete for %s", application.spec.application)
class TprWatcher(DaemonThread): def __init__(self, spec_factory, deploy_queue, config, lifecycle): super(TprWatcher, self).__init__() self._spec_factory = spec_factory self._deploy_queue = deploy_queue self._watcher = Watcher(PaasbetaApplication) self._lifecycle = lifecycle self.namespace = config.namespace self.enable_deprecated_multi_namespace_support = config.enable_deprecated_multi_namespace_support def __call__(self): while True: if self.enable_deprecated_multi_namespace_support: self._watch(namespace=None) else: self._watch(namespace=self.namespace) def _watch(self, namespace): try: for event in self._watcher.watch(namespace=namespace): self._handle_watch_event(event) except NotFound: self.create_third_party_resource() except Exception: LOG.exception("Error while watching for changes on PaasbetaApplications") @staticmethod def create_third_party_resource(): metadata = ObjectMeta(name="paasbeta-application.schibsted.io") paasbeta_application_resource = ThirdPartyResource.get_or_create( metadata=metadata, description='A paas application definition', versions=[APIVersion(name='v1beta')]) paasbeta_application_resource.save() LOG.debug("Created ThirdPartyResource with name PaasbetaApplication") metadata = ObjectMeta(name="paasbeta-status.schibsted.io") paasbeta_status_resource = ThirdPartyResource.get_or_create( metadata=metadata, description='A paas application status', versions=[APIVersion(name='v1beta')]) paasbeta_status_resource.save() LOG.debug("Created ThirdPartyResource with name PaasbetaStatus") def _handle_watch_event(self, event): if event.type in (WatchEvent.ADDED, WatchEvent.MODIFIED): self._deploy(event.object) elif event.type == WatchEvent.DELETED: self._delete(event.object) else: raise ValueError("Unknown WatchEvent type {}".format(event.type)) def _deploy(self, application): LOG.debug("Deploying %s", application.spec.application) try: deployment_id = application.metadata.labels["fiaas/deployment_id"] set_extras(app_name=application.spec.application, namespace=application.metadata.namespace, deployment_id=deployment_id) except (AttributeError, KeyError, TypeError): raise ValueError("The Application {} is missing the 'fiaas/deployment_id' label".format( application.spec.application)) try: repository = _repository(application) self._lifecycle.initiate(app_name=application.spec.application, namespace=application.metadata.namespace, deployment_id=deployment_id, repository=repository) app_spec = self._spec_factory( name=application.spec.application, image=application.spec.image, app_config=application.spec.config, teams=[], tags=[], deployment_id=deployment_id, namespace=application.metadata.namespace ) set_extras(app_spec) self._deploy_queue.put(DeployerEvent("UPDATE", app_spec)) LOG.debug("Queued deployment for %s", application.spec.application) except (InvalidConfiguration, YAMLError): LOG.exception("Failed to create app spec from fiaas config file") self._lifecycle.failed(app_name=application.spec.application, namespace=application.metadata.namespace, deployment_id=deployment_id, repository=repository) def _delete(self, application): app_spec = self._spec_factory( name=application.spec.application, image=application.spec.image, app_config=application.spec.config, teams=[], tags=[], deployment_id="deletion", namespace=application.metadata.namespace, ) set_extras(app_spec) self._deploy_queue.put(DeployerEvent("DELETE", app_spec)) LOG.debug("Queued delete for %s", application.spec.application)