feat(性能测试-xPack): 增加k8s资源池
This commit is contained in:
parent
0bd4675886
commit
e071f33dbd
|
@ -4,5 +4,5 @@ public enum ResourcePoolTypeEnum {
|
|||
/**
|
||||
* node controller 资源池
|
||||
*/
|
||||
NODE
|
||||
NODE, K8S
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package io.metersphere.controller;
|
|||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.TestResourcePool;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
|
@ -14,7 +13,6 @@ import org.apache.shiro.authz.annotation.RequiresRoles;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RequestMapping("testresourcepool")
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import io.metersphere.dto.TestResourcePoolDTO;
|
||||
|
||||
public interface KubernetesResourcePoolService {
|
||||
boolean validate(TestResourcePoolDTO testResourcePool);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.base.domain.TestResource;
|
||||
import io.metersphere.base.domain.TestResourceExample;
|
||||
import io.metersphere.base.mapper.TestResourceMapper;
|
||||
import io.metersphere.commons.constants.ResourceStatusEnum;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.dto.NodeDTO;
|
||||
import io.metersphere.dto.TestResourcePoolDTO;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static io.metersphere.commons.constants.ResourceStatusEnum.VALID;
|
||||
|
||||
@Service
|
||||
public class NodeResourcePoolService {
|
||||
private final static String nodeControllerUrl = "http://%s:%s/status";
|
||||
|
||||
@Resource
|
||||
private RestTemplate restTemplateWithTimeOut;
|
||||
@Resource
|
||||
private TestResourceMapper testResourceMapper;
|
||||
|
||||
public boolean validate(TestResourcePoolDTO testResourcePool) {
|
||||
if (CollectionUtils.isEmpty(testResourcePool.getResources())) {
|
||||
MSException.throwException(Translator.get("no_nodes_message"));
|
||||
}
|
||||
|
||||
deleteTestResource(testResourcePool.getId());
|
||||
List<String> nodeIps = testResourcePool.getResources().stream()
|
||||
.map(resource -> {
|
||||
NodeDTO nodeDTO = JSON.parseObject(resource.getConfiguration(), NodeDTO.class);
|
||||
return nodeDTO.getIp();
|
||||
})
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (nodeIps.size() < testResourcePool.getResources().size()) {
|
||||
MSException.throwException(Translator.get("duplicate_node_ip"));
|
||||
}
|
||||
testResourcePool.setStatus(VALID.name());
|
||||
boolean isValid = true;
|
||||
for (TestResource resource : testResourcePool.getResources()) {
|
||||
NodeDTO nodeDTO = JSON.parseObject(resource.getConfiguration(), NodeDTO.class);
|
||||
boolean isValidate = validateNode(nodeDTO);
|
||||
if (!isValidate) {
|
||||
testResourcePool.setStatus(ResourceStatusEnum.INVALID.name());
|
||||
resource.setStatus(ResourceStatusEnum.INVALID.name());
|
||||
isValid = false;
|
||||
} else {
|
||||
resource.setStatus(VALID.name());
|
||||
}
|
||||
resource.setTestResourcePoolId(testResourcePool.getId());
|
||||
updateTestResource(resource);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
|
||||
private boolean validateNode(NodeDTO node) {
|
||||
try {
|
||||
ResponseEntity<String> entity = restTemplateWithTimeOut.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), String.class);
|
||||
return HttpStatus.OK.equals(entity.getStatusCode());
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTestResource(TestResource testResource) {
|
||||
testResource.setUpdateTime(System.currentTimeMillis());
|
||||
testResource.setCreateTime(System.currentTimeMillis());
|
||||
if (StringUtils.isBlank(testResource.getId())) {
|
||||
testResource.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
// 如果是更新操作,插入与原来的ID相同的数据
|
||||
testResourceMapper.insertSelective(testResource);
|
||||
}
|
||||
|
||||
private void deleteTestResource(String testResourcePoolId) {
|
||||
TestResourceExample testResourceExample = new TestResourceExample();
|
||||
testResourceExample.createCriteria().andTestResourcePoolIdEqualTo(testResourcePoolId);
|
||||
testResourceMapper.deleteByExample(testResourceExample);
|
||||
}
|
||||
}
|
|
@ -1,26 +1,20 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.LoadTestMapper;
|
||||
import io.metersphere.base.mapper.TestResourceMapper;
|
||||
import io.metersphere.base.mapper.TestResourcePoolMapper;
|
||||
import io.metersphere.commons.constants.ResourceStatusEnum;
|
||||
import io.metersphere.commons.constants.ResourcePoolTypeEnum;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.controller.request.resourcepool.QueryResourcePoolRequest;
|
||||
import io.metersphere.dto.NodeDTO;
|
||||
import io.metersphere.dto.TestResourcePoolDTO;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
@ -40,16 +34,14 @@ import static io.metersphere.commons.constants.ResourceStatusEnum.VALID;
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
public class TestResourcePoolService {
|
||||
|
||||
private final static String nodeControllerUrl = "http://%s:%s/status";
|
||||
|
||||
@Resource
|
||||
private TestResourcePoolMapper testResourcePoolMapper;
|
||||
@Resource
|
||||
private TestResourceMapper testResourceMapper;
|
||||
@Resource
|
||||
private RestTemplate restTemplateWithTimeOut;
|
||||
@Resource
|
||||
private LoadTestMapper loadTestMapper;
|
||||
@Resource
|
||||
private NodeResourcePoolService nodeResourcePoolService;
|
||||
|
||||
public TestResourcePoolDTO addTestResourcePool(TestResourcePoolDTO testResourcePool) {
|
||||
checkTestResourcePool(testResourcePool);
|
||||
|
@ -168,61 +160,14 @@ public class TestResourcePoolService {
|
|||
}
|
||||
|
||||
private boolean validateTestResourcePool(TestResourcePoolDTO testResourcePool) {
|
||||
return validateNodes(testResourcePool);
|
||||
}
|
||||
|
||||
private boolean validateNodes(TestResourcePoolDTO testResourcePool) {
|
||||
if (CollectionUtils.isEmpty(testResourcePool.getResources())) {
|
||||
MSException.throwException(Translator.get("no_nodes_message"));
|
||||
}
|
||||
|
||||
deleteTestResource(testResourcePool.getId());
|
||||
List<String> nodeIps = testResourcePool.getResources().stream()
|
||||
.map(resource -> {
|
||||
NodeDTO nodeDTO = JSON.parseObject(resource.getConfiguration(), NodeDTO.class);
|
||||
return nodeDTO.getIp();
|
||||
})
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (nodeIps.size() < testResourcePool.getResources().size()) {
|
||||
MSException.throwException(Translator.get("duplicate_node_ip"));
|
||||
}
|
||||
testResourcePool.setStatus(VALID.name());
|
||||
boolean isValid = true;
|
||||
for (TestResource resource : testResourcePool.getResources()) {
|
||||
NodeDTO nodeDTO = JSON.parseObject(resource.getConfiguration(), NodeDTO.class);
|
||||
boolean isValidate = validateNode(nodeDTO);
|
||||
if (!isValidate) {
|
||||
testResourcePool.setStatus(ResourceStatusEnum.INVALID.name());
|
||||
resource.setStatus(ResourceStatusEnum.INVALID.name());
|
||||
isValid = false;
|
||||
} else {
|
||||
resource.setStatus(VALID.name());
|
||||
if (StringUtils.equalsIgnoreCase(testResourcePool.getType(), ResourcePoolTypeEnum.K8S.name())) {
|
||||
KubernetesResourcePoolService resourcePoolService = CommonBeanFactory.getBean(KubernetesResourcePoolService.class);
|
||||
if (resourcePoolService == null) {
|
||||
return false;
|
||||
}
|
||||
resource.setTestResourcePoolId(testResourcePool.getId());
|
||||
updateTestResource(resource);
|
||||
return resourcePoolService.validate(testResourcePool);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private boolean validateNode(NodeDTO node) {
|
||||
try {
|
||||
ResponseEntity<String> entity = restTemplateWithTimeOut.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), String.class);
|
||||
return HttpStatus.OK.equals(entity.getStatusCode());
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTestResource(TestResource testResource) {
|
||||
testResource.setUpdateTime(System.currentTimeMillis());
|
||||
testResource.setCreateTime(System.currentTimeMillis());
|
||||
if (StringUtils.isBlank(testResource.getId())) {
|
||||
testResource.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
// 如果是更新操作,插入与原来的ID相同的数据
|
||||
testResourceMapper.insertSelective(testResource);
|
||||
return nodeResourcePoolService.validate(testResourcePool);
|
||||
}
|
||||
|
||||
private void deleteTestResource(String testResourcePoolId) {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 905ca8af61ce966d26109e9c5c8c0aee3ca1324e
|
||||
Subproject commit 2ba1351aa0135cdf3e5de740fa2d9c185b60ce9d
|
|
@ -12,6 +12,7 @@
|
|||
<el-table-column prop="type" :label="$t('test_resource_pool.type')">
|
||||
<template v-slot:default="scope">
|
||||
<span v-if="scope.row.type === 'NODE'">Node</span>
|
||||
<span v-if="scope.row.type === 'K8S'" v-xpack>Kubernetes</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('test_resource_pool.enable_disable')">
|
||||
|
@ -52,7 +53,7 @@
|
|||
:destroy-on-close="true"
|
||||
v-loading="result.loading"
|
||||
>
|
||||
<el-form :model="form" label-position="right" label-width="100px" size="small" :rules="rule"
|
||||
<el-form :model="form" label-position="right" label-width="120px" size="small" :rules="rule"
|
||||
ref="createTestResourcePoolForm">
|
||||
<el-form-item :label="$t('commons.name')" prop="name">
|
||||
<el-input v-model="form.name" autocomplete="off"/>
|
||||
|
@ -64,9 +65,40 @@
|
|||
<el-select v-model="form.type" :placeholder="$t('test_resource_pool.select_pool_type')"
|
||||
@change="changeResourceType()">
|
||||
<el-option key="NODE" value="NODE" label="Node">Node</el-option>
|
||||
<el-option key="K8S" value="K8S" label="Kubernetes" v-xpack>Kubernetes</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div v-for="(item,index) in infoList " :key="index">
|
||||
<div class="node-line" v-if="form.type === 'K8S'" v-xpack>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item prop="controllerUrl" label="Controller URL">
|
||||
<el-input v-model="item.controllerUrl" autocomplete="new-password"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item prop="masterUrl" label="Master URL">
|
||||
<el-input v-model="item.masterUrl" autocomplete="new-password"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item prop="password" label="Token">
|
||||
<el-input v-model="item.token" type="password" show-password autocomplete="new-password"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')">
|
||||
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="node-line" v-if="form.type === 'NODE'">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
|
@ -115,7 +147,7 @@
|
|||
:title="$t('test_resource_pool.update_resource_pool')" :visible.sync="updateVisible" width="70%"
|
||||
:destroy-on-close="true"
|
||||
@close="closeFunc">
|
||||
<el-form :model="form" label-position="right" label-width="100px" size="small" :rules="rule"
|
||||
<el-form :model="form" label-position="right" label-width="120px" size="small" :rules="rule"
|
||||
ref="updateTestResourcePoolForm">
|
||||
<el-form-item :label="$t('commons.name')" prop="name">
|
||||
<el-input v-model="form.name" autocomplete="off"/>
|
||||
|
@ -127,9 +159,40 @@
|
|||
<el-select v-model="form.type" :placeholder="$t('test_resource_pool.select_pool_type')"
|
||||
@change="changeResourceType()">
|
||||
<el-option key="NODE" value="NODE" label="Node">Node</el-option>
|
||||
<el-option key="K8S" value="K8S" label="Kubernetes" v-xpack>Kubernetes</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div v-for="(item,index) in infoList " :key="index">
|
||||
<div class="node-line" v-if="form.type === 'K8S'" v-xpack>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item prop="controllerUrl" label="Controller URL">
|
||||
<el-input v-model="item.controllerUrl" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item prop="masterUrl" label="Master URL">
|
||||
<el-input v-model="item.masterUrl" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item prop="password" label="Token">
|
||||
<el-input v-model="item.token" type="password" show-password autocomplete="off"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')">
|
||||
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="node-line" v-if="form.type === 'NODE'">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit a22a3005d9bd254793fcf634d72539cbdf31be3a
|
||||
Subproject commit 29a8fc09602fde5708af06582ac972d98eb69836
|
Loading…
Reference in New Issue