Plugins

The service supports the system of plugins. Plugins must be written in the Python programming language.

Plugin types

There are three sorts of plugins:

  • On event plugin. The plugin is triggered when an event occurs. The plugin should implement a callback function. This function is called on each event of the corresponding type. The set of event types is defined by the service developers.

    Supported event types:

    event type

    description

    monitoring_event

    Event contains monitoring points for sending to a custom monitoring system

    Plugin example:

    """
    Module request monitoring plugin example
    """
    
    import asyncio
    from abc import abstractmethod
    from typing import TypeVar
    
    from aiohttp import ClientSession
    from luna_plugins.base.plugins_meta.base_plugins import BaseEventPlugin
    
    MonitoringPoint = TypeVar("MonitoringPoint")
    LunaApplication = TypeVar("LunaApplication")
    
    
    class BaseRequestMonitoringPlugin(BaseEventPlugin):
        """
        Base class for requests monitoring.
        """
    
        # event name for triggering callback
        eventName = "monitoring_event"
    
        @abstractmethod
        async def flushPointToMonitoring(self, point: "MonitoringPoint", logger) -> None:
            """
            All plugins must realize this method.
    
            This function call after end of request
    
            Args:
                point: point for monitoring
                logger: logger
            """
    
        async def handleEvent(self, points: list["MonitoringPoint"], logger):
            await asyncio.gather(*[self.flushPointToMonitoring(point, logger) for point in points])
    
    
    class RequestMonitoringPlugin(BaseRequestMonitoringPlugin):
        """
        Example plugin sends a request data for monitoring to third-party source.
        Only one instance of this class exist during the program execution.
        """
    
        def __init__(self, app: "LunaApplication"):
            super().__init__(app)
            self.url = "http://127.0.0.1:5020/1/buckets"
            self.session: ClientSession | None = None
            self.bucket = "plugin_test_bucket"
    
        async def close(self):
            """
            Stop plugin.
    
            Close all open connections and ect
            """
            if self.session:
                await self.session.close()
    
        async def initialize(self):
            """
            Initialize plugin.
    
            Close all open connections and ect
            """
            self.session = ClientSession()
            async with self.session.post(f"{self.url}?bucket={self.bucket}") as resp:
                if resp.status not in (201, 409):
                    response = await resp.json()
                    raise RuntimeError(f"failed create bucket, {self.bucket}, response: {response}")
    
        async def flushPointToMonitoring(self, point: "MonitoringPoint", logger) -> None:
            """
            Callback for sending a request monitoring data.
    
            Args:
                point: point for monitoring
                logger: logger
            """
            logger.debug(f"Plugin 'flushPointToMonitoring' get point, series: {point.series}, time: {point.eventTime}")
            msg = {"tags": point.tags, "fields": point.fields}
            async with self.session.post(f"{self.url}/{self.bucket}/objects", json=msg) as resp:
                logger.info(resp.status)
                logger.info(await resp.text())
    

    This plugin demonstrates the sending of a request monitoring data to another service. All monitoring plugins must implement the BaseRequestMonitoringPlugin abstract class.

  • Background plugin. This sort of plugin is intended for background work.

    The background plugin can implement:

    • custom route

    • background monitoring of service resources

    • collaboration of an event plugin and a background plugin (batching monitoring points)

    • connection to other data sources (Redis, RabbitMQ) and their data processing

    Plugin example:

    """
    Module realizes background plugin example
    """
    
    import asyncio
    from asyncio import Task
    
    from luna_plugins.base.plugins_meta.base_plugins import BaseBackgroundHandler, BaseBackgroundPlugin, pluginHTTPResponse
    
    
    class HandlerExample(BaseBackgroundHandler):
        """
        Handler example
        """
    
        async def get(self, request):  # pylint: disable=unused-argument
            """
            Method get example.
    
            Returns:
                response
            """
            return self.response(body="I am teapot", headers={"Content-Type": "text/plain"}, statusCode=418)
    
    
    def anotherHandlerExample(request):  # pylint: disable=unused-argument
        """Standalone handler example"""
        return pluginHTTPResponse(statusCode=200, body="T800", headers={"Content-Type": "text/plain"})
    
    
    class BackgroundPluginExample(BaseBackgroundPlugin):
        """
        Background plugin example.
    
        Create background task and add a route.
        """
    
        def __init__(self, app: "LunaApplication"):
            super().__init__(app)
            self.task: Task | None = None
            self.temperature = 0
    
        async def initialize(self):
            """
            Initialize plugin
            """
            self.addRoute("/teapot", HandlerExample)
            self.addRoute("/teapot/version", anotherHandlerExample, methods={"get"})
    
        async def close(self):
            """
            Stop background process
            Returns:
    
            """
            if self.task:
                self.task.cancel()
    
        async def usefulJob(self):
            """
            Some useful  async work
            """
            while True:
                await asyncio.sleep(1)
                self.temperature = min(100, self.temperature + 1)
                if self.temperature < 100:
                    self.app.ctx.logger.info(f"I boil water, temperature: {self.temperature}")
                else:
                    self.app.ctx.logger.info("boiling water is ready, would you care for a cup of tea?")
    
        async def start(self):
            """
            Run background process
    
            .. warning::
                The function suppose that the process is handle in this coroutine. The coroutine must start
                the process only without awaiting a end of the process
            """
            self.task = asyncio.create_task(self.usefulJob())
    

    This plugin demonstrates background work and implements a route. All background plugins must implement the BaseRequestMonitoringPlugin abstract class.

  • Builtin plugin. This sort of plugin is provided only by the software provider and allows to get access to external services. Each plugin provides access to part or whole resources of external service.

    At this moment available plugins are:
    • luna-streams

    The plugin also check authorization parameters for access to several resources. It is possible to use Luna-Account-Id/Basic/Bearer authorization to make requests just like for all other endpoint of luna-api.

    It is also possible to delimit access permissions using token permission. To make it possible, it needs to create new or update existing token passing custom token permission keys, for example {“permissions”: {“streams”: [“creation”, “view”, “modification”, “deletion”], “groups”: [“create”, “view”]}}

    These parameters will works the same way as for all other luna-api resources.

    Luna-Streams builtin plugin

    Requirements:
    • properly executed Luna-Streams service

    • properly specified plugin settings. For example:

      "LUNA_API_PLUGINS_SETTINGS": {
              "luna-streams": {
                  "luna-streams-address": {"origin": "http://127.0.0.1:5160", "api_version": 1},
                  "luna-streams-timeouts": {"request": 42, "connect": 42}
              }
          }
      }
      

    * prefix is /6/plugins/luna-streams

    luna-api route

    luna-streams route

    description

    permission key

    prefix/version

    /version

    get luna-streams api version

    prefix/docs/spec

    /{api_version}/docs/spec

    get luna-streams spec documentation

    prefix/streams

    /{api_version}/streams

    create new/get/delete existing streams

    streams

    prefix/streams/count

    /{api_version}/streams/count

    get streams count

    streams

    prefix/streams/<stream-id>

    /{api_version}/streams/<stream-id>

    replace/get/delete stream

    streams

    prefix/plugins

    /{api_version}/plugins

    get list of active plugins

    prefix/streams/logs

    /{api_version}/streams/logs

    get/delete streams logs

    streams

    prefix/streams/<stream-id> /preview/handler/live

    /{api_version}/streams/<stream-id> /preview/handler/live

    get stream live preview

    streams streams

    prefix/streams/<stream-id> /preview/handler/frame

    /{api_version}/streams/logs /preview/handler/frame

    get stream frame preview

    streams streams

    prefix/groups

    /{api_version}/groups

    create new/get existing groups

    groups

    prefix/groups/count

    /{api_version}/groups/count

    get groups count

    groups

    prefix/groups/<group-id>

    /{api_version}/groups/<group-id>

    update/get/delete group

    groups

    prefix/linker

    /{api_version}/linker

    link/unlink stream(s) to/from group(s)

    groups

Note

It is allowed to make requests to Luna-Streams v1 or v2 api version using LUNA-STREAMS-API-VERSION header:

  • LUNA-STREAMS-API-VERSION header is not specified, the 1 luna-streams api_version will be selected

  • LUNA-STREAMS-API-VERSION header value is 1, the 1 luna-streams api_version will be selected

  • LUNA-STREAMS-API-VERSION header value is 2, the 2 luna-streams api_version will be selected

  • otherwise, an error will be received as respone

Enable plugin

If the user implements a plugin, the file with the plugin should be added to the luna_api/plugins directory of the service. The plugin filename should be added to the LUNA_API_ACTIVE_PLUGINS configuration setting.