You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by do...@apache.org on 2024/10/18 15:13:40 UTC

(spark) branch branch-3.5 updated: [SPARK-50021][CORE][UI] Fix `ApplicationPage` to hide App UI links when UI is disabled

This is an automated email from the ASF dual-hosted git repository.

dongjoon pushed a commit to branch branch-3.5
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-3.5 by this push:
     new 3a56f8d59753 [SPARK-50021][CORE][UI] Fix `ApplicationPage` to hide App UI links when UI is disabled
3a56f8d59753 is described below

commit 3a56f8d5975379db4b5886ba1f916cebaaa318c2
Author: Dongjoon Hyun <do...@apache.org>
AuthorDate: Fri Oct 18 15:09:38 2024 +0200

    [SPARK-50021][CORE][UI] Fix `ApplicationPage` to hide App UI links when UI is disabled
    
    This PR aims to fix `ApplicationPage` to hide UI link when UI is disabled.
    
    Previously, Spark throws `HTTP ERROR 500 java.lang.IllegalArgumentException: Invalid URI host: null (authority: null)` like the following
    
    **1. PREPARATION**
    ```
    $ cat conf/spark-defaults.conf
    spark.ui.reverseProxy true
    spark.ui.reverseProxyUrl http://localhost:8080
    
    $ sbin/start-master.sh
    
    $ sbin/start-worker.sh spark://$(hostname):7077
    
    $ bin/spark-shell --master spark://$(hostname):7077 -c spark.ui.enabled=false
    ```
    
    **2. BEFORE**
    <img width="496" alt="Screenshot 2024-10-17 at 21 24 32" src="https://github.com/user-attachments/assets/9884790c-a294-4e61-b630-7758c5532afc">
    
    <img width="1002" alt="Screenshot 2024-10-17 at 21 24 51" src="https://github.com/user-attachments/assets/f1e3a121-37ba-4525-a433-21ad15402edf">
    
    **3. AFTER**
    <img width="493" alt="Screenshot 2024-10-17 at 21 22 26" src="https://github.com/user-attachments/assets/7a1ef578-3d9f-495e-9545-6edd26b4d565">
    
    Yes, but previously it was a broken link.
    
    Pass the CIs with the newly added test case.
    
    No.
    
    Closes #48534 from dongjoon-hyun/SPARK-50021.
    
    Authored-by: Dongjoon Hyun <do...@apache.org>
    Signed-off-by: Max Gekk <ma...@gmail.com>
    (cherry picked from commit ff47dd9516e83f23d8ab3731286ef18ddfdaba62)
    Signed-off-by: Dongjoon Hyun <do...@apache.org>
---
 .../spark/deploy/master/ui/ApplicationPage.scala   | 12 ++--
 .../deploy/master/ui/ApplicationPageSuite.scala    | 73 ++++++++++++++++++++++
 2 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/core/src/main/scala/org/apache/spark/deploy/master/ui/ApplicationPage.scala b/core/src/main/scala/org/apache/spark/deploy/master/ui/ApplicationPage.scala
index 9e10a0bbf396..fd7e730b63d1 100644
--- a/core/src/main/scala/org/apache/spark/deploy/master/ui/ApplicationPage.scala
+++ b/core/src/main/scala/org/apache/spark/deploy/master/ui/ApplicationPage.scala
@@ -93,10 +93,14 @@ private[ui] class ApplicationPage(parent: MasterWebUI) extends WebUIPage("app")
             <li><strong>State:</strong> {app.state}</li>
             {
               if (!app.isFinished) {
-                <li><strong>
-                    <a href={UIUtils.makeHref(parent.master.reverseProxy,
-                      app.id, app.desc.appUiUrl)}>Application Detail UI</a>
-                </strong></li>
+                if (app.desc.appUiUrl.isBlank()) {
+                  <li><strong>Application UI:</strong> Disabled</li>
+                } else {
+                  <li><strong>
+                      <a href={UIUtils.makeHref(parent.master.reverseProxy,
+                        app.id, app.desc.appUiUrl)}>Application Detail UI</a>
+                  </strong></li>
+                }
               }
             }
           </ul>
diff --git a/core/src/test/scala/org/apache/spark/deploy/master/ui/ApplicationPageSuite.scala b/core/src/test/scala/org/apache/spark/deploy/master/ui/ApplicationPageSuite.scala
new file mode 100644
index 000000000000..e1edef8f4155
--- /dev/null
+++ b/core/src/test/scala/org/apache/spark/deploy/master/ui/ApplicationPageSuite.scala
@@ -0,0 +1,73 @@
+/*
+ * 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.master.ui
+
+import java.util.Date
+import javax.servlet.http.HttpServletRequest
+
+import org.mockito.Mockito.{mock, when}
+
+import org.apache.spark.SparkFunSuite
+import org.apache.spark.deploy.ApplicationDescription
+import org.apache.spark.deploy.DeployMessages.{MasterStateResponse, RequestMasterState}
+import org.apache.spark.deploy.master.{ApplicationInfo, ApplicationState, Master}
+import org.apache.spark.resource.ResourceProfile
+import org.apache.spark.rpc.RpcEndpointRef
+
+class ApplicationPageSuite extends SparkFunSuite {
+
+  private val master = mock(classOf[Master])
+
+  private val rp = new ResourceProfile(Map.empty, Map.empty)
+  private val desc = ApplicationDescription("name", Some(4), null, "appUiUrl", rp)
+  private val descWithoutUI = ApplicationDescription("name", Some(4), null, "", rp)
+  private val appFinished = new ApplicationInfo(0, "app-finished", desc, new Date, null, 1)
+  appFinished.markFinished(ApplicationState.FINISHED)
+  private val appLive = new ApplicationInfo(0, "app-live", desc, new Date, null, 1)
+  private val appLiveWithoutUI =
+    new ApplicationInfo(0, "app-live-without-ui", descWithoutUI, new Date, null, 1)
+
+  private val state = mock(classOf[MasterStateResponse])
+  when(state.completedApps).thenReturn(Array(appFinished))
+  when(state.activeApps).thenReturn(Array(appLive, appLiveWithoutUI))
+
+  private val rpc = mock(classOf[RpcEndpointRef])
+  when(rpc.askSync[MasterStateResponse](RequestMasterState)).thenReturn(state)
+
+  private val masterWebUI = mock(classOf[MasterWebUI])
+  when(masterWebUI.master).thenReturn(master)
+  when(masterWebUI.masterEndpointRef).thenReturn(rpc)
+
+  test("SPARK-45774: Application Detail UI") {
+    val request = mock(classOf[HttpServletRequest])
+    when(request.getParameter("appId")).thenReturn("app-live")
+
+    val result = new ApplicationPage(masterWebUI).render(request).toString()
+    assert(result.contains("Application Detail UI"))
+    assert(!result.contains("Application History UI"))
+  }
+
+  test("SPARK-50021: Application Detail UI is empty when spark.ui.enabled=false") {
+    val request = mock(classOf[HttpServletRequest])
+    when(request.getParameter("appId")).thenReturn("app-live-without-ui")
+
+    val result = new ApplicationPage(masterWebUI).render(request).toString()
+    assert(result.contains("Application UI:</strong> Disabled"))
+    assert(!result.contains("Application History UI"))
+  }
+}


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