Browse Source

[Feature-10629][Improvement] Support multi cluster environments - namespace use cluster (#10650)

* server code

* namespace ui

* fix dao test

* fix test

* fix

* docs and image update

Co-authored-by: qianl4 <qianl4@cicso.com>
3.1.0-release
qianli2022 2 years ago committed by GitHub
parent
commit
1e48bbc5c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      docs/docs/en/guide/security.md
  2. 2
      docs/docs/zh/guide/security.md
  3. BIN
      docs/img/new_ui/dev/security/create-namespace.png
  4. 64
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceController.java
  5. 1
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java
  6. 29
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/k8s/K8sClientService.java
  7. 90
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/k8s/K8sManager.java
  8. 15
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/K8sNamespaceService.java
  9. 29
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ClusterServiceImpl.java
  10. 102
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/K8SNamespaceServiceImpl.java
  11. 2
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ClusterControllerTest.java
  12. 14
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceControllerTest.java
  13. 34
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/k8s/K8sManagerTest.java
  14. 5
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ClusterServiceTest.java
  15. 55
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/K8SNamespaceServiceTest.java
  16. 4
      dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/Constants.java
  17. 49
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespace.java
  18. 16
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.java
  19. 28
      dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.xml
  20. 21
      dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_h2.sql
  21. 6
      dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_mysql.sql
  22. 7
      dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_postgresql.sql
  23. 6
      dolphinscheduler-dao/src/main/resources/sql/upgrade/3.0.0_schema/mysql/dolphinscheduler_ddl.sql
  24. 6
      dolphinscheduler-dao/src/main/resources/sql/upgrade/3.0.0_schema/postgresql/dolphinscheduler_ddl.sql
  25. 6
      dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapperTest.java
  26. 2
      dolphinscheduler-ui/src/locales/en_US/security.ts
  27. 2
      dolphinscheduler-ui/src/locales/zh_CN/security.ts
  28. 4
      dolphinscheduler-ui/src/service/modules/k8s-namespace/types.ts
  29. 215
      dolphinscheduler-ui/src/views/security/k8s-namespace-manage/components/k8s-namespace-modal.tsx
  30. 56
      dolphinscheduler-ui/src/views/security/k8s-namespace-manage/components/use-modal.ts
  31. 147
      dolphinscheduler-ui/src/views/security/k8s-namespace-manage/use-table.ts

2
docs/docs/en/guide/security.md

@ -162,7 +162,7 @@ worker.groups=default,test
> Add or update k8s cluster
- First enter the configuration of the k8s cluster connection into the table `t_ds_k8s` in the database, which will be configured later by the web page.
- First enter the configuration of the k8s cluster connection into the table `t_ds_k8s` in the database for batch job and will removed later, the creation of the namespace now selects the cluster by drop-down options.
> Add or update namespace

2
docs/docs/zh/guide/security.md

@ -165,7 +165,7 @@ worker.groups=default,test
> 创建/更新 k8s集群
- 先把k8s集群连接的配置录入 database 的表 `t_ds_k8s`,后续会通过页面配置.
- 先把k8s集群连接的配置录入 database 的表 `t_ds_k8s`给批次使用后续移除,namespace的创建现在通过下拉选择集群.
> 创建/更新 namespace

BIN
docs/img/new_ui/dev/security/create-namespace.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 700 KiB

After

Width:  |  Height:  |  Size: 753 KiB

64
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceController.java

@ -20,7 +20,7 @@ package org.apache.dolphinscheduler.api.controller;
import static org.apache.dolphinscheduler.api.enums.Status.CREATE_K8S_NAMESPACE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.DELETE_K8S_NAMESPACE_BY_ID_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_AUTHORIZED_NAMESPACE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_CAN_USE_K8S_CLUSTER_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_CAN_USE_K8S_NAMESPACE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_K8S_NAMESPACE_LIST_PAGING_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_UNAUTHORIZED_NAMESPACE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_K8S_NAMESPACE_ERROR;
@ -78,18 +78,18 @@ public class K8sNamespaceController extends BaseController {
*/
@ApiOperation(value = "queryNamespaceListPaging", notes = "QUERY_NAMESPACE_LIST_PAGING_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType = "String"),
@ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", required = true, dataType = "Int", example = "10"),
@ApiImplicitParam(name = "pageNo", value = "PAGE_NO", required = true, dataType = "Int", example = "1")
@ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType = "String"),
@ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", required = true, dataType = "Int", example = "10"),
@ApiImplicitParam(name = "pageNo", value = "PAGE_NO", required = true, dataType = "Int", example = "1")
})
@GetMapping()
@ResponseStatus(HttpStatus.OK)
@ApiException(QUERY_K8S_NAMESPACE_LIST_PAGING_ERROR)
@AccessLogAnnotation(ignoreRequestArgs = "loginUser")
public Result queryProjectListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "searchVal", required = false) String searchVal,
@RequestParam("pageSize") Integer pageSize,
@RequestParam("pageNo") Integer pageNo
public Result queryNamespaceListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "searchVal", required = false) String searchVal,
@RequestParam("pageSize") Integer pageSize,
@RequestParam("pageNo") Integer pageNo
) {
Result result = checkPageParams(pageNo, pageSize);
@ -107,17 +107,17 @@ public class K8sNamespaceController extends BaseController {
*
* @param loginUser
* @param namespace k8s namespace
* @param k8s k8s name
* @param clusterCode clusterCode
* @param limitsCpu max cpu
* @param limitsMemory max memory
* @return
*/
@ApiOperation(value = "createK8sNamespace", notes = "CREATE_NAMESPACE_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "namespace", value = "NAMESPACE", required = true, dataType = "String"),
@ApiImplicitParam(name = "k8s", value = "K8S", required = true, dataType = "String"),
@ApiImplicitParam(name = "limits_cpu", value = "LIMITS_CPU", required = false, dataType = "Double"),
@ApiImplicitParam(name = "limits_memory", value = "LIMITS_MEMORY", required = false, dataType = "Integer")
@ApiImplicitParam(name = "namespace", value = "NAMESPACE", required = true, dataType = "String"),
@ApiImplicitParam(name = "clusterCode", value = "CLUSTER_CODE", required = true, dataType = "Long"),
@ApiImplicitParam(name = "limits_cpu", value = "LIMITS_CPU", required = false, dataType = "Double"),
@ApiImplicitParam(name = "limits_memory", value = "LIMITS_MEMORY", required = false, dataType = "Integer")
})
@PostMapping()
@ResponseStatus(HttpStatus.CREATED)
@ -125,11 +125,11 @@ public class K8sNamespaceController extends BaseController {
@AccessLogAnnotation(ignoreRequestArgs = "loginUser")
public Result createNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "namespace") String namespace,
@RequestParam(value = "k8s") String k8s,
@RequestParam(value = "clusterCode") Long clusterCode,
@RequestParam(value = "limitsCpu", required = false) Double limitsCpu,
@RequestParam(value = "limitsMemory", required = false) Integer limitsMemory
) {
Map<String, Object> result = k8sNamespaceService.createK8sNamespace(loginUser, namespace, k8s, limitsCpu, limitsMemory);
Map<String, Object> result = k8sNamespaceService.createK8sNamespace(loginUser, namespace, clusterCode, limitsCpu, limitsMemory);
return returnDataList(result);
}
@ -137,17 +137,17 @@ public class K8sNamespaceController extends BaseController {
* update namespace,namespace and k8s not allowed update, because may create on k8s,can delete and create new instead
*
* @param loginUser
* @param userName owner
* @param userName owner
* @param limitsCpu max cpu
* @param limitsMemory max memory
* @return
*/
@ApiOperation(value = "updateK8sNamespace", notes = "UPDATE_NAMESPACE_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "K8S_NAMESPACE_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "userName", value = "OWNER", required = false, dataType = "String"),
@ApiImplicitParam(name = "limitsCpu", value = "LIMITS_CPU", required = false, dataType = "Double"),
@ApiImplicitParam(name = "limitsMemory", value = "LIMITS_MEMORY", required = false, dataType = "Integer")})
@ApiImplicitParam(name = "id", value = "K8S_NAMESPACE_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "userName", value = "OWNER", required = false, dataType = "String"),
@ApiImplicitParam(name = "limitsCpu", value = "LIMITS_CPU", required = false, dataType = "Double"),
@ApiImplicitParam(name = "limitsMemory", value = "LIMITS_MEMORY", required = false, dataType = "Integer")})
@PutMapping(value = "/{id}")
@ResponseStatus(HttpStatus.CREATED)
@ApiException(UPDATE_K8S_NAMESPACE_ERROR)
@ -165,15 +165,15 @@ public class K8sNamespaceController extends BaseController {
/**
* verify namespace and k8s,one k8s namespace is unique
*
* @param loginUser login user
* @param namespace namespace
* @param k8s k8s
* @param loginUser login user
* @param namespace namespace
* @param clusterCode cluster code
* @return true if the k8s and namespace not exists, otherwise return false
*/
@ApiOperation(value = "verifyNamespaceK8s", notes = "VERIFY_NAMESPACE_K8S_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "namespace", value = "NAMESPACE", required = true, dataType = "String"),
@ApiImplicitParam(name = "k8s", value = "K8S", required = true, dataType = "String")
@ApiImplicitParam(name = "namespace", value = "NAMESPACE", required = true, dataType = "String"),
@ApiImplicitParam(name = "clusterCode", value = "CLUSTER_CODE", required = true, dataType = "Long"),
})
@PostMapping(value = "/verify")
@ResponseStatus(HttpStatus.OK)
@ -181,10 +181,10 @@ public class K8sNamespaceController extends BaseController {
@AccessLogAnnotation(ignoreRequestArgs = "loginUser")
public Result verifyNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "namespace") String namespace,
@RequestParam(value = "k8s") String k8s
@RequestParam(value = "clusterCode") Long clusterCode
) {
return k8sNamespaceService.verifyNamespaceK8s(namespace, k8s);
return k8sNamespaceService.verifyNamespaceK8s(namespace, clusterCode);
}
@ -197,7 +197,7 @@ public class K8sNamespaceController extends BaseController {
*/
@ApiOperation(value = "delNamespaceById", notes = "DELETE_NAMESPACE_BY_ID_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "NAMESPACE_ID", required = true, dataType = "Int", example = "100")
@ApiImplicitParam(name = "id", value = "NAMESPACE_ID", required = true, dataType = "Int", example = "100")
})
@PostMapping(value = "/delete")
@ResponseStatus(HttpStatus.OK)
@ -213,7 +213,7 @@ public class K8sNamespaceController extends BaseController {
* query unauthorized namespace
*
* @param loginUser login user
* @param userId user id
* @param userId user id
* @return the namespaces which user have not permission to see
*/
@ApiOperation(value = "queryUnauthorizedNamespace", notes = "QUERY_UNAUTHORIZED_NAMESPACE_NOTES")
@ -234,7 +234,7 @@ public class K8sNamespaceController extends BaseController {
* query unauthorized namespace
*
* @param loginUser login user
* @param userId user id
* @param userId user id
* @return namespaces which the user have permission to see
*/
@ApiOperation(value = "queryAuthorizedNamespace", notes = "QUERY_AUTHORIZED_NAMESPACE_NOTES")
@ -260,10 +260,10 @@ public class K8sNamespaceController extends BaseController {
@ApiOperation(value = "queryAvailableNamespaceList", notes = "QUERY_AVAILABLE_NAMESPACE_LIST_NOTES")
@GetMapping(value = "/available-list")
@ResponseStatus(HttpStatus.OK)
@ApiException(QUERY_CAN_USE_K8S_CLUSTER_ERROR)
@ApiException(QUERY_CAN_USE_K8S_NAMESPACE_ERROR)
@AccessLogAnnotation(ignoreRequestArgs = "loginUser")
public Result queryAvailableNamespaceList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
List<K8sNamespace> result = k8sNamespaceService.queryNamespaceAvailable(loginUser);
return success(result);
}
}
}

