예제 #1
0
 def event(self):
     return Event(
         name="REQUEST_CREATED",
         error=False,
         payload={"request": "request"},
         metadata={},
     )
예제 #2
0
    def prepare(self):
        """Called before each verb handler"""

        # Used for recording prometheus metrics.
        # We keep this time in seconds, also time-zone does not matter
        # because we are just calculating a duration.
        self.request.created_time = time.time()

        # This is used for sending event notifications
        self.request.event = Event()
        self.request.event_extras = {}

        content_type = self.request.headers.get('content-type', '')
        if self.request.method.upper() in ['POST', 'PATCH'] and content_type:
            content_type = content_type.split(';')

            self.request.mime_type = content_type[0]
            if self.request.mime_type not in [
                    'application/json', 'application/x-www-form-urlencoded'
            ]:
                raise ModelValidationError(
                    'Unsupported or missing content-type header')

            # Attempt to parse out the charset and decode the body, default to utf-8
            charset = 'utf-8'
            if len(content_type) > 1:
                search_result = self.charset_re.search(content_type[1])
                if search_result:
                    charset = search_result.group(1)
            self.request.charset = charset
            self.request.decoded_body = self.request.body.decode(charset)
예제 #3
0
def startup():
    """Do startup things.

    This is the first thing called from within the ioloop context.
    """
    global event_publishers

    # Ensure we have a mongo connection
    yield _progressive_backoff(
        partial(bg_utils.setup_database, config),
        'Unable to connect to mongo, is it started?'
    )

    logger.info(
        'Starting metrics server on %s:%d' %
        (config.web.host, config.metrics.port)
    )
    start_http_server(config.metrics.port)

    logger.info(
        'Starting HTTP server on %s:%d' %
        (config.web.host, config.web.port)
    )
    server.listen(config.web.port, config.web.host)

    logger.info("Starting event publishers")
    event_publishers = _setup_event_publishers(client_ssl)

    logger.info('Starting scheduler')
    request_scheduler.start()

    logger.debug("Publishing application startup event")
    event_publishers.publish_event(Event(name=Events.BREWVIEW_STARTED.name))

    brew_view.logger.info("Application is started. Hello!")
예제 #4
0
    def publish_event(self, *args, **kwargs):
        """Publish a new event

        Args:
            *args: If a positional argument is given it's assumed to be an
                Event and will be used
            **kwargs: Will be used to construct a new Event to publish if no
                Event is given in the positional arguments

        Keyword Args:
            _publishers (Optional[List[str]]): List of publisher names.
                If given the Event will only be published to the specified
                publishers. Otherwise all publishers known to Beergarden will
                be used.

        Returns:
            bool: True if the publish was successful

        """
        publishers = kwargs.pop("_publishers", None)

        event = args[0] if args else Event(**kwargs)

        return self.client.post_event(self.parser.serialize_event(event),
                                      publishers=publishers)
예제 #5
0
def shutdown():
    """Do shutdown things

    This still operates within the ioloop, so stopping it should be the last
    thing done.

    Because things in startup aren't guaranteed to have been run we need to be
    careful about checking to make sure things actually need to be shut down.

    This execution is normally scheduled by the signal handler.
    """
    if request_scheduler.running:
        logger.info('Stopping scheduler')
        request_scheduler.shutdown(wait=False)

    logger.info("Stopping HTTP server")
    server.stop()

    if event_publishers:
        logger.debug("Publishing application shutdown event")
        event_publishers.publish_event(Event(name=Events.BREWVIEW_STOPPED.name))

        logger.info("Shutting down event publishers")
        yield list(filter(
            lambda x: isinstance(x, Future),
            event_publishers.shutdown()
        ))

    logger.info("Stopping IO loop")
    io_loop.add_callback(io_loop.stop)
예제 #6
0
    def prepare(self):
        """Called before each verb handler"""

        # Used for calculating request handling duration
        self.request.created_time = datetime.datetime.utcnow()

        # This is used for sending event notifications
        self.request.event = Event()
        self.request.event_extras = {}

        content_type = self.request.headers.get("content-type", "")
        if self.request.method.upper() in ["POST", "PATCH"] and content_type:
            content_type = content_type.split(";")

            self.request.mime_type = content_type[0]
            if self.request.mime_type not in [
                    "application/json",
                    "application/x-www-form-urlencoded",
            ]:
                raise ModelValidationError(
                    "Unsupported or missing content-type header")

            # Attempt to parse out the charset and decode the body, default to utf-8
            charset = "utf-8"
            if len(content_type) > 1:
                search_result = self.charset_re.search(content_type[1])
                if search_result:
                    charset = search_result.group(1)
            self.request.charset = charset
            self.request.decoded_body = self.request.body.decode(charset)
