|
20 | 20 |
|
21 | 21 | import pathlib |
22 | 22 | from dataclasses import dataclass, field, fields |
23 | | -from typing import Dict, List, Optional, Union, get_args, get_origin, Any |
| 23 | +from typing import Dict, List, Optional, Union, get_args, get_origin, Any, Tuple |
24 | 24 | from kubernetes.client import ( |
25 | 25 | V1ConfigMapVolumeSource, |
26 | 26 | V1KeyToPath, |
@@ -515,3 +515,88 @@ def _build_gcs_ft_options(self) -> Dict[str, Any]: |
515 | 515 | } |
516 | 516 |
|
517 | 517 | return gcs_ft_options |
| 518 | + |
| 519 | + def add_script_volumes( |
| 520 | + self, configmap_name: str, mount_path: str = "/home/ray/scripts" |
| 521 | + ): |
| 522 | + """ |
| 523 | + Add script volume and mount references to cluster configuration. |
| 524 | +
|
| 525 | + Args: |
| 526 | + configmap_name: Name of the ConfigMap containing scripts |
| 527 | + mount_path: Where to mount scripts in containers (default: /home/ray/scripts) |
| 528 | + """ |
| 529 | + # Check if script volume already exists |
| 530 | + volume_name = "ray-job-scripts" |
| 531 | + existing_volume = next( |
| 532 | + (v for v in self.volumes if getattr(v, "name", None) == volume_name), None |
| 533 | + ) |
| 534 | + if existing_volume: |
| 535 | + logger.debug(f"Script volume '{volume_name}' already exists, skipping...") |
| 536 | + return |
| 537 | + |
| 538 | + # Check if script mount already exists |
| 539 | + existing_mount = next( |
| 540 | + (m for m in self.volume_mounts if getattr(m, "name", None) == volume_name), |
| 541 | + None, |
| 542 | + ) |
| 543 | + if existing_mount: |
| 544 | + logger.debug( |
| 545 | + f"Script volume mount '{volume_name}' already exists, skipping..." |
| 546 | + ) |
| 547 | + return |
| 548 | + |
| 549 | + # Add script volume to cluster configuration |
| 550 | + script_volume = V1Volume( |
| 551 | + name=volume_name, config_map=V1ConfigMapVolumeSource(name=configmap_name) |
| 552 | + ) |
| 553 | + self.volumes.append(script_volume) |
| 554 | + |
| 555 | + # Add script volume mount to cluster configuration |
| 556 | + script_mount = V1VolumeMount(name=volume_name, mount_path=mount_path) |
| 557 | + self.volume_mounts.append(script_mount) |
| 558 | + |
| 559 | + logger.info( |
| 560 | + f"Added script volume '{configmap_name}' to cluster config: mount_path={mount_path}" |
| 561 | + ) |
| 562 | + |
| 563 | + def build_script_configmap_spec( |
| 564 | + self, job_name: str, namespace: str, scripts: Dict[str, str] |
| 565 | + ) -> Dict[str, Any]: |
| 566 | + """ |
| 567 | + Build ConfigMap specification for scripts (no API calls). |
| 568 | +
|
| 569 | + Args: |
| 570 | + job_name: Name of the RayJob (used for ConfigMap naming) |
| 571 | + namespace: Kubernetes namespace |
| 572 | + scripts: Dictionary of script_name -> script_content |
| 573 | +
|
| 574 | + Returns: |
| 575 | + Dict: ConfigMap specification ready for Kubernetes API |
| 576 | + """ |
| 577 | + configmap_name = f"{job_name}-scripts" |
| 578 | + return { |
| 579 | + "apiVersion": "v1", |
| 580 | + "kind": "ConfigMap", |
| 581 | + "metadata": {"name": configmap_name, "namespace": namespace}, |
| 582 | + "data": scripts, |
| 583 | + } |
| 584 | + |
| 585 | + def build_script_volume_specs( |
| 586 | + self, configmap_name: str, mount_path: str = "/home/ray/scripts" |
| 587 | + ) -> Tuple[Dict[str, Any], Dict[str, Any]]: |
| 588 | + """ |
| 589 | + Build volume and mount specifications for scripts (no API calls). |
| 590 | +
|
| 591 | + Args: |
| 592 | + configmap_name: Name of the ConfigMap containing scripts |
| 593 | + mount_path: Where to mount scripts in containers |
| 594 | +
|
| 595 | + Returns: |
| 596 | + Tuple of (volume_spec, mount_spec) as dictionaries |
| 597 | + """ |
| 598 | + volume_spec = {"name": "ray-job-scripts", "configMap": {"name": configmap_name}} |
| 599 | + |
| 600 | + mount_spec = {"name": "ray-job-scripts", "mountPath": mount_path} |
| 601 | + |
| 602 | + return volume_spec, mount_spec |
0 commit comments