You are viewing a plain text version of this content. The canonical link for it is here.
Posted to reviews@spark.apache.org by liyinan926 <gi...@git.apache.org> on 2017/11/10 17:32:56 UTC

[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

GitHub user liyinan926 opened a pull request:

    https://github.com/apache/spark/pull/19717

    [SPARK-18278] [Submission] Spark on Kubernetes - basic submission client

    ## What changes were proposed in this pull request?
    
    This PR contains implementation of the basic submission client for the cluster mode of Spark on Kubernetes. It's step 2 from the step-wise plan documented [here](https://github.com/apache-spark-on-k8s/spark/issues/441#issuecomment-330802935).
    This addition is covered by the [SPIP](http://apache-spark-developers-list.1001551.n3.nabble.com/SPIP-Spark-on-Kubernetes-td22147.html) vote which passed on Aug 31.
    
    This PR and #19468 together form a MVP of Spark on Kubernetes that allows users to run Spark applications that use resources locally within the driver and executor containers on Kubernetes 1.6 and up. Some changes on pom and build/test setup are copied over from #19468 to make this PR self contained and testable.
    
    The submission client is mainly responsible for creating the Kubernetes pod that runs the Spark driver. It follows a step-based approach to construct the driver pod, as the code under the `submit.steps` package shows. The steps are orchestrated by `DriverConfigurationStepsOrchestrator`. `Client` creates the driver pod and waits for the application to complete if it's configured to do so, which is the case by default. 
    
    This PR also contains Dockerfiles of the driver and executor images. They are included because some of the environment variables set in the code would not make sense without referring to the Dockerfiles.
    
    ## How was this patch tested?
    
    * The patch contains unit tests which are passing.
    * Manual testing: ./build/mvn -Pkubernetes clean package succeeded.
    * It is a subset of the entire changelist hosted at http://github.com/apache-spark-on-k8s/spark which is in active use in several organizations.
    * There is integration testing enabled in the fork currently hosted by PepperData which is being moved over to RiseLAB CI.
    * Detailed documentation on trying out the patch in its entirety is in: https://apache-spark-on-k8s.github.io/userdocs/running-on-kubernetes.html
    
    cc @rxin @felixcheung @mateiz (shepherd)
    k8s-big-data SIG members & contributors: @mccheah @foxish @ash211 @ssuchter @varunkatta @kimoonkim @erikerlandson @tnachen @ifilonenko


You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/apache-spark-on-k8s/spark spark-kubernetes-4

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/spark/pull/19717.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #19717
    
----
commit 37c7ad6e8a4d107b69e7ec7842ae74446de229a0
Author: Yinan Li <li...@gmail.com>
Date:   2017-11-10T00:28:10Z

    Spark on Kubernetes - basic submission client

----


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153538185
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,236 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainAppResource.isDefined,
    +      "Main app resource must be defined by --primary-java-resource.")
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource.get,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    --- End diff --
    
    I feel that the current syntax is more readable and intuitive.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84350 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84350/testReport)** for PR 19717 at commit [`9cf9f71`](https://github.com/apache/spark/commit/9cf9f71fffbdb9fc06000774d0ea5ab2d02f0391).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155849351
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    Done in https://github.com/apache/spark/pull/19717/commits/67bc847e4f71bd6974735ac43090a0df266bc5f3.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84348 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84348/testReport)** for PR 19717 at commit [`563e9f4`](https://github.com/apache/spark/commit/563e9f4afea8b8f243c783f81b0347644d9b4e38).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    All existing clusters work with client mode, so you're on your own here... maybe setting an environment variable when starting the driver and checking that it's set in your code. Or something.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155039780
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    --- End diff --
    
    What are your thoughts of breaking up the change in how I suggest in https://github.com/apache/spark/pull/19717#pullrequestreview-81034043? We can have some PRs that create unusable code but the combination of all of said PRs create the final product.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jerryshao <gi...@git.apache.org>.
Github user jerryshao commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154871648
  
    --- Diff: core/src/main/scala/org/apache/spark/util/Utils.scala ---
    @@ -2744,6 +2744,25 @@ private[spark] object Utils extends Logging {
         }
       }
     
    +  /**
    +   * Check the validity of the given Kubernetes master URL and return the resolved URL.
    +   */
    +  def checkAndGetK8sMasterUrl(rawMasterURL: String): String = {
    +    require(rawMasterURL.startsWith("k8s://"),
    +      "Kubernetes master URL must start with k8s://.")
    +    val masterWithoutK8sPrefix = rawMasterURL.substring("k8s://".length)
    +    if (masterWithoutK8sPrefix.startsWith("https://")) {
    +      masterWithoutK8sPrefix
    +    } else if (masterWithoutK8sPrefix.startsWith("http://")) {
    +      logWarning("Kubernetes master URL uses HTTP instead of HTTPS.")
    +      masterWithoutK8sPrefix
    +    } else {
    --- End diff --
    
    Should this be happened only when scheme is not existed?  From the code looks like if user misconfigured with different scheme, the code will also be executed here.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366627
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -702,6 +715,18 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (isKubernetesCluster) {
    +      childMainClass = "org.apache.spark.deploy.k8s.submit.Client"
    +      childArgs ++= Array("--primary-java-resource", args.primaryResource)
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153543569
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,162 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf.get(DRIVER_DOCKER_IMAGE)
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    +    .get(KUBERNETES_DRIVER_MEMORY_OVERHEAD)
    +    .getOrElse(math.max((MEMORY_OVERHEAD_FACTOR * driverMemoryMiB).toInt,
    +      MEMORY_OVERHEAD_MIN_MIB))
    +  private val driverContainerMemoryWithOverheadMiB = driverMemoryMiB + memoryOverheadMiB
    +
    +  override def configureDriver(
    +      driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverExtraClasspathEnv = driverExtraClasspath.map { classPath =>
    +      new EnvVarBuilder()
    +        .withName(ENV_SUBMIT_EXTRA_CLASSPATH)
    +        .withValue(classPath)
    +        .build()
    +    }
    --- End diff --
    
    No, I don't think so. @mccheah to confirm.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test FAILed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84437/
    Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155007004
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala ---
    @@ -0,0 +1,128 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.UUID
    +
    +import com.google.common.primitives.Longs
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.steps._
    +import org.apache.spark.launcher.SparkLauncher
    +import org.apache.spark.util.SystemClock
    +
    +/**
    + * Constructs the complete list of driver configuration steps to run to deploy the Spark driver.
    + */
    +private[spark] class DriverConfigurationStepsOrchestrator(
    +    namespace: String,
    +    kubernetesAppId: String,
    +    launchTime: Long,
    +    mainAppResource: Option[MainAppResource],
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) {
    +
    +  // The resource name prefix is derived from the application name, making it easy to connect the
    +  // names of the Kubernetes resources from e.g. kubectl or the Kubernetes dashboard to the
    +  // application the user submitted. However, we can't use the application name in the label, as
    +  // label values are considerably restrictive, e.g. must be no longer than 63 characters in
    +  // length. So we generate a separate identifier for the app ID itself, and bookkeeping that
    +  // requires finding "all pods for this application" should use the kubernetesAppId.
    +  private val kubernetesResourceNamePrefix = {
    +    val uuid = UUID.nameUUIDFromBytes(Longs.toByteArray(launchTime))
    +    s"$appName-$uuid".toLowerCase.replaceAll("\\.", "-")
    +  }
    +
    +  private val dockerImagePullPolicy = submissionSparkConf.get(DOCKER_IMAGE_PULL_POLICY)
    +  private val jarsDownloadPath = submissionSparkConf.get(JARS_DOWNLOAD_LOCATION)
    +  private val filesDownloadPath = submissionSparkConf.get(FILES_DOWNLOAD_LOCATION)
    +
    +  def getAllConfigurationSteps(): Seq[DriverConfigurationStep] = {
    +    val driverCustomLabels = ConfigurationUtils.parsePrefixedKeyValuePairs(
    +      submissionSparkConf,
    +      KUBERNETES_DRIVER_LABEL_PREFIX)
    +    require(!driverCustomLabels.contains(SPARK_APP_ID_LABEL), "Label with key " +
    +      s"$SPARK_APP_ID_LABEL is not allowed as it is reserved for Spark bookkeeping " +
    +      "operations.")
    +    require(!driverCustomLabels.contains(SPARK_ROLE_LABEL), "Label with key " +
    +      s"$SPARK_ROLE_LABEL is not allowed as it is reserved for Spark bookkeeping " +
    +      "operations.")
    +
    +    val allDriverLabels = driverCustomLabels ++ Map(
    +      SPARK_APP_ID_LABEL -> kubernetesAppId,
    +      SPARK_ROLE_LABEL -> SPARK_POD_DRIVER_ROLE)
    +
    +    val initialSubmissionStep = new BaseDriverConfigurationStep(
    +      kubernetesAppId,
    +      kubernetesResourceNamePrefix,
    +      allDriverLabels,
    +      dockerImagePullPolicy,
    +      appName,
    +      mainClass,
    +      appArgs,
    +      submissionSparkConf)
    +
    +    val driverAddressStep = new DriverServiceBootstrapStep(
    +      kubernetesResourceNamePrefix,
    +      allDriverLabels,
    +      submissionSparkConf,
    +      new SystemClock)
    +
    +    val kubernetesCredentialsStep = new DriverKubernetesCredentialsStep(
    +      submissionSparkConf, kubernetesResourceNamePrefix)
    +
    +    val additionalMainAppJar = if (mainAppResource.nonEmpty) {
    +       val mayBeResource = mainAppResource.get match {
    +        case JavaMainAppResource(resource) if resource != SparkLauncher.NO_RESOURCE =>
    +          Some(resource)
    +        case _ => Option.empty
    +      }
    +      mayBeResource
    +    } else {
    +      None
    +    }
    +
    +    val sparkJars = submissionSparkConf.getOption("spark.jars")
    --- End diff --
    
    In our fork, we use Kubernetes init-container col-located with the driver/executor containers for downloading remote dependencies, e.g., from http/https endpoints. The init-container will be introduced in a subsequent PR.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84542 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84542/testReport)** for PR 19717 at commit [`0e8ca01`](https://github.com/apache/spark/commit/0e8ca012dc6d5dfd5645f45b6d1bb84e1e9e72a5).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by ueshin <gi...@git.apache.org>.
Github user ueshin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153412770
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverServiceBootstrapStep.scala ---
    @@ -0,0 +1,103 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.ServiceBuilder
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Clock
    +
    +/**
    + * Allows the driver to be reachable by executor pods through a headless service. The service's
    + * ports should correspond to the ports that the executor will reach the pod at for RPC.
    + */
    +private[spark] class DriverServiceBootstrapStep(
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    submissionSparkConf: SparkConf,
    +    clock: Clock) extends DriverConfigurationStep with Logging {
    +  import DriverServiceBootstrapStep._
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    require(submissionSparkConf.getOption(DRIVER_BIND_ADDRESS_KEY).isEmpty,
    +      s"$DRIVER_BIND_ADDRESS_KEY is not supported in Kubernetes mode, as the driver's bind" +
    +      " address is managed and set to the driver pod's IP address.")
    +    require(submissionSparkConf.getOption(DRIVER_HOST_KEY).isEmpty,
    +      s"$DRIVER_HOST_KEY is not supported in Kubernetes mode, as the driver's hostname will be" +
    +      " managed via a Kubernetes service.")
    --- End diff --
    
    nit: ditto about concat strings.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154537561
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -119,5 +139,60 @@ private[spark] object Config extends Logging {
             "must be a positive integer")
           .createWithDefault(10)
     
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  private[spark] val JARS_DOWNLOAD_LOCATION =
    +    ConfigBuilder("spark.kubernetes.mountDependencies.jarsDownloadDir")
    +      .doc("Location to download jars to in the driver and executors. When using" +
    +        " spark-submit, this directory must be empty and will be mounted as an empty directory" +
    +        " volume on the driver and executor pod.")
    +      .stringConf
    +      .createWithDefault("/var/spark-data/spark-jars")
    --- End diff --
    
    This is somehow implementation details and we expect normally users wouldn't need to set or even know about it, so having a default makes sense.  


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154800789
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    +        BaseEncoding.base64().encode(Files.toByteArray(file))
    +      }
    +  }
    +
    +  private def resolveSecretLocation(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      mountedCanonicalLocation: String): Option[String] = {
    +    mountedUserSpecified.orElse(valueMountedFromSubmitter.map( _ =>
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154813008
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmitArguments.scala ---
    @@ -590,6 +600,11 @@ private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, S
             |                              the node running the Application Master via the Secure
             |                              Distributed Cache, for renewing the login tickets and the
             |                              delegation tokens periodically.
    +        |
    +        | Kubernetes only:
    +        |  --kubernetes-namespace NS   The namespace in the Kubernetes cluster within which the
    --- End diff --
    
    This was more for convenience to allow command line parameters for setting common Kubernetes options. We added these to be similar in spirit to the other cluster managers. We can revert these.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154707798
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,160 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using " +
    +        "spark-submit in cluster mode, this can also be passed to spark-submit via the " +
    +        "--kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag " +
    +        "format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses " +
    +        "this service account when requesting executor pods from the API server. If specific " +
    +        "credentials are given for the driver pod to use, the driver will favor " +
    +        "using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the " +
    +        "driver submission server. This is memory that accounts for things like VM overheads, " +
    +        "interned strings, other native overheads, etc. This tends to grow with the driver's " +
    +        "memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This " +
    +        "is memory that accounts for things like VM overheads, interned strings, other native " +
    +        "overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
    +  val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    require(rawMasterString.startsWith("k8s://"),
    --- End diff --
    
    Added a util method in `Utils` and used it in `SparkSubmit` to check the k8s master URL and set `args.master` to the resolved URL. This method is no longer needed and is removed as the check is redundant.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155042677
  
    --- Diff: core/src/main/scala/org/apache/spark/util/Utils.scala ---
    @@ -2744,6 +2744,25 @@ private[spark] object Utils extends Logging {
         }
       }
     
    +  /**
    +   * Check the validity of the given Kubernetes master URL and return the resolved URL.
    +   */
    +  def checkAndGetK8sMasterUrl(rawMasterURL: String): String = {
    +    require(rawMasterURL.startsWith("k8s://"),
    +      "Kubernetes master URL must start with k8s://.")
    +    val masterWithoutK8sPrefix = rawMasterURL.substring("k8s://".length)
    --- End diff --
    
    (see https://spark.apache.org/docs/latest/running-on-mesos.html#using-a-mesos-master-url)


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84500 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84500/testReport)** for PR 19717 at commit [`f4a57e0`](https://github.com/apache/spark/commit/f4a57e082d19639c0c6b8bef2bae369bd242baac).
     * This patch **fails Spark unit tests**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by ueshin <gi...@git.apache.org>.
Github user ueshin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153400379
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,173 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using" +
    --- End diff --
    
    Maybe we should do it in #19468 as well.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test PASSed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84656/
    Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test FAILed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84243/
    Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test PASSed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84354/
    Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84613 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84613/testReport)** for PR 19717 at commit [`83d0b9c`](https://github.com/apache/spark/commit/83d0b9cc29eec2654308344ac8b2208e5e3f08df).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154721032
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/LoggingPodStatusWatcher.scala ---
    @@ -0,0 +1,182 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.concurrent.{CountDownLatch, TimeUnit}
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerStateRunning, ContainerStateTerminated, ContainerStateWaiting, ContainerStatus, Pod, Time}
    +import io.fabric8.kubernetes.client.{KubernetesClientException, Watcher}
    +import io.fabric8.kubernetes.client.Watcher.Action
    +
    +import org.apache.spark.SparkException
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.ThreadUtils
    +
    +private[k8s] trait LoggingPodStatusWatcher extends Watcher[Pod] {
    +  def awaitCompletion(): Unit
    +}
    +
    +/**
    + * A monitor for the running Kubernetes pod of a Spark application. Status logging occurs on
    + * every state change and also at an interval for liveness.
    + *
    + * @param appId application ID.
    + * @param maybeLoggingInterval ms between each state request. If provided, must be a positive
    + *                             number.
    + */
    +private[k8s] class LoggingPodStatusWatcherImpl(
    +    appId: String,
    +    maybeLoggingInterval: Option[Long])
    +  extends LoggingPodStatusWatcher with Logging {
    +
    +  private val podCompletedFuture = new CountDownLatch(1)
    +  // start timer for periodic logging
    +  private val scheduler =
    +    ThreadUtils.newDaemonSingleThreadScheduledExecutor("logging-pod-status-watcher")
    +  private val logRunnable: Runnable = new Runnable {
    +    override def run() = logShortStatus()
    +  }
    +
    +  private var pod = Option.empty[Pod]
    +
    +  private def phase: String = pod.map(_.getStatus.getPhase).getOrElse("unknown")
    +
    +  def start(): Unit = {
    +    maybeLoggingInterval.foreach { interval =>
    +      require(interval > 0, s"Logging interval must be a positive time value, got: $interval ms.")
    +      scheduler.scheduleAtFixedRate(logRunnable, 0, interval, TimeUnit.MILLISECONDS)
    +    }
    +  }
    +
    +  override def eventReceived(action: Action, pod: Pod): Unit = {
    +    this.pod = Option(pod)
    +    action match {
    +      case Action.DELETED | Action.ERROR =>
    +        closeWatch()
    +
    +      case _ =>
    +        logLongStatus()
    +        if (hasCompleted()) {
    +          closeWatch()
    +        }
    +    }
    +  }
    +
    +  override def onClose(e: KubernetesClientException): Unit = {
    +    logDebug(s"Stopping watching application $appId with last-observed phase $phase")
    +    closeWatch()
    +  }
    +
    +  private def logShortStatus() = {
    +    logInfo(s"Application status for $appId (phase: $phase)")
    +  }
    +
    +  private def logLongStatus() = {
    +    logInfo("State changed, new state: " + pod.map(formatPodState).getOrElse("unknown"))
    +  }
    +
    +  private def hasCompleted(): Boolean = {
    +    phase == "Succeeded" || phase == "Failed"
    +  }
    +
    +  private def closeWatch(): Unit = {
    +    podCompletedFuture.countDown()
    +    scheduler.shutdown()
    +  }
    +
    +  private def formatPodState(pod: Pod): String = {
    +    // TODO include specific container state
    --- End diff --
    
    Actually it already includes the `containerStatuses`. Removed this TODO.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    will do a pass on the latest over the weekend


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154503411
  
    --- Diff: resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/submit/steps/DependencyResolutionStepSuite.scala ---
    @@ -0,0 +1,81 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, HasMetadata, PodBuilder}
    +
    +import org.apache.spark.{SparkConf, SparkFunSuite}
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +private[spark] class DependencyResolutionStepSuite extends SparkFunSuite {
    --- End diff --
    
    don't `private[spark]`
    could you double check jenkins' run


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155957034
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/KubernetesClientApplication.scala ---
    @@ -0,0 +1,240 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +      kubernetesClient
    +        .pods()
    +        .withName(resolvedDriverPod.getMetadata.getName)
    +        .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case NonFatal(e) =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    --- End diff --
    
    not sure if we have discussed this, should it delete the pod even when the exception is Fatal?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84243 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84243/testReport)** for PR 19717 at commit [`4164ba5`](https://github.com/apache/spark/commit/4164ba575e58aa4e5756d5d82d319c2ed6196d5a).
     * This patch **fails due to an unknown error code, -9**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test PASSed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84440/
    Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jerryshao <gi...@git.apache.org>.
Github user jerryshao commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153410482
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,162 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf.get(DRIVER_DOCKER_IMAGE)
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    +    .get(KUBERNETES_DRIVER_MEMORY_OVERHEAD)
    +    .getOrElse(math.max((MEMORY_OVERHEAD_FACTOR * driverMemoryMiB).toInt,
    +      MEMORY_OVERHEAD_MIN_MIB))
    +  private val driverContainerMemoryWithOverheadMiB = driverMemoryMiB + memoryOverheadMiB
    +
    +  override def configureDriver(
    +      driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverExtraClasspathEnv = driverExtraClasspath.map { classPath =>
    +      new EnvVarBuilder()
    +        .withName(ENV_SUBMIT_EXTRA_CLASSPATH)
    +        .withValue(classPath)
    +        .build()
    +    }
    --- End diff --
    
    Did you add support of this configuration "spark.driver.userClassPathFirst" and "spark.driver.userClassPathFirst"? Sorry I cannot find it.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155845314
  
    --- Diff: core/src/main/scala/org/apache/spark/SparkConf.scala ---
    @@ -668,7 +668,9 @@ private[spark] object SparkConf extends Logging {
         MAX_REMOTE_BLOCK_SIZE_FETCH_TO_MEM.key -> Seq(
           AlternateConfig("spark.reducer.maxReqSizeShuffleToMem", "2.3")),
         LISTENER_BUS_EVENT_QUEUE_CAPACITY.key -> Seq(
    -      AlternateConfig("spark.scheduler.listenerbus.eventqueue.size", "2.3"))
    +      AlternateConfig("spark.scheduler.listenerbus.eventqueue.size", "2.3")),
    +    "spark.driver.memoryOverhead" -> Seq(
    --- End diff --
    
    `DRIVER_MEMORY_OVERHEAD.key`.
    
    Don't you also need one for the executor in k8s?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test FAILed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84646/
    Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    There are two cases to consider here. 1) If the client that creates the `SparkContext` is running in a Kubernetes pod, it should work the same way as the driver pod of an application submitted through `spark-submit`. But if the client is running outside a Kubernetes cluster, whether it works or not is yet to be verified.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    @vanzin @jerryshao @felixcheung @viirya @mridulm @ueshin @jiangxb1987 all comments have been addressed/answered. PTAL. Thanks! 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366646
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala ---
    @@ -0,0 +1,84 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.steps.{BaseDriverConfigurationStep, DriverConfigurationStep, DriverKubernetesCredentialsStep, DriverServiceBootstrapStep}
    +import org.apache.spark.util.SystemClock
    +
    +/**
    + * Constructs the complete list of driver configuration steps to run to deploy the Spark driver.
    + */
    +private[spark] class DriverConfigurationStepsOrchestrator(
    +    namespace: String,
    +    kubernetesAppId: String,
    +    launchTime: Long,
    +    mainAppResource: MainAppResource,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) {
    +
    +  // The resource name prefix is derived from the application name, making it easy to connect the
    +  // names of the Kubernetes resources from e.g. kubectl or the Kubernetes dashboard to the
    +  // application the user submitted. However, we can't use the application name in the label, as
    +  // label values are considerably restrictive, e.g. must be no longer than 63 characters in
    +  // length. So we generate a separate identifier for the app ID itself, and bookkeeping that
    +  // requires finding "all pods for this application" should use the kubernetesAppId.
    +  private val kubernetesResourceNamePrefix =
    +      s"$appName-$launchTime".toLowerCase.replaceAll("\\.", "-")
    +  private val dockerImagePullPolicy = submissionSparkConf.get(DOCKER_IMAGE_PULL_POLICY)
    +
    +  def getAllConfigurationSteps(): Seq[DriverConfigurationStep] = {
    +    val driverCustomLabels = ConfigurationUtils.parsePrefixedKeyValuePairs(
    +      submissionSparkConf,
    +      KUBERNETES_DRIVER_LABEL_PREFIX)
    +    require(!driverCustomLabels.contains(SPARK_APP_ID_LABEL), "Label with key " +
    --- End diff --
    
    Yes, done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155039435
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case e: Throwable =>
    --- End diff --
    
    We don't want to be attempting to do anything on `OutOfMemmoryError` though for example. Perhaps we can register a shutdown hook that deletes the pod, and remove the shutdown hook after we successfully create everything. See `ShutdownHookManager`. But I think just catching `NonFatal` will deal with the majority use case.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155710752
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    The docker files in this PR are for references only for now, and are by no means the official docker images moving forward. We will address concerns around the base image as part of SPARK-22647.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    We do enforce that by:
    ```
    case (KUBERNETES, CLIENT) =>
            printErrorAndExit("Client mode is currently not supported for Kubernetes.")
    ``` 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154567054
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/driver/Dockerfile ---
    @@ -0,0 +1,31 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM spark-base
    +
    +# If this docker file is being used in the context of building your images from a Spark distribution, the docker build
    +# command should be invoked from the top level directory of the Spark distribution. E.g.:
    +# docker build -t spark-driver:latest -f dockerfiles/driver/Dockerfile .
    +
    +COPY examples /opt/spark/examples
    +
    +CMD SPARK_CLASSPATH="${SPARK_HOME}/jars/*" && \
    +    env | grep SPARK_JAVA_OPT_ | sed 's/[^=]*=\(.*\)/\1/g' > /tmp/java_opts.txt && \
    +    readarray -t SPARK_DRIVER_JAVA_OPTS < /tmp/java_opts.txt && \
    +    if ! [ -z ${SPARK_MOUNTED_CLASSPATH+x} ]; then SPARK_CLASSPATH="$SPARK_MOUNTED_CLASSPATH:$SPARK_CLASSPATH"; fi && \
    +    if ! [ -z ${SPARK_SUBMIT_EXTRA_CLASSPATH+x} ]; then SPARK_CLASSPATH="$SPARK_SUBMIT_EXTRA_CLASSPATH:$SPARK_CLASSPATH"; fi && \
    +    ${JAVA_HOME}/bin/java "${SPARK_DRIVER_JAVA_OPTS[@]}" -cp "$SPARK_CLASSPATH" -Xms$SPARK_DRIVER_MEMORY -Xmx$SPARK_DRIVER_MEMORY -Dspark.driver.bindAddress=$SPARK_DRIVER_BIND_ADDRESS $SPARK_DRIVER_CLASS $SPARK_DRIVER_ARGS
    --- End diff --
    
    I think this is debatable. They have some env variables with similar purposes, but they also have role-specific arguments/properties that probably make sense to warrant separate images.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by ueshin <gi...@git.apache.org>.
Github user ueshin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153403697
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,236 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    --- End diff --
    
    nit: can we use `ArrayBuffer` explicitly?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366751
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,219 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainAppResource.isDefined,
    +      "Main app resource must be defined by either --primary-py-file or --primary-java-resource.")
    --- End diff --
    
    No, not yet. Removed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154566070
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/executor/Dockerfile ---
    @@ -0,0 +1,31 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM spark-base
    +
    +# If this docker file is being used in the context of building your images from a Spark distribution, the docker build
    +# command should be invoked from the top level directory of the Spark distribution. E.g.:
    +# docker build -t spark-executor:latest -f dockerfiles/executor/Dockerfile .
    +
    +COPY examples /opt/spark/examples
    +
    +CMD SPARK_CLASSPATH="${SPARK_HOME}/jars/*" && \
    +    env | grep SPARK_JAVA_OPT_ | sed 's/[^=]*=\(.*\)/\1/g' > /tmp/java_opts.txt && \
    +    readarray -t SPARK_EXECUTOR_JAVA_OPTS < /tmp/java_opts.txt && \
    +    if ! [ -z ${SPARK_MOUNTED_CLASSPATH}+x} ]; then SPARK_CLASSPATH="$SPARK_MOUNTED_CLASSPATH:$SPARK_CLASSPATH"; fi && \
    +    if ! [ -z ${SPARK_EXECUTOR_EXTRA_CLASSPATH+x} ]; then SPARK_CLASSPATH="$SPARK_EXECUTOR_EXTRA_CLASSPATH:$SPARK_CLASSPATH"; fi && \
    +    ${JAVA_HOME}/bin/java "${SPARK_EXECUTOR_JAVA_OPTS[@]}" -Dspark.executor.port=$SPARK_EXECUTOR_PORT -Xms$SPARK_EXECUTOR_MEMORY -Xmx$SPARK_EXECUTOR_MEMORY -cp "$SPARK_CLASSPATH" org.apache.spark.executor.CoarseGrainedExecutorBackend --driver-url $SPARK_DRIVER_URL --executor-id $SPARK_EXECUTOR_ID --cores $SPARK_EXECUTOR_CORES --app-id $SPARK_APPLICATION_ID --hostname $SPARK_EXECUTOR_POD_IP
    --- End diff --
    
    `SPARK_EXECUTOR_PORT` is no longer set as `spark.executor.port` is no longer used by Spark. Removed `SPARK_EXECUTOR_PORT`. `SPARK_MOUNTED_CLASSPATH` is set in `DependencyResolutionStep`.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by yangw1234 <gi...@git.apache.org>.
Github user yangw1234 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r157666054
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    @foxish The libc6-compat did not fix our issue because libiomp5.so can not be loaded.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by foxish <gi...@git.apache.org>.
Github user foxish commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    @liyinan926, please change PR title to match sub-task: https://issues.apache.org/jira/browse/SPARK-22646


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154503161
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case NonFatal(e) =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    +          throw e
    --- End diff --
    
    just want to check: NonFatal but delete & rethrow?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test PASSed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84658/
    Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154168292
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,161 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf.get(DRIVER_DOCKER_IMAGE)
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    +    .get(KUBERNETES_DRIVER_MEMORY_OVERHEAD)
    +    .getOrElse(math.max((MEMORY_OVERHEAD_FACTOR * driverMemoryMiB).toInt,
    +      MEMORY_OVERHEAD_MIN_MIB))
    +  private val driverContainerMemoryWithOverheadMiB = driverMemoryMiB + memoryOverheadMiB
    +
    +  override def configureDriver(
    +      driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverExtraClasspathEnv = driverExtraClasspath.map { classPath =>
    +      new EnvVarBuilder()
    +        .withName(ENV_SUBMIT_EXTRA_CLASSPATH)
    +        .withValue(classPath)
    +        .build()
    +    }
    +
    +    val driverCustomAnnotations = ConfigurationUtils
    +      .parsePrefixedKeyValuePairs(
    +        submissionSparkConf,
    +        KUBERNETES_DRIVER_ANNOTATION_PREFIX)
    +    require(!driverCustomAnnotations.contains(SPARK_APP_NAME_ANNOTATION),
    +      s"Annotation with key $SPARK_APP_NAME_ANNOTATION is not allowed as it is reserved for" +
    +        " Spark bookkeeping operations.")
    +
    +    val driverCustomEnvs = submissionSparkConf.getAllWithPrefix(KUBERNETES_DRIVER_ENV_KEY).toSeq
    +      .map(env => new EnvVarBuilder()
    +        .withName(env._1)
    +        .withValue(env._2)
    +        .build())
    +
    +    val allDriverAnnotations = driverCustomAnnotations ++ Map(SPARK_APP_NAME_ANNOTATION -> appName)
    +
    +    val nodeSelector = ConfigurationUtils.parsePrefixedKeyValuePairs(
    +      submissionSparkConf, KUBERNETES_NODE_SELECTOR_PREFIX)
    +
    +    val driverCpuQuantity = new QuantityBuilder(false)
    +      .withAmount(driverCpuCores)
    +      .build()
    +    val driverMemoryQuantity = new QuantityBuilder(false)
    +      .withAmount(s"${driverMemoryMiB}Mi")
    +      .build()
    +    val driverMemoryLimitQuantity = new QuantityBuilder(false)
    +      .withAmount(s"${driverContainerMemoryWithOverheadMiB}Mi")
    +      .build()
    +    val maybeCpuLimitQuantity = driverLimitCores.map { limitCores =>
    +      ("cpu", new QuantityBuilder(false).withAmount(limitCores).build())
    +    }
    +
    +    val driverContainer = new ContainerBuilder(driverSpec.driverContainer)
    +      .withName(DRIVER_CONTAINER_NAME)
    +      .withImage(driverDockerImage)
    +      .withImagePullPolicy(dockerImagePullPolicy)
    +      .addAllToEnv(driverCustomEnvs.asJava)
    +      .addToEnv(driverExtraClasspathEnv.toSeq: _*)
    +      .addNewEnv()
    +        .withName(ENV_DRIVER_MEMORY)
    +        .withValue(driverMemoryString)
    +        .endEnv()
    +      .addNewEnv()
    +        .withName(ENV_DRIVER_MAIN_CLASS)
    +        .withValue(mainClass)
    +        .endEnv()
    +      .addNewEnv()
    +        .withName(ENV_DRIVER_ARGS)
    +        .withValue(appArgs.mkString(" "))
    --- End diff --
    
    Good catch. Added `.map(arg => s"\\\"$arg\\\"")` before `mkString`.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155311310
  
    --- Diff: core/src/test/scala/org/apache/spark/deploy/SparkSubmitSuite.scala ---
    @@ -388,6 +388,33 @@ class SparkSubmitSuite
         conf.get("spark.ui.enabled") should be ("false")
       }
     
    +  test("handles k8s cluster mode") {
    +    val clArgs = Seq(
    +      "--deploy-mode", "cluster",
    +      "--master", "k8s://host:port",
    +      "--executor-memory", "5g",
    +      "--class", "org.SomeClass",
    +      "--driver-memory", "4g",
    +      "--conf", "spark.kubernetes.namespace=spark",
    +      "--conf", "spark.kubernetes.driver.docker.image=bar",
    --- End diff --
    
    `--kubernetes-namespace` has been removed in this PR.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84646 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84646/testReport)** for PR 19717 at commit [`44c40b1`](https://github.com/apache/spark/commit/44c40b1b7d546e4d214aff78c282c768aa0e3d42).
     * This patch **fails due to an unknown error code, -9**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    @vanzin @mridulm @jerryshao would love your comment.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test FAILed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84235/
    Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153281970
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,219 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainAppResource.isDefined,
    +      "Main app resource must be defined by either --primary-py-file or --primary-java-resource.")
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource.get,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) => new EnvVarBuilder()
    +        .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +        .withValue(option)
    +        .build()
    +    }
    --- End diff --
    
    Thanks ! Not sure how I missed this one when I searched ... something messed up in Intellij


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84658 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84658/testReport)** for PR 19717 at commit [`7d2b303`](https://github.com/apache/spark/commit/7d2b30373b2e4d8d5311e10c3f9a62a2d900d568).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366798
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverServiceBootstrapStep.scala ---
    @@ -0,0 +1,103 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.ServiceBuilder
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Clock
    +
    +/**
    + * Allows the driver to be reachable by executor pods through a headless service. The service's
    + * ports should correspond to the ports that the executor will reach the pod at for RPC.
    + */
    +private[spark] class DriverServiceBootstrapStep(
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    submissionSparkConf: SparkConf,
    +    clock: Clock) extends DriverConfigurationStep with Logging {
    +  import DriverServiceBootstrapStep._
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    require(submissionSparkConf.getOption(DRIVER_BIND_ADDRESS_KEY).isEmpty,
    +      s"$DRIVER_BIND_ADDRESS_KEY is not supported in Kubernetes mode, as the driver's bind" +
    +      " address is managed and set to the driver pod's IP address.")
    +    require(submissionSparkConf.getOption(DRIVER_HOST_KEY).isEmpty,
    +      s"$DRIVER_HOST_KEY is not supported in Kubernetes mode, as the driver's hostname will be" +
    +      " managed via a Kubernetes service.")
    +
    +    val preferredServiceName = s"$kubernetesResourceNamePrefix$DRIVER_SVC_POSTFIX"
    +    val resolvedServiceName = if (preferredServiceName.length <= MAX_SERVICE_NAME_LENGTH) {
    +      preferredServiceName
    +    } else {
    +      val randomServiceId = clock.getTimeMillis()
    +      val shorterServiceName = s"spark-$randomServiceId$DRIVER_SVC_POSTFIX"
    +      logWarning(s"Driver's hostname would preferably be $preferredServiceName, but this is" +
    +        s" too long (must be <= 63 characters). Falling back to use $shorterServiceName" +
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154502636
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmitArguments.scala ---
    @@ -304,7 +313,9 @@ private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, S
       }
     
       private def validateStatusRequestArguments(): Unit = {
    -    if (!master.startsWith("spark://") && !master.startsWith("mesos://")) {
    +    if (!master.startsWith("spark://")
    +      && !master.startsWith("mesos://")
    +      && !master.startsWith("k8s://")) {
           SparkSubmit.printErrorAndExit(
             "Requesting submission statuses is only supported in standalone or Mesos mode!")
    --- End diff --
    
    add `Kubernetes` 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155054674
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case e: Throwable =>
    --- End diff --
    
    Makes sense to me. @felixcheung WDYT about catching only `NonFatal`?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154833630
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala ---
    @@ -0,0 +1,121 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.steps._
    +import org.apache.spark.launcher.SparkLauncher
    +import org.apache.spark.util.SystemClock
    +
    +/**
    + * Constructs the complete list of driver configuration steps to run to deploy the Spark driver.
    + */
    +private[spark] class DriverConfigurationStepsOrchestrator(
    +    namespace: String,
    +    kubernetesAppId: String,
    +    launchTime: Long,
    +    mainAppResource: Option[MainAppResource],
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) {
    +
    +  // The resource name prefix is derived from the application name, making it easy to connect the
    +  // names of the Kubernetes resources from e.g. kubectl or the Kubernetes dashboard to the
    +  // application the user submitted. However, we can't use the application name in the label, as
    +  // label values are considerably restrictive, e.g. must be no longer than 63 characters in
    +  // length. So we generate a separate identifier for the app ID itself, and bookkeeping that
    +  // requires finding "all pods for this application" should use the kubernetesAppId.
    +  private val kubernetesResourceNamePrefix =
    --- End diff --
    
    I cant see rest of the context of this conversation - but UUID will be 36 odd bytes, well within the 63 char limit ?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84568 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84568/testReport)** for PR 19717 at commit [`3a0b8e3`](https://github.com/apache/spark/commit/3a0b8e301e0c47d333a65267ab9e2dbb1a06968f).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155313063
  
    --- Diff: core/src/test/scala/org/apache/spark/deploy/SparkSubmitSuite.scala ---
    @@ -388,6 +388,33 @@ class SparkSubmitSuite
         conf.get("spark.ui.enabled") should be ("false")
       }
     
    +  test("handles k8s cluster mode") {
    +    val clArgs = Seq(
    +      "--deploy-mode", "cluster",
    +      "--master", "k8s://host:port",
    +      "--executor-memory", "5g",
    +      "--class", "org.SomeClass",
    +      "--driver-memory", "4g",
    +      "--conf", "spark.kubernetes.namespace=spark",
    +      "--conf", "spark.kubernetes.driver.docker.image=bar",
    --- End diff --
    
    Should also remove https://github.com/apache/spark/pull/19717/files#diff-63a5d817d2d45ae24de577f6a1bd80f9R595?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test FAILed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84346/
    Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84701 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84701/testReport)** for PR 19717 at commit [`2e7810b`](https://github.com/apache/spark/commit/2e7810b49f00bbdb0cd5f4ac5cf99a74c748cf5a).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153548666
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,236 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155041880
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    --- End diff --
    
    Given the current state of this PR, I believe it's not far from being OK to merge. Breaking this up into multiple ones likely will not make the time to merge shorter, and possibly even make them harder to review because of lack of contexts in a single PR. IMO, I think we should keep this PR in its current form and dedicate the next one to integration tests. @felixcheung @foxish WDYT? 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84354 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84354/testReport)** for PR 19717 at commit [`0f48f03`](https://github.com/apache/spark/commit/0f48f036d6d68fb06d24298a11cc2c139d00735e).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by viirya <gi...@git.apache.org>.
Github user viirya commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154554648
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverServiceBootstrapStep.scala ---
    @@ -0,0 +1,103 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.ServiceBuilder
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Clock
    +
    +/**
    + * Allows the driver to be reachable by executor pods through a headless service. The service's
    + * ports should correspond to the ports that the executor will reach the pod at for RPC.
    + */
    +private[spark] class DriverServiceBootstrapStep(
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    submissionSparkConf: SparkConf,
    +    clock: Clock) extends DriverConfigurationStep with Logging {
    +  import DriverServiceBootstrapStep._
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    require(submissionSparkConf.getOption(DRIVER_BIND_ADDRESS_KEY).isEmpty,
    +      s"$DRIVER_BIND_ADDRESS_KEY is not supported in Kubernetes mode, as the driver's bind " +
    +      "address is managed and set to the driver pod's IP address.")
    +    require(submissionSparkConf.getOption(DRIVER_HOST_KEY).isEmpty,
    +      s"$DRIVER_HOST_KEY is not supported in Kubernetes mode, as the driver's hostname will be " +
    +      "managed via a Kubernetes service.")
    +
    +    val preferredServiceName = s"$kubernetesResourceNamePrefix$DRIVER_SVC_POSTFIX"
    +    val resolvedServiceName = if (preferredServiceName.length <= MAX_SERVICE_NAME_LENGTH) {
    +      preferredServiceName
    +    } else {
    +      val randomServiceId = clock.getTimeMillis()
    +      val shorterServiceName = s"spark-$randomServiceId$DRIVER_SVC_POSTFIX"
    +      logWarning(s"Driver's hostname would preferably be $preferredServiceName, but this is " +
    +        s"too long (must be <= $MAX_SERVICE_NAME_LENGTH characters). Falling back to use " +
    +        s"$shorterServiceName as the driver service's name.")
    +      shorterServiceName
    +    }
    +
    +    val driverPort = submissionSparkConf.getInt("spark.driver.port", DEFAULT_DRIVER_PORT)
    +    val driverBlockManagerPort = submissionSparkConf.getInt(
    +        org.apache.spark.internal.config.DRIVER_BLOCK_MANAGER_PORT.key, DEFAULT_BLOCKMANAGER_PORT)
    +    val driverService = new ServiceBuilder()
    +      .withNewMetadata()
    +        .withName(resolvedServiceName)
    +        .endMetadata()
    +      .withNewSpec()
    +        .withClusterIP("None")
    +        .withSelector(driverLabels.asJava)
    +        .addNewPort()
    +          .withName(DRIVER_PORT_NAME)
    +          .withPort(driverPort)
    +          .withNewTargetPort(driverPort)
    +          .endPort()
    +        .addNewPort()
    +          .withName(BLOCK_MANAGER_PORT_NAME)
    +          .withPort(driverBlockManagerPort)
    +          .withNewTargetPort(driverBlockManagerPort)
    +          .endPort()
    +        .endSpec()
    +      .build()
    +
    +    val namespace = submissionSparkConf.get(KUBERNETES_NAMESPACE)
    +    val driverHostname = s"${driverService.getMetadata.getName}.$namespace.svc.cluster.local"
    +    val resolvedSparkConf = driverSpec.driverSparkConf.clone()
    +      .set(org.apache.spark.internal.config.DRIVER_HOST_ADDRESS, driverHostname)
    --- End diff --
    
    `DRIVER_HOST_KEY`?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154800774
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,164 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.{SparkConf, SparkException}
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf
    +    .get(DRIVER_DOCKER_IMAGE)
    +    .getOrElse(throw new SparkException("Must specify the driver Docker image"))
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    +    .get(KUBERNETES_DRIVER_MEMORY_OVERHEAD)
    +    .getOrElse(math.max((MEMORY_OVERHEAD_FACTOR * driverMemoryMiB).toInt,
    +      MEMORY_OVERHEAD_MIN_MIB))
    +  private val driverContainerMemoryWithOverheadMiB = driverMemoryMiB + memoryOverheadMiB
    +
    +  override def configureDriver(
    +      driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverExtraClasspathEnv = driverExtraClasspath.map { classPath =>
    +      new EnvVarBuilder()
    +        .withName(ENV_SUBMIT_EXTRA_CLASSPATH)
    +        .withValue(classPath)
    +        .build()
    +    }
    +
    +    val driverCustomAnnotations = ConfigurationUtils
    +      .parsePrefixedKeyValuePairs(
    +        submissionSparkConf,
    +        KUBERNETES_DRIVER_ANNOTATION_PREFIX)
    +    require(!driverCustomAnnotations.contains(SPARK_APP_NAME_ANNOTATION),
    +      s"Annotation with key $SPARK_APP_NAME_ANNOTATION is not allowed as it is reserved for" +
    +        " Spark bookkeeping operations.")
    +
    +    val driverCustomEnvs = submissionSparkConf.getAllWithPrefix(KUBERNETES_DRIVER_ENV_KEY).toSeq
    +      .map(env =>
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153375960
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/LoggingPodStatusWatcher.scala ---
    @@ -0,0 +1,184 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.concurrent.{CountDownLatch, TimeUnit}
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerStateRunning, ContainerStateTerminated, ContainerStateWaiting, ContainerStatus, Pod, Time}
    +import io.fabric8.kubernetes.client.{KubernetesClientException, Watcher}
    +import io.fabric8.kubernetes.client.Watcher.Action
    +
    +import org.apache.spark.SparkException
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.ThreadUtils
    +
    +private[k8s] trait LoggingPodStatusWatcher extends Watcher[Pod] {
    +  def awaitCompletion(): Unit
    +}
    +
    +/**
    + * A monitor for the running Kubernetes pod of a Spark application. Status logging occurs on
    + * every state change and also at an interval for liveness.
    + *
    + * @param appId application ID.
    + * @param maybeLoggingInterval ms between each state request. If provided, must be a positive
    + *                             number.
    + */
    +private[k8s] class LoggingPodStatusWatcherImpl(
    +      appId: String, maybeLoggingInterval: Option[Long])
    +    extends LoggingPodStatusWatcher with Logging {
    +
    +  private val podCompletedFuture = new CountDownLatch(1)
    +  // start timer for periodic logging
    +  private val scheduler =
    +    ThreadUtils.newDaemonSingleThreadScheduledExecutor("logging-pod-status-watcher")
    +  private val logRunnable: Runnable = new Runnable {
    +    override def run() = logShortStatus()
    +  }
    +
    +  private var pod = Option.empty[Pod]
    +
    +  private def phase: String = pod.map(_.getStatus.getPhase).getOrElse("unknown")
    +
    +  def start(): Unit = {
    +    maybeLoggingInterval.foreach { interval =>
    +      require(interval > 0, s"Logging interval must be a positive time value, got: $interval ms.")
    +      scheduler.scheduleAtFixedRate(logRunnable, 0, interval, TimeUnit.MILLISECONDS)
    +    }
    +  }
    +
    +  override def eventReceived(action: Action, pod: Pod): Unit = {
    +    this.pod = Option(pod)
    +    action match {
    +      case Action.DELETED =>
    +        closeWatch()
    +
    +      case Action.ERROR =>
    +        closeWatch()
    --- End diff --
    
    It is checked only for case of `action != DELETED` and `action != ERROR` right ?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155979008
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -294,6 +298,16 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (clusterManager == KUBERNETES) {
    +      args.master = Utils.checkAndGetK8sMasterUrl(args.master)
    +      // Make sure YARN is included in our build if we're trying to use it
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155908121
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -119,5 +117,46 @@ private[spark] object Config extends Logging {
             "must be a positive integer")
           .createWithDefault(10)
     
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .checkValue(interval => interval > 0, s"Logging interval must be a positive time value.")
    +      .createWithDefaultString("1s")
    +
    +  private[spark] val JARS_DOWNLOAD_LOCATION =
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153228381
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverServiceBootstrapStep.scala ---
    @@ -0,0 +1,103 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.ServiceBuilder
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Clock
    +
    +/**
    + * Allows the driver to be reachable by executor pods through a headless service. The service's
    + * ports should correspond to the ports that the executor will reach the pod at for RPC.
    + */
    +private[spark] class DriverServiceBootstrapStep(
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    submissionSparkConf: SparkConf,
    +    clock: Clock) extends DriverConfigurationStep with Logging {
    +  import DriverServiceBootstrapStep._
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    require(submissionSparkConf.getOption(DRIVER_BIND_ADDRESS_KEY).isEmpty,
    +      s"$DRIVER_BIND_ADDRESS_KEY is not supported in Kubernetes mode, as the driver's bind" +
    +      " address is managed and set to the driver pod's IP address.")
    +    require(submissionSparkConf.getOption(DRIVER_HOST_KEY).isEmpty,
    +      s"$DRIVER_HOST_KEY is not supported in Kubernetes mode, as the driver's hostname will be" +
    +      " managed via a Kubernetes service.")
    +
    +    val preferredServiceName = s"$kubernetesResourceNamePrefix$DRIVER_SVC_POSTFIX"
    +    val resolvedServiceName = if (preferredServiceName.length <= MAX_SERVICE_NAME_LENGTH) {
    +      preferredServiceName
    +    } else {
    +      val randomServiceId = clock.getTimeMillis()
    +      val shorterServiceName = s"spark-$randomServiceId$DRIVER_SVC_POSTFIX"
    +      logWarning(s"Driver's hostname would preferably be $preferredServiceName, but this is" +
    +        s" too long (must be <= 63 characters). Falling back to use $shorterServiceName" +
    --- End diff --
    
    nit: `must be <= $MAX_SERVICE_NAME_LENGTH characters`


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84235 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84235/testReport)** for PR 19717 at commit [`0f90746`](https://github.com/apache/spark/commit/0f90746447b73148544fae02b69855d367bbf0e9).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154759371
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,164 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.{SparkConf, SparkException}
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf
    +    .get(DRIVER_DOCKER_IMAGE)
    +    .getOrElse(throw new SparkException("Must specify the driver Docker image"))
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    --- End diff --
    
    You could move the `DRIVER_CORES` config from the YARN module to core.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by ueshin <gi...@git.apache.org>.
Github user ueshin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154022151
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource.get,
    --- End diff --
    
    Don't we need to check if `mainAppResource` is defined or not before here? Otherwise some exception will be thrown if it is not defined.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jerryshao <gi...@git.apache.org>.
Github user jerryshao commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154874378
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case e: Throwable =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    +          throw e
    +      }
    +
    +      if (waitForAppCompletion) {
    +        logInfo(s"Waiting for application $appName to finish...")
    +        loggingPodStatusWatcher.awaitCompletion()
    +        logInfo(s"Application $appName finished.")
    +      } else {
    +        logInfo(s"Deployed Spark application $appName into Kubernetes.")
    +      }
    +    }
    +  }
    +
    +  // Add a OwnerReference to the given resources making the driver pod an owner of them so when
    +  // the driver pod is deleted, the resources are garbage collected.
    +  private def addDriverOwnerReference(driverPod: Pod, resources: Seq[HasMetadata]): Unit = {
    +    val driverPodOwnerReference = new OwnerReferenceBuilder()
    +      .withName(driverPod.getMetadata.getName)
    +      .withApiVersion(driverPod.getApiVersion)
    +      .withUid(driverPod.getMetadata.getUid)
    +      .withKind(driverPod.getKind)
    +      .withController(true)
    +      .build()
    +    resources.foreach { resource =>
    +      val originalMetadata = resource.getMetadata
    +      originalMetadata.setOwnerReferences(Collections.singletonList(driverPodOwnerReference))
    +    }
    +  }
    +}
    +
    +/**
    + * Main class and entry point of application submission in KUBERNETES mode.
    + */
    +private[spark] object Client extends SparkApplication {
    +
    +  override def start(args: Array[String], conf: SparkConf): Unit = {
    +    val parsedArguments = ClientArguments.fromCommandLineArgs(args)
    +    run(parsedArguments, conf)
    +  }
    +
    +  private def run(clientArguments: ClientArguments, sparkConf: SparkConf): Unit = {
    +    val namespace = sparkConf.get(KUBERNETES_NAMESPACE)
    +    val kubernetesAppId = s"spark-${UUID.randomUUID().toString.replaceAll("-", "")}"
    --- End diff --
    
    Is it an application Id for Spark k8s application? Does K8s have a convention for app id? Normally like YARN, it is timestamp based, which is easy for sorting, retrieving, I'm not sure how k8s treat this?
    
    From my understanding of the code, seems like k8s doesn't have app id related concept, so it is up to Spark to define here? Is it posssible to change to timestamp based like standalone, local and yarn deploy?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84500 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84500/testReport)** for PR 19717 at commit [`f4a57e0`](https://github.com/apache/spark/commit/f4a57e082d19639c0c6b8bef2bae369bd242baac).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154746008
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -702,6 +715,19 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (isKubernetesCluster) {
    +      childMainClass = "org.apache.spark.deploy.k8s.submit.Client"
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155042592
  
    --- Diff: core/src/main/scala/org/apache/spark/util/Utils.scala ---
    @@ -2744,6 +2744,25 @@ private[spark] object Utils extends Logging {
         }
       }
     
    +  /**
    +   * Check the validity of the given Kubernetes master URL and return the resolved URL.
    +   */
    +  def checkAndGetK8sMasterUrl(rawMasterURL: String): String = {
    +    require(rawMasterURL.startsWith("k8s://"),
    +      "Kubernetes master URL must start with k8s://.")
    +    val masterWithoutK8sPrefix = rawMasterURL.substring("k8s://".length)
    --- End diff --
    
    This decision is to match how Mesos works with Zookeeper, as these Strings are valid IIRC: `mesos://zk://host1:2181,host2:2181,host3:2181/mesos`. That's just the precedent we are using, but we can use a different convention.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by viirya <gi...@git.apache.org>.
Github user viirya commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154572754
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverServiceBootstrapStep.scala ---
    @@ -0,0 +1,103 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.ServiceBuilder
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Clock
    +
    +/**
    + * Allows the driver to be reachable by executor pods through a headless service. The service's
    + * ports should correspond to the ports that the executor will reach the pod at for RPC.
    + */
    +private[spark] class DriverServiceBootstrapStep(
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    submissionSparkConf: SparkConf,
    +    clock: Clock) extends DriverConfigurationStep with Logging {
    +  import DriverServiceBootstrapStep._
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    require(submissionSparkConf.getOption(DRIVER_BIND_ADDRESS_KEY).isEmpty,
    +      s"$DRIVER_BIND_ADDRESS_KEY is not supported in Kubernetes mode, as the driver's bind " +
    +      "address is managed and set to the driver pod's IP address.")
    +    require(submissionSparkConf.getOption(DRIVER_HOST_KEY).isEmpty,
    +      s"$DRIVER_HOST_KEY is not supported in Kubernetes mode, as the driver's hostname will be " +
    +      "managed via a Kubernetes service.")
    +
    +    val preferredServiceName = s"$kubernetesResourceNamePrefix$DRIVER_SVC_POSTFIX"
    +    val resolvedServiceName = if (preferredServiceName.length <= MAX_SERVICE_NAME_LENGTH) {
    +      preferredServiceName
    +    } else {
    +      val randomServiceId = clock.getTimeMillis()
    +      val shorterServiceName = s"spark-$randomServiceId$DRIVER_SVC_POSTFIX"
    +      logWarning(s"Driver's hostname would preferably be $preferredServiceName, but this is " +
    +        s"too long (must be <= $MAX_SERVICE_NAME_LENGTH characters). Falling back to use " +
    +        s"$shorterServiceName as the driver service's name.")
    +      shorterServiceName
    +    }
    +
    +    val driverPort = submissionSparkConf.getInt("spark.driver.port", DEFAULT_DRIVER_PORT)
    +    val driverBlockManagerPort = submissionSparkConf.getInt(
    +        org.apache.spark.internal.config.DRIVER_BLOCK_MANAGER_PORT.key, DEFAULT_BLOCKMANAGER_PORT)
    +    val driverService = new ServiceBuilder()
    +      .withNewMetadata()
    +        .withName(resolvedServiceName)
    +        .endMetadata()
    +      .withNewSpec()
    +        .withClusterIP("None")
    +        .withSelector(driverLabels.asJava)
    +        .addNewPort()
    +          .withName(DRIVER_PORT_NAME)
    +          .withPort(driverPort)
    +          .withNewTargetPort(driverPort)
    +          .endPort()
    +        .addNewPort()
    +          .withName(BLOCK_MANAGER_PORT_NAME)
    +          .withPort(driverBlockManagerPort)
    +          .withNewTargetPort(driverBlockManagerPort)
    +          .endPort()
    +        .endSpec()
    +      .build()
    +
    +    val namespace = submissionSparkConf.get(KUBERNETES_NAMESPACE)
    +    val driverHostname = s"${driverService.getMetadata.getName}.$namespace.svc.cluster.local"
    +    val resolvedSparkConf = driverSpec.driverSparkConf.clone()
    +      .set(org.apache.spark.internal.config.DRIVER_HOST_ADDRESS, driverHostname)
    --- End diff --
    
    I meant isn't `org.apache.spark.internal.config.DRIVER_HOST_ADDRESS` just `DRIVER_HOST_KEY`?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154800800
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    +        BaseEncoding.base64().encode(Files.toByteArray(file))
    +      }
    +  }
    +
    +  private def resolveSecretLocation(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      mountedCanonicalLocation: String): Option[String] = {
    +    mountedUserSpecified.orElse(valueMountedFromSubmitter.map( _ =>
    +      mountedCanonicalLocation
    +    ))
    +  }
    +
    +  private def resolveSecretData(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      secretName: String): Map[String, String] = {
    +    mountedUserSpecified.map { _ => Map.empty[String, String] }
    +      .getOrElse {
    +        valueMountedFromSubmitter.map { valueBase64 =>
    +          Map(secretName -> valueBase64)
    +        }.getOrElse(Map.empty[String, String])
    +      }
    +  }
    +
    +  private implicit def augmentSparkConf(sparkConf: SparkConf): OptionSettableSparkConf = {
    +    new OptionSettableSparkConf(sparkConf)
    +  }
    +}
    +
    +private class OptionSettableSparkConf(sparkConf: SparkConf) {
    +  def setOption(configEntry: String, option: Option[String]): SparkConf = {
    +    option.map( opt =>
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154537691
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmitArguments.scala ---
    @@ -294,7 +301,9 @@ private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, S
       }
     
       private def validateKillArguments(): Unit = {
    -    if (!master.startsWith("spark://") && !master.startsWith("mesos://")) {
    +    if (!master.startsWith("spark://")
    +      && !master.startsWith("mesos://")
    +      && !master.startsWith("k8s://")) {
           SparkSubmit.printErrorAndExit(
             "Killing submissions is only supported in standalone or Mesos mode!")
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155845669
  
    --- Diff: core/src/main/scala/org/apache/spark/SparkContext.scala ---
    @@ -380,6 +380,10 @@ class SparkContext(config: SparkConf) extends Logging {
             "Deployment to YARN is not supported directly by SparkContext. Please use spark-submit.")
         }
     
    +    if (master.startsWith("k8s") && deployMode == "client") {
    --- End diff --
    
    Can you do this in `KubernetesClusterManager` instead?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154816823
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala ---
    @@ -0,0 +1,121 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.steps._
    +import org.apache.spark.launcher.SparkLauncher
    +import org.apache.spark.util.SystemClock
    +
    +/**
    + * Constructs the complete list of driver configuration steps to run to deploy the Spark driver.
    + */
    +private[spark] class DriverConfigurationStepsOrchestrator(
    +    namespace: String,
    +    kubernetesAppId: String,
    +    launchTime: Long,
    +    mainAppResource: Option[MainAppResource],
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) {
    +
    +  // The resource name prefix is derived from the application name, making it easy to connect the
    +  // names of the Kubernetes resources from e.g. kubectl or the Kubernetes dashboard to the
    +  // application the user submitted. However, we can't use the application name in the label, as
    +  // label values are considerably restrictive, e.g. must be no longer than 63 characters in
    +  // length. So we generate a separate identifier for the app ID itself, and bookkeeping that
    +  // requires finding "all pods for this application" should use the kubernetesAppId.
    +  private val kubernetesResourceNamePrefix =
    +      s"$appName-$launchTime".toLowerCase.replaceAll("\\.", "-")
    +  private val dockerImagePullPolicy = submissionSparkConf.get(DOCKER_IMAGE_PULL_POLICY)
    +  private val jarsDownloadPath = submissionSparkConf.get(JARS_DOWNLOAD_LOCATION)
    +  private val filesDownloadPath = submissionSparkConf.get(FILES_DOWNLOAD_LOCATION)
    +
    +  def getAllConfigurationSteps(): Seq[DriverConfigurationStep] = {
    +    val driverCustomLabels = ConfigurationUtils.parsePrefixedKeyValuePairs(
    +      submissionSparkConf,
    +      KUBERNETES_DRIVER_LABEL_PREFIX)
    +    require(!driverCustomLabels.contains(SPARK_APP_ID_LABEL), "Label with key " +
    +      s"$SPARK_APP_ID_LABEL is not allowed as it is reserved for Spark bookkeeping " +
    +      "operations.")
    +    require(!driverCustomLabels.contains(SPARK_ROLE_LABEL), "Label with key " +
    +      s"$SPARK_ROLE_LABEL is not allowed as it is reserved for Spark bookkeeping " +
    +      "operations.")
    +
    +    val allDriverLabels = driverCustomLabels ++ Map(
    +      SPARK_APP_ID_LABEL -> kubernetesAppId,
    +      SPARK_ROLE_LABEL -> SPARK_POD_DRIVER_ROLE)
    +
    +    val initialSubmissionStep = new BaseDriverConfigurationStep(
    +      kubernetesAppId,
    +      kubernetesResourceNamePrefix,
    +      allDriverLabels,
    +      dockerImagePullPolicy,
    +      appName,
    +      mainClass,
    +      appArgs,
    +      submissionSparkConf)
    +
    +    val driverAddressStep = new DriverServiceBootstrapStep(
    +      kubernetesResourceNamePrefix,
    +      allDriverLabels,
    +      submissionSparkConf,
    +      new SystemClock)
    +
    +    val kubernetesCredentialsStep = new DriverKubernetesCredentialsStep(
    +      submissionSparkConf, kubernetesResourceNamePrefix)
    +
    +    val additionalMainAppJar = if (mainAppResource.nonEmpty) {
    +       val mayBeResource = mainAppResource.get match {
    +        case JavaMainAppResource(resource) if resource != SparkLauncher.NO_RESOURCE =>
    +          Some(resource)
    +        case _ => Option.empty
    --- End diff --
    
    Nit: think `None` reads better.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84513 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84513/testReport)** for PR 19717 at commit [`0936fbe`](https://github.com/apache/spark/commit/0936fbe5651fdc534b5ccfe608cb4b9c41928397).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153218673
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,219 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainAppResource.isDefined,
    +      "Main app resource must be defined by either --primary-py-file or --primary-java-resource.")
    --- End diff --
    
    Have we already support `--primary-py-file` here?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366783
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,161 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf.get(DRIVER_DOCKER_IMAGE)
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    +    .get(KUBERNETES_DRIVER_MEMORY_OVERHEAD)
    +    .getOrElse(math.max((MEMORY_OVERHEAD_FACTOR * driverMemoryMiB).toInt,
    +      MEMORY_OVERHEAD_MIN_MIB))
    +  private val driverContainerMemoryWithOverheadMiB = driverMemoryMiB + memoryOverheadMiB
    +
    +  override def configureDriver(
    +      driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverExtraClasspathEnv = driverExtraClasspath.map { classPath =>
    +      new EnvVarBuilder()
    +        .withName(ENV_SUBMIT_EXTRA_CLASSPATH)
    +        .withValue(classPath)
    +        .build()
    +    }
    +
    +    val driverCustomAnnotations = ConfigurationUtils
    +      .parsePrefixedKeyValuePairs(
    +        submissionSparkConf,
    +        KUBERNETES_DRIVER_ANNOTATION_PREFIX)
    +    require(!driverCustomAnnotations.contains(SPARK_APP_NAME_ANNOTATION),
    +      s"Annotation with key $SPARK_APP_NAME_ANNOTATION is not allowed as it is reserved for" +
    +        " Spark bookkeeping operations.")
    +
    +    val driverCustomEnvs = submissionSparkConf.getAllWithPrefix(KUBERNETES_DRIVER_ENV_KEY).toSeq
    +      .map(env => new EnvVarBuilder()
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84701 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84701/testReport)** for PR 19717 at commit [`2e7810b`](https://github.com/apache/spark/commit/2e7810b49f00bbdb0cd5f4ac5cf99a74c748cf5a).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by echarles <gi...@git.apache.org>.
Github user echarles commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r159282180
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    @yangw1234 Adding `libc6-compat` on the spark-k8s base image works for me. I read and write parquet without issue. May be you had it only on the 'driver` image or made other changes that override the correct behavior (just random guess...)?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153101406
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -702,6 +715,18 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (isKubernetesCluster) {
    +      childMainClass = "org.apache.spark.deploy.k8s.submit.Client"
    +      childArgs ++= Array("--primary-java-resource", args.primaryResource)
    +      childArgs ++= Array("--main-class", args.mainClass)
    +      if (args.childArgs != null) {
    +        args.childArgs.foreach { arg =>
    +          childArgs += "--arg"
    --- End diff --
    
    Why not `childArgs += ("--arg", arg)`?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by erikerlandson <gi...@git.apache.org>.
Github user erikerlandson commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r156841175
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    I'm in favor of using centos.  The compat layer makes me a bit nervous.  It may work fine but it smells like the kind of thing that could cause inscrutable binary incompatability bugs


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by yangw1234 <gi...@git.apache.org>.
Github user yangw1234 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155707713
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    The base image **openjdk:8-alpine** uses **musl libc** instead of **glibc**, so certain library (like Intel MKL for native BLAS support in mllib) cannot run on the container.  
    There is also report on openjdk compatibility issue on alpine (https://github.com/apache-spark-on-k8s/spark/issues/326). 
    Should we change to a base image that supports glibc?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r156176319
  
    --- Diff: docs/configuration.md ---
    @@ -157,13 +157,33 @@ of the most common options to set are:
         or in your default properties file.
       </td>
     </tr>
    +<tr>
    +  <td><code>spark.driver.memoryOverhead</code></td>
    +  <td>driverMemory * 0.10, with minimum of 384 </td>
    +  <td>
    +    The amount of off-heap memory (in megabytes) to be allocated per driver in cluster mode. This is
    +    memory that accounts for things like VM overheads, interned strings, other native overheads, etc.
    +    This tends to grow with the container size (typically 6-10%). This option is supported in Yarn
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84726 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84726/testReport)** for PR 19717 at commit [`cbcd30e`](https://github.com/apache/spark/commit/cbcd30ea0a5eb75ed831f50707bc97035c0a3371).
     * This patch **fails SparkR unit tests**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155934917
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,43 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    I was referring to the docker image openjdk
     https://github.com/docker-library/openjdk/blob/b4f29ba829765552239bd18f272fcdaf09eca259/LICENSE
    
    of course, the openjdk bin/bit is not under MIT license. I'm actually not quite sure how "releasing" the docker image goes from the licensing point of view...


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84656 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84656/testReport)** for PR 19717 at commit [`67bc847`](https://github.com/apache/spark/commit/67bc847e4f71bd6974735ac43090a0df266bc5f3).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154771774
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    --- End diff --
    
    Not really familiar with how these things are used  by k8s, but don't these certs generally have passwords? I can't seem to find anything related to passwords for these things.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155979284
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/KubernetesClientApplication.scala ---
    @@ -0,0 +1,240 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +      kubernetesClient
    +        .pods()
    +        .withName(resolvedDriverPod.getMetadata.getName)
    +        .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case NonFatal(e) =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    --- End diff --
    
    @mccheah commented above that we probably don't want to take action in case of `OutOfMemoryError` for example. See https://github.com/apache/spark/pull/19717#discussion_r155039435.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154815505
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    --- End diff --
    
    These types of files are typically not password protected. Java KeyStores are. (Which reminds me that we should eventually support JKS files for these Kubernetes clients).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84530 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84530/testReport)** for PR 19717 at commit [`0e8ca01`](https://github.com/apache/spark/commit/0e8ca012dc6d5dfd5645f45b6d1bb84e1e9e72a5).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155846548
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    When we send in the integration tests they will need to specify images so we're going to have to decide sooner or later.
    
    I think using centos is fine, or fixing the base image to support glibc? Can't the Docker image be configured to use glibc despite using alpine?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by viirya <gi...@git.apache.org>.
Github user viirya commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    retest this please.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153582956
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,236 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainAppResource.isDefined,
    +      "Main app resource must be defined by --primary-java-resource.")
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource.get,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    --- End diff --
    
    Other reviews have stated that `foldLeft` is to be avoided.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Checking the presence of `kubernetes.default.svc` is not sufficient as the driver service must exist. When the submission client is not involved, the driver itself must create that service before requesting the executor pods. I think we can go with a env var flag as @vanzin suggested.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84438 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84438/testReport)** for PR 19717 at commit [`cfcf2a7`](https://github.com/apache/spark/commit/cfcf2a7622f2bd4a21ea2d5197ec652363645aca).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154750025
  
    --- Diff: core/src/test/scala/org/apache/spark/deploy/SparkSubmitSuite.scala ---
    @@ -388,6 +388,32 @@ class SparkSubmitSuite
         conf.get("spark.ui.enabled") should be ("false")
       }
     
    +  test("handles k8s cluster mode") {
    +    val clArgs = Seq(
    +      "--deploy-mode", "cluster",
    +      "--master", "k8s://host:port",
    +      "--executor-memory", "5g",
    +      "--class", "org.SomeClass",
    +      "--kubernetes-namespace", "foo",
    +      "--driver-memory", "4g",
    +      "--conf", "spark.kubernetes.driver.docker.image=bar",
    +      "/home/thejar.jar",
    +      "arg1")
    +    val appArgs = new SparkSubmitArguments(clArgs)
    +    val (childArgs, classpath, conf, mainClass) = prepareSubmitEnvironment(appArgs)
    +
    +    val childArgsMap = childArgs.grouped(2).map(a => a(0) -> a(1)).toMap
    +    childArgsMap.get("--primary-java-resource") should be (Some("file:/home/thejar.jar"))
    +    childArgsMap.get("--main-class") should be (Some("org.SomeClass"))
    +    childArgsMap.get("--arg") should be (Some("arg1"))
    +    mainClass should be ("org.apache.spark.deploy.k8s.submit.Client")
    +    classpath should have length (0)
    +    conf.get("spark.executor.memory") should be ("5g")
    --- End diff --
    
    Check `spark.master` too?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154833745
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,162 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf.get(DRIVER_DOCKER_IMAGE)
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    +    .get(KUBERNETES_DRIVER_MEMORY_OVERHEAD)
    +    .getOrElse(math.max((MEMORY_OVERHEAD_FACTOR * driverMemoryMiB).toInt,
    +      MEMORY_OVERHEAD_MIN_MIB))
    +  private val driverContainerMemoryWithOverheadMiB = driverMemoryMiB + memoryOverheadMiB
    +
    +  override def configureDriver(
    +      driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverExtraClasspathEnv = driverExtraClasspath.map { classPath =>
    +      new EnvVarBuilder()
    +        .withName(ENV_SUBMIT_EXTRA_CLASSPATH)
    +        .withValue(classPath)
    +        .build()
    +    }
    --- End diff --
    
    custom docker images ftw !


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154834535
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    --- End diff --
    
    We make more use of try than Try in spark codebase.
    An unscientific test:
    ```
    $ find . -type f -name '*.scala' | grep -v Suite | xargs grep Try | wc -l
         269
    $ find . -type f -name '*.scala' | grep -v Suite | xargs grep try | wc -l
        2506
    ```



---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by ueshin <gi...@git.apache.org>.
Github user ueshin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154029412
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    +        BaseEncoding.base64().encode(Files.toByteArray(file))
    +      }
    +  }
    +
    +  private def resolveSecretLocation(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      mountedCanonicalLocation: String): Option[String] = {
    +    mountedUserSpecified.orElse(valueMountedFromSubmitter.map( _ => {
    --- End diff --
    
    nit: we can remove `{` and corresponding `}`.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154833947
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    --- End diff --
    
    I personally don't think we can reduce much complexity as what are included in this PR and the first one form a MVP that support the most basic use cases: allowing users to submit Spark applications to a Kubernetes cluster with `local://` dependencies baked into images. Also given that this has already gone through several rounds of reviews by different reviewers, we are probably at a better position if we keep the current form. The next PR most probably will be for integration tests so we get more confident on the health of the MVP.  


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jerryshao <gi...@git.apache.org>.
Github user jerryshao commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153408574
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,160 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using " +
    +        "spark-submit in cluster mode, this can also be passed to spark-submit via the " +
    +        "--kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag " +
    +        "format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses " +
    +        "this service account when requesting executor pods from the API server. If specific " +
    +        "credentials are given for the driver pod to use, the driver will favor " +
    +        "using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the " +
    +        "driver submission server. This is memory that accounts for things like VM overheads, " +
    +        "interned strings, other native overheads, etc. This tends to grow with the driver's " +
    +        "memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This " +
    +        "is memory that accounts for things like VM overheads, interned strings, other native " +
    +        "overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
    +  val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    require(rawMasterString.startsWith("k8s://"),
    --- End diff --
    
    Shall we add some checks in `SparkSubmitArguments` for k8s master?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    @mridulm @jiangxb1987 @jerryshao @ueshin all comments so far have been addressed. The PR has also been updated to include a missing step for `local://` dependency support. PTAL. Thanks!


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155306429
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -119,5 +130,46 @@ private[spark] object Config extends Logging {
             "must be a positive integer")
           .createWithDefault(10)
     
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .checkValue(interval => interval > 0, s"Logging interval must be a positive time value.")
    +      .createWithDefaultString("1s")
    +
    +  private[spark] val JARS_DOWNLOAD_LOCATION =
    +    ConfigBuilder("spark.kubernetes.mountDependencies.jarsDownloadDir")
    +      .doc("Location to download jars to in the driver and executors. When using" +
    +        " spark-submit, this directory must be empty and will be mounted as an empty directory" +
    +        " volume on the driver and executor pod.")
    +      .stringConf
    +      .createWithDefault("/var/spark-data/spark-jars")
    +
    +  private[spark] val FILES_DOWNLOAD_LOCATION =
    +    ConfigBuilder("spark.kubernetes.mountDependencies.filesDownloadDir")
    +      .doc("Location to download files to in the driver and executors. When using" +
    +        " spark-submit, this directory must be empty and will be mounted as an empty directory" +
    +        " volume on the driver and executor pods.")
    +      .stringConf
    +      .createWithDefault("/var/spark-data/spark-files")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
       val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    --- End diff --
    
    This prefix is not used either.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153216292
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/ConfigurationUtils.scala ---
    @@ -0,0 +1,50 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package org.apache.spark.deploy.k8s
    +
    +import org.apache.spark.SparkConf
    +
    +private[spark] object ConfigurationUtils {
    +
    +  /**
    +   * Extract and parse Spark configuration properties with a given name prefix and
    +   * return the result as a Map. Keys must not have more than one value.
    +   *
    +   * @param sparkConf Spark configuration
    +   * @param prefix the given property name prefix
    +   * @return a Map storing the configuration property keys and values
    +   */
    +  def parsePrefixedKeyValuePairs(
    +      sparkConf: SparkConf,
    +      prefix: String): Map[String, String] = {
    +    sparkConf.getAllWithPrefix(prefix).toMap
    +  }
    +
    +  def requireSecondIfFirstIsDefined(
    --- End diff --
    
    This function is never used.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test PASSed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84453/
    Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155300795
  
    --- Diff: core/src/main/scala/org/apache/spark/internal/config/package.scala ---
    @@ -41,6 +41,10 @@ package object config {
         .bytesConf(ByteUnit.MiB)
         .createWithDefaultString("1g")
     
    +  private[spark] val DRIVER_MEMORY_OVERHEAD = ConfigBuilder("spark.driver.memoryOverhead")
    --- End diff --
    
    nit: should also add doc for this config here.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366657
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/LoggingPodStatusWatcher.scala ---
    @@ -0,0 +1,184 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.concurrent.{CountDownLatch, TimeUnit}
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerStateRunning, ContainerStateTerminated, ContainerStateWaiting, ContainerStatus, Pod, Time}
    +import io.fabric8.kubernetes.client.{KubernetesClientException, Watcher}
    +import io.fabric8.kubernetes.client.Watcher.Action
    +
    +import org.apache.spark.SparkException
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.ThreadUtils
    +
    +private[k8s] trait LoggingPodStatusWatcher extends Watcher[Pod] {
    +  def awaitCompletion(): Unit
    +}
    +
    +/**
    + * A monitor for the running Kubernetes pod of a Spark application. Status logging occurs on
    + * every state change and also at an interval for liveness.
    + *
    + * @param appId application ID.
    + * @param maybeLoggingInterval ms between each state request. If provided, must be a positive
    + *                             number.
    + */
    +private[k8s] class LoggingPodStatusWatcherImpl(
    +      appId: String, maybeLoggingInterval: Option[Long])
    +    extends LoggingPodStatusWatcher with Logging {
    +
    +  private val podCompletedFuture = new CountDownLatch(1)
    +  // start timer for periodic logging
    +  private val scheduler =
    +    ThreadUtils.newDaemonSingleThreadScheduledExecutor("logging-pod-status-watcher")
    +  private val logRunnable: Runnable = new Runnable {
    +    override def run() = logShortStatus()
    +  }
    +
    +  private var pod = Option.empty[Pod]
    +
    +  private def phase: String = pod.map(_.getStatus.getPhase).getOrElse("unknown")
    +
    +  def start(): Unit = {
    +    maybeLoggingInterval.foreach { interval =>
    +      require(interval > 0, s"Logging interval must be a positive time value, got: $interval ms.")
    +      scheduler.scheduleAtFixedRate(logRunnable, 0, interval, TimeUnit.MILLISECONDS)
    +    }
    +  }
    +
    +  override def eventReceived(action: Action, pod: Pod): Unit = {
    +    this.pod = Option(pod)
    +    action match {
    +      case Action.DELETED =>
    +        closeWatch()
    +
    +      case Action.ERROR =>
    +        closeWatch()
    --- End diff --
    
    `hasCompleted` is checked below. Refactored the cases.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366717
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/ConfigurationUtils.scala ---
    @@ -0,0 +1,50 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package org.apache.spark.deploy.k8s
    +
    +import org.apache.spark.SparkConf
    +
    +private[spark] object ConfigurationUtils {
    +
    +  /**
    +   * Extract and parse Spark configuration properties with a given name prefix and
    +   * return the result as a Map. Keys must not have more than one value.
    +   *
    +   * @param sparkConf Spark configuration
    +   * @param prefix the given property name prefix
    +   * @return a Map storing the configuration property keys and values
    +   */
    +  def parsePrefixedKeyValuePairs(
    +      sparkConf: SparkConf,
    +      prefix: String): Map[String, String] = {
    +    sparkConf.getAllWithPrefix(prefix).toMap
    +  }
    +
    +  def requireSecondIfFirstIsDefined(
    --- End diff --
    
    Removed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154806055
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    +        BaseEncoding.base64().encode(Files.toByteArray(file))
    +      }
    +  }
    +
    +  private def resolveSecretLocation(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      mountedCanonicalLocation: String): Option[String] = {
    +    mountedUserSpecified.orElse(valueMountedFromSubmitter.map( _ =>
    +      mountedCanonicalLocation
    +    ))
    +  }
    +
    +  private def resolveSecretData(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      secretName: String): Map[String, String] = {
    +    mountedUserSpecified.map { _ => Map.empty[String, String] }
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153093170
  
    --- Diff: resource-managers/kubernetes/core/pom.xml ---
    @@ -0,0 +1,102 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    +  ~ Licensed to the Apache Software Foundation (ASF) under one or more
    +  ~ contributor license agreements.  See the NOTICE file distributed with
    +  ~ this work for additional information regarding copyright ownership.
    +  ~ The ASF licenses this file to You 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
    +  ~
    +  ~    http://www.apache.org/licenses/LICENSE-2.0
    +  ~
    +  ~ Unless required by applicable law or agreed to in writing, software
    +  ~ distributed under the License is distributed on an "AS IS" BASIS,
    +  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +  ~ See the License for the specific language governing permissions and
    +  ~ limitations under the License.
    +  -->
    +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    --- End diff --
    
    Hopefully the duplicated files/diffs between #19468 and #19717 are in sync.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test FAILed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84348/
    Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153581863
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,162 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf.get(DRIVER_DOCKER_IMAGE)
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    +    .get(KUBERNETES_DRIVER_MEMORY_OVERHEAD)
    +    .getOrElse(math.max((MEMORY_OVERHEAD_FACTOR * driverMemoryMiB).toInt,
    +      MEMORY_OVERHEAD_MIN_MIB))
    +  private val driverContainerMemoryWithOverheadMiB = driverMemoryMiB + memoryOverheadMiB
    +
    +  override def configureDriver(
    +      driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverExtraClasspathEnv = driverExtraClasspath.map { classPath =>
    +      new EnvVarBuilder()
    +        .withName(ENV_SUBMIT_EXTRA_CLASSPATH)
    +        .withValue(classPath)
    +        .build()
    +    }
    --- End diff --
    
    We don't. It can be supported in the future. However, we also expect many of the use cases that normally would be handled by these properties to be manageable by building custom Docker images and letting those define the artifacts that are on the classpath.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84354 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84354/testReport)** for PR 19717 at commit [`0f48f03`](https://github.com/apache/spark/commit/0f48f036d6d68fb06d24298a11cc2c139d00735e).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test FAILed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84513/
    Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84352 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84352/testReport)** for PR 19717 at commit [`357bd85`](https://github.com/apache/spark/commit/357bd8529e44266beacaec50bf6507da41e4cd24).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153548631
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,160 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using " +
    +        "spark-submit in cluster mode, this can also be passed to spark-submit via the " +
    +        "--kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag " +
    +        "format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses " +
    +        "this service account when requesting executor pods from the API server. If specific " +
    +        "credentials are given for the driver pod to use, the driver will favor " +
    +        "using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the " +
    +        "driver submission server. This is memory that accounts for things like VM overheads, " +
    +        "interned strings, other native overheads, etc. This tends to grow with the driver's " +
    +        "memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This " +
    +        "is memory that accounts for things like VM overheads, interned strings, other native " +
    +        "overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
    +  val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    require(rawMasterString.startsWith("k8s://"),
    +      "Master URL should start with k8s:// in Kubernetes mode.")
    +    val masterWithoutK8sPrefix = rawMasterString.replaceFirst("k8s://", "")
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154503012
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmitArguments.scala ---
    @@ -590,6 +600,11 @@ private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, S
             |                              the node running the Application Master via the Secure
             |                              Distributed Cache, for renewing the login tickets and the
             |                              delegation tokens periodically.
    +        |
    +        | Kubernetes only:
    +        |  --kubernetes-namespace NS   The namespace in the Kubernetes cluster within which the
    +        |                              application must be launched. The namespace must already
    +        |                              exist in the cluster. (Default: default).
    --- End diff --
    
    now that docker image is without a default value (`spark.kubernetes.*.docker.image`), (yes that discussion is ongoing) I wonder if it makes sense to bubble that up as a `--param` for visibility/convenient.
    
    tbh I'm generally against adding `--param` to submit because of the potential confusion it can cause, but since we are here and there's a `--kubernetes-namespace`


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jerryshao <gi...@git.apache.org>.
Github user jerryshao commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155125699
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case e: Throwable =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    +          throw e
    +      }
    +
    +      if (waitForAppCompletion) {
    +        logInfo(s"Waiting for application $appName to finish...")
    +        loggingPodStatusWatcher.awaitCompletion()
    +        logInfo(s"Application $appName finished.")
    +      } else {
    +        logInfo(s"Deployed Spark application $appName into Kubernetes.")
    +      }
    +    }
    +  }
    +
    +  // Add a OwnerReference to the given resources making the driver pod an owner of them so when
    +  // the driver pod is deleted, the resources are garbage collected.
    +  private def addDriverOwnerReference(driverPod: Pod, resources: Seq[HasMetadata]): Unit = {
    +    val driverPodOwnerReference = new OwnerReferenceBuilder()
    +      .withName(driverPod.getMetadata.getName)
    +      .withApiVersion(driverPod.getApiVersion)
    +      .withUid(driverPod.getMetadata.getUid)
    +      .withKind(driverPod.getKind)
    +      .withController(true)
    +      .build()
    +    resources.foreach { resource =>
    +      val originalMetadata = resource.getMetadata
    +      originalMetadata.setOwnerReferences(Collections.singletonList(driverPodOwnerReference))
    +    }
    +  }
    +}
    +
    +/**
    + * Main class and entry point of application submission in KUBERNETES mode.
    + */
    +private[spark] object Client extends SparkApplication {
    +
    +  override def start(args: Array[String], conf: SparkConf): Unit = {
    +    val parsedArguments = ClientArguments.fromCommandLineArgs(args)
    +    run(parsedArguments, conf)
    +  }
    +
    +  private def run(clientArguments: ClientArguments, sparkConf: SparkConf): Unit = {
    +    val namespace = sparkConf.get(KUBERNETES_NAMESPACE)
    +    val kubernetesAppId = s"spark-${UUID.randomUUID().toString.replaceAll("-", "")}"
    --- End diff --
    
    Thanks for the explanation. 
    
    My hunch is that if we can make this app-id more meaningful, like timestamp-xxx, which will be easier for user to distinguish. Using UUID though achieves uniqueness, seems not so intuitive.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155956779
  
    --- Diff: core/src/main/scala/org/apache/spark/util/Utils.scala ---
    @@ -2744,6 +2744,42 @@ private[spark] object Utils extends Logging {
         }
       }
     
    +  /**
    +   * Check the validity of the given Kubernetes master URL and return the resolved URL. Prefix
    +   * "k8s:" is appended to the resolved URL as the prefix is used by KubernetesClusterManager
    +   * in canCreate to determine if the KubernetesClusterManager should be used.
    +   */
    +  def checkAndGetK8sMasterUrl(rawMasterURL: String): String = {
    +    require(rawMasterURL.startsWith("k8s://"),
    +      "Kubernetes master URL must start with k8s://.")
    +    val masterWithoutK8sPrefix = rawMasterURL.substring("k8s://".length)
    +
    +    // To handle master URLs, e.g., k8s://host:port.
    +    if (!masterWithoutK8sPrefix.contains("://")) {
    --- End diff --
    
    startsWith instead of contains?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Actually given that in our implementations the executors talk to the driver through a Kubernetes headless service that is only created by the submission client. I don't think any of the two cases above works. So creating a `SparkContext` directly with a `k8s` url should not be allowed until we find a way to make it work. @vanzin I'm not sure if there's any precedence for this, and how this can be enforced. Thoughts or suggestions? 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84613 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84613/testReport)** for PR 19717 at commit [`83d0b9c`](https://github.com/apache/spark/commit/83d0b9cc29eec2654308344ac8b2208e5e3f08df).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by viirya <gi...@git.apache.org>.
Github user viirya commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154549172
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -296,6 +298,12 @@ object SparkSubmit extends CommandLineUtils with Logging {
           case (STANDALONE, CLUSTER) if args.isR =>
             printErrorAndExit("Cluster deploy mode is currently not supported for R " +
               "applications on standalone clusters.")
    +      case (KUBERNETES, CLIENT) =>
    +        printErrorAndExit("Client mode is currently not supported for Kubernetes.")
    +      case (KUBERNETES, _) if args.isPython =>
    +        printErrorAndExit("Python applications are currently not supported for Kubernetes.")
    +      case (KUBERNETES, _) if args.isR =>
    +        printErrorAndExit("R applications are currently not supported for Kubernetes.")
    --- End diff --
    
    nit: Not affect the result, but logically I think it is better:
    
    ```scala
    case (KUBERNETES, _) if args.isPython =>
      printErrorAndExit("Python applications are currently not supported for Kubernetes.")
    case (KUBERNETES, _) if args.isR =>
      printErrorAndExit("R applications are currently not supported for Kubernetes.")
    case (KUBERNETES, CLIENT) =>
      printErrorAndExit("Client mode is currently not supported for Kubernetes.")
    ```


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154134088
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/KubernetesClientFactory.scala ---
    @@ -0,0 +1,104 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.io.File
    +
    +import com.google.common.base.Charsets
    +import com.google.common.io.Files
    +import io.fabric8.kubernetes.client.{ConfigBuilder, DefaultKubernetesClient, KubernetesClient}
    +import io.fabric8.kubernetes.client.utils.HttpClientUtils
    +import okhttp3.Dispatcher
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.util.ThreadUtils
    +
    +/**
    + * Spark-opinionated builder for Kubernetes clients. It uses a prefix plus common suffixes to
    + * parse configuration keys, similar to the manner in which Spark's SecurityManager parses SSL
    + * options for different components.
    + */
    +private[spark] object KubernetesClientFactory {
    --- End diff --
    
    Ah, it's duplicated. Removed this class.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84438 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84438/testReport)** for PR 19717 at commit [`cfcf2a7`](https://github.com/apache/spark/commit/cfcf2a7622f2bd4a21ea2d5197ec652363645aca).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154814933
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala ---
    @@ -0,0 +1,121 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.steps._
    +import org.apache.spark.launcher.SparkLauncher
    +import org.apache.spark.util.SystemClock
    +
    +/**
    + * Constructs the complete list of driver configuration steps to run to deploy the Spark driver.
    + */
    +private[spark] class DriverConfigurationStepsOrchestrator(
    +    namespace: String,
    +    kubernetesAppId: String,
    +    launchTime: Long,
    +    mainAppResource: Option[MainAppResource],
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) {
    +
    +  // The resource name prefix is derived from the application name, making it easy to connect the
    +  // names of the Kubernetes resources from e.g. kubectl or the Kubernetes dashboard to the
    +  // application the user submitted. However, we can't use the application name in the label, as
    +  // label values are considerably restrictive, e.g. must be no longer than 63 characters in
    +  // length. So we generate a separate identifier for the app ID itself, and bookkeeping that
    +  // requires finding "all pods for this application" should use the kubernetesAppId.
    +  private val kubernetesResourceNamePrefix =
    --- End diff --
    
    UUIDs are a bit long which leads to name length problems since Kubernetes requires some names to be < 63 characters. I would agree that a UUID would be better if we can get away with it.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155858142
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala ---
    @@ -0,0 +1,121 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.steps._
    +import org.apache.spark.launcher.SparkLauncher
    +import org.apache.spark.util.SystemClock
    +
    +/**
    + * Constructs the complete list of driver configuration steps to run to deploy the Spark driver.
    + */
    +private[spark] class DriverConfigurationStepsOrchestrator(
    +    namespace: String,
    +    kubernetesAppId: String,
    +    launchTime: Long,
    +    mainAppResource: Option[MainAppResource],
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) {
    +
    +  // The resource name prefix is derived from the application name, making it easy to connect the
    +  // names of the Kubernetes resources from e.g. kubectl or the Kubernetes dashboard to the
    +  // application the user submitted. However, we can't use the application name in the label, as
    +  // label values are considerably restrictive, e.g. must be no longer than 63 characters in
    +  // length. So we generate a separate identifier for the app ID itself, and bookkeeping that
    +  // requires finding "all pods for this application" should use the kubernetesAppId.
    +  private val kubernetesResourceNamePrefix =
    +      s"$appName-$launchTime".toLowerCase.replaceAll("\\.", "-")
    +  private val dockerImagePullPolicy = submissionSparkConf.get(DOCKER_IMAGE_PULL_POLICY)
    +  private val jarsDownloadPath = submissionSparkConf.get(JARS_DOWNLOAD_LOCATION)
    +  private val filesDownloadPath = submissionSparkConf.get(FILES_DOWNLOAD_LOCATION)
    +
    +  def getAllConfigurationSteps(): Seq[DriverConfigurationStep] = {
    +    val driverCustomLabels = ConfigurationUtils.parsePrefixedKeyValuePairs(
    +      submissionSparkConf,
    +      KUBERNETES_DRIVER_LABEL_PREFIX)
    +    require(!driverCustomLabels.contains(SPARK_APP_ID_LABEL), "Label with key " +
    +      s"$SPARK_APP_ID_LABEL is not allowed as it is reserved for Spark bookkeeping " +
    +      "operations.")
    +    require(!driverCustomLabels.contains(SPARK_ROLE_LABEL), "Label with key " +
    +      s"$SPARK_ROLE_LABEL is not allowed as it is reserved for Spark bookkeeping " +
    +      "operations.")
    +
    +    val allDriverLabels = driverCustomLabels ++ Map(
    +      SPARK_APP_ID_LABEL -> kubernetesAppId,
    +      SPARK_ROLE_LABEL -> SPARK_POD_DRIVER_ROLE)
    +
    +    val initialSubmissionStep = new BaseDriverConfigurationStep(
    +      kubernetesAppId,
    +      kubernetesResourceNamePrefix,
    +      allDriverLabels,
    +      dockerImagePullPolicy,
    +      appName,
    +      mainClass,
    +      appArgs,
    +      submissionSparkConf)
    +
    +    val driverAddressStep = new DriverServiceBootstrapStep(
    +      kubernetesResourceNamePrefix,
    +      allDriverLabels,
    +      submissionSparkConf,
    +      new SystemClock)
    +
    +    val kubernetesCredentialsStep = new DriverKubernetesCredentialsStep(
    +      submissionSparkConf, kubernetesResourceNamePrefix)
    +
    +    val additionalMainAppJar = if (mainAppResource.nonEmpty) {
    +       val mayBeResource = mainAppResource.get match {
    +        case JavaMainAppResource(resource) if resource != SparkLauncher.NO_RESOURCE =>
    +          Some(resource)
    +        case _ => Option.empty
    --- End diff --
    
    Yes, it will eventually looks like https://github.com/apache-spark-on-k8s/spark/blob/branch-2.2-kubernetes/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala#L56 once we port Python and R support over.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r156172335
  
    --- Diff: core/src/test/scala/org/apache/spark/util/UtilsSuite.scala ---
    @@ -1146,6 +1146,27 @@ class UtilsSuite extends SparkFunSuite with ResetSystemProperties with Logging {
         }
       }
     
    +  test("check Kubernetes master URL") {
    +    val k8sMasterURLHttps = Utils.checkAndGetK8sMasterUrl("k8s://https://host:port")
    +    assert(k8sMasterURLHttps == "k8s:https://host:port")
    --- End diff --
    
    `===` (also in other places)


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84268 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84268/testReport)** for PR 19717 at commit [`350932b`](https://github.com/apache/spark/commit/350932bad96a903c1e95404e3c24490b55d354d2).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153548705
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,236 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainAppResource.isDefined,
    +      "Main app resource must be defined by --primary-java-resource.")
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource.get,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) => new EnvVarBuilder()
    +        .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +        .withValue(option)
    +        .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case NonFatal(e) =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    +          throw e
    +      }
    +
    +      if (waitForAppCompletion) {
    +        logInfo(s"Waiting for application $appName to finish...")
    +        loggingPodStatusWatcher.awaitCompletion()
    +        logInfo(s"Application $appName finished.")
    +      } else {
    +        logInfo(s"Deployed Spark application $appName into Kubernetes.")
    +      }
    +    }
    +  }
    +
    +  // Add a OwnerReference to the given resources making the driver pod an owner of them so when
    +  // the driver pod is deleted, the resources are garbage collected.
    +  private def addDriverOwnerReference(driverPod: Pod, resources: Seq[HasMetadata]): Unit = {
    +    val driverPodOwnerReference = new OwnerReferenceBuilder()
    +      .withName(driverPod.getMetadata.getName)
    +      .withApiVersion(driverPod.getApiVersion)
    +      .withUid(driverPod.getMetadata.getUid)
    +      .withKind(driverPod.getKind)
    +      .withController(true)
    +      .build()
    +    resources.foreach { resource =>
    +      val originalMetadata = resource.getMetadata
    +      originalMetadata.setOwnerReferences(Collections.singletonList(driverPodOwnerReference))
    +    }
    +  }
    +}
    +
    +/**
    + * Main class and entry point of application submission in KUBERNETES mode.
    + */
    +private[spark] object Client extends SparkApplication {
    +
    +  override def start(args: Array[String], conf: SparkConf): Unit = {
    +    val parsedArguments = ClientArguments.fromCommandLineArgs(args)
    +    run(parsedArguments, conf)
    +  }
    +
    +  private def run(clientArguments: ClientArguments, sparkConf: SparkConf): Unit = {
    +    val namespace = sparkConf.get(KUBERNETES_NAMESPACE)
    +    val kubernetesAppId = s"spark-${UUID.randomUUID().toString.replaceAll("-", "")}"
    +    val launchTime = System.currentTimeMillis()
    +    val waitForAppCompletion = sparkConf.get(WAIT_FOR_APP_COMPLETION)
    +    val appName = sparkConf.getOption("spark.app.name").getOrElse("spark")
    +    val master = getK8sMasterUrl(sparkConf.get("spark.master"))
    +    val loggingInterval = Option(sparkConf.get(REPORT_INTERVAL)).filter(_ => waitForAppCompletion)
    +
    +    val loggingPodStatusWatcher = new LoggingPodStatusWatcherImpl(
    +      kubernetesAppId, loggingInterval)
    +
    +    val configurationStepsOrchestrator = new DriverConfigurationStepsOrchestrator(
    +      namespace,
    +      kubernetesAppId,
    +      launchTime,
    +      clientArguments.mainAppResource,
    +      appName,
    +      clientArguments.mainClass,
    +      clientArguments.driverArgs,
    +      sparkConf)
    +
    +    Utils.tryWithResource(KubernetesClientFactory.createKubernetesClient(
    +      master,
    +      Some(namespace),
    +      KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX,
    +      sparkConf,
    +      None,
    +      None)) {
    +      kubernetesClient =>
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155016194
  
    --- Diff: docs/running-on-yarn.md ---
    @@ -234,18 +234,11 @@ To use a custom metrics.properties for the application master and executors, upd
         The amount of off-heap memory (in megabytes) to be allocated per executor. This is memory that accounts for things like VM overheads, interned strings, other native overheads, etc. This tends to grow with the executor size (typically 6-10%).
       </td>
     </tr>
    -<tr>
    -  <td><code>spark.yarn.driver.memoryOverhead</code></td>
    --- End diff --
    
    Is there an example PR for deprecating a config property that I can follow?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154756363
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/KubernetesFileUtils.scala ---
    @@ -0,0 +1,68 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.io.File
    +
    +import org.apache.spark.util.Utils
    +
    +private[spark] object KubernetesFileUtils {
    +
    +  /**
    +   * For the given collection of file URIs, resolves them as follows:
    +   * - File URIs with scheme file:// are resolved to the given download path.
    +   * - File URIs with scheme local:// resolve to just the path of the URI.
    +   * - Otherwise, the URIs are returned as-is.
    +   */
    +  def resolveSubmittedUris(
    +      fileUris: Iterable[String],
    +      fileDownloadPath: String): Iterable[String] = {
    +    fileUris.map { uri =>
    +      val fileUri = Utils.resolveURI(uri)
    +      val fileScheme = Option(fileUri.getScheme).getOrElse("file")
    +      fileScheme match {
    --- End diff --
    
    This looks a lot like `resolveFilePaths`, you could probably merge the two in some way.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153123413
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala ---
    @@ -0,0 +1,84 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.steps.{BaseDriverConfigurationStep, DriverConfigurationStep, DriverKubernetesCredentialsStep, DriverServiceBootstrapStep}
    +import org.apache.spark.util.SystemClock
    +
    +/**
    + * Constructs the complete list of driver configuration steps to run to deploy the Spark driver.
    + */
    +private[spark] class DriverConfigurationStepsOrchestrator(
    +    namespace: String,
    +    kubernetesAppId: String,
    +    launchTime: Long,
    +    mainAppResource: MainAppResource,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) {
    +
    +  // The resource name prefix is derived from the application name, making it easy to connect the
    +  // names of the Kubernetes resources from e.g. kubectl or the Kubernetes dashboard to the
    +  // application the user submitted. However, we can't use the application name in the label, as
    +  // label values are considerably restrictive, e.g. must be no longer than 63 characters in
    +  // length. So we generate a separate identifier for the app ID itself, and bookkeeping that
    +  // requires finding "all pods for this application" should use the kubernetesAppId.
    +  private val kubernetesResourceNamePrefix =
    +      s"$appName-$launchTime".toLowerCase.replaceAll("\\.", "-")
    +  private val dockerImagePullPolicy = submissionSparkConf.get(DOCKER_IMAGE_PULL_POLICY)
    +
    +  def getAllConfigurationSteps(): Seq[DriverConfigurationStep] = {
    +    val driverCustomLabels = ConfigurationUtils.parsePrefixedKeyValuePairs(
    +      submissionSparkConf,
    +      KUBERNETES_DRIVER_LABEL_PREFIX)
    +    require(!driverCustomLabels.contains(SPARK_APP_ID_LABEL), "Label with key " +
    --- End diff --
    
    nit: `SPARK_ROLE_LABEL` also ?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84453 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84453/testReport)** for PR 19717 at commit [`347ed69`](https://github.com/apache/spark/commit/347ed697e3f17546af83a8cd8a9578527cf65384).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154754517
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case e: Throwable =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    +          throw e
    +      }
    +
    +      if (waitForAppCompletion) {
    +        logInfo(s"Waiting for application $appName to finish...")
    +        loggingPodStatusWatcher.awaitCompletion()
    +        logInfo(s"Application $appName finished.")
    +      } else {
    +        logInfo(s"Deployed Spark application $appName into Kubernetes.")
    +      }
    +    }
    +  }
    +
    +  // Add a OwnerReference to the given resources making the driver pod an owner of them so when
    +  // the driver pod is deleted, the resources are garbage collected.
    +  private def addDriverOwnerReference(driverPod: Pod, resources: Seq[HasMetadata]): Unit = {
    +    val driverPodOwnerReference = new OwnerReferenceBuilder()
    +      .withName(driverPod.getMetadata.getName)
    +      .withApiVersion(driverPod.getApiVersion)
    +      .withUid(driverPod.getMetadata.getUid)
    +      .withKind(driverPod.getKind)
    +      .withController(true)
    +      .build()
    +    resources.foreach { resource =>
    +      val originalMetadata = resource.getMetadata
    +      originalMetadata.setOwnerReferences(Collections.singletonList(driverPodOwnerReference))
    +    }
    +  }
    +}
    +
    +/**
    + * Main class and entry point of application submission in KUBERNETES mode.
    + */
    +private[spark] object Client extends SparkApplication {
    +
    +  override def start(args: Array[String], conf: SparkConf): Unit = {
    +    val parsedArguments = ClientArguments.fromCommandLineArgs(args)
    +    run(parsedArguments, conf)
    +  }
    +
    +  private def run(clientArguments: ClientArguments, sparkConf: SparkConf): Unit = {
    +    val namespace = sparkConf.get(KUBERNETES_NAMESPACE)
    +    val kubernetesAppId = s"spark-${UUID.randomUUID().toString.replaceAll("-", "")}"
    +    val launchTime = System.currentTimeMillis()
    +    val waitForAppCompletion = sparkConf.get(WAIT_FOR_APP_COMPLETION)
    +    val appName = sparkConf.getOption("spark.app.name").getOrElse("spark")
    +    // The master URL has been checked for validity already in SparkSubmit.
    +    val master = sparkConf.get("spark.master")
    +    val loggingInterval = Option(sparkConf.get(REPORT_INTERVAL)).filter(_ => waitForAppCompletion)
    --- End diff --
    
    Kind of an odd way to write `if (waitForAppCompletion) Some(...) else None`.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155039001
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case e: Throwable =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    +          throw e
    +      }
    +
    +      if (waitForAppCompletion) {
    +        logInfo(s"Waiting for application $appName to finish...")
    +        loggingPodStatusWatcher.awaitCompletion()
    +        logInfo(s"Application $appName finished.")
    +      } else {
    +        logInfo(s"Deployed Spark application $appName into Kubernetes.")
    +      }
    +    }
    +  }
    +
    +  // Add a OwnerReference to the given resources making the driver pod an owner of them so when
    +  // the driver pod is deleted, the resources are garbage collected.
    +  private def addDriverOwnerReference(driverPod: Pod, resources: Seq[HasMetadata]): Unit = {
    +    val driverPodOwnerReference = new OwnerReferenceBuilder()
    +      .withName(driverPod.getMetadata.getName)
    +      .withApiVersion(driverPod.getApiVersion)
    +      .withUid(driverPod.getMetadata.getUid)
    +      .withKind(driverPod.getKind)
    +      .withController(true)
    +      .build()
    +    resources.foreach { resource =>
    +      val originalMetadata = resource.getMetadata
    +      originalMetadata.setOwnerReferences(Collections.singletonList(driverPodOwnerReference))
    +    }
    +  }
    +}
    +
    +/**
    + * Main class and entry point of application submission in KUBERNETES mode.
    + */
    +private[spark] object Client extends SparkApplication {
    +
    +  override def start(args: Array[String], conf: SparkConf): Unit = {
    +    val parsedArguments = ClientArguments.fromCommandLineArgs(args)
    +    run(parsedArguments, conf)
    +  }
    +
    +  private def run(clientArguments: ClientArguments, sparkConf: SparkConf): Unit = {
    +    val namespace = sparkConf.get(KUBERNETES_NAMESPACE)
    +    val kubernetesAppId = s"spark-${UUID.randomUUID().toString.replaceAll("-", "")}"
    --- End diff --
    
    Kubernetes is different from YARN in the sense that the API server expects one to provide their own names for things that are unique in their namespace. However we're coming up with the unique object names for the user since a single spark-submit command generates multiple kinds of objects (secrets, service, pod) that may end up being difficult for the user to reason about in terms of uniqueness.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154834099
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -119,5 +139,60 @@ private[spark] object Config extends Logging {
             "must be a positive integer")
           .createWithDefault(10)
     
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  private[spark] val JARS_DOWNLOAD_LOCATION =
    +    ConfigBuilder("spark.kubernetes.mountDependencies.jarsDownloadDir")
    +      .doc("Location to download jars to in the driver and executors. When using" +
    +        " spark-submit, this directory must be empty and will be mounted as an empty directory" +
    +        " volume on the driver and executor pod.")
    +      .stringConf
    +      .createWithDefault("/var/spark-data/spark-jars")
    --- End diff --
    
    To add to @vanzin's query - do we have a default user we expect to run the container as ? (or not run as ?)
    Or does it not matter either way and is up to the user ?
    
    Also, would be great if we documentation assumptions like this in the k8s documentation - which paths should be available, should be writable, ports accessible, etc (perhaps this is already in the docs, and I am yet to find it)


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    @vanzin @jiangxb1987 @mridulm PTAL. Thanks!


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84306 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84306/testReport)** for PR 19717 at commit [`2d02e39`](https://github.com/apache/spark/commit/2d02e39cecd0d2bc01ad8f706089da2532455160).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366725
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,219 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +private[spark] case class ClientArguments(
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84440 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84440/testReport)** for PR 19717 at commit [`faa2849`](https://github.com/apache/spark/commit/faa2849b57e158471285df51a9a3b1cb0acb6b68).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84450 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84450/testReport)** for PR 19717 at commit [`cdca57e`](https://github.com/apache/spark/commit/cdca57ece845c86ac16d6089460f28e3ff3c298f).
     * This patch **fails PySpark unit tests**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154818296
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmitArguments.scala ---
    @@ -590,6 +600,11 @@ private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, S
             |                              the node running the Application Master via the Secure
             |                              Distributed Cache, for renewing the login tickets and the
             |                              delegation tokens periodically.
    +        |
    +        | Kubernetes only:
    +        |  --kubernetes-namespace NS   The namespace in the Kubernetes cluster within which the
    --- End diff --
    
    Reverted.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84437 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84437/testReport)** for PR 19717 at commit [`05f528a`](https://github.com/apache/spark/commit/05f528a0178fa0f567b8904780d837a4f95d888b).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154801257
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    --- End diff --
    
    /cc @mccheah.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84646 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84646/testReport)** for PR 19717 at commit [`44c40b1`](https://github.com/apache/spark/commit/44c40b1b7d546e4d214aff78c282c768aa0e3d42).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154771100
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    +        BaseEncoding.base64().encode(Files.toByteArray(file))
    +      }
    +  }
    +
    +  private def resolveSecretLocation(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      mountedCanonicalLocation: String): Option[String] = {
    +    mountedUserSpecified.orElse(valueMountedFromSubmitter.map( _ =>
    +      mountedCanonicalLocation
    +    ))
    +  }
    +
    +  private def resolveSecretData(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      secretName: String): Map[String, String] = {
    +    mountedUserSpecified.map { _ => Map.empty[String, String] }
    --- End diff --
    
    Kinda hard to see what's going on here. Adding a scaladoc would help, especially explaining the arguments.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155041617
  
    --- Diff: core/src/main/scala/org/apache/spark/util/Utils.scala ---
    @@ -2744,6 +2744,25 @@ private[spark] object Utils extends Logging {
         }
       }
     
    +  /**
    +   * Check the validity of the given Kubernetes master URL and return the resolved URL.
    +   */
    +  def checkAndGetK8sMasterUrl(rawMasterURL: String): String = {
    +    require(rawMasterURL.startsWith("k8s://"),
    +      "Kubernetes master URL must start with k8s://.")
    +    val masterWithoutK8sPrefix = rawMasterURL.substring("k8s://".length)
    --- End diff --
    
    Are those URIs set in stone? This seems more readable to me:
    
    ```
    scala> new URI("k8s:http://foo:1234")
    res0: java.net.URI = k8s:http://foo:1234
    
    scala> res0.getScheme
    res1: String = k8s
    
    scala> res0.getSchemeSpecificPart
    res2: String = http://foo:1234
    ```
    
    It also allows parsing using the `URI` APIs instead of doing string manipulation.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153092633
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -702,6 +715,18 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (isKubernetesCluster) {
    +      childMainClass = "org.apache.spark.deploy.k8s.submit.Client"
    +      childArgs ++= Array("--primary-java-resource", args.primaryResource)
    --- End diff --
    
    Handle primary resource == `SparkLauncher.NO_RESOURCE` ?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153216011
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,173 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using" +
    +        " spark-submit in cluster mode, this can also be passed to spark-submit via the" +
    +        " --kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag" +
    +        " format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses" +
    +        " this service account when requesting executor pods from the API server. If specific" +
    +        " credentials are given for the driver pod to use, the driver will favor" +
    +        " using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the" +
    +        " driver submission server. This is memory that accounts for things like VM overheads," +
    +        " interned strings, other native overheads, etc. This tends to grow with the driver's" +
    +        " memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This" +
    +        " is memory that accounts for things like VM overheads, interned strings, other native" +
    +        " overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val KUBERNETES_ALLOCATION_BATCH_SIZE =
    +    ConfigBuilder("spark.kubernetes.allocation.batch.size")
    +      .doc("Number of pods to launch at once in each round of executor allocation.")
    +      .intConf
    +      .createWithDefault(5)
    +
    +  val KUBERNETES_ALLOCATION_BATCH_DELAY =
    +    ConfigBuilder("spark.kubernetes.allocation.batch.delay")
    +      .doc("Number of seconds to wait between each round of executor allocation.")
    +      .longConf
    +      .createWithDefault(1)
    +
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the" +
    +        " launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
    +  val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    if (!rawMasterString.startsWith("k8s://")) {
    --- End diff --
    
    Why not use `require` here?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jerryshao <gi...@git.apache.org>.
Github user jerryshao commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153678912
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,160 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using " +
    +        "spark-submit in cluster mode, this can also be passed to spark-submit via the " +
    +        "--kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag " +
    +        "format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses " +
    +        "this service account when requesting executor pods from the API server. If specific " +
    +        "credentials are given for the driver pod to use, the driver will favor " +
    +        "using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the " +
    +        "driver submission server. This is memory that accounts for things like VM overheads, " +
    +        "interned strings, other native overheads, etc. This tends to grow with the driver's " +
    +        "memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This " +
    +        "is memory that accounts for things like VM overheads, interned strings, other native " +
    +        "overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
    +  val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    require(rawMasterString.startsWith("k8s://"),
    --- End diff --
    
    I see, I was just wondering if we can fail fast for illegal master url.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154503100
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,160 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using " +
    +        "spark-submit in cluster mode, this can also be passed to spark-submit via the " +
    +        "--kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag " +
    +        "format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses " +
    +        "this service account when requesting executor pods from the API server. If specific " +
    +        "credentials are given for the driver pod to use, the driver will favor " +
    +        "using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the " +
    +        "driver submission server. This is memory that accounts for things like VM overheads, " +
    +        "interned strings, other native overheads, etc. This tends to grow with the driver's " +
    +        "memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This " +
    +        "is memory that accounts for things like VM overheads, interned strings, other native " +
    +        "overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
    +  val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    require(rawMasterString.startsWith("k8s://"),
    --- End diff --
    
    could we also check the slashes and "k8s://http" sequence up front?
    we might want to explore re-using this check/logic by exposing a helper func


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by ueshin <gi...@git.apache.org>.
Github user ueshin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154016042
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/KubernetesClientFactory.scala ---
    @@ -0,0 +1,104 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.io.File
    +
    +import com.google.common.base.Charsets
    +import com.google.common.io.Files
    +import io.fabric8.kubernetes.client.{ConfigBuilder, DefaultKubernetesClient, KubernetesClient}
    +import io.fabric8.kubernetes.client.utils.HttpClientUtils
    +import okhttp3.Dispatcher
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.util.ThreadUtils
    +
    +/**
    + * Spark-opinionated builder for Kubernetes clients. It uses a prefix plus common suffixes to
    + * parse configuration keys, similar to the manner in which Spark's SecurityManager parses SSL
    + * options for different components.
    + */
    +private[spark] object KubernetesClientFactory {
    --- End diff --
    
    What's the difference between this and `SparkKubernetesClientFactory`?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by viirya <gi...@git.apache.org>.
Github user viirya commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154549074
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -251,6 +252,7 @@ object SparkSubmit extends CommandLineUtils with Logging {
             YARN
           case m if m.startsWith("spark") => STANDALONE
           case m if m.startsWith("mesos") => MESOS
    +      case m if m.startsWith("k8s") => KUBERNETES
           case m if m.startsWith("local") => LOCAL
           case _ =>
             printErrorAndExit("Master must either be yarn or start with spark, mesos, local")
    --- End diff --
    
    `Master must either be yarn or start with spark, mesos, k8s, local`


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by ueshin <gi...@git.apache.org>.
Github user ueshin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153402807
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,160 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using " +
    +        "spark-submit in cluster mode, this can also be passed to spark-submit via the " +
    +        "--kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag " +
    +        "format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses " +
    +        "this service account when requesting executor pods from the API server. If specific " +
    +        "credentials are given for the driver pod to use, the driver will favor " +
    +        "using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the " +
    +        "driver submission server. This is memory that accounts for things like VM overheads, " +
    +        "interned strings, other native overheads, etc. This tends to grow with the driver's " +
    +        "memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This " +
    +        "is memory that accounts for things like VM overheads, interned strings, other native " +
    +        "overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
    +  val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    require(rawMasterString.startsWith("k8s://"),
    +      "Master URL should start with k8s:// in Kubernetes mode.")
    +    val masterWithoutK8sPrefix = rawMasterString.replaceFirst("k8s://", "")
    --- End diff --
    
    nit: `rawMasterString.substring("k8s://".length)`?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154719522
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,43 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    +
    +# If this docker file is being used in the context of building your images from a Spark distribution, the docker build
    +# command should be invoked from the top level directory of the Spark distribution. E.g.:
    +# docker build -t spark-base:latest -f dockerfiles/spark-base/Dockerfile .
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by foxish <gi...@git.apache.org>.
Github user foxish commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r157643149
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    @yangw1234, can you confirm that libc6-compat fixed the issue you had?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153375735
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,219 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainAppResource.isDefined,
    +      "Main app resource must be defined by either --primary-py-file or --primary-java-resource.")
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    --- End diff --
    
    For case of spark examples, spark thrift server, etc - there is no main app resource - the bits are bundled as part of spark itself.
    You can take a look at how yarn or mesos handles this case : I would assume something similar should suffice (look for usage of SparkLauncher.NO_RESOURCE)


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by ueshin <gi...@git.apache.org>.
Github user ueshin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153414331
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/entrypoint.sh ---
    @@ -0,0 +1,37 @@
    +#!/bin/bash
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +# echo commands to the terminal output
    +set -x
    +
    +# Check whether there is a passwd entry for the container UID
    +myuid=$(id -u)
    +mygid=$(id -g)
    +uidentry=$(getent passwd $myuid)
    +
    +# If there is no passwd entry for the container UID, attempt to create one
    +if [ -z "$uidentry" ] ; then
    +    if [ -w /etc/passwd ] ; then
    +        echo "$myuid:x:$myuid:$mygid:anonymous uid:$SPARK_HOME:/bin/false" >> /etc/passwd
    +    else
    +        echo "Container ENTRYPOINT failed to add passwd entry for anonymous UID"
    +    fi
    +fi
    +
    +# Execute the container CMD under tini for better hygiene
    +/sbin/tini -s -- "$@"
    --- End diff --
    
    nit: ditto.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84437 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84437/testReport)** for PR 19717 at commit [`05f528a`](https://github.com/apache/spark/commit/05f528a0178fa0f567b8904780d837a4f95d888b).
     * This patch **fails Scala style tests**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153548743
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/driver/Dockerfile ---
    @@ -0,0 +1,31 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM spark-base
    +
    +# If this docker file is being used in the context of building your images from a Spark distribution, the docker build
    +# command should be invoked from the top level directory of the Spark distribution. E.g.:
    +# docker build -t spark-driver:latest -f dockerfiles/driver/Dockerfile .
    +
    +COPY examples /opt/spark/examples
    +
    +CMD SPARK_CLASSPATH="${SPARK_HOME}/jars/*" && \
    +    env | grep SPARK_JAVA_OPT_ | sed 's/[^=]*=\(.*\)/\1/g' > /tmp/java_opts.txt && \
    +    readarray -t SPARK_DRIVER_JAVA_OPTS < /tmp/java_opts.txt && \
    +    if ! [ -z ${SPARK_MOUNTED_CLASSPATH+x} ]; then SPARK_CLASSPATH="$SPARK_MOUNTED_CLASSPATH:$SPARK_CLASSPATH"; fi && \
    +    if ! [ -z ${SPARK_SUBMIT_EXTRA_CLASSPATH+x} ]; then SPARK_CLASSPATH="$SPARK_SUBMIT_EXTRA_CLASSPATH:$SPARK_CLASSPATH"; fi && \
    +    ${JAVA_HOME}/bin/java "${SPARK_DRIVER_JAVA_OPTS[@]}" -cp "$SPARK_CLASSPATH" -Xms$SPARK_DRIVER_MEMORY -Xmx$SPARK_DRIVER_MEMORY -Dspark.driver.bindAddress=$SPARK_DRIVER_BIND_ADDRESS $SPARK_DRIVER_CLASS $SPARK_DRIVER_ARGS
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153548646
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,236 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154771178
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    +        BaseEncoding.base64().encode(Files.toByteArray(file))
    +      }
    +  }
    +
    +  private def resolveSecretLocation(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      mountedCanonicalLocation: String): Option[String] = {
    +    mountedUserSpecified.orElse(valueMountedFromSubmitter.map( _ =>
    +      mountedCanonicalLocation
    +    ))
    +  }
    +
    +  private def resolveSecretData(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      secretName: String): Map[String, String] = {
    +    mountedUserSpecified.map { _ => Map.empty[String, String] }
    +      .getOrElse {
    +        valueMountedFromSubmitter.map { valueBase64 =>
    +          Map(secretName -> valueBase64)
    +        }.getOrElse(Map.empty[String, String])
    +      }
    +  }
    +
    +  private implicit def augmentSparkConf(sparkConf: SparkConf): OptionSettableSparkConf = {
    +    new OptionSettableSparkConf(sparkConf)
    +  }
    +}
    +
    +private class OptionSettableSparkConf(sparkConf: SparkConf) {
    +  def setOption(configEntry: String, option: Option[String]): SparkConf = {
    +    option.map( opt =>
    --- End diff --
    
    `.map { opt => `
    
    Using `option.foreach` here would also be cleaner.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by foxish <gi...@git.apache.org>.
Github user foxish commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    > Yes, I agree that we eventually should allow client mode including use cases that directly create SparkContext
    
    Sorry, in your previous comment, it seemed like you were suggesting disabling it entirely and that had me concerned. As a separate issue, we may in future want to have a nicer error message for inaccessible in-cluster service (`kubernetes.default.svc`). That actually has no dependency on the driver service, or its creation - we can check for that and it should exist anyway. Disabling client mode as you did in the latest commit works for this particular scenario I think. 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153219134
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,219 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.KubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +private[spark] case class ClientArguments(
    +     mainAppResource: MainAppResource,
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.Buffer.empty[String]
    +
    +    args.sliding(2, 2).toList.collect {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainAppResource.isDefined,
    +      "Main app resource must be defined by either --primary-py-file or --primary-java-resource.")
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource.get,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +private[spark] class Client(
    --- End diff --
    
    nit: add comments to describe the usage of this class, and the meaning of each params.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155303789
  
    --- Diff: core/src/test/scala/org/apache/spark/deploy/SparkSubmitSuite.scala ---
    @@ -388,6 +388,33 @@ class SparkSubmitSuite
         conf.get("spark.ui.enabled") should be ("false")
       }
     
    +  test("handles k8s cluster mode") {
    +    val clArgs = Seq(
    +      "--deploy-mode", "cluster",
    +      "--master", "k8s://host:port",
    +      "--executor-memory", "5g",
    +      "--class", "org.SomeClass",
    +      "--driver-memory", "4g",
    +      "--conf", "spark.kubernetes.namespace=spark",
    +      "--conf", "spark.kubernetes.driver.docker.image=bar",
    --- End diff --
    
    Should we also test the arg "--kubernetes-namespace"?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84120 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84120/testReport)** for PR 19717 at commit [`f38144b`](https://github.com/apache/spark/commit/f38144bcd11a2a261c857d09dc220d11da5ead5f).
     * This patch **fails to build**.
     * This patch merges cleanly.
     * This patch adds the following public classes _(experimental)_:
      * `    require(mainClass.isDefined, \"Main class must be specified via --main-class\")`


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84453 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84453/testReport)** for PR 19717 at commit [`347ed69`](https://github.com/apache/spark/commit/347ed697e3f17546af83a8cd8a9578527cf65384).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84346 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84346/testReport)** for PR 19717 at commit [`78e5940`](https://github.com/apache/spark/commit/78e59405eed90607e4914d25f66e7275e990690e).
     * This patch **fails to build**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit closed the pull request at:

    https://github.com/apache/spark/pull/19717


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154134483
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    +        BaseEncoding.base64().encode(Files.toByteArray(file))
    +      }
    +  }
    +
    +  private def resolveSecretLocation(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      mountedCanonicalLocation: String): Option[String] = {
    +    mountedUserSpecified.orElse(valueMountedFromSubmitter.map( _ => {
    +      mountedCanonicalLocation
    +    }))
    +  }
    +
    +  private def resolveSecretData(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      secretName: String): Map[String, String] = {
    +    mountedUserSpecified.map { _ => Map.empty[String, String]}
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154694277
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverServiceBootstrapStep.scala ---
    @@ -0,0 +1,103 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.ServiceBuilder
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Clock
    +
    +/**
    + * Allows the driver to be reachable by executor pods through a headless service. The service's
    + * ports should correspond to the ports that the executor will reach the pod at for RPC.
    + */
    +private[spark] class DriverServiceBootstrapStep(
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    submissionSparkConf: SparkConf,
    +    clock: Clock) extends DriverConfigurationStep with Logging {
    +  import DriverServiceBootstrapStep._
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    require(submissionSparkConf.getOption(DRIVER_BIND_ADDRESS_KEY).isEmpty,
    +      s"$DRIVER_BIND_ADDRESS_KEY is not supported in Kubernetes mode, as the driver's bind " +
    +      "address is managed and set to the driver pod's IP address.")
    +    require(submissionSparkConf.getOption(DRIVER_HOST_KEY).isEmpty,
    +      s"$DRIVER_HOST_KEY is not supported in Kubernetes mode, as the driver's hostname will be " +
    +      "managed via a Kubernetes service.")
    +
    +    val preferredServiceName = s"$kubernetesResourceNamePrefix$DRIVER_SVC_POSTFIX"
    +    val resolvedServiceName = if (preferredServiceName.length <= MAX_SERVICE_NAME_LENGTH) {
    +      preferredServiceName
    +    } else {
    +      val randomServiceId = clock.getTimeMillis()
    +      val shorterServiceName = s"spark-$randomServiceId$DRIVER_SVC_POSTFIX"
    +      logWarning(s"Driver's hostname would preferably be $preferredServiceName, but this is " +
    +        s"too long (must be <= $MAX_SERVICE_NAME_LENGTH characters). Falling back to use " +
    +        s"$shorterServiceName as the driver service's name.")
    +      shorterServiceName
    +    }
    +
    +    val driverPort = submissionSparkConf.getInt("spark.driver.port", DEFAULT_DRIVER_PORT)
    +    val driverBlockManagerPort = submissionSparkConf.getInt(
    +        org.apache.spark.internal.config.DRIVER_BLOCK_MANAGER_PORT.key, DEFAULT_BLOCKMANAGER_PORT)
    +    val driverService = new ServiceBuilder()
    +      .withNewMetadata()
    +        .withName(resolvedServiceName)
    +        .endMetadata()
    +      .withNewSpec()
    +        .withClusterIP("None")
    +        .withSelector(driverLabels.asJava)
    +        .addNewPort()
    +          .withName(DRIVER_PORT_NAME)
    +          .withPort(driverPort)
    +          .withNewTargetPort(driverPort)
    +          .endPort()
    +        .addNewPort()
    +          .withName(BLOCK_MANAGER_PORT_NAME)
    +          .withPort(driverBlockManagerPort)
    +          .withNewTargetPort(driverBlockManagerPort)
    +          .endPort()
    +        .endSpec()
    +      .build()
    +
    +    val namespace = submissionSparkConf.get(KUBERNETES_NAMESPACE)
    +    val driverHostname = s"${driverService.getMetadata.getName}.$namespace.svc.cluster.local"
    +    val resolvedSparkConf = driverSpec.driverSparkConf.clone()
    +      .set(org.apache.spark.internal.config.DRIVER_HOST_ADDRESS, driverHostname)
    --- End diff --
    
    Ah, yeah, done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153252119
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,173 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using" +
    +        " spark-submit in cluster mode, this can also be passed to spark-submit via the" +
    +        " --kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag" +
    +        " format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses" +
    +        " this service account when requesting executor pods from the API server. If specific" +
    +        " credentials are given for the driver pod to use, the driver will favor" +
    +        " using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the" +
    +        " driver submission server. This is memory that accounts for things like VM overheads," +
    +        " interned strings, other native overheads, etc. This tends to grow with the driver's" +
    +        " memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This" +
    +        " is memory that accounts for things like VM overheads, interned strings, other native" +
    +        " overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val KUBERNETES_ALLOCATION_BATCH_SIZE =
    +    ConfigBuilder("spark.kubernetes.allocation.batch.size")
    +      .doc("Number of pods to launch at once in each round of executor allocation.")
    +      .intConf
    +      .createWithDefault(5)
    +
    +  val KUBERNETES_ALLOCATION_BATCH_DELAY =
    +    ConfigBuilder("spark.kubernetes.allocation.batch.delay")
    +      .doc("Number of seconds to wait between each round of executor allocation.")
    +      .longConf
    +      .createWithDefault(1)
    +
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the" +
    +        " launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
    +  val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    if (!rawMasterString.startsWith("k8s://")) {
    +      throw new IllegalArgumentException("Master URL should start with k8s:// in Kubernetes mode.")
    +    }
    +    val masterWithoutK8sPrefix = rawMasterString.replaceFirst("k8s://", "")
    +    if (masterWithoutK8sPrefix.startsWith("http://")
    +      || masterWithoutK8sPrefix.startsWith("https://")) {
    +      masterWithoutK8sPrefix
    +    } else {
    +      val resolvedURL = s"https://$masterWithoutK8sPrefix"
    +      logInfo("No scheme specified for kubernetes master URL, so defaulting to https. Resolved" +
    +        s" URL is $resolvedURL")
    +      resolvedURL
    --- End diff --
    
    Communication with the Kubernetes API server (aka master) from outside the cluster is secured by default, so we expect `https` is always used.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    @jerryshao Made `Client` implement `SparkApplication` in https://github.com/apache/spark/pull/19717/commits/4164ba575e58aa4e5756d5d82d319c2ed6196d5a.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84120 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84120/testReport)** for PR 19717 at commit [`f38144b`](https://github.com/apache/spark/commit/f38144bcd11a2a261c857d09dc220d11da5ead5f).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by jerryshao <gi...@git.apache.org>.
Github user jerryshao commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153407820
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -702,6 +715,19 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (isKubernetesCluster) {
    +      childMainClass = "org.apache.spark.deploy.k8s.submit.Client"
    --- End diff --
    
    Here the style should be unified after #19631 is merged.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154566501
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -119,5 +139,60 @@ private[spark] object Config extends Logging {
             "must be a positive integer")
           .createWithDefault(10)
     
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  private[spark] val JARS_DOWNLOAD_LOCATION =
    +    ConfigBuilder("spark.kubernetes.mountDependencies.jarsDownloadDir")
    +      .doc("Location to download jars to in the driver and executors. When using" +
    +        " spark-submit, this directory must be empty and will be mounted as an empty directory" +
    +        " volume on the driver and executor pod.")
    +      .stringConf
    +      .createWithDefault("/var/spark-data/spark-jars")
    +
    +  private[spark] val FILES_DOWNLOAD_LOCATION =
    +    ConfigBuilder("spark.kubernetes.mountDependencies.filesDownloadDir")
    +      .doc("Location to download files to in the driver and executors. When using" +
    +        " spark-submit, this directory must be empty and will be mounted as an empty directory" +
    +        " volume on the driver and executor pods.")
    +      .stringConf
    +      .createWithDefault("/var/spark-data/spark-files")
    --- End diff --
    
    See comment above.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jason-dai <gi...@git.apache.org>.
Github user jason-dai commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155710219
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,47 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    --- End diff --
    
    I wonder if there are any follow-ups of this particular issue (apache-spark-on-k8s#326).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84658 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84658/testReport)** for PR 19717 at commit [`7d2b303`](https://github.com/apache/spark/commit/7d2b30373b2e4d8d5311e10c3f9a62a2d900d568).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154565597
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -296,6 +298,12 @@ object SparkSubmit extends CommandLineUtils with Logging {
           case (STANDALONE, CLUSTER) if args.isR =>
             printErrorAndExit("Cluster deploy mode is currently not supported for R " +
               "applications on standalone clusters.")
    +      case (KUBERNETES, CLIENT) =>
    +        printErrorAndExit("Client mode is currently not supported for Kubernetes.")
    +      case (KUBERNETES, _) if args.isPython =>
    +        printErrorAndExit("Python applications are currently not supported for Kubernetes.")
    +      case (KUBERNETES, _) if args.isR =>
    +        printErrorAndExit("R applications are currently not supported for Kubernetes.")
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155047041
  
    --- Diff: core/src/main/scala/org/apache/spark/util/Utils.scala ---
    @@ -2744,6 +2744,25 @@ private[spark] object Utils extends Logging {
         }
       }
     
    +  /**
    +   * Check the validity of the given Kubernetes master URL and return the resolved URL.
    +   */
    +  def checkAndGetK8sMasterUrl(rawMasterURL: String): String = {
    +    require(rawMasterURL.startsWith("k8s://"),
    +      "Kubernetes master URL must start with k8s://.")
    +    val masterWithoutK8sPrefix = rawMasterURL.substring("k8s://".length)
    --- End diff --
    
    OK, did some changes to make all supported formats work. See https://github.com/apache/spark/pull/19717/commits/51844cc60753456cdf8c2d34a660c239a3c8aa13.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by jiangxb1987 <gi...@git.apache.org>.
Github user jiangxb1987 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155305418
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -55,14 +63,26 @@ private[spark] object Config extends Logging {
       val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
     
       val KUBERNETES_SERVICE_ACCOUNT_NAME =
    -    ConfigBuilder(s"$APISERVER_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
           .doc("Service account that is used when running the driver pod. The driver pod uses " +
             "this service account when requesting executor pods from the API server. If specific " +
             "credentials are given for the driver pod to use, the driver will favor " +
             "using those credentials instead.")
           .stringConf
           .createOptional
     
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    --- End diff --
    
    Seems this config is never used?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153249656
  
    --- Diff: resource-managers/kubernetes/core/pom.xml ---
    @@ -0,0 +1,102 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    +  ~ Licensed to the Apache Software Foundation (ASF) under one or more
    +  ~ contributor license agreements.  See the NOTICE file distributed with
    +  ~ this work for additional information regarding copyright ownership.
    +  ~ The ASF licenses this file to You 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
    +  ~
    +  ~    http://www.apache.org/licenses/LICENSE-2.0
    +  ~
    +  ~ Unless required by applicable law or agreed to in writing, software
    +  ~ distributed under the License is distributed on an "AS IS" BASIS,
    +  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +  ~ See the License for the specific language governing permissions and
    +  ~ limitations under the License.
    +  -->
    +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    --- End diff --
    
    We will rebase this PR once #19468 gets merged.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84121 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84121/testReport)** for PR 19717 at commit [`60234a2`](https://github.com/apache/spark/commit/60234a29846955b8a6e8cb6fbb1dc35da3c3b4f2).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds the following public classes _(experimental)_:
      * `    require(mainClass.isDefined, \"Main class must be specified via --main-class\")`


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by foxish <gi...@git.apache.org>.
Github user foxish commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Thanks to all the reviewers, @liyinan926 and @mccheah for the comments & fixes. This looks real close! 
    @ifilonenko - if we could get our integration tests to run once against this PR this week and update it with results, that would give us a lot of confidence. PTAL.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154800754
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,164 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.{SparkConf, SparkException}
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf
    +    .get(DRIVER_DOCKER_IMAGE)
    +    .getOrElse(throw new SparkException("Must specify the driver Docker image"))
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    --- End diff --
    
    Merged both into `spark.driver.memoryOverhead` and used it in both yarn and k8s.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154783790
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,164 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.{SparkConf, SparkException}
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf
    +    .get(DRIVER_DOCKER_IMAGE)
    +    .getOrElse(throw new SparkException("Must specify the driver Docker image"))
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    +    .get(KUBERNETES_DRIVER_MEMORY_OVERHEAD)
    +    .getOrElse(math.max((MEMORY_OVERHEAD_FACTOR * driverMemoryMiB).toInt,
    +      MEMORY_OVERHEAD_MIN_MIB))
    +  private val driverContainerMemoryWithOverheadMiB = driverMemoryMiB + memoryOverheadMiB
    +
    +  override def configureDriver(
    +      driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154770165
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    --- End diff --
    
    Instead of passing the format string as an argument, I'd pass the file type instead and inject that into a hardcoded format string here. (Mostly paranoia from C, where it's fishy to use parameters as format strings.)


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366681
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -702,6 +715,18 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (isKubernetesCluster) {
    +      childMainClass = "org.apache.spark.deploy.k8s.submit.Client"
    +      childArgs ++= Array("--primary-java-resource", args.primaryResource)
    +      childArgs ++= Array("--main-class", args.mainClass)
    +      if (args.childArgs != null) {
    +        args.childArgs.foreach { arg =>
    +          childArgs += "--arg"
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by viirya <gi...@git.apache.org>.
Github user viirya commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154553669
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/executor/Dockerfile ---
    @@ -0,0 +1,31 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM spark-base
    +
    +# If this docker file is being used in the context of building your images from a Spark distribution, the docker build
    +# command should be invoked from the top level directory of the Spark distribution. E.g.:
    +# docker build -t spark-executor:latest -f dockerfiles/executor/Dockerfile .
    +
    +COPY examples /opt/spark/examples
    +
    +CMD SPARK_CLASSPATH="${SPARK_HOME}/jars/*" && \
    +    env | grep SPARK_JAVA_OPT_ | sed 's/[^=]*=\(.*\)/\1/g' > /tmp/java_opts.txt && \
    +    readarray -t SPARK_EXECUTOR_JAVA_OPTS < /tmp/java_opts.txt && \
    +    if ! [ -z ${SPARK_MOUNTED_CLASSPATH}+x} ]; then SPARK_CLASSPATH="$SPARK_MOUNTED_CLASSPATH:$SPARK_CLASSPATH"; fi && \
    +    if ! [ -z ${SPARK_EXECUTOR_EXTRA_CLASSPATH+x} ]; then SPARK_CLASSPATH="$SPARK_EXECUTOR_EXTRA_CLASSPATH:$SPARK_CLASSPATH"; fi && \
    +    ${JAVA_HOME}/bin/java "${SPARK_EXECUTOR_JAVA_OPTS[@]}" -Dspark.executor.port=$SPARK_EXECUTOR_PORT -Xms$SPARK_EXECUTOR_MEMORY -Xmx$SPARK_EXECUTOR_MEMORY -cp "$SPARK_CLASSPATH" org.apache.spark.executor.CoarseGrainedExecutorBackend --driver-url $SPARK_DRIVER_URL --executor-id $SPARK_EXECUTOR_ID --cores $SPARK_EXECUTOR_CORES --app-id $SPARK_APPLICATION_ID --hostname $SPARK_EXECUTOR_POD_IP
    --- End diff --
    
    For `SPARK_EXECUTOR_PORT`, I don't see the corresponding `ENV_EXECUTOR_PORT` has been set in `createExecutorPod`.  Is it missing?
    
    ditto for`SPARK_MOUNTED_CLASSPATH`.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by mccheah <gi...@git.apache.org>.
Github user mccheah commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154814332
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    --- End diff --
    
    In the interest of reducing the complexity of this particular change, I think it's fine to remove the logging pod status watcher for now and introduce it in a follow up commit. We can make a simpler version here. For example, we can have a simpler watcher that checks the pod ends up in the running state before some timeout. We can also explicitly not support waiting for app completion.
    
    Basically I'm curious as to how simple we can make this to minimize the amount of new code we're introducing in the first push. The tradeoff of course is that we're expiicitly limiting functionality we introduce in 2.3.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366699
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,173 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using" +
    +        " spark-submit in cluster mode, this can also be passed to spark-submit via the" +
    +        " --kubernetes-namespace command line argument.")
    +      .stringConf
    +      .createWithDefault("default")
    +
    +  val DRIVER_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.driver.docker.image")
    +      .doc("Docker image to use for the driver. Specify this using the standard Docker tag format.")
    +      .stringConf
    +      .createWithDefault(s"spark-driver:$sparkVersion")
    +
    +  val EXECUTOR_DOCKER_IMAGE =
    +    ConfigBuilder("spark.kubernetes.executor.docker.image")
    +      .doc("Docker image to use for the executors. Specify this using the standard Docker tag" +
    +        " format.")
    +      .stringConf
    +      .createWithDefault(s"spark-executor:$sparkVersion")
    +
    +  val DOCKER_IMAGE_PULL_POLICY =
    +    ConfigBuilder("spark.kubernetes.docker.image.pullPolicy")
    +      .doc("Docker image pull policy when pulling any docker image in Kubernetes integration")
    +      .stringConf
    +      .createWithDefault("IfNotPresent")
    +
    +
    +  val KUBERNETES_AUTH_DRIVER_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver"
    +  val KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX =
    +      "spark.kubernetes.authenticate.driver.mounted"
    +  val OAUTH_TOKEN_CONF_SUFFIX = "oauthToken"
    +  val OAUTH_TOKEN_FILE_CONF_SUFFIX = "oauthTokenFile"
    +  val CLIENT_KEY_FILE_CONF_SUFFIX = "clientKeyFile"
    +  val CLIENT_CERT_FILE_CONF_SUFFIX = "clientCertFile"
    +  val CA_CERT_FILE_CONF_SUFFIX = "caCertFile"
    +
    +  val KUBERNETES_SERVICE_ACCOUNT_NAME =
    +    ConfigBuilder(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.serviceAccountName")
    +      .doc("Service account that is used when running the driver pod. The driver pod uses" +
    +        " this service account when requesting executor pods from the API server. If specific" +
    +        " credentials are given for the driver pod to use, the driver will favor" +
    +        " using those credentials instead.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.driver.limit.cores")
    +      .doc("Specify the hard cpu limit for the driver pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_LIMIT_CORES =
    +    ConfigBuilder("spark.kubernetes.executor.limit.cores")
    +      .doc("Specify the hard cpu limit for a single executor pod")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.driver.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated for the driver and the" +
    +        " driver submission server. This is memory that accounts for things like VM overheads," +
    +        " interned strings, other native overheads, etc. This tends to grow with the driver's" +
    +        " memory size (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  // Note that while we set a default for this when we start up the
    +  // scheduler, the specific default value is dynamically determined
    +  // based on the executor memory.
    +  val KUBERNETES_EXECUTOR_MEMORY_OVERHEAD =
    +    ConfigBuilder("spark.kubernetes.executor.memoryOverhead")
    +      .doc("The amount of off-heap memory (in megabytes) to be allocated per executor. This" +
    +        " is memory that accounts for things like VM overheads, interned strings, other native" +
    +        " overheads, etc. This tends to grow with the executor size. (typically 6-10%).")
    +      .bytesConf(ByteUnit.MiB)
    +      .createOptional
    +
    +  val KUBERNETES_DRIVER_POD_NAME =
    +    ConfigBuilder("spark.kubernetes.driver.pod.name")
    +      .doc("Name of the driver pod.")
    +      .stringConf
    +      .createOptional
    +
    +  val KUBERNETES_EXECUTOR_POD_NAME_PREFIX =
    +    ConfigBuilder("spark.kubernetes.executor.podNamePrefix")
    +      .doc("Prefix to use in front of the executor pod names.")
    +      .internal()
    +      .stringConf
    +      .createWithDefault("spark")
    +
    +  val KUBERNETES_ALLOCATION_BATCH_SIZE =
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84513 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84513/testReport)** for PR 19717 at commit [`0936fbe`](https://github.com/apache/spark/commit/0936fbe5651fdc534b5ccfe608cb4b9c41928397).
     * This patch **fails PySpark unit tests**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155847061
  
    --- Diff: launcher/src/main/java/org/apache/spark/launcher/SparkSubmitOptionParser.java ---
    @@ -114,7 +114,7 @@
         { QUEUE },
         { REPOSITORIES },
         { STATUS },
    -    { TOTAL_EXECUTOR_CORES },
    +    { TOTAL_EXECUTOR_CORES }
    --- End diff --
    
    Don't change this.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366790
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    +        BaseEncoding.base64().encode(Files.toByteArray(file))
    +      }
    +  }
    +
    +  private def resolveSecretLocation(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      mountedCanonicalLocation: String): Option[String] = {
    +    mountedUserSpecified.orElse(valueMountedFromSubmitter.map( _ => {
    +      mountedCanonicalLocation
    +    }))
    +  }
    +
    +  private def resolveSecretData(
    +      mountedUserSpecified: Option[String],
    +      valueMountedFromSubmitter: Option[String],
    +      secretName: String): Map[String, String] = {
    +    mountedUserSpecified.map { _ => Map.empty[String, String]}
    +      .getOrElse {
    +        valueMountedFromSubmitter.map { valueBase64 =>
    +          Map(secretName -> valueBase64)
    +        }.getOrElse(Map.empty[String, String])
    +      }
    +  }
    +
    +  private implicit def augmentSparkConf(sparkConf: SparkConf): OptionSettableSparkConf = {
    +    new OptionSettableSparkConf(sparkConf)
    +  }
    +}
    +
    +private class OptionSettableSparkConf(sparkConf: SparkConf) {
    +  def setOption(configEntry: String, option: Option[String]): SparkConf = {
    +    option.map( opt => {
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-18278] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r153366693
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -0,0 +1,173 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s
    +
    +import java.util.concurrent.TimeUnit
    +
    +import org.apache.spark.{SPARK_VERSION => sparkVersion}
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.internal.config.ConfigBuilder
    +import org.apache.spark.network.util.ByteUnit
    +
    +private[spark] object Config extends Logging {
    +
    +  val KUBERNETES_NAMESPACE =
    +    ConfigBuilder("spark.kubernetes.namespace")
    +      .doc("The namespace that will be used for running the driver and executor pods. When using" +
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84672 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84672/testReport)** for PR 19717 at commit [`caf2206`](https://github.com/apache/spark/commit/caf22060f600b3b382e2e98b7ee5f0aacc165f2d).
     * This patch passes all tests.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84530 has finished](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84530/testReport)** for PR 19717 at commit [`0e8ca01`](https://github.com/apache/spark/commit/0e8ca012dc6d5dfd5645f45b6d1bb84e1e9e72a5).
     * This patch **fails due to an unknown error code, -9**.
     * This patch merges cleanly.
     * This patch adds no public classes.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155896644
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/DriverConfigurationStepsOrchestrator.scala ---
    @@ -0,0 +1,121 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.steps._
    +import org.apache.spark.launcher.SparkLauncher
    +import org.apache.spark.util.SystemClock
    +
    +/**
    + * Constructs the complete list of driver configuration steps to run to deploy the Spark driver.
    + */
    +private[spark] class DriverConfigurationStepsOrchestrator(
    +    namespace: String,
    +    kubernetesAppId: String,
    +    launchTime: Long,
    +    mainAppResource: Option[MainAppResource],
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) {
    +
    +  // The resource name prefix is derived from the application name, making it easy to connect the
    +  // names of the Kubernetes resources from e.g. kubectl or the Kubernetes dashboard to the
    +  // application the user submitted. However, we can't use the application name in the label, as
    +  // label values are considerably restrictive, e.g. must be no longer than 63 characters in
    +  // length. So we generate a separate identifier for the app ID itself, and bookkeeping that
    +  // requires finding "all pods for this application" should use the kubernetesAppId.
    +  private val kubernetesResourceNamePrefix =
    +      s"$appName-$launchTime".toLowerCase.replaceAll("\\.", "-")
    +  private val dockerImagePullPolicy = submissionSparkConf.get(DOCKER_IMAGE_PULL_POLICY)
    +  private val jarsDownloadPath = submissionSparkConf.get(JARS_DOWNLOAD_LOCATION)
    +  private val filesDownloadPath = submissionSparkConf.get(FILES_DOWNLOAD_LOCATION)
    +
    +  def getAllConfigurationSteps(): Seq[DriverConfigurationStep] = {
    +    val driverCustomLabels = ConfigurationUtils.parsePrefixedKeyValuePairs(
    +      submissionSparkConf,
    +      KUBERNETES_DRIVER_LABEL_PREFIX)
    +    require(!driverCustomLabels.contains(SPARK_APP_ID_LABEL), "Label with key " +
    +      s"$SPARK_APP_ID_LABEL is not allowed as it is reserved for Spark bookkeeping " +
    +      "operations.")
    +    require(!driverCustomLabels.contains(SPARK_ROLE_LABEL), "Label with key " +
    +      s"$SPARK_ROLE_LABEL is not allowed as it is reserved for Spark bookkeeping " +
    +      "operations.")
    +
    +    val allDriverLabels = driverCustomLabels ++ Map(
    +      SPARK_APP_ID_LABEL -> kubernetesAppId,
    +      SPARK_ROLE_LABEL -> SPARK_POD_DRIVER_ROLE)
    +
    +    val initialSubmissionStep = new BaseDriverConfigurationStep(
    +      kubernetesAppId,
    +      kubernetesResourceNamePrefix,
    +      allDriverLabels,
    +      dockerImagePullPolicy,
    +      appName,
    +      mainClass,
    +      appArgs,
    +      submissionSparkConf)
    +
    +    val driverAddressStep = new DriverServiceBootstrapStep(
    +      kubernetesResourceNamePrefix,
    +      allDriverLabels,
    +      submissionSparkConf,
    +      new SystemClock)
    +
    +    val kubernetesCredentialsStep = new DriverKubernetesCredentialsStep(
    +      submissionSparkConf, kubernetesResourceNamePrefix)
    +
    +    val additionalMainAppJar = if (mainAppResource.nonEmpty) {
    +       val mayBeResource = mainAppResource.get match {
    +        case JavaMainAppResource(resource) if resource != SparkLauncher.NO_RESOURCE =>
    +          Some(resource)
    +        case _ => Option.empty
    --- End diff --
    
    I don't follow. The code still says `Option.empty` when Matt asked it to be replace with `None`.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155956719
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -294,6 +298,16 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (clusterManager == KUBERNETES) {
    +      args.master = Utils.checkAndGetK8sMasterUrl(args.master)
    +      // Make sure YARN is included in our build if we're trying to use it
    --- End diff --
    
    this should say KUBERNETES


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154502901
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala ---
    @@ -702,6 +715,19 @@ object SparkSubmit extends CommandLineUtils with Logging {
           }
         }
     
    +    if (isKubernetesCluster) {
    +      childMainClass = "org.apache.spark.deploy.k8s.submit.Client"
    --- End diff --
    
    let's wait for that and rebase this PR


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154153331
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/LoggingPodStatusWatcher.scala ---
    @@ -0,0 +1,184 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.concurrent.{CountDownLatch, TimeUnit}
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerStateRunning, ContainerStateTerminated, ContainerStateWaiting, ContainerStatus, Pod, Time}
    +import io.fabric8.kubernetes.client.{KubernetesClientException, Watcher}
    +import io.fabric8.kubernetes.client.Watcher.Action
    +
    +import org.apache.spark.SparkException
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.ThreadUtils
    +
    +private[k8s] trait LoggingPodStatusWatcher extends Watcher[Pod] {
    +  def awaitCompletion(): Unit
    +}
    +
    +/**
    + * A monitor for the running Kubernetes pod of a Spark application. Status logging occurs on
    + * every state change and also at an interval for liveness.
    + *
    + * @param appId application ID.
    + * @param maybeLoggingInterval ms between each state request. If provided, must be a positive
    + *                             number.
    + */
    +private[k8s] class LoggingPodStatusWatcherImpl(
    +      appId: String, maybeLoggingInterval: Option[Long])
    +    extends LoggingPodStatusWatcher with Logging {
    +
    +  private val podCompletedFuture = new CountDownLatch(1)
    +  // start timer for periodic logging
    +  private val scheduler =
    +    ThreadUtils.newDaemonSingleThreadScheduledExecutor("logging-pod-status-watcher")
    +  private val logRunnable: Runnable = new Runnable {
    +    override def run() = logShortStatus()
    +  }
    +
    +  private var pod = Option.empty[Pod]
    +
    +  private def phase: String = pod.map(_.getStatus.getPhase).getOrElse("unknown")
    +
    +  def start(): Unit = {
    +    maybeLoggingInterval.foreach { interval =>
    +      require(interval > 0, s"Logging interval must be a positive time value, got: $interval ms.")
    +      scheduler.scheduleAtFixedRate(logRunnable, 0, interval, TimeUnit.MILLISECONDS)
    +    }
    +  }
    +
    +  override def eventReceived(action: Action, pod: Pod): Unit = {
    +    this.pod = Option(pod)
    +    action match {
    +      case Action.DELETED =>
    +        closeWatch()
    +
    +      case Action.ERROR =>
    +        closeWatch()
    --- End diff --
    
    It's because in both `DELETE` and `ERROR` cases, the application is considered being terminated. In other cases, we need to check the phase of the driver pod to determine if the application terminated. 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154759574
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,164 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.{SparkConf, SparkException}
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    +
    +  private val driverDockerImage = submissionSparkConf
    +    .get(DRIVER_DOCKER_IMAGE)
    +    .getOrElse(throw new SparkException("Must specify the driver Docker image"))
    +
    +  // CPU settings
    +  private val driverCpuCores = submissionSparkConf.getOption("spark.driver.cores").getOrElse("1")
    +  private val driverLimitCores = submissionSparkConf.get(KUBERNETES_DRIVER_LIMIT_CORES)
    +
    +  // Memory settings
    +  private val driverMemoryMiB = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY)
    +  private val driverMemoryString = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_MEMORY.key,
    +    org.apache.spark.internal.config.DRIVER_MEMORY.defaultValueString)
    +  private val memoryOverheadMiB = submissionSparkConf
    --- End diff --
    
    Another config that could use some re-factoring so that YARN and k8s use the same logic.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155314730
  
    --- Diff: core/src/test/scala/org/apache/spark/deploy/SparkSubmitSuite.scala ---
    @@ -388,6 +388,33 @@ class SparkSubmitSuite
         conf.get("spark.ui.enabled") should be ("false")
       }
     
    +  test("handles k8s cluster mode") {
    +    val clArgs = Seq(
    +      "--deploy-mode", "cluster",
    +      "--master", "k8s://host:port",
    +      "--executor-memory", "5g",
    +      "--class", "org.SomeClass",
    +      "--driver-memory", "4g",
    +      "--conf", "spark.kubernetes.namespace=spark",
    +      "--conf", "spark.kubernetes.driver.docker.image=bar",
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r156176301
  
    --- Diff: core/src/test/scala/org/apache/spark/util/UtilsSuite.scala ---
    @@ -1146,6 +1146,27 @@ class UtilsSuite extends SparkFunSuite with ResetSystemProperties with Logging {
         }
       }
     
    +  test("check Kubernetes master URL") {
    +    val k8sMasterURLHttps = Utils.checkAndGetK8sMasterUrl("k8s://https://host:port")
    +    assert(k8sMasterURLHttps == "k8s:https://host:port")
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154503638
  
    --- Diff: resource-managers/kubernetes/docker/src/main/dockerfiles/spark-base/Dockerfile ---
    @@ -0,0 +1,43 @@
    +#
    +# Licensed to the Apache Software Foundation (ASF) under one or more
    +# contributor license agreements.  See the NOTICE file distributed with
    +# this work for additional information regarding copyright ownership.
    +# The ASF licenses this file to You 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
    +#
    +#    http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS,
    +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +# See the License for the specific language governing permissions and
    +# limitations under the License.
    +#
    +
    +FROM openjdk:8-alpine
    +
    +# If this docker file is being used in the context of building your images from a Spark distribution, the docker build
    +# command should be invoked from the top level directory of the Spark distribution. E.g.:
    +# docker build -t spark-base:latest -f dockerfiles/spark-base/Dockerfile .
    +
    +RUN apk upgrade --no-cache && \
    --- End diff --
    
    consider - add `set -ex`


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154502635
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmitArguments.scala ---
    @@ -294,7 +301,9 @@ private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, S
       }
     
       private def validateKillArguments(): Unit = {
    -    if (!master.startsWith("spark://") && !master.startsWith("mesos://")) {
    +    if (!master.startsWith("spark://")
    +      && !master.startsWith("mesos://")
    +      && !master.startsWith("k8s://")) {
           SparkSubmit.printErrorAndExit(
             "Killing submissions is only supported in standalone or Mesos mode!")
    --- End diff --
    
    add `Kubernetes` to the msg


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    With some bug fixes in https://github.com/apache/spark/pull/19717/commits/83d0b9cc29eec2654308344ac8b2208e5e3f08df, I was able to run `SparkPi` on a GKE cluster.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155012873
  
    --- Diff: core/src/main/scala/org/apache/spark/util/Utils.scala ---
    @@ -2744,6 +2744,25 @@ private[spark] object Utils extends Logging {
         }
       }
     
    +  /**
    +   * Check the validity of the given Kubernetes master URL and return the resolved URL.
    +   */
    +  def checkAndGetK8sMasterUrl(rawMasterURL: String): String = {
    +    require(rawMasterURL.startsWith("k8s://"),
    +      "Kubernetes master URL must start with k8s://.")
    +    val masterWithoutK8sPrefix = rawMasterURL.substring("k8s://".length)
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test FAILed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84530/
    Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by foxish <gi...@git.apache.org>.
Github user foxish commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154952728
  
    --- Diff: core/src/main/scala/org/apache/spark/deploy/SparkSubmitArguments.scala ---
    @@ -590,6 +600,11 @@ private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, S
             |                              the node running the Application Master via the Secure
             |                              Distributed Cache, for renewing the login tickets and the
             |                              delegation tokens periodically.
    +        |
    +        | Kubernetes only:
    +        |  --kubernetes-namespace NS   The namespace in the Kubernetes cluster within which the
    --- End diff --
    
    +1, this can be removed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by mridulm <gi...@git.apache.org>.
Github user mridulm commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    @liyinan926 I dont have any other comments. Given that I am not as familiar with this part of the codebase as I would have liked to be, I will defer to others on whether it is ready for merge or not.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test FAILed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154782915
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case e: Throwable =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    +          throw e
    +      }
    +
    +      if (waitForAppCompletion) {
    +        logInfo(s"Waiting for application $appName to finish...")
    +        loggingPodStatusWatcher.awaitCompletion()
    +        logInfo(s"Application $appName finished.")
    +      } else {
    +        logInfo(s"Deployed Spark application $appName into Kubernetes.")
    +      }
    +    }
    +  }
    +
    +  // Add a OwnerReference to the given resources making the driver pod an owner of them so when
    +  // the driver pod is deleted, the resources are garbage collected.
    +  private def addDriverOwnerReference(driverPod: Pod, resources: Seq[HasMetadata]): Unit = {
    +    val driverPodOwnerReference = new OwnerReferenceBuilder()
    +      .withName(driverPod.getMetadata.getName)
    +      .withApiVersion(driverPod.getApiVersion)
    +      .withUid(driverPod.getMetadata.getUid)
    +      .withKind(driverPod.getKind)
    +      .withController(true)
    +      .build()
    +    resources.foreach { resource =>
    +      val originalMetadata = resource.getMetadata
    +      originalMetadata.setOwnerReferences(Collections.singletonList(driverPodOwnerReference))
    +    }
    +  }
    +}
    +
    +/**
    + * Main class and entry point of application submission in KUBERNETES mode.
    + */
    +private[spark] object Client extends SparkApplication {
    +
    +  override def start(args: Array[String], conf: SparkConf): Unit = {
    +    val parsedArguments = ClientArguments.fromCommandLineArgs(args)
    +    run(parsedArguments, conf)
    +  }
    +
    +  private def run(clientArguments: ClientArguments, sparkConf: SparkConf): Unit = {
    +    val namespace = sparkConf.get(KUBERNETES_NAMESPACE)
    +    val kubernetesAppId = s"spark-${UUID.randomUUID().toString.replaceAll("-", "")}"
    +    val launchTime = System.currentTimeMillis()
    +    val waitForAppCompletion = sparkConf.get(WAIT_FOR_APP_COMPLETION)
    +    val appName = sparkConf.getOption("spark.app.name").getOrElse("spark")
    +    // The master URL has been checked for validity already in SparkSubmit.
    +    val master = sparkConf.get("spark.master")
    +    val loggingInterval = Option(sparkConf.get(REPORT_INTERVAL)).filter(_ => waitForAppCompletion)
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test PASSed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/84613/
    Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    > I think we should move the headless service creation into the backend code - anything essential for the backend to run shouldn't depend on the submission client/steps.
    
    I agree service creation should be done by the scheduler backend code so to get rid of dependency on the submission client. 
    
    > Keeping that possible will let us plug Jupyter/spark-shell (running in-cluster for now). Disabling it completely will create an unnecessary dependency on spark-submit which IMO is undesirable. We do want people to be able to programmatically construct a spark context that can point at a k8s cluster in their code I think. kubernetes.default.svc is the in-cluster kube-dns provisioned DNS address that should point to the API server - its availability is a good indicator that we're in a place that can address other pods - so, that can be used to detect when we don't want to let users try and fail client mode.
    
    Yes, I agree that we eventually should allow client mode including use cases that directly create `SparkContext`. But until we have a solid well-tested solution, I think we should disable it for now. We can always revisit this once we have a good solution. Regarding `kubernetes.default.svc`, yes, it's a good indication. But again, the driver service must exist. Unless we change to have the backend create that service, this still won't work. 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84121 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84121/testReport)** for PR 19717 at commit [`60234a2`](https://github.com/apache/spark/commit/60234a29846955b8a6e8cb6fbb1dc35da3c3b4f2).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by SparkQA <gi...@git.apache.org>.
Github user SparkQA commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    **[Test build #84503 has started](https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/84503/testReport)** for PR 19717 at commit [`51844cc`](https://github.com/apache/spark/commit/51844cc60753456cdf8c2d34a660c239a3c8aa13).


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Merged build finished. Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-22646] [Submission] Spark on Kubernetes - basic s...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Updated the title and rebased onto the latest master.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154707001
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala ---
    @@ -119,5 +139,60 @@ private[spark] object Config extends Logging {
             "must be a positive integer")
           .createWithDefault(10)
     
    +  val WAIT_FOR_APP_COMPLETION =
    +    ConfigBuilder("spark.kubernetes.submission.waitAppCompletion")
    +      .doc("In cluster mode, whether to wait for the application to finish before exiting the " +
    +        "launcher process.")
    +      .booleanConf
    +      .createWithDefault(true)
    +
    +  val REPORT_INTERVAL =
    +    ConfigBuilder("spark.kubernetes.report.interval")
    +      .doc("Interval between reports of the current app status in cluster mode.")
    +      .timeConf(TimeUnit.MILLISECONDS)
    +      .createWithDefaultString("1s")
    +
    +  private[spark] val JARS_DOWNLOAD_LOCATION =
    +    ConfigBuilder("spark.kubernetes.mountDependencies.jarsDownloadDir")
    +      .doc("Location to download jars to in the driver and executors. When using" +
    +        " spark-submit, this directory must be empty and will be mounted as an empty directory" +
    +        " volume on the driver and executor pod.")
    +      .stringConf
    +      .createWithDefault("/var/spark-data/spark-jars")
    +
    +  private[spark] val FILES_DOWNLOAD_LOCATION =
    +    ConfigBuilder("spark.kubernetes.mountDependencies.filesDownloadDir")
    +      .doc("Location to download files to in the driver and executors. When using" +
    +        " spark-submit, this directory must be empty and will be mounted as an empty directory" +
    +        " volume on the driver and executor pods.")
    +      .stringConf
    +      .createWithDefault("/var/spark-data/spark-files")
    +
    +  val KUBERNETES_AUTH_SUBMISSION_CONF_PREFIX =
    +    "spark.kubernetes.authenticate.submission"
    +
       val KUBERNETES_NODE_SELECTOR_PREFIX = "spark.kubernetes.node.selector."
    +
    +  val KUBERNETES_DRIVER_LABEL_PREFIX = "spark.kubernetes.driver.label."
    +  val KUBERNETES_DRIVER_ANNOTATION_PREFIX = "spark.kubernetes.driver.annotation."
    +
    +  val KUBERNETES_EXECUTOR_LABEL_PREFIX = "spark.kubernetes.executor.label."
    +  val KUBERNETES_EXECUTOR_ANNOTATION_PREFIX = "spark.kubernetes.executor.annotation."
    +
    +  val KUBERNETES_DRIVER_ENV_KEY = "spark.kubernetes.driverEnv."
    +
    +  def getK8sMasterUrl(rawMasterString: String): String = {
    +    require(rawMasterString.startsWith("k8s://"),
    +      "Master URL should start with k8s:// in Kubernetes mode.")
    +    val masterWithoutK8sPrefix = rawMasterString.substring("k8s://".length)
    +    if (masterWithoutK8sPrefix.startsWith("http://")
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r155853090
  
    --- Diff: core/src/main/scala/org/apache/spark/SparkConf.scala ---
    @@ -668,7 +668,9 @@ private[spark] object SparkConf extends Logging {
         MAX_REMOTE_BLOCK_SIZE_FETCH_TO_MEM.key -> Seq(
           AlternateConfig("spark.reducer.maxReqSizeShuffleToMem", "2.3")),
         LISTENER_BUS_EVENT_QUEUE_CAPACITY.key -> Seq(
    -      AlternateConfig("spark.scheduler.listenerbus.eventqueue.size", "2.3"))
    +      AlternateConfig("spark.scheduler.listenerbus.eventqueue.size", "2.3")),
    +    "spark.driver.memoryOverhead" -> Seq(
    --- End diff --
    
    Yes, we do need. Combined `spark.yarn.executor.memoryOverhead` and `spark.kubernetes.executor.memoryOverhead` into `spark.executor.memoryOverhead`.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by liyinan926 <gi...@git.apache.org>.
Github user liyinan926 commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154802593
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/DriverKubernetesCredentialsStep.scala ---
    @@ -0,0 +1,244 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import java.io.File
    +import java.nio.charset.StandardCharsets
    +
    +import scala.collection.JavaConverters._
    +import scala.language.implicitConversions
    +
    +import com.google.common.io.{BaseEncoding, Files}
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, PodBuilder, Secret, SecretBuilder}
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Mounts Kubernetes credentials into the driver pod. The driver will use such mounted credentials
    + * to request executors.
    + */
    +private[spark] class DriverKubernetesCredentialsStep(
    +    submissionSparkConf: SparkConf,
    +    kubernetesResourceNamePrefix: String) extends DriverConfigurationStep {
    +
    +  private val maybeMountedOAuthTokenFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientKeyFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX")
    +  private val maybeMountedClientCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX")
    +  private val maybeMountedCaCertFile = submissionSparkConf.getOption(
    +      s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX")
    +  private val driverServiceAccount = submissionSparkConf.get(KUBERNETES_SERVICE_ACCOUNT_NAME)
    +
    +  override def configureDriver(driverSpec: KubernetesDriverSpec): KubernetesDriverSpec = {
    +    val driverSparkConf = driverSpec.driverSparkConf.clone()
    +
    +    val oauthTokenBase64 = submissionSparkConf
    +        .getOption(s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$OAUTH_TOKEN_CONF_SUFFIX")
    +        .map { token =>
    +          BaseEncoding.base64().encode(token.getBytes(StandardCharsets.UTF_8))
    +        }
    +    val caCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        "Driver CA cert file provided at %s does not exist or is not a file.")
    +    val clientKeyDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        "Driver client key file provided at %s does not exist or is not a file.")
    +    val clientCertDataBase64 = safeFileConfToBase64(
    +        s"$KUBERNETES_AUTH_DRIVER_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        "Driver client cert file provided at %s does not exist or is not a file.")
    +
    +    val driverSparkConfWithCredentialsLocations = setDriverPodKubernetesCredentialLocations(
    +        driverSparkConf,
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val kubernetesCredentialsSecret = createCredentialsSecret(
    +        oauthTokenBase64,
    +        caCertDataBase64,
    +        clientKeyDataBase64,
    +        clientCertDataBase64)
    +
    +    val driverPodWithMountedKubernetesCredentials = kubernetesCredentialsSecret.map { secret =>
    +      new PodBuilder(driverSpec.driverPod)
    +        .editOrNewSpec()
    +          .addNewVolume()
    +            .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +            .withNewSecret().withSecretName(secret.getMetadata.getName).endSecret()
    +            .endVolume()
    +          .endSpec()
    +        .build()
    +    }.getOrElse(
    +      driverServiceAccount.map { account =>
    +        new PodBuilder(driverSpec.driverPod)
    +          .editOrNewSpec()
    +          .withServiceAccount(account)
    +          .withServiceAccountName(account)
    +          .endSpec()
    +          .build()
    +      }.getOrElse(driverSpec.driverPod)
    +    )
    +
    +    val driverContainerWithMountedSecretVolume = kubernetesCredentialsSecret.map { secret =>
    +      new ContainerBuilder(driverSpec.driverContainer)
    +        .addNewVolumeMount()
    +          .withName(DRIVER_CREDENTIALS_SECRET_VOLUME_NAME)
    +          .withMountPath(DRIVER_CREDENTIALS_SECRETS_BASE_DIR)
    +          .endVolumeMount()
    +        .build()
    +    }.getOrElse(driverSpec.driverContainer)
    +
    +    driverSpec.copy(
    +      driverPod = driverPodWithMountedKubernetesCredentials,
    +      otherKubernetesResources =
    +        driverSpec.otherKubernetesResources ++ kubernetesCredentialsSecret.toSeq,
    +      driverSparkConf = driverSparkConfWithCredentialsLocations,
    +      driverContainer = driverContainerWithMountedSecretVolume)
    +  }
    +
    +  private def createCredentialsSecret(
    +      driverOAuthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): Option[Secret] = {
    +    val allSecretData =
    +      resolveSecretData(
    +        maybeMountedClientKeyFile,
    +        driverClientKeyDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_KEY_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedClientCertFile,
    +        driverClientCertDataBase64,
    +        DRIVER_CREDENTIALS_CLIENT_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedCaCertFile,
    +        driverCaCertDataBase64,
    +        DRIVER_CREDENTIALS_CA_CERT_SECRET_NAME) ++
    +      resolveSecretData(
    +        maybeMountedOAuthTokenFile,
    +        driverOAuthTokenBase64,
    +        DRIVER_CREDENTIALS_OAUTH_TOKEN_SECRET_NAME)
    +
    +    if (allSecretData.isEmpty) {
    +      None
    +    } else {
    +      Some(new SecretBuilder()
    +        .withNewMetadata()
    +          .withName(s"$kubernetesResourceNamePrefix-kubernetes-credentials")
    +          .endMetadata()
    +        .withData(allSecretData.asJava)
    +        .build())
    +    }
    +  }
    +
    +  private def setDriverPodKubernetesCredentialLocations(
    +      driverSparkConf: SparkConf,
    +      driverOauthTokenBase64: Option[String],
    +      driverCaCertDataBase64: Option[String],
    +      driverClientKeyDataBase64: Option[String],
    +      driverClientCertDataBase64: Option[String]): SparkConf = {
    +    val resolvedMountedOAuthTokenFile = resolveSecretLocation(
    +      maybeMountedOAuthTokenFile,
    +      driverOauthTokenBase64,
    +      DRIVER_CREDENTIALS_OAUTH_TOKEN_PATH)
    +    val resolvedMountedClientKeyFile = resolveSecretLocation(
    +      maybeMountedClientKeyFile,
    +      driverClientKeyDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_KEY_PATH)
    +    val resolvedMountedClientCertFile = resolveSecretLocation(
    +      maybeMountedClientCertFile,
    +      driverClientCertDataBase64,
    +      DRIVER_CREDENTIALS_CLIENT_CERT_PATH)
    +    val resolvedMountedCaCertFile = resolveSecretLocation(
    +      maybeMountedCaCertFile,
    +      driverCaCertDataBase64,
    +      DRIVER_CREDENTIALS_CA_CERT_PATH)
    +
    +    val sparkConfWithCredentialLocations = driverSparkConf
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CA_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedCaCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_KEY_FILE_CONF_SUFFIX",
    +        resolvedMountedClientKeyFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$CLIENT_CERT_FILE_CONF_SUFFIX",
    +        resolvedMountedClientCertFile)
    +      .setOption(
    +        s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.$OAUTH_TOKEN_FILE_CONF_SUFFIX",
    +        resolvedMountedOAuthTokenFile)
    +
    +    // Redact all OAuth token values
    +    sparkConfWithCredentialLocations
    +      .getAll
    +      .filter(_._1.endsWith(OAUTH_TOKEN_CONF_SUFFIX)).map(_._1)
    +      .foreach {
    +        sparkConfWithCredentialLocations.set(_, "<present_but_redacted>")
    +      }
    +    sparkConfWithCredentialLocations
    +  }
    +
    +  private def safeFileConfToBase64(
    +      conf: String,
    +      fileNotFoundFormatString: String): Option[String] = {
    +    submissionSparkConf.getOption(conf)
    +      .map(new File(_))
    +      .map { file =>
    +        require(file.isFile, String.format(fileNotFoundFormatString, file.getAbsolutePath))
    --- End diff --
    
    Done.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test PASSed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/83692/
    Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by vanzin <gi...@git.apache.org>.
Github user vanzin commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154759045
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/steps/BaseDriverConfigurationStep.scala ---
    @@ -0,0 +1,164 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit.steps
    +
    +import scala.collection.JavaConverters._
    +
    +import io.fabric8.kubernetes.api.model.{ContainerBuilder, EnvVarBuilder, EnvVarSourceBuilder, PodBuilder, QuantityBuilder}
    +
    +import org.apache.spark.{SparkConf, SparkException}
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.ConfigurationUtils
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.submit.KubernetesDriverSpec
    +
    +/**
    + * Represents the initial setup required for the driver.
    + */
    +private[spark] class BaseDriverConfigurationStep(
    +    kubernetesAppId: String,
    +    kubernetesResourceNamePrefix: String,
    +    driverLabels: Map[String, String],
    +    dockerImagePullPolicy: String,
    +    appName: String,
    +    mainClass: String,
    +    appArgs: Array[String],
    +    submissionSparkConf: SparkConf) extends DriverConfigurationStep {
    +
    +  private val kubernetesDriverPodName = submissionSparkConf.get(KUBERNETES_DRIVER_POD_NAME)
    +    .getOrElse(s"$kubernetesResourceNamePrefix-driver")
    +
    +  private val driverExtraClasspath = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_CLASS_PATH)
    --- End diff --
    
    Why use the full path all over the place instead of importing?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark pull request #19717: [SPARK-22646] [Submission] Spark on Kubernetes - ...

Posted by felixcheung <gi...@git.apache.org>.
Github user felixcheung commented on a diff in the pull request:

    https://github.com/apache/spark/pull/19717#discussion_r154503222
  
    --- Diff: resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/submit/Client.scala ---
    @@ -0,0 +1,234 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You 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
    + *
    + *    http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.spark.deploy.k8s.submit
    +
    +import java.util.{Collections, UUID}
    +
    +import scala.collection.JavaConverters._
    +import scala.collection.mutable
    +import scala.util.control.NonFatal
    +
    +import io.fabric8.kubernetes.api.model._
    +import io.fabric8.kubernetes.client.KubernetesClient
    +
    +import org.apache.spark.SparkConf
    +import org.apache.spark.deploy.SparkApplication
    +import org.apache.spark.deploy.k8s.Config._
    +import org.apache.spark.deploy.k8s.Constants._
    +import org.apache.spark.deploy.k8s.SparkKubernetesClientFactory
    +import org.apache.spark.deploy.k8s.submit.steps.DriverConfigurationStep
    +import org.apache.spark.internal.Logging
    +import org.apache.spark.util.Utils
    +
    +/**
    + * Encapsulates arguments to the submission client.
    + *
    + * @param mainAppResource the main application resource if any
    + * @param mainClass the main class of the application to run
    + * @param driverArgs arguments to the driver
    + */
    +private[spark] case class ClientArguments(
    +     mainAppResource: Option[MainAppResource],
    +     mainClass: String,
    +     driverArgs: Array[String])
    +
    +private[spark] object ClientArguments {
    +
    +  def fromCommandLineArgs(args: Array[String]): ClientArguments = {
    +    var mainAppResource: Option[MainAppResource] = None
    +    var mainClass: Option[String] = None
    +    val driverArgs = mutable.ArrayBuffer.empty[String]
    +
    +    args.sliding(2, 2).toList.foreach {
    +      case Array("--primary-java-resource", primaryJavaResource: String) =>
    +        mainAppResource = Some(JavaMainAppResource(primaryJavaResource))
    +      case Array("--main-class", clazz: String) =>
    +        mainClass = Some(clazz)
    +      case Array("--arg", arg: String) =>
    +        driverArgs += arg
    +      case other =>
    +        val invalid = other.mkString(" ")
    +        throw new RuntimeException(s"Unknown arguments: $invalid")
    +    }
    +
    +    require(mainClass.isDefined, "Main class must be specified via --main-class")
    +
    +    ClientArguments(
    +      mainAppResource,
    +      mainClass.get,
    +      driverArgs.toArray)
    +  }
    +}
    +
    +/**
    + * Submits a Spark application to run on Kubernetes by creating the driver pod and starting a
    + * watcher that monitors and logs the application status. Waits for the application to terminate if
    + * spark.kubernetes.submission.waitAppCompletion is true.
    + *
    + * @param submissionSteps steps that collectively configure the driver
    + * @param submissionSparkConf the submission client Spark configuration
    + * @param kubernetesClient the client to talk to the Kubernetes API server
    + * @param waitForAppCompletion a flag indicating whether the client should wait for the application
    + *                             to complete
    + * @param appName the application name
    + * @param loggingPodStatusWatcher a watcher that monitors and logs the application status
    + */
    +private[spark] class Client(
    +    submissionSteps: Seq[DriverConfigurationStep],
    +    submissionSparkConf: SparkConf,
    +    kubernetesClient: KubernetesClient,
    +    waitForAppCompletion: Boolean,
    +    appName: String,
    +    loggingPodStatusWatcher: LoggingPodStatusWatcher) extends Logging {
    +
    +  private val driverJavaOptions = submissionSparkConf.get(
    +    org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +
    +   /**
    +    * Run command that initializes a DriverSpec that will be updated after each
    +    * DriverConfigurationStep in the sequence that is passed in. The final KubernetesDriverSpec
    +    * will be used to build the Driver Container, Driver Pod, and Kubernetes Resources
    +    */
    +  def run(): Unit = {
    +    var currentDriverSpec = KubernetesDriverSpec.initialSpec(submissionSparkConf)
    +    // submissionSteps contain steps necessary to take, to resolve varying
    +    // client arguments that are passed in, created by orchestrator
    +    for (nextStep <- submissionSteps) {
    +      currentDriverSpec = nextStep.configureDriver(currentDriverSpec)
    +    }
    +
    +    val resolvedDriverJavaOpts = currentDriverSpec
    +      .driverSparkConf
    +      // Remove this as the options are instead extracted and set individually below using
    +      // environment variables with prefix SPARK_JAVA_OPT_.
    +      .remove(org.apache.spark.internal.config.DRIVER_JAVA_OPTIONS)
    +      .getAll
    +      .map {
    +        case (confKey, confValue) => s"-D$confKey=$confValue"
    +      } ++ driverJavaOptions.map(Utils.splitCommandString).getOrElse(Seq.empty)
    +    val driverJavaOptsEnvs: Seq[EnvVar] = resolvedDriverJavaOpts.zipWithIndex.map {
    +      case (option, index) =>
    +        new EnvVarBuilder()
    +          .withName(s"$ENV_JAVA_OPT_PREFIX$index")
    +          .withValue(option)
    +          .build()
    +    }
    +
    +    val resolvedDriverContainer = new ContainerBuilder(currentDriverSpec.driverContainer)
    +      .addAllToEnv(driverJavaOptsEnvs.asJava)
    +      .build()
    +    val resolvedDriverPod = new PodBuilder(currentDriverSpec.driverPod)
    +      .editSpec()
    +        .addToContainers(resolvedDriverContainer)
    +        .endSpec()
    +      .build()
    +
    +    Utils.tryWithResource(
    +        kubernetesClient
    +          .pods()
    +          .withName(resolvedDriverPod.getMetadata.getName)
    +          .watch(loggingPodStatusWatcher)) { _ =>
    +      val createdDriverPod = kubernetesClient.pods().create(resolvedDriverPod)
    +      try {
    +        if (currentDriverSpec.otherKubernetesResources.nonEmpty) {
    +          val otherKubernetesResources = currentDriverSpec.otherKubernetesResources
    +          addDriverOwnerReference(createdDriverPod, otherKubernetesResources)
    +          kubernetesClient.resourceList(otherKubernetesResources: _*).createOrReplace()
    +        }
    +      } catch {
    +        case NonFatal(e) =>
    +          kubernetesClient.pods().delete(createdDriverPod)
    +          throw e
    --- End diff --
    
    also should it delete if it is fatal?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org


[GitHub] spark issue #19717: [SPARK-18278] [Submission] Spark on Kubernetes - basic s...

Posted by AmplabJenkins <gi...@git.apache.org>.
Github user AmplabJenkins commented on the issue:

    https://github.com/apache/spark/pull/19717
  
    Test PASSed.
    Refer to this link for build results (access rights to CI server needed): 
    https://amplab.cs.berkeley.edu/jenkins//job/SparkPullRequestBuilder/83694/
    Test PASSed.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@spark.apache.org
For additional commands, e-mail: reviews-help@spark.apache.org