in core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/WhiskPodBuilder.scala [48:164]
def buildPodSpec(
name: String,
image: String,
memory: ByteSize,
environment: Map[String, String],
labels: Map[String, String],
config: KubernetesClientConfig)(implicit transid: TransactionId): (Pod, Option[PodDisruptionBudget]) = {
val envVars = environment.map {
case (key, value) => new EnvVarBuilder().withName(key).withValue(value).build()
}.toSeq ++ config.fieldRefEnvironment
.map(_.map({
case (key, value) =>
new EnvVarBuilder()
.withName(key)
.withValueFrom(new EnvVarSourceBuilder().withNewFieldRef().withFieldPath(value).endFieldRef().build())
.build()
}).toSeq)
.getOrElse(Seq.empty)
val baseBuilder = template match {
case Some(bytes) =>
new PodBuilder(loadPodSpec(bytes))
case None => new PodBuilder()
}
val pb1 = baseBuilder
.editOrNewMetadata()
.withName(name)
.addToLabels("name", name)
.addToLabels("user-action-pod", "true")
.addToLabels(labels.asJava)
.endMetadata()
val specBuilder = pb1.editOrNewSpec().withRestartPolicy("Always")
if (config.userPodNodeAffinity.enabled) {
val affinity = specBuilder
.editOrNewAffinity()
.editOrNewNodeAffinity()
.editOrNewRequiredDuringSchedulingIgnoredDuringExecution()
affinity
.addNewNodeSelectorTerm()
.addNewMatchExpression()
.withKey(config.userPodNodeAffinity.key)
.withOperator("In")
.withValues(config.userPodNodeAffinity.value)
.endMatchExpression()
.endNodeSelectorTerm()
.endRequiredDuringSchedulingIgnoredDuringExecution()
.endNodeAffinity()
.endAffinity()
}
val containerBuilder = if (specBuilder.hasMatchingContainer(actionContainerPredicate)) {
specBuilder.editMatchingContainer(actionContainerPredicate)
} else specBuilder.addNewContainer()
//if cpu scaling is enabled, calculate cpu from memory, 100m per 256Mi, min is 100m(.1cpu), max is 10000 (10cpu)
val cpu = config.cpuScaling
.map(cpuConfig => Map("cpu" -> new Quantity(calculateCpu(cpuConfig, memory) + "m")))
.getOrElse(Map.empty)
val diskLimit = config.ephemeralStorage
.map(
diskConfig =>
// Scale the ephemeral storage unless it exceeds the limit, if it exceeds the limit use the limit.
if ((diskConfig.scaleFactor > 0) && (diskConfig.scaleFactor * memory.toMB < diskConfig.limit.toMB)) {
Map("ephemeral-storage" -> new Quantity(diskConfig.scaleFactor * memory.toMB + "Mi"))
} else {
Map("ephemeral-storage" -> new Quantity(diskConfig.limit.toMB + "Mi"))
})
.getOrElse(Map.empty)
//In container its assumed that env, port, resource limits are set explicitly
//Here if any value exist in template then that would be overridden
containerBuilder
.withNewResources()
//explicitly set requests and limits to same values
.withLimits((Map("memory" -> new Quantity(memory.toMB + "Mi")) ++ cpu ++ diskLimit).asJava)
.withRequests((Map("memory" -> new Quantity(memory.toMB + "Mi")) ++ cpu ++ diskLimit).asJava)
.endResources()
.withName(actionContainerName)
.withImage(image)
.withEnv(envVars.asJava)
.addNewPort()
.withContainerPort(8080)
.withName("action")
.endPort()
//If any existing context entry is present then "update" it else add new
containerBuilder
.editOrNewSecurityContext()
.editOrNewCapabilities()
.addToDrop("NET_RAW", "NET_ADMIN")
.endCapabilities()
.endSecurityContext()
val pod = containerBuilder
.endContainer()
.endSpec()
.build()
val pdb = if (config.pdbEnabled) {
Some(
new PodDisruptionBudgetBuilder().withNewMetadata
.withName(name)
.addToLabels(labels.asJava)
.endMetadata()
.withNewSpec()
.withMinAvailable(new IntOrString(1))
.withSelector(new LabelSelectorBuilder().withMatchLabels(Map("name" -> name).asJava).build())
.and
.build)
} else {
None
}
(pod, pdb)
}