feat(性能测试): 使用K8S资源池时可以配置Job模版
--story=1008704 --user=刘瑞斌 【性能测试】性能测试K8S资源池支持自定义Job模板 https://www.tapd.cn/55049933/s/1295149
This commit is contained in:
parent
cc1a601281
commit
33b064b94c
|
@ -1,9 +0,0 @@
|
||||||
package io.metersphere.engine.request;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public class BaseRequest {
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package io.metersphere.engine.request;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public class StopTestRequest {
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package io.metersphere.engine.request;
|
package io.metersphere.request;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
|
@ -13,4 +13,5 @@ public class ClientCredential {
|
||||||
private Integer podThreadLimit;
|
private Integer podThreadLimit;
|
||||||
private String apiImage;
|
private String apiImage;
|
||||||
private String deployName;
|
private String deployName;
|
||||||
|
private String jobTemplate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package io.metersphere.xpack.resourcepool.engine.provider;
|
package io.metersphere.xpack.resourcepool.engine.provider;
|
||||||
|
|
||||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
import io.metersphere.engine.request.StartTestRequest;
|
import io.metersphere.request.StartTestRequest;
|
||||||
|
|
||||||
public interface KubernetesProvider {
|
public interface KubernetesProvider {
|
||||||
void deployJmeter(StartTestRequest request, ClientCredential clientCredential);
|
void deployJmeter(StartTestRequest request, ClientCredential clientCredential);
|
||||||
|
|
|
@ -17,7 +17,7 @@ import io.metersphere.commons.utils.UrlTestUtils;
|
||||||
import io.metersphere.config.JmeterProperties;
|
import io.metersphere.config.JmeterProperties;
|
||||||
import io.metersphere.config.KafkaProperties;
|
import io.metersphere.config.KafkaProperties;
|
||||||
import io.metersphere.dto.BaseSystemConfigDTO;
|
import io.metersphere.dto.BaseSystemConfigDTO;
|
||||||
import io.metersphere.engine.request.StartTestRequest;
|
import io.metersphere.request.StartTestRequest;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.service.BaseTestResourcePoolService;
|
import io.metersphere.service.BaseTestResourcePoolService;
|
||||||
import io.metersphere.service.BaseTestResourceService;
|
import io.metersphere.service.BaseTestResourceService;
|
||||||
|
|
|
@ -8,9 +8,8 @@ import io.metersphere.commons.utils.CommonBeanFactory;
|
||||||
import io.metersphere.commons.utils.JSON;
|
import io.metersphere.commons.utils.JSON;
|
||||||
import io.metersphere.controller.handler.ResultHolder;
|
import io.metersphere.controller.handler.ResultHolder;
|
||||||
import io.metersphere.dto.NodeDTO;
|
import io.metersphere.dto.NodeDTO;
|
||||||
import io.metersphere.engine.request.StartTestRequest;
|
import io.metersphere.request.StartTestRequest;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
public class DockerTestEngine extends AbstractEngine {
|
public class DockerTestEngine extends AbstractEngine {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import io.metersphere.commons.utils.CommonBeanFactory;
|
||||||
import io.metersphere.commons.utils.JSON;
|
import io.metersphere.commons.utils.JSON;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
import io.metersphere.dto.JmeterRunRequestDTO;
|
import io.metersphere.dto.JmeterRunRequestDTO;
|
||||||
import io.metersphere.engine.request.StartTestRequest;
|
import io.metersphere.request.StartTestRequest;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.xpack.resourcepool.engine.provider.ClientCredential;
|
import io.metersphere.xpack.resourcepool.engine.provider.ClientCredential;
|
||||||
import io.metersphere.xpack.resourcepool.engine.provider.KubernetesProvider;
|
import io.metersphere.xpack.resourcepool.engine.provider.KubernetesProvider;
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
<template>
|
||||||
|
<ms-drawer :visible="dialogVisible" :size="50" @close="handleClose" direction="right"
|
||||||
|
:show-full-screen="false" :is-show-close="false">
|
||||||
|
<div>
|
||||||
|
<el-row class="header">
|
||||||
|
<el-col :span="24" class="buttons">
|
||||||
|
<el-button size="mini" @click="handleClose">{{ $t('commons.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" size="mini" @click="confirm" @keydown.enter.native.prevent>
|
||||||
|
{{ $t('commons.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="ms-code">
|
||||||
|
<ms-code-edit class="ms-code" :enable-format="false" mode="yaml" :data.sync="template" theme="eclipse"
|
||||||
|
:modes="['yaml']"
|
||||||
|
ref="codeEdit"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ms-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const TEMPLATE = `apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test-id: \${TEST_ID}
|
||||||
|
name: \${JOB_NAME}
|
||||||
|
spec:
|
||||||
|
parallelism: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test-id: \${TEST_ID}
|
||||||
|
spec:
|
||||||
|
affinity:
|
||||||
|
podAntiAffinity:
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- podAffinityTerm:
|
||||||
|
labelSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- key: test-id
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- \${TEST_ID}
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
weight: 100
|
||||||
|
containers:
|
||||||
|
- command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- /run-test.sh
|
||||||
|
env:
|
||||||
|
- name: START_TIME
|
||||||
|
value: "\${START_TIME}"
|
||||||
|
- name: GRANULARITY
|
||||||
|
value: "\${GRANULARITY}"
|
||||||
|
- name: JMETER_REPORTS_TOPIC
|
||||||
|
value: \${JMETER_REPORTS_TOPIC}
|
||||||
|
- name: METERSPHERE_URL
|
||||||
|
value: \${METERSPHERE_URL}
|
||||||
|
- name: RESOURCE_ID
|
||||||
|
value: \${RESOURCE_ID}
|
||||||
|
- name: BACKEND_LISTENER
|
||||||
|
value: "\${BACKEND_LISTENER}"
|
||||||
|
- name: BOOTSTRAP_SERVERS
|
||||||
|
value: \${BOOTSTRAP_SERVERS}
|
||||||
|
- name: RATIO
|
||||||
|
value: "\${RATIO}"
|
||||||
|
- name: REPORT_FINAL
|
||||||
|
value: "\${REPORT_FINAL}"
|
||||||
|
- name: TEST_ID
|
||||||
|
value: \${TEST_ID}
|
||||||
|
- name: THREAD_NUM
|
||||||
|
value: "\${THREAD_NUM}"
|
||||||
|
- name: HEAP
|
||||||
|
value: \${HEAP}
|
||||||
|
- name: REPORT_ID
|
||||||
|
value: \${REPORT_ID}
|
||||||
|
- name: REPORT_REALTIME
|
||||||
|
value: "\${REPORT_REALTIME}"
|
||||||
|
- name: RESOURCE_INDEX
|
||||||
|
value: "\${RESOURCE_INDEX}"
|
||||||
|
- name: LOG_TOPIC
|
||||||
|
value: \${LOG_TOPIC}
|
||||||
|
- name: GC_ALGO
|
||||||
|
value: \${GC_ALGO}
|
||||||
|
image: \${JMETER_IMAGE}
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: jmeter
|
||||||
|
ports:
|
||||||
|
- containerPort: 60000
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /test
|
||||||
|
name: test-files
|
||||||
|
- mountPath: /jmeter-log
|
||||||
|
name: log-files
|
||||||
|
- command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- /generate-report.sh
|
||||||
|
env:
|
||||||
|
- name: START_TIME
|
||||||
|
value: "\${START_TIME}"
|
||||||
|
- name: GRANULARITY
|
||||||
|
value: "\${GRANULARITY}"
|
||||||
|
- name: JMETER_REPORTS_TOPIC
|
||||||
|
value: \${JMETER_REPORTS_TOPIC}
|
||||||
|
- name: METERSPHERE_URL
|
||||||
|
value: \${METERSPHERE_URL}
|
||||||
|
- name: RESOURCE_ID
|
||||||
|
value: \${RESOURCE_ID}
|
||||||
|
- name: BACKEND_LISTENER
|
||||||
|
value: "\${BACKEND_LISTENER}"
|
||||||
|
- name: BOOTSTRAP_SERVERS
|
||||||
|
value: \${BOOTSTRAP_SERVERS}
|
||||||
|
- name: RATIO
|
||||||
|
value: "\${RATIO}"
|
||||||
|
- name: REPORT_FINAL
|
||||||
|
value: "\${REPORT_FINAL}"
|
||||||
|
- name: TEST_ID
|
||||||
|
value: \${TEST_ID}
|
||||||
|
- name: THREAD_NUM
|
||||||
|
value: "\${THREAD_NUM}"
|
||||||
|
- name: HEAP
|
||||||
|
value: \${HEAP}
|
||||||
|
- name: REPORT_ID
|
||||||
|
value: \${REPORT_ID}
|
||||||
|
- name: REPORT_REALTIME
|
||||||
|
value: "\${REPORT_REALTIME}"
|
||||||
|
- name: RESOURCE_INDEX
|
||||||
|
value: "\${RESOURCE_INDEX}"
|
||||||
|
- name: LOG_TOPIC
|
||||||
|
value: \${LOG_TOPIC}
|
||||||
|
- name: GC_ALGO
|
||||||
|
value: \${GC_ALGO}
|
||||||
|
image: \${JMETER_IMAGE}
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: report
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /test
|
||||||
|
name: test-files
|
||||||
|
- mountPath: /jmeter-log
|
||||||
|
name: log-files
|
||||||
|
restartPolicy: Never
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: test-files
|
||||||
|
- emptyDir: {}
|
||||||
|
name: log-files
|
||||||
|
`;
|
||||||
|
|
||||||
|
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
||||||
|
import {listenGoBack, removeGoBackListener} from "metersphere-frontend/src/utils";
|
||||||
|
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
|
||||||
|
import MsDrawer from "metersphere-frontend/src/components/MsDrawer";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "JobTemplate",
|
||||||
|
components: {
|
||||||
|
MsDrawer,
|
||||||
|
MsDialogFooter,
|
||||||
|
MsCodeEdit
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
template: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(template) {
|
||||||
|
this.dialogVisible = true;
|
||||||
|
this.template = template || TEMPLATE;
|
||||||
|
listenGoBack(this.handleClose);
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.template = TEMPLATE;
|
||||||
|
this.dialogVisible = false;
|
||||||
|
removeGoBackListener(this.handleClose);
|
||||||
|
},
|
||||||
|
confirm() {
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.$emit("saveJobTemplate", this.template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.ms-drawer {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ms-code {
|
||||||
|
height: calc(97vh);
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons .el-button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons .el-button:nth-child(2) {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
position: fixed;
|
||||||
|
top: 15px;
|
||||||
|
right: 50px;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -154,40 +154,38 @@
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="8">
|
<el-col :span="6">
|
||||||
<el-form-item :label="$t('test_resource_pool.max_threads')"
|
<el-form-item :label="$t('test_resource_pool.max_threads')"
|
||||||
:rules="requiredRules">
|
:rules="requiredRules">
|
||||||
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"/>
|
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="6">
|
||||||
<el-form-item :label="$t('test_resource_pool.pod_thread_limit')"
|
<el-form-item :label="$t('test_resource_pool.pod_thread_limit')"
|
||||||
:rules="requiredRules">
|
:rules="requiredRules">
|
||||||
<el-input-number v-model="item.podThreadLimit" :min="1" :max="1000000"/>
|
<el-input-number v-model="item.podThreadLimit" :min="1" :max="1000000"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="4">
|
||||||
<el-form-item :label="$t('test_resource_pool.sync_jar')">
|
<el-form-item :label="$t('test_resource_pool.sync_jar')">
|
||||||
<el-checkbox v-model="item.enable"/>
|
<el-checkbox v-model="item.enable"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
<el-col :span="8">
|
||||||
<el-row>
|
|
||||||
<el-col>
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<template v-slot:label>
|
<template v-slot:label>
|
||||||
NodeSelector
|
<el-link type="primary" @click="jobTemplate">{{ $t('system.test_resource_pool.edit_job_template') }}</el-link>
|
||||||
<el-tooltip :content="$t('test_resource_pool.node_selector_tip')"
|
<el-tooltip :content="$t('system.test_resource_pool.edit_job_template_tip')"
|
||||||
effect="light"
|
effect="light"
|
||||||
trigger="hover">
|
trigger="hover">
|
||||||
<i class="el-icon-info"></i>
|
<i class="el-icon-info"></i>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<el-input v-model="item.nodeSelector" placeholder='{"disktype": "ssd",...}'/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
<job-template ref="jobTemplate" @saveJobTemplate="saveJobTemplate"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="node-line" v-if="form.type === 'NODE'">
|
<div class="node-line" v-if="form.type === 'NODE'">
|
||||||
|
@ -294,10 +292,11 @@ import {
|
||||||
} from "../../../api/resource-pool";
|
} from "../../../api/resource-pool";
|
||||||
import {getSystemVersion} from "../../../api/system";
|
import {getSystemVersion} from "../../../api/system";
|
||||||
import {operationConfirm} from "metersphere-frontend/src/utils";
|
import {operationConfirm} from "metersphere-frontend/src/utils";
|
||||||
|
import JobTemplate from "@/business/system/components/JobTemplate";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsTestResourcePool",
|
name: "MsTestResourcePool",
|
||||||
components: {BatchAddResource, MsTablePagination, MsTableHeader, MsTableOperator, MsDialogFooter},
|
components: {JobTemplate, BatchAddResource, MsTablePagination, MsTableHeader, MsTableOperator, MsDialogFooter},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -390,6 +389,9 @@ export default {
|
||||||
batchAddResource() {
|
batchAddResource() {
|
||||||
this.$refs.batchAddResource.open();
|
this.$refs.batchAddResource.open();
|
||||||
},
|
},
|
||||||
|
jobTemplate() {
|
||||||
|
this.$refs.jobTemplate.open(this.infoList[0].jobTemplate);
|
||||||
|
},
|
||||||
batchSave(resources) {
|
batchSave(resources) {
|
||||||
let targets = this._handleBatchVars(resources);
|
let targets = this._handleBatchVars(resources);
|
||||||
targets.forEach(row => {
|
targets.forEach(row => {
|
||||||
|
@ -413,6 +415,11 @@ export default {
|
||||||
});
|
});
|
||||||
return keyValues;
|
return keyValues;
|
||||||
},
|
},
|
||||||
|
saveJobTemplate(template) {
|
||||||
|
this.infoList.forEach(item => {
|
||||||
|
item.jobTemplate = template;
|
||||||
|
});
|
||||||
|
},
|
||||||
validateResourceInfo() {
|
validateResourceInfo() {
|
||||||
if (this.infoList.length <= 0) {
|
if (this.infoList.length <= 0) {
|
||||||
return {validate: false, msg: this.$t('test_resource_pool.cannot_empty')};
|
return {validate: false, msg: this.$t('test_resource_pool.cannot_empty')};
|
||||||
|
@ -607,7 +614,7 @@ export default {
|
||||||
}
|
}
|
||||||
let i = res.data.lastIndexOf('-');
|
let i = res.data.lastIndexOf('-');
|
||||||
this.apiImageTag = res.data.substring(0, i);
|
this.apiImageTag = res.data.substring(0, i);
|
||||||
}).catch(err=>{
|
}).catch(err => {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
isJsonString(str) {
|
isJsonString(str) {
|
||||||
|
|
|
@ -12,6 +12,10 @@ const message = {
|
||||||
check_third_project_success: "inspection passed",
|
check_third_project_success: "inspection passed",
|
||||||
api_default_run_message: 'In order not to affect the normal execution of the interface, please configure the resource pool for interface execution in [Project Settings - Application Management - Interface Test]',
|
api_default_run_message: 'In order not to affect the normal execution of the interface, please configure the resource pool for interface execution in [Project Settings - Application Management - Interface Test]',
|
||||||
api_default_run: 'The interface is executed locally by default',
|
api_default_run: 'The interface is executed locally by default',
|
||||||
|
test_resource_pool: {
|
||||||
|
edit_job_template: "Edit Job Template",
|
||||||
|
edit_job_template_tip: "The Kubernetes Job template is a text in YAML format that defines the running parameters of the Job. You can edit the Job template here.",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
display: {
|
display: {
|
||||||
title: 'Theme',
|
title: 'Theme',
|
||||||
|
|
|
@ -12,6 +12,10 @@ const message = {
|
||||||
check_third_project_success: "检查通过",
|
check_third_project_success: "检查通过",
|
||||||
api_default_run_message: '为了不影响接口正常执行,请在【 项目设置-应用管理-接口测试 】中配置接口执行的资源池',
|
api_default_run_message: '为了不影响接口正常执行,请在【 项目设置-应用管理-接口测试 】中配置接口执行的资源池',
|
||||||
api_default_run: '接口默认本地执行',
|
api_default_run: '接口默认本地执行',
|
||||||
|
test_resource_pool: {
|
||||||
|
edit_job_template: "编辑Job模版",
|
||||||
|
edit_job_template_tip: "Kubernetes Job模版是一个YAML格式的文本,用于定义Job的运行参数,您可以在此处编辑Job模版。",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
display: {
|
display: {
|
||||||
title: '显示设置',
|
title: '显示设置',
|
||||||
|
|
|
@ -12,6 +12,10 @@ const message = {
|
||||||
check_third_project_success: "檢查通過",
|
check_third_project_success: "檢查通過",
|
||||||
api_default_run_message: '為了不影響接口正常執行,請在【 項目設置-應用管理-接口測試 】中配置接口執行的資源池',
|
api_default_run_message: '為了不影響接口正常執行,請在【 項目設置-應用管理-接口測試 】中配置接口執行的資源池',
|
||||||
api_default_run: '接口默認本地執行',
|
api_default_run: '接口默認本地執行',
|
||||||
|
test_resource_pool: {
|
||||||
|
edit_job_template: "編輯Job模版",
|
||||||
|
edit_job_template_tip: "Kubernetes Job模版是一個YAML格式的文本,用於定義Job的運行參數,您可以在此處編輯Job模版。",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
display: {
|
display: {
|
||||||
title: '顯示設置',
|
title: '顯示設置',
|
||||||
|
|
Loading…
Reference in New Issue