Skip to content

Commit c2d866b

Browse files
committed
RHOAIENG-33283: Change ConfigMaps to Secrets
1 parent e9a7766 commit c2d866b

File tree

7 files changed

+217
-209
lines changed

7 files changed

+217
-209
lines changed

src/codeflare_sdk/ray/rayjobs/config.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from kubernetes.client import (
2525
V1ConfigMapVolumeSource,
2626
V1KeyToPath,
27+
V1LocalObjectReference,
28+
V1SecretVolumeSource,
2729
V1Toleration,
2830
V1Volume,
2931
V1VolumeMount,
@@ -415,8 +417,6 @@ def _build_pod_spec(self, container: V1Container, is_head: bool) -> V1PodSpec:
415417

416418
# Add image pull secrets if specified
417419
if hasattr(self, "image_pull_secrets") and self.image_pull_secrets:
418-
from kubernetes.client import V1LocalObjectReference
419-
420420
pod_spec.image_pull_secrets = [
421421
V1LocalObjectReference(name=secret)
422422
for secret in self.image_pull_secrets
@@ -448,12 +448,12 @@ def _build_env_vars(self) -> list:
448448
"""Build environment variables list."""
449449
return [V1EnvVar(name=key, value=value) for key, value in self.envs.items()]
450450

451-
def add_file_volumes(self, configmap_name: str, mount_path: str = MOUNT_PATH):
451+
def add_file_volumes(self, secret_name: str, mount_path: str = MOUNT_PATH):
452452
"""
453453
Add file volume and mount references to cluster configuration.
454454
455455
Args:
456-
configmap_name: Name of the ConfigMap containing files
456+
secret_name: Name of the Secret containing files
457457
mount_path: Where to mount files in containers (default: /home/ray/scripts)
458458
"""
459459
# Check if file volume already exists
@@ -478,7 +478,7 @@ def add_file_volumes(self, configmap_name: str, mount_path: str = MOUNT_PATH):
478478

479479
# Add file volume to cluster configuration
480480
file_volume = V1Volume(
481-
name=volume_name, config_map=V1ConfigMapVolumeSource(name=configmap_name)
481+
name=volume_name, secret=V1SecretVolumeSource(secret_name=secret_name)
482482
)
483483
self.volumes.append(file_volume)
484484

@@ -487,36 +487,37 @@ def add_file_volumes(self, configmap_name: str, mount_path: str = MOUNT_PATH):
487487
self.volume_mounts.append(file_mount)
488488

489489
logger.info(
490-
f"Added file volume '{configmap_name}' to cluster config: mount_path={mount_path}"
490+
f"Added file volume '{secret_name}' to cluster config: mount_path={mount_path}"
491491
)
492492

493-
def validate_configmap_size(self, files: Dict[str, str]) -> None:
493+
def validate_secret_size(self, files: Dict[str, str]) -> None:
494494
total_size = sum(len(content.encode("utf-8")) for content in files.values())
495495
if total_size > 1024 * 1024: # 1MB
496496
raise ValueError(
497-
f"ConfigMap size exceeds 1MB limit. Total size: {total_size} bytes"
497+
f"Secret size exceeds 1MB limit. Total size: {total_size} bytes"
498498
)
499499

500-
def build_file_configmap_spec(
500+
def build_file_secret_spec(
501501
self, job_name: str, namespace: str, files: Dict[str, str]
502502
) -> Dict[str, Any]:
503503
"""
504-
Build ConfigMap specification for files
504+
Build Secret specification for files
505505
506506
Args:
507-
job_name: Name of the RayJob (used for ConfigMap naming)
507+
job_name: Name of the RayJob (used for Secret naming)
508508
namespace: Kubernetes namespace
509509
files: Dictionary of file_name -> file_content
510510
511511
Returns:
512-
Dict: ConfigMap specification ready for Kubernetes API
512+
Dict: Secret specification ready for Kubernetes API
513513
"""
514-
configmap_name = f"{job_name}-files"
514+
secret_name = f"{job_name}-files"
515515
return {
516516
"apiVersion": "v1",
517-
"kind": "ConfigMap",
517+
"kind": "Secret",
518+
"type": "Opaque",
518519
"metadata": {
519-
"name": configmap_name,
520+
"name": secret_name,
520521
"namespace": namespace,
521522
"labels": {
522523
"ray.io/job-name": job_name,
@@ -528,19 +529,19 @@ def build_file_configmap_spec(
528529
}
529530

530531
def build_file_volume_specs(
531-
self, configmap_name: str, mount_path: str = MOUNT_PATH
532+
self, secret_name: str, mount_path: str = MOUNT_PATH
532533
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
533534
"""
534535
Build volume and mount specifications for files
535536
536537
Args:
537-
configmap_name: Name of the ConfigMap containing files
538+
secret_name: Name of the Secret containing files
538539
mount_path: Where to mount files in containers
539540
540541
Returns:
541542
Tuple of (volume_spec, mount_spec) as dictionaries
542543
"""
543-
volume_spec = {"name": "ray-job-files", "configMap": {"name": configmap_name}}
544+
volume_spec = {"name": "ray-job-files", "secret": {"secretName": secret_name}}
544545

545546
mount_spec = {"name": "ray-job-files", "mountPath": mount_path}
546547

src/codeflare_sdk/ray/rayjobs/rayjob.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from python_client.kuberay_cluster_api import RayClusterApi
3232
from codeflare_sdk.ray.rayjobs.config import ManagedClusterConfig
3333
from codeflare_sdk.ray.rayjobs.runtime_env import (
34-
create_file_configmap,
34+
create_file_secret,
3535
extract_all_local_files,
3636
process_runtime_env,
3737
)
@@ -169,10 +169,10 @@ def submit(self) -> str:
169169
# Extract files from entrypoint and runtime_env working_dir
170170
files = extract_all_local_files(self)
171171

172-
# Create ConfigMap for files (will be mounted to submitter pod)
173-
configmap_name = None
172+
# Create Secret for files (will be mounted to submitter pod)
173+
secret_name = None
174174
if files:
175-
configmap_name = f"{self.name}-files"
175+
secret_name = f"{self.name}-files"
176176

177177
rayjob_cr = self._build_rayjob_cr()
178178

@@ -182,9 +182,9 @@ def submit(self) -> str:
182182
if result:
183183
logger.info(f"Successfully submitted RayJob {self.name}")
184184

185-
# Create ConfigMap with owner reference after RayJob exists
185+
# Create Secret with owner reference after RayJob exists
186186
if files:
187-
create_file_configmap(self, files, result)
187+
create_file_secret(self, files, result)
188188

189189
return self.name
190190
else:
@@ -285,10 +285,10 @@ def _build_rayjob_cr(self) -> Dict[str, Any]:
285285

286286
# Add submitterPodTemplate if we have files to mount
287287
if files:
288-
configmap_name = f"{self.name}-files"
288+
secret_name = f"{self.name}-files"
289289
rayjob_cr["spec"][
290290
"submitterPodTemplate"
291-
] = self._build_submitter_pod_template(files, configmap_name)
291+
] = self._build_submitter_pod_template(files, secret_name)
292292

293293
# Configure cluster: either use existing or create new
294294
if self._cluster_config is not None:
@@ -311,17 +311,17 @@ def _build_rayjob_cr(self) -> Dict[str, Any]:
311311
return rayjob_cr
312312

313313
def _build_submitter_pod_template(
314-
self, files: Dict[str, str], configmap_name: str
314+
self, files: Dict[str, str], secret_name: str
315315
) -> Dict[str, Any]:
316316
"""
317-
Build submitterPodTemplate with ConfigMap volume mount for local files.
317+
Build submitterPodTemplate with Secret volume mount for local files.
318318
319319
If files contain working_dir.zip, an init container will unzip it before
320320
the main submitter container runs.
321321
322322
Args:
323323
files: Dict of file_name -> file_content
324-
configmap_name: Name of the ConfigMap containing the files
324+
secret_name: Name of the Secret containing the files
325325
326326
Returns:
327327
submitterPodTemplate specification
@@ -337,8 +337,8 @@ def _build_submitter_pod_template(
337337
):
338338
image = self._cluster_config.image
339339

340-
# Build ConfigMap items for each file
341-
config_map_items = []
340+
# Build Secret items for each file
341+
secret_items = []
342342
entrypoint_path = files.get(
343343
"__entrypoint_path__"
344344
) # Metadata for single file case
@@ -349,9 +349,9 @@ def _build_submitter_pod_template(
349349

350350
# For single file case, use the preserved path structure
351351
if entrypoint_path:
352-
config_map_items.append({"key": file_name, "path": entrypoint_path})
352+
secret_items.append({"key": file_name, "path": entrypoint_path})
353353
else:
354-
config_map_items.append({"key": file_name, "path": file_name})
354+
secret_items.append({"key": file_name, "path": file_name})
355355

356356
# Check if we need to unzip working_dir
357357
has_working_dir_zip = "working_dir.zip" in files
@@ -378,9 +378,9 @@ def _build_submitter_pod_template(
378378
"volumes": [
379379
{
380380
"name": "ray-job-files",
381-
"configMap": {
382-
"name": configmap_name,
383-
"items": config_map_items,
381+
"secret": {
382+
"secretName": secret_name,
383+
"items": secret_items,
384384
},
385385
}
386386
],

src/codeflare_sdk/ray/rayjobs/runtime_env.py

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def _normalize_runtime_env(
3939

4040
def extract_all_local_files(job: RayJob) -> Optional[Dict[str, str]]:
4141
"""
42-
Prepare local files for ConfigMap upload.
42+
Prepare local files for Secret upload.
4343
4444
- If runtime_env has local working_dir: zip entire directory into single file
4545
- If single entrypoint file (no working_dir): extract that file
@@ -76,7 +76,7 @@ def extract_all_local_files(job: RayJob) -> Optional[Dict[str, str]]:
7676
logger.info(f"Zipping local working_dir: {working_dir}")
7777
zip_data = _zip_directory(working_dir)
7878
if zip_data:
79-
# Encode zip as base64 for ConfigMap storage
79+
# Encode zip as base64 for Secret storage
8080
zip_base64 = base64.b64encode(zip_data).decode("utf-8")
8181
return {"working_dir.zip": zip_base64}
8282

@@ -128,7 +128,7 @@ def _extract_single_entrypoint_file(job: RayJob) -> Optional[Dict[str, str]]:
128128
Extract single Python file from entrypoint if no working_dir specified.
129129
130130
Returns a dict with metadata about the file path structure so we can
131-
preserve it when mounting via ConfigMap.
131+
preserve it when mounting via Secret.
132132
133133
Args:
134134
job: RayJob instance
@@ -150,8 +150,8 @@ def _extract_single_entrypoint_file(job: RayJob) -> Optional[Dict[str, str]]:
150150
with open(file_path, "r") as f:
151151
content = f.read()
152152

153-
# Use basename as key (ConfigMap keys can't have slashes)
154-
# But store the full path for later use in ConfigMap item.path
153+
# Use basename as key (Secret keys can't have slashes)
154+
# But store the full path for later use in Secret item.path
155155
filename = os.path.basename(file_path)
156156
relative_path = file_path.lstrip("./")
157157

@@ -283,23 +283,23 @@ def parse_requirements_file(requirements_path: str) -> Optional[List[str]]:
283283
return None
284284

285285

286-
def create_configmap_from_spec(
287-
job: RayJob, configmap_spec: Dict[str, Any], rayjob_result: Dict[str, Any] = None
286+
def create_secret_from_spec(
287+
job: RayJob, secret_spec: Dict[str, Any], rayjob_result: Dict[str, Any] = None
288288
) -> str:
289289
"""
290-
Create ConfigMap from specification via Kubernetes API.
290+
Create Secret from specification via Kubernetes API.
291291
292292
Args:
293-
configmap_spec: ConfigMap specification dictionary
293+
secret_spec: Secret specification dictionary
294294
rayjob_result: The result from RayJob creation containing UID
295295
296296
Returns:
297-
str: Name of the created ConfigMap
297+
str: Name of the created Secret
298298
"""
299299

300-
configmap_name = configmap_spec["metadata"]["name"]
300+
secret_name = secret_spec["metadata"]["name"]
301301

302-
metadata = client.V1ObjectMeta(**configmap_spec["metadata"])
302+
metadata = client.V1ObjectMeta(**secret_spec["metadata"])
303303

304304
# Add owner reference if we have the RayJob result
305305
if (
@@ -308,7 +308,7 @@ def create_configmap_from_spec(
308308
and rayjob_result.get("metadata", {}).get("uid")
309309
):
310310
logger.info(
311-
f"Adding owner reference to ConfigMap '{configmap_name}' with RayJob UID: {rayjob_result['metadata']['uid']}"
311+
f"Adding owner reference to Secret '{secret_name}' with RayJob UID: {rayjob_result['metadata']['uid']}"
312312
)
313313
metadata.owner_references = [
314314
client.V1OwnerReference(
@@ -322,52 +322,55 @@ def create_configmap_from_spec(
322322
]
323323
else:
324324
logger.warning(
325-
f"No valid RayJob result with UID found, ConfigMap '{configmap_name}' will not have owner reference. Result: {rayjob_result}"
325+
f"No valid RayJob result with UID found, Secret '{secret_name}' will not have owner reference. Result: {rayjob_result}"
326326
)
327327

328-
# Convert dict spec to V1ConfigMap
329-
configmap = client.V1ConfigMap(
328+
# Convert dict spec to V1Secret
329+
# Use stringData instead of data to avoid double base64 encoding
330+
# Our zip files are already base64-encoded, so stringData will handle the final encoding
331+
secret = client.V1Secret(
330332
metadata=metadata,
331-
data=configmap_spec["data"],
333+
type=secret_spec.get("type", "Opaque"),
334+
string_data=secret_spec["data"],
332335
)
333336

334-
# Create ConfigMap via Kubernetes API
337+
# Create Secret via Kubernetes API
335338
k8s_api = client.CoreV1Api(get_api_client())
336339
try:
337-
k8s_api.create_namespaced_config_map(namespace=job.namespace, body=configmap)
340+
k8s_api.create_namespaced_secret(namespace=job.namespace, body=secret)
338341
logger.info(
339-
f"Created ConfigMap '{configmap_name}' with {len(configmap_spec['data'])} files"
342+
f"Created Secret '{secret_name}' with {len(secret_spec['data'])} files"
340343
)
341344
except client.ApiException as e:
342345
if e.status == 409: # Already exists
343-
logger.info(f"ConfigMap '{configmap_name}' already exists, updating...")
344-
k8s_api.replace_namespaced_config_map(
345-
name=configmap_name, namespace=job.namespace, body=configmap
346+
logger.info(f"Secret '{secret_name}' already exists, updating...")
347+
k8s_api.replace_namespaced_secret(
348+
name=secret_name, namespace=job.namespace, body=secret
346349
)
347350
else:
348-
raise RuntimeError(f"Failed to create ConfigMap '{configmap_name}': {e}")
351+
raise RuntimeError(f"Failed to create Secret '{secret_name}': {e}")
349352

350-
return configmap_name
353+
return secret_name
351354

352355

353-
def create_file_configmap(
356+
def create_file_secret(
354357
job: RayJob, files: Dict[str, str], rayjob_result: Dict[str, Any]
355358
):
356359
"""
357-
Create ConfigMap with owner reference for local files.
360+
Create Secret with owner reference for local files.
358361
"""
359-
# Use a basic config builder for ConfigMap creation
362+
# Use a basic config builder for Secret creation
360363
config_builder = ManagedClusterConfig()
361364

362-
# Filter out metadata keys (like __entrypoint_path__) from ConfigMap data
363-
configmap_files = {k: v for k, v in files.items() if not k.startswith("__")}
365+
# Filter out metadata keys (like __entrypoint_path__) from Secret data
366+
secret_files = {k: v for k, v in files.items() if not k.startswith("__")}
364367

365-
# Validate and build ConfigMap spec
366-
config_builder.validate_configmap_size(configmap_files)
367-
configmap_spec = config_builder.build_file_configmap_spec(
368-
job_name=job.name, namespace=job.namespace, files=configmap_files
368+
# Validate and build Secret spec
369+
config_builder.validate_secret_size(secret_files)
370+
secret_spec = config_builder.build_file_secret_spec(
371+
job_name=job.name, namespace=job.namespace, files=secret_files
369372
)
370373

371-
# Create ConfigMap with owner reference
374+
# Create Secret with owner reference
372375
# TODO Error handling
373-
create_configmap_from_spec(job, configmap_spec, rayjob_result)
376+
create_secret_from_spec(job, secret_spec, rayjob_result)

0 commit comments

Comments
 (0)