Skip to content

fix(k8s): propagate readOnly flag to PVC volume spec in pod manifest#1033

Open
ashishpatel26 wants to merge 2 commits into
opensandbox-group:mainfrom
ashishpatel26:fix/issue-545-readonly-pvc-flag
Open

fix(k8s): propagate readOnly flag to PVC volume spec in pod manifest#1033
ashishpatel26 wants to merge 2 commits into
opensandbox-group:mainfrom
ashishpatel26:fix/issue-545-readonly-pvc-flag

Conversation

@ashishpatel26

Copy link
Copy Markdown

Summary

  • volume_helper.py set readOnly on the VolumeMount but not on the persistentVolumeClaim volume source in the pod spec
  • CSI drivers such as mountpoint-s3-csi-driver enforce read-only at the volume-source level, so the mount stayed writable even when readOnly=True was declared
  • Also fixes a secondary bug: the dedup key was claim_name alone, so mounting the same PVC as both read-only and read-write in one pod would silently reuse the wrong volume entry

Changes

  • Set persistentVolumeClaim.readOnly: true in the pod volumes list when vol.read_only is True
  • Change dedup key from claim_name to (claim_name, read_only) to allow independent RO/RW mounts of the same PVC
  • Add focused unit tests in server/tests/k8s/test_volume_helper.py covering RW (no readOnly key), RO (both flags set), mixed RO+RW same PVC (two volume entries), host volume RO, subPath, and name conflict
  • Extend existing test_create_workload_with_pvc_volume_readonly to also assert persistentVolumeClaim.readOnly

Test plan

  • uv run pytest server/tests/k8s/test_volume_helper.py — 6 new tests, all pass
  • uv run pytest server/tests/k8s/test_batchsandbox_provider.py -k readonly — existing test passes with new assertion

Fixes #545

When a Volume with pvc backend and readOnly=True is mounted, only the
VolumeMount readOnly field was set. Some CSI drivers (e.g.
mountpoint-s3-csi-driver) require readOnly on the PersistentVolumeClaim
volume source as well, so the mount remained writable despite the flag.

Fix: also set persistentVolumeClaim.readOnly=true in the pod volumes list
when the volume is declared read-only.

Additionally change the dedup key from claim_name alone to
(claim_name, read_only) so the same PVC can be mounted both read-only
and read-write within the same pod without one mount silently overriding
the other.

Fixes opensandbox-group#545
Copilot AI review requested due to automatic review settings June 12, 2026 12:40
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This PR has no labels. Please add one based on the changes.

Changed directories: server.

📋 Recommended labels (based on changed files):

  • component/server ⬅️

Other available labels:

  • bug - Something isn't working
  • dependencies - Pull requests that update a dependency file
  • documentation - Improvements or additions to documentation
  • feature - New feature or request
  • packages - Changes for package, image and configuration

💡 Tip: Use feature for new functionality or improvements, bug for fixes.

cc @ashishpatel26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds support and test coverage for correctly handling read-only PVC mounts in Kubernetes pod specs, including allowing the same PVC to be mounted both read-only and read-write.

Changes:

  • Update apply_volumes_to_pod_spec to key PVC volume entries by (claim_name, read_only) and propagate readOnly into the PVC volume spec.
  • Improve structured logging for added volumes.
  • Add focused unit tests for PVC/host volume readOnly behavior, conflicts, and subPath forwarding.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
server/opensandbox_server/services/k8s/volume_helper.py Updates PVC volume mapping to support both RO/RW mounts and sets persistentVolumeClaim.readOnly when applicable.
server/tests/k8s/test_volume_helper.py New unit tests validating PVC/host readOnly propagation, RO+RW PVC behavior, conflicts, and subPath.
server/tests/k8s/test_batchsandbox_provider.py Extends an existing test to assert persistentVolumeClaim.readOnly is set for read-only PVCs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +60 to 77
if pvc_key not in pvc_to_volume_name:
pvc_volume: Dict[str, Any] = {
"claimName": pvc_claim_name,
}
if vol.read_only:
pvc_volume["readOnly"] = True
pod_volumes.append({
"name": vol_name,
"persistentVolumeClaim": {
"claimName": pvc_claim_name,
},
"persistentVolumeClaim": pvc_volume,
})
pvc_to_volume_name[pvc_claim_name] = vol_name
pvc_to_volume_name[pvc_key] = vol_name
existing_volume_names.add(vol_name)

mount = {
"name": pvc_to_volume_name[pvc_claim_name],
"name": pvc_to_volume_name[pvc_key],
"mountPath": vol.mount_path,
"readOnly": vol.read_only,
}
Comment on lines 82 to 86
logger.info(
f"Added PVC volume '{vol_name}' (claim: {pvc_claim_name}) mounted at '{vol.mount_path}' for sandbox"
"Added PVC volume '%s' (claim: %s, readOnly: %s) "
"mounted at '%s' for sandbox",
vol_name, pvc_claim_name, vol.read_only, vol.mount_path,
)
if pvc_claim_name not in pvc_to_volume_name:
pvc_key = (pvc_claim_name, vol.read_only)

if pvc_key not in pvc_to_volume_name:
Comment on lines 66 to 77
pod_volumes.append({
"name": vol_name,
"persistentVolumeClaim": {
"claimName": pvc_claim_name,
},
"persistentVolumeClaim": pvc_volume,
})
pvc_to_volume_name[pvc_claim_name] = vol_name
pvc_to_volume_name[pvc_key] = vol_name
existing_volume_names.add(vol_name)

mount = {
"name": pvc_to_volume_name[pvc_claim_name],
"name": pvc_to_volume_name[pvc_key],
"mountPath": vol.mount_path,
"readOnly": vol.read_only,
}
v.get("name") for v in pod_volumes if isinstance(v, dict)
}
# Key: (claim_name, read_only) so the same PVC can be mounted both RO and RW.
pvc_to_volume_name: Dict[tuple, str] = {}
Comment on lines +120 to +126
ro_mount = _get_mount(pod_spec, "/mnt/ro")
rw_mount = _get_mount(pod_spec, "/mnt/rw")

assert ro_mount["name"] == ro_entry["name"]
assert ro_mount["readOnly"] is True
assert rw_mount["name"] == rw_entry["name"]
assert rw_mount["readOnly"] is False
@ashishpatel26

Copy link
Copy Markdown
Author

Thanks for the reviews. Addressing feedback:

Copilot – Log emitted for every iteration, not just new entries: Valid. The logger.info call is inside the mount-append block but outside the if pvc_key not in pvc_to_volume_name guard, so it fires even when reusing an existing pod volume entry (and incorrectly logs the new vol_name rather than the mapped name). Will move the log inside the guard block and log the actual mapped volume name.

Copilot – Duplicate vol.name silently ignored: Valid. When two Volume entries share the same (claim_name, read_only) key but different name values, the second name is silently dropped. Will add a ValueError or at minimum a warning log when this occurs so callers are not surprised.

Copilot – Type annotation too generic: Valid. Will change Dict[tuple, str] to Dict[Tuple[str, bool], str] to make the key shape explicit.

Copilot – Missing assert ... is not None before subscript in test: Valid. Will add guards so failures produce a clear assertion error rather than TypeError: 'NoneType' is not subscriptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

readOnly: True flag in Volume definition is ignored for S3-backed PVCs

2 participants