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.Setter;
|
|
@ -13,4 +13,5 @@ public class ClientCredential {
|
|||
private Integer podThreadLimit;
|
||||
private String apiImage;
|
||||
private String deployName;
|
||||
private String jobTemplate;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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-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) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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: '显示设置',
|
||||
|
|
|
@ -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: '顯示設置',
|
||||
|
|
Loading…
Reference in New Issue