From ec83007972d51eb2000cf0c7c3b8c556f105ea88 Mon Sep 17 00:00:00 2001 From: MoritzWeber Date: Wed, 13 Nov 2024 21:49:03 +0100 Subject: [PATCH] feat: Add custom promtail / logging configuration for tools --- .../sessions/hooks/log_collector.py | 32 +++++++++++-------- backend/capellacollab/tools/models.py | 19 +++++++++++ backend/capellacollab/tools/routes.py | 14 ++++++++ .../src/app/openapi/.openapi-generator/FILES | 2 ++ .../model/logging-configuration-input.ts | 27 ++++++++++++++++ .../model/logging-configuration-output.ts | 27 ++++++++++++++++ frontend/src/app/openapi/model/models.ts | 2 ++ .../openapi/model/session-monitoring-input.ts | 5 +++ .../model/session-monitoring-output.ts | 5 +++ frontend/src/storybook/tool.ts | 4 +++ 10 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 frontend/src/app/openapi/model/logging-configuration-input.ts create mode 100644 frontend/src/app/openapi/model/logging-configuration-output.ts diff --git a/backend/capellacollab/sessions/hooks/log_collector.py b/backend/capellacollab/sessions/hooks/log_collector.py index fea9bb802b..2e0c3e02a3 100644 --- a/backend/capellacollab/sessions/hooks/log_collector.py +++ b/backend/capellacollab/sessions/hooks/log_collector.py @@ -9,6 +9,7 @@ from capellacollab.config import config from capellacollab.sessions import models as sessions_models from capellacollab.sessions.operators import models as operators_models +from capellacollab.tools import models as tools_models from capellacollab.users.workspaces import crud as users_workspaces_crud from . import interface @@ -23,10 +24,7 @@ def post_session_creation_hook( self, request: interface.PostSessionCreationHookRequest, ) -> interface.PostSessionCreationHookResult: - if ( - not self._loki_enabled - or request.db_session.type == sessions_models.SessionType.READONLY - ): + if not self._log_collection_enabled(request.db_session): return interface.PostSessionCreationHookResult() workspaces = users_workspaces_crud.get_workspaces_for_user( @@ -40,7 +38,7 @@ def post_session_creation_hook( data=self._promtail_configuration( username=request.user.name, session_type=request.db_session.type.value, - tool_name=request.db_session.tool.name, + tool=request.db_session.tool, version_name=request.db_session.version.name, ), ) @@ -62,7 +60,7 @@ def post_session_creation_hook( operators_models.PersistentVolume( name="workspace", read_only=False, - container_path=pathlib.PurePosixPath("/var/log/promtail"), + container_path=pathlib.PurePosixPath("/workspace"), volume_name=workspaces[0].pvc_name, ), ] @@ -84,10 +82,7 @@ def pre_session_termination_hook( self, request: interface.PreSessionTerminationHookRequest, ) -> interface.PreSessionTerminationHookResult: - if ( - not self._loki_enabled - or request.session.type == sessions_models.SessionType.READONLY - ): + if not self._log_collection_enabled(request.session): return interface.PostSessionCreationHookResult() request.operator._delete_config_map(name=request.session.id) @@ -95,12 +90,21 @@ def pre_session_termination_hook( return interface.PreSessionTerminationHookResult() + def _log_collection_enabled( + self, session: sessions_models.DatabaseSession + ) -> bool: + return ( + self._loki_enabled + and session.type == sessions_models.SessionType.PERSISTENT + and session.tool.config.monitoring.logging.enabled + ) + @classmethod def _promtail_configuration( cls, username: str, session_type: str, - tool_name: str, + tool: tools_models.DatabaseTool, version_name: str, ) -> dict: cfg = config.k8s.promtail @@ -121,7 +125,7 @@ def _promtail_configuration( } ], "positions": { - "filename": "/var/log/promtail/positions.yaml" + "filename": f"/workspace/.promtail/positions-tool-{tool.id}.yaml" }, "scrape_configs": [ { @@ -139,9 +143,9 @@ def _promtail_configuration( "labels": { "username": username, "session_type": session_type, - "tool": tool_name, + "tool": tool.name, "version": version_name, - "__path__": "/var/log/promtail/**/*.log", + "__path__": tool.config.monitoring.logging.path, }, } ], diff --git a/backend/capellacollab/tools/models.py b/backend/capellacollab/tools/models.py index 46e506b79c..5115dca55b 100644 --- a/backend/capellacollab/tools/models.py +++ b/backend/capellacollab/tools/models.py @@ -15,6 +15,7 @@ from sqlalchemy import orm from capellacollab import core +from capellacollab.config import config from capellacollab.core import database from capellacollab.core import pydantic as core_pydantic from capellacollab.core.database import decorator @@ -246,11 +247,29 @@ class PrometheusConfiguration(core_pydantic.BaseModel): path: str = pydantic.Field(default="/prometheus") +class LoggingConfiguration(core_pydantic.BaseModel): + """Side-car container to push logs to Grafana Loki""" + + enabled: bool = pydantic.Field( + default=config.k8s.promtail.loki_enabled, + description="If enabled, logs will be pushed to Grafana Loki.", + ) + + path: str = pydantic.Field( + default="/workspace/**/*.log", + description="Path to the log files, can be a glob string.", + ) + + class SessionMonitoring(core_pydantic.BaseModel): prometheus: PrometheusConfiguration = pydantic.Field( default=PrometheusConfiguration(), description="Configuration for monitoring and garbage collection.", ) + logging: LoggingConfiguration = pydantic.Field( + default=LoggingConfiguration(), + description="Configuration for side-car logging container.", + ) class ToolModelProvisioning(core_pydantic.BaseModel): diff --git a/backend/capellacollab/tools/routes.py b/backend/capellacollab/tools/routes.py index 79f769c39e..8bfc81bac3 100644 --- a/backend/capellacollab/tools/routes.py +++ b/backend/capellacollab/tools/routes.py @@ -8,9 +8,11 @@ import capellacollab.projects.toolmodels.crud as projects_models_crud import capellacollab.settings.modelsources.t4c.instance.crud as settings_t4c_crud +from capellacollab.config import config from capellacollab.core import database from capellacollab.core import exceptions as core_exceptions from capellacollab.core.authentication import injectables as auth_injectables +from capellacollab.core.logging import exceptions as logging_exceptions from capellacollab.users import models as users_models from . import crud, exceptions, injectables, models @@ -66,6 +68,12 @@ def create_tool( To use this route, the user role `administrator` is required. """ + if ( + body.config.monitoring.logging.enabled + and not config.k8s.promtail.loki_enabled + ): + raise logging_exceptions.GrafanaLokiDisabled() + return crud.create_tool(db, body) @@ -85,6 +93,12 @@ def update_tool( tool: models.DatabaseTool = fastapi.Depends(injectables.get_existing_tool), db: orm.Session = fastapi.Depends(database.get_db), ) -> models.DatabaseTool: + if ( + body.config.monitoring.logging.enabled + and not config.k8s.promtail.loki_enabled + ): + raise logging_exceptions.GrafanaLokiDisabled() + return crud.update_tool(db, tool, body) diff --git a/frontend/src/app/openapi/.openapi-generator/FILES b/frontend/src/app/openapi/.openapi-generator/FILES index c6482e0fe5..e09b205864 100644 --- a/frontend/src/app/openapi/.openapi-generator/FILES +++ b/frontend/src/app/openapi/.openapi-generator/FILES @@ -82,6 +82,8 @@ model/http-connection-method-output.ts model/http-ports-input.ts model/http-ports-output.ts model/http-validation-error.ts +model/logging-configuration-input.ts +model/logging-configuration-output.ts model/memory-resources-input.ts model/memory-resources-output.ts model/message.ts diff --git a/frontend/src/app/openapi/model/logging-configuration-input.ts b/frontend/src/app/openapi/model/logging-configuration-input.ts new file mode 100644 index 0000000000..847e552508 --- /dev/null +++ b/frontend/src/app/openapi/model/logging-configuration-input.ts @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Capella Collaboration + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit the class manually. + + To generate a new version, run `make openapi` in the root directory of this repository. + */ + + + +/** + * Side-car container to push logs to Grafana Loki + */ +export interface LoggingConfigurationInput { + /** + * If enabled, logs will be pushed to Grafana Loki. + */ + enabled?: boolean; + /** + * Path to the log files, can be a glob string. + */ + path?: string; +} + diff --git a/frontend/src/app/openapi/model/logging-configuration-output.ts b/frontend/src/app/openapi/model/logging-configuration-output.ts new file mode 100644 index 0000000000..8e2fa6e33f --- /dev/null +++ b/frontend/src/app/openapi/model/logging-configuration-output.ts @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Capella Collaboration + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit the class manually. + + To generate a new version, run `make openapi` in the root directory of this repository. + */ + + + +/** + * Side-car container to push logs to Grafana Loki + */ +export interface LoggingConfigurationOutput { + /** + * If enabled, logs will be pushed to Grafana Loki. + */ + enabled: boolean; + /** + * Path to the log files, can be a glob string. + */ + path: string; +} + diff --git a/frontend/src/app/openapi/model/models.ts b/frontend/src/app/openapi/model/models.ts index 9c61980e98..45ab795ce4 100644 --- a/frontend/src/app/openapi/model/models.ts +++ b/frontend/src/app/openapi/model/models.ts @@ -61,6 +61,8 @@ export * from './http-ports-input'; export * from './http-ports-output'; export * from './http-validation-error'; export * from './history-event'; +export * from './logging-configuration-input'; +export * from './logging-configuration-output'; export * from './memory-resources-input'; export * from './memory-resources-output'; export * from './message'; diff --git a/frontend/src/app/openapi/model/session-monitoring-input.ts b/frontend/src/app/openapi/model/session-monitoring-input.ts index 6ceaebbfcf..119bea8620 100644 --- a/frontend/src/app/openapi/model/session-monitoring-input.ts +++ b/frontend/src/app/openapi/model/session-monitoring-input.ts @@ -9,6 +9,7 @@ + To generate a new version, run `make openapi` in the root directory of this repository. */ +import { LoggingConfigurationInput } from './logging-configuration-input'; import { PrometheusConfigurationInput } from './prometheus-configuration-input'; @@ -17,5 +18,9 @@ export interface SessionMonitoringInput { * Configuration for monitoring and garbage collection. */ prometheus?: PrometheusConfigurationInput; + /** + * Configuration for side-car logging container. + */ + logging?: LoggingConfigurationInput; } diff --git a/frontend/src/app/openapi/model/session-monitoring-output.ts b/frontend/src/app/openapi/model/session-monitoring-output.ts index b98f2314fa..769bab9d83 100644 --- a/frontend/src/app/openapi/model/session-monitoring-output.ts +++ b/frontend/src/app/openapi/model/session-monitoring-output.ts @@ -9,6 +9,7 @@ + To generate a new version, run `make openapi` in the root directory of this repository. */ +import { LoggingConfigurationOutput } from './logging-configuration-output'; import { PrometheusConfigurationOutput } from './prometheus-configuration-output'; @@ -17,5 +18,9 @@ export interface SessionMonitoringOutput { * Configuration for monitoring and garbage collection. */ prometheus: PrometheusConfigurationOutput; + /** + * Configuration for side-car logging container. + */ + logging: LoggingConfigurationOutput; } diff --git a/frontend/src/storybook/tool.ts b/frontend/src/storybook/tool.ts index 126c7a08ff..839bcc7f7f 100644 --- a/frontend/src/storybook/tool.ts +++ b/frontend/src/storybook/tool.ts @@ -83,6 +83,10 @@ const defaultToolConfig: ToolSessionConfigurationOutput = { prometheus: { path: '/metrics', }, + logging: { + enabled: true, + path: '/workspace/*.log', + }, }, supported_project_types: ['general', 'training'], };