예제 #7
0
    def test_handle_event_for_user_updated(self):
        role_assignments = [{
            "role_name": "role1",
            "domain": {
                "scope": "Global"
            }
        }]
        user_updated_result = {
            "garden": "garden1",
            "user": {
                "username": "******",
                "role_assignments": role_assignments
            },
        }

        event = Event(
            name=Events.USER_UPDATED.name,
            garden="garden1",
            metadata=user_updated_result,
        )

        assert len(
            RemoteUser.objects.filter(username="******", garden="garden1")) == 0

        handle_event(event)
        remote_user = RemoteUser.objects.get(username="******",
                                             garden="garden1")

        assert remote_user.role_assignments == role_assignments
예제 #8
0
async def shutdown():
    """Do shutdown things

    This still operates within the ioloop, so stopping it should be the last
    thing done.

    Because things in startup aren't guaranteed to have been run we need to be
    careful about checking to make sure things actually need to be shut down.

    This execution is normally scheduled by the signal handler.
    """

    logger.debug("Stopping server for new HTTP connections")
    server.stop()

    logger.debug("Stopping forward processing")
    beer_garden.router.forward_processor.stop()

    # This will almost definitely not be published to the websocket, because it would
    # need to make it up to the main process and back down into this process. We just
    # publish this here in case the main process is looking for it.
    publish(
        Event(name=Events.ENTRY_STOPPED.name, metadata={"entry_point_type": "HTTP"})
    )

    # We need to do this before the scheduler shuts down completely in order to kick any
    # currently waiting request creations
    logger.debug("Closing all open HTTP connections")
    await server.close_all_connections()

    logger.debug("Stopping IO loop")
    io_loop.add_callback(io_loop.stop)
예제 #9
0
    def test_publish_skips_events_on_blocklist(self):
        yield self.ws_connect()

        EventSocket.write_message = Mock()
        event = Event(name=WEBSOCKET_EVENT_TYPE_BLOCKLIST[0])
        EventSocket.publish(event)

        assert EventSocket.write_message.called is False
예제 #10
0
    def initialize(self):
        """Actually construct all the various component pieces"""

        self.scheduler = self._setup_scheduler()

        load_plugin_log_config()

        plugin_config = config.get("plugin")
        self.helper_threads = [
            HelperThread(
                StatusMonitor,
                timeout_seconds=plugin_config.status_timeout,
                heartbeat_interval=plugin_config.status_heartbeat,
            )
        ]

        # Only want to run the MongoPruner if it would do anything
        tasks, run_every = db.prune_tasks(**config.get("db.ttl"))
        if run_every:
            self.helper_threads.append(
                HelperThread(
                    db.get_pruner(), tasks=tasks, run_every=timedelta(minutes=run_every)
                )
            )

        metrics_config = config.get("metrics")
        if metrics_config.prometheus.enabled:
            self.helper_threads.append(
                HelperThread(
                    PrometheusServer,
                    metrics_config.prometheus.host,
                    metrics_config.prometheus.port,
                )
            )

        beer_garden.router.forward_processor = QueueListener(
            action=beer_garden.router.forward, name="forwarder"
        )

        self.mp_manager = self._setup_multiprocessing_manager()

        beer_garden.local_plugins.manager.lpm_proxy = self.mp_manager.PluginManager()

        self.entry_manager = beer_garden.api.entry_point.Manager()

        beer_garden.events.manager = self._setup_events_manager()

        file_event = Event(name=Events.PLUGIN_LOGGER_FILE_CHANGE.name)
        self.plugin_log_config_observer = MonitorFile(
            path=config.get("plugin.logging.config_file"),
            create_event=file_event,
            modify_event=file_event,
        )
        self.plugin_local_log_config_observer = MonitorFile(
            path=config.get("plugin.local.logging.config_file"),
            create_event=file_event,
            modify_event=file_event,
        )
