|
17 | 17 | import pytest |
18 | 18 | from snowflake import snowpark |
19 | 19 | from snowflake.cli._plugins.remote.constants import ( |
| 20 | + BLOCK_STORAGE_VOLUME_NAME, |
| 21 | + DEFAULT_BLOCK_STORAGE_SIZE, |
20 | 22 | DEFAULT_IMAGE_TAG, |
21 | 23 | RAY_DASHBOARD_ENDPOINT_NAME, |
22 | 24 | SERVER_UI_ENDPOINT_NAME, |
| 25 | + USER_WORKSPACE_VOLUME_MOUNT_PATH, |
23 | 26 | WEBSOCKET_SSH_ENDPOINT_NAME, |
24 | 27 | ) |
25 | 28 | from snowflake.cli._plugins.remote.container_spec import ( |
@@ -418,3 +421,119 @@ def test_generate_spec_with_gpu_resources(self, mock_session): |
418 | 421 | assert resources["limits"]["cpu"] == "8000m" |
419 | 422 | assert resources["requests"]["memory"] == "32Gi" |
420 | 423 | assert resources["limits"]["memory"] == "32Gi" |
| 424 | + |
| 425 | + def test_generate_spec_without_stage_uses_block_storage(self, mock_session): |
| 426 | + """Test that block storage is created when no stage is provided.""" |
| 427 | + spec = generate_service_spec(session=mock_session, compute_pool="test_pool") |
| 428 | + |
| 429 | + # Check volumes |
| 430 | + volumes = spec["spec"]["volumes"] |
| 431 | + block_volume = next( |
| 432 | + (v for v in volumes if v["name"] == BLOCK_STORAGE_VOLUME_NAME), None |
| 433 | + ) |
| 434 | + |
| 435 | + # Block storage volume should exist |
| 436 | + assert block_volume is not None |
| 437 | + assert block_volume["source"] == "block" |
| 438 | + assert block_volume["size"] == DEFAULT_BLOCK_STORAGE_SIZE |
| 439 | + |
| 440 | + # Check volume mounts |
| 441 | + container = spec["spec"]["containers"][0] |
| 442 | + volume_mounts = container["volumeMounts"] |
| 443 | + block_mount = next( |
| 444 | + (vm for vm in volume_mounts if vm["name"] == BLOCK_STORAGE_VOLUME_NAME), |
| 445 | + None, |
| 446 | + ) |
| 447 | + |
| 448 | + # Block storage should be mounted at user workspace path |
| 449 | + assert block_mount is not None |
| 450 | + assert block_mount["mountPath"] == USER_WORKSPACE_VOLUME_MOUNT_PATH |
| 451 | + |
| 452 | + def test_generate_spec_with_stage_no_block_storage(self, mock_session): |
| 453 | + """Test that block storage is NOT created when stage is provided.""" |
| 454 | + spec = generate_service_spec( |
| 455 | + session=mock_session, compute_pool="test_pool", stage="@my_stage" |
| 456 | + ) |
| 457 | + |
| 458 | + # Check volumes |
| 459 | + volumes = spec["spec"]["volumes"] |
| 460 | + block_volume = next( |
| 461 | + (v for v in volumes if v["name"] == BLOCK_STORAGE_VOLUME_NAME), None |
| 462 | + ) |
| 463 | + |
| 464 | + # Block storage volume should NOT exist when stage is provided |
| 465 | + assert block_volume is None |
| 466 | + |
| 467 | + # Check that stage volumes are present instead |
| 468 | + workspace_volume = next(v for v in volumes if v["name"] == "user-workspace") |
| 469 | + assert workspace_volume["source"] == "@my_stage/user-default" |
| 470 | + |
| 471 | + def test_generate_spec_block_storage_yaml_format(self, mock_session): |
| 472 | + """Test that block storage is correctly formatted in YAML output.""" |
| 473 | + from snowflake.cli._plugins.remote.container_spec import ( |
| 474 | + generate_service_spec_yaml, |
| 475 | + ) |
| 476 | + |
| 477 | + yaml_output = generate_service_spec_yaml( |
| 478 | + session=mock_session, compute_pool="test_pool" |
| 479 | + ) |
| 480 | + |
| 481 | + # Verify it's valid YAML |
| 482 | + import yaml |
| 483 | + |
| 484 | + parsed_spec = yaml.safe_load(yaml_output) |
| 485 | + |
| 486 | + # Check block storage volume exists |
| 487 | + volumes = parsed_spec["spec"]["volumes"] |
| 488 | + block_volume = next( |
| 489 | + (v for v in volumes if v["name"] == BLOCK_STORAGE_VOLUME_NAME), None |
| 490 | + ) |
| 491 | + |
| 492 | + assert block_volume is not None |
| 493 | + assert block_volume["source"] == "block" |
| 494 | + assert block_volume["size"] == DEFAULT_BLOCK_STORAGE_SIZE |
| 495 | + |
| 496 | + # Check that YAML string contains block storage references |
| 497 | + assert BLOCK_STORAGE_VOLUME_NAME in yaml_output |
| 498 | + assert "source: block" in yaml_output |
| 499 | + assert DEFAULT_BLOCK_STORAGE_SIZE in yaml_output |
| 500 | + |
| 501 | + def test_generate_spec_block_storage_size(self, mock_session): |
| 502 | + """Test that block storage has correct default size.""" |
| 503 | + spec = generate_service_spec(session=mock_session, compute_pool="test_pool") |
| 504 | + |
| 505 | + volumes = spec["spec"]["volumes"] |
| 506 | + block_volume = next( |
| 507 | + v for v in volumes if v["name"] == BLOCK_STORAGE_VOLUME_NAME |
| 508 | + ) |
| 509 | + |
| 510 | + # Verify the default size is set correctly (10Gi as per constant) |
| 511 | + assert block_volume["size"] == "10Gi" |
| 512 | + |
| 513 | + def test_generate_spec_block_storage_only_one_workspace_mount(self, mock_session): |
| 514 | + """Test that only block storage OR stage volumes are present, not both.""" |
| 515 | + # Test without stage - should have block storage |
| 516 | + spec_no_stage = generate_service_spec( |
| 517 | + session=mock_session, compute_pool="test_pool" |
| 518 | + ) |
| 519 | + |
| 520 | + volumes_no_stage = spec_no_stage["spec"]["volumes"] |
| 521 | + volume_names_no_stage = [v["name"] for v in volumes_no_stage] |
| 522 | + |
| 523 | + # Should have block storage, but not stage-based volumes |
| 524 | + assert BLOCK_STORAGE_VOLUME_NAME in volume_names_no_stage |
| 525 | + assert "user-workspace" not in volume_names_no_stage |
| 526 | + assert "user-vscode-data" not in volume_names_no_stage |
| 527 | + |
| 528 | + # Test with stage - should have stage volumes |
| 529 | + spec_with_stage = generate_service_spec( |
| 530 | + session=mock_session, compute_pool="test_pool", stage="@my_stage" |
| 531 | + ) |
| 532 | + |
| 533 | + volumes_with_stage = spec_with_stage["spec"]["volumes"] |
| 534 | + volume_names_with_stage = [v["name"] for v in volumes_with_stage] |
| 535 | + |
| 536 | + # Should have stage volumes, but not block storage |
| 537 | + assert "user-workspace" in volume_names_with_stage |
| 538 | + assert "user-vscode-data" in volume_names_with_stage |
| 539 | + assert BLOCK_STORAGE_VOLUME_NAME not in volume_names_with_stage |
0 commit comments