Skip to content

Commit 74e6ebc

Browse files
lukebaumanncopybara-github
authored andcommitted
Add Colocated Python support to PathwaysJobSet.
PiperOrigin-RevId: 948118787
1 parent 5690461 commit 74e6ebc

2 files changed

Lines changed: 215 additions & 0 deletions

File tree

pathwaysutils/experimental/gke/jobset.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,77 @@ def _add_volume_to_pod_spec(
574574
volumes.append(volume)
575575
pod_spec.volumes = volumes
576576

577+
def add_colocated_python(
578+
self,
579+
image: str,
580+
shm_mount_path: str = "/tmp/shared-memory",
581+
shm_size_limit: str | None = None,
582+
) -> "PathwaysJobSet":
583+
"""Adds colocated python sidecar to the worker pods."""
584+
pod_spec = self._worker_job_template.spec.template.spec
585+
586+
# Add shared memory volume if not exists.
587+
volumes = pod_spec.volumes or []
588+
shm_volume_name = "shared-memory"
589+
shm_exists = any(v.name == shm_volume_name for v in volumes)
590+
if not shm_exists:
591+
volumes.append(
592+
client.V1Volume(
593+
name=shm_volume_name,
594+
empty_dir=client.V1EmptyDirVolumeSource(
595+
medium="Memory", size_limit=shm_size_limit
596+
),
597+
)
598+
)
599+
pod_spec.volumes = volumes
600+
601+
# Add colocated python container.
602+
colocated_container = client.V1Container(
603+
name="colocated-python-sidecar",
604+
image=image,
605+
image_pull_policy="Always",
606+
env=[
607+
client.V1EnvVar(name="GRPC_SERVER_ADDRESS", value="0.0.0.0:50051"),
608+
client.V1EnvVar(
609+
name="CLOUD_PATHWAYS_SIDECAR_SHM_DIRECTORY",
610+
value=shm_mount_path,
611+
),
612+
],
613+
ports=[client.V1ContainerPort(container_port=50051)],
614+
volume_mounts=[
615+
client.V1VolumeMount(name="shared-tmp", mount_path="/tmp"),
616+
client.V1VolumeMount(name=shm_volume_name, mount_path=shm_mount_path),
617+
],
618+
)
619+
colocated_container.restart_policy = "Always" # pyrefly: ignore[missing-attribute]
620+
621+
init_containers = pod_spec.init_containers or []
622+
init_containers.append(colocated_container)
623+
pod_spec.init_containers = init_containers
624+
625+
# Add volume mount to pathways-worker.
626+
for container in pod_spec.containers:
627+
if container.name == "pathways-worker":
628+
volume_mounts = container.volume_mounts or []
629+
volume_mounts.append(
630+
client.V1VolumeMount(
631+
name=shm_volume_name, mount_path=shm_mount_path
632+
)
633+
)
634+
container.volume_mounts = volume_mounts
635+
# Add env var for shm dir.
636+
env = container.env or []
637+
env.append(
638+
client.V1EnvVar(
639+
name="cloud_pathways_sidecar_shm_directory",
640+
value=shm_mount_path,
641+
)
642+
)
643+
container.env = env
644+
break
645+
646+
return self
647+
577648
def add_gcsfuse(
578649
self,
579650
containers: str | Sequence[str],

pathwaysutils/test/experimental/gke/jobset_test.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,150 @@ def test_add_gcsfuse_preserves_existing_metadata(self):
475475
self.assertEqual(pod_meta.get("annotations", {}).get("existing-pod-anno"), "value")
476476
self.assertEqual(pod_meta.get("annotations", {}).get("gke-gcsfuse/volumes"), "true")
477477

478+
def test_add_colocated_python_sidecar(self):
479+
pw_jobset = self._create_jobset(topology="2x2", num_slices=1)
480+
481+
pw_jobset.add_colocated_python(image="gcr.io/my-project/colocated-python:custom")
482+
helper = JobSetManifestHelper(pw_jobset.to_dict())
483+
484+
self.assertIn("colocated-python-sidecar", helper.init_containers["pathways-worker"])
485+
sidecar = helper.init_containers["pathways-worker"]["colocated-python-sidecar"]
486+
self.assertEqual(sidecar["restartPolicy"], "Always")
487+
self.assertEqual(sidecar["image"], "gcr.io/my-project/colocated-python:custom")
488+
self.assertTrue(
489+
any(
490+
m["name"] == "shared-memory" and m["mountPath"] == "/tmp/shared-memory"
491+
for m in sidecar["volumeMounts"]
492+
)
493+
)
494+
self.assertTrue(
495+
any(
496+
e["name"] == "CLOUD_PATHWAYS_SIDECAR_SHM_DIRECTORY"
497+
and e["value"] == "/tmp/shared-memory"
498+
for e in sidecar["env"]
499+
)
500+
)
501+
502+
def test_add_colocated_python_preserves_init_containers(self):
503+
pw_jobset = self._create_jobset(topology="2x2", num_slices=1)
504+
505+
# Pre-populate init container on worker pod
506+
worker_spec = pw_jobset._worker_job_template.spec.template.spec
507+
existing_init = client.V1Container(name="existing-init-container", image="ubuntu:latest")
508+
worker_spec.init_containers = [existing_init]
509+
510+
pw_jobset.add_colocated_python(image="gcr.io/my-project/colocated-python:custom")
511+
helper = JobSetManifestHelper(pw_jobset.to_dict())
512+
513+
# Verify both exist
514+
self.assertIn("existing-init-container", helper.init_containers["pathways-worker"])
515+
self.assertIn("colocated-python-sidecar", helper.init_containers["pathways-worker"])
516+
517+
def test_add_colocated_python_volume_default(self):
518+
pw_jobset = self._create_jobset(topology="2x2", num_slices=1)
519+
520+
pw_jobset.add_colocated_python(image="gcr.io/my-project/colocated-python:custom")
521+
helper = JobSetManifestHelper(pw_jobset.to_dict())
522+
523+
self.assertIn("shared-memory", helper.volumes["pathways-worker"])
524+
shm_vol = helper.volumes["pathways-worker"]["shared-memory"]
525+
self.assertNotIn("sizeLimit", shm_vol["emptyDir"])
526+
527+
def test_add_colocated_python_worker_mount(self):
528+
pw_jobset = self._create_jobset(topology="2x2", num_slices=1)
529+
530+
pw_jobset.add_colocated_python(image="gcr.io/my-project/colocated-python:custom")
531+
helper = JobSetManifestHelper(pw_jobset.to_dict())
532+
533+
self.assertIn("pathways-worker", helper.containers["pathways-worker"])
534+
worker_container = helper.containers["pathways-worker"]["pathways-worker"]
535+
self.assertTrue(
536+
any(
537+
m["name"] == "shared-memory" and m["mountPath"] == "/tmp/shared-memory"
538+
for m in worker_container["volumeMounts"]
539+
)
540+
)
541+
self.assertTrue(
542+
any(
543+
e["name"] == "cloud_pathways_sidecar_shm_directory"
544+
and e["value"] == "/tmp/shared-memory"
545+
for e in worker_container["env"]
546+
)
547+
)
548+
549+
def test_add_colocated_python_custom_shm(self):
550+
pw_jobset = self._create_jobset(topology="2x2", num_slices=1)
551+
552+
pw_jobset.add_colocated_python(
553+
image="gcr.io/my-project/colocated-python:custom",
554+
shm_mount_path="/tmp/custom-shm",
555+
shm_size_limit="50Gi",
556+
)
557+
helper = JobSetManifestHelper(pw_jobset.to_dict())
478558

559+
self.assertIn("colocated-python-sidecar", helper.init_containers["pathways-worker"])
560+
sidecar = helper.init_containers["pathways-worker"]["colocated-python-sidecar"]
561+
self.assertTrue(
562+
any(
563+
m["name"] == "shared-memory" and m["mountPath"] == "/tmp/custom-shm"
564+
for m in sidecar["volumeMounts"]
565+
)
566+
)
567+
self.assertTrue(
568+
any(
569+
e["name"] == "CLOUD_PATHWAYS_SIDECAR_SHM_DIRECTORY"
570+
and e["value"] == "/tmp/custom-shm"
571+
for e in sidecar["env"]
572+
)
573+
)
574+
575+
self.assertIn("shared-memory", helper.volumes["pathways-worker"])
576+
shm_vol = helper.volumes["pathways-worker"]["shared-memory"]
577+
self.assertEqual(shm_vol["emptyDir"]["sizeLimit"], "50Gi")
578+
579+
self.assertIn("pathways-worker", helper.containers["pathways-worker"])
580+
worker_container = helper.containers["pathways-worker"]["pathways-worker"]
581+
self.assertTrue(
582+
any(
583+
m["name"] == "shared-memory" and m["mountPath"] == "/tmp/custom-shm"
584+
for m in worker_container["volumeMounts"]
585+
)
586+
)
587+
self.assertTrue(
588+
any(
589+
e["name"] == "cloud_pathways_sidecar_shm_directory"
590+
and e["value"] == "/tmp/custom-shm"
591+
for e in worker_container["env"]
592+
)
593+
)
594+
595+
def test_colocated_python_with_jax_command(self):
596+
jax_command = "import jax; print(jax.devices());"
597+
user_pod_template = {
598+
"spec": {
599+
"containers": [{
600+
"name": "jax-tpu",
601+
"image": "gcr.io/my-project/jax-tpu:latest",
602+
"command": ["python3", "-c", jax_command],
603+
}]
604+
}
605+
}
606+
pw_jobset = self._create_jobset(
607+
topology="2x2",
608+
num_slices=1,
609+
user_pod_template=user_pod_template,
610+
main_container_name="jax-tpu",
611+
)
612+
613+
pw_jobset.add_colocated_python(image="gcr.io/my-project/colocated-python:custom")
614+
helper = JobSetManifestHelper(pw_jobset.to_dict())
615+
616+
self.assertIn("pathways-head", helper.jobs)
617+
self.assertIn("jax-tpu", helper.containers["pathways-head"])
618+
jax_container = helper.containers["pathways-head"]["jax-tpu"]
619+
self.assertEqual(jax_container["command"], ["python3", "-c", jax_command])
620+
621+
self.assertIn("pathways-worker", helper.jobs)
622+
self.assertIn("colocated-python-sidecar", helper.init_containers["pathways-worker"])
479623
if __name__ == "__main__":
480624
absltest.main()

0 commit comments

Comments
 (0)