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
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ def to_api_volume(volume: Volume):
from opensandbox.api.lifecycle.types import UNSET

api_host = UNSET
if volume.host is not None:
if volume.host is not None and not isinstance(volume.host, Unset):
Comment thread
Pangjiping marked this conversation as resolved.
api_host = ApiHost(path=volume.host.path)

api_pvc = UNSET
if volume.pvc is not None:
if volume.pvc is not None and not isinstance(volume.pvc, Unset):
Comment thread
Pangjiping marked this conversation as resolved.
api_pvc = ApiPVC(
claim_name=volume.pvc.claim_name,
create_if_not_exists=volume.pvc.create_if_not_exists,
Expand All @@ -143,7 +143,12 @@ def to_api_volume(volume: Volume):
)

api_ossfs = UNSET
if volume.ossfs is not None and volume.ossfs.access_key_id is not None and volume.ossfs.access_key_secret is not None:
if (
volume.ossfs is not None
and not isinstance(volume.ossfs, Unset)
and volume.ossfs.access_key_id is not None
and volume.ossfs.access_key_secret is not None
):
api_ossfs = ApiOSSFS(
bucket=volume.ossfs.bucket,
endpoint=volume.ossfs.endpoint,
Expand All @@ -154,7 +159,7 @@ def to_api_volume(volume: Volume):
)

api_sub_path = UNSET
if volume.sub_path is not None:
if volume.sub_path is not None and not isinstance(volume.sub_path, Unset):
api_sub_path = volume.sub_path

return ApiVolume(
Expand Down
60 changes: 60 additions & 0 deletions sdks/sandbox/python/tests/test_converters_and_error_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,66 @@ def test_sandbox_model_converter_snapshot_restore_request() -> None:
assert "image" not in dumped
assert "entrypoint" not in dumped

def test_sandbox_model_converter_to_api_volume_skips_unset_fields() -> None:
from opensandbox.api.lifecycle.types import UNSET
from opensandbox.models.sandboxes import Volume

# Inject UNSET into backend fields (bypassing Pydantic validation) to
# simulate a domain Volume carrying Unset values from an API round-trip.
volume = Volume.model_construct(
name="workdir",
mount_path="/mnt/work",
read_only=False,
host=UNSET,
pvc=UNSET,
ossfs=UNSET,
sub_path=UNSET,
)

api_volume = SandboxModelConverter.to_api_volume(volume)
dumped = api_volume.to_dict()
assert dumped == {"name": "workdir", "mountPath": "/mnt/work", "readOnly": False}
assert "host" not in dumped
assert "pvc" not in dumped
assert "ossfs" not in dumped
assert "subPath" not in dumped

def test_sandbox_model_converter_to_api_volume_maps_backends() -> None:
from opensandbox.models.sandboxes import OSSFS, PVC, Host, Volume

volume = Volume(
name="workdir",
host=Host(path="/data/opensandbox"),
mount_path="/mnt/work",
sub_path="sub",
)
dumped = SandboxModelConverter.to_api_volume(volume).to_dict()
assert dumped["host"] == {"path": "/data/opensandbox"}
assert dumped["subPath"] == "sub"
assert "pvc" not in dumped
assert "ossfs" not in dumped

pvc_volume = Volume(
name="models",
pvc=PVC(claim_name="shared-models-pvc"),
mount_path="/mnt/models",
read_only=True,
)
pvc_dumped = SandboxModelConverter.to_api_volume(pvc_volume).to_dict()
assert pvc_dumped["pvc"]["claimName"] == "shared-models-pvc"
assert pvc_dumped["readOnly"] is True

ossfs_volume = Volume(
name="oss", ossfs=OSSFS(
bucket="b",
endpoint="oss-cn-hangzhou.aliyuncs.com",
accessKeyId="ak",
accessKeySecret="sk",
),
mount_path="/mnt/oss",
)
ossfs_dumped = SandboxModelConverter.to_api_volume(ossfs_volume).to_dict()
assert ossfs_dumped["ossfs"]["bucket"] == "b"

def test_sandbox_model_converter_maps_platform_from_create_response() -> None:
from opensandbox.api.lifecycle.models.create_sandbox_response import (
Expand Down
Loading