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
76 changes: 61 additions & 15 deletions datacrunch/containers/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,31 @@ class VolumeMount:
@dataclass_json
@dataclass
class Container:
"""Container configuration for deployment creation and updates.
This class omits the name field which is managed by the system.

:param image: Container image to use
:param exposed_port: Port to expose from the container
:param healthcheck: Optional health check configuration
:param entrypoint_overrides: Optional entrypoint override settings
:param env: Optional list of environment variables
:param volume_mounts: Optional list of volume mounts
"""
image: str
exposed_port: int
healthcheck: Optional[HealthcheckSettings] = None
entrypoint_overrides: Optional[EntrypointOverridesSettings] = None
env: Optional[List[EnvVar]] = None
volume_mounts: Optional[List[VolumeMount]] = None


@dataclass_json
@dataclass
class ContainerInfo:
"""Container configuration for deployments.
This class is read-only and includes the system-managed name field.

:param name: Name of the container
:param name: Name of the container (system-managed)
:param image: Container image to use
:param exposed_port: Port to expose from the container
:param healthcheck: Optional health check configuration
Expand Down Expand Up @@ -224,7 +246,31 @@ class ScalingOptions:
@dataclass_json(undefined=Undefined.EXCLUDE)
@dataclass
class Deployment:
"""Configuration for creating or updating a container deployment.
This class uses Container instead of ContainerInfo to prevent name setting.

:param name: Name of the deployment
:param container_registry_settings: Settings for accessing container registry
:param containers: List of container specifications in the deployment
:param compute: Compute resource configuration
:param is_spot: Whether is spot deployment
:param endpoint_base_url: Optional base URL for the deployment endpoint
:param scaling: Optional scaling configuration
"""
name: str
container_registry_settings: ContainerRegistrySettings
containers: List[Container]
compute: ComputeResource
is_spot: bool = False
endpoint_base_url: Optional[str] = None
scaling: Optional[ScalingOptions] = None


@dataclass_json(undefined=Undefined.EXCLUDE)
@dataclass
class DeploymentInfo:
"""Configuration for a container deployment.
This class is read-only and includes system-managed fields.

:param name: Name of the deployment
:param container_registry_settings: Settings for accessing container registry
Expand All @@ -237,7 +283,7 @@ class Deployment:
"""
name: str
container_registry_settings: ContainerRegistrySettings
containers: List[Container]
containers: List[ContainerInfo]
compute: ComputeResource
is_spot: bool = False
endpoint_base_url: Optional[str] = None
Expand Down Expand Up @@ -359,59 +405,59 @@ def __init__(self, http_client) -> None:
"""
self.client = http_client

def get_deployments(self) -> List[Deployment]:
def get_deployments(self) -> List[DeploymentInfo]:
"""Get all deployments

:return: list of deployments
:rtype: List[Deployment]
:rtype: List[DeploymentInfo]
"""
response = self.client.get(CONTAINER_DEPLOYMENTS_ENDPOINT)
return [Deployment.from_dict(deployment, infer_missing=True) for deployment in response.json()]
return [DeploymentInfo.from_dict(deployment, infer_missing=True) for deployment in response.json()]

def get_deployment_by_name(self, deployment_name: str) -> Deployment:
def get_deployment_by_name(self, deployment_name: str) -> DeploymentInfo:
"""Get a deployment by name

:param deployment_name: name of the deployment
:type deployment_name: str
:return: deployment
:rtype: Deployment
:rtype: DeploymentInfo
"""
response = self.client.get(
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}")
return Deployment.from_dict(response.json(), infer_missing=True)
return DeploymentInfo.from_dict(response.json(), infer_missing=True)

def create_deployment(
self,
deployment: Deployment
) -> Deployment:
) -> DeploymentInfo:
"""Create a new deployment

:param deployment: deployment configuration
:type deployment: Deployment
:return: created deployment
:rtype: Deployment
:rtype: DeploymentInfo
"""
response = self.client.post(
CONTAINER_DEPLOYMENTS_ENDPOINT,
deployment.to_dict()
)
return Deployment.from_dict(response.json(), infer_missing=True)
return DeploymentInfo.from_dict(response.json(), infer_missing=True)

def update_deployment(self, deployment_name: str, deployment: Deployment) -> Deployment:
def update_deployment(self, deployment_name: str, deployment: DeploymentInfo) -> DeploymentInfo:
"""Update an existing deployment

:param deployment_name: name of the deployment to update
:type deployment_name: str
:param deployment: updated deployment
:type deployment: Deployment
:type deployment: DeploymentInfo
:return: updated deployment
:rtype: Deployment
:rtype: DeploymentInfo
"""
response = self.client.patch(
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}",
deployment.to_dict()
)
return Deployment.from_dict(response.json(), infer_missing=True)
return DeploymentInfo.from_dict(response.json(), infer_missing=True)

