This is an automated email from the ASF dual-hosted git repository.
jlfsdtc pushed a commit to branch kylin5
in repository https://gitbox.apache.org/repos/asf/kylin.git
The following commit(s) were added to refs/heads/kylin5 by this push:
new 87753ce44c KYLIN-6128 allow job tracking URL reported from job servers
(#2358)
87753ce44c is described below
commit 87753ce44c2569674c8af0102b998cf35927045a
Author: jlfsdtc <[email protected]>
AuthorDate: Wed Nov 11 10:12:48 2026 +0800
KYLIN-6128 allow job tracking URL reported from job servers (#2358)
---
.../org/apache/kylin/rest/util/SparkUIUtil.java | 23 +++++++++++------
.../apache/kylin/rest/util/SparkUIUtilTest.java | 10 +++++---
.../org/apache/kylin/common/KylinConfigBase.java | 28 ++++++++++++---------
.../kylin/rest/controller/JobController.java | 5 +---
.../kylin/rest/controller/JobControllerTest.java | 29 ++++++++++------------
.../apache/kylin/rest/service/JobInfoService.java | 5 ++++
6 files changed, 57 insertions(+), 43 deletions(-)
diff --git
a/src/common-service/src/main/java/org/apache/kylin/rest/util/SparkUIUtil.java
b/src/common-service/src/main/java/org/apache/kylin/rest/util/SparkUIUtil.java
index 36b562dbe1..edbd03c9e5 100644
---
a/src/common-service/src/main/java/org/apache/kylin/rest/util/SparkUIUtil.java
+++
b/src/common-service/src/main/java/org/apache/kylin/rest/util/SparkUIUtil.java
@@ -24,7 +24,6 @@ import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Locale;
import java.util.Objects;
-
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -34,6 +33,9 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.kylin.common.KylinConfig;
import org.apache.kylin.common.exception.KylinRuntimeException;
import org.apache.kylin.common.util.AddressUtil;
+import org.apache.kylin.common.util.HadoopUtil;
+import org.apache.kylin.rest.cluster.ClusterManager;
+import org.apache.kylin.tool.util.HadoopConfExtractor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
@@ -42,7 +44,9 @@ import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.util.UriComponentsBuilder;
+import lombok.experimental.UtilityClass;
+@UtilityClass
public class SparkUIUtil {
private static final HttpComponentsClientHttpRequestFactory factory = new
HttpComponentsClientHttpRequestFactory(
@@ -53,9 +57,6 @@ public class SparkUIUtil {
private static final String SPARK_UI_PROXY_HEADER = "X-Kylin-Proxy-Path";
- private SparkUIUtil() {
- }
-
public static void resendSparkUIRequest(HttpServletRequest servletRequest,
HttpServletResponse servletResponse,
String sparkUiUrl, String uriPath, String proxyLocationBase)
throws IOException {
URI originTarget = validateProxyUrl(sparkUiUrl);
@@ -140,7 +141,7 @@ public class SparkUIUtil {
return target;
}
- public static void validateJobTrackingUrl(String url, String requestHost) {
+ public static void validateJobTrackingUrl(String url, String requestHost,
ClusterManager clusterManager) {
URI target = validateProxyUrl(url);
KylinConfig config = KylinConfig.getInstanceFromEnv();
@@ -149,7 +150,7 @@ public class SparkUIUtil {
return;
}
- String yarnStatusUrl = config.getYarnStatusCheckUrl();
+ String yarnStatusUrl =
HadoopConfExtractor.extractYarnMasterUrl(HadoopUtil.getHadoopConfFromSparkEngine());
if (StringUtils.isNotBlank(yarnStatusUrl)) {
URI allowedYarnTarget = validateProxyUrl(yarnStatusUrl);
if (isSameAuthority(target, allowedYarnTarget)) {
@@ -161,7 +162,15 @@ public class SparkUIUtil {
return;
}
- throw new KylinRuntimeException("Tracking URL host is not allowed: " +
target.getHost());
+ boolean matchJobServer =
clusterManager.getJobServers().stream().map(request ->
request.getHost().split(":")[0])
+ .anyMatch(host -> isSameHost(requestHost, host));
+
+ if (matchJobServer) {
+ return;
+ }
+
+ throw new KylinRuntimeException(String.format(Locale.ROOT,
+ "Tracking URL host is not allowed: %s, requestHost: %s",
target.getHost(), requestHost));
}
private static boolean isSameAuthority(URI first, URI second) {
diff --git
a/src/common-service/src/test/java/org/apache/kylin/rest/util/SparkUIUtilTest.java
b/src/common-service/src/test/java/org/apache/kylin/rest/util/SparkUIUtilTest.java
index fefed0122e..87cb6a61cb 100644
---
a/src/common-service/src/test/java/org/apache/kylin/rest/util/SparkUIUtilTest.java
+++
b/src/common-service/src/test/java/org/apache/kylin/rest/util/SparkUIUtilTest.java
@@ -22,10 +22,12 @@ import static org.junit.Assert.assertEquals;
import org.apache.kylin.common.exception.KylinRuntimeException;
import org.apache.kylin.common.util.NLocalFileMetadataTestCase;
+import org.apache.kylin.rest.cluster.ClusterManager;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
public class SparkUIUtilTest extends NLocalFileMetadataTestCase {
@@ -53,12 +55,12 @@ public class SparkUIUtilTest extends
NLocalFileMetadataTestCase {
@Test
public void testValidateJobTrackingUrl() {
- SparkUIUtil.validateJobTrackingUrl("http://127.0.0.1:4040",
"127.0.0.1");
+ ClusterManager clusterManager = Mockito.mock(ClusterManager.class);
+ SparkUIUtil.validateJobTrackingUrl("http://127.0.0.1:4040",
"127.0.0.1", clusterManager);
Assert.assertThrows(KylinRuntimeException.class,
- () ->
SparkUIUtil.validateJobTrackingUrl("http://10.0.0.1:4040", "127.0.0.1"));
+ () ->
SparkUIUtil.validateJobTrackingUrl("http://10.0.0.1:4040", "127.0.0.1",
clusterManager));
Assert.assertThrows(KylinRuntimeException.class,
- () -> SparkUIUtil.validateJobTrackingUrl("ftp://127.0.0.1",
"127.0.0.1"));
+ () -> SparkUIUtil.validateJobTrackingUrl("ftp://127.0.0.1",
"127.0.0.1", clusterManager));
}
}
-
diff --git
a/src/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
b/src/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
index ddad0607a3..24505d4766 100644
--- a/src/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
+++ b/src/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
@@ -790,7 +790,7 @@ public abstract class KylinConfigBase implements
Serializable {
* check stays meaningful.
*/
private String getDefaultBroadcastToken() {
- return DigestUtils.sha256Hex("kylin.server.broadcast-token:" +
getMetadataUrl());
+ return DigestUtils.sha256Hex("kylin.server.broadcast-token:" +
getMetadataUrlPrefix());
}
public String getServerHttpsKeyAlias() {
@@ -4549,12 +4549,13 @@ public abstract class KylinConfigBase implements
Serializable {
}
public Set<String> getSourceJdbcWhiteListSchemes() {
- String config =
StringUtils.deleteWhitespace(getOptional("kylin.source.jdbc.white-list.schemes",
- "h2,mysql,postgresql,sqlserver,snowflake"));
+ String config = StringUtils.deleteWhitespace(
+ getOptional("kylin.source.jdbc.white-list.schemes",
"h2,mysql,postgresql,sqlserver,snowflake"));
if (StringUtils.isBlank(config)) {
return Collections.emptySet();
}
- return Arrays.stream(config.split(",")).map(value ->
value.toLowerCase(Locale.ROOT)).collect(Collectors.toSet());
+ return Arrays.stream(config.split(",")).map(value ->
value.toLowerCase(Locale.ROOT))
+ .collect(Collectors.toSet());
}
public String getSourceJdbcWhiteListValidatorClassByScheme(String scheme) {
@@ -4571,8 +4572,8 @@ public abstract class KylinConfigBase implements
Serializable {
if (!whiteListSchemes.contains(scheme)) {
return Collections.emptySet();
}
- String config =
StringUtils.deleteWhitespace(getOptional(String.format(Locale.ROOT,
- "kylin.source.jdbc.white-list.%s.url-param-keys", scheme),
""));
+ String config = StringUtils.deleteWhitespace(
+ getOptional(String.format(Locale.ROOT,
"kylin.source.jdbc.white-list.%s.url-param-keys", scheme), ""));
if (StringUtils.isBlank(config)) {
return Collections.emptySet();
}
@@ -4584,8 +4585,9 @@ public abstract class KylinConfigBase implements
Serializable {
if (!whiteListSchemes.contains(scheme)) {
return Collections.emptySet();
}
- String config =
StringUtils.deleteWhitespace(getOptional(String.format(Locale.ROOT,
- "kylin.source.jdbc.white-list.%s.drivers", scheme),
getDefaultSourceJdbcDrivers(scheme)));
+ String config = StringUtils.deleteWhitespace(
+ getOptional(String.format(Locale.ROOT,
"kylin.source.jdbc.white-list.%s.drivers", scheme),
+ getDefaultSourceJdbcDrivers(scheme)));
if (StringUtils.isBlank(config)) {
return Collections.emptySet();
}
@@ -4609,8 +4611,9 @@ public abstract class KylinConfigBase implements
Serializable {
defaultValue = "";
break;
}
- String config =
StringUtils.deleteWhitespace(getOptional(String.format(Locale.ROOT,
- "kylin.source.jdbc.white-list.%s.semicolon-param-keys",
scheme), defaultValue));
+ String config = StringUtils.deleteWhitespace(
+ getOptional(String.format(Locale.ROOT,
"kylin.source.jdbc.white-list.%s.semicolon-param-keys", scheme),
+ defaultValue));
if (StringUtils.isBlank(config)) {
return Collections.emptySet();
}
@@ -4631,8 +4634,9 @@ public abstract class KylinConfigBase implements
Serializable {
defaultValue = "";
break;
}
- String config =
StringUtils.deleteWhitespace(getOptional(String.format(Locale.ROOT,
- "kylin.source.jdbc.white-list.%s.parenthesis-param-keys",
scheme), defaultValue));
+ String config = StringUtils.deleteWhitespace(getOptional(
+ String.format(Locale.ROOT,
"kylin.source.jdbc.white-list.%s.parenthesis-param-keys", scheme),
+ defaultValue));
if (StringUtils.isBlank(config)) {
return Collections.emptySet();
}
diff --git
a/src/data-loading-server/src/main/java/org/apache/kylin/rest/controller/JobController.java
b/src/data-loading-server/src/main/java/org/apache/kylin/rest/controller/JobController.java
index 3234d5a0be..d6aa21aa4d 100644
---
a/src/data-loading-server/src/main/java/org/apache/kylin/rest/controller/JobController.java
+++
b/src/data-loading-server/src/main/java/org/apache/kylin/rest/controller/JobController.java
@@ -30,7 +30,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
-
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -64,7 +63,6 @@ import org.apache.kylin.rest.service.JobInfoService;
import org.apache.kylin.rest.service.JobService;
import org.apache.kylin.rest.service.RouteService;
import org.apache.kylin.rest.util.AclEvaluate;
-import org.apache.kylin.rest.util.SparkUIUtil;
import org.apache.kylin.utils.GlutenCacheUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -83,7 +81,6 @@ import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
-
import io.swagger.annotations.ApiOperation;
import lombok.val;
@@ -325,7 +322,7 @@ public class JobController extends BaseController {
InternalRpcSecurity.requireGlobalAdminOrServiceToken(servletRequest);
checkProjectName(sparkJobUpdateRequest.getProject());
if (StringUtils.isNotBlank(sparkJobUpdateRequest.getYarnAppUrl())) {
-
SparkUIUtil.validateJobTrackingUrl(sparkJobUpdateRequest.getYarnAppUrl(),
+
jobInfoService.validateJobTrackingUrl(sparkJobUpdateRequest.getYarnAppUrl(),
servletRequest.getRemoteAddr());
}
return updateJobInfoWithCheck(sparkJobUpdateRequest.getProject(),
sparkJobUpdateRequest.getJobId(),
diff --git
a/src/data-loading-server/src/test/java/org/apache/kylin/rest/controller/JobControllerTest.java
b/src/data-loading-server/src/test/java/org/apache/kylin/rest/controller/JobControllerTest.java
index ead614d1e9..2b7db50098 100644
---
a/src/data-loading-server/src/test/java/org/apache/kylin/rest/controller/JobControllerTest.java
+++
b/src/data-loading-server/src/test/java/org/apache/kylin/rest/controller/JobControllerTest.java
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
import org.apache.kylin.common.KylinConfig;
import org.apache.kylin.common.exception.KylinException;
+import org.apache.kylin.common.exception.KylinRuntimeException;
import
org.apache.kylin.common.persistence.transaction.BroadcastEventReadyNotifier;
import org.apache.kylin.common.util.JsonUtil;
import org.apache.kylin.common.util.NLocalFileMetadataTestCase;
@@ -342,14 +343,11 @@ public class JobControllerTest extends
NLocalFileMetadataTestCase {
request.setMemory("1024");
request.setQueueName("queue");
Mockito.doNothing().when(jobInfoService).updateSparkJobInfo(request);
- MvcResult result = mockMvc
-
.perform(MockMvcRequestBuilders.put("/api/jobs/spark").contentType(MediaType.APPLICATION_JSON)
- .content(JsonUtil.writeValueAsString(request))
- .with(req -> {
- req.setRemoteAddr("127.0.0.1");
- return req;
- })
-
.accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_JSON)))
+ MvcResult result =
mockMvc.perform(MockMvcRequestBuilders.put("/api/jobs/spark")
+
.contentType(MediaType.APPLICATION_JSON).content(JsonUtil.writeValueAsString(request)).with(req
-> {
+ req.setRemoteAddr("127.0.0.1");
+ return req;
+
}).accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_JSON)))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
Map<String, String> response =
JsonUtil.readValueAsMap(result.getResponse().getContentAsString());
Assert.assertEquals(response.get("code"), KylinException.CODE_SUCCESS);
@@ -367,13 +365,14 @@ public class JobControllerTest extends
NLocalFileMetadataTestCase {
request.setTaskId(job.getId() + "_00_00");
request.setYarnAppUrl("http://10.0.0.1:4040");
+ Mockito.doThrow(new KylinRuntimeException("Tracking URL host is not
allowed")).when(jobInfoService)
+ .validateJobTrackingUrl(request.getYarnAppUrl(), "127.0.0.1");
+
mockMvc.perform(MockMvcRequestBuilders.put("/api/jobs/spark").contentType(MediaType.APPLICATION_JSON)
- .content(JsonUtil.writeValueAsString(request))
- .with(req -> {
+ .content(JsonUtil.writeValueAsString(request)).with(req -> {
req.setRemoteAddr("127.0.0.1");
return req;
- })
- .accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_JSON)))
+
}).accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_JSON)))
.andExpect(MockMvcResultMatchers.status().isInternalServerError());
Mockito.verify(jobInfoService,
Mockito.never()).updateSparkJobInfo(Mockito.any());
@@ -574,8 +573,7 @@ public class JobControllerTest extends
NLocalFileMetadataTestCase {
MvcResult result = mockMvc
.perform(MockMvcRequestBuilders.put("/api/jobs/error").contentType(MediaType.APPLICATION_JSON)
-
.header(BroadcastEventReadyNotifier.BROADCAST_TOKEN_HEADER,
- getTestConfig().getBroadcastToken())
+
.header(BroadcastEventReadyNotifier.BROADCAST_TOKEN_HEADER,
getTestConfig().getBroadcastToken())
.content(JsonUtil.writeValueAsString(request))
.accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_JSON)))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
@@ -686,8 +684,7 @@ public class JobControllerTest extends
NLocalFileMetadataTestCase {
@Test
public void routeGlutenCache() throws Exception {
val servletRequest = new MockHttpServletRequest();
- val request = new LoadGlutenCacheRequest("default",
- Lists.newArrayList("CACHE DATA SELECT * FROM 'test-path'"));
+ val request = new LoadGlutenCacheRequest("default",
Lists.newArrayList("CACHE DATA SELECT * FROM 'test-path'"));
Mockito.when(routeService.routeGlutenCache(request.getCacheCommands(),
servletRequest)).thenReturn(true);
val mvcResult =
mockMvc.perform(MockMvcRequestBuilders.post("/api/jobs/gluten_cache") //
.contentType(MediaType.APPLICATION_JSON).content(JsonUtil.writeValueAsString(request)))
diff --git
a/src/data-loading-service/src/main/java/org/apache/kylin/rest/service/JobInfoService.java
b/src/data-loading-service/src/main/java/org/apache/kylin/rest/service/JobInfoService.java
index 32953f9bf4..68efbb2d1e 100644
---
a/src/data-loading-service/src/main/java/org/apache/kylin/rest/service/JobInfoService.java
+++
b/src/data-loading-service/src/main/java/org/apache/kylin/rest/service/JobInfoService.java
@@ -110,6 +110,7 @@ import org.apache.kylin.rest.util.AclEvaluate;
import org.apache.kylin.rest.util.JobDriverUIUtil;
import org.apache.kylin.rest.util.JobFilterUtil;
import org.apache.kylin.rest.util.SparkHistoryUIUtil;
+import org.apache.kylin.rest.util.SparkUIUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -560,6 +561,10 @@ public class JobInfoService extends BasicService
implements JobSupporter {
return ExecutableState.valueOf(status);
}
+ public void validateJobTrackingUrl(String url, String requestHost) {
+ SparkUIUtil.validateJobTrackingUrl(url, requestHost, clusterManager);
+ }
+
/**
* update the spark job info, such as yarnAppId, yarnAppUrl.
*