-
Notifications
You must be signed in to change notification settings - Fork 28.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPARK-49598][K8S] Support user-defined labels for OnDemand PVCs #48079
Changes from 8 commits
945f9af
12c1f30
98e12ae
3a4b210
cf7ccfe
3c974f5
04070c1
7edb8ff
d017e41
740b47e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,13 +45,21 @@ object KubernetesVolumeUtils { | |
val pathKey = s"$volumeType.$volumeName.$KUBERNETES_VOLUMES_MOUNT_PATH_KEY" | ||
val readOnlyKey = s"$volumeType.$volumeName.$KUBERNETES_VOLUMES_MOUNT_READONLY_KEY" | ||
val subPathKey = s"$volumeType.$volumeName.$KUBERNETES_VOLUMES_MOUNT_SUBPATH_KEY" | ||
val labelsKey = s"$volumeType.$volumeName.label." | ||
|
||
val volumeSpecificLabelsMap = properties | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
.filter(_._1.startsWith(labelsKey)) | ||
.map { | ||
case (k, v) => k.replaceAll(labelsKey, "") -> v | ||
} | ||
|
||
KubernetesVolumeSpec( | ||
volumeName = volumeName, | ||
mountPath = properties(pathKey), | ||
mountSubPath = properties.getOrElse(subPathKey, ""), | ||
mountReadOnly = properties.get(readOnlyKey).exists(_.toBoolean), | ||
volumeConf = parseVolumeSpecificConf(properties, volumeType, volumeName)) | ||
volumeConf = parseVolumeSpecificConf(properties, | ||
volumeType, volumeName, Option(volumeSpecificLabelsMap))) | ||
}.toSeq | ||
} | ||
|
||
|
@@ -74,7 +82,8 @@ object KubernetesVolumeUtils { | |
private def parseVolumeSpecificConf( | ||
options: Map[String, String], | ||
volumeType: String, | ||
volumeName: String): KubernetesVolumeSpecificConf = { | ||
volumeName: String, | ||
labels: Option[Map[String, String]]): KubernetesVolumeSpecificConf = { | ||
volumeType match { | ||
case KUBERNETES_VOLUMES_HOSTPATH_TYPE => | ||
val pathKey = s"$volumeType.$volumeName.$KUBERNETES_VOLUMES_OPTIONS_PATH_KEY" | ||
|
@@ -91,7 +100,8 @@ object KubernetesVolumeUtils { | |
KubernetesPVCVolumeConf( | ||
options(claimNameKey), | ||
options.get(storageClassKey), | ||
options.get(sizeLimitKey)) | ||
options.get(sizeLimitKey), | ||
labels) | ||
|
||
case KUBERNETES_VOLUMES_EMPTYDIR_TYPE => | ||
val mediumKey = s"$volumeType.$volumeName.$KUBERNETES_VOLUMES_OPTIONS_MEDIUM_KEY" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,12 +117,17 @@ object KubernetesTestConf { | |
(KUBERNETES_VOLUMES_HOSTPATH_TYPE, | ||
Map(KUBERNETES_VOLUMES_OPTIONS_PATH_KEY -> path)) | ||
|
||
case KubernetesPVCVolumeConf(claimName, storageClass, sizeLimit) => | ||
case KubernetesPVCVolumeConf(claimName, storageClass, sizeLimit, labels) => | ||
val sconf = storageClass | ||
.map { s => (KUBERNETES_VOLUMES_OPTIONS_CLAIM_STORAGE_CLASS_KEY, s) }.toMap | ||
val lconf = sizeLimit.map { l => (KUBERNETES_VOLUMES_OPTIONS_SIZE_LIMIT_KEY, l) }.toMap | ||
val llabels = labels match { | ||
case Some(value) => value.map { case(k, v) => s"label.$k" -> v } | ||
case None => Map() | ||
} | ||
(KUBERNETES_VOLUMES_PVC_TYPE, | ||
Map(KUBERNETES_VOLUMES_OPTIONS_CLAIM_NAME_KEY -> claimName) ++ sconf ++ lconf) | ||
Map(KUBERNETES_VOLUMES_OPTIONS_CLAIM_NAME_KEY -> claimName) ++ | ||
sconf ++ lconf ++ llabels) | ||
|
||
case KubernetesEmptyDirVolumeConf(medium, sizeLimit) => | ||
val mconf = medium.map { m => (KUBERNETES_VOLUMES_OPTIONS_MEDIUM_KEY, m) }.toMap | ||
|
@@ -142,6 +147,7 @@ object KubernetesTestConf { | |
} | ||
conf.set(key(vtype, spec.volumeName, KUBERNETES_VOLUMES_MOUNT_READONLY_KEY), | ||
spec.mountReadOnly.toString) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this. |
||
configs.foreach { case (k, v) => | ||
conf.set(key(vtype, spec.volumeName, k), v) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -120,7 +120,7 @@ class MountVolumesFeatureStepSuite extends SparkFunSuite { | |
"/tmp", | ||
"", | ||
true, | ||
KubernetesPVCVolumeConf("OnDemand") | ||
KubernetesPVCVolumeConf(MountVolumesFeatureStep.PVC_ON_DEMAND) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't touch irrelevant line. |
||
) | ||
val kubernetesConf = KubernetesTestConf.createDriverConf(volumes = Seq(volumeConf)) | ||
val step = new MountVolumesFeatureStep(kubernetesConf) | ||
|
@@ -131,6 +131,79 @@ class MountVolumesFeatureStepSuite extends SparkFunSuite { | |
assert(pvcClaim.getClaimName.endsWith("-driver-pvc-0")) | ||
} | ||
|
||
test("SPARK-49598: Create and mounts persistentVolumeClaims in driver with labels") { | ||
val volumeConf = KubernetesVolumeSpec( | ||
"testVolume", | ||
"/tmp", | ||
"", | ||
true, | ||
KubernetesPVCVolumeConf(claimName = MountVolumesFeatureStep.PVC_ON_DEMAND, | ||
storageClass = Some("gp"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit. Let's use the latest storage class; "gp" -> "gp3" |
||
size = Some("1Mi"), | ||
labels = Some(Map("foo" -> "bar", "env" -> "test"))) | ||
) | ||
|
||
val kubernetesConf = KubernetesTestConf.createDriverConf(volumes = Seq(volumeConf)) | ||
val step = new MountVolumesFeatureStep(kubernetesConf) | ||
val configuredPod = step.configurePod(SparkPod.initialPod()) | ||
assert(configuredPod.pod.getSpec.getVolumes.size() === 1) | ||
val pvcClaim = configuredPod.pod.getSpec.getVolumes.get(0).getPersistentVolumeClaim | ||
assert(pvcClaim.getClaimName.endsWith("-driver-pvc-0")) | ||
} | ||
|
||
test("SPARK-49598: Create and mounts persistentVolumeClaims in executors with labels") { | ||
val volumeConf = KubernetesVolumeSpec( | ||
"testVolume", | ||
"/tmp", | ||
"", | ||
true, | ||
KubernetesPVCVolumeConf(claimName = MountVolumesFeatureStep.PVC_ON_DEMAND, | ||
storageClass = Some("gp"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
size = Some("1Mi"), | ||
labels = Some(Map("foo1" -> "bar1", "env" -> "exec-test"))) | ||
) | ||
|
||
val executorConf = KubernetesTestConf.createExecutorConf(volumes = Seq(volumeConf)) | ||
val executorStep = new MountVolumesFeatureStep(executorConf) | ||
val executorPod = executorStep.configurePod(SparkPod.initialPod()) | ||
|
||
assert(executorPod.pod.getSpec.getVolumes.size() === 1) | ||
val executorPVC = executorPod.pod.getSpec.getVolumes.get(0).getPersistentVolumeClaim | ||
assert(executorPVC.getClaimName.endsWith("-exec-1-pvc-0")) | ||
} | ||
|
||
test("SPARK-49598: Mount multiple volumes to executor with labels") { | ||
val pvcVolumeConf1 = KubernetesVolumeSpec( | ||
"checkpointVolume1", | ||
"/checkpoints1", | ||
"", | ||
true, | ||
KubernetesPVCVolumeConf(claimName = "pvcClaim1", | ||
storageClass = Some("gp"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto. |
||
size = Some("1Mi"), | ||
labels = Some(Map("foo1" -> "bar1", "env1" -> "exec-test-1"))) | ||
) | ||
|
||
val pvcVolumeConf2 = KubernetesVolumeSpec( | ||
"checkpointVolume2", | ||
"/checkpoints2", | ||
"", | ||
true, | ||
KubernetesPVCVolumeConf(claimName = "pvcClaim2", | ||
storageClass = Some("gp"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto. |
||
size = Some("1Mi"), | ||
labels = Some(Map("foo2" -> "bar2", "env2" -> "exec-test-2"))) | ||
) | ||
|
||
val kubernetesConf = KubernetesTestConf.createExecutorConf( | ||
volumes = Seq(pvcVolumeConf1, pvcVolumeConf2)) | ||
val step = new MountVolumesFeatureStep(kubernetesConf) | ||
val configuredPod = step.configurePod(SparkPod.initialPod()) | ||
|
||
assert(configuredPod.pod.getSpec.getVolumes.size() === 2) | ||
assert(configuredPod.container.getVolumeMounts.size() === 2) | ||
} | ||
|
||
test("Create and mount persistentVolumeClaims in executors") { | ||
val volumeConf = KubernetesVolumeSpec( | ||
"testVolume", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
labelsKey
tolabelKey
to match the plural.KUBERNETES_VOLUMES_LABEL_KEY
atConfig.scala
likespark/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala
Line 773 in 98f0d9f