def delete_deployment(self, deployment_name: str) -> None:
"""Delete a deployment
Expand Down
2 changes: 0 additions & 2 deletions examples/containers/container_deployments_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

# Configuration constants
DEPLOYMENT_NAME = "my-deployment"
CONTAINER_NAME = "my-app"
IMAGE_NAME = "your-image-name:version"

# Environment variables
Expand Down Expand Up @@ -93,7 +92,6 @@ def main() -> None:

# Create container configuration
container = Container(
name=CONTAINER_NAME,
image=IMAGE_NAME,
exposed_port=80,
healthcheck=HealthcheckSettings(
Expand Down
1 change: 0 additions & 1 deletion examples/containers/sglang_deployment_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ def main() -> None:

# Create container configuration
container = Container(
name=CONTAINER_NAME,
image=IMAGE_URL,
exposed_port=30000,
healthcheck=HealthcheckSettings(
Expand Down
19 changes: 10 additions & 9 deletions tests/unit_tests/containers/test_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
SECRETS_ENDPOINT,
SERVERLESS_COMPUTE_RESOURCES_ENDPOINT,
Container,
ContainerInfo,
ContainerDeploymentStatus,
ContainerRegistrySettings,
ContainersService,
Deployment,
DeploymentInfo,
EnvVar,
EnvVarType,
EntrypointOverridesSettings,
Expand Down Expand Up @@ -213,10 +215,10 @@ def test_get_deployments(self, containers_service, deployments_endpoint):
# assert
assert type(deployments) == list
assert len(deployments) == 1
assert type(deployment) == Deployment
assert type(deployment) == DeploymentInfo
assert deployment.name == DEPLOYMENT_NAME
assert len(deployment.containers) == 1
assert type(deployment.containers[0]) == Container
assert type(deployment.containers[0]) == ContainerInfo
assert type(deployment.compute) == ComputeResource
assert deployment.compute.name == COMPUTE_RESOURCE_NAME
assert responses.assert_call_count(deployments_endpoint, 1) is True
Expand All @@ -236,7 +238,7 @@ def test_get_deployment_by_name(self, containers_service, deployments_endpoint):
deployment = containers_service.get_deployment_by_name(DEPLOYMENT_NAME)

# assert
assert type(deployment) == Deployment
assert type(deployment) == DeploymentInfo
assert deployment.name == DEPLOYMENT_NAME
assert len(deployment.containers) == 1
assert deployment.containers[0].name == CONTAINER_NAME
Expand Down Expand Up @@ -273,9 +275,7 @@ def test_create_deployment(self, containers_service, deployments_endpoint):
status=200
)

# create deployment object
container = Container(
name=CONTAINER_NAME,
image="nginx:latest",
exposed_port=80,
healthcheck=HealthcheckSettings(
Expand All @@ -292,6 +292,7 @@ def test_create_deployment(self, containers_service, deployments_endpoint):
container_registry_settings = ContainerRegistrySettings(
is_private=False)

# create deployment object
deployment = Deployment(
name=DEPLOYMENT_NAME,
container_registry_settings=container_registry_settings,
Expand All @@ -304,7 +305,7 @@ def test_create_deployment(self, containers_service, deployments_endpoint):
created_deployment = containers_service.create_deployment(deployment)

# assert
assert type(created_deployment) == Deployment
assert type(created_deployment) == DeploymentInfo
assert created_deployment.name == DEPLOYMENT_NAME
assert len(created_deployment.containers) == 1
assert created_deployment.containers[0].name == CONTAINER_NAME
Expand All @@ -323,7 +324,7 @@ def test_update_deployment(self, containers_service, deployments_endpoint):
)

# create deployment object
container = Container(
container = ContainerInfo(
name=CONTAINER_NAME,
image="nginx:latest",
exposed_port=80
Expand All @@ -334,7 +335,7 @@ def test_update_deployment(self, containers_service, deployments_endpoint):

compute = ComputeResource(name=COMPUTE_RESOURCE_NAME, size=1)

deployment = Deployment(
deployment = DeploymentInfo(
name=DEPLOYMENT_NAME,
container_registry_settings=container_registry_settings,
containers=[container],
Expand All @@ -346,7 +347,7 @@ def test_update_deployment(self, containers_service, deployments_endpoint):
DEPLOYMENT_NAME, deployment)

# assert
assert type(updated_deployment) == Deployment
assert type(updated_deployment) == DeploymentInfo
assert updated_deployment.name == DEPLOYMENT_NAME
assert len(updated_deployment.containers) == 1
assert updated_deployment.containers[0].name == CONTAINER_NAME
Expand Down