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
4 changes: 2 additions & 2 deletions src/dstack/_internal/core/models/gateways.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

class Gateway(BaseModel):
name: str
ip_address: str
instance_id: str
ip_address: Optional[str]
instance_id: Optional[str]
region: str
wildcard_domain: Optional[str]
default: bool
Expand Down
4 changes: 2 additions & 2 deletions src/dstack/_internal/server/app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import time
from contextlib import asynccontextmanager
from typing import Callable
from typing import Awaitable, Callable
from urllib.parse import urlparse

import sentry_sdk
Expand Down Expand Up @@ -90,7 +90,7 @@ async def lifespan(app: FastAPI):
_ON_STARTUP_HOOKS = []


def register_on_startup_hook(func: Callable[[FastAPI], None]):
def register_on_startup_hook(func: Callable[[FastAPI], Awaitable[None]]):
_ON_STARTUP_HOOKS.append(func)


Expand Down
3 changes: 2 additions & 1 deletion src/dstack/_internal/server/migrations/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,6 @@ def main():
run_migrations_online()


if __name__ == "__main__":
# invoked via alembic command
if __name__ == "env_py":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Separate gateways from compute

Revision ID: e6391ca6a264
Revises: 686fb8341ea5
Create Date: 2023-12-11 10:34:28.112500

"""
import sqlalchemy as sa
import sqlalchemy_utils
from alembic import op

# revision identifiers, used by Alembic.
revision = "e6391ca6a264"
down_revision = "686fb8341ea5"
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"gateway_computes",
sa.Column("id", sqlalchemy_utils.types.uuid.UUIDType(binary=False), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("instance_id", sa.String(length=100), nullable=False),
sa.Column("ip_address", sa.String(length=100), nullable=False),
sa.Column("region", sa.String(length=100), nullable=False),
sa.Column("backend_id", sqlalchemy_utils.types.uuid.UUIDType(binary=False), nullable=True),
sa.Column("ssh_private_key", sa.Text(), nullable=False),
sa.Column("ssh_public_key", sa.Text(), nullable=False),
sa.ForeignKeyConstraint(
["backend_id"],
["backends.id"],
name=op.f("fk_gateway_computes_backend_id_backends"),
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_gateway_computes")),
)
with op.batch_alter_table("gateways", schema=None) as batch_op:
batch_op.add_column(
sa.Column(
"gateway_compute_id",
sqlalchemy_utils.types.uuid.UUIDType(binary=False),
nullable=True,
)
)
batch_op.create_foreign_key(
batch_op.f("fk_gateways_gateway_compute_id_gateway_computes"),
"gateway_computes",
["gateway_compute_id"],
["id"],
ondelete="CASCADE",
)
batch_op.drop_column("ip_address")
batch_op.drop_column("instance_id")

# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("gateways", schema=None) as batch_op:
batch_op.add_column(sa.Column("instance_id", sa.VARCHAR(length=100), nullable=False))
batch_op.add_column(sa.Column("ip_address", sa.VARCHAR(length=100), nullable=False))
batch_op.drop_constraint(
batch_op.f("fk_gateways_gateway_compute_id_gateway_computes"), type_="foreignkey"
)
batch_op.drop_column("gateway_compute_id")

op.drop_table("gateway_computes")
# ### end Alembic commands ###
30 changes: 27 additions & 3 deletions src/dstack/_internal/server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class ProjectModel(BaseModel):
back_populates="project", lazy="selectin"
)

default_gateway_id: Mapped[Optional[UUIDType]] = mapped_column(
default_gateway_id: Mapped[Optional[uuid.UUID]] = mapped_column(
ForeignKey("gateways.id", use_alter=True, ondelete="SET NULL"), nullable=True
)
default_gateway: Mapped["GatewayModel"] = relationship(
Expand Down Expand Up @@ -189,8 +189,6 @@ class GatewayModel(BaseModel):
UUIDType(binary=False), primary_key=True, default=uuid.uuid4
)
name: Mapped[str] = mapped_column(String(100))
ip_address: Mapped[str] = mapped_column(String(100))
instance_id: Mapped[str] = mapped_column(String(100))
region: Mapped[str] = mapped_column(String(100))
wildcard_domain: Mapped[str] = mapped_column(String(100), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=get_current_datetime)
Expand All @@ -200,4 +198,30 @@ class GatewayModel(BaseModel):
backend_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("backends.id", ondelete="CASCADE"))
backend: Mapped["BackendModel"] = relationship(lazy="selectin")

gateway_compute_id: Mapped[Optional[uuid.UUID]] = mapped_column(
ForeignKey("gateway_computes.id", ondelete="CASCADE")
)
gateway_compute: Mapped[Optional["GatewayComputeModel"]] = relationship(lazy="joined")

__table_args__ = (UniqueConstraint("project_id", "name", name="uq_gateways_project_id_name"),)


class GatewayComputeModel(BaseModel):
__tablename__ = "gateway_computes"

id: Mapped[uuid.UUID] = mapped_column(
UUIDType(binary=False), primary_key=True, default=uuid.uuid4
)
created_at: Mapped[datetime] = mapped_column(DateTime, default=get_current_datetime)
instance_id: Mapped[str] = mapped_column(String(100))
ip_address: Mapped[str] = mapped_column(String(100))
region: Mapped[str] = mapped_column(String(100))

backend_id: Mapped[Optional[uuid.UUID]] = mapped_column(
ForeignKey("backends.id", ondelete="CASCADE")
)
backend: Mapped[Optional["BackendModel"]] = relationship()

# The key to authorize the server with the gateway
ssh_private_key: Mapped[str] = mapped_column(Text)
ssh_public_key: Mapped[str] = mapped_column(Text)
71 changes: 71 additions & 0 deletions src/dstack/_internal/server/scripts/configure_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,74 @@

owner = "www-data"

nginx_global_conf_path = Path("/etc/nginx/nginx.conf")

nginx_global_conf = """
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
worker_connections 768;
# multi_accept on;
}

http {

##
# Basic Settings
##

sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;

server_names_hash_bucket_size 128;
# server_name_in_redirect off;

include /etc/nginx/mime.types;
default_type application/octet-stream;

##
# SSL Settings
##

ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;

##
# Logging Settings
##

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

##
# Gzip Settings
##

gzip on;

# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

"""


# payload schema
# {
# "authorized_key": "ssh-rsa ...",
Expand Down Expand Up @@ -104,6 +172,9 @@ def main():
}
)
service["conf_path"].write_text(format_nginx_conf(conf))

nginx_global_conf_path.write_text(nginx_global_conf)

print(json.dumps(sock_paths))
reload_nginx()

Expand Down
2 changes: 2 additions & 0 deletions src/dstack/_internal/server/services/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ async def delete_backends(
project: ProjectModel,
backends_types: List[BackendType],
):
if BackendType.DSTACK in backends_types:
raise ServerClientError("Cannot delete dstack backend")
await session.execute(
delete(BackendModel).where(
BackendModel.type.in_(backends_types),
Expand Down
Loading