1
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java

@ -423,6 +423,7 @@ public enum Status {
RESOURCE_FULL_NAME_TOO_LONG_ERROR(1300015, "resource's fullname is too long error", "资源文件名过长"),
TENANT_FULL_NAME_TOO_LONG_ERROR(1300016, "tenant's fullname is too long error", "租户名过长"),
USER_PASSWORD_LENGTH_ERROR(1300017, "user's password length error", "用户密码长度错误"),
QUERY_CAN_USE_K8S_NAMESPACE_ERROR(1300018, "login user query can used namespace list error", "查询可用命名空间错误"),
NO_CURRENT_OPERATING_PERMISSION(1400001, "The current user does not have this permission.", "当前用户无此权限"),
FUNCTION_DISABLED(1400002, "The current feature is disabled.", "当前功能已被禁用"),

29
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/k8s/K8sClientService.java

@ -18,6 +18,7 @@
package org.apache.dolphinscheduler.api.k8s;
import org.apache.dolphinscheduler.dao.entity.K8sNamespace;
import org.apache.dolphinscheduler.remote.exceptions.RemotingException;
import java.util.Optional;
@ -41,15 +42,15 @@ public class K8sClientService {
@Autowired
private K8sManager k8sManager;
public ResourceQuota upsertNamespaceAndResourceToK8s(K8sNamespace k8sNamespace, String yamlStr) {
upsertNamespaceToK8s(k8sNamespace.getNamespace(), k8sNamespace.getK8s());
public ResourceQuota upsertNamespaceAndResourceToK8s(K8sNamespace k8sNamespace, String yamlStr) throws RemotingException {
upsertNamespaceToK8s(k8sNamespace.getNamespace(), k8sNamespace.getClusterCode());
return upsertNamespacedResourceToK8s(k8sNamespace, yamlStr);
}
public Optional<Namespace> deleteNamespaceToK8s(String name, String k8s) {
Optional<Namespace> result = getNamespaceFromK8s(name, k8s);
public Optional<Namespace> deleteNamespaceToK8s(String name, Long clusterCode) throws RemotingException {
Optional<Namespace> result = getNamespaceFromK8s(name, clusterCode);
if (result.isPresent()) {
KubernetesClient client = k8sManager.getK8sClient(k8s);
KubernetesClient client = k8sManager.getK8sClient(clusterCode);
Namespace body = new Namespace();
ObjectMeta meta = new ObjectMeta();
meta.setNamespace(name);
@ -57,12 +58,12 @@ public class K8sClientService {
body.setMetadata(meta);
client.namespaces().delete(body);
}
return getNamespaceFromK8s(name, k8s);
return getNamespaceFromK8s(name, clusterCode);
}
private ResourceQuota upsertNamespacedResourceToK8s(K8sNamespace k8sNamespace, String yamlStr) {
private ResourceQuota upsertNamespacedResourceToK8s(K8sNamespace k8sNamespace, String yamlStr) throws RemotingException {
KubernetesClient client = k8sManager.getK8sClient(k8sNamespace.getK8s());
KubernetesClient client = k8sManager.getK8sClient(k8sNamespace.getClusterCode());
//创建资源
ResourceQuota queryExist = client.resourceQuotas()
@ -86,9 +87,9 @@ public class K8sClientService {
.createOrReplace(body);
}
private Optional<Namespace> getNamespaceFromK8s(String name, String k8s) {
private Optional<Namespace> getNamespaceFromK8s(String name, Long clusterCode) throws RemotingException {
NamespaceList listNamespace =
k8sManager.getK8sClient(k8s).namespaces().list();
k8sManager.getK8sClient(clusterCode).namespaces().list();
Optional<Namespace> list =
listNamespace.getItems().stream()
@ -99,11 +100,11 @@ public class K8sClientService {
return list;
}
private Namespace upsertNamespaceToK8s(String name, String k8s) {
Optional<Namespace> result = getNamespaceFromK8s(name, k8s);
private Namespace upsertNamespaceToK8s(String name, Long clusterCode) throws RemotingException {
Optional<Namespace> result = getNamespaceFromK8s(name, clusterCode);
//if not exist create
if (!result.isPresent()) {
KubernetesClient client = k8sManager.getK8sClient(k8s);
KubernetesClient client = k8sManager.getK8sClient(clusterCode);
Namespace body = new Namespace();
ObjectMeta meta = new ObjectMeta();
meta.setNamespace(name);
@ -114,4 +115,4 @@ public class K8sClientService {
return result.get();
}
}
}

90
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/k8s/K8sManager.java

@ -17,29 +17,25 @@
package org.apache.dolphinscheduler.api.k8s;
import org.apache.dolphinscheduler.dao.entity.K8s;
import org.apache.dolphinscheduler.dao.mapper.K8sMapper;
import org.apache.dolphinscheduler.common.utils.ClusterConfUtils;
import org.apache.dolphinscheduler.dao.entity.Cluster;
import org.apache.dolphinscheduler.dao.mapper.ClusterMapper;
import org.apache.dolphinscheduler.remote.exceptions.RemotingException;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
/**
* A separate class, because then wait for multiple environment feature, currently using db configuration, later unified
* use multiple environment feature
*/
@Component
public class K8sManager {
@ -48,27 +44,76 @@ public class K8sManager {
/**
* cache k8s client
*/
private static Map<String, KubernetesClient> clientMap = new Hashtable<>();
private static Map<Long, KubernetesClient> clientMap = new Hashtable<>();
@Autowired
private K8sMapper k8sMapper;
private ClusterMapper clusterMapper;
public KubernetesClient getK8sClient(String k8sName) {
if (null == k8sName) {
/**
* get k8s client for api use
*
* @param clusterCode
* @return
*/
public synchronized KubernetesClient getK8sClient(Long clusterCode) throws RemotingException {
if (null == clusterCode) {
return null;
}
return clientMap.get(k8sName);
return getAndUpdateK8sClient(clusterCode, false);
}
/**
* @param clusterCode
* @return new client if need updated
*/
public synchronized KubernetesClient getAndUpdateK8sClient(Long clusterCode, boolean update) throws RemotingException {
if (null == clusterCode) {
return null;
}
if (update) {
deleteK8sClientInner(clusterCode);
}
if (clientMap.containsKey(clusterCode)) {
return clientMap.get(clusterCode);
} else {
createK8sClientInner(clusterCode);
}
return clientMap.get(clusterCode);
}
@EventListener
public void buildApiClientAll(ApplicationReadyEvent readyEvent) throws RemotingException {
QueryWrapper<K8s> nodeWrapper = new QueryWrapper<>();
List<K8s> k8sList = k8sMapper.selectList(nodeWrapper);
if (k8sList != null) {
for (K8s k8s : k8sList) {
DefaultKubernetesClient client = getClient(k8s.getK8sConfig());
clientMap.put(k8s.getK8sName(), client);
private void deleteK8sClientInner(Long clusterCode) {
if (clusterCode == null) {
return;
}
Cluster cluster = clusterMapper.queryByClusterCode(clusterCode);
if (cluster == null) {
return;
}
KubernetesClient client = clientMap.get(clusterCode);
if (client != null) {
client.close();
}
}
private void createK8sClientInner(Long clusterCode) throws RemotingException {
Cluster cluster = clusterMapper.queryByClusterCode(clusterCode);
if (cluster == null) {
return;
}
String k8sConfig = ClusterConfUtils.getK8sConfig(cluster.getConfig());
if (k8sConfig != null) {
DefaultKubernetesClient client = null;
try {
client = getClient(k8sConfig);
clientMap.put(clusterCode, client);
} catch (RemotingException e) {
logger.error("cluster code ={},fail to get k8s ApiClient: {}", clusterCode, e.getMessage());
throw new RemotingException("fail to get k8s ApiClient:" + e.getMessage());
}
}
}
@ -82,4 +127,5 @@ public class K8sManager {
throw new RemotingException("fail to get k8s ApiClient:" + e.getMessage());
}
}
}
}

15
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/K8sNamespaceService.java

@ -28,6 +28,7 @@ import java.util.Map;
* k8s namespace service impl
*/
public interface K8sNamespaceService {
/**
* query namespace list paging
*
@ -39,19 +40,17 @@ public interface K8sNamespaceService {
*/
Result queryListPaging(User loginUser, String searchVal, Integer pageNo, Integer pageSize);
/**
* create namespace,if not exist on k8s,will create,if exist only register in db
*
* @param loginUser login user
* @param namespace namespace
* @param k8s k8s not null
* @param clusterCode k8s not null
* @param limitsCpu limits cpu, can null means not limit
* @param limitsMemory limits memory, can null means not limit
* @return
*/
Map<String, Object> createK8sNamespace(User loginUser, String namespace, String k8s, Double limitsCpu, Integer limitsMemory);
Map<String, Object> createK8sNamespace(User loginUser, String namespace, Long clusterCode, Double limitsCpu, Integer limitsMemory);
/**
* update K8s Namespace tag and resource limit
@ -67,11 +66,11 @@ public interface K8sNamespaceService {
/**
* verify namespace and k8s
*
* @param namespace namespace
* @param k8s k8s
* @param namespace namespace
* @param clusterCode cluster code
* @return true if the k8s and namespace not exists, otherwise return false
*/
Result<Object> verifyNamespaceK8s(String namespace, String k8s);
Result<Object> verifyNamespaceK8s(String namespace, Long clusterCode);
/**
* delete namespace by id
@ -107,4 +106,4 @@ public interface K8sNamespaceService {
* @return namespace list
*/
List<K8sNamespace> queryNamespaceAvailable(User loginUser);
}
}

29
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ClusterServiceImpl.java

@ -19,15 +19,20 @@ package org.apache.dolphinscheduler.api.service.impl;
import org.apache.dolphinscheduler.api.dto.ClusterDto;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.k8s.K8sManager;
import org.apache.dolphinscheduler.api.service.ClusterService;
import org.apache.dolphinscheduler.api.utils.PageInfo;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.common.utils.ClusterConfUtils;
import org.apache.dolphinscheduler.common.utils.CodeGenerateUtils;
import org.apache.dolphinscheduler.common.utils.CodeGenerateUtils.CodeGenerateException;
import org.apache.dolphinscheduler.dao.entity.Cluster;
import org.apache.dolphinscheduler.dao.entity.K8sNamespace;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.ClusterMapper;
import org.apache.dolphinscheduler.dao.mapper.K8sNamespaceMapper;
import org.apache.dolphinscheduler.remote.exceptions.RemotingException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
@ -46,6 +51,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -60,6 +66,11 @@ public class ClusterServiceImpl extends BaseServiceImpl implements ClusterServic
@Autowired
private ClusterMapper clusterMapper;
@Autowired
private K8sManager k8sManager;
@Autowired
private K8sNamespaceMapper k8sNamespaceMapper;
/**
* create cluster
*
@ -237,6 +248,14 @@ public class ClusterServiceImpl extends BaseServiceImpl implements ClusterServic
return result;
}
Integer relatedNamespaceNumber = k8sNamespaceMapper
.selectCount(new QueryWrapper<K8sNamespace>().lambda().eq(K8sNamespace::getClusterCode, code));
if (relatedNamespaceNumber > 0) {
putMsg(result, Status.DELETE_CLUSTER_RELATED_NAMESPACE_EXISTS);
return result;
}
int delete = clusterMapper.deleteByCode(code);
if (delete > 0) {
putMsg(result, Status.SUCCESS);
@ -281,6 +300,16 @@ public class ClusterServiceImpl extends BaseServiceImpl implements ClusterServic
return result;
}
if (!Constants.K8S_LOCAL_TEST_CLUSTER_CODE.equals(clusterExist.getCode())
&& !config.equals(ClusterConfUtils.getK8sConfig(clusterExist.getConfig()))) {
try {
k8sManager.getAndUpdateK8sClient(code, true);
} catch (RemotingException e) {
putMsg(result, Status.K8S_CLIENT_OPS_ERROR, name);
return result;
}
}
//update cluster
clusterExist.setConfig(config);
clusterExist.setName(name);

102
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/K8SNamespaceServiceImpl.java

@ -22,13 +22,16 @@ import org.apache.dolphinscheduler.api.service.K8sNamespaceService;
import org.apache.dolphinscheduler.api.utils.PageInfo;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.common.enums.AuthorizationType;
import org.apache.dolphinscheduler.common.utils.CodeGenerateUtils;
import org.apache.dolphinscheduler.dao.entity.Cluster;
import org.apache.dolphinscheduler.dao.entity.K8sNamespace;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.ClusterMapper;
import org.apache.dolphinscheduler.dao.mapper.K8sNamespaceMapper;
import org.apache.dolphinscheduler.remote.exceptions.RemotingException;
import org.apache.dolphinscheduler.api.k8s.K8sClientService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;
import java.util.Date;
@ -63,11 +66,16 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
+ " hard:\n"
+ " ${limitCpu}\n"
+ " ${limitMemory}\n";
@Autowired
private K8sNamespaceMapper k8sNamespaceMapper;
@Autowired
private K8sClientService k8sClientService;
@Autowired
private ClusterMapper clusterMapper;
/**
* query namespace list paging
*
@ -80,6 +88,11 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
@Override
public Result queryListPaging(User loginUser, String searchVal, Integer pageNo, Integer pageSize) {
Result result = new Result();
if (!isAdmin(loginUser)) {
putMsg(result, Status.USER_NO_OPERATION_PERM);
return result;
}
Page<K8sNamespace> page = new Page<>(pageNo, pageSize);
IPage<K8sNamespace> k8sNamespaceList = k8sNamespaceMapper.queryK8sNamespacePaging(page, searchVal);
@ -99,16 +112,15 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
*
* @param loginUser login user
* @param namespace namespace
* @param k8s k8s not null
* @param clusterCode k8s not null
* @param limitsCpu limits cpu, can null means not limit
* @param limitsMemory limits memory, can null means not limit
* @return
*/
@Override
public Map<String, Object> createK8sNamespace(User loginUser, String namespace, String k8s, Double limitsCpu, Integer limitsMemory) {
public Map<String, Object> createK8sNamespace(User loginUser, String namespace, Long clusterCode, Double limitsCpu, Integer limitsMemory) {
Map<String, Object> result = new HashMap<>();
if (!canOperatorPermissions(loginUser, null,AuthorizationType.K8S_NAMESPACE,null)) {
putMsg(result, Status.USER_NO_OPERATION_PERM);
if (isNotAdmin(loginUser, result)) {
return result;
}
@ -117,8 +129,8 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
return result;
}
if (StringUtils.isEmpty(k8s)) {
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.K8S);
if (clusterCode == null) {
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.CLUSTER);
return result;
}
@ -132,27 +144,45 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
return result;
}
if (checkNamespaceExistInDb(namespace, k8s)) {
putMsg(result, Status.K8S_NAMESPACE_EXIST, namespace, k8s);
if (checkNamespaceExistInDb(namespace, clusterCode)) {
putMsg(result, Status.K8S_NAMESPACE_EXIST, namespace, clusterCode);
return result;
}
Cluster cluster = clusterMapper.queryByClusterCode(clusterCode);
if (cluster == null) {
putMsg(result, Status.CLUSTER_NOT_EXISTS, namespace, clusterCode);
return result;
}
long code = 0L;
try {
code = CodeGenerateUtils.getInstance().genCode();
cluster.setCode(code);
} catch (CodeGenerateUtils.CodeGenerateException e) {
logger.error("Cluster code get error, ", e);
}
if (code == 0L) {
putMsg(result, Status.INTERNAL_SERVER_ERROR_ARGS, "Error generating cluster code");
return result;
}
K8sNamespace k8sNamespaceObj = new K8sNamespace();
Date now = new Date();
k8sNamespaceObj.setCode(code);
k8sNamespaceObj.setNamespace(namespace);
k8sNamespaceObj.setK8s(k8s);
k8sNamespaceObj.setClusterCode(clusterCode);
k8sNamespaceObj.setUserId(loginUser.getId());
k8sNamespaceObj.setLimitsCpu(limitsCpu);
k8sNamespaceObj.setLimitsMemory(limitsMemory);
k8sNamespaceObj.setOnlineJobNum(0);
k8sNamespaceObj.setPodReplicas(0);
k8sNamespaceObj.setPodRequestCpu(0.0);
k8sNamespaceObj.setPodRequestMemory(0);
k8sNamespaceObj.setCreateTime(now);
k8sNamespaceObj.setUpdateTime(now);
if (!Constants.K8S_LOCAL_TEST_CLUSTER.equals(k8sNamespaceObj.getK8s())) {
if (!Constants.K8S_LOCAL_TEST_CLUSTER_CODE.equals(k8sNamespaceObj.getClusterCode())) {
try {
String yamlStr = genDefaultResourceYaml(k8sNamespaceObj);
k8sClientService.upsertNamespaceAndResourceToK8s(k8sNamespaceObj, yamlStr);
@ -181,8 +211,7 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
@Override
public Map<String, Object> updateK8sNamespace(User loginUser, int id, String userName, Double limitsCpu, Integer limitsMemory) {
Map<String, Object> result = new HashMap<>();
if (!canOperatorPermissions(loginUser, null,AuthorizationType.K8S_NAMESPACE,null)) {
putMsg(result, Status.USER_NO_OPERATION_PERM);
if (isNotAdmin(loginUser, result)) {
return result;
}
@ -207,7 +236,7 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
k8sNamespaceObj.setLimitsMemory(limitsMemory);
k8sNamespaceObj.setUpdateTime(now);
if (!Constants.K8S_LOCAL_TEST_CLUSTER.equals(k8sNamespaceObj.getK8s())) {
if (!Constants.K8S_LOCAL_TEST_CLUSTER_CODE.equals(k8sNamespaceObj.getClusterCode())) {
try {
String yamlStr = genDefaultResourceYaml(k8sNamespaceObj);
k8sClientService.upsertNamespaceAndResourceToK8s(k8sNamespaceObj, yamlStr);
@ -227,25 +256,25 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
/**
* verify namespace and k8s
*
* @param namespace namespace
* @param k8s k8s
* @param namespace namespace
* @param clusterCode cluster code
* @return true if the k8s and namespace not exists, otherwise return false
*/
@Override
public Result<Object> verifyNamespaceK8s(String namespace, String k8s) {
public Result<Object> verifyNamespaceK8s(String namespace, Long clusterCode) {
Result<Object> result = new Result<>();
if (StringUtils.isEmpty(namespace)) {
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.NAMESPACE);
return result;
}
if (StringUtils.isEmpty(k8s)) {
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.K8S);
if (clusterCode == null) {
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.CLUSTER);
return result;
}
if (checkNamespaceExistInDb(namespace, k8s)) {
putMsg(result, Status.K8S_NAMESPACE_EXIST, namespace, k8s);
if (checkNamespaceExistInDb(namespace, clusterCode)) {
putMsg(result, Status.K8S_NAMESPACE_EXIST, namespace, clusterCode);
return result;
}
@ -263,8 +292,7 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
@Override
public Map<String, Object> deleteNamespaceById(User loginUser, int id) {
Map<String, Object> result = new HashMap<>();
if (!canOperatorPermissions(loginUser, null,AuthorizationType.K8S_NAMESPACE,null)) {
putMsg(result, Status.USER_NO_OPERATION_PERM);
if (isNotAdmin(loginUser, result)) {
return result;
}
@ -273,8 +301,13 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
putMsg(result, Status.K8S_NAMESPACE_NOT_EXIST, id);
return result;
}
if (!Constants.K8S_LOCAL_TEST_CLUSTER.equals(k8sNamespaceObj.getK8s())) {
k8sClientService.deleteNamespaceToK8s(k8sNamespaceObj.getNamespace(), k8sNamespaceObj.getK8s());
if (!Constants.K8S_LOCAL_TEST_CLUSTER_CODE.equals(k8sNamespaceObj.getClusterCode())) {
try {
k8sClientService.deleteNamespaceToK8s(k8sNamespaceObj.getNamespace(), k8sNamespaceObj.getClusterCode());
} catch (RemotingException e) {
putMsg(result, Status.K8S_CLIENT_OPS_ERROR, id);
return result;
}
}
k8sNamespaceMapper.deleteById(id);
putMsg(result, Status.SUCCESS);
@ -287,8 +320,8 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
* @param namespace namespace
* @return true if the k8s and namespace not exists, otherwise return false
*/
private boolean checkNamespaceExistInDb(String namespace, String k8s) {
return k8sNamespaceMapper.existNamespace(namespace, k8s) == Boolean.TRUE;
private boolean checkNamespaceExistInDb(String namespace, Long clusterCode) {
return k8sNamespaceMapper.existNamespace(namespace, clusterCode) == Boolean.TRUE;
}
/**
@ -338,8 +371,7 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
@Override
public Map<String, Object> queryUnauthorizedNamespace(User loginUser, Integer userId) {
Map<String, Object> result = new HashMap<>();
if (loginUser.getId() != userId && !canOperatorPermissions(loginUser, null,AuthorizationType.K8S_NAMESPACE,null)) {
putMsg(result, Status.USER_NO_OPERATION_PERM);
if (loginUser.getId() != userId && isNotAdmin(loginUser, result)) {
return result;
}
// query all namespace list,this auth does not like project
@ -367,8 +399,7 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
public Map<String, Object> queryAuthorizedNamespace(User loginUser, Integer userId) {
Map<String, Object> result = new HashMap<>();
if (loginUser.getId() != userId && !canOperatorPermissions(loginUser, null,AuthorizationType.K8S_NAMESPACE,null)) {
putMsg(result, Status.USER_NO_OPERATION_PERM);
if (loginUser.getId() != userId && isNotAdmin(loginUser, result)) {
return result;
}
@ -387,7 +418,7 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
*/
@Override
public List<K8sNamespace> queryNamespaceAvailable(User loginUser) {
if (canOperatorPermissions(loginUser,null,AuthorizationType.K8S_NAMESPACE,null)) {
if (isAdmin(loginUser)) {
return k8sNamespaceMapper.selectList(null);
} else {
return k8sNamespaceMapper.queryNamespaceAvailable(loginUser.getId());
@ -419,5 +450,4 @@ public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNames
}
return resultList;
}
}
}

2
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ClusterControllerTest.java

@ -42,7 +42,7 @@ import com.google.common.base.Preconditions;
public class ClusterControllerTest extends AbstractControllerTest {
public static final String clusterName = "Cluster1";
public static final String config = "this is config content";
public static final String config = "{\"k8s\":\"apiVersion: v1\"}";
public static final String desc = "this is cluster description";
private static final Logger logger = LoggerFactory.getLogger(ClusterControllerTest.class);
private String clusterCode;

14
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceControllerTest.java

@ -28,12 +28,8 @@ import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.dao.entity.User;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
@ -72,7 +68,7 @@ public class K8sNamespaceControllerTest extends AbstractControllerTest {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
paramsMap.add("namespace", NAMESPACE_CREATE_STRING);
paramsMap.add("k8s", "k8s");
paramsMap.add("clusterCode", "0");
MvcResult mvcResult = mockMvc.perform(post("/k8s-namespace")
.header(SESSION_ID, sessionId)
@ -82,7 +78,7 @@ public class K8sNamespaceControllerTest extends AbstractControllerTest {
.andReturn();
Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class);
Assert.assertEquals(Status.K8S_CLIENT_OPS_ERROR.getCode(), result.getCode().intValue());//because we not have a k8s cluster in test env
Assert.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue());//because we not have a k8s cluster in test env
logger.info("create queue return result:{}", mvcResult.getResponse().getContentAsString());
}
@ -108,8 +104,8 @@ public class K8sNamespaceControllerTest extends AbstractControllerTest {
public void verifyNamespace() throws Exception {
// queue value exist
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
paramsMap.add("namespace", NAMESPACE_CREATE_STRING);
paramsMap.add("k8s", "default");
paramsMap.add("namespace", "NAMESPACE_CREATE_STRING");
paramsMap.add("clusterCode", "100");
// success
@ -127,7 +123,7 @@ public class K8sNamespaceControllerTest extends AbstractControllerTest {
//error
paramsMap.clear();
paramsMap.add("namespace", null);
paramsMap.add("k8s", "default");
paramsMap.add("clusterCode", "100");
mvcResult = mockMvc.perform(post("/k8s-namespace/verify")
.header(SESSION_ID, sessionId)
.params(paramsMap))

34
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/k8s/K8sManagerTest.java

@ -17,8 +17,9 @@
package org.apache.dolphinscheduler.api.k8s;
import org.apache.dolphinscheduler.dao.entity.K8s;
import org.apache.dolphinscheduler.dao.mapper.K8sMapper;
import org.apache.dolphinscheduler.dao.entity.Cluster;
import org.apache.dolphinscheduler.dao.mapper.ClusterMapper;
import org.apache.dolphinscheduler.remote.exceptions.RemotingException;
import java.util.ArrayList;
import java.util.List;
@ -42,7 +43,7 @@ public class K8sManagerTest {
private K8sManager k8sManager;
@Mock
private K8sMapper k8sMapper;
private ClusterMapper clusterMapper;
@Before
public void setUp() throws Exception {
@ -53,26 +54,27 @@ public class K8sManagerTest {
}
@Test
public void getK8sClient() {
Mockito.when(k8sMapper.selectList(Mockito.any())).thenReturn(getK8sList());
public void getK8sClient() throws RemotingException {
Mockito.when(clusterMapper.selectList(Mockito.any())).thenReturn(getClusterList());
KubernetesClient result = k8sManager.getK8sClient("must null");
KubernetesClient result = k8sManager.getK8sClient(1L);
Assert.assertNull(result);
result = k8sManager.getK8sClient(null);
Assert.assertNull(result);
}
private K8s getK8s() {
K8s k8s = new K8s();
k8s.setId(1);
k8s.setK8sName("default");
k8s.setK8sConfig("k8s config");
return k8s;
private Cluster getCluster() {
Cluster cluster = new Cluster();
cluster.setId(1);
cluster.setCode(1L);
cluster.setName("cluster");
cluster.setConfig("{\"k8s\":\"k8s config yaml\"}");
return cluster;
}
private List<K8s> getK8sList() {
List<K8s> k8sList = new ArrayList<>();
k8sList.add(getK8s());
return k8sList;
private List<Cluster> getClusterList() {
List<Cluster> clusterList = new ArrayList<>();
clusterList.add(getCluster());
return clusterList;
}
}

5
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ClusterServiceTest.java

@ -221,6 +221,11 @@ public class ClusterServiceTest {
result = clusterService.deleteClusterByCode(loginUser,1L);
logger.info(result.toString());
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
Mockito.when(k8sNamespaceMapper.selectCount(Mockito.any())).thenReturn(1);
result = clusterService.deleteClusterByCode(loginUser,1L);
logger.info(result.toString());
Assert.assertEquals(Status.DELETE_CLUSTER_RELATED_NAMESPACE_EXISTS, result.get(Constants.STATUS));
}
@Test

55
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/K8SNamespaceServiceTest.java

@ -18,15 +18,15 @@
package org.apache.dolphinscheduler.api.service;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.service.impl.BaseServiceImpl;
import org.apache.dolphinscheduler.api.service.impl.K8SNamespaceServiceImpl;
import org.apache.dolphinscheduler.api.utils.PageInfo;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.common.enums.AuthorizationType;
import org.apache.dolphinscheduler.common.enums.UserType;
import org.apache.dolphinscheduler.dao.entity.Cluster;
import org.apache.dolphinscheduler.dao.entity.K8sNamespace;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.ClusterMapper;
import org.apache.dolphinscheduler.dao.mapper.K8sNamespaceMapper;
import org.apache.dolphinscheduler.dao.mapper.UserMapper;
import org.apache.dolphinscheduler.api.k8s.K8sClientService;
@ -37,7 +37,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.dolphinscheduler.api.permission.ResourcePermissionCheckService;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -57,7 +56,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
public class K8SNamespaceServiceTest {
private static final Logger logger = LoggerFactory.getLogger(K8SNamespaceServiceTest.class);
private static final Logger baseServiceLogger = LoggerFactory.getLogger(BaseServiceImpl.class);
@InjectMocks
private K8SNamespaceServiceImpl k8sNamespaceService;
@ -69,18 +67,18 @@ public class K8SNamespaceServiceTest {
private K8sClientService k8sClientService;
@Mock
private ResourcePermissionCheckService resourcePermissionCheckService;
private UserMapper userMapper;
@Mock
private UserMapper userMapper;
private ClusterMapper clusterMapper;
private String namespace = "default";
private String k8s = "default";
private Long clusterCode = 100L;
@Before
public void setUp() throws Exception {
Mockito.when(k8sClientService.upsertNamespaceAndResourceToK8s(Mockito.any(K8sNamespace.class), Mockito.anyString())).thenReturn(null);
Mockito.when(k8sClientService.deleteNamespaceToK8s(Mockito.anyString(), Mockito.anyString())).thenReturn(null);
Mockito.when(k8sClientService.deleteNamespaceToK8s(Mockito.anyString(), Mockito.anyLong())).thenReturn(null);
}
@After
@ -101,10 +99,8 @@ public class K8SNamespaceServiceTest {
@Test
public void createK8sNamespace() {
Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.K8S_NAMESPACE, getLoginUser().getId(), null, baseServiceLogger)).thenReturn(true);
Mockito.when(resourcePermissionCheckService.resourcePermissionCheck(AuthorizationType.K8S_NAMESPACE, null, 0, baseServiceLogger)).thenReturn(true);
// namespace is null
Map<String, Object> result = k8sNamespaceService.createK8sNamespace(getLoginUser(), null, k8s, 10.0, 100);
Map<String, Object> result = k8sNamespaceService.createK8sNamespace(getLoginUser(), null, clusterCode, 10.0, 100);
logger.info(result.toString());
Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS));
// k8s is null
@ -112,11 +108,12 @@ public class K8SNamespaceServiceTest {
logger.info(result.toString());
Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS));
// correct
result = k8sNamespaceService.createK8sNamespace(getLoginUser(), namespace, k8s, 10.0, 100);
Mockito.when(clusterMapper.queryByClusterCode(Mockito.anyLong())).thenReturn(getCluster());
result = k8sNamespaceService.createK8sNamespace(getLoginUser(), namespace, clusterCode, 10.0, 100);
logger.info(result.toString());
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
//null limit cpu and mem
result = k8sNamespaceService.createK8sNamespace(getLoginUser(), namespace, k8s, null, null);
result = k8sNamespaceService.createK8sNamespace(getLoginUser(), namespace, clusterCode, null, null);
logger.info(result.toString());
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
}
@ -124,8 +121,7 @@ public class K8SNamespaceServiceTest {
@Test
public void updateK8sNamespace() {
Mockito.when(k8sNamespaceMapper.selectById(1)).thenReturn(getNamespace());
Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.K8S_NAMESPACE, getLoginUser().getId(), null, baseServiceLogger)).thenReturn(true);
Mockito.when(resourcePermissionCheckService.resourcePermissionCheck(AuthorizationType.K8S_NAMESPACE, null, 0, baseServiceLogger)).thenReturn(true);
Map<String, Object> result = k8sNamespaceService.updateK8sNamespace(getLoginUser(), 1, null, null, null);
logger.info(result.toString());
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
@ -142,10 +138,10 @@ public class K8SNamespaceServiceTest {
@Test
public void verifyNamespaceK8s() {
Mockito.when(k8sNamespaceMapper.existNamespace(namespace, k8s)).thenReturn(true);
Mockito.when(k8sNamespaceMapper.existNamespace(namespace, clusterCode)).thenReturn(true);
//namespace null
Result result = k8sNamespaceService.verifyNamespaceK8s(null, k8s);
Result result = k8sNamespaceService.verifyNamespaceK8s(null, clusterCode);
logger.info(result.toString());
Assert.assertEquals(result.getCode().intValue(), Status.REQUEST_PARAMS_NOT_VALID_ERROR.getCode());
@ -155,12 +151,12 @@ public class K8SNamespaceServiceTest {
Assert.assertEquals(result.getCode().intValue(), Status.REQUEST_PARAMS_NOT_VALID_ERROR.getCode());
//exist
result = k8sNamespaceService.verifyNamespaceK8s(namespace, k8s);
result = k8sNamespaceService.verifyNamespaceK8s(namespace, clusterCode);
logger.info(result.toString());
Assert.assertEquals(result.getCode().intValue(), Status.K8S_NAMESPACE_EXIST.getCode());
//not exist
result = k8sNamespaceService.verifyNamespaceK8s(namespace, "other k8s");
result = k8sNamespaceService.verifyNamespaceK8s(namespace, 9999L);
logger.info(result.toString());
Assert.assertEquals(result.getCode().intValue(), Status.SUCCESS.getCode());
}
@ -169,8 +165,7 @@ public class K8SNamespaceServiceTest {
public void deleteNamespaceById() {
Mockito.when(k8sNamespaceMapper.deleteById(Mockito.any())).thenReturn(1);
Mockito.when(k8sNamespaceMapper.selectById(1)).thenReturn(getNamespace());
Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.K8S_NAMESPACE, getLoginUser().getId(), null, baseServiceLogger)).thenReturn(true);
Mockito.when(resourcePermissionCheckService.resourcePermissionCheck(AuthorizationType.K8S_NAMESPACE, null, 0, baseServiceLogger)).thenReturn(true);
Map<String, Object> result = k8sNamespaceService.deleteNamespaceById(getLoginUser(), 1);
logger.info(result.toString());
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
@ -179,8 +174,7 @@ public class K8SNamespaceServiceTest {
@Test
public void testQueryAuthorizedNamespace() {
Mockito.when(k8sNamespaceMapper.queryAuthedNamespaceListByUserId(2)).thenReturn(getNamespaceList());
Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.K8S_NAMESPACE, getLoginUser().getId(), null, baseServiceLogger)).thenReturn(true);
Mockito.when(resourcePermissionCheckService.resourcePermissionCheck(AuthorizationType.K8S_NAMESPACE, null, 0, baseServiceLogger)).thenReturn(true);
User loginUser = getLoginUser();
// test admin user
@ -203,8 +197,7 @@ public class K8SNamespaceServiceTest {
public void testQueryUnAuthorizedNamespace() {
Mockito.when(k8sNamespaceMapper.queryAuthedNamespaceListByUserId(2)).thenReturn(new ArrayList<>());
Mockito.when(k8sNamespaceMapper.selectList(Mockito.any())).thenReturn(getNamespaceList());
Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.K8S_NAMESPACE, 0, null, baseServiceLogger)).thenReturn(true);
Mockito.when(resourcePermissionCheckService.resourcePermissionCheck(AuthorizationType.K8S_NAMESPACE, null, 0, baseServiceLogger)).thenReturn(true);
// test admin user
User loginUser = new User();
loginUser.setUserType(UserType.ADMIN_USER);
@ -235,7 +228,7 @@ public class K8SNamespaceServiceTest {
private K8sNamespace getNamespace() {
K8sNamespace k8sNamespace = new K8sNamespace();
k8sNamespace.setId(1);
k8sNamespace.setK8s(k8s);
k8sNamespace.setClusterCode(clusterCode);
k8sNamespace.setNamespace(namespace);
return k8sNamespace;
}
@ -245,4 +238,14 @@ public class K8SNamespaceServiceTest {
k8sNamespaceList.add(getNamespace());
return k8sNamespaceList;
}
private Cluster getCluster() {
Cluster cluster = new Cluster();
cluster.setId(1);
cluster.setCode(1L);
cluster.setName("clusterName");
cluster.setConfig("{}");
cluster.setOperator(1);
return cluster;
}
}

4
dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/Constants.java

@ -804,10 +804,10 @@ public final class Constants {
* use for k8s
*/
public static final String NAMESPACE = "namespace";
public static final String K8S = "k8s";
public static final String CLUSTER = "cluster";
public static final String LIMITS_CPU = "limitsCpu";
public static final String LIMITS_MEMORY = "limitsMemory";
public static final String K8S_LOCAL_TEST_CLUSTER = "ds_null_k8s";
public static final Long K8S_LOCAL_TEST_CLUSTER_CODE = 0L;
/**
* schedule timezone

49
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespace.java

@ -32,6 +32,11 @@ public class K8sNamespace {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* cluster code
*/
private Long code;
/**
* namespace name
*/
@ -92,16 +97,16 @@ public class K8sNamespace {
private Integer podReplicas = 0;
/**
* online job
* cluster code
*/
@TableField("online_job_num")
private Integer onlineJobNum = 0;
@TableField("cluster_code")
private Long clusterCode;
/**
* k8s name
*/
@TableField("k8s")
private String k8s;
@TableField(exist = false)
private String clusterName;
public Integer getId() {
return id;
@ -175,20 +180,20 @@ public class K8sNamespace {
this.podReplicas = podReplicas;
}
public Integer getOnlineJobNum() {
return onlineJobNum;
public Long getClusterCode() {
return clusterCode;
}
public void setOnlineJobNum(Integer onlineJobNum) {
this.onlineJobNum = onlineJobNum;
public void setClusterCode(Long clusterCode) {
this.clusterCode = clusterCode;
}
public String getK8s() {
return k8s;
public String getClusterName() {
return clusterName;
}
public void setK8s(String k8s) {
this.k8s = k8s;
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public Double getPodRequestCpu() {
@ -211,14 +216,14 @@ public class K8sNamespace {
public String toString() {
return "K8sNamespace{" +
"id=" + id +
"code=" + code +
", namespace=" + namespace +
", limitsCpu=" + limitsCpu +
", limitsMemory=" + limitsMemory +
", userId=" + userId +
", podRequestCpu=" + podRequestCpu +
", podRequestMemory=" + podRequestMemory +
", podReplicas=" + podReplicas +
", k8s=" + k8s +
", clusterCode=" + clusterCode +
", createTime=" + createTime +
", updateTime=" + updateTime +
'}';
@ -239,13 +244,21 @@ public class K8sNamespace {
return true;
}
return namespace.equals(k8sNamespace.namespace) && k8s.equals(k8sNamespace.k8s);
return namespace.equals(k8sNamespace.namespace) && clusterName.equals(k8sNamespace.clusterName);
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (k8s+namespace).hashCode();
result = 31 * result + (clusterName + namespace).hashCode();
return result;
}
}
public Long getCode() {
return code;
}
public void setCode(Long code) {
this.code = code;
}
}

16
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.java

@ -43,11 +43,11 @@ public interface K8sNamespaceMapper extends BaseMapper<K8sNamespace> {
/**
* check the target namespace exist
*
* @param namespace namespace
* @param k8s k8s name
* @param namespace namespace
* @param clusterCode clusterCode
* @return true if exist else return null
*/
Boolean existNamespace(@Param("namespace") String namespace, @Param("k8s") String k8s);
Boolean existNamespace(@Param("namespace") String namespace, @Param("clusterCode") Long clusterCode);
/**
* query namespace except userId
@ -72,4 +72,12 @@ public interface K8sNamespaceMapper extends BaseMapper<K8sNamespace> {
* @return namespace list
*/
List<K8sNamespace> queryNamespaceAvailable(@Param("userId") Integer userId);
}
/**
* check the target namespace
*
* @param namespaceCode namespaceCode
* @return true if exist else return null
*/
K8sNamespace queryByNamespaceCode(@Param("clusterCode") Long namespaceCode);
}

28
dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.xml

@ -20,22 +20,26 @@
<mapper namespace="org.apache.dolphinscheduler.dao.mapper.K8sNamespaceMapper">
<sql id="baseSql">
id, namespace, k8s, user_id, limits_memory, limits_cpu, online_job_num, pod_replicas, pod_request_cpu, pod_request_memory, create_time, update_time
id, code, namespace, user_id, limits_memory, limits_cpu, pod_replicas, pod_request_cpu, pod_request_memory, cluster_code, create_time, update_time
</sql>
<sql id="baseSqlV2">
${alias}.id, ${alias}.namespace, ${alias}.k8s, ${alias}.user_id, ${alias}.limits_memory, ${alias}.limits_cpu, ${alias}.online_job_num, ${alias}.pod_replicas, ${alias}.pod_request_cpu, ${alias}.pod_request_memory, ${alias}.create_time, ${alias}.update_time
${alias}.id, ${alias}.code, ${alias}.namespace, ${alias}.user_id, ${alias}.limits_memory, ${alias}.limits_cpu, ${alias}.pod_replicas, ${alias}.pod_request_cpu, ${alias}.pod_request_memory, ${alias}.cluster_code, ${alias}.create_time, ${alias}.update_time
</sql>
<select id="queryK8sNamespacePaging" resultType="org.apache.dolphinscheduler.dao.entity.K8sNamespace">
select
<include refid="baseSql"/>
from t_ds_k8s_namespace
<include refid="baseSqlV2">
<property name="alias" value="p"/>
</include>
,u.name as cluster_name
from t_ds_k8s_namespace p
left join t_ds_cluster u on u.code=p.cluster_code
where 1= 1
<if test="searchVal != null and searchVal != ''">
and namespace like concat('%', #{searchVal}, '%')
and p.namespace like concat('%', #{searchVal}, '%')
</if>
order by update_time desc
order by p.update_time desc
</select>
<select id="existNamespace" resultType="java.lang.Boolean">
@ -45,8 +49,8 @@
<if test="namespace != null and namespace != ''">
and namespace = #{namespace}
</if>
<if test="k8s != null and k8s != ''">
and k8s =#{k8s}
<if test="clusterCode != null and clusterCode != ''">
and cluster_code =#{clusterCode}
</if>
</select>
@ -76,4 +80,10 @@
on b.id = a.namespace_id
where b.id is not null
</select>
</mapper>
<select id="queryByNamespaceCode" resultType="org.apache.dolphinscheduler.dao.entity.K8sNamespace">
select
<include refid="baseSql"/>
from t_ds_k8s_namespace
where code = #{namespaceCode}
</select>
</mapper>

21
dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_h2.sql

@ -1907,35 +1907,34 @@ CREATE TABLE t_ds_k8s
DROP TABLE IF EXISTS t_ds_k8s_namespace;
CREATE TABLE t_ds_k8s_namespace (
id int(11) NOT NULL AUTO_INCREMENT ,
code bigint(20) NOT NULL,
limits_memory int(11) DEFAULT NULL,
namespace varchar(100) DEFAULT NULL,
online_job_num int(11) DEFAULT NULL,
user_id int(11) DEFAULT NULL,
pod_replicas int(11) DEFAULT NULL,
pod_request_cpu decimal(14,3) DEFAULT NULL,
pod_request_memory int(11) DEFAULT NULL,
limits_cpu decimal(14,3) DEFAULT NULL,
k8s varchar(100) DEFAULT NULL,
cluster_code bigint(20) NOT NULL,
create_time datetime DEFAULT NULL ,
update_time datetime DEFAULT NULL ,
PRIMARY KEY (id) ,
UNIQUE KEY k8s_namespace_unique (namespace,k8s)
UNIQUE KEY k8s_namespace_unique (namespace,cluster_code)
);
-- ----------------------------
-- Records of t_ds_k8s_namespace
-- ----------------------------
INSERT INTO `t_ds_k8s_namespace`
(`id`,`limits_memory`,`namespace`,`online_job_num`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`k8s`,`create_time`,`update_time`)
VALUES (1, 1000, 'flink_test', 99, 1, 1, 0.1, 1, NULL, 'ds_null_k8s', '2022-03-03 11:31:24.0', '2022-03-03 11:31:24.0');
(`id`,`code`,`limits_memory`,`namespace`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`cluster_code`,`create_time`,`update_time`)
VALUES (1, 990001, 1000, 'flink_test', 1, 1, 0.1, 1, 100, 0, '2022-03-03 11:31:24.0', '2022-03-03 11:31:24.0');
INSERT INTO `t_ds_k8s_namespace`
(`id`,`limits_memory`,`namespace`,`online_job_num`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`k8s`,`create_time`,`update_time`)
VALUES (2, 500, 'spark_test', 90, 2,1,10000,1, NULL, 'ds_null_k8s', '2021-03-03 11:31:24.0', '2021-03-03 11:31:24.0');
(`id`,`code`,`limits_memory`,`namespace`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`cluster_code`,`create_time`,`update_time`)
VALUES (2, 990002, 500, 'spark_test', 2, 1, 10000, 1, 100, 0, '2021-03-03 11:31:24.0', '2021-03-03 11:31:24.0');
INSERT INTO `t_ds_k8s_namespace`
(`id`,`limits_memory`,`namespace`,`online_job_num`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`k8s`,`create_time`,`update_time`)
VALUES (3, 200, 'auth_test', 68, 3,1,100,1, 10000, 'ds_null_k8s', '2020-03-03 11:31:24.0', '2020-03-03 11:31:24.0');
(`id`,`code`,`limits_memory`,`namespace`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`cluster_code`,`create_time`,`update_time`)
VALUES (3, 990003, 200, 'auth_test', 3, 1, 100, 1, 10000, 0, '2020-03-03 11:31:24.0', '2020-03-03 11:31:24.0');
-- ----------------------------
-- Table structure for t_ds_relation_namespace_user
@ -1990,4 +1989,4 @@ CREATE TABLE t_ds_cluster
INSERT INTO `t_ds_cluster`
(`id`,`code`,`name`,`config`,`description`,`operator`,`create_time`,`update_time`)
VALUES (100, 100, 'ds_null_k8s', '{"k8s":"ds_null_k8s"}', 'test', 1, '2021-03-03 11:31:24.0', '2021-03-03 11:31:24.0');
VALUES (100, 0, 'ds_null_k8s', '{"k8s":"ds_null_k8s"}', 'test', 1, '2021-03-03 11:31:24.0', '2021-03-03 11:31:24.0');

6
dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_mysql.sql

@ -1901,19 +1901,19 @@ CREATE TABLE `t_ds_k8s` (
DROP TABLE IF EXISTS `t_ds_k8s_namespace`;
CREATE TABLE `t_ds_k8s_namespace` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` bigint(20) NOT NULL DEFAULT '0',
`limits_memory` int(11) DEFAULT NULL,
`namespace` varchar(100) DEFAULT NULL,
`online_job_num` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`pod_replicas` int(11) DEFAULT NULL,
`pod_request_cpu` decimal(14,3) DEFAULT NULL,
`pod_request_memory` int(11) DEFAULT NULL,
`limits_cpu` decimal(14,3) DEFAULT NULL,
`k8s` varchar(100) DEFAULT NULL,
`cluster_code` bigint(20) NOT NULL DEFAULT '0',
`create_time` datetime DEFAULT NULL COMMENT 'create time',
`update_time` datetime DEFAULT NULL COMMENT 'update time',
PRIMARY KEY (`id`),
UNIQUE KEY `k8s_namespace_unique` (`namespace`,`k8s`)
UNIQUE KEY `k8s_namespace_unique` (`namespace`,`cluster_code`)
) ENGINE= INNODB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8;
-- ----------------------------

7
dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_postgresql.sql

@ -1900,21 +1900,22 @@ CREATE TABLE t_ds_k8s (
DROP TABLE IF EXISTS t_ds_k8s_namespace;
CREATE TABLE t_ds_k8s_namespace (
id serial NOT NULL,
code bigint NOT NULL,
limits_memory int DEFAULT NULL ,
namespace varchar(100) DEFAULT NULL ,
online_job_num int DEFAULT '0' ,
user_id int DEFAULT NULL,
pod_replicas int DEFAULT NULL,
pod_request_cpu NUMERIC(13,4) NULL,
pod_request_memory int DEFAULT NULL,
limits_cpu NUMERIC(13,4) NULL,
k8s varchar(100) DEFAULT NULL,
cluster_code bigint NOT NULL,
create_time timestamp DEFAULT NULL ,
update_time timestamp DEFAULT NULL ,
PRIMARY KEY (id) ,
CONSTRAINT k8s_namespace_unique UNIQUE (namespace,k8s)
CONSTRAINT k8s_namespace_unique UNIQUE (namespace,cluster_code)
);
--
-- Table structure for table t_ds_relation_namespace_user
--

6
dolphinscheduler-dao/src/main/resources/sql/upgrade/3.0.0_schema/mysql/dolphinscheduler_ddl.sql

@ -266,19 +266,19 @@ CREATE TABLE `t_ds_k8s` (
DROP TABLE IF EXISTS `t_ds_k8s_namespace`;
CREATE TABLE `t_ds_k8s_namespace` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` bigint(20) NOT NULL DEFAULT '0',
`limits_memory` int(11) DEFAULT NULL,
`namespace` varchar(100) DEFAULT NULL,
`online_job_num` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`pod_replicas` int(11) DEFAULT NULL,
`pod_request_cpu` decimal(14,3) DEFAULT NULL,
`pod_request_memory` int(11) DEFAULT NULL,
`limits_cpu` decimal(14,3) DEFAULT NULL,
`k8s` varchar(100) DEFAULT NULL,
`cluster_code` bigint(20) NOT NULL DEFAULT '0',
`create_time` datetime DEFAULT NULL COMMENT 'create time',
`update_time` datetime DEFAULT NULL COMMENT 'update time',
PRIMARY KEY (`id`),
UNIQUE KEY `k8s_namespace_unique` (`namespace`,`k8s`)
UNIQUE KEY `k8s_namespace_unique` (`namespace`,`cluster_code`)
) ENGINE= INNODB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8;
-- ----------------------------

6
dolphinscheduler-dao/src/main/resources/sql/upgrade/3.0.0_schema/postgresql/dolphinscheduler_ddl.sql

@ -242,19 +242,19 @@ EXECUTE 'CREATE TABLE IF NOT EXISTS '|| quote_ident(v_schema) ||'."t_ds_k8s" (
EXECUTE 'CREATE TABLE IF NOT EXISTS '|| quote_ident(v_schema) ||'."t_ds_k8s_namespace" (
id serial NOT NULL,
code bigint NOT NULL,
limits_memory int DEFAULT NULL ,
namespace varchar(100) DEFAULT NULL ,
online_job_num int DEFAULT NULL,
user_id int DEFAULT NULL,
pod_replicas int DEFAULT NULL,
pod_request_cpu NUMERIC(13,4) NULL,
pod_request_memory int DEFAULT NULL,
limits_cpu NUMERIC(13,4) NULL,
k8s varchar(100) DEFAULT NULL,
cluster_code bigint NOT NULL,
create_time timestamp DEFAULT NULL ,
update_time timestamp DEFAULT NULL ,
PRIMARY KEY (id) ,
CONSTRAINT k8s_namespace_unique UNIQUE (namespace,k8s)
CONSTRAINT k8s_namespace_unique UNIQUE (namespace,cluster_code)
)';
EXECUTE 'CREATE TABLE IF NOT EXISTS '|| quote_ident(v_schema) ||'."t_ds_relation_namespace_user" (

6
dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapperTest.java

@ -23,7 +23,6 @@ import org.apache.dolphinscheduler.dao.entity.K8sNamespace;
import java.util.Date;
import java.util.List;
import org.apache.dolphinscheduler.dao.entity.User;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -46,13 +45,14 @@ public class K8sNamespaceMapperTest extends BaseDaoTest {
private K8sNamespace insertOne() {
//insertOne
K8sNamespace k8sNamespace = new K8sNamespace();
k8sNamespace.setCode(999L);
k8sNamespace.setNamespace("testNamespace");
k8sNamespace.setK8s("ds_null_k8s");
k8sNamespace.setClusterCode(100L);
k8sNamespace.setClusterName("ds_null_k8s");
k8sNamespace.setLimitsCpu(100.0);
k8sNamespace.setLimitsMemory(100);
k8sNamespace.setCreateTime(new Date());
k8sNamespace.setUpdateTime(new Date());
k8sNamespace.setId(1);
k8sNamespaceMapper.insert(k8sNamespace);
return k8sNamespace;
}

2
dolphinscheduler-ui/src/locales/en_US/security.ts

@ -269,8 +269,6 @@ export default {
k8s_namespace_tips: 'Please enter k8s namespace',
k8s_cluster: 'K8S Cluster',
k8s_cluster_tips: 'Please enter k8s cluster',
owner: 'Owner',
owner_tips: 'Please enter owner',
limit_cpu: 'Limit CPU',
limit_cpu_tips: 'Please enter limit CPU',
limit_memory: 'Limit Memory',

2
dolphinscheduler-ui/src/locales/zh_CN/security.ts

@ -269,8 +269,6 @@ export default {
k8s_namespace_tips: '请输入k8s命名空间',
k8s_cluster: 'K8S集群',
k8s_cluster_tips: '请输入k8s集群',
owner: '负责人',
owner_tips: '请输入负责人',
limit_cpu: '最大CPU',
limit_cpu_tips: '请输入最大CPU',
limit_memory: '最大内存',

4
dolphinscheduler-ui/src/service/modules/k8s-namespace/types.ts

@ -23,7 +23,7 @@ interface ListReq {
interface K8SReq {
namespace: string
k8s: string
clusterCode: string
owner?: string
tag?: string
limitsCpu?: number | string
@ -49,4 +49,4 @@ interface NamespaceListRes {
start: number
}
export { ListReq, K8SReq, NamespaceItem, NamespaceListRes }
export { ListReq, K8SReq, NamespaceItem, NamespaceListRes }

215
dolphinscheduler-ui/src/views/security/k8s-namespace-manage/components/k8s-namespace-modal.tsx

@ -22,7 +22,8 @@ import {
NFormItem,
NInput,
NInputGroup,
NInputGroupLabel
NInputGroupLabel,
NSelect
} from 'naive-ui'
import { useModal } from './use-modal'
import { useI18n } from 'vue-i18n'
@ -45,13 +46,13 @@ const K8sNamespaceModal = defineComponent({
},
emits: ['cancelModal', 'confirmModal'],
setup(props, ctx) {
const { variables, handleValidate } = useModal(props, ctx)
const { variables, handleValidate, getListData } = useModal(props, ctx)
const { t } = useI18n()
const cancelModal = () => {
if (props.statusRef === 0) {
variables.model.namespace = ''
variables.model.k8s = ''
variables.model.clusterCode = ''
variables.model.limitsCpu = ''
variables.model.limitsMemory = ''
variables.model.userId = ''
@ -64,35 +65,42 @@ const K8sNamespaceModal = defineComponent({
}
watch(
() => props.statusRef,
() => {
if (props.statusRef === 0) {
variables.model.namespace = ''
variables.model.k8s = ''
variables.model.limitsCpu = ''
variables.model.limitsMemory = ''
variables.model.userId = ''
} else {
() => props.showModalRef,
() => {
props.showModalRef && getListData()
}
)
watch(
() => props.statusRef,
() => {
if (props.statusRef === 0) {
variables.model.namespace = ''
variables.model.clusterCode = ''
variables.model.limitsCpu = ''
variables.model.limitsMemory = ''
variables.model.userId = ''
} else {
variables.model.id = props.row.id
variables.model.namespace = props.row.namespace
variables.model.clusterCode = props.row.clusterCode
variables.model.limitsCpu = props.row.limitsCpu + ''
variables.model.limitsMemory = props.row.limitsMemory + ''
variables.model.userId = props.row.userId
}
}
)
watch(
() => props.row,
() => {
variables.model.id = props.row.id
variables.model.namespace = props.row.namespace
variables.model.k8s = props.row.k8s
variables.model.clusterCode = props.row.clusterCode
variables.model.limitsCpu = props.row.limitsCpu + ''
variables.model.limitsMemory = props.row.limitsMemory + ''
variables.model.userId = props.row.userId
}
}
)
watch(
() => props.row,
() => {
variables.model.id = props.row.id
variables.model.namespace = props.row.namespace
variables.model.k8s = props.row.k8s
variables.model.limitsCpu = props.row.limitsCpu + ''
variables.model.limitsMemory = props.row.limitsMemory + ''
variables.model.userId = props.row.userId
}
)
return { t, ...toRefs(variables), cancelModal, confirmModal }
@ -100,89 +108,80 @@ const K8sNamespaceModal = defineComponent({
render() {
const { t } = this
return (
<div>
<Modal
title={
this.statusRef === 0
? t('security.k8s_namespace.create_namespace')
: t('security.k8s_namespace.edit_namespace')
}
show={this.showModalRef}
onCancel={this.cancelModal}
onConfirm={this.confirmModal}
confirmDisabled={!this.model.namespace || !this.model.k8s}
confirmLoading={this.saving}
>
{{
default: () => (
<NForm
model={this.model}
rules={this.rules}
ref='k8sNamespaceFormRef'
>
<NFormItem
label={t('security.k8s_namespace.k8s_namespace')}
path='namespace'
>
<NInput
placeholder={t('security.k8s_namespace.k8s_namespace_tips')}
v-model={[this.model.namespace, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.k8s_cluster')}
path='k8s'
>
<NInput
placeholder={t('security.k8s_namespace.k8s_cluster_tips')}
v-model={[this.model.k8s, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.limit_cpu')}
path='limitsCpu'
>
<NInputGroup>
<NInput
placeholder={t('security.k8s_namespace.limit_cpu_tips')}
v-model={[this.model.limitsCpu, 'value']}
/>
<NInputGroupLabel>CORE</NInputGroupLabel>
</NInputGroup>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.limit_memory')}
path='limitsMemory'
>
<NInputGroup>
<NInput
placeholder={t(
'security.k8s_namespace.limit_memory_tips'
)}
v-model={[this.model.limitsMemory, 'value']}
/>
<NInputGroupLabel>GB</NInputGroupLabel>
</NInputGroup>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.owner')}
path='userId'
>
<NInput
placeholder={t('security.k8s_namespace.owner_tips')}
v-model={[this.model.userId, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
</NForm>
)
}}
</Modal>
</div>
<div>
<Modal
title={
this.statusRef === 0
? t('security.k8s_namespace.create_namespace')
: t('security.k8s_namespace.edit_namespace')
}
show={this.showModalRef}
onCancel={this.cancelModal}
onConfirm={this.confirmModal}
confirmDisabled={!this.model.namespace || (this.model.clusterCode == null || this.model.clusterCode === '')}
confirmLoading={this.saving}
>
{{
default: () => (
<NForm
model={this.model}
rules={this.rules}
ref='k8sNamespaceFormRef'
>
<NFormItem
label={t('security.k8s_namespace.k8s_namespace')}
path='namespace'
>
<NInput
placeholder={t('security.k8s_namespace.k8s_namespace_tips')}
v-model={[this.model.namespace, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.k8s_cluster')}
path='clusterCode'
>
<NSelect
placeholder={t('security.k8s_namespace.k8s_cluster_tips')}
options={this.model.clusterOptions}
v-model={[this.model.clusterCode, 'value']}
disabled={this.statusRef !== 0}
/>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.limit_cpu')}
path='limitsCpu'
>
<NInputGroup>
<NInput
placeholder={t('security.k8s_namespace.limit_cpu_tips')}
v-model={[this.model.limitsCpu, 'value']}
/>
<NInputGroupLabel>CORE</NInputGroupLabel>
</NInputGroup>
</NFormItem>
<NFormItem
label={t('security.k8s_namespace.limit_memory')}
path='limitsMemory'
>
<NInputGroup>
<NInput
placeholder={t(
'security.k8s_namespace.limit_memory_tips'
)}
v-model={[this.model.limitsMemory, 'value']}
/>
<NInputGroupLabel>GB</NInputGroupLabel>
</NInputGroup>
</NFormItem>
</NForm>
)
}}
</Modal>
</div>
)
}
})
export default K8sNamespaceModal
export default K8sNamespaceModal

56
dolphinscheduler-ui/src/views/security/k8s-namespace-manage/components/use-modal.ts

@ -22,10 +22,12 @@ import {
createK8sNamespace,
updateK8sNamespace
} from '@/service/modules/k8s-namespace'
import { queryAllClusterList } from '@/service/modules/cluster'
import { useAsyncState } from '@vueuse/core'
export function useModal(
props: any,
ctx: SetupContext<('cancelModal' | 'confirmModal')[]>
props: any,
ctx: SetupContext<('cancelModal' | 'confirmModal')[]>
) {
const { t } = useI18n()
@ -34,10 +36,11 @@ export function useModal(
model: {
id: ref<number>(-1),
namespace: ref(''),
k8s: ref(''),
clusterCode: ref(''),
userId: ref(''),
limitsCpu: ref(''),
limitsMemory: ref('')
limitsMemory: ref(''),
clusterOptions: []
},
saving: false,
rules: {
@ -50,11 +53,11 @@ export function useModal(
}
}
},
k8s: {
clusterCode: {
required: true,
trigger: ['input', 'blur'],
validator() {
if (variables.model.k8s === '') {
if (variables.model.clusterCode === '') {
return new Error(t('security.k8s_namespace.k8s_cluster_tips'))
}
}
@ -70,19 +73,43 @@ export function useModal(
try {
statusRef === 0
? await submitK8SNamespaceModal()
: await updateK8SNamespaceModal()
? await submitK8SNamespaceModal()
: await updateK8SNamespaceModal()
variables.saving = false
} catch (err) {
variables.saving = false
}
}
const getListData = () => {
const { state } = useAsyncState(
queryAllClusterList().then((res: any) => {
variables.model.clusterOptions = res
.filter((item: any) => {
if (item.config) {
const k8s = JSON.parse(item.config).k8s
return !!k8s
}
return false
})
.map((item: any) => {
return {
label: item.name,
value: item.code
}
})
}),
{}
)
return state
}
const submitK8SNamespaceModal = () => {
verifyNamespaceK8s(variables.model).then(() => {
createK8sNamespace(variables.model).then(() => {
variables.model.namespace = ''
variables.model.k8s = ''
variables.model.clusterCode = ''
variables.model.limitsCpu = ''
variables.model.limitsMemory = ''
variables.model.userId = ''
@ -93,14 +120,15 @@ export function useModal(
const updateK8SNamespaceModal = () => {
updateK8sNamespace(variables.model, variables.model.id).then(
(ignored: any) => {
ctx.emit('confirmModal', props.showModalRef)
}
(ignored: any) => {
ctx.emit('confirmModal', props.showModalRef)
}
)
}
return {
variables,
handleValidate
handleValidate,
getListData
}
}
}

147
dolphinscheduler-ui/src/views/security/k8s-namespace-manage/use-table.ts

@ -50,9 +50,9 @@ export function useTable() {
getTableData({
pageSize: variables.pageSize,
pageNo:
variables.tableData.length === 1 && variables.page > 1
? variables.page - 1
: variables.page,
variables.tableData.length === 1 && variables.page > 1
? variables.page - 1
: variables.page,
searchVal: variables.searchVal
})
})
@ -73,14 +73,9 @@ export function useTable() {
},
{
title: t('security.k8s_namespace.k8s_cluster'),
key: 'k8s',
key: 'clusterName',
...COLUMN_WIDTH_CONFIG['name']
},
{
title: t('security.k8s_namespace.owner'),
key: 'userId',
...COLUMN_WIDTH_CONFIG['userName']
},
{
title: t('security.k8s_namespace.limit_cpu'),
key: 'limitsCpu',
@ -109,61 +104,61 @@ export function useTable() {
return h(NSpace, null, {
default: () => [
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'info',
size: 'small',
onClick: () => {
handleEdit(row)
}
},
{
icon: () =>
h(NIcon, null, { default: () => h(EditOutlined) })
}
),
default: () => t('security.k8s_namespace.edit')
}
),
h(
NPopconfirm,
{
onPositiveClick: () => {
handleDelete(row)
}
},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'error',
size: 'small'
type: 'info',
size: 'small',
onClick: () => {
handleEdit(row)
}
},
{
icon: () =>
h(NIcon, null, {
default: () => h(DeleteOutlined)
})
h(NIcon, null, { default: () => h(EditOutlined) })
}
),
default: () => t('security.k8s_namespace.delete')
}
),
default: () => t('security.k8s_namespace.delete_confirm')
}
),
default: () => t('security.k8s_namespace.edit')
}
),
h(
NPopconfirm,
{
onPositiveClick: () => {
handleDelete(row)
}
},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'error',
size: 'small'
},
{
icon: () =>
h(NIcon, null, {
default: () => h(DeleteOutlined)
})
}
),
default: () => t('security.k8s_namespace.delete')
}
),
default: () => t('security.k8s_namespace.delete_confirm')
}
)
]
})
@ -193,24 +188,24 @@ export function useTable() {
if (variables.loadingRef) return
variables.loadingRef = true
const { state } = useAsyncState(
queryNamespaceListPaging({ ...params }).then((res: NamespaceListRes) => {
variables.tableData = res.totalList.map((item, unused) => {
item.createTime = format(
parseTime(item.createTime),
'yyyy-MM-dd HH:mm:ss'
)
item.updateTime = format(
parseTime(item.updateTime),
'yyyy-MM-dd HH:mm:ss'
)
return {
...item
}
}) as any
variables.totalPage = res.totalPage
variables.loadingRef = false
}),
{}
queryNamespaceListPaging({ ...params }).then((res: NamespaceListRes) => {
variables.tableData = res.totalList.map((item, unused) => {
item.createTime = format(
parseTime(item.createTime),
'yyyy-MM-dd HH:mm:ss'
)
item.updateTime = format(
parseTime(item.updateTime),
'yyyy-MM-dd HH:mm:ss'
)
return {
...item
}
}) as any
variables.totalPage = res.totalPage
variables.loadingRef = false
}),
{}
)
return state
@ -221,4 +216,4 @@ export function useTable() {
getTableData,
createColumns
}
}
}
Loading…
Cancel
Save