Skip to content

Commit 9e23658

Browse files
authored
Kubernetes: add read_only volume property (#3838)
If set to `true`, enforces `readOnly: true` in `volumeMounts[]` Closes: #3837
1 parent db972ea commit 9e23658

4 files changed

Lines changed: 28 additions & 9 deletions

File tree

scripts/docs/gen_schema_reference.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import importlib
66
import inspect
7+
import json
78
import logging
89
import re
910
from enum import Enum
@@ -233,15 +234,26 @@ def generate_schema_reference(
233234
schema_props = {}
234235
for name, field in cls.__fields__.items():
235236
default = field.default
236-
if isinstance(default, Enum):
237-
default = default.value
237+
default_repr: Optional[str]
238+
if default is None:
239+
default_repr = None
240+
elif isinstance(default, (list, tuple, dict)) and len(default) == 0:
241+
default_repr = None
242+
elif isinstance(default, str):
243+
default_repr = default
244+
elif isinstance(default, BaseModel):
245+
default_repr = str(default)
246+
elif isinstance(default, Enum):
247+
default_repr = str(default.value)
248+
else:
249+
default_repr = json.dumps(default)
238250
friendly_type = get_friendly_type(field.annotation)
239251
friendly_type = _enrich_type_from_schema(friendly_type, schema_props.get(name, {}))
240252
values = dict(
241253
name=name,
242254
description=field.field_info.description,
243255
type=friendly_type,
244-
default=default,
256+
default=default_repr,
245257
required=field.required,
246258
)
247259
# TODO: If the field doesn't have description (e.g. BaseConfiguration.type), we could fallback to docstring

src/dstack/_internal/core/backends/kubernetes/compute.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ def run_job(
275275
else:
276276
assert False, f"unexpected mount point: {mount_point!r}"
277277
for volume in volumes:
278+
assert isinstance(volume.configuration, KubernetesVolumeConfiguration)
278279
pvc_name = volume.volume_id
279280
assert pvc_name is not None, f"missing PVC name: {volume!r}"
280281
mount_path = volume_name_path_map.get(volume.name)
@@ -285,14 +286,15 @@ def run_job(
285286
name=volume_name,
286287
persistent_volume_claim=client.V1PersistentVolumeClaimVolumeSource(
287288
claim_name=pvc_name,
288-
read_only=False,
289289
),
290290
),
291291
)
292292
volume_mounts.append(
293293
client.V1VolumeMount(
294294
name=volume_name,
295295
mount_path=mount_path,
296+
read_only=volume.configuration.read_only,
297+
recursive_read_only="IfPossible" if volume.configuration.read_only else None,
296298
)
297299
)
298300

src/dstack/_internal/core/compatibility/volumes.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
from dstack._internal.core.models.common import IncludeExcludeDictType
2-
from dstack._internal.core.models.volumes import AnyVolumeConfiguration, VolumeSpec
2+
from dstack._internal.core.models.volumes import (
3+
AnyVolumeConfiguration,
4+
KubernetesVolumeConfiguration,
5+
VolumeSpec,
6+
)
37

48

59
def get_volume_spec_excludes(volume_spec: VolumeSpec) -> IncludeExcludeDictType:
@@ -29,9 +33,7 @@ def _get_volume_configuration_excludes(
2933
) -> IncludeExcludeDictType:
3034
configuration_excludes: IncludeExcludeDictType = {}
3135

32-
# Add excludes like this:
33-
#
34-
# if configuration.tags is None:
35-
# configuration_excludes["tags"] = True
36+
if isinstance(configuration, KubernetesVolumeConfiguration) and not configuration.read_only:
37+
configuration_excludes["read_only"] = True
3638

3739
return configuration_excludes

src/dstack/_internal/core/models/volumes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ class KubernetesVolumeConfiguration(BaseVolumeConfiguration):
165165
list[str],
166166
Field(description="A list of accepted access modes. Ignored if `claim_name` is set"),
167167
] = ["ReadWriteOnce"]
168+
read_only: Annotated[
169+
bool, Field(description="If `true`, enforces the volume to be mounted as read-only")
170+
] = False
168171

169172
@property
170173
def external_volume_id(self) -> Optional[str]:

0 commit comments

Comments
 (0)