예제 #11
0
def run(ep_conn):
    conn_manager = StompManager(ep_conn)

    _setup_event_handling(conn_manager)
    _setup_operation_forwarding()

    entry_config = config.get("entry.stomp")
    parent_config = config.get("parent.stomp")
    garden_name = config.get("garden.name")

    if entry_config.get("enabled"):
        conn_manager.add_connection(stomp_config=entry_config,
                                    name=f"{garden_name}_entry",
                                    is_main=True)

    if parent_config.get("enabled"):
        conn_manager.add_connection(stomp_config=parent_config,
                                    name=f"{garden_name}_parent",
                                    is_main=True)

    for garden in get_gardens(include_local=False):
        if garden.name != garden_name and garden.connection_type:
            if garden.connection_type.casefold() == "stomp":
                connection_params = garden.connection_params.get("stomp", {})
                connection_params["send_destination"] = None
                conn_manager.add_connection(stomp_config=connection_params,
                                            name=garden.name)

    conn_manager.start()

    logger.info("Stomp entry point started")

    publish(
        Event(name=Events.ENTRY_STARTED.name,
              metadata={"entry_point_type": "STOMP"}))

    while not shutdown_event.wait(10):
        for name, info in conn_manager.conn_dict.items():
            connection = info.get("conn")

            if connection:
                logger.debug(f"{name}: Checking connection")
                if not connection.is_connected():
                    logger.debug(f"{name}: Attempting to reconnect")

                    if connection.connect():
                        logger.debug(f"{name}: Reconnect successful")
                    else:
                        logger.debug(f"{name}: Reconnect failed")

    conn_manager.shutdown()
    conn_manager.stop()
    conn_manager.join(5)

    logger.debug("Stopping forward processing")
    beer_garden.router.forward_processor.stop()
예제 #12
0
class TestSendEventToParent(object):
    event = Event(
        payload_type="Request",
        payload=Request(
            system="system_test",
            command="command_test",
            namespace="test",
        ),
    )

    def config_get(self, config_name):
        return []

    @pytest.fixture()
    def command_blocklist(self):
        blocklist = CommandPublishingBlockList(
            namespace=self.event.payload.namespace,
            system=self.event.payload.system,
            command=self.event.payload.command,
        ).save()

        yield blocklist
        blocklist.delete()

    def test_command_exists_in_blocklist(self, command_blocklist, monkeypatch):
        monkeypatch.setattr(config, "get", self.config_get)

        assert event_blocklisted(self.event)

    def test_command_missing_in_blocklist(self, monkeypatch):
        monkeypatch.setattr(config, "get", self.config_get)

        assert not event_blocklisted(self.event)

    def test_event_not_request(self, monkeypatch):
        monkeypatch.setattr(config, "get", self.config_get)
        event = Event(name="ENTRY_STARTED")

        assert not event_blocklisted(event)

    def test_can_send_event_error(self, monkeypatch):
        monkeypatch.setattr(config, "get", self.config_get)
        event = Event(name="REQUEST_CREATE", error=True)

        assert event_blocklisted(event)

    def test_can_send_event_to_parent(self, monkeypatch):
        monkeypatch.setattr(config, "get", self.config_get)

        assert not event_blocklisted(self.event)

    def test_can_send_event_to_parent_blocklist(self, command_blocklist, monkeypatch):
        monkeypatch.setattr(config, "get", self.config_get)

        assert event_blocklisted(self.event)
예제 #13
0
    def test_publish_auth_enabled_publishes_event_without_payload_type(self):
        ws_client = yield self.ws_connect()
        yield ws_client.read_message()  # Read the AUTHORIZATION_REQUIRED message

        event = Event(name="ENTRY_STARTED")
        EventSocket.publish(event)

        response = yield ws_client.read_message()
        ws_client.close()
        response_dict = json.loads(response)

        assert response_dict["payload"] is None
        assert response_dict["name"] == event.name
예제 #14
0
def publish(event: Event) -> None:
    """Convenience method for publishing events

    All this does is place the event on the queue for the process-wide manager to pick
    up and process.

    Args:
        event: The event to publish

    Returns:
        None
    """
    try:
        # Do some formatting / tweaking
        if not event.garden:
            event.garden = config.get("garden.name")
        if not event.timestamp:
            event.timestamp = datetime.now(timezone.utc)

        return manager.put(event)
    except Exception as ex:
        logger.exception(f"Error publishing event: {ex}")
