From 4b71fa95412716fb644bac2f7ba60cff9b95b60b Mon Sep 17 00:00:00 2001 From: MoritzWeber Date: Fri, 13 Sep 2024 10:33:22 +0200 Subject: [PATCH] feat: Several improvements for caching of diagram cache This commit requests changes on the PR #1689. In particular, the following has changed: - Replace cache key from path and revision with git_model_id (database id). The unique database ID of the Git model in the Collaboration Manager has to be part of the cache key. For safety reasons, I'd not share a cache between projects even though they link to the same repository. This doesn't really matter currently since we make new requests against the Git API every time, but we can't assure that for the future. - An attack could look like: - Project 1 links Git repository "https://example.com" to a model. - Project 2 links the same Git repository to a model with a different token. - The token of project 1 expires. Project 2 keeps updating the cache with a valid token. - Users of project 1 can still access the cache for project 2 without a valid token. - Since a git_model only has one revision, it's safe to drop the revision for artifacts. The revision is only relevant for files from the repository. In those cases, the value was passed as parameter every time. Therefore, I've removed it from the Cache object. - For license reasons, I switched from Redis to Valkey. - Use a Kubernetes secret for the valkey password. - For REST compatibility, I've changed the resource name of "empty_cache" to "cache". - I've reenabled the model badge error handling. - Added a new function loadDiagramCacheMetadata in model-diagram-dialog.component.ts to reduce duplicated code. - Added the job id to the diagram cache code snippet. - Changed the styling of the "Clear cache" button. --- .pre-commit-config.yaml | 1 - backend/Makefile | 16 +++--- backend/capellacollab/config/models.py | 6 +-- .../capellacollab/core/database/__init__.py | 6 +-- .../projects/toolmodels/modelbadge/routes.py | 4 +- .../modelsources/git/handler/cache.py | 50 ++++++++---------- .../modelsources/git/handler/factory.py | 2 + .../modelsources/git/handler/handler.py | 5 +- .../toolmodels/modelsources/git/routes.py | 4 +- backend/pyproject.toml | 5 +- backend/tests/projects/toolmodels/conftest.py | 10 ++-- .../projects/toolmodels/test_diagrams.py | 10 ++-- .../projects/toolmodels/test_model_badge.py | 4 +- .../api/projects-models-git.service.ts | 2 +- .../model-diagram-code-block.component.ts | 7 ++- .../model-diagram-code-block.stories.ts | 19 +++++++ .../model-diagram-dialog.component.html | 9 ++-- .../model-diagram-dialog.component.ts | 51 ++++++++----------- .../model-complexity-badge.component.ts | 7 +++ .../session-overview.component.html | 1 - helm/config/backend.yaml | 4 +- helm/templates/backend/redis.configmap.yaml | 13 ----- .../valkey.deployment.yaml} | 39 +++++++------- .../valkey.disruptionsbudget.yaml} | 4 +- helm/templates/valkey/valkey.secret.yaml | 13 +++++ .../valkey.service.yaml} | 10 ++-- helm/values.yaml | 6 +-- 27 files changed, 162 insertions(+), 146 deletions(-) delete mode 100644 helm/templates/backend/redis.configmap.yaml rename helm/templates/{backend/redis.deployment.yaml => valkey/valkey.deployment.yaml} (57%) rename helm/templates/{backend/redis.disruptionsbudget.yaml => valkey/valkey.disruptionsbudget.yaml} (69%) create mode 100644 helm/templates/valkey/valkey.secret.yaml rename helm/templates/{backend/redis.service.yaml => valkey/valkey.service.yaml} (55%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e6a652b11..a0b55263b0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -65,7 +65,6 @@ repos: - capellambse - typer - types-lxml - - types-redis - repo: local hooks: - id: pylint diff --git a/backend/Makefile b/backend/Makefile index fa193c406a..9c17bcf70d 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -9,8 +9,7 @@ VENV = .venv HOST ?= 127.0.0.1 -REDIS_PORT = 6379 -REDIS_INSIGHT_PORT = 8001 +VALKEY_PORT = 6379 DATABASE_LOAD_FILE ?= ../local/load.sql DATABASE_SAVE_DIR ?= ../local @@ -34,13 +33,12 @@ database: -e POSTGRES_DB=$(DB_NAME) \ postgres -redis: - docker start redis || \ +valkey: + docker start valkey || \ docker run -d \ - --name redis \ - -p $(REDIS_PORT):6379 \ - -p $(REDIS_INSIGHT_PORT):8001 \ - redis/redis-stack:latest + --name valkey \ + -p $(VALKEY_PORT):6379 \ + valkey/valkey:latest app: if [ -d "$(VENV)/bin" ]; then @@ -68,7 +66,7 @@ install: openapi: CAPELLACOLLAB_SKIP_OPENAPI_ERROR_RESPONSES=1 $(VENV)/bin/python -m capellacollab.cli openapi generate /tmp/openapi.json -dev: database app +dev: database valkey app cleanup: docker stop capella-collab-postgres diff --git a/backend/capellacollab/config/models.py b/backend/capellacollab/config/models.py index 923f26585f..db19535c38 100644 --- a/backend/capellacollab/config/models.py +++ b/backend/capellacollab/config/models.py @@ -288,8 +288,8 @@ class DatabaseConfig(BaseConfig): ) -class RedisConfig(BaseConfig): - url: str = pydantic.Field(default="redis://localhost:6379/0") +class ValkeyConfig(BaseConfig): + url: str = pydantic.Field(default="valkey://localhost:6379/0") class InitialConfig(BaseConfig): @@ -342,7 +342,7 @@ class AppConfig(BaseConfig): authentication: AuthenticationConfig = AuthenticationConfig() prometheus: PrometheusConfig = PrometheusConfig() database: DatabaseConfig = DatabaseConfig() - redis: RedisConfig = RedisConfig() + valkey: ValkeyConfig = ValkeyConfig() initial: InitialConfig = InitialConfig() logging: LoggingConfig = LoggingConfig() requests: RequestsConfig = RequestsConfig() diff --git a/backend/capellacollab/core/database/__init__.py b/backend/capellacollab/core/database/__init__.py index 6dfd9f71b2..0e7bc67fe4 100644 --- a/backend/capellacollab/core/database/__init__.py +++ b/backend/capellacollab/core/database/__init__.py @@ -5,8 +5,8 @@ import typing as t import pydantic -import redis import sqlalchemy as sa +import valkey from sqlalchemy import orm from sqlalchemy.dialects import postgresql @@ -38,8 +38,8 @@ def get_db() -> t.Iterator[orm.Session]: @functools.lru_cache -def get_redis() -> redis.Redis: - return redis.Redis.from_url(config.redis.url, decode_responses=True) +def get_valkey() -> valkey.Valkey: + return valkey.Valkey.from_url(config.valkey.url, decode_responses=True) def patch_database_with_pydantic_object( diff --git a/backend/capellacollab/projects/toolmodels/modelbadge/routes.py b/backend/capellacollab/projects/toolmodels/modelbadge/routes.py index cde79e2d69..942e995f43 100644 --- a/backend/capellacollab/projects/toolmodels/modelbadge/routes.py +++ b/backend/capellacollab/projects/toolmodels/modelbadge/routes.py @@ -41,7 +41,9 @@ async def get_model_complexity_badge( logger: logging.LoggerAdapter = fastapi.Depends(log.get_request_logger), ): try: - file = await git_handler.get_file("model-complexity-badge.svg") + file = await git_handler.get_file( + "model-complexity-badge.svg", git_handler.revision + ) return responses.SVGResponse(content=file[1]) except Exception: logger.debug( diff --git a/backend/capellacollab/projects/toolmodels/modelsources/git/handler/cache.py b/backend/capellacollab/projects/toolmodels/modelsources/git/handler/cache.py index 592d11a4c1..f783fa6108 100644 --- a/backend/capellacollab/projects/toolmodels/modelsources/git/handler/cache.py +++ b/backend/capellacollab/projects/toolmodels/modelsources/git/handler/cache.py @@ -6,19 +6,16 @@ from capellacollab.core import database -class GitRedisCache: - def __init__(self, path: str, revision: str) -> None: - self._redis = database.get_redis() - self.path = path.replace(":", "-") - self.revision = revision +class GitValkeyCache: + def __init__(self, git_model_id: int) -> None: + self._valkey = database.get_valkey() + self.git_model_id = git_model_id super().__init__() def get_file_data( - self, file_path: str, revision: str | None = None + self, file_path: str, revision: str ) -> tuple[datetime.datetime, bytes] | None: - revision = revision or self.revision - - file_data = self._redis.hmget( + file_data = self._valkey.hmget( name=self._get_file_key(file_path, revision), keys=["last_updated", "content"], ) @@ -31,7 +28,7 @@ def get_file_data( def get_artifact_data( self, job_id: str | int, file_path: str ) -> tuple[datetime.datetime, bytes] | None: - artifact_data = self._redis.hmget( + artifact_data = self._valkey.hmget( name=self._get_artifact_key(job_id, file_path), keys=["started_at", "content"], ) @@ -46,19 +43,17 @@ def put_file_data( file_path: str, last_updated: datetime.datetime, content: bytes, - revision: str | None = None, + revision: str, ttl: int = 3600, ) -> None: - revision = revision or self.revision - - self._redis.hset( + self._valkey.hset( name=self._get_file_key(file_path, revision), mapping={ "last_updated": last_updated.isoformat(), "content": content, }, ) - self._redis.expire( + self._valkey.expire( name=self._get_file_key(file_path, revision), time=ttl ) @@ -70,26 +65,25 @@ def put_artifact_data( content: bytes, ttl: int = 3600, ) -> None: - self._redis.hset( + self._valkey.hset( name=self._get_artifact_key(job_id, file_path), mapping={"started_at": started_at.isoformat(), "content": content}, ) - self._redis.expire( + self._valkey.expire( name=self._get_artifact_key(job_id, file_path), time=ttl ) - def clear(self, ignore_revision: bool = False) -> None: - pattern = f"{self.path}:{self.revision}:*" - if ignore_revision: - pattern = f"{self.path}:*" - - for key in self._redis.scan_iter(match=pattern): - self._redis.delete(key) + def clear(self) -> None: + for key in self._valkey.scan_iter(match=f"{self.git_model_id}:*"): + self._valkey.delete(key) def _get_file_key(self, file_path: str, revision: str) -> str: - file_path = file_path.replace(":", "-") - return f"{self.path}:{revision}:f:{file_path}" + return f"{self.git_model_id}:f:{self._escape_string(revision)}:{self._escape_string(file_path)}" def _get_artifact_key(self, job_id: str | int, file_path: str) -> str: - file_path = file_path.replace(":", "-") - return f"{self.path}:{self.revision}:a:{job_id}:{file_path}" + return ( + f"{self.git_model_id}:a:{job_id}:{self._escape_string(file_path)}" + ) + + def _escape_string(self, string: str) -> str: + return string.replace(":", "-") diff --git a/backend/capellacollab/projects/toolmodels/modelsources/git/handler/factory.py b/backend/capellacollab/projects/toolmodels/modelsources/git/handler/factory.py index 5d44010638..a3c9149298 100644 --- a/backend/capellacollab/projects/toolmodels/modelsources/git/handler/factory.py +++ b/backend/capellacollab/projects/toolmodels/modelsources/git/handler/factory.py @@ -105,6 +105,7 @@ def _create_specific_git_handler( match git_instance.type: case settings_git_models.GitType.GITLAB: return gitlab_handler.GitlabHandler( + git_model.id, git_model.path, git_model.revision, git_model.password, @@ -113,6 +114,7 @@ def _create_specific_git_handler( ) case settings_git_models.GitType.GITHUB: return github_handler.GithubHandler( + git_model.id, git_model.path, git_model.revision, git_model.password, diff --git a/backend/capellacollab/projects/toolmodels/modelsources/git/handler/handler.py b/backend/capellacollab/projects/toolmodels/modelsources/git/handler/handler.py index 97241b0b1e..900d520c01 100644 --- a/backend/capellacollab/projects/toolmodels/modelsources/git/handler/handler.py +++ b/backend/capellacollab/projects/toolmodels/modelsources/git/handler/handler.py @@ -12,6 +12,7 @@ class GitHandler: def __init__( self, + git_model_id: int, path: str, revision: str, password: str, @@ -23,7 +24,7 @@ def __init__( self.password = password self.api_url = api_url self.repository_id = repository_id - self.cache = cache.GitRedisCache(path, revision) + self.cache = cache.GitValkeyCache(git_model_id) @classmethod @abc.abstractmethod @@ -114,7 +115,7 @@ def get_started_at_for_job(self, job_id: str | int) -> datetime.datetime: """ async def get_file( - self, trusted_file_path: str, revision: str | None = None + self, trusted_file_path: str, revision: str ) -> tuple[datetime.datetime, bytes]: last_updated = self.get_last_updated_for_file( trusted_file_path, revision diff --git a/backend/capellacollab/projects/toolmodels/modelsources/git/routes.py b/backend/capellacollab/projects/toolmodels/modelsources/git/routes.py index d093d8bd21..be3fc568a5 100644 --- a/backend/capellacollab/projects/toolmodels/modelsources/git/routes.py +++ b/backend/capellacollab/projects/toolmodels/modelsources/git/routes.py @@ -147,13 +147,13 @@ def update_git_model_by_id( return crud.update_git_model(db, db_git_model, put_git_model) -@router.delete("/empty_cache") +@router.delete("/cache") def empty_cache( git_model: models.DatabaseGitModel = fastapi.Depends( injectables.get_existing_primary_git_model ), ): - cache.GitRedisCache(git_model.path, git_model.revision).clear() + cache.GitValkeyCache(git_model_id=git_model.id).clear() @router.delete( diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 9b7f01d397..7b668868df 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -43,8 +43,7 @@ dependencies = [ "argon2-cffi", "typer", "lxml", - "redis", - "redis[hiredis]", + "valkey[libvalkey]", ] [project.urls] @@ -67,7 +66,6 @@ dev = [ "pytest-cov", "aioresponses", "types-lxml", - "types-redis", ] [tool.black] @@ -128,6 +126,7 @@ module = [ "argon2.*", "websocket.*", "testcontainers.*", + "valkey.*", ] ignore_missing_imports = true diff --git a/backend/tests/projects/toolmodels/conftest.py b/backend/tests/projects/toolmodels/conftest.py index d5c8cfd713..e322e35ebe 100644 --- a/backend/tests/projects/toolmodels/conftest.py +++ b/backend/tests/projects/toolmodels/conftest.py @@ -16,9 +16,9 @@ from capellacollab.core import credentials -@pytest.fixture(name="mock_git_redis_cache") -def fixture_mock_git_redis_cache(monkeypatch: pytest.MonkeyPatch): - class MockGitRedisCache: +@pytest.fixture(name="mock_git_valkey_cache") +def fixture_mock_git_valkey_cache(monkeypatch: pytest.MonkeyPatch): + class MockGitValkeyCache: def __init__(self, *args, **kwargs) -> None: super().__init__() @@ -42,8 +42,8 @@ def clear(self) -> None: pass monkeypatch.setattr( - "capellacollab.projects.toolmodels.modelsources.git.handler.cache.GitRedisCache", - MockGitRedisCache, + "capellacollab.projects.toolmodels.modelsources.git.handler.cache.GitValkeyCache", + MockGitValkeyCache, ) diff --git a/backend/tests/projects/toolmodels/test_diagrams.py b/backend/tests/projects/toolmodels/test_diagrams.py index e585425628..9c3e8c28ad 100644 --- a/backend/tests/projects/toolmodels/test_diagrams.py +++ b/backend/tests/projects/toolmodels/test_diagrams.py @@ -168,7 +168,7 @@ def fixture_mock_gitlab_diagram_cache_svg(git_type: git_models.GitType): "mock_git_rest_api_for_artifacts", "mock_git_diagram_cache_index_api", "mock_git_get_commit_information_api", - "mock_git_redis_cache", + "mock_git_valkey_cache", ) def test_get_diagram_metadata_from_repository( project: project_models.DatabaseProject, @@ -216,7 +216,7 @@ def test_get_diagram_metadata_from_repository( "mock_git_rest_api_for_artifacts", "mock_git_diagram_cache_index_api", "mock_git_get_commit_information_api", - "mock_git_redis_cache", + "mock_git_valkey_cache", ) def test_get_diagram_metadata_from_artifacts( project: project_models.DatabaseProject, @@ -423,7 +423,7 @@ def test_get_diagrams_failed_diagram_cache_job_found( "mock_git_diagram_cache_index_api", "mock_git_diagram_cache_svg", "mock_git_get_commit_information_api", - "mock_git_redis_cache", + "mock_git_valkey_cache", ) @pytest.mark.usefixtures("project_user", "git_instance", "git_model") def test_get_single_diagram_from_artifacts( @@ -482,7 +482,7 @@ def test_get_single_diagram_from_artifacts( "mock_git_diagram_cache_index_api", "mock_git_diagram_cache_svg", "mock_git_get_commit_information_api", - "mock_git_redis_cache", + "mock_git_valkey_cache", ) @pytest.mark.usefixtures("project_user", "git_instance", "git_model") def test_get_single_diagram_from_artifacts_with_file_ending( @@ -596,7 +596,7 @@ def test_get_single_diagram_from_artifacts_with_wrong_file_ending( "mock_git_diagram_cache_index_api", "mock_git_diagram_cache_svg", "mock_git_get_commit_information_api", - "mock_git_redis_cache", + "mock_git_valkey_cache", ) @pytest.mark.usefixtures("project_user", "git_instance", "git_model") def test_get_single_diagram_from_repository( diff --git a/backend/tests/projects/toolmodels/test_model_badge.py b/backend/tests/projects/toolmodels/test_model_badge.py index 5cd53fecef..852d666d13 100644 --- a/backend/tests/projects/toolmodels/test_model_badge.py +++ b/backend/tests/projects/toolmodels/test_model_badge.py @@ -194,7 +194,7 @@ def test_get_model_badge_fails_without_api_endpoint( "mock_git_rest_api", "mock_git_model_badge_file_api", "mock_git_get_commit_information_api", - "mock_git_redis_cache", + "mock_git_valkey_cache", ) def test_get_model_badge( project: project_models.DatabaseProject, @@ -233,7 +233,7 @@ def test_get_model_badge( "mock_git_model_badge_file_api_not_found", "mock_get_model_badge_from_artifacts_api", "mock_git_get_commit_information_api", - "mock_git_redis_cache", + "mock_git_valkey_cache", ) def test_get_model_badge_from_artifacts( project: project_models.DatabaseProject, diff --git a/frontend/src/app/openapi/api/projects-models-git.service.ts b/frontend/src/app/openapi/api/projects-models-git.service.ts index 59584332b8..20d6a66f4f 100644 --- a/frontend/src/app/openapi/api/projects-models-git.service.ts +++ b/frontend/src/app/openapi/api/projects-models-git.service.ts @@ -329,7 +329,7 @@ export class ProjectsModelsGitService { } } - let localVarPath = `/api/v1/projects/${this.configuration.encodeParam({name: "projectSlug", value: projectSlug, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}/models/${this.configuration.encodeParam({name: "modelSlug", value: modelSlug, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}/modelsources/git/empty_cache`; + let localVarPath = `/api/v1/projects/${this.configuration.encodeParam({name: "projectSlug", value: projectSlug, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}/models/${this.configuration.encodeParam({name: "modelSlug", value: modelSlug, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}/modelsources/git/cache`; return this.httpClient.request('delete', `${this.configuration.basePath}${localVarPath}`, { context: localVarHttpContext, diff --git a/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-code-block/model-diagram-code-block.component.ts b/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-code-block/model-diagram-code-block.component.ts index da44ccb1ee..c1c69b7632 100644 --- a/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-code-block/model-diagram-code-block.component.ts +++ b/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-code-block/model-diagram-code-block.component.ts @@ -23,7 +23,7 @@ import { MatTooltip } from '@angular/material/tooltip'; import hljs from 'highlight.js'; import { MetadataService } from 'src/app/general/metadata/metadata.service'; import { ToastService } from 'src/app/helpers/toast/toast.service'; -import { Metadata, Project, ToolModel } from 'src/app/openapi'; +import { JobId, Metadata, Project, ToolModel } from 'src/app/openapi'; import { getPrimaryGitModel } from 'src/app/projects/models/service/model.service'; import { UserWrapperService } from 'src/app/services/user/user.service'; import { TokenService } from 'src/app/users/basic-auth-service/basic-auth-token.service'; @@ -70,6 +70,9 @@ export class ModelDiagramCodeBlockComponent implements OnInit, AfterViewInit { @Input({ required: true }) project!: Project; + @Input() + jobId: JobId | undefined; + @Input() expanded = false; @@ -103,7 +106,7 @@ model = capellambse.MelodyModel( diagram_cache={ "path": "${basePath}/api/v1/projects/${this.project!.slug}/models/${ this.model.slug - }/diagrams/%s", + }/diagrams/%s${this.jobId ? '?job_id=' + this.jobId : ''}", "username": "${this.userService.user?.name}", "password": "${this.passwordValue ? this.passwordValue : '**************'}", } diff --git a/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-code-block/model-diagram-code-block.stories.ts b/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-code-block/model-diagram-code-block.stories.ts index 23bf5e246a..e5d81edd1f 100644 --- a/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-code-block/model-diagram-code-block.stories.ts +++ b/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-code-block/model-diagram-code-block.stories.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import { Meta, StoryObj } from '@storybook/angular'; +import { JobId } from 'src/app/openapi'; import { mockModel } from 'src/storybook/model'; import { mockProject } from 'src/storybook/project'; import { ModelDiagramCodeBlockComponent } from './model-diagram-code-block.component'; @@ -22,3 +23,21 @@ export const CodeBlock: Story = { expanded: true, }, }; + +export const CodeBlockWithToken: Story = { + args: { + model: mockModel, + project: mockProject, + expanded: true, + passwordValue: 'verysecretpassword', + }, +}; + +export const CodeBlockWithJobID: Story = { + args: { + model: mockModel, + project: mockProject, + expanded: true, + jobId: '1234' as JobId, + }, +}; diff --git a/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-dialog.component.html b/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-dialog.component.html index 26bbefa8f1..68a936fc47 100644 --- a/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-dialog.component.html +++ b/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-dialog.component.html @@ -15,10 +15,11 @@

