Browse Source
* add db operator for namespace * add k8s client * add k8s controller * add namespace ui * add licenses * add kubernetes model known-dependencies * add new licenses * fix SonarCloud Code Analysis bug * remove license for standard protocol * fix variable MemoryStr to memoryStr Co-authored-by: qianl4 <qianl4@cicso.com>3.0.0/version-upgrade
qianli2022
3 years ago
committed by
GitHub
35 changed files with 2659 additions and 5 deletions
@ -0,0 +1,214 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.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_K8S_NAMESPACE_LIST_PAGING_ERROR; |
||||
import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_K8S_NAMESPACE_ERROR; |
||||
import static org.apache.dolphinscheduler.api.enums.Status.VERIFY_K8S_NAMESPACE_ERROR; |
||||
|
||||
import org.apache.dolphinscheduler.api.aspect.AccessLogAnnotation; |
||||
import org.apache.dolphinscheduler.api.exceptions.ApiException; |
||||
import org.apache.dolphinscheduler.api.service.K8sNameSpaceService; |
||||
import org.apache.dolphinscheduler.api.utils.Result; |
||||
import org.apache.dolphinscheduler.common.Constants; |
||||
import org.apache.dolphinscheduler.common.utils.ParameterUtils; |
||||
import org.apache.dolphinscheduler.dao.entity.User; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.PutMapping; |
||||
import org.springframework.web.bind.annotation.RequestAttribute; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestParam; |
||||
import org.springframework.web.bind.annotation.ResponseStatus; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import io.swagger.annotations.Api; |
||||
import io.swagger.annotations.ApiImplicitParam; |
||||
import io.swagger.annotations.ApiImplicitParams; |
||||
import io.swagger.annotations.ApiOperation; |
||||
import springfox.documentation.annotations.ApiIgnore; |
||||
|
||||
/** |
||||
* k8s namespace controller |
||||
*/ |
||||
@Api(tags = "K8S_NAMESPACE_TAG") |
||||
@RestController |
||||
@RequestMapping("/k8s-namespace") |
||||
public class K8sNamespaceController extends BaseController { |
||||
|
||||
@Autowired |
||||
private K8sNameSpaceService k8sNameSpaceService; |
||||
|
||||
/** |
||||
* query namespace list paging |
||||
* |
||||
* @param loginUser login user |
||||
* @param searchVal search value |
||||
* @param pageSize page size |
||||
* @param pageNo page number |
||||
* @return namespace list which the login user have permission to see |
||||
*/ |
||||
@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") |
||||
}) |
||||
@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 |
||||
) { |
||||
|
||||
Result result = checkPageParams(pageNo, pageSize); |
||||
if (!result.checkResult()) { |
||||
return result; |
||||
} |
||||
searchVal = ParameterUtils.handleEscapes(searchVal); |
||||
result = k8sNameSpaceService.queryListPaging(loginUser, searchVal, pageNo, pageSize); |
||||
return result; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* create namespace,if not exist on k8s,will create,if exist only register in db |
||||
* |
||||
* @param loginUser |
||||
* @param namespace k8s namespace |
||||
* @param owner owner |
||||
* @param tag Which type of job is available, can be empty, all available |
||||
* @param k8s k8s name |
||||
* @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 = "owner", value = "OWNER", required = false, dataType = "String"), |
||||
@ApiImplicitParam(name = "tag", value = "TAG", required = false, 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") |
||||
}) |
||||
@PostMapping() |
||||
@ResponseStatus(HttpStatus.CREATED) |
||||
@ApiException(CREATE_K8S_NAMESPACE_ERROR) |
||||
@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 = "owner", required = false) String owner, |
||||
@RequestParam(value = "tag", required = false) String tag, |
||||
@RequestParam(value = "limitsCpu", required = false) Double limitsCpu, |
||||
@RequestParam(value = "limitsMemory", required = false) Integer limitsMemory |
||||
) { |
||||
Map<String, Object> result = k8sNameSpaceService.createK8sNamespace(loginUser, namespace, k8s, owner, tag, limitsCpu, limitsMemory); |
||||
return returnDataList(result); |
||||
} |
||||
|
||||
/** |
||||
* update namespace,namespace and k8s not allowed update, because may create on k8s,can delete and create new instead |
||||
* |
||||
* @param loginUser |
||||
* @param owner owner |
||||
* @param tag Which type of job is available,such as flink,means only flink job can use, can be empty, all available |
||||
* @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 = "owner", value = "OWNER", required = false, dataType = "String"), |
||||
@ApiImplicitParam(name = "tag", value = "TAG", 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) |
||||
@AccessLogAnnotation(ignoreRequestArgs = "loginUser") |
||||
public Result updateNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, |
||||
@PathVariable(value = "id") int id, |
||||
@RequestParam(value = "owner", required = false) String owner, |
||||
@RequestParam(value = "tag", required = false) String tag, |
||||
@RequestParam(value = "limitsCpu", required = false) Double limitsCpu, |
||||
@RequestParam(value = "limitsMemory", required = false) Integer limitsMemory) { |
||||
Map<String, Object> result = k8sNameSpaceService.updateK8sNamespace(loginUser, id, owner, tag, limitsCpu, limitsMemory); |
||||
return returnDataList(result); |
||||
} |
||||
|
||||
/** |
||||
* verify namespace and k8s,one k8s namespace is unique |
||||
* |
||||
* @param loginUser login user |
||||
* @param namespace namespace |
||||
* @param k8s k8s |
||||
* @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") |
||||
}) |
||||
@PostMapping(value = "/verify") |
||||
@ResponseStatus(HttpStatus.OK) |
||||
@ApiException(VERIFY_K8S_NAMESPACE_ERROR) |
||||
@AccessLogAnnotation(ignoreRequestArgs = "loginUser") |
||||
public Result verifyNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, |
||||
@RequestParam(value = "namespace") String namespace, |
||||
@RequestParam(value = "k8s") String k8s |
||||
) { |
||||
|
||||
return k8sNameSpaceService.verifyNamespaceK8s(namespace, k8s); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* delete namespace by id |
||||
* |
||||
* @param loginUser login user |
||||
* @param id namespace id |
||||
* @return delete result code |
||||
*/ |
||||
@ApiOperation(value = "delNamespaceById", notes = "DELETE_NAMESPACE_BY_ID_NOTES") |
||||
@ApiImplicitParams({ |
||||
@ApiImplicitParam(name = "id", value = "NAMESPACE_ID", required = true, dataType = "Int", example = "100") |
||||
}) |
||||
@PostMapping(value = "/delete") |
||||
@ResponseStatus(HttpStatus.OK) |
||||
@ApiException(DELETE_K8S_NAMESPACE_BY_ID_ERROR) |
||||
@AccessLogAnnotation |
||||
public Result delNamespaceById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, |
||||
@RequestParam(value = "id") int id) { |
||||
Map<String, Object> result = k8sNameSpaceService.deleteNamespaceById(loginUser, id); |
||||
return returnDataList(result); |
||||
} |
||||
} |
@ -0,0 +1,85 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.api.service; |
||||
|
||||
import org.apache.dolphinscheduler.api.utils.Result; |
||||
import org.apache.dolphinscheduler.dao.entity.User; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* k8s namespace service impl |
||||
*/ |
||||
public interface K8sNameSpaceService { |
||||
/** |
||||
* query namespace list paging |
||||
* |
||||
* @param loginUser login user |
||||
* @param pageNo page number |
||||
* @param searchVal search value |
||||
* @param pageSize page size |
||||
* @return k8s namespace list |
||||
*/ |
||||
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 owner owner can null |
||||
* @param tag can null,if set means just used for one type job,such as flink,spark |
||||
* @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, String owner, String tag, Double limitsCpu, Integer limitsMemory); |
||||
|
||||
|
||||
/** |
||||
* update K8s Namespace tag and resource limit |
||||
* |
||||
* @param loginUser login user |
||||
* @param owner owner |
||||
* @param tag Which type of job is available,such as flink,means only flink job can use, can be empty, all available |
||||
* @param limitsCpu max cpu |
||||
* @param limitsMemory max memory |
||||
* @return |
||||
*/ |
||||
Map<String, Object> updateK8sNamespace(User loginUser, int id, String owner, String tag, Double limitsCpu, Integer limitsMemory); |
||||
|
||||
/** |
||||
* verify namespace and k8s |
||||
* |
||||
* @param namespace namespace |
||||
* @param k8s k8s |
||||
* @return true if the k8s and namespace not exists, otherwise return false |
||||
*/ |
||||
Result<Object> verifyNamespaceK8s(String namespace, String k8s); |
||||
|
||||
/** |
||||
* delete namespace by id |
||||
* |
||||
* @param loginUser login user |
||||
* @param id namespace id |
||||
* @return |
||||
*/ |
||||
Map<String, Object> deleteNamespaceById(User loginUser, int id); |
||||
} |
@ -0,0 +1,326 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.api.service.impl; |
||||
|
||||
import org.apache.dolphinscheduler.api.enums.Status; |
||||
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.dao.entity.K8sNamespace; |
||||
import org.apache.dolphinscheduler.dao.entity.User; |
||||
import org.apache.dolphinscheduler.dao.mapper.K8sNamespaceMapper; |
||||
import org.apache.dolphinscheduler.service.k8s.K8sClientService; |
||||
|
||||
import org.apache.commons.lang.StringUtils; |
||||
|
||||
import java.util.Date; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
||||
/** |
||||
* k8s namespace service impl |
||||
*/ |
||||
@Service |
||||
public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameSpaceService { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(K8sNameSpaceServiceImpl.class); |
||||
|
||||
private static String resourceYaml = "apiVersion: v1\n" |
||||
+ "kind: ResourceQuota\n" |
||||
+ "metadata:\n" |
||||
+ " name: ${name}\n" |
||||
+ " namespace: ${namespace}\n" |
||||
+ "spec:\n" |
||||
+ " hard:\n" |
||||
+ " ${limitCpu}\n" |
||||
+ " ${limitMemory}\n"; |
||||
@Autowired |
||||
private K8sNamespaceMapper k8sNamespaceMapper; |
||||
@Autowired |
||||
private K8sClientService k8sClientService; |
||||
|
||||
/** |
||||
* query namespace list paging |
||||
* |
||||
* @param loginUser login user |
||||
* @param pageNo page number |
||||
* @param searchVal search value |
||||
* @param pageSize page size |
||||
* @return k8s namespace list |
||||
*/ |
||||
@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); |
||||
|
||||
Integer count = (int) k8sNamespaceList.getTotal(); |
||||
PageInfo<K8sNamespace> pageInfo = new PageInfo<>(pageNo, pageSize); |
||||
pageInfo.setTotal(count); |
||||
pageInfo.setTotalList(k8sNamespaceList.getRecords()); |
||||
result.setData(pageInfo); |
||||
putMsg(result, Status.SUCCESS); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* 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 owner owner can null |
||||
* @param tag can null,if set means just used for one type job,such as flink,spark |
||||
* @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, String owner, String tag, Double limitsCpu, Integer limitsMemory) { |
||||
Map<String, Object> result = new HashMap<>(); |
||||
if (isNotAdmin(loginUser, result)) { |
||||
return 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); |
||||
return result; |
||||
} |
||||
|
||||
if (limitsCpu != null && limitsCpu < 0.0) { |
||||
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.LIMITS_CPU); |
||||
return result; |
||||
} |
||||
|
||||
if (limitsMemory != null && limitsMemory < 0) { |
||||
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.LIMITS_MEMORY); |
||||
return result; |
||||
} |
||||
|
||||
if (checkNamespaceExistInDb(namespace, k8s)) { |
||||
putMsg(result, Status.K8S_NAMESPACE_EXIST, namespace, k8s); |
||||
return result; |
||||
} |
||||
|
||||
K8sNamespace k8sNamespaceObj = new K8sNamespace(); |
||||
Date now = new Date(); |
||||
|
||||
k8sNamespaceObj.setNamespace(namespace); |
||||
k8sNamespaceObj.setK8s(k8s); |
||||
k8sNamespaceObj.setOwner(owner); |
||||
k8sNamespaceObj.setTag(tag); |
||||
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); |
||||
|
||||
try { |
||||
String yamlStr = genDefaultResourceYaml(k8sNamespaceObj); |
||||
k8sClientService.upsertNamespaceAndResourceToK8s(k8sNamespaceObj, yamlStr); |
||||
} catch (Exception e) { |
||||
logger.error("namespace create to k8s error", e); |
||||
putMsg(result, Status.K8S_CLIENT_OPS_ERROR, e.getMessage()); |
||||
return result; |
||||
} |
||||
|
||||
k8sNamespaceMapper.insert(k8sNamespaceObj); |
||||
putMsg(result, Status.SUCCESS); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* update K8s Namespace tag and resource limit |
||||
* |
||||
* @param loginUser login user |
||||
* @param owner owner |
||||
* @param tag Which type of job is available,such as flink,means only flink job can use, can be empty, all available |
||||
* @param limitsCpu max cpu |
||||
* @param limitsMemory max memory |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public Map<String, Object> updateK8sNamespace(User loginUser, int id, String owner, String tag, Double limitsCpu, Integer limitsMemory) { |
||||
Map<String, Object> result = new HashMap<>(); |
||||
if (isNotAdmin(loginUser, result)) { |
||||
return result; |
||||
} |
||||
|
||||
if (limitsCpu != null && limitsCpu < 0.0) { |
||||
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.LIMITS_CPU); |
||||
return result; |
||||
} |
||||
|
||||
if (limitsMemory != null && limitsMemory < 0) { |
||||
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.LIMITS_MEMORY); |
||||
return result; |
||||
} |
||||
|
||||
K8sNamespace k8sNamespaceObj = k8sNamespaceMapper.selectById(id); |
||||
if (k8sNamespaceObj == null) { |
||||
putMsg(result, Status.K8S_NAMESPACE_NOT_EXIST, id); |
||||
return result; |
||||
} |
||||
|
||||
Date now = new Date(); |
||||
k8sNamespaceObj.setTag(tag); |
||||
k8sNamespaceObj.setLimitsCpu(limitsCpu); |
||||
k8sNamespaceObj.setLimitsMemory(limitsMemory); |
||||
k8sNamespaceObj.setUpdateTime(now); |
||||
k8sNamespaceObj.setOwner(owner); |
||||
try { |
||||
String yamlStr = genDefaultResourceYaml(k8sNamespaceObj); |
||||
k8sClientService.upsertNamespaceAndResourceToK8s(k8sNamespaceObj, yamlStr); |
||||
} catch (Exception e) { |
||||
logger.error("namespace update to k8s error", e); |
||||
putMsg(result, Status.K8S_CLIENT_OPS_ERROR, e.getMessage()); |
||||
return result; |
||||
} |
||||
// update to db
|
||||
k8sNamespaceMapper.updateById(k8sNamespaceObj); |
||||
|
||||
putMsg(result, Status.SUCCESS); |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* verify namespace and k8s |
||||
* |
||||
* @param namespace namespace |
||||
* @param k8s k8s |
||||
* @return true if the k8s and namespace not exists, otherwise return false |
||||
*/ |
||||
@Override |
||||
public Result<Object> verifyNamespaceK8s(String namespace, String k8s) { |
||||
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); |
||||
return result; |
||||
} |
||||
|
||||
if (checkNamespaceExistInDb(namespace, k8s)) { |
||||
putMsg(result, Status.K8S_NAMESPACE_EXIST, namespace, k8s); |
||||
return result; |
||||
} |
||||
|
||||
putMsg(result, Status.SUCCESS); |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* delete namespace by id |
||||
* |
||||
* @param loginUser login user |
||||
* @param id namespace id |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public Map<String, Object> deleteNamespaceById(User loginUser, int id) { |
||||
Map<String, Object> result = new HashMap<>(); |
||||
if (isNotAdmin(loginUser, result)) { |
||||
return result; |
||||
} |
||||
|
||||
K8sNamespace k8sNamespaceObj = k8sNamespaceMapper.selectById(id); |
||||
if (k8sNamespaceObj == null) { |
||||
putMsg(result, Status.K8S_NAMESPACE_NOT_EXIST, id); |
||||
return result; |
||||
} |
||||
|
||||
k8sClientService.deleteNamespaceToK8s(k8sNamespaceObj.getNamespace(), k8sNamespaceObj.getK8s()); |
||||
k8sNamespaceMapper.deleteById(id); |
||||
putMsg(result, Status.SUCCESS); |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* check namespace name exist |
||||
* |
||||
* @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; |
||||
} |
||||
|
||||
/** |
||||
* use cpu memory create yaml |
||||
* |
||||
* @param k8sNamespace |
||||
* @return yaml file |
||||
*/ |
||||
private String genDefaultResourceYaml(K8sNamespace k8sNamespace) { |
||||
//resource use same name with namespace
|
||||
String name = k8sNamespace.getNamespace(); |
||||
String namespace = k8sNamespace.getNamespace(); |
||||
String cpuStr = null; |
||||
if (k8sNamespace.getLimitsCpu() != null) { |
||||
cpuStr = k8sNamespace.getLimitsCpu() + ""; |
||||
} |
||||
|
||||
String memoryStr = null; |
||||
if (k8sNamespace.getLimitsMemory() != null) { |
||||
memoryStr = k8sNamespace.getLimitsMemory() + "Gi"; |
||||
} |
||||
|
||||
String result = resourceYaml.replace("${name}", name) |
||||
.replace("${namespace}", namespace); |
||||
if (cpuStr == null) { |
||||
result = result.replace("${limitCpu}", ""); |
||||
} else { |
||||
result = result.replace("${limitCpu}", "limits.cpu: '" + cpuStr + "'"); |
||||
} |
||||
|
||||
if (memoryStr == null) { |
||||
result = result.replace("${limitMemory}", ""); |
||||
} else { |
||||
result = result.replace("${limitMemory}", "limits.memory: " + memoryStr); |
||||
} |
||||
return result; |
||||
} |
||||
} |
@ -0,0 +1,155 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.api.controller; |
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; |
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; |
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
import org.apache.dolphinscheduler.api.enums.Status; |
||||
import org.apache.dolphinscheduler.api.utils.Result; |
||||
import org.apache.dolphinscheduler.common.utils.JSONUtils; |
||||
import org.apache.dolphinscheduler.dao.entity.User; |
||||
|
||||
import org.junit.Assert; |
||||
import org.junit.Test; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.test.web.servlet.MvcResult; |
||||
import org.springframework.util.LinkedMultiValueMap; |
||||
import org.springframework.util.MultiValueMap; |
||||
|
||||
/** |
||||
* k8s namespace controller test |
||||
*/ |
||||
public class K8sNamespaceControllerTest extends AbstractControllerTest { |
||||
|
||||
private static final String NAMESPACE_CREATE_STRING = "namespace1"; |
||||
private static final Logger logger = LoggerFactory.getLogger(K8sNamespaceControllerTest.class); |
||||
protected User user; |
||||
|
||||
@Test |
||||
public void queryProjectListPaging() throws Exception { |
||||
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>(); |
||||
paramsMap.add("searchVal", ""); |
||||
paramsMap.add("pageNo", "1"); |
||||
paramsMap.add("pageSize", "20"); |
||||
|
||||
MvcResult mvcResult = mockMvc.perform(get("/k8s-namespace") |
||||
.header(SESSION_ID, sessionId) |
||||
.params(paramsMap)) |
||||
.andExpect(status().isOk()) |
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) |
||||
.andReturn(); |
||||
Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); |
||||
Assert.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); |
||||
} |
||||
|
||||
@Test |
||||
public void createNamespace() throws Exception { |
||||
|
||||
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>(); |
||||
paramsMap.add("namespace", NAMESPACE_CREATE_STRING); |
||||
paramsMap.add("k8s", "k8s"); |
||||
|
||||
MvcResult mvcResult = mockMvc.perform(post("/k8s-namespace") |
||||
.header(SESSION_ID, sessionId) |
||||
.params(paramsMap)) |
||||
.andExpect(status().isCreated()) //it can
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) |
||||
.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
|
||||
logger.info("create queue return result:{}", mvcResult.getResponse().getContentAsString()); |
||||
} |
||||
|
||||
@Test |
||||
public void updateNamespace() throws Exception { |
||||
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>(); |
||||
paramsMap.add("id", "1"); |
||||
paramsMap.add("owner", "owmer1"); |
||||
paramsMap.add("tag", "flink"); |
||||
|
||||
MvcResult mvcResult = mockMvc.perform(put("/k8s-namespace/{id}", 1) |
||||
.header(SESSION_ID, sessionId) |
||||
.params(paramsMap)) |
||||
.andExpect(status().isCreated()) |
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) |
||||
.andReturn(); |
||||
Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); |
||||
Assert.assertEquals(Status.K8S_CLIENT_OPS_ERROR.getCode(), result.getCode().intValue()); |
||||
logger.info("update queue return result:{}", mvcResult.getResponse().getContentAsString()); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyNamespace() throws Exception { |
||||
// queue value exist
|
||||
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>(); |
||||
paramsMap.add("namespace", NAMESPACE_CREATE_STRING); |
||||
paramsMap.add("k8s", "default"); |
||||
|
||||
// success
|
||||
|
||||
MvcResult mvcResult = mockMvc.perform(post("/k8s-namespace/verify") |
||||
.header(SESSION_ID, sessionId) |
||||
.params(paramsMap)) |
||||
.andExpect(status().isOk()) |
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) |
||||
.andReturn(); |
||||
Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); |
||||
Assert.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); |
||||
logger.info(mvcResult.getResponse().getContentAsString()); |
||||
logger.info("verify namespace return result:{}", mvcResult.getResponse().getContentAsString()); |
||||
|
||||
//error
|
||||
paramsMap.clear(); |
||||
paramsMap.add("namespace", null); |
||||
paramsMap.add("k8s", "default"); |
||||
mvcResult = mockMvc.perform(post("/k8s-namespace/verify") |
||||
.header(SESSION_ID, sessionId) |
||||
.params(paramsMap)) |
||||
.andExpect(status().isOk()) |
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) |
||||
.andReturn(); |
||||
result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); |
||||
Assert.assertEquals(Status.VERIFY_K8S_NAMESPACE_ERROR.getCode(), result.getCode().intValue()); |
||||
logger.info(mvcResult.getResponse().getContentAsString()); |
||||
logger.info("verify namespace return result:{}", mvcResult.getResponse().getContentAsString()); |
||||
} |
||||
|
||||
@Test |
||||
public void delNamespaceById() throws Exception { |
||||
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>(); |
||||
paramsMap.add("id", "1"); |
||||
|
||||
MvcResult mvcResult = mockMvc.perform(post("/k8s-namespace/delete") |
||||
.header(SESSION_ID, sessionId) |
||||
.params(paramsMap)) |
||||
.andExpect(status().isOk()) |
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) |
||||
.andReturn(); |
||||
|
||||
Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); |
||||
Assert.assertEquals(Status.DELETE_K8S_NAMESPACE_BY_ID_ERROR.getCode(), result.getCode().intValue());//there is no k8s cluster in test env
|
||||
logger.info(mvcResult.getResponse().getContentAsString()); |
||||
} |
||||
} |
@ -0,0 +1,189 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.api.service; |
||||
|
||||
import org.apache.dolphinscheduler.api.enums.Status; |
||||
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.UserType; |
||||
import org.apache.dolphinscheduler.dao.entity.K8sNamespace; |
||||
import org.apache.dolphinscheduler.dao.entity.User; |
||||
import org.apache.dolphinscheduler.dao.mapper.K8sNamespaceMapper; |
||||
import org.apache.dolphinscheduler.dao.mapper.UserMapper; |
||||
import org.apache.dolphinscheduler.service.k8s.K8sClientService; |
||||
|
||||
import org.apache.commons.collections.CollectionUtils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Assert; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.InjectMocks; |
||||
import org.mockito.Mock; |
||||
import org.mockito.Mockito; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class K8sNameSpaceServiceTest { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(K8sNameSpaceServiceTest.class); |
||||
|
||||
@InjectMocks |
||||
private K8sNameSpaceServiceImpl k8sNameSpaceService; |
||||
|
||||
@Mock |
||||
private K8sNamespaceMapper k8sNamespaceMapper; |
||||
|
||||
@Mock |
||||
private K8sClientService k8sClientService; |
||||
|
||||
@Mock |
||||
private UserMapper userMapper; |
||||
|
||||
private String namespace = "default"; |
||||
private String k8s = "default"; |
||||
|
||||
@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); |
||||
} |
||||
|
||||
@After |
||||
public void tearDown() throws Exception { |
||||
} |
||||
|
||||
@Test |
||||
public void queryListPaging() { |
||||
IPage<K8sNamespace> page = new Page<>(1, 10); |
||||
page.setTotal(1L); |
||||
page.setRecords(getNamespaceList()); |
||||
Mockito.when(k8sNamespaceMapper.queryK8sNamespacePaging(Mockito.any(Page.class), Mockito.eq(namespace))).thenReturn(page); |
||||
Result result = k8sNameSpaceService.queryListPaging(getLoginUser(), namespace, 1, 10); |
||||
logger.info(result.toString()); |
||||
PageInfo<K8sNamespace> pageInfo = (PageInfo<K8sNamespace>) result.getData(); |
||||
Assert.assertTrue(CollectionUtils.isNotEmpty(pageInfo.getTotalList())); |
||||
} |
||||
|
||||
@Test |
||||
public void createK8sNamespace() { |
||||
// namespace is null
|
||||
Map<String, Object> result = k8sNameSpaceService.createK8sNamespace(getLoginUser(), null, k8s, null, "tag", 10.0, 100); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS)); |
||||
// k8s is null
|
||||
result = k8sNameSpaceService.createK8sNamespace(getLoginUser(), namespace, null, null, "tag", 10.0, 100); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS)); |
||||
// correct
|
||||
result = k8sNameSpaceService.createK8sNamespace(getLoginUser(), namespace, k8s, null, "tag", 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, "tag", null, null); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); |
||||
} |
||||
|
||||
@Test |
||||
public void updateK8sNamespace() { |
||||
Mockito.when(k8sNamespaceMapper.selectById(1)).thenReturn(getNamespace()); |
||||
|
||||
Map<String, Object> result = k8sNameSpaceService.updateK8sNamespace(getLoginUser(), 1, null, "tag", null, null); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); |
||||
|
||||
result = k8sNameSpaceService.updateK8sNamespace(getLoginUser(), 1, null, "tag", -1.0, 100); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS)); |
||||
|
||||
result = k8sNameSpaceService.updateK8sNamespace(getLoginUser(), 1, null, "tag", 1.0, 100); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyNamespaceK8s() { |
||||
|
||||
Mockito.when(k8sNamespaceMapper.existNamespace(namespace, k8s)).thenReturn(true); |
||||
|
||||
//namespace null
|
||||
Result result = k8sNameSpaceService.verifyNamespaceK8s(null, k8s); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(result.getCode().intValue(), Status.REQUEST_PARAMS_NOT_VALID_ERROR.getCode()); |
||||
|
||||
//k8s null
|
||||
result = k8sNameSpaceService.verifyNamespaceK8s(namespace, null); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(result.getCode().intValue(), Status.REQUEST_PARAMS_NOT_VALID_ERROR.getCode()); |
||||
|
||||
//exist
|
||||
result = k8sNameSpaceService.verifyNamespaceK8s(namespace, k8s); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(result.getCode().intValue(), Status.K8S_NAMESPACE_EXIST.getCode()); |
||||
|
||||
//not exist
|
||||
result = k8sNameSpaceService.verifyNamespaceK8s(namespace, "other k8s"); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(result.getCode().intValue(), Status.SUCCESS.getCode()); |
||||
} |
||||
|
||||
@Test |
||||
public void deleteNamespaceById() { |
||||
Mockito.when(k8sNamespaceMapper.deleteById(Mockito.any())).thenReturn(1); |
||||
Mockito.when(k8sNamespaceMapper.selectById(1)).thenReturn(getNamespace()); |
||||
|
||||
Map<String, Object> result = k8sNameSpaceService.deleteNamespaceById(getLoginUser(), 1); |
||||
logger.info(result.toString()); |
||||
Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); |
||||
} |
||||
|
||||
private User getLoginUser() { |
||||
|
||||
User loginUser = new User(); |
||||
loginUser.setUserType(UserType.ADMIN_USER); |
||||
loginUser.setId(99999999); |
||||
return loginUser; |
||||
} |
||||
|
||||
private K8sNamespace getNamespace() { |
||||
K8sNamespace k8sNamespace = new K8sNamespace(); |
||||
k8sNamespace.setId(1); |
||||
k8sNamespace.setK8s(k8s); |
||||
k8sNamespace.setNamespace(namespace); |
||||
return k8sNamespace; |
||||
} |
||||
|
||||
private List<K8sNamespace> getNamespaceList() { |
||||
List<K8sNamespace> k8sNamespaceList = new ArrayList<>(); |
||||
k8sNamespaceList.add(getNamespace()); |
||||
return k8sNamespaceList; |
||||
} |
||||
} |
@ -0,0 +1,103 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.entity; |
||||
|
||||
import java.util.Date; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
||||
/** |
||||
* multi-data centre k8s temporary structure, waiting for new feature to complete will switch |
||||
*/ |
||||
@TableName("t_ds_k8s") |
||||
public class K8s { |
||||
/** |
||||
* id |
||||
*/ |
||||
@TableId(value = "id", type = IdType.AUTO) |
||||
private int id; |
||||
/** |
||||
* k8s name |
||||
*/ |
||||
@TableField(value = "k8s_name") |
||||
private String k8sName; |
||||
/** |
||||
* k8s client config(yaml or json) |
||||
*/ |
||||
@TableField(value = "k8s_config") |
||||
private String k8sConfig; |
||||
|
||||
/** |
||||
* create_time |
||||
*/ |
||||
@TableField("create_time") |
||||
private Date createTime; |
||||
/** |
||||
* update_time |
||||
*/ |
||||
@TableField("update_time") |
||||
private Date updateTime; |
||||
|
||||
|
||||
public K8s() { |
||||
|
||||
} |
||||
|
||||
public int getId() { |
||||
return id; |
||||
} |
||||
|
||||
public void setId(int id) { |
||||
this.id = id; |
||||
} |
||||
|
||||
public String getK8sName() { |
||||
return k8sName; |
||||
} |
||||
|
||||
public void setK8sName(String k8sName) { |
||||
this.k8sName = k8sName; |
||||
} |
||||
|
||||
public String getK8sConfig() { |
||||
return k8sConfig; |
||||
} |
||||
|
||||
public void setK8sConfig(String k8sConfig) { |
||||
this.k8sConfig = k8sConfig; |
||||
} |
||||
|
||||
public Date getCreateTime() { |
||||
return createTime; |
||||
} |
||||
|
||||
public void setCreateTime(Date createTime) { |
||||
this.createTime = createTime; |
||||
} |
||||
|
||||
public Date getUpdateTime() { |
||||
return updateTime; |
||||
} |
||||
|
||||
public void setUpdateTime(Date updateTime) { |
||||
this.updateTime = updateTime; |
||||
} |
||||
} |
@ -0,0 +1,197 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.entity; |
||||
|
||||
import java.util.Date; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
||||
/** |
||||
* k8s namespace |
||||
*/ |
||||
@TableName("t_ds_k8s_namespace") |
||||
public class K8sNamespace { |
||||
@TableId(value = "id", type = IdType.AUTO) |
||||
private Integer id; |
||||
|
||||
/** |
||||
* namespace name |
||||
*/ |
||||
@TableField(value = "namespace") |
||||
private String namespace; |
||||
/** |
||||
* total cpu limit |
||||
*/ |
||||
@TableField(value = "limits_cpu") |
||||
private Double limitsCpu; |
||||
/** |
||||
* total memory limit,mi |
||||
*/ |
||||
private Integer limitsMemory; |
||||
/** |
||||
* owner |
||||
*/ |
||||
@TableField(value = "owner") |
||||
private String owner; |
||||
|
||||
/** |
||||
* create_time |
||||
*/ |
||||
@TableField("create_time") |
||||
private Date createTime; |
||||
/** |
||||
* update_time |
||||
*/ |
||||
@TableField("update_time") |
||||
private Date updateTime; |
||||
/** |
||||
* tag use for set this namespace allow run which type |
||||
*/ |
||||
@TableField("tag") |
||||
private String tag; |
||||
|
||||
@TableField("pod_request_cpu") |
||||
private Double podRequestCpu = 0.0; |
||||
/** |
||||
* Mi |
||||
*/ |
||||
@TableField("pod_request_memory") |
||||
private Integer podRequestMemory = 0; |
||||
/** |
||||
* |
||||
*/ |
||||
@TableField("pod_replicas") |
||||
private Integer podReplicas = 0; |
||||
/** |
||||
* online job |
||||
*/ |
||||
@TableField("online_job_num") |
||||
private Integer onlineJobNum = 0; |
||||
/** |
||||
* k8s name |
||||
*/ |
||||
@TableField("k8s") |
||||
private String k8s; |
||||
|
||||
public Integer getId() { |
||||
return id; |
||||
} |
||||
|
||||
public void setId(int id) { |
||||
this.id = id; |
||||
} |
||||
|
||||
public String getNamespace() { |
||||
return namespace; |
||||
} |
||||
|
||||
public void setNamespace(String namespace) { |
||||
this.namespace = namespace; |
||||
} |
||||
|
||||
public Double getLimitsCpu() { |
||||
return limitsCpu; |
||||
} |
||||
|
||||
public void setLimitsCpu(Double limitsCpu) { |
||||
this.limitsCpu = limitsCpu; |
||||
} |
||||
|
||||
public Integer getLimitsMemory() { |
||||
return limitsMemory; |
||||
} |
||||
|
||||
public void setLimitsMemory(Integer limitsMemory) { |
||||
this.limitsMemory = limitsMemory; |
||||
} |
||||
|
||||
public String getOwner() { |
||||
return owner; |
||||
} |
||||
|
||||
public void setOwner(String owner) { |
||||
this.owner = owner; |
||||
} |
||||
|
||||
public Date getCreateTime() { |
||||
return createTime; |
||||
} |
||||
|
||||
public void setCreateTime(Date createTime) { |
||||
this.createTime = createTime; |
||||
} |
||||
|
||||
public Date getUpdateTime() { |
||||
return updateTime; |
||||
} |
||||
|
||||
public void setUpdateTime(Date updateTime) { |
||||
this.updateTime = updateTime; |
||||
} |
||||
|
||||
public String getTag() { |
||||
return tag; |
||||
} |
||||
|
||||
public void setTag(String tag) { |
||||
this.tag = tag; |
||||
} |
||||
|
||||
public Integer getPodRequestMemory() { |
||||
return podRequestMemory; |
||||
} |
||||
|
||||
public void setPodRequestMemory(Integer podRequestMemory) { |
||||
this.podRequestMemory = podRequestMemory; |
||||
} |
||||
|
||||
public Integer getPodReplicas() { |
||||
return podReplicas; |
||||
} |
||||
|
||||
public void setPodReplicas(Integer podReplicas) { |
||||
this.podReplicas = podReplicas; |
||||
} |
||||
|
||||
public Integer getOnlineJobNum() { |
||||
return onlineJobNum; |
||||
} |
||||
|
||||
public void setOnlineJobNum(Integer onlineJobNum) { |
||||
this.onlineJobNum = onlineJobNum; |
||||
} |
||||
|
||||
public String getK8s() { |
||||
return k8s; |
||||
} |
||||
|
||||
public void setK8s(String k8s) { |
||||
this.k8s = k8s; |
||||
} |
||||
|
||||
public Double getPodRequestCpu() { |
||||
return podRequestCpu; |
||||
} |
||||
|
||||
public void setPodRequestCpu(Double podRequestCpu) { |
||||
this.podRequestCpu = podRequestCpu; |
||||
} |
||||
} |
@ -0,0 +1,29 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.mapper; |
||||
|
||||
import org.apache.dolphinscheduler.dao.entity.K8s; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
||||
/** |
||||
* k8s mapper interface
|
||||
*/ |
||||
public interface K8sMapper extends BaseMapper<K8s> { |
||||
|
||||
} |
@ -0,0 +1,49 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.mapper; |
||||
|
||||
import org.apache.dolphinscheduler.dao.entity.K8sNamespace; |
||||
|
||||
import org.apache.ibatis.annotations.Param; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
|
||||
/** |
||||
* namespace interface
|
||||
*/ |
||||
public interface K8sNamespaceMapper extends BaseMapper<K8sNamespace> { |
||||
/** |
||||
* k8s namespace page |
||||
* |
||||
* @param page page |
||||
* @param searchVal searchVal |
||||
* @return k8s namespace IPage |
||||
*/ |
||||
IPage<K8sNamespace> queryK8sNamespacePaging(IPage<K8sNamespace> page, |
||||
@Param("searchVal") String searchVal); |
||||
|
||||
/** |
||||
* check the target namespace exist |
||||
* |
||||
* @param namespace namespace |
||||
* @param k8s k8s name |
||||
* @return true if exist else return null |
||||
*/ |
||||
Boolean existNamespace(@Param("namespace") String namespace, @Param("k8s") String k8s); |
||||
} |
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!-- |
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
||||
~ contributor license agreements. See the NOTICE file distributed with |
||||
~ this work for additional information regarding copyright ownership. |
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
~ (the "License"); you may not use this file except in compliance with |
||||
~ the License. You may obtain a copy of the License at |
||||
~ |
||||
~ http://www.apache.org/licenses/LICENSE-2.0 |
||||
~ |
||||
~ Unless required by applicable law or agreed to in writing, software |
||||
~ distributed under the License is distributed on an "AS IS" BASIS, |
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
~ See the License for the specific language governing permissions and |
||||
~ limitations under the License. |
||||
--> |
||||
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
<mapper namespace="org.apache.dolphinscheduler.dao.mapper.K8sNamespaceMapper"> |
||||
|
||||
<sql id="baseSql"> |
||||
id, namespace, k8s, owner, tag, limits_memory, limits_cpu, online_job_num, pod_replicas, pod_request_cpu, pod_request_memory, create_time, update_time |
||||
</sql> |
||||
<select id="queryK8sNamespacePaging" resultType="org.apache.dolphinscheduler.dao.entity.K8sNamespace"> |
||||
select |
||||
<include refid="baseSql"/> |
||||
from t_ds_k8s_namespace |
||||
where 1= 1 |
||||
<if test="searchVal != null and searchVal != ''"> |
||||
and namespace like concat('%', #{searchVal}, '%') |
||||
</if> |
||||
order by update_time desc |
||||
</select> |
||||
|
||||
<select id="existNamespace" resultType="java.lang.Boolean"> |
||||
select 1 = 1 |
||||
from t_ds_k8s_namespace |
||||
where 1 = 1 |
||||
<if test="namespace != null and namespace != ''"> |
||||
and namespace = #{namespace} |
||||
</if> |
||||
<if test="k8s != null and k8s != ''"> |
||||
and k8s =#{k8s} |
||||
</if> |
||||
</select> |
||||
|
||||
</mapper> |
@ -0,0 +1,24 @@
|
||||
Copyright (c) 2001-2022 Anders Moeller |
||||
All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions |
||||
are met: |
||||
1. Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
2. Redistributions in binary form must reproduce the above copyright |
||||
notice, this list of conditions and the following disclaimer in the |
||||
documentation and/or other materials provided with the distribution. |
||||
3. The name of the author may not be used to endorse or promote products |
||||
derived from this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,94 @@
|
||||
/* |
||||
* Licensed to Apache Software Foundation (ASF) under one or more contributor |
||||
* license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright |
||||
* ownership. Apache Software Foundation (ASF) licenses this file to you under |
||||
* the Apache License, Version 2.0 (the "License"); you may |
||||
* not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, |
||||
* software distributed under the License is distributed on an |
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
* KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations |
||||
* under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.e2e.pages.security; |
||||
|
||||
import org.apache.dolphinscheduler.e2e.pages.common.NavBarPage; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.openqa.selenium.By; |
||||
import org.openqa.selenium.WebElement; |
||||
import org.openqa.selenium.remote.RemoteWebDriver; |
||||
import org.openqa.selenium.support.FindBy; |
||||
import org.openqa.selenium.support.PageFactory; |
||||
|
||||
import lombok.Getter; |
||||
|
||||
@Getter |
||||
public final class NamespacePage extends NavBarPage implements SecurityPage.Tab { |
||||
@FindBy(id = "btnCreateNamespace") |
||||
private WebElement buttonCreateNamespace; |
||||
|
||||
@FindBy(className = "items") |
||||
private List<WebElement> namespaceList; |
||||
|
||||
private final NamespaceForm createNamespaceForm; |
||||
private final NamespaceForm editNamespaceForm; |
||||
|
||||
public NamespacePage(RemoteWebDriver driver) { |
||||
super(driver); |
||||
createNamespaceForm = new NamespaceForm(); |
||||
editNamespaceForm = new NamespaceForm(); |
||||
} |
||||
|
||||
public NamespacePage create(String namespaceName, String namespaceValue) { |
||||
buttonCreateNamespace().click(); |
||||
createNamespaceForm().inputNamespaceName().sendKeys(namespaceName); |
||||
createNamespaceForm().inputNamespaceValue().sendKeys(namespaceValue); |
||||
createNamespaceForm().buttonSubmit().click(); |
||||
return this; |
||||
} |
||||
|
||||
public NamespacePage update(String namespaceName, String editNamespaceName, String editNamespaceValue) { |
||||
namespaceList() |
||||
.stream() |
||||
.filter(it -> it.findElement(By.className("namespaceName")).getAttribute("innerHTML").contains(namespaceName)) |
||||
.flatMap(it -> it.findElements(By.className("edit")).stream()) |
||||
.filter(WebElement::isDisplayed) |
||||
.findFirst() |
||||
.orElseThrow(() -> new RuntimeException("No edit button in namespace list")) |
||||
.click(); |
||||
|
||||
editNamespaceForm().inputNamespaceName().sendKeys(editNamespaceName); |
||||
editNamespaceForm().inputNamespaceValue().sendKeys(editNamespaceValue); |
||||
editNamespaceForm().buttonSubmit().click(); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
@Getter |
||||
public class NamespaceForm { |
||||
NamespaceForm() { |
||||
PageFactory.initElements(driver, this); |
||||
} |
||||
|
||||
@FindBy(id = "inputNamespaceName") |
||||
private WebElement inputNamespaceName; |
||||
|
||||
@FindBy(id = "inputNamespaceValue") |
||||
private WebElement inputNamespaceValue; |
||||
|
||||
@FindBy(id = "btnSubmit") |
||||
private WebElement buttonSubmit; |
||||
|
||||
@FindBy(id = "btnCancel") |
||||
private WebElement buttonCancel; |
||||
} |
||||
} |
@ -0,0 +1,118 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.service.k8s; |
||||
|
||||
import org.apache.dolphinscheduler.dao.entity.K8sNamespace; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Component; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
|
||||
import io.fabric8.kubernetes.api.model.Namespace; |
||||
import io.fabric8.kubernetes.api.model.NamespaceList; |
||||
import io.fabric8.kubernetes.api.model.ObjectMeta; |
||||
import io.fabric8.kubernetes.api.model.ResourceQuota; |
||||
import io.fabric8.kubernetes.client.KubernetesClient; |
||||
|
||||
/** |
||||
* Encapsulates all client-related operations, not involving the db |
||||
*/ |
||||
@Component |
||||
public class K8sClientService { |
||||
|
||||
private static Yaml yaml = new Yaml(); |
||||
@Autowired |
||||
private K8sManager k8sManager; |
||||
|
||||
public ResourceQuota upsertNamespaceAndResourceToK8s(K8sNamespace k8sNamespace, String yamlStr) { |
||||
upsertNamespaceToK8s(k8sNamespace.getNamespace(), k8sNamespace.getK8s()); |
||||
return upsertNamespacedResourceToK8s(k8sNamespace, yamlStr); |
||||
} |
||||
|
||||
public Optional<Namespace> deleteNamespaceToK8s(String name, String k8s) { |
||||
Optional<Namespace> result = getNamespaceFromK8s(name, k8s); |
||||
if (result.isPresent()) { |
||||
KubernetesClient client = k8sManager.getK8sClient(k8s); |
||||
Namespace body = new Namespace(); |
||||
ObjectMeta meta = new ObjectMeta(); |
||||
meta.setNamespace(name); |
||||
meta.setName(name); |
||||
body.setMetadata(meta); |
||||
client.namespaces().delete(body); |
||||
} |
||||
return getNamespaceFromK8s(name, k8s); |
||||
} |
||||
|
||||
private ResourceQuota upsertNamespacedResourceToK8s(K8sNamespace k8sNamespace, String yamlStr) { |
||||
|
||||
KubernetesClient client = k8sManager.getK8sClient(k8sNamespace.getK8s()); |
||||
|
||||
//创建资源
|
||||
ResourceQuota queryExist = client.resourceQuotas() |
||||
.inNamespace(k8sNamespace.getNamespace()) |
||||
.withName(k8sNamespace.getNamespace()) |
||||
.get(); |
||||
|
||||
|
||||
ResourceQuota body = yaml.loadAs(yamlStr, ResourceQuota.class); |
||||
|
||||
if (queryExist != null) { |
||||
if (k8sNamespace.getLimitsCpu() == null && k8sNamespace.getLimitsMemory() == null) { |
||||
client.resourceQuotas().inNamespace(k8sNamespace.getNamespace()) |
||||
.withName(k8sNamespace.getNamespace()) |
||||
.delete(); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
return client.resourceQuotas().inNamespace(k8sNamespace.getNamespace()) |
||||
.withName(k8sNamespace.getNamespace()) |
||||
.createOrReplace(body); |
||||
} |
||||
|
||||
private Optional<Namespace> getNamespaceFromK8s(String name, String k8s) { |
||||
NamespaceList listNamespace = |
||||
k8sManager.getK8sClient(k8s).namespaces().list(); |
||||
|
||||
Optional<Namespace> list = |
||||
listNamespace.getItems().stream() |
||||
.filter((Namespace namespace) -> |
||||
namespace.getMetadata().getName().equals(name)) |
||||
.findFirst(); |
||||
|
||||
return list; |
||||
} |
||||
|
||||
private Namespace upsertNamespaceToK8s(String name, String k8s) { |
||||
Optional<Namespace> result = getNamespaceFromK8s(name, k8s); |
||||
//if not exist create
|
||||
if (!result.isPresent()) { |
||||
KubernetesClient client = k8sManager.getK8sClient(k8s); |
||||
Namespace body = new Namespace(); |
||||
ObjectMeta meta = new ObjectMeta(); |
||||
meta.setNamespace(name); |
||||
meta.setName(name); |
||||
body.setMetadata(meta); |
||||
return client.namespaces().create(body); |
||||
} |
||||
return result.get(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,86 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.service.k8s; |
||||
|
||||
import org.apache.dolphinscheduler.dao.entity.K8s; |
||||
import org.apache.dolphinscheduler.dao.mapper.K8sMapper; |
||||
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 |
||||
*/ |
||||
@Component |
||||
public class K8sManager { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(K8sManager.class); |
||||
/** |
||||
* cache k8s client |
||||
*/ |
||||
private static Map<String, KubernetesClient> clientMap = new Hashtable<>(); |
||||
|
||||
@Autowired |
||||
private K8sMapper k8sMapper; |
||||
|
||||
public KubernetesClient getK8sClient(String k8sName) { |
||||
if (null == k8sName) { |
||||
return null; |
||||
} |
||||
return clientMap.get(k8sName); |
||||
} |
||||
|
||||
|
||||
@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 DefaultKubernetesClient getClient(String configYaml) throws RemotingException { |
||||
try { |
||||
Config config = Config.fromKubeconfig(configYaml); |
||||
return new DefaultKubernetesClient(config); |
||||
} catch (Exception e) { |
||||
logger.error("fail to get k8s ApiClient", e); |
||||
throw new RemotingException("fail to get k8s ApiClient:" + e.getMessage()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,78 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.service.k8s; |
||||
|
||||
import org.apache.dolphinscheduler.dao.entity.K8s; |
||||
import org.apache.dolphinscheduler.dao.mapper.K8sMapper; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Assert; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.InjectMocks; |
||||
import org.mockito.Mock; |
||||
import org.mockito.Mockito; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
import io.fabric8.kubernetes.client.KubernetesClient; |
||||
|
||||
@RunWith(MockitoJUnitRunner.Silent.class) |
||||
public class K8sManagerTest { |
||||
|
||||
@InjectMocks |
||||
private K8sManager k8sManager; |
||||
|
||||
@Mock |
||||
private K8sMapper k8sMapper; |
||||
|
||||
@Before |
||||
public void setUp() throws Exception { |
||||
} |
||||
|
||||
@After |
||||
public void tearDown() throws Exception { |
||||
} |
||||
|
||||
@Test |
||||
public void getK8sClient() { |
||||
Mockito.when(k8sMapper.selectList(Mockito.any())).thenReturn(getK8sList()); |
||||
|
||||
KubernetesClient result = k8sManager.getK8sClient("must null"); |
||||
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 List<K8s> getK8sList() { |
||||
List<K8s> k8sList = new ArrayList<>(); |
||||
k8sList.add(getK8s()); |
||||
return k8sList; |
||||
} |
||||
} |
@ -0,0 +1,219 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
<template> |
||||
<m-popover |
||||
ref="popover" |
||||
:ok-text="item ? $t('Edit') : $t('Submit')" |
||||
@ok="_ok" |
||||
@close="close"> |
||||
<template slot="content"> |
||||
<div class="create-tenement-model"> |
||||
<m-list-box-f> |
||||
<template slot="name"><strong>*</strong>{{$t('Name')}}</template> |
||||
<template slot="content"> |
||||
<el-input |
||||
type="input" |
||||
v-model="namespace" |
||||
maxlength="60" |
||||
size="mini" |
||||
:disabled="item ? true: false" |
||||
:placeholder="$t('Please enter name')"> |
||||
</el-input> |
||||
</template> |
||||
</m-list-box-f> |
||||
<m-list-box-f> |
||||
<template slot="name"><strong>*</strong>{{$t('K8s Cluster')}}</template> |
||||
<template slot="content"> |
||||
<el-input |
||||
type="input" |
||||
v-model="k8s" |
||||
maxlength="60" |
||||
size="mini" |
||||
:disabled="item ? true: false" |
||||
:placeholder="$t('Please enter k8s cluster')"> |
||||
</el-input> |
||||
</template> |
||||
</m-list-box-f> |
||||
<m-list-box-f> |
||||
<template slot="name">{{$t('K8s Tag')}}</template> |
||||
<template slot="content"> |
||||
<el-input |
||||
type="input" |
||||
v-model="tag" |
||||
maxlength="60" |
||||
size="mini" |
||||
:placeholder="$t('Please enter k8s cluster')"> |
||||
</el-input> |
||||
</template> |
||||
</m-list-box-f> |
||||
|
||||
<m-list-box-f> |
||||
<template slot="name">{{$t('Limits Cpu')}}</template> |
||||
<template slot="content"> |
||||
<el-input |
||||
v-model="limitsCpu" |
||||
size="small" |
||||
> |
||||
<template slot="append">Core</template> |
||||
</el-input> |
||||
</template> |
||||
</m-list-box-f> |
||||
|
||||
<m-list-box-f> |
||||
<template slot="name">{{$t('Limits Memory')}}</template> |
||||
<template slot="content"> |
||||
<el-input |
||||
v-model="limitsMemory" |
||||
size="small" |
||||
> |
||||
<template slot="append">GB</template> |
||||
</el-input> |
||||
</template> |
||||
</m-list-box-f> |
||||
|
||||
<m-list-box-f> |
||||
<template slot="name">{{$t('Namespace Owner')}}</template> |
||||
<template slot="content"> |
||||
<el-input |
||||
type="input" |
||||
v-model="owner" |
||||
maxlength="60" |
||||
size="mini" |
||||
:placeholder="$t('Please enter owner')"> |
||||
</el-input> |
||||
</template> |
||||
</m-list-box-f> |
||||
|
||||
</div> |
||||
</template> |
||||
</m-popover> |
||||
</template> |
||||
<script> |
||||
import _ from 'lodash' |
||||
import i18n from '@/module/i18n' |
||||
import store from '@/conf/home/store' |
||||
import mPopover from '@/module/components/popup/popover' |
||||
import mListBoxF from '@/module/components/listBoxF/listBoxF' |
||||
|
||||
export default { |
||||
name: 'create-namespace', |
||||
data () { |
||||
return { |
||||
store, |
||||
namespace: '', |
||||
k8s: '', |
||||
owner: '', |
||||
tag: '', |
||||
limitsCpu: '', |
||||
limitsMemory: '' |
||||
} |
||||
}, |
||||
props: { |
||||
item: Object |
||||
}, |
||||
methods: { |
||||
_ok () { |
||||
if (!this._verification()) { |
||||
return |
||||
} |
||||
|
||||
let param = { |
||||
namespace: _.trim(this.namespace), |
||||
k8s: _.trim(this.k8s), |
||||
owner: _.trim(this.owner), |
||||
tag: _.trim(this.tag), |
||||
limitsCpu: _.trim(this.limitsCpu), |
||||
limitsMemory: _.trim(this.limitsMemory) |
||||
} |
||||
// edit |
||||
if (this.item) { |
||||
param.id = this.item.id |
||||
} |
||||
|
||||
let $then = (res) => { |
||||
this.$emit('onUpdate') |
||||
this.$message.success(res.msg) |
||||
this.$refs.popover.spinnerLoading = false |
||||
} |
||||
|
||||
let $catch = (e) => { |
||||
this.$message.error(e.msg || '') |
||||
this.$refs.popover.spinnerLoading = false |
||||
} |
||||
|
||||
if (this.item) { |
||||
this.$refs.popover.spinnerLoading = true |
||||
this.store.dispatch('security/updateNamespace', param).then(res => { |
||||
$then(res) |
||||
}).catch(e => { |
||||
$catch(e) |
||||
}) |
||||
} else { |
||||
this._verifyName(param).then(() => { |
||||
this.$refs.popover.spinnerLoading = true |
||||
this.store.dispatch('security/createNamespace', param).then(res => { |
||||
$then(res) |
||||
}).catch(e => { |
||||
$catch(e) |
||||
}) |
||||
}).catch(e => { |
||||
this.$message.error(e.msg || '') |
||||
}) |
||||
} |
||||
}, |
||||
_verification () { |
||||
if (!this.namespace.replace(/\s*/g, '')) { |
||||
this.$message.warning(`${i18n.$t('Please enter name')}`) |
||||
return false |
||||
} |
||||
if (!this.k8s.replace(/\s*/g, '')) { |
||||
this.$message.warning(`${i18n.$t('Please enter namespace')}`) |
||||
return false |
||||
} |
||||
return true |
||||
}, |
||||
_verifyName (param) { |
||||
return new Promise((resolve, reject) => { |
||||
this.store.dispatch('security/verifyNamespaceK8s', param).then(res => { |
||||
resolve() |
||||
}).catch(e => { |
||||
reject(e) |
||||
}) |
||||
}) |
||||
}, |
||||
close () { |
||||
this.$emit('close') |
||||
} |
||||
}, |
||||
watch: { |
||||
}, |
||||
created () { |
||||
if (this.item) { |
||||
this.namespace = this.item.namespace |
||||
this.k8s = this.item.k8s |
||||
this.owner = this.item.owner |
||||
this.tag = this.item.tag |
||||
this.limitsCpu = this.item.limitsCpu |
||||
this.limitsMemory = this.item.limitsMemory |
||||
} |
||||
}, |
||||
mounted () { |
||||
|
||||
}, |
||||
components: { mPopover, mListBoxF } |
||||
} |
||||
</script> |
@ -0,0 +1,110 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
<template> |
||||
<div class="list-model"> |
||||
<div class="table-box"> |
||||
<el-table :data="list" size="mini" style="width: 100%"> |
||||
<el-table-column type="index" :label="$t('#')" width="50"></el-table-column> |
||||
<el-table-column prop="namespace" :label="$t('K8s Namespace')"></el-table-column> |
||||
<el-table-column prop="k8s" :label="$t('K8s Cluster')"></el-table-column> |
||||
<el-table-column prop="owner" :label="$t('Namespace Owner')"></el-table-column> |
||||
<el-table-column prop="tag" :label="$t('K8s Tag')"></el-table-column> |
||||
<el-table-column prop="limitsCpu" :label="$t('Limits Cpu')"></el-table-column> |
||||
<el-table-column prop="limitsMemory" :label="$t('Limits Memory')"></el-table-column> |
||||
|
||||
<el-table-column :label="$t('Create Time')" min-width="120"> |
||||
<template slot-scope="scope"> |
||||
<span>{{scope.row.createTime | formatDate}}</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column :label="$t('Update Time')" min-width="120"> |
||||
<template slot-scope="scope"> |
||||
<span>{{scope.row.updateTime | formatDate}}</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column :label="$t('Operation')" width="100"> |
||||
<template slot-scope="scope"> |
||||
<el-tooltip :content="$t('Edit')" placement="top"> |
||||
<el-button type="primary" size="mini" icon="el-icon-edit-outline" @click="_edit(scope.row)" circle></el-button> |
||||
</el-tooltip> |
||||
<el-tooltip :content="$t('Delete')" placement="top"> |
||||
<el-popconfirm |
||||
:confirmButtonText="$t('Confirm')" |
||||
:cancelButtonText="$t('Cancel')" |
||||
icon="el-icon-info" |
||||
iconColor="red" |
||||
:title="$t('Delete?')" |
||||
@onConfirm="_delete(scope.row,scope.row.id)" |
||||
> |
||||
<el-button type="danger" size="mini" icon="el-icon-delete" circle slot="reference"></el-button> |
||||
</el-popconfirm> |
||||
</el-tooltip> |
||||
</template> |
||||
</el-table-column> |
||||
</el-table> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
import { mapActions } from 'vuex' |
||||
|
||||
export default { |
||||
name: 'namespace-list', |
||||
data () { |
||||
return { |
||||
list: [] |
||||
} |
||||
}, |
||||
props: { |
||||
namespaceList: Array, |
||||
pageNo: Number, |
||||
pageSize: Number |
||||
}, |
||||
methods: { |
||||
...mapActions('security', ['deleteNamespace']), |
||||
_delete (item, i) { |
||||
this.deleteNamespace({ |
||||
id: item.id |
||||
}).then(res => { |
||||
this.list.splice(i, 1) |
||||
this.$message.success(res.msg) |
||||
this.$emit('onUpdate') |
||||
}).catch(e => { |
||||
this.$message.error(e.msg || '') |
||||
}) |
||||
}, |
||||
_edit (item) { |
||||
this.$emit('on-edit', item) |
||||
} |
||||
}, |
||||
watch: { |
||||
namespaceList (a) { |
||||
console.warn(a) |
||||
this.list = [] |
||||
setTimeout(() => { |
||||
this.list = a |
||||
}) |
||||
} |
||||
}, |
||||
created () { |
||||
this.list = this.namespaceList |
||||
}, |
||||
mounted () { |
||||
}, |
||||
components: { } |
||||
} |
||||
</script> |
@ -0,0 +1,160 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
<template> |
||||
<m-list-construction :title="$t('Namespace manage')"> |
||||
<template slot="conditions"> |
||||
<m-conditions @on-conditions="_onConditions"> |
||||
<template slot="button-group" v-if="isADMIN"> |
||||
<el-button size="mini" @click="_create('')">{{$t('Create namespace')}}</el-button> |
||||
<el-dialog |
||||
:title="item ? $t('Edit namespace') : $t('Create namespace')" |
||||
v-if="createNamespaceDialog" |
||||
:visible.sync="createNamespaceDialog" |
||||
width="auto"> |
||||
<m-create-namespace :item="item" @onUpdate="onUpdate" @close="close"></m-create-namespace> |
||||
</el-dialog> |
||||
</template> |
||||
</m-conditions> |
||||
</template> |
||||
<template slot="content"> |
||||
<template v-if="namespaceList.length || total>0"> |
||||
<m-list @on-edit="_onEdit" |
||||
@onUpdate="onUpdate" |
||||
:namespace-list="namespaceList" |
||||
:page-no="searchParams.pageNo" |
||||
:page-size="searchParams.pageSize"> |
||||
|
||||
</m-list> |
||||
<div class="page-box"> |
||||
<el-pagination |
||||
background |
||||
@current-change="_page" |
||||
@size-change="_pageSize" |
||||
:page-size="searchParams.pageSize" |
||||
:current-page.sync="searchParams.pageNo" |
||||
:page-sizes="[10, 30, 50]" |
||||
layout="sizes, prev, pager, next, jumper" |
||||
:total="total"> |
||||
</el-pagination> |
||||
</div> |
||||
</template> |
||||
<template v-if="!namespaceList.length && total<=0"> |
||||
<m-no-data></m-no-data> |
||||
</template> |
||||
<m-spin :is-spin="isLoading" :is-left="isLeft"></m-spin> |
||||
</template> |
||||
</m-list-construction> |
||||
</template> |
||||
<script> |
||||
import _ from 'lodash' |
||||
import { mapActions } from 'vuex' |
||||
import mList from './_source/list' |
||||
import store from '@/conf/home/store' |
||||
import mSpin from '@/module/components/spin/spin' |
||||
import mCreateNamespace from './_source/createNamespace' |
||||
import mNoData from '@/module/components/noData/noData' |
||||
import listUrlParamHandle from '@/module/mixin/listUrlParamHandle' |
||||
import mConditions from '@/module/components/conditions/conditions' |
||||
import mListConstruction from '@/module/components/listConstruction/listConstruction' |
||||
|
||||
export default { |
||||
name: 'namespace-index', |
||||
data () { |
||||
return { |
||||
total: null, |
||||
isLoading: true, |
||||
namespaceList: [], |
||||
searchParams: { |
||||
pageSize: 10, |
||||
pageNo: 1, |
||||
searchVal: '' |
||||
}, |
||||
isLeft: true, |
||||
isADMIN: store.state.user.userInfo.userType === 'ADMIN_USER', |
||||
item: {}, |
||||
createNamespaceDialog: false |
||||
|
||||
} |
||||
}, |
||||
mixins: [listUrlParamHandle], |
||||
props: {}, |
||||
methods: { |
||||
...mapActions('security', ['getNamespaceListP']), |
||||
/** |
||||
* Query |
||||
*/ |
||||
_onConditions (o) { |
||||
this.searchParams = _.assign(this.searchParams, o) |
||||
this.searchParams.pageNo = 1 |
||||
}, |
||||
_page (val) { |
||||
this.searchParams.pageNo = val |
||||
}, |
||||
_pageSize (val) { |
||||
this.searchParams.pageSize = val |
||||
}, |
||||
_onEdit (item) { |
||||
this._create(item) |
||||
}, |
||||
_create (item) { |
||||
this.item = item |
||||
this.createNamespaceDialog = true |
||||
}, |
||||
onUpdate () { |
||||
this._debounceGET('false') |
||||
this.createNamespaceDialog = false |
||||
}, |
||||
close () { |
||||
this.createNamespaceDialog = false |
||||
}, |
||||
_getList (flag) { |
||||
if (sessionStorage.getItem('isLeft') === '0') { |
||||
this.isLeft = false |
||||
} else { |
||||
this.isLeft = true |
||||
} |
||||
this.isLoading = !flag |
||||
this.getNamespaceListP(this.searchParams).then(res => { |
||||
if (this.searchParams.pageNo > 1 && res.totalList.length === 0) { |
||||
this.searchParams.pageNo = this.searchParams.pageNo - 1 |
||||
} else { |
||||
this.namespaceList = res.totalList |
||||
this.total = res.total |
||||
this.isLoading = false |
||||
} |
||||
}).catch(e => { |
||||
this.isLoading = false |
||||
}) |
||||
} |
||||
}, |
||||
watch: { |
||||
// router |
||||
'$route' (a) { |
||||
// url no params get instance list |
||||
this.searchParams.pageNo = _.isEmpty(a.query) ? 1 : a.query.pageNo |
||||
} |
||||
}, |
||||
created () { |
||||
}, |
||||
mounted () { |
||||
}, |
||||
beforeDestroy () { |
||||
sessionStorage.setItem('isLeft', 1) |
||||
}, |
||||
components: { mList, mListConstruction, mConditions, mSpin, mNoData, mCreateNamespace } |
||||
} |
||||
</script> |
Loading…
Reference in new issue