예제 #15
0
    def shutdown(self):
        self.logger.debug("Disconnecting connections")
        for value in self.conn_dict.values():
            value["conn"].disconnect()

        # This will almost definitely not be published because
        # it would need to make it up to the main process and
        # back down into this process. We just publish this
        # here in case the main process is looking for it.
        publish(
            Event(
                name=Events.ENTRY_STOPPED.name,
                metadata={"entry_point_type": "STOMP"},
            ), )
예제 #16
0
def rescan(*args, **kwargs) -> List[Runner]:
    """Scans plugin directory and starts any new runners"""
    new_runners = lpm_proxy.scan_path(*args, **kwargs)

    for runner in new_runners:
        publish(
            Event(
                name=Events.RUNNER_STARTED.name,
                payload_type=Runner.__name__,
                payload=runner,
            )
        )

    return new_runners
예제 #17
0
    def process(self, event: Event):
        # TODO - This shouldn't be set here
        event.garden = conf.get("garden.name")

        if not event_blocklisted(event):
            try:
                operation = Operation(
                    operation_type="PUBLISH_EVENT", model=event, model_type="Event"
                )
                self._ez_client.forward(operation)
            except RequestException as ex:
                self.logger.error(f"Error while publishing event to parent: {ex}")

                self._connected = False
                self._reconnect()
예제 #18
0
    def test_status_updated_at_preserved_on_child_garden_requests(
        self, child_garden_request
    ):
        status_updated_at = datetime.utcnow() - timedelta(days=1)
        status_updated_at = status_updated_at.replace(microsecond=0)
        child_garden_request.status = "SUCCESS"
        child_garden_request.status_updated_at = status_updated_at
        request_event = Event(
            payload=child_garden_request, name=Events.REQUEST_UPDATED.name
        )

        beer_garden.requests.handle_event(request_event)

        updated_request = Request.objects.get(id=child_garden_request.id)

        assert updated_request.status_updated_at == status_updated_at
예제 #19
0
async def startup():
    """Do startup things.

    This is the first thing called from within the ioloop context.
    """
    global anonymous_principal

    http_config = config.get("entry.http")
    logger.debug(
        f"Starting HTTP server on {http_config.host}:{http_config.port}")
    server.listen(http_config.port, http_config.host)

    logger.info("Http entry point started")

    publish(
        Event(name=Events.ENTRY_STARTED.name,
              metadata={"entry_point_type": "HTTP"}))
예제 #20
0
    def publish_event(self, *args, **kwargs):
        """Publish a new event by POSTing

        :param args: The Event to create
        :param _publishers: Optional list of specific publishers. If None all publishers will be
            used.
        :param kwargs: If no Event is given in the *args, on will be constructed from the kwargs
        :return: The response
        """
        publishers = kwargs.pop('_publishers', None)
        json_event = self.parser.serialize_event(args[0] if args else Event(
            **kwargs))

        response = self.client.post_event(json_event, publishers=publishers)

        if response.ok:
            return True
        else:
            self._handle_response_failure(response)
예제 #21
0
def _publish_failed_forward(operation: Operation = None,
                            error_message: str = None,
                            event_name: str = None):
    if operation.operation_type == "REQUEST_CREATE":
        complete_request(
            operation.model.id,
            status="ERROR",
            output=error_message,
            error_class=event_name,
        )

    publish(
        Event(
            name=event_name,
            payload_type="Operation",
            payload=operation,
            error_message=error_message,
        ))

    raise RoutingRequestException(error_message)
예제 #22
0
def _async_callback(task, event_type=None):
    event = Event(name=event_type.name)

    try:
        result = task.result()

        event.payload_type = result.__class__.__name__
        event.payload = result
    except Exception as ex:
        event.error = True
        event.error_message = str(ex)
    finally:
        try:
            publish(event)
        except Exception as ex:
            logger.exception(f"Error publishing event: {ex}")
예제 #23
0
async def startup():
    """Do startup things.

    This is the first thing called from within the ioloop context.
    """
    global anonymous_principal

    # Need to wait until after mongo connection established to load
    anonymous_principal = load_anonymous()

    http_config = config.get("entry.http")
    logger.debug(f"Starting HTTP server on {http_config.host}:{http_config.port}")
    server.listen(http_config.port, http_config.host)

    logger.debug("Starting forward processor")
    beer_garden.router.forward_processor.start()

    beer_garden.api.http.logger.info("Http entry point is started. Hello!")

    publish(
        Event(name=Events.ENTRY_STARTED.name, metadata={"entry_point_type": "HTTP"})
    )
