Exemple #1
0
 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
Exemple #2
0
    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) == []
Exemple #3
0
    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) == []
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
    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)
Exemple #7
0
    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)
Exemple #8
0
    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) == []
Exemple #9
0
    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)
Exemple #10
0
    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)
Exemple #11
0
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)