feat(性能测试): 使用K8S资源池时可以配置Job模版

--story=1008704 --user=刘瑞斌 【性能测试】性能测试K8S资源池支持自定义Job模板 https://www.tapd.cn/55049933/s/1295149
This commit is contained in:
CaptainB 2022-11-12 15:14:34 +08:00 committed by 刘瑞斌
parent cc1a601281
commit 33b064b94c
13 changed files with 251 additions and 35 deletions

View File

@ -1,9 +0,0 @@
package io.metersphere.engine.request;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class BaseRequest {
}

View File

@ -1,9 +0,0 @@
package io.metersphere.engine.request;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class StopTestRequest {
}

View File

@ -1,4 +1,4 @@
package io.metersphere.engine.request;
package io.metersphere.request;
import lombok.Getter;
import lombok.Setter;

View File

@ -13,4 +13,5 @@ public class ClientCredential {
private Integer podThreadLimit;
private String apiImage;
private String deployName;
private String jobTemplate;
}

View File

@ -1,7 +1,7 @@
package io.metersphere.xpack.resourcepool.engine.provider;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.metersphere.engine.request.StartTestRequest;
import io.metersphere.request.StartTestRequest;
public interface KubernetesProvider {
void deployJmeter(StartTestRequest request, ClientCredential clientCredential);

View File

@ -17,7 +17,7 @@ import io.metersphere.commons.utils.UrlTestUtils;
import io.metersphere.config.JmeterProperties;
import io.metersphere.config.KafkaProperties;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.engine.request.StartTestRequest;
import io.metersphere.request.StartTestRequest;
import io.metersphere.i18n.Translator;
import io.metersphere.service.BaseTestResourcePoolService;
import io.metersphere.service.BaseTestResourceService;

View File

@ -8,9 +8,8 @@ import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.JSON;
import io.metersphere.controller.handler.ResultHolder;
import io.metersphere.dto.NodeDTO;
import io.metersphere.engine.request.StartTestRequest;
import io.metersphere.request.StartTestRequest;
import io.metersphere.i18n.Translator;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
public class DockerTestEngine extends AbstractEngine {

View File

@ -7,7 +7,7 @@ import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.JSON;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.engine.request.StartTestRequest;
import io.metersphere.request.StartTestRequest;
import io.metersphere.i18n.Translator;
import io.metersphere.xpack.resourcepool.engine.provider.ClientCredential;
import io.metersphere.xpack.resourcepool.engine.provider.KubernetesProvider;

View File

@ -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>

View File

@ -154,40 +154,38 @@
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-col :span="6">
<el-form-item :label="$t('test_resource_pool.max_threads')"
:rules="requiredRules">
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item :label="$t('test_resource_pool.pod_thread_limit')"
:rules="requiredRules">
<el-input-number v-model="item.podThreadLimit" :min="1" :max="1000000"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="4">
<el-form-item :label="$t('test_resource_pool.sync_jar')">
<el-checkbox v-model="item.enable"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-col :span="8">
<el-form-item>
<template v-slot:label>
NodeSelector
<el-tooltip :content="$t('test_resource_pool.node_selector_tip')"
<el-link type="primary" @click="jobTemplate">{{ $t('system.test_resource_pool.edit_job_template') }}</el-link>
<el-tooltip :content="$t('system.test_resource_pool.edit_job_template_tip')"
effect="light"
trigger="hover">
<i class="el-icon-info"></i>
</el-tooltip>
</template>
<el-input v-model="item.nodeSelector" placeholder='{"disktype": "ssd",...}'/>
</el-form-item>
</el-col>
</el-row>
</div>
<job-template ref="jobTemplate" @saveJobTemplate="saveJobTemplate"/>
</div>
<div class="node-line" v-if="form.type === 'NODE'">
@ -294,10 +292,11 @@ import {
} from "../../../api/resource-pool";
import {getSystemVersion} from "../../../api/system";
import {operationConfirm} from "metersphere-frontend/src/utils";
import JobTemplate from "@/business/system/components/JobTemplate";
export default {
name: "MsTestResourcePool",
components: {BatchAddResource, MsTablePagination, MsTableHeader, MsTableOperator, MsDialogFooter},
components: {JobTemplate, BatchAddResource, MsTablePagination, MsTableHeader, MsTableOperator, MsDialogFooter},
data() {
return {
loading: false,
@ -390,6 +389,9 @@ export default {
batchAddResource() {
this.$refs.batchAddResource.open();
},
jobTemplate() {
this.$refs.jobTemplate.open(this.infoList[0].jobTemplate);
},
batchSave(resources) {
let targets = this._handleBatchVars(resources);
targets.forEach(row => {
@ -413,6 +415,11 @@ export default {
});
return keyValues;
},
saveJobTemplate(template) {
this.infoList.forEach(item => {
item.jobTemplate = template;
});
},
validateResourceInfo() {
if (this.infoList.length <= 0) {
return {validate: false, msg: this.$t('test_resource_pool.cannot_empty')};
@ -607,7 +614,7 @@ export default {
}
let i = res.data.lastIndexOf('-');
this.apiImageTag = res.data.substring(0, i);
}).catch(err=>{
}).catch(err => {
})
},
isJsonString(str) {

View File

@ -12,6 +12,10 @@ const message = {
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: '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: {
title: 'Theme',

View File

@ -12,6 +12,10 @@ const message = {
check_third_project_success: "检查通过",
api_default_run_message: '为了不影响接口正常执行,请在【 项目设置-应用管理-接口测试 】中配置接口执行的资源池',
api_default_run: '接口默认本地执行',
test_resource_pool: {
edit_job_template: "编辑Job模版",
edit_job_template_tip: "Kubernetes Job模版是一个YAML格式的文本用于定义Job的运行参数您可以在此处编辑Job模版。",
}
},
display: {
title: '显示设置',

View File

@ -12,6 +12,10 @@ const message = {
check_third_project_success: "檢查通過",
api_default_run_message: '為了不影響接口正常執行,請在【 項目設置-應用管理-接口測試 】中配置接口執行的資源池',
api_default_run: '接口默認本地執行',
test_resource_pool: {
edit_job_template: "編輯Job模版",
edit_job_template_tip: "Kubernetes Job模版是一個YAML格式的文本用於定義Job的運行參數您可以在此處編輯Job模版。",
}
},
display: {
title: '顯示設置',