예제 #24
0
def rescan(*args, **kwargs) -> List[Runner]:
    """Scan plugin directory and start any new runners.

    Args:
        *args: Arguments to pass to ``scan_path`` in the PluginManager object.
        **kwargs: Keyword arguments to pass to ``scan_path`` in the PluginManager
            object.

    Returns:
        A list of the new runners
    """
    new_runners = lpm_proxy.scan_path(*args, **kwargs)

    for the_runner in new_runners:
        publish(
            Event(
                name=Events.RUNNER_STARTED.name,
                payload_type=Runner.__name__,
                payload=the_runner,
            ))

    return new_runners
예제 #25
0
def shutdown():
    """Do shutdown things

    This still operates within the ioloop, so stopping it should be the last
    thing done.

    Because things in startup aren't guaranteed to have been run we need to be
    careful about checking to make sure things actually need to be shut down.

    This execution is normally scheduled by the signal handler.
    """
    if request_scheduler.running:
        logger.info("Pausing scheduler - no more jobs will be run")
        yield request_scheduler.pause()

    logger.info("Stopping server for new HTTP connections")
    server.stop()

    if event_publishers:
        logger.debug("Publishing application shutdown event")
        event_publishers.publish_event(
            Event(name=Events.BREWVIEW_STOPPED.name))

        logger.info("Shutting down event publishers")
        yield list(
            filter(lambda x: isinstance(x, Future),
                   event_publishers.shutdown()))

    # We need to do this before the scheduler shuts down completely in order to kick any
    # currently waiting request creations
    logger.info("Closing all open HTTP connections")
    yield server.close_all_connections()

    if request_scheduler.running:
        logger.info("Shutting down scheduler")
        yield request_scheduler.shutdown(wait=False)

    logger.info("Stopping IO loop")
    io_loop.add_callback(io_loop.stop)
예제 #26
0
def startup():
    """Do startup things.

    This is the first thing called from within the ioloop context.
    """
    global event_publishers, anonymous_principal

    # Ensure we have a mongo connection
    logger.info("Checking for Mongo connection")
    yield _progressive_backoff(partial(setup_database, config),
                               "Unable to connect to mongo, is it started?")

    # Need to wait until after mongo connection established to load
    anonymous_principal = load_anonymous()

    logger.info("Starting event publishers")
    event_publishers = _setup_event_publishers(client_ssl)

    logger.info("Initializing metrics")
    initialize_counts()

    logger.info("Starting metrics server on %s:%d" %
                (config.web.host, config.metrics.port))
    start_http_server(config.metrics.port)

    logger.info("Starting HTTP server on %s:%d" %
                (config.web.host, config.web.port))
    server.listen(config.web.port, config.web.host)

    logger.info("Starting scheduler")
    request_scheduler.start()

    logger.debug("Publishing application startup event")
    event_publishers.publish_event(Event(name=Events.BREWVIEW_STARTED.name))

    brew_view.logger.info("Application is started. Hello!")
예제 #27
0
    def wrapper(wrapped, _, args, kwargs):
        # Allows for conditionally disabling publishing
        _publish_success = kwargs.pop("_publish_success", True)
        _publish_error = kwargs.pop("_publish_error", True)

        event = Event(name=event_type.name)

        try:
            result = wrapped(*args, **kwargs)

            event.payload_type = result.__class__.__name__
            event.payload = result

            return result
        except Exception as ex:
            event.error = True
            event.error_message = str(ex)

            raise
        finally:
            if (not event.error and _publish_success) or (event.error
                                                          and _publish_error):
                publish(event)
예제 #28
0
def bg_event(event_dict, ts_dt):
    """An event as a model."""
    dict_copy = copy.deepcopy(event_dict)
    dict_copy['timestamp'] = ts_dt
    return Event(**dict_copy)
예제 #29
0
    def test_can_send_event_error(self, monkeypatch):
        monkeypatch.setattr(config, "get", self.config_get)
        event = Event(name="REQUEST_CREATE", error=True)

        assert event_blocklisted(event)
예제 #30
0
    def test_event_not_request(self, monkeypatch):
        monkeypatch.setattr(config, "get", self.config_get)
        event = Event(name="ENTRY_STARTED")

        assert not event_blocklisted(event)