View diagrams

-
- +
+ Search View diagrams search
diff --git a/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-dialog.component.ts b/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-dialog.component.ts index 34d7fc0f9d..a6672e1119 100644 --- a/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-dialog.component.ts +++ b/frontend/src/app/projects/models/diagrams/model-diagram-dialog/model-diagram-dialog.component.ts @@ -30,7 +30,7 @@ import { MatInput } from '@angular/material/input'; import { MatTooltip } from '@angular/material/tooltip'; import { saveAs } from 'file-saver'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { switchMap } from 'rxjs'; +import { Observable, switchMap, tap } from 'rxjs'; import { DiagramCacheMetadata, DiagramMetadata, @@ -92,48 +92,39 @@ export class ModelDiagramDialogComponent implements OnInit { constructor( private dialog: MatDialog, private dialogRef: MatDialogRef, - private projcetsModelsGitService: ProjectsModelsGitService, + private projectsModelsGitService: ProjectsModelsGitService, private projectsModelsDiagramsService: ProjectsModelsDiagramsService, @Inject(MAT_DIALOG_DATA) public data: { model: ToolModel; project: Project }, ) {} ngOnInit(): void { - this.projectsModelsDiagramsService + this.loadDiagramCacheMetadata().subscribe(); + } + + loadDiagramCacheMetadata(): Observable { + return this.projectsModelsDiagramsService .getDiagramMetadata(this.data.project.slug, this.data.model.slug) - .subscribe({ - next: (diagramMetadata) => { - this.diagramMetadata = diagramMetadata; - this.observeVisibleDiagrams(); - }, - error: () => { - this.dialogRef.close(); - }, - }); + .pipe( + tap({ + next: (diagramMetadata) => { + this.diagramMetadata = diagramMetadata; + this.observeVisibleDiagrams(); + }, + error: () => { + this.dialogRef.close(); + }, + }), + ); } clearCache() { this.diagramMetadata = undefined; this.diagrams = {}; - this.projcetsModelsGitService + this.projectsModelsGitService .emptyCache(this.data.project.slug, this.data.model.slug) - .pipe( - switchMap(() => - this.projectsModelsDiagramsService.getDiagramMetadata( - this.data.project.slug, - this.data.model.slug, - ), - ), - ) - .subscribe({ - next: (diagramMetadata) => { - this.diagramMetadata = diagramMetadata; - this.observeVisibleDiagrams(); - }, - error: () => { - this.dialogRef.close(); - }, - }); + .pipe(switchMap(() => this.loadDiagramCacheMetadata())) + .subscribe(); } observeVisibleDiagrams() { diff --git a/frontend/src/app/projects/project-detail/model-overview/model-complexity-badge/model-complexity-badge.component.ts b/frontend/src/app/projects/project-detail/model-overview/model-complexity-badge/model-complexity-badge.component.ts index f65aa9149e..5b212653ab 100644 --- a/frontend/src/app/projects/project-detail/model-overview/model-complexity-badge/model-complexity-badge.component.ts +++ b/frontend/src/app/projects/project-detail/model-overview/model-complexity-badge/model-complexity-badge.component.ts @@ -3,12 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ import { CommonModule } from '@angular/common'; +import { HttpContext } from '@angular/common/http'; import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { filter, map, switchMap } from 'rxjs'; +import { SKIP_ERROR_HANDLING } from 'src/app/general/error-handling/error-handling.interceptor'; import { ProjectsModelsModelComplexityBadgeService } from 'src/app/openapi'; import { ProjectWrapperService } from 'src/app/projects/service/project.service'; import { environment } from 'src/environments/environment'; @@ -56,6 +58,11 @@ export class ModelComplexityBadgeComponent implements OnChanges { return this.modelComplexityBadgeService.getModelComplexityBadge( projectSlug, this.modelSlug!, + undefined, + undefined, + { + context: new HttpContext().set(SKIP_ERROR_HANDLING, true), + }, ); }), ) diff --git a/frontend/src/app/sessions/session-overview/session-overview.component.html b/frontend/src/app/sessions/session-overview/session-overview.component.html index 18a23d9bc0..82ca01b496 100644 --- a/frontend/src/app/sessions/session-overview/session-overview.component.html +++ b/frontend/src/app/sessions/session-overview/session-overview.component.html @@ -11,7 +11,6 @@ Select all sessions
- diff --git a/helm/config/backend.yaml b/helm/config/backend.yaml index fe59eee0e4..670ae1a9a2 100644 --- a/helm/config/backend.yaml +++ b/helm/config/backend.yaml @@ -66,8 +66,8 @@ database: url: "{{ .Values.database.backend.external.uri }}" {{ end }} -redis: - url: "redis://default:{{ .Values.redis.password }}@{{ .Release.Name }}-backend-redis:6379/0" +valkey: + url: "valkey://default:{{ .Values.valkey.password }}@{{ .Release.Name }}-backend-valkey:6379/0" pipelines: timeout: {{ .Values.pipelines.timeout }} diff --git a/helm/templates/backend/redis.configmap.yaml b/helm/templates/backend/redis.configmap.yaml deleted file mode 100644 index 81d95f703d..0000000000 --- a/helm/templates/backend/redis.configmap.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors -# SPDX-License-Identifier: Apache-2.0 - -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ .Release.Name }}-redis - namespace: {{ .Release.Namespace }} - labels: - id: {{ .Release.Name }}-configmap-redis -data: - redis-config: | - requirepass {{ .Values.redis.password }} diff --git a/helm/templates/backend/redis.deployment.yaml b/helm/templates/valkey/valkey.deployment.yaml similarity index 57% rename from helm/templates/backend/redis.deployment.yaml rename to helm/templates/valkey/valkey.deployment.yaml index cc71a7196d..0a2947c3e1 100644 --- a/helm/templates/backend/redis.deployment.yaml +++ b/helm/templates/valkey/valkey.deployment.yaml @@ -4,50 +4,48 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ .Release.Name }}-backend-redis + name: {{ .Release.Name }}-backend-valkey labels: - id: {{ .Release.Name }}-deployment-backend-redis + id: {{ .Release.Name }}-deployment-valkey spec: - replicas: {{ .Values.replicaCount.backendRedis | default 1 }} + replicas: {{ .Values.replicaCount.valkey | default 1 }} selector: matchLabels: - id: {{ .Release.Name }}-deployment-backend-redis + id: {{ .Release.Name }}-deployment-valkey strategy: type: Recreate template: metadata: labels: - id: {{ .Release.Name }}-deployment-backend-redis + id: {{ .Release.Name }}-deployment-valkey spec: automountServiceAccountToken: false volumes: - name: data emptyDir: {} - name: config - configMap: - name: {{ .Release.Name }}-redis - items: - - key: redis-config - path: redis.conf + secret: + secretName: {{ .Release.Name }}-valkey containers: - - name: {{ .Release.Name }}-backend-redis - {{ if .Values.docker.images.redis }} - image: {{ .Values.docker.images.redis }} + - name: {{ .Release.Name }}-backend-valkey + {{ if .Values.docker.images.valkey }} + image: {{ .Values.docker.images.valkey }} {{ else }} - image: {{ .Values.docker.registry.external }}/redis:7.4 + image: {{ .Values.docker.registry.external }}/valkey/valkey:7.2.6 {{ end }} ports: - - name: redis + - name: valkey containerPort: 6379 protocol: TCP resources: {{ if .Values.development }} - limits: - cpu: "0.25" - memory: 250Mi requests: cpu: "0.06" memory: 20Mi + limits: + cpu: "0.25" + memory: 250Mi + ephemeral-storage: "2Gi" {{ else }} requests: cpu: "250m" @@ -55,9 +53,10 @@ spec: limits: cpu: "1" memory: "3Gi" + ephemeral-storage: "5Gi" {{ end }} volumeMounts: - - mountPath: /redis-master-data + - mountPath: /valkey-master-data name: data - - mountPath: /redis-master + - mountPath: /valkey-master name: config diff --git a/helm/templates/backend/redis.disruptionsbudget.yaml b/helm/templates/valkey/valkey.disruptionsbudget.yaml similarity index 69% rename from helm/templates/backend/redis.disruptionsbudget.yaml rename to helm/templates/valkey/valkey.disruptionsbudget.yaml index 970ba92697..3ce9d7286e 100644 --- a/helm/templates/backend/redis.disruptionsbudget.yaml +++ b/helm/templates/valkey/valkey.disruptionsbudget.yaml @@ -4,9 +4,9 @@ apiVersion: policy/v1 kind: PodDisruptionBudget metadata: - name: {{ .Release.Name }}-backend-redis + name: {{ .Release.Name }}-backend-valkey spec: minAvailable: 1 selector: matchLabels: - id: {{ .Release.Name }}-deployment-backend-redis + id: {{ .Release.Name }}-deployment-valkey diff --git a/helm/templates/valkey/valkey.secret.yaml b/helm/templates/valkey/valkey.secret.yaml new file mode 100644 index 0000000000..ae4b1bf422 --- /dev/null +++ b/helm/templates/valkey/valkey.secret.yaml @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-valkey + labels: + id: {{ .Release.Name }}-secret-valkey +type: Opaque +stringData: + valkey.conf: | + requirepass {{ .Values.valkey.password }} diff --git a/helm/templates/backend/redis.service.yaml b/helm/templates/valkey/valkey.service.yaml similarity index 55% rename from helm/templates/backend/redis.service.yaml rename to helm/templates/valkey/valkey.service.yaml index 0d2ae754e0..cfc3e99e9a 100644 --- a/helm/templates/backend/redis.service.yaml +++ b/helm/templates/valkey/valkey.service.yaml @@ -4,15 +4,15 @@ apiVersion: v1 kind: Service metadata: - name: {{ .Release.Name }}-backend-redis + name: {{ .Release.Name }}-backend-valkey labels: - id: {{ .Release.Name }}-service-backend-redis + id: {{ .Release.Name }}-service-backend-valkey spec: type: ClusterIP selector: - id: {{ .Release.Name }}-deployment-backend-redis + id: {{ .Release.Name }}-deployment-valkey ports: - port: 6379 - targetPort: redis + targetPort: valkey protocol: TCP - name: redis + name: valkey diff --git a/helm/values.yaml b/helm/values.yaml index 454d71d939..3481da78da 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -9,7 +9,7 @@ replicaCount: backend: 1 backendPostgres: 1 - backendRedis: 1 + valkey: 1 docs: 1 frontend: 1 grafana: 1 @@ -55,7 +55,7 @@ docker: promtail: null postgres: null - redis: null + valkey: null grafana: null nginxUnprivileged: null mockOauth2Server: null @@ -142,7 +142,7 @@ database: # Provide URI to the datebase in the format: postgresql://user:password@url:port/db_name uri: postgresql://user:password@url:port/db_name -redis: +valkey: password: secret backend: