Skip to content

Commit

Permalink
feat!: Separate T4C Instances and License Servers
Browse files Browse the repository at this point in the history
Until now, T4C instances had a field for setting up a license server. This was a problem because
setting up multiple instances meant inputting duplicate license server details. Now, users create a
license server and link it to one or more instances.
  • Loading branch information
zusorio committed Oct 2, 2024
1 parent 4c48e08 commit f4c4864
Show file tree
Hide file tree
Showing 78 changed files with 2,822 additions and 737 deletions.
2 changes: 1 addition & 1 deletion backend/capellacollab/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import capellacollab.projects.toolmodels.backups.runs.interface as pipeline_runs_interface
import capellacollab.sessions.metrics as sessions_metrics
import capellacollab.settings.modelsources.t4c.metrics as t4c_metrics
import capellacollab.settings.modelsources.t4c.license_server.metrics as t4c_metrics
from capellacollab import core

# This import statement is required and should not be removed! (Alembic will not work otherwise)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

"""Split T4C Instances and License Servers
Revision ID: 3818a5009130
Revises: 7cf3357ddd7b
Create Date: 2024-10-01 15:46:26.054936
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.orm import Session

# revision identifiers, used by Alembic.
revision = "3818a5009130"
down_revision = "7cf3357ddd7b"
branch_labels = None
depends_on = None


def upgrade():
op.create_table(
"t4c_license_servers",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("usage_api", sa.String(), nullable=False),
sa.Column("license_key", sa.String(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
)
op.create_index(
op.f("ix_t4c_license_servers_id"),
"t4c_license_servers",
["id"],
unique=True,
)
op.add_column(
"t4c_instances",
sa.Column("license_server_id", sa.Integer(), nullable=True),
)
op.create_foreign_key(
None,
"t4c_instances",
"t4c_license_servers",
["license_server_id"],
["id"],
)

# Fetch existing t4c_instances and group by usage_api and license
bind = op.get_bind()
session = Session(bind=bind)
t4c_instances = session.execute(
sa.text("SELECT id, usage_api, license FROM t4c_instances")
).fetchall()

grouped_instances = {}
for instance in t4c_instances:
key = (instance.usage_api, instance.license)
if key not in grouped_instances:
grouped_instances[key] = []
grouped_instances[key].append(instance.id)

# Create new license_server for each group and associate with instances
for (usage_api, license_key), instance_ids in grouped_instances.items():
license_server_id = session.execute(
sa.text(
"INSERT INTO t4c_license_servers (name, usage_api, license_key) VALUES (:name, :usage_api, :license_key) RETURNING id"
),
{
"name": usage_api,
"usage_api": usage_api,
"license_key": license_key,
},
).scalar()

session.execute(
sa.text(
"UPDATE t4c_instances SET license_server_id = :license_server_id WHERE id = ANY(:instance_ids)"
),
{
"license_server_id": license_server_id,
"instance_ids": instance_ids,
},
)

session.commit()

op.alter_column("t4c_instances", "license_server_id", nullable=False)
op.drop_column("t4c_instances", "license")
op.drop_column("t4c_instances", "usage_api")
37 changes: 28 additions & 9 deletions backend/capellacollab/core/database/migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,21 @@
from capellacollab.settings.modelsources.git import (
models as modelssources_models,
)
from capellacollab.settings.modelsources.t4c import crud as settings_t4c_crud
from capellacollab.settings.modelsources.t4c import (
models as settings_t4c_models,
from capellacollab.settings.modelsources.t4c.instance import (
crud as t4c_instance_crud,
)
from capellacollab.settings.modelsources.t4c.repositories import (
from capellacollab.settings.modelsources.t4c.instance import (
models as t4c_instance_models,
)
from capellacollab.settings.modelsources.t4c.instance.repositories import (
crud as repositories_crud,
)
from capellacollab.settings.modelsources.t4c.license_server import (
crud as t4c_license_server_crud,
)
from capellacollab.settings.modelsources.t4c.license_server import (
models as t4c_license_server_models,
)
from capellacollab.tools import crud as tools_crud
from capellacollab.tools import models as tools_models
from capellacollab.users import crud as users_crud
Expand Down Expand Up @@ -352,20 +360,31 @@ def create_t4c_instance_and_repositories(db):
db, tool.id, "5.2.0"
)
assert version
default_instance = settings_t4c_models.DatabaseT4CInstance(

default_license_server = (
t4c_license_server_models.DatabaseT4CLicenseServer(
name="default",
usage_api="http://localhost:8086",
license_key="placeholder",
)
)
t4c_license_server_crud.create_t4c_license_server(
db, default_license_server
)

default_instance = t4c_instance_models.DatabaseT4CInstance(
name="default",
license="placeholder",
protocol=settings_t4c_models.Protocol.tcp,
protocol=t4c_instance_models.Protocol.tcp,
host="localhost",
port=2036,
cdo_port=12036,
usage_api="http://localhost:8086",
license_server=default_license_server,
rest_api="http://localhost:8081/api/v1.0",
username="admin",
password="password",
version=version,
)
settings_t4c_crud.create_t4c_instance(db, default_instance)
t4c_instance_crud.create_t4c_instance(db, default_instance)
for t4c_model in t4c_crud.get_t4c_models(db):
t4c_model.repository = repositories_crud.create_t4c_repository(
db=db, repo_name=t4c_model.name, instance=default_instance
Expand Down
3 changes: 2 additions & 1 deletion backend/capellacollab/core/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
import capellacollab.settings.configuration.models
import capellacollab.settings.integrations.purevariants.models
import capellacollab.settings.modelsources.git.models
import capellacollab.settings.modelsources.t4c.models
import capellacollab.settings.modelsources.t4c.instance.models
import capellacollab.settings.modelsources.t4c.license_server.models
import capellacollab.tools.models
import capellacollab.users.models
import capellacollab.users.tokens.models
Expand Down
5 changes: 3 additions & 2 deletions backend/capellacollab/projects/toolmodels/backups/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import requests
from sqlalchemy import orm

import capellacollab.settings.modelsources.t4c.repositories.interface as t4c_repository_interface
from capellacollab.core.authentication import injectables as auth_injectables
from capellacollab.projects.toolmodels import models as toolmodels_models
from capellacollab.projects.toolmodels.modelsources.git import (
Expand All @@ -18,8 +17,10 @@
models as t4c_models,
)
from capellacollab.sessions import operators
from capellacollab.settings.modelsources.t4c.instance.repositories import (
interface as t4c_repository_interface,
)
from capellacollab.users import models as users_models

from . import crud, exceptions, models

log = logging.getLogger(__name__)
Expand Down
7 changes: 4 additions & 3 deletions backend/capellacollab/projects/toolmodels/backups/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import requests
from sqlalchemy import orm

import capellacollab.settings.modelsources.t4c.repositories.interface as t4c_repository_interface
from capellacollab.core import credentials, database
from capellacollab.core.authentication import injectables as auth_injectables
from capellacollab.projects.toolmodels import (
Expand All @@ -24,11 +23,13 @@
)
from capellacollab.projects.users import models as projects_users_models
from capellacollab.sessions import operators
from capellacollab.settings.modelsources.t4c.instance.repositories import (
interface as t4c_repository_interface,
)
from capellacollab.tools import crud as tools_crud

from .. import exceptions as toolmodels_exceptions
from . import core, crud, exceptions, injectables, models
from .runs import routes as runs_routes
from .. import exceptions as toolmodels_exceptions

router = fastapi.APIRouter(
dependencies=[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from capellacollab.projects.toolmodels import models as toolmodels_models
from capellacollab.projects.toolmodels.modelsources.t4c import models
from capellacollab.settings.modelsources.t4c.repositories import (
from capellacollab.settings.modelsources.t4c.instance.repositories import (
models as repositories_models,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

from capellacollab.core import database
from capellacollab.core import pydantic as core_pydantic
from capellacollab.settings.modelsources.t4c.repositories import (
from capellacollab.settings.modelsources.t4c.instance.repositories import (
models as repositories_models,
)

if t.TYPE_CHECKING:
from capellacollab.projects.toolmodels.models import DatabaseToolModel
from capellacollab.settings.modelsources.t4c.repositories.models import (
from capellacollab.settings.modelsources.t4c.instance.repositories.models import (
DatabaseT4CRepository,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
from capellacollab.projects.toolmodels import models as toolmodels_models
from capellacollab.projects.toolmodels.backups import crud as backups_crud
from capellacollab.projects.users import models as projects_users_models
from capellacollab.settings.modelsources.t4c import (
from capellacollab.settings.modelsources.t4c.instance import (
injectables as settings_t4c_injectables,
)
from capellacollab.settings.modelsources.t4c.repositories import (
from capellacollab.settings.modelsources.t4c.instance.repositories import (
injectables as settings_t4c_repositories_injectables,
)
from capellacollab.users import models as users_models
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

from capellacollab.settings.modelsources.t4c.repositories import (
from capellacollab.settings.modelsources.t4c.instance.repositories import (
models as t4c_repository_models,
)
from capellacollab.tools import models as tools_models
Expand Down
8 changes: 5 additions & 3 deletions backend/capellacollab/sessions/hooks/t4c.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
from capellacollab.core import credentials
from capellacollab.core import models as core_models
from capellacollab.core.authentication import injectables as auth_injectables
from capellacollab.settings.modelsources.t4c.repositories import (
from capellacollab.settings.modelsources.t4c.instance.repositories import (
crud as repo_crud,
)
from capellacollab.settings.modelsources.t4c.repositories import (
from capellacollab.settings.modelsources.t4c.instance.repositories import (
interface as repo_interface,
)
from capellacollab.tools import models as tools_models
Expand Down Expand Up @@ -70,7 +70,9 @@ def configuration_hook( # type: ignore
)

t4c_licence_secret = (
t4c_repositories[0].instance.license if t4c_repositories else ""
t4c_repositories[0].instance.license_server.license_key
if t4c_repositories
else ""
)

environment = T4CConfigEnvironment(
Expand Down
6 changes: 1 addition & 5 deletions backend/capellacollab/settings/modelsources/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,4 @@
prefix="/git",
tags=["Settings - Modelsources - Git"],
)
router.include_router(
settings_t4c_routes.router,
prefix="/t4c",
tags=["Settings - Modelsources - T4C"],
)
router.include_router(settings_t4c_routes.router, prefix="/t4c")
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

from fastapi import status

from capellacollab.core import exceptions as core_exceptions


class T4CInstanceIsArchivedError(core_exceptions.BaseError):
def __init__(self, t4c_instance_id: int):
self.t4c_instance_id = t4c_instance_id
super().__init__(
status_code=status.HTTP_400_BAD_REQUEST,
title="T4C instance is archived",
reason=f"The T4C instance identified by {t4c_instance_id} is archived, thus prohibiting the execution of the requested operation.",
err_code="T4C_INSTANCE_IS_ARCHIVED",
)


class T4CInstanceNotFoundError(core_exceptions.BaseError):
def __init__(self, t4c_instance_id: int):
super().__init__(

Check warning on line 22 in backend/capellacollab/settings/modelsources/t4c/instance/exceptions.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/settings/modelsources/t4c/instance/exceptions.py#L22

Added line #L22 was not covered by tests
status_code=status.HTTP_404_NOT_FOUND,
title="T4C server instance not found",
reason=f"The T4C instance with id {t4c_instance_id} was not found.",
err_code="T4C_INSTANCE_NOT_FOUND",
)


class T4CInstanceWithNameAlreadyExistsError(
core_exceptions.ResourceAlreadyExistsError
):
def __init__(self):
super().__init__(resource_name="T4C Instance", identifier_name="name")
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
from sqlalchemy import orm

from capellacollab.core import database

from . import crud, exceptions, models
from capellacollab.settings.modelsources.t4c.instance import (
crud,
exceptions,
models,
)


def get_existing_instance(
Expand Down
Loading

0 comments on commit f4c4864

Please sign in to comment.