feat(接口测试): 测试计划支持禁用本地执行

--story=1009842 --user=赵勇 接口测试支持禁用本地执行 https://www.tapd.cn/55049933/s/1291450
This commit is contained in:
fit2-zhao 2022-11-16 14:30:47 +08:00 committed by fit2-zhao
parent 0b8d80f49d
commit 61b82c28fd
10 changed files with 49 additions and 43 deletions

View File

@ -23,6 +23,7 @@ import io.metersphere.service.BaseProjectApplicationService;
import io.metersphere.service.RemakeReportService; import io.metersphere.service.RemakeReportService;
import io.metersphere.service.SystemParameterService; import io.metersphere.service.SystemParameterService;
import io.metersphere.utils.LoggerUtil; import io.metersphere.utils.LoggerUtil;
import io.metersphere.xpack.api.service.ApiPoolDebugService;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
@ -58,11 +59,14 @@ public class JMeterService {
private SystemParameterService systemParameterService; private SystemParameterService systemParameterService;
@Resource @Resource
private BaseProjectApplicationService projectApplicationService; private BaseProjectApplicationService projectApplicationService;
@Resource
private RemakeReportService remakeReportService;
@Resource
private ExecThreadPoolExecutor execThreadPoolExecutor;
@PostConstruct @PostConstruct
private void init() { private void init() {
String JMETER_HOME = getJmeterHome(); String JMETER_HOME = getJmeterHome();
String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties"; String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties";
JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES); JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES);
JMeterUtils.setJMeterHome(JMETER_HOME); JMeterUtils.setJMeterHome(JMETER_HOME);
@ -150,8 +154,7 @@ public class JMeterService {
final Engine engine = EngineFactory.createApiEngine(request); final Engine engine = EngineFactory.createApiEngine(request);
engine.start(); engine.start();
} catch (Exception e) { } catch (Exception e) {
RemakeReportService apiScenarioReportService = CommonBeanFactory.getBean(RemakeReportService.class); remakeReportService.testEnded(request, e.getMessage());
apiScenarioReportService.testEnded(request, e.getMessage());
LoggerUtil.error("调用K8S执行请求[ " + request.getTestId() + " ]失败:", request.getReportId(), e); LoggerUtil.error("调用K8S执行请求[ " + request.getTestId() + " ]失败:", request.getReportId(), e);
} }
} else if ((MapUtils.isNotEmpty(request.getExtendedParameters()) } else if ((MapUtils.isNotEmpty(request.getExtendedParameters())
@ -166,34 +169,15 @@ public class JMeterService {
private synchronized void nodeDebug(JmeterRunRequestDTO request) { private synchronized void nodeDebug(JmeterRunRequestDTO request) {
try { try {
if (request.isDebug() && !StringUtils.equalsAny(request.getRunMode(), ApiRunMode.DEFINITION.name())) { ApiPoolDebugService apiPoolDebugService = CommonBeanFactory.getBean(ApiPoolDebugService.class);
request.getExtendedParameters().put(ExtendedParameter.SAVE_RESULT, true); if (apiPoolDebugService != null) {
} else if (!request.isDebug()) { List<TestResource> resources = GenerateHashTreeUtil.setPoolResource(request.getPoolId());
request.getExtendedParameters().put(ExtendedParameter.SAVE_RESULT, true); request.getExtendedParameters().put(ExtendedParameter.JMX, new MsTestPlan().getJmx(request.getHashTree()));
} request.setHashTree(null);
List<TestResource> resources = GenerateHashTreeUtil.setPoolResource(request.getPoolId()); apiPoolDebugService.run(request, resources);
String uri = null;
int index = (int) (Math.random() * resources.size());
String configuration = resources.get(index).getConfiguration();
if (StringUtils.isNotEmpty(configuration)) {
NodeDTO node = com.alibaba.fastjson.JSON.parseObject(configuration, NodeDTO.class);
uri = String.format(BASE_URL + "/jmeter/debug", node.getIp(), node.getPort());
}
if (StringUtils.isEmpty(uri)) {
LoggerUtil.info("未获取到资源池,请检查配置【系统设置-系统-测试资源池】", request.getReportId());
MSException.throwException("调用资源池执行失败,请检查资源池是否配置正常");
}
request.getExtendedParameters().put(ExtendedParameter.JMX, new MsTestPlan().getJmx(request.getHashTree()));
request.setHashTree(null);
LoggerUtil.info("开始发送请求【 " + request.getTestId() + " 】到 " + uri + " 节点执行", request.getReportId());
ResponseEntity<String> result = restTemplate.postForEntity(uri, request, String.class);
if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) {
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 到" + uri + " 节点执行失败", request.getReportId());
LoggerUtil.info(result);
MSException.throwException("调用资源池执行失败,请检查资源池是否配置正常");
} }
} catch (Exception e) { } catch (Exception e) {
RemakeReportService remakeReportService = CommonBeanFactory.getBean(RemakeReportService.class); LoggerUtil.error(e);
remakeReportService.remake(request); remakeReportService.remake(request);
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 执行失败,进行数据回滚:", request.getReportId(), e); LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 执行失败,进行数据回滚:", request.getReportId(), e);
MSException.throwException("调用资源池执行失败,请检查资源池是否配置正常"); MSException.throwException("调用资源池执行失败,请检查资源池是否配置正常");
@ -212,7 +196,6 @@ public class JMeterService {
} }
if (config == null) { if (config == null) {
LoggerUtil.info("未获取到资源池,请检查配置【系统设置-系统-测试资源池】", request.getReportId()); LoggerUtil.info("未获取到资源池,请检查配置【系统设置-系统-测试资源池】", request.getReportId());
RemakeReportService remakeReportService = CommonBeanFactory.getBean(RemakeReportService.class);
remakeReportService.remake(request); remakeReportService.remake(request);
return; return;
} }
@ -221,13 +204,11 @@ public class JMeterService {
LoggerUtil.info("开始发送请求【 " + request.getTestId() + " 】到 " + config.getUrl() + " 节点执行", request.getReportId()); LoggerUtil.info("开始发送请求【 " + request.getTestId() + " 】到 " + config.getUrl() + " 节点执行", request.getReportId());
ResponseEntity<String> result = restTemplate.postForEntity(config.getUrl(), request, String.class); ResponseEntity<String> result = restTemplate.postForEntity(config.getUrl(), request, String.class);
if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) { if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) {
RemakeReportService remakeReportService = CommonBeanFactory.getBean(RemakeReportService.class);
remakeReportService.remake(request); remakeReportService.remake(request);
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 到" + config.getUrl() + " 节点执行失败", request.getReportId()); LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 到" + config.getUrl() + " 节点执行失败", request.getReportId());
LoggerUtil.info(result.getBody()); LoggerUtil.info(result.getBody());
} }
} catch (Exception e) { } catch (Exception e) {
RemakeReportService remakeReportService = CommonBeanFactory.getBean(RemakeReportService.class);
remakeReportService.remake(request); remakeReportService.remake(request);
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 执行失败,进行数据回滚:", request.getReportId(), e); LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 执行失败,进行数据回滚:", request.getReportId(), e);
} }
@ -240,7 +221,7 @@ public class JMeterService {
} else if (request.getHashTree() != null) { } else if (request.getHashTree() != null) {
//解析hashTree是否含有文件库文件 //解析hashTree是否含有文件库文件
HashTreeUtil.initRepositoryFiles(request); HashTreeUtil.initRepositoryFiles(request);
CommonBeanFactory.getBean(ExecThreadPoolExecutor.class).addTask(request); execThreadPoolExecutor.addTask(request);
} }
} }
@ -275,7 +256,8 @@ public class JMeterService {
public void verifyPool(String projectId, RunModeConfigDTO runConfig) { public void verifyPool(String projectId, RunModeConfigDTO runConfig) {
// 检查是否禁用了本地执行 // 检查是否禁用了本地执行
if (runConfig != null && StringUtils.isEmpty(runConfig.getResourcePoolId())) { if (runConfig != null && StringUtils.isEmpty(runConfig.getResourcePoolId())
&& CommonBeanFactory.getBean(ApiPoolDebugService.class) != null) {
BaseSystemConfigDTO configDTO = systemParameterService.getBaseInfo(); BaseSystemConfigDTO configDTO = systemParameterService.getBaseInfo();
if (StringUtils.equals(configDTO.getRunMode(), POOL)) { if (StringUtils.equals(configDTO.getRunMode(), POOL)) {
ProjectConfig config = projectApplicationService.getProjectConfig(projectId); ProjectConfig config = projectApplicationService.getProjectConfig(projectId);

View File

@ -1,9 +1,9 @@
package io.metersphere.api.jmeter; package io.metersphere.api.jmeter;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.api.dto.MsgDTO; import io.metersphere.api.dto.MsgDTO;
import io.metersphere.commons.constants.KafkaTopicConstants; import io.metersphere.commons.constants.KafkaTopicConstants;
import io.metersphere.commons.utils.JSONUtil;
import io.metersphere.commons.utils.NamedThreadFactory; import io.metersphere.commons.utils.NamedThreadFactory;
import io.metersphere.commons.utils.WebSocketUtil; import io.metersphere.commons.utils.WebSocketUtil;
import io.metersphere.service.ApiExecutionQueueService; import io.metersphere.service.ApiExecutionQueueService;
@ -74,7 +74,7 @@ public class MsKafkaListener {
try { try {
LoggerUtil.info("接收到执行结果:", record.key()); LoggerUtil.info("接收到执行结果:", record.key());
if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) { if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) {
MsgDTO dto = JSON.parseObject(record.value(), MsgDTO.class); MsgDTO dto = JSONUtil.parseObject(record.value(), MsgDTO.class);
WebSocketUtil.sendMessageSingle(dto); WebSocketUtil.sendMessageSingle(dto);
} }
} catch (Exception e) { } catch (Exception e) {

View File

@ -95,6 +95,7 @@ import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getSystemBaseSetting} from "metersphere-frontend/src/api/system"; import {getSystemBaseSetting} from "metersphere-frontend/src/api/system";
import EnvSelectPopover from "@/business/automation/scenario/EnvSelectPopover"; import EnvSelectPopover from "@/business/automation/scenario/EnvSelectPopover";
import {getApiCaseEnvironments} from "@/api/api-test-case"; import {getApiCaseEnvironments} from "@/api/api-test-case";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
export default { export default {
name: "ApiRunMode", name: "ApiRunMode",
@ -149,7 +150,9 @@ export default {
this.runModeVisible = true; this.runModeVisible = true;
this.getResourcePools(); this.getResourcePools();
this.getWsProjects(); this.getWsProjects();
this.query(); if(hasLicense()) {
this.query();
}
this.showPopover(); this.showPopover();
this.runConfig.environmentType = ENV_TYPE.JSON; this.runConfig.environmentType = ENV_TYPE.JSON;
}, },

View File

@ -109,6 +109,7 @@ import EnvPopover from "@/business/automation/scenario/EnvPopover";
import {getMaintainer, getOwnerProjects, getProjectConfig} from "@/api/project"; import {getMaintainer, getOwnerProjects, getProjectConfig} from "@/api/project";
import {getTestResourcePools} from "@/api/test-resource-pool"; import {getTestResourcePools} from "@/api/test-resource-pool";
import {getSystemBaseSetting} from "metersphere-frontend/src/api/system"; import {getSystemBaseSetting} from "metersphere-frontend/src/api/system";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
function defaultCustomValidate() { function defaultCustomValidate() {
return {pass: true}; return {pass: true};
@ -317,7 +318,9 @@ export default {
this.activeName = 'first'; this.activeName = 'first';
this.getResourcePools(); this.getResourcePools();
this.getWsProjects(); this.getWsProjects();
this.query(); if(hasLicense()) {
this.query();
}
this.runConfig.environmentType = ENV_TYPE.JSON; this.runConfig.environmentType = ENV_TYPE.JSON;
}, },
findSchedule() { findSchedule() {

View File

@ -0,0 +1,10 @@
package io.metersphere.xpack.api.service;
import io.metersphere.base.domain.TestResource;
import io.metersphere.dto.JmeterRunRequestDTO;
import java.util.List;
public interface ApiPoolDebugService {
public void run(JmeterRunRequestDTO request, List<TestResource> resources);
}

View File

@ -97,7 +97,7 @@
<!-- 接口测试资源池 --> <!-- 接口测试资源池 -->
<app-manage-item :title="$t('pj.api_run_pool_title')" :prepend-span="8" :middle-span="12" <app-manage-item :title="$t('pj.api_run_pool_title')" :prepend-span="8" :middle-span="12"
:append-span="4" v-if="isPool"> :append-span="4" v-if="isPool && isXpack">
<template #middle> <template #middle>
<el-select v-model="config.resourcePoolId" <el-select v-model="config.resourcePoolId"
size="mini" size="mini"

View File

@ -19,7 +19,7 @@
<el-input v-model="formInline.seleniumDockerUrl" :placeholder="$t('system_config.selenium_docker.url_tip')"/> <el-input v-model="formInline.seleniumDockerUrl" :placeholder="$t('system_config.selenium_docker.url_tip')"/>
<i>({{ $t('commons.examples') }}:http://localhost:4444)</i> <i>({{ $t('commons.examples') }}:http://localhost:4444)</i>
</el-form-item> </el-form-item>
<el-form-item :label="$t('system.api_default_run')" prop="seleniumDockerUrl"> <el-form-item :label="$t('system.api_default_run')" prop="runMode" v-if="hasLicense()">
<el-switch active-value="LOCAL" inactive-value="POOL" v-model="formInline.runMode" @change="modeChange"/> <el-switch active-value="LOCAL" inactive-value="POOL" v-model="formInline.runMode" @change="modeChange"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -38,6 +38,7 @@
<script> <script>
import {getSystemBaseSetting, saveSystemBaseSetting} from "../../../api/system"; import {getSystemBaseSetting, saveSystemBaseSetting} from "../../../api/system";
import {hasLicense} from 'metersphere-frontend/src/utils/permission';
export default { export default {
name: "BaseSetting", name: "BaseSetting",
@ -76,6 +77,7 @@ export default {
this.query() this.query()
}, },
methods: { methods: {
hasLicense,
query() { query() {
this.loading = getSystemBaseSetting().then(res => { this.loading = getSystemBaseSetting().then(res => {
if(!res.data.runMode) { if(!res.data.runMode) {

View File

@ -281,7 +281,9 @@ export default {
this.testType = testType; this.testType = testType;
this.getResourcePools(); this.getResourcePools();
this.getWsProjects(); this.getWsProjects();
this.query(); if(hasLicense()) {
this.query();
}
}, },
query() { query() {
this.loading = true; this.loading = true;

View File

@ -292,7 +292,9 @@ export default {
this.getResourcePools(); this.getResourcePools();
this.getWsProjects(); this.getWsProjects();
this.showPopover(); this.showPopover();
this.query(); if(hasLicense()) {
this.query();
}
}, },
query() { query() {
this.loading = true; this.loading = true;

View File

@ -274,7 +274,9 @@ export default {
this.testType = testType; this.testType = testType;
this.getResourcePools(); this.getResourcePools();
this.getWsProjects(); this.getWsProjects();
this.query(); if(hasLicense()) {
this.query();
}
}, },
query() { query() {
this.loading = true; this.loading = true;