diff --git a/docs/additional-configuration.adoc b/docs/additional-configuration.adoc index 8418fd4cb..b0c994193 100644 --- a/docs/additional-configuration.adoc +++ b/docs/additional-configuration.adoc @@ -141,6 +141,7 @@ By default, resources will be mounted based on the resource name: Mounting resources can be additionally configured via **annotations**: * `controller.devfile.io/mount-path`: configure where the resource should be mounted +* `controller.devfile.io/mount-sub-path`: for persistent volume claims only, configure a subpath within the PVC to mount instead of its root * `controller.devfile.io/mount-access-mode`: for secrets and configmaps only, configure file permissions on files mounted from this configmap/secret. Permissions can be specified in decimal (e.g. `"511"`) or octal notation by prefixing with a "0" (e.g. `"0777"`) * `controller.devfile.io/mount-as`: for secrets and configmaps only, configure how the resource should be mounted to the workspace + diff --git a/pkg/constants/metadata.go b/pkg/constants/metadata.go index b9664e3e1..1ee607be7 100644 --- a/pkg/constants/metadata.go +++ b/pkg/constants/metadata.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2025 Red Hat, Inc. +// Copyright (c) 2019-2026 Red Hat, Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -121,6 +121,12 @@ const ( // read-write. Automounted configmaps and secrets are always mounted read-only and this annotation is ignored. DevWorkspaceMountReadyOnlyAnnotation = "controller.devfile.io/read-only" + // DevWorkspaceMountSubPathAnnotation is an annotation to configure a subPath for a mounted PVC volume. + // When set on a PVC with the automount label, the volume mount will use the specified subPath, + // allowing a subdirectory within the PVC to be mounted instead of the root. + // This annotation is only used for PersistentVolumeClaims. + DevWorkspaceMountSubPathAnnotation = "controller.devfile.io/mount-sub-path" + // DevWorkspaceRestrictedAccessAnnotation marks the intention that devworkspace access is restricted to only the creator; setting this // annotation will cause devworkspace start to fail if webhooks are disabled. // Operator also propagates it to the devworkspace-related objects to perform authorization. diff --git a/pkg/provision/automount/common_test.go b/pkg/provision/automount/common_test.go index 060c3ac79..df046c9be 100644 --- a/pkg/provision/automount/common_test.go +++ b/pkg/provision/automount/common_test.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2025 Red Hat, Inc. +// Copyright (c) 2019-2026 Red Hat, Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -51,10 +51,11 @@ const ( type testCase struct { Name string `json:"name"` Input struct { - // Secrets and Configmaps are necessary for deserialization from a testcase - Secrets []corev1.Secret `json:"secrets"` - ConfigMaps []corev1.ConfigMap `json:"configmaps"` - // allObjects contains all Secrets and Configmaps defined above, for convenience + // Secrets, Configmaps, and PVCs are necessary for deserialization from a testcase + Secrets []corev1.Secret `json:"secrets"` + ConfigMaps []corev1.ConfigMap `json:"configmaps"` + PVCs []corev1.PersistentVolumeClaim `json:"pvcs"` + // allObjects contains all Secrets, Configmaps, and PVCs defined above, for convenience allObjects []client.Object } `json:"input"` Output struct { @@ -382,6 +383,9 @@ func loadTestCaseOrPanic(t *testing.T, testPath string) testCase { for idx := range test.Input.Secrets { test.Input.allObjects = append(test.Input.allObjects, &test.Input.Secrets[idx]) } + for idx := range test.Input.PVCs { + test.Input.allObjects = append(test.Input.allObjects, &test.Input.PVCs[idx]) + } // Overwrite namespace for convenience for _, obj := range test.Input.allObjects { diff --git a/pkg/provision/automount/pvcs.go b/pkg/provision/automount/pvcs.go index 653038b3d..83dbc82b0 100644 --- a/pkg/provision/automount/pvcs.go +++ b/pkg/provision/automount/pvcs.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2025 Red Hat, Inc. +// Copyright (c) 2019-2026 Red Hat, Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -41,6 +41,7 @@ func getAutoMountPVCs(namespace string, api sync.ClusterAPI) (*Resources, error) var volumeMounts []corev1.VolumeMount for _, pvc := range pvcs.Items { mountPath := pvc.Annotations[constants.DevWorkspaceMountPathAnnotation] + subPath := pvc.Annotations[constants.DevWorkspaceMountSubPathAnnotation] if mountPath == "" { mountPath = path.Join("/tmp/", pvc.Name) } @@ -62,6 +63,7 @@ func getAutoMountPVCs(namespace string, api sync.ClusterAPI) (*Resources, error) volumeMounts = append(volumeMounts, corev1.VolumeMount{ Name: common.AutoMountPVCVolumeName(pvc.Name), MountPath: mountPath, + SubPath: subPath, }) } return &Resources{ diff --git a/pkg/provision/automount/testdata/testProvisionsPVCWithSubPath.yaml b/pkg/provision/automount/testdata/testProvisionsPVCWithSubPath.yaml new file mode 100644 index 000000000..63164684d --- /dev/null +++ b/pkg/provision/automount/testdata/testProvisionsPVCWithSubPath.yaml @@ -0,0 +1,30 @@ +name: Provisions automount PVC with subPath + +input: + pvcs: + - + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: test-pvc + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-path: /data + controller.devfile.io/mount-sub-path: my/subdirectory + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + +output: + volumes: + - name: test-pvc + persistentVolumeClaim: + claimName: test-pvc + volumeMounts: + - name: test-pvc + mountPath: /data + subPath: my/subdirectory diff --git a/pkg/provision/automount/testdata/testProvisionsPVCWithoutSubPath.yaml b/pkg/provision/automount/testdata/testProvisionsPVCWithoutSubPath.yaml new file mode 100644 index 000000000..5e1307ab1 --- /dev/null +++ b/pkg/provision/automount/testdata/testProvisionsPVCWithoutSubPath.yaml @@ -0,0 +1,28 @@ +name: Provisions automount PVC without subPath + +input: + pvcs: + - + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: test-pvc + labels: + controller.devfile.io/mount-to-devworkspace: "true" + annotations: + controller.devfile.io/mount-path: /data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + +output: + volumes: + - name: test-pvc + persistentVolumeClaim: + claimName: test-pvc + volumeMounts: + - name: test-pvc + mountPath: /data