Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[alembic]
script_location = control_plane/storage/migrations
prepend_sys_path = .
path_separator = os
timezone = UTC

[post_write_hooks]

[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
29 changes: 29 additions & 0 deletions control_plane/contracts/lane_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from pydantic import BaseModel, ConfigDict

from control_plane.contracts.backup_gate_record import BackupGateRecord
from control_plane.contracts.deployment_record import DeploymentRecord
from control_plane.contracts.dokploy_target_id_record import DokployTargetIdRecord
from control_plane.contracts.dokploy_target_record import DokployTargetRecord
from control_plane.contracts.environment_inventory import EnvironmentInventory
from control_plane.contracts.odoo_instance_override_record import OdooInstanceOverrideRecord
from control_plane.contracts.promotion_record import PromotionRecord
from control_plane.contracts.release_tuple_record import ReleaseTupleRecord
from control_plane.contracts.runtime_environment_record import RuntimeEnvironmentRecord
from control_plane.contracts.secret_record import SecretBinding


class LaunchplaneLaneSummary(BaseModel):
model_config = ConfigDict(extra="forbid")

context: str
instance: str
inventory: EnvironmentInventory | None = None
release_tuple: ReleaseTupleRecord | None = None
latest_deployment: DeploymentRecord | None = None
latest_promotion: PromotionRecord | None = None
latest_backup_gate: BackupGateRecord | None = None
dokploy_target_id: DokployTargetIdRecord | None = None
dokploy_target: DokployTargetRecord | None = None
runtime_environment_records: tuple[RuntimeEnvironmentRecord, ...] = ()
odoo_instance_override: OdooInstanceOverrideRecord | None = None
secret_bindings: tuple[SecretBinding, ...] = ()
12 changes: 12 additions & 0 deletions control_plane/contracts/preview_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from pydantic import BaseModel, ConfigDict, Field

from control_plane.contracts.preview_generation_record import PreviewGenerationRecord
from control_plane.contracts.preview_record import PreviewRecord


class LaunchplanePreviewSummary(BaseModel):
model_config = ConfigDict(extra="forbid")

preview: PreviewRecord
latest_generation: PreviewGenerationRecord | None = None
recent_generations: tuple[PreviewGenerationRecord, ...] = Field(default_factory=tuple)
Empty file.
62 changes: 62 additions & 0 deletions control_plane/storage/migrations/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from __future__ import annotations

from logging.config import fileConfig
import os

from alembic import context
from sqlalchemy import engine_from_config, pool

from control_plane.storage.postgres import Base

config = context.config

if config.config_file_name is not None:
fileConfig(config.config_file_name)

target_metadata = Base.metadata


def _database_url() -> str:
configured_url = config.get_main_option("sqlalchemy.url")
if configured_url:
return configured_url
environment_url = os.environ.get("LAUNCHPLANE_DATABASE_URL", "").strip()
if environment_url:
return environment_url
raise RuntimeError(
"Set sqlalchemy.url or LAUNCHPLANE_DATABASE_URL before running Launchplane migrations."
)


def run_migrations_offline() -> None:
context.configure(
url=_database_url(),
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)

with context.begin_transaction():
context.run_migrations()


def run_migrations_online() -> None:
configuration = config.get_section(config.config_ini_section, {})
configuration["sqlalchemy.url"] = _database_url()
connectable = engine_from_config(
configuration,
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)

with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata)

with context.begin_transaction():
context.run_migrations()


if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
27 changes: 27 additions & 0 deletions control_plane/storage/migrations/script.py.mako
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""

from __future__ import annotations

from collections.abc import Sequence

from alembic import op
import sqlalchemy as sa
${imports if imports else ""}

revision: str = ${repr(up_revision)}
down_revision: str | None = ${repr(down_revision)}
branch_labels: str | Sequence[str] | None = ${repr(branch_labels)}
depends_on: str | Sequence[str] | None = ${repr(depends_on)}


def upgrade() -> None:
${upgrades if upgrades else "pass"}


def downgrade() -> None:
${downgrades if downgrades else "pass"}
Empty file.
Loading