Merge branch 'dev' of https://github.com/fit2cloudrd/metersphere-server into dev
This commit is contained in:
commit
b6e3b104f3
|
@ -18,9 +18,16 @@
|
|||
<shiro.version>1.5.1</shiro.version>
|
||||
<java.version>1.8</java.version>
|
||||
<jmeter.version>5.2.1</jmeter.version>
|
||||
<kubernetes-client.version>4.9.0</kubernetes-client.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- kubernetes client-->
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-client</artifactId>
|
||||
<version>${kubernetes-client.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
|
@ -57,7 +64,7 @@
|
|||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<version>2.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -9,12 +9,14 @@ public class FileMetadata implements Serializable {
|
|||
|
||||
private String type;
|
||||
|
||||
private Long size;
|
||||
private String engine;
|
||||
|
||||
private Long createTime;
|
||||
|
||||
private Long updateTime;
|
||||
|
||||
private Long size;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public String getId() {
|
||||
|
@ -41,12 +43,12 @@ public class FileMetadata implements Serializable {
|
|||
this.type = type == null ? null : type.trim();
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
return size;
|
||||
public String getEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
public void setSize(Long size) {
|
||||
this.size = size;
|
||||
public void setEngine(String engine) {
|
||||
this.engine = engine == null ? null : engine.trim();
|
||||
}
|
||||
|
||||
public Long getCreateTime() {
|
||||
|
@ -64,4 +66,12 @@ public class FileMetadata implements Serializable {
|
|||
public void setUpdateTime(Long updateTime) {
|
||||
this.updateTime = updateTime;
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
}
|
|
@ -314,63 +314,73 @@ public class FileMetadataExample {
|
|||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeIsNull() {
|
||||
addCriterion("size is null");
|
||||
public Criteria andEngineIsNull() {
|
||||
addCriterion("engine is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeIsNotNull() {
|
||||
addCriterion("size is not null");
|
||||
public Criteria andEngineIsNotNull() {
|
||||
addCriterion("engine is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeEqualTo(Long value) {
|
||||
addCriterion("size =", value, "size");
|
||||
public Criteria andEngineEqualTo(String value) {
|
||||
addCriterion("engine =", value, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeNotEqualTo(Long value) {
|
||||
addCriterion("size <>", value, "size");
|
||||
public Criteria andEngineNotEqualTo(String value) {
|
||||
addCriterion("engine <>", value, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeGreaterThan(Long value) {
|
||||
addCriterion("size >", value, "size");
|
||||
public Criteria andEngineGreaterThan(String value) {
|
||||
addCriterion("engine >", value, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeGreaterThanOrEqualTo(Long value) {
|
||||
addCriterion("size >=", value, "size");
|
||||
public Criteria andEngineGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("engine >=", value, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeLessThan(Long value) {
|
||||
addCriterion("size <", value, "size");
|
||||
public Criteria andEngineLessThan(String value) {
|
||||
addCriterion("engine <", value, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeLessThanOrEqualTo(Long value) {
|
||||
addCriterion("size <=", value, "size");
|
||||
public Criteria andEngineLessThanOrEqualTo(String value) {
|
||||
addCriterion("engine <=", value, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeIn(List<Long> values) {
|
||||
addCriterion("size in", values, "size");
|
||||
public Criteria andEngineLike(String value) {
|
||||
addCriterion("engine like", value, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeNotIn(List<Long> values) {
|
||||
addCriterion("size not in", values, "size");
|
||||
public Criteria andEngineNotLike(String value) {
|
||||
addCriterion("engine not like", value, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeBetween(Long value1, Long value2) {
|
||||
addCriterion("size between", value1, value2, "size");
|
||||
public Criteria andEngineIn(List<String> values) {
|
||||
addCriterion("engine in", values, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeNotBetween(Long value1, Long value2) {
|
||||
addCriterion("size not between", value1, value2, "size");
|
||||
public Criteria andEngineNotIn(List<String> values) {
|
||||
addCriterion("engine not in", values, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andEngineBetween(String value1, String value2) {
|
||||
addCriterion("engine between", value1, value2, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andEngineNotBetween(String value1, String value2) {
|
||||
addCriterion("engine not between", value1, value2, "engine");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
|
@ -493,6 +503,66 @@ public class FileMetadataExample {
|
|||
addCriterion("update_time not between", value1, value2, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeIsNull() {
|
||||
addCriterion("size is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeIsNotNull() {
|
||||
addCriterion("size is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeEqualTo(Long value) {
|
||||
addCriterion("size =", value, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeNotEqualTo(Long value) {
|
||||
addCriterion("size <>", value, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeGreaterThan(Long value) {
|
||||
addCriterion("size >", value, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeGreaterThanOrEqualTo(Long value) {
|
||||
addCriterion("size >=", value, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeLessThan(Long value) {
|
||||
addCriterion("size <", value, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeLessThanOrEqualTo(Long value) {
|
||||
addCriterion("size <=", value, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeIn(List<Long> values) {
|
||||
addCriterion("size in", values, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeNotIn(List<Long> values) {
|
||||
addCriterion("size not in", values, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeBetween(Long value1, Long value2) {
|
||||
addCriterion("size between", value1, value2, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andSizeNotBetween(Long value1, Long value2) {
|
||||
addCriterion("size not between", value1, value2, "size");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
<id column="id" jdbcType="VARCHAR" property="id" />
|
||||
<result column="name" jdbcType="VARCHAR" property="name" />
|
||||
<result column="type" jdbcType="VARCHAR" property="type" />
|
||||
<result column="size" jdbcType="BIGINT" property="size" />
|
||||
<result column="engine" jdbcType="VARCHAR" property="engine" />
|
||||
<result column="create_time" jdbcType="BIGINT" property="createTime" />
|
||||
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
|
||||
<result column="size" jdbcType="BIGINT" property="size" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
|
@ -68,7 +69,7 @@
|
|||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, name, type, size, create_time, update_time
|
||||
id, name, type, engine, create_time, update_time, size
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.FileMetadataExample" resultMap="BaseResultMap">
|
||||
select
|
||||
|
@ -102,11 +103,11 @@
|
|||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.FileMetadata">
|
||||
insert into file_metadata (id, name, type,
|
||||
size, create_time, update_time
|
||||
)
|
||||
engine, create_time, update_time,
|
||||
size)
|
||||
values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR},
|
||||
#{size,jdbcType=BIGINT}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}
|
||||
)
|
||||
#{engine,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{size,jdbcType=BIGINT})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.FileMetadata">
|
||||
insert into file_metadata
|
||||
|
@ -120,8 +121,8 @@
|
|||
<if test="type != null">
|
||||
type,
|
||||
</if>
|
||||
<if test="size != null">
|
||||
size,
|
||||
<if test="engine != null">
|
||||
engine,
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
create_time,
|
||||
|
@ -129,6 +130,9 @@
|
|||
<if test="updateTime != null">
|
||||
update_time,
|
||||
</if>
|
||||
<if test="size != null">
|
||||
size,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -140,8 +144,8 @@
|
|||
<if test="type != null">
|
||||
#{type,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="size != null">
|
||||
#{size,jdbcType=BIGINT},
|
||||
<if test="engine != null">
|
||||
#{engine,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
#{createTime,jdbcType=BIGINT},
|
||||
|
@ -149,6 +153,9 @@
|
|||
<if test="updateTime != null">
|
||||
#{updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="size != null">
|
||||
#{size,jdbcType=BIGINT},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.FileMetadataExample" resultType="java.lang.Long">
|
||||
|
@ -169,8 +176,8 @@
|
|||
<if test="record.type != null">
|
||||
type = #{record.type,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.size != null">
|
||||
size = #{record.size,jdbcType=BIGINT},
|
||||
<if test="record.engine != null">
|
||||
engine = #{record.engine,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.createTime != null">
|
||||
create_time = #{record.createTime,jdbcType=BIGINT},
|
||||
|
@ -178,6 +185,9 @@
|
|||
<if test="record.updateTime != null">
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="record.size != null">
|
||||
size = #{record.size,jdbcType=BIGINT},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
|
@ -188,9 +198,10 @@
|
|||
set id = #{record.id,jdbcType=VARCHAR},
|
||||
name = #{record.name,jdbcType=VARCHAR},
|
||||
type = #{record.type,jdbcType=VARCHAR},
|
||||
size = #{record.size,jdbcType=BIGINT},
|
||||
engine = #{record.engine,jdbcType=VARCHAR},
|
||||
create_time = #{record.createTime,jdbcType=BIGINT},
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT}
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT},
|
||||
size = #{record.size,jdbcType=BIGINT}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -204,8 +215,8 @@
|
|||
<if test="type != null">
|
||||
type = #{type,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="size != null">
|
||||
size = #{size,jdbcType=BIGINT},
|
||||
<if test="engine != null">
|
||||
engine = #{engine,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
create_time = #{createTime,jdbcType=BIGINT},
|
||||
|
@ -213,6 +224,9 @@
|
|||
<if test="updateTime != null">
|
||||
update_time = #{updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="size != null">
|
||||
size = #{size,jdbcType=BIGINT},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
|
@ -220,9 +234,10 @@
|
|||
update file_metadata
|
||||
set name = #{name,jdbcType=VARCHAR},
|
||||
type = #{type,jdbcType=VARCHAR},
|
||||
size = #{size,jdbcType=BIGINT},
|
||||
engine = #{engine,jdbcType=VARCHAR},
|
||||
create_time = #{createTime,jdbcType=BIGINT},
|
||||
update_time = #{updateTime,jdbcType=BIGINT}
|
||||
update_time = #{updateTime,jdbcType=BIGINT},
|
||||
size = #{size,jdbcType=BIGINT}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -11,7 +11,7 @@
|
|||
<result column="create_time" jdbcType="BIGINT" property="createTime" />
|
||||
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
|
||||
<result column="language" jdbcType="VARCHAR" property="language" />
|
||||
<collection property="roles" ofType="io.metersphere.base.domain.Role">
|
||||
<collection property="roles" javaType="arraylist" ofType="io.metersphere.base.domain.Role">
|
||||
<id column="rid" jdbcType="VARCHAR" property="id" />
|
||||
<result column="rname" jdbcType="VARCHAR" property="name" />
|
||||
</collection>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum EngineType {
|
||||
JMX
|
||||
DOCKER, KUBERNETES
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum FileType {
|
||||
JMX
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package io.metersphere.controller.request;
|
||||
|
||||
public class TestRequest {
|
||||
|
||||
int size;
|
||||
String fileString;
|
||||
String testId;
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String getFileString() {
|
||||
return fileString;
|
||||
}
|
||||
|
||||
public void setFileString(String fileString) {
|
||||
this.fileString = fileString;
|
||||
}
|
||||
|
||||
public String getTestId() {
|
||||
return testId;
|
||||
}
|
||||
|
||||
public void setTestId(String testId) {
|
||||
this.testId = testId;
|
||||
}
|
||||
}
|
|
@ -1,21 +1,39 @@
|
|||
package io.metersphere.engine;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class EngineContext {
|
||||
private String engineId;
|
||||
private String testId;
|
||||
private String testName;
|
||||
private String namespace;
|
||||
private String engineType;
|
||||
private InputStream inputStream;
|
||||
private String fileType;
|
||||
private String content;
|
||||
private Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
public String getEngineId() {
|
||||
return engineId;
|
||||
public String getTestId() {
|
||||
return testId;
|
||||
}
|
||||
|
||||
public void setEngineId(String engineId) {
|
||||
this.engineId = engineId;
|
||||
public void setTestId(String testId) {
|
||||
this.testId = testId;
|
||||
}
|
||||
|
||||
public String getTestName() {
|
||||
return testName;
|
||||
}
|
||||
|
||||
public void setTestName(String testName) {
|
||||
this.testName = testName;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public String getEngineType() {
|
||||
|
@ -26,14 +44,6 @@ public class EngineContext {
|
|||
this.engineType = engineType;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
public void setInputStream(InputStream inputStream) {
|
||||
this.inputStream = inputStream;
|
||||
}
|
||||
|
||||
public void addProperty(String key, Object value) {
|
||||
this.properties.put(key, value);
|
||||
}
|
||||
|
@ -41,4 +51,20 @@ public class EngineContext {
|
|||
public Object getProperty(String key) {
|
||||
return this.properties.get(key);
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getFileType() {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
public void setFileType(String fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,29 +7,34 @@ import io.metersphere.base.domain.FileMetadata;
|
|||
import io.metersphere.base.domain.LoadTestWithBLOBs;
|
||||
import io.metersphere.commons.constants.EngineType;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.engine.jmx.JmxEngine;
|
||||
import io.metersphere.engine.docker.DockerTestEngine;
|
||||
import io.metersphere.engine.kubernetes.KubernetesTestEngine;
|
||||
import io.metersphere.parse.EngineSourceParser;
|
||||
import io.metersphere.parse.EngineSourceParserFactory;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class EngineFactory {
|
||||
public static Engine createEngine(String engineType) {
|
||||
final EngineType type = EngineType.valueOf(engineType);
|
||||
|
||||
if (type == EngineType.JMX) {
|
||||
return new JmxEngine();
|
||||
switch (type) {
|
||||
case DOCKER:
|
||||
return new DockerTestEngine();
|
||||
case KUBERNETES:
|
||||
return new KubernetesTestEngine();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static EngineContext createContext(LoadTestWithBLOBs loadTest, FileMetadata fileMetadata, FileContent fileContent) throws Exception {
|
||||
final EngineContext engineContext = new EngineContext();
|
||||
engineContext.setEngineId(loadTest.getId());
|
||||
engineContext.setInputStream(new ByteArrayInputStream(fileContent.getFile()));
|
||||
engineContext.setEngineType(fileMetadata.getType());
|
||||
engineContext.setTestId(loadTest.getId());
|
||||
engineContext.setTestName(loadTest.getName());
|
||||
engineContext.setNamespace(loadTest.getProjectId());
|
||||
engineContext.setEngineType(fileMetadata.getEngine());
|
||||
engineContext.setFileType(fileMetadata.getType());
|
||||
|
||||
if (!StringUtils.isEmpty(loadTest.getLoadConfiguration())) {
|
||||
final JSONArray jsonArray = JSONObject.parseArray(loadTest.getLoadConfiguration());
|
||||
|
@ -40,14 +45,14 @@ public class EngineFactory {
|
|||
}
|
||||
}
|
||||
|
||||
final EngineSourceParser engineSourceParser = EngineSourceParserFactory.createEngineSourceParser(engineContext.getEngineType());
|
||||
final EngineSourceParser engineSourceParser = EngineSourceParserFactory.createEngineSourceParser(engineContext.getFileType());
|
||||
|
||||
if (engineSourceParser == null) {
|
||||
MSException.throwException("未知的文件类型!");
|
||||
}
|
||||
|
||||
final InputStream inputStream = engineSourceParser.parse(engineContext, engineContext.getInputStream());
|
||||
engineContext.setInputStream(inputStream);
|
||||
String content = engineSourceParser.parse(engineContext, new ByteArrayInputStream(fileContent.getFile()));
|
||||
engineContext.setContent(content);
|
||||
|
||||
return engineContext;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package io.metersphere.engine.docker;
|
||||
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.controller.request.TestRequest;
|
||||
import io.metersphere.engine.Engine;
|
||||
import io.metersphere.engine.EngineContext;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class DockerTestEngine implements Engine {
|
||||
private EngineContext context;
|
||||
|
||||
@Override
|
||||
public boolean init(EngineContext context) {
|
||||
// todo 初始化操作
|
||||
this.context = context;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
String testId = context.getTestId();
|
||||
String content = context.getContent();
|
||||
|
||||
String uri = "http://localhost:8082/jmeter/container/start";
|
||||
|
||||
TestRequest testRequest = new TestRequest();
|
||||
testRequest.setSize(1);
|
||||
testRequest.setTestId(testId);
|
||||
testRequest.setFileString(content);
|
||||
|
||||
String taskStatusUri = "http://localhost:8082/jmeter/task/status/" + testId;
|
||||
List containerList = restTemplate.getForObject(taskStatusUri, List.class);
|
||||
for (int i = 0; i < containerList.size(); i++) {
|
||||
HashMap h = (HashMap) containerList.get(i);
|
||||
if (StringUtils.equals((String)h.get("State"), "running")) {
|
||||
MSException.throwException("the test is running!");
|
||||
}
|
||||
}
|
||||
|
||||
restTemplate.postForObject(uri, testRequest, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
String testId = context.getTestId();
|
||||
|
||||
String uri = "http://localhost:8082/jmeter/container/stop" + testId;
|
||||
restTemplate.getForObject(uri, String.class);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package io.metersphere.engine.jmx;
|
||||
|
||||
import io.metersphere.engine.Engine;
|
||||
import io.metersphere.engine.EngineContext;
|
||||
import io.metersphere.engine.EngineThread;
|
||||
import io.metersphere.engine.jmx.client.DistributedRunner;
|
||||
import io.metersphere.engine.jmx.client.JmeterProperties;
|
||||
import org.apache.jmeter.JMeter;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.services.FileServer;
|
||||
import org.apache.jmeter.threads.ThreadGroup;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
public class JmxEngine extends EngineThread implements Engine {
|
||||
private static final Logger log = LoggerFactory.getLogger(JmxEngine.class);
|
||||
/// todo:从测试属性中读取
|
||||
private final static Integer MAX_DURATION = 60;
|
||||
/// todo:从测试属性中读取
|
||||
private final static String REMOTE_HOSTS = "127.0.0.1";
|
||||
/// todo:jmeter home如何确定
|
||||
private final static String jmeterHome = "/opt/fit2cloud/apache-jmeter-5.2.1";
|
||||
private static Method readTreeMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
readTreeMethod = SaveService.class.getDeclaredMethod("readTree", InputStream.class, File.class);
|
||||
readTreeMethod.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private EngineContext context;
|
||||
private DistributedRunner runner;
|
||||
|
||||
private static void setMaxTestDuration(HashTree jmxTree) {
|
||||
for (HashTree item : jmxTree.values()) {
|
||||
Set treeKeys = item.keySet();
|
||||
for (Object key : treeKeys) {
|
||||
if (key instanceof ThreadGroup) {
|
||||
((ThreadGroup) key).setProperty(ThreadGroup.SCHEDULER, true);
|
||||
((ThreadGroup) key).setProperty(ThreadGroup.DURATION, MAX_DURATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEngineName() {
|
||||
return "JMX";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean init(EngineContext context) {
|
||||
this.context = context;
|
||||
|
||||
new JmeterProperties(JmxEngine.jmeterHome).initJmeterProperties();
|
||||
FileServer.getFileServer().setBaseForScript(new File(JmxEngine.jmeterHome + File.separator + "nothing"));
|
||||
|
||||
final HashTree jmxTree = loadTree(this.context.getInputStream());
|
||||
if (jmxTree == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JMeter.convertSubTree(jmxTree, true);
|
||||
|
||||
setMaxTestDuration(jmxTree);
|
||||
|
||||
this.runner = new DistributedRunner(jmxTree, REMOTE_HOSTS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
this.runner.run();
|
||||
} catch (Throwable e) {
|
||||
log.error("run test error, id: " + this.context.getEngineId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop(false);
|
||||
this.runner.stop();
|
||||
}
|
||||
|
||||
private HashTree loadTree(InputStream inputStream) {
|
||||
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) {
|
||||
return (HashTree) readTreeMethod.invoke(null, bufferedInputStream, null);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to load tree", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
package io.metersphere.engine.jmx.client;
|
||||
|
||||
import org.apache.jmeter.engine.ClientJMeterEngine;
|
||||
import org.apache.jmeter.engine.JMeterEngine;
|
||||
import org.apache.jmeter.report.dashboard.ReportGenerator;
|
||||
import org.apache.jmeter.samplers.Remoteable;
|
||||
import org.apache.jmeter.testelement.TestStateListener;
|
||||
import org.apache.jmeter.util.JMeterUtils;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class DistributedRunner extends org.apache.jmeter.engine.DistributedRunner {
|
||||
private static final String HOSTS_SEPARATOR = ",";
|
||||
private HashTree jmxTree;
|
||||
private String hosts;
|
||||
// 脚本运行完成后是否停止jmeter-server
|
||||
private boolean remoteStop = false;
|
||||
|
||||
public DistributedRunner(HashTree jmxTree, String hosts) {
|
||||
this.jmxTree = jmxTree;
|
||||
this.hosts = hosts;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
final List<String> hosts = getRemoteHosts();
|
||||
final ListenToTest listener = new ListenToTest(remoteStop, null);
|
||||
jmxTree.add(jmxTree.getArray()[0], listener);
|
||||
init(hosts, jmxTree);
|
||||
listener.setStartedRemoteEngines(new ArrayList<>(getEngines()));
|
||||
start();
|
||||
}
|
||||
|
||||
private List<String> getRemoteHosts() {
|
||||
StringTokenizer st = new StringTokenizer(hosts, HOSTS_SEPARATOR);
|
||||
List<String> list = new LinkedList<>();
|
||||
while (st.hasMoreElements()) {
|
||||
list.add((String) st.nextElement());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static class ListenToTest implements TestStateListener, Remoteable {
|
||||
private final Logger log = LoggerFactory.getLogger(ListenToTest.class);
|
||||
private final ReportGenerator reportGenerator;
|
||||
private AtomicInteger startedRemoteEngines = new AtomicInteger(0);
|
||||
private ConcurrentLinkedQueue<JMeterEngine> remoteEngines = new ConcurrentLinkedQueue<>();
|
||||
private boolean remoteStop;
|
||||
|
||||
ListenToTest(boolean remoteStop, ReportGenerator reportGenerator) {
|
||||
this.remoteStop = remoteStop;
|
||||
this.reportGenerator = reportGenerator;
|
||||
}
|
||||
|
||||
void setStartedRemoteEngines(List<JMeterEngine> engines) {
|
||||
this.remoteEngines.clear();
|
||||
this.remoteEngines.addAll(engines);
|
||||
this.startedRemoteEngines = new AtomicInteger(remoteEngines.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
// N.B. this is called by a daemon RMI thread from the remote host
|
||||
public void testEnded(String host) {
|
||||
final long now = System.currentTimeMillis();
|
||||
log.info("Finished remote host: {} ({})", host, now);
|
||||
if (startedRemoteEngines.decrementAndGet() <= 0) {
|
||||
log.info("All remote engines have ended test, starting RemoteTestStopper thread");
|
||||
Thread stopSoon = new Thread(() -> endTest(true), "RemoteTestStopper");
|
||||
// the calling thread is a daemon; this thread must not be
|
||||
// see Bug 59391
|
||||
stopSoon.setDaemon(false);
|
||||
stopSoon.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testEnded() {
|
||||
endTest(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testStarted(String host) {
|
||||
final long now = System.currentTimeMillis();
|
||||
log.info("Started remote host: {} ({})", host, now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testStarted() {
|
||||
if (log.isInfoEnabled()) {
|
||||
final long now = System.currentTimeMillis();
|
||||
log.info("{} ({})", JMeterUtils.getResString("running_test"), now);//$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
private void endTest(boolean isDistributed) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (isDistributed) {
|
||||
log.info("Tidying up remote @ " + new Date(now) + " (" + now + ")");
|
||||
} else {
|
||||
log.info("Tidying up ... @ " + new Date(now) + " (" + now + ")");
|
||||
}
|
||||
|
||||
if (isDistributed) {
|
||||
if (remoteStop) {
|
||||
log.info("Exiting remote servers:" + remoteEngines);
|
||||
for (JMeterEngine engine : remoteEngines) {
|
||||
log.info("Exiting remote server:" + engine);
|
||||
engine.exit();
|
||||
}
|
||||
}
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(5); // Allow listeners to close files
|
||||
} catch (InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
ClientJMeterEngine.tidyRMI(log);
|
||||
}
|
||||
|
||||
if (reportGenerator != null) {
|
||||
try {
|
||||
log.info("Generating Dashboard");
|
||||
reportGenerator.generate();
|
||||
log.info("Dashboard generated");
|
||||
} catch (Exception ex) {
|
||||
System.err.println("Error generating the report: " + ex);
|
||||
log.error("Error generating the report: {}", ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
log.info("... end of run");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package io.metersphere.engine.jmx.client;
|
||||
|
||||
import org.apache.jmeter.util.JMeterUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
public class JmeterProperties extends Properties {
|
||||
private final static Logger logger = LoggerFactory.getLogger(JmeterProperties.class);
|
||||
|
||||
private final String jmeterHome;
|
||||
|
||||
public JmeterProperties(String jmeterHome) {
|
||||
this.jmeterHome = jmeterHome;
|
||||
}
|
||||
|
||||
public void initJmeterProperties() {
|
||||
JMeterUtils.loadJMeterProperties(getJmeterHomeBin() + File.separator + "jmeter.properties");
|
||||
JMeterUtils.setJMeterHome(getJmeterHome());
|
||||
JMeterUtils.initLocale();
|
||||
|
||||
Properties jmeterProps = JMeterUtils.getJMeterProperties();
|
||||
|
||||
// Add local JMeter properties, if the file is found
|
||||
String userProp = JMeterUtils.getPropDefault("user.properties", "");
|
||||
if (userProp.length() > 0) {
|
||||
File file = JMeterUtils.findFile(userProp);
|
||||
if (file.canRead()) {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
Properties tmp = new Properties();
|
||||
tmp.load(fis);
|
||||
jmeterProps.putAll(tmp);
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to init jmeter properties", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add local system properties, if the file is found
|
||||
String sysProp = JMeterUtils.getPropDefault("system.properties", "");
|
||||
if (sysProp.length() > 0) {
|
||||
File file = JMeterUtils.findFile(sysProp);
|
||||
if (file.canRead()) {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
System.getProperties().load(fis);
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to init jmeter properties", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jmeterProps.put("jmeter.version", JMeterUtils.getJMeterVersion());
|
||||
for (Map.Entry<Object, Object> entry : jmeterProps.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private String getJmeterHome() {
|
||||
return jmeterHome;
|
||||
}
|
||||
|
||||
private String getJmeterHomeBin() {
|
||||
return getJmeterHome() + File.separator + "bin";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package io.metersphere.engine.kubernetes;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.ObjectMeta;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.engine.Engine;
|
||||
import io.metersphere.engine.EngineContext;
|
||||
import io.metersphere.engine.kubernetes.crds.jmeter.Jmeter;
|
||||
import io.metersphere.engine.kubernetes.crds.jmeter.JmeterSpec;
|
||||
import io.metersphere.engine.kubernetes.provider.ClientCredential;
|
||||
import io.metersphere.engine.kubernetes.provider.KubernetesProvider;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class KubernetesTestEngine implements Engine {
|
||||
private EngineContext context;
|
||||
|
||||
@Override
|
||||
public boolean init(EngineContext context) {
|
||||
// todo 初始化操作
|
||||
this.context = context;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (context == null) {
|
||||
LogUtil.warn("Please initial the engine.");
|
||||
return;
|
||||
}
|
||||
// todo 运行测试
|
||||
ClientCredential credential = new ClientCredential();
|
||||
credential.setMasterUrl("https://172.16.10.93:6443");
|
||||
KubernetesProvider kubernetesProvider = new KubernetesProvider(JSON.toJSONString(credential));
|
||||
// create namespace
|
||||
kubernetesProvider.confirmNamespace(context.getNamespace());
|
||||
// create cm
|
||||
try (KubernetesClient client = kubernetesProvider.getKubernetesClient()) {
|
||||
String configMapName = context.getTestId() + "-files";
|
||||
ConfigMap configMap = client.configMaps().inNamespace(context.getNamespace()).withName(configMapName).get();
|
||||
if (configMap == null) {
|
||||
ConfigMap item = new ConfigMap();
|
||||
item.setMetadata(new ObjectMeta() {{
|
||||
setName(configMapName);
|
||||
}});
|
||||
item.setData(new HashMap<String, String>() {{
|
||||
put(context.getTestId() + ".jmx", context.getContent());
|
||||
}});
|
||||
client.configMaps().inNamespace(context.getNamespace()).create(item);
|
||||
}
|
||||
}
|
||||
// create jmeter
|
||||
try {
|
||||
Jmeter jmeter = new Jmeter();
|
||||
jmeter.setMetadata(new ObjectMeta() {{
|
||||
setNamespace(context.getNamespace());
|
||||
setName(context.getTestId());
|
||||
}});
|
||||
jmeter.setSpec(new JmeterSpec() {{
|
||||
setReplicas(1);
|
||||
setImage("registry.fit2cloud.com/metersphere/jmeter-master:0.0.2");
|
||||
}});
|
||||
LogUtil.info("Load test started. " + context.getTestId());
|
||||
kubernetesProvider.applyCustomResource(jmeter);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package io.metersphere.engine.kubernetes.crds;
|
||||
|
||||
import io.fabric8.kubernetes.client.CustomResource;
|
||||
|
||||
public class MeterSphereCustomResource extends CustomResource {
|
||||
|
||||
private String crd;
|
||||
|
||||
private Object spec;
|
||||
|
||||
private Object status;
|
||||
|
||||
public String getCrd() {
|
||||
return crd;
|
||||
}
|
||||
|
||||
public void setCrd(String crd) {
|
||||
this.crd = crd;
|
||||
}
|
||||
|
||||
public Object getSpec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
public void setSpec(Object spec) {
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
public Object getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Object status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.engine.kubernetes.crds;
|
||||
|
||||
import io.fabric8.kubernetes.api.builder.Function;
|
||||
import io.fabric8.kubernetes.client.CustomResourceDoneable;
|
||||
|
||||
public class MeterSphereCustomResourceDoneable<T extends MeterSphereCustomResource> extends CustomResourceDoneable<T> {
|
||||
public MeterSphereCustomResourceDoneable(T resource, Function<T, T> function) {
|
||||
super(resource, function);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package io.metersphere.engine.kubernetes.crds;
|
||||
|
||||
import io.fabric8.kubernetes.client.CustomResourceList;
|
||||
|
||||
public class MeterSphereCustomResourceList<T extends MeterSphereCustomResource> extends CustomResourceList<T> {
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package io.metersphere.engine.kubernetes.crds.jmeter;
|
||||
|
||||
import io.metersphere.engine.kubernetes.crds.MeterSphereCustomResource;
|
||||
|
||||
public class Jmeter extends MeterSphereCustomResource {
|
||||
public static final String CRD = "jmeters.metersphere.io";
|
||||
public static final String KIND = "Jmeter";
|
||||
private JmeterSpec spec;
|
||||
private JmeterStatus status;
|
||||
|
||||
public Jmeter() {
|
||||
this.setCrd(CRD);
|
||||
this.setKind(KIND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JmeterSpec getSpec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
public void setSpec(JmeterSpec spec) {
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JmeterStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(JmeterStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.engine.kubernetes.crds.jmeter;
|
||||
|
||||
import io.fabric8.kubernetes.api.builder.Function;
|
||||
import io.fabric8.kubernetes.client.CustomResourceDoneable;
|
||||
|
||||
public class JmeterDoneable extends CustomResourceDoneable<Jmeter> {
|
||||
public JmeterDoneable(Jmeter resource, Function<Jmeter, Jmeter> function) {
|
||||
super(resource, function);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package io.metersphere.engine.kubernetes.crds.jmeter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import io.fabric8.kubernetes.api.model.KubernetesResource;
|
||||
|
||||
@JsonDeserialize
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
public class JmeterSpec implements KubernetesResource {
|
||||
private int replicas = 1;
|
||||
private String image;
|
||||
|
||||
public int getReplicas() {
|
||||
return replicas;
|
||||
}
|
||||
|
||||
public void setReplicas(int replicas) {
|
||||
this.replicas = replicas;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setImage(String image) {
|
||||
this.image = image;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package io.metersphere.engine.kubernetes.crds.jmeter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import io.fabric8.kubernetes.api.model.KubernetesResource;
|
||||
|
||||
@JsonDeserialize
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class JmeterStatus implements KubernetesResource {
|
||||
private String phase;
|
||||
private String reason;
|
||||
|
||||
public String getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
public void setPhase(String phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JmeterStatus{" +
|
||||
"phase='" + phase + '\'' +
|
||||
", reason='" + reason + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
package io.metersphere.engine.kubernetes.provider;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.fabric8.kubernetes.api.model.*;
|
||||
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition;
|
||||
import io.fabric8.kubernetes.client.ConfigBuilder;
|
||||
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.dsl.*;
|
||||
import io.metersphere.engine.kubernetes.crds.MeterSphereCustomResource;
|
||||
import io.metersphere.engine.kubernetes.crds.MeterSphereCustomResourceDoneable;
|
||||
import io.metersphere.engine.kubernetes.crds.MeterSphereCustomResourceList;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class AbstractClientProvider {
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private String credential;
|
||||
|
||||
public AbstractClientProvider(String credential) {
|
||||
setCredential(credential);
|
||||
}
|
||||
|
||||
public String getCredential() {
|
||||
return credential;
|
||||
}
|
||||
|
||||
public void setCredential(String credential) {
|
||||
this.credential = credential;
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenShiftClient继承自KubernetesClient,OpenShiftClient对OpenShift和kubernetes的共有资源也是用KubernetesClient代理的,
|
||||
* 所以可以在把client提取到抽象类
|
||||
*/
|
||||
public KubernetesClient getKubernetesClient() {
|
||||
ClientCredential providerCredential = JSONObject.parseObject(getCredential(), ClientCredential.class);
|
||||
io.fabric8.kubernetes.client.ConfigBuilder configBuilder = new ConfigBuilder();
|
||||
configBuilder.withMasterUrl(providerCredential.getMasterUrl());
|
||||
configBuilder.withOauthToken(providerCredential.getToken());
|
||||
configBuilder.withTrustCerts(true);
|
||||
//设置默认的 namespace 为 null,
|
||||
configBuilder.withNamespace(null);
|
||||
return new DefaultKubernetesClient(configBuilder.build());
|
||||
}
|
||||
|
||||
|
||||
public void validateCredential() {
|
||||
try (KubernetesClient client = getKubernetesClient()) {
|
||||
client.namespaces().list();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 确保指定的namespace存在,不存在直接创建一个
|
||||
*
|
||||
* @param namespace namespace标识
|
||||
* @return
|
||||
*/
|
||||
public synchronized String confirmNamespace(String namespace) {
|
||||
KubernetesClient kubernetesClient = getKubernetesClient();
|
||||
Namespace currentNamespace = kubernetesClient.namespaces().withName(namespace).get();
|
||||
if (currentNamespace == null) {
|
||||
Map<String, String> annotations = new HashMap<>();
|
||||
Namespace newNamespace = new NamespaceBuilder()
|
||||
.withNewMetadata()
|
||||
.withName(namespace)
|
||||
.withAnnotations(annotations)
|
||||
.endMetadata()
|
||||
.build();
|
||||
currentNamespace = kubernetesClient.namespaces().createOrReplace(newNamespace);
|
||||
}
|
||||
return currentNamespace.getMetadata().getName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取集群UUID,当前集群UUID等于default namespace的UID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getClusterUUID() {
|
||||
KubernetesClient kubernetesClient = getKubernetesClient();
|
||||
Namespace defaultNamespace = kubernetesClient.namespaces().withName("kube-system").get();
|
||||
if (defaultNamespace == null) {
|
||||
throw new RuntimeException("无法获取集群的kube-system namespace");
|
||||
} else {
|
||||
return defaultNamespace.getMetadata().getUid();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 确保docker registry存在,
|
||||
* 不存在 创建一个harbor-secret,并修改serviceaccount default
|
||||
*
|
||||
* @param registry
|
||||
*/
|
||||
public void dockerRegistry(DockerRegistry registry) {
|
||||
|
||||
if (StringUtils.isEmpty(registry.getUsername()) ||
|
||||
StringUtils.isEmpty(registry.getPassword()) ||
|
||||
StringUtils.isEmpty(registry.getUrl())
|
||||
) {
|
||||
throw new RuntimeException("Please set the docker registry information");
|
||||
}
|
||||
|
||||
String secretName = "docker-registry-ms-secret";
|
||||
KubernetesClient kubernetesClient = getKubernetesClient();
|
||||
|
||||
Secret secretRegistry = new SecretBuilder()
|
||||
.withNewMetadata().withName(secretName).endMetadata()
|
||||
.addToData(".dockerconfigjson", DockerRegistryUtil.getDockerConfig(registry))
|
||||
.withType("kubernetes.io/dockerconfigjson")
|
||||
.build();
|
||||
kubernetesClient.secrets().inNamespace(registry.getNamespace()).createOrReplace(secretRegistry);
|
||||
|
||||
//sa
|
||||
ServiceAccount serviceAccount = kubernetesClient.serviceAccounts().inNamespace(registry.getNamespace())
|
||||
.withName("default").get();
|
||||
List<LocalObjectReference> imagePullSecrets = serviceAccount.getImagePullSecrets();
|
||||
for (LocalObjectReference pullSecret : imagePullSecrets) {
|
||||
if (secretName.equals(pullSecret.getName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LocalObjectReference localObjectReference = new LocalObjectReference(secretName);
|
||||
imagePullSecrets.add(localObjectReference);
|
||||
serviceAccount.setImagePullSecrets(imagePullSecrets);
|
||||
|
||||
kubernetesClient.serviceAccounts().inNamespace(registry.getNamespace())
|
||||
.createOrReplace(serviceAccount);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public <T> T applyCustomResource(MeterSphereCustomResource customResource) throws Exception {
|
||||
try (KubernetesClient kubernetesClient = getKubernetesClient()) {
|
||||
CustomResourceDefinition crd = kubernetesClient.customResourceDefinitions().withName(customResource.getCrd()).get();
|
||||
if (crd == null) {
|
||||
throw new Exception("CRD does not exists.");
|
||||
}
|
||||
MixedOperation<MeterSphereCustomResource, MeterSphereCustomResourceList, MeterSphereCustomResourceDoneable, Resource<MeterSphereCustomResource, MeterSphereCustomResourceDoneable>>
|
||||
operation = kubernetesClient.customResources(crd, MeterSphereCustomResource.class, MeterSphereCustomResourceList.class, MeterSphereCustomResourceDoneable.class);
|
||||
MeterSphereCustomResource replace = operation.inNamespace(customResource.getMetadata().getNamespace()).createOrReplace(customResource);
|
||||
return (T) objectMapper.readValue(objectMapper.writeValueAsString(replace), customResource.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean deleteCustomResource(MeterSphereCustomResource customResource) {
|
||||
try (KubernetesClient kubernetesClient = getKubernetesClient()) {
|
||||
CustomResourceDefinition crd = kubernetesClient.customResourceDefinitions().withName(customResource.getCrd()).get();
|
||||
MixedOperation<MeterSphereCustomResource, MeterSphereCustomResourceList, MeterSphereCustomResourceDoneable, Resource<MeterSphereCustomResource, MeterSphereCustomResourceDoneable>>
|
||||
operation = kubernetesClient.customResources(crd, MeterSphereCustomResource.class, MeterSphereCustomResourceList.class, MeterSphereCustomResourceDoneable.class);
|
||||
Boolean result = operation.inNamespace(customResource.getMetadata().getNamespace()).withName(customResource.getMetadata().getName()).cascading(true).delete();
|
||||
return result == null ? false : result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public <T> T getCustomResource(MeterSphereCustomResource customResource) throws Exception {
|
||||
try (KubernetesClient kubernetesClient = getKubernetesClient()) {
|
||||
CustomResourceDefinition crd = kubernetesClient.customResourceDefinitions().withName(customResource.getCrd()).get();
|
||||
MixedOperation<MeterSphereCustomResource, MeterSphereCustomResourceList, MeterSphereCustomResourceDoneable, Resource<MeterSphereCustomResource, MeterSphereCustomResourceDoneable>>
|
||||
operation = kubernetesClient.customResources(crd, MeterSphereCustomResource.class, MeterSphereCustomResourceList.class, MeterSphereCustomResourceDoneable.class);
|
||||
MeterSphereCustomResource meterSphereCustomResource = operation.inNamespace(customResource.getMetadata().getNamespace()).withName(customResource.getMetadata().getName()).get();
|
||||
if (meterSphereCustomResource == null) {
|
||||
return null;
|
||||
}
|
||||
return (T) objectMapper.readValue(objectMapper.writeValueAsString(meterSphereCustomResource), customResource.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public <T> List<T> listCustomResource(MeterSphereCustomResource customResource) throws Exception {
|
||||
try (KubernetesClient kubernetesClient = getKubernetesClient()) {
|
||||
CustomResourceDefinition crd = kubernetesClient.customResourceDefinitions().withName(customResource.getCrd()).get();
|
||||
MixedOperation<MeterSphereCustomResource, MeterSphereCustomResourceList, MeterSphereCustomResourceDoneable, Resource<MeterSphereCustomResource, MeterSphereCustomResourceDoneable>>
|
||||
operation = kubernetesClient.customResources(crd, MeterSphereCustomResource.class, MeterSphereCustomResourceList.class, MeterSphereCustomResourceDoneable.class);
|
||||
MeterSphereCustomResourceList list;
|
||||
if (StringUtils.isNotEmpty(customResource.getMetadata().getNamespace())) {
|
||||
list = operation.inNamespace(customResource.getMetadata().getNamespace()).list();
|
||||
} else {
|
||||
list = operation.inAnyNamespace().list();
|
||||
}
|
||||
|
||||
List<T> resultList = new ArrayList<>();
|
||||
for (Object cr : list.getItems()) {
|
||||
resultList.add((T) objectMapper.readValue(objectMapper.writeValueAsString(cr), customResource.getClass()));
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean checkPVCNotExists(String namespace, String statefulsetName) {
|
||||
KubernetesClient kubernetesClient = getKubernetesClient();
|
||||
NonNamespaceOperation<PersistentVolumeClaim, PersistentVolumeClaimList, DoneablePersistentVolumeClaim, Resource<PersistentVolumeClaim, DoneablePersistentVolumeClaim>> operation = kubernetesClient.persistentVolumeClaims()
|
||||
.inNamespace(namespace);
|
||||
|
||||
PersistentVolumeClaimList pvcList = operation.list(100, null);
|
||||
Pattern compile = Pattern.compile(statefulsetName + "-\\d+");
|
||||
return checkPVCNotExists(pvcList, compile, operation);
|
||||
}
|
||||
|
||||
private boolean checkPVCNotExists(PersistentVolumeClaimList pvcList, Pattern compile, NonNamespaceOperation<PersistentVolumeClaim, PersistentVolumeClaimList, DoneablePersistentVolumeClaim, Resource<PersistentVolumeClaim, DoneablePersistentVolumeClaim>> operation) {
|
||||
if (pvcList == null || CollectionUtils.isEmpty(pvcList.getItems())) {
|
||||
return true;
|
||||
}
|
||||
Optional<PersistentVolumeClaim> claimOptional = pvcList.getItems().stream().filter(pvc -> compile.matcher(pvc.getMetadata().getName()).matches()).findAny();
|
||||
if (claimOptional.isPresent()) {
|
||||
return false;
|
||||
} else if (StringUtils.isNotEmpty(pvcList.getMetadata().getContinue())) {
|
||||
return checkPVCNotExists(operation.list(100, pvcList.getMetadata().getContinue()), compile, operation);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deletePVC(HasMetadata hasMetadata) {
|
||||
KubernetesClient kubernetesClient = getKubernetesClient();
|
||||
NonNamespaceOperation<PersistentVolumeClaim, PersistentVolumeClaimList, DoneablePersistentVolumeClaim, Resource<PersistentVolumeClaim, DoneablePersistentVolumeClaim>> operation = kubernetesClient.persistentVolumeClaims()
|
||||
.inNamespace(hasMetadata.getMetadata().getNamespace());
|
||||
|
||||
if (MapUtils.isNotEmpty(hasMetadata.getMetadata().getLabels())) {
|
||||
operation.withLabelSelector(new LabelSelector(null, hasMetadata.getMetadata().getLabels()));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(hasMetadata.getMetadata().getName())) {
|
||||
operation.withName(hasMetadata.getMetadata().getName());
|
||||
}
|
||||
Boolean delete = operation.delete();
|
||||
return delete == null ? false : delete;
|
||||
}
|
||||
|
||||
|
||||
public boolean deleteNamespace(String namespace) {
|
||||
KubernetesClient kubernetesClient = getKubernetesClient();
|
||||
Boolean delete = kubernetesClient.namespaces().withName(namespace).delete();
|
||||
|
||||
return delete == null ? false : delete;
|
||||
}
|
||||
|
||||
|
||||
public String getLog(String namespace, String pod, String container, int tailingLines) {
|
||||
try (KubernetesClient client = getKubernetesClient()) {
|
||||
PrettyLoggable<String, LogWatch> loggable;
|
||||
if (tailingLines > 0) {
|
||||
loggable = client.pods().inNamespace(namespace).withName(pod).inContainer(container).tailingLines(tailingLines);
|
||||
} else {
|
||||
loggable = client.pods().inNamespace(namespace).withName(pod).inContainer(container);
|
||||
}
|
||||
|
||||
return loggable.getLog();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package io.metersphere.engine.kubernetes.provider;
|
||||
|
||||
public class ClientCredential {
|
||||
|
||||
private String masterUrl;
|
||||
private String token;
|
||||
|
||||
public String getMasterUrl() {
|
||||
return masterUrl;
|
||||
}
|
||||
|
||||
public void setMasterUrl(String masterUrl) {
|
||||
this.masterUrl = masterUrl;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package io.metersphere.engine.kubernetes.provider;
|
||||
|
||||
public class DockerRegistry {
|
||||
|
||||
private String url;
|
||||
private String username;
|
||||
private String password;
|
||||
private String email;
|
||||
private String namespace;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package io.metersphere.engine.kubernetes.provider;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DockerRegistryUtil {
|
||||
|
||||
|
||||
public static String getDockerConfig(DockerRegistry registry) {
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("username", registry.getUsername());
|
||||
config.put("password", registry.getPassword());
|
||||
config.put("auth", Base64.getEncoder().encodeToString((registry.getUsername() + ":" + registry.getPassword()).getBytes()));
|
||||
|
||||
JSONObject jb = new JSONObject();
|
||||
jb.put(registry.getUrl(), config);
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("auths", jb);
|
||||
return Base64.getEncoder().encodeToString(result.toJSONString().getBytes());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package io.metersphere.engine.kubernetes.provider;
|
||||
|
||||
public class KubernetesProvider extends AbstractClientProvider {
|
||||
public KubernetesProvider(String credential) {
|
||||
super(credential);
|
||||
}
|
||||
}
|
|
@ -5,5 +5,5 @@ import io.metersphere.engine.EngineContext;
|
|||
import java.io.InputStream;
|
||||
|
||||
public interface EngineSourceParser {
|
||||
InputStream parse(EngineContext context, InputStream source) throws Exception;
|
||||
String parse(EngineContext context, InputStream source) throws Exception;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package io.metersphere.parse;
|
||||
|
||||
import io.metersphere.commons.constants.EngineType;
|
||||
import io.metersphere.commons.constants.FileType;
|
||||
import io.metersphere.parse.xml.XmlEngineSourceParse;
|
||||
|
||||
public class EngineSourceParserFactory {
|
||||
public static EngineSourceParser createEngineSourceParser(String type) {
|
||||
final EngineType engineType = EngineType.valueOf(type);
|
||||
final FileType engineType = FileType.valueOf(type);
|
||||
|
||||
if (EngineType.JMX.equals(engineType)) {
|
||||
if (FileType.JMX.equals(engineType)) {
|
||||
return new XmlEngineSourceParse();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import java.io.InputStream;
|
|||
|
||||
public class XmlEngineSourceParse implements EngineSourceParser {
|
||||
@Override
|
||||
public InputStream parse(EngineContext context, InputStream source) throws Exception {
|
||||
public String parse(EngineContext context, InputStream source) throws Exception {
|
||||
final InputSource inputSource = new InputSource(source);
|
||||
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
|
@ -21,7 +21,7 @@ public class XmlEngineSourceParse implements EngineSourceParser {
|
|||
DocumentBuilder docBuilder = factory.newDocumentBuilder();
|
||||
final Document document = docBuilder.parse(inputSource);
|
||||
|
||||
final DocumentParser documentParser = createDocumentParser(context.getEngineType());
|
||||
final DocumentParser documentParser = createDocumentParser(context.getFileType());
|
||||
|
||||
return documentParser.parse(context, document);
|
||||
}
|
||||
|
|
|
@ -6,5 +6,5 @@ import org.w3c.dom.Document;
|
|||
import java.io.InputStream;
|
||||
|
||||
public interface DocumentParser {
|
||||
InputStream parse(EngineContext context, Document document) throws Exception;
|
||||
String parse(EngineContext context, Document document) throws Exception;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
package io.metersphere.parse.xml.reader;
|
||||
|
||||
import io.metersphere.commons.constants.EngineType;
|
||||
import io.metersphere.commons.constants.FileType;
|
||||
import io.metersphere.parse.xml.reader.jmx.JmeterDocumentParser;
|
||||
|
||||
public class DocumentParserFactory {
|
||||
public static DocumentParser createDocumentParser(String type) {
|
||||
final EngineType engineType = EngineType.valueOf(type);
|
||||
final FileType fileType = FileType.valueOf(type);
|
||||
|
||||
if (EngineType.JMX.equals(engineType)) {
|
||||
switch (fileType) {
|
||||
case JMX:
|
||||
return new JmeterDocumentParser();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,11 @@ import javax.xml.transform.TransformerException;
|
|||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class JmeterDocumentParser implements DocumentParser {
|
||||
private final static String HASH_TREE_ELEMENT = "hashTree";
|
||||
private final static String TEST_PLAN = "TestPlan";
|
||||
private final static String STRING_PROP = "stringProp";
|
||||
private final static String COLLECTION_PROP = "collectionProp";
|
||||
private final static String CONCURRENCY_THREAD_GROUP = "com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup";
|
||||
|
@ -32,7 +30,7 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
private EngineContext context;
|
||||
|
||||
@Override
|
||||
public InputStream parse(EngineContext context, Document document) throws Exception {
|
||||
public String parse(EngineContext context, Document document) throws Exception {
|
||||
this.context = context;
|
||||
|
||||
final Element jmeterTestPlan = document.getDocumentElement();
|
||||
|
@ -49,18 +47,17 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
}
|
||||
}
|
||||
|
||||
return documentToInputStream(document);
|
||||
return documentToString(document);
|
||||
}
|
||||
|
||||
private InputStream documentToInputStream(Document document) throws TransformerException {
|
||||
private String documentToString(Document document) throws TransformerException {
|
||||
DOMSource domSource = new DOMSource(document);
|
||||
StringWriter writer = new StringWriter();
|
||||
StreamResult result = new StreamResult(writer);
|
||||
TransformerFactory tf = TransformerFactory.newInstance();
|
||||
Transformer transformer = tf.newTransformer();
|
||||
transformer.transform(domSource, result);
|
||||
final String resultStr = writer.toString();
|
||||
return new ByteArrayInputStream(resultStr.getBytes(StandardCharsets.UTF_8));
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
private void parseHashTree(Element hashTree) {
|
||||
|
@ -80,6 +77,8 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
|
||||
if (nodeNameEquals(ele, HASH_TREE_ELEMENT)) {
|
||||
parseHashTree(ele);
|
||||
} else if (nodeNameEquals(ele, TEST_PLAN)) {
|
||||
processTearDownTestPlan(ele);
|
||||
} else if (nodeNameEquals(ele, CONCURRENCY_THREAD_GROUP)) {
|
||||
processConcurrencyThreadGroup(ele);
|
||||
processCheckoutTimer(ele);
|
||||
|
@ -100,12 +99,107 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void processTearDownTestPlan(Element ele) {
|
||||
/*<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>*/
|
||||
Document document = ele.getOwnerDocument();
|
||||
Element tearDownSwitch = createBoolProp(document, "TestPlan.tearDown_on_shutdown", true);
|
||||
ele.appendChild(tearDownSwitch);
|
||||
|
||||
Node hashTree = ele.getNextSibling();
|
||||
while (!(hashTree instanceof Element)) {
|
||||
hashTree = hashTree.getNextSibling();
|
||||
}
|
||||
/*
|
||||
<PostThreadGroup guiclass="PostThreadGroupGui" testclass="PostThreadGroup" testname="tearDown Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">1</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">1</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
|
||||
</PostThreadGroup>
|
||||
*/
|
||||
Element tearDownElement = document.createElement("PostThreadGroup");
|
||||
tearDownElement.setAttribute("guiclass", "PostThreadGroupGui");
|
||||
tearDownElement.setAttribute("testclass", "PostThreadGroup");
|
||||
tearDownElement.setAttribute("testname", "tearDown Thread Group");
|
||||
tearDownElement.setAttribute("enabled", "true");
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "continue"));
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.num_threads", "1"));
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.ramp_time", "1"));
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.duration", ""));
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.delay", ""));
|
||||
tearDownElement.appendChild(createBoolProp(document, "ThreadGroup.scheduler", false));
|
||||
tearDownElement.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true));
|
||||
Element elementProp = document.createElement("elementProp");
|
||||
elementProp.setAttribute("name", "ThreadGroup.main_controller");
|
||||
elementProp.setAttribute("elementType", "LoopController");
|
||||
elementProp.setAttribute("guiclass", "LoopControlPanel");
|
||||
elementProp.setAttribute("testclass", "LoopController");
|
||||
elementProp.setAttribute("testname", "Loop Controller");
|
||||
elementProp.setAttribute("enabled", "true");
|
||||
elementProp.appendChild(createBoolProp(document, "LoopController.continue_forever", false));
|
||||
elementProp.appendChild(createStringProp(document, "LoopController.loops", "1"));
|
||||
tearDownElement.appendChild(elementProp);
|
||||
hashTree.appendChild(tearDownElement);
|
||||
|
||||
Element tearDownHashTree = document.createElement(HASH_TREE_ELEMENT);
|
||||
/*
|
||||
<OnceOnlyController guiclass="OnceOnlyControllerGui" testclass="OnceOnlyController" testname="Once Only Controller" enabled="true"/>
|
||||
*/
|
||||
Element onceOnlyController = document.createElement("OnceOnlyController");
|
||||
onceOnlyController.setAttribute("guiclass", "OnceOnlyControllerGui");
|
||||
onceOnlyController.setAttribute("testclass", "OnceOnlyController");
|
||||
onceOnlyController.setAttribute("testname", "Once Only Controller");
|
||||
onceOnlyController.setAttribute("enabled", "true");
|
||||
tearDownHashTree.appendChild(onceOnlyController);
|
||||
/*
|
||||
<hashTree>
|
||||
<DebugSampler guiclass="TestBeanGUI" testclass="DebugSampler" testname="Debug Sampler" enabled="true">
|
||||
<boolProp name="displayJMeterProperties">false</boolProp>
|
||||
<boolProp name="displayJMeterVariables">true</boolProp>
|
||||
<boolProp name="displaySystemProperties">false</boolProp>
|
||||
</DebugSampler>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
*/
|
||||
Element onceOnlyHashTree = document.createElement(HASH_TREE_ELEMENT);
|
||||
Element debugSampler = document.createElement("DebugSampler");
|
||||
debugSampler.setAttribute("guiclass", "TestBeanGUI");
|
||||
debugSampler.setAttribute("testclass", "DebugSampler");
|
||||
debugSampler.setAttribute("testname", "Debug Sampler");
|
||||
debugSampler.setAttribute("enabled", "true");
|
||||
debugSampler.appendChild(createBoolProp(document, "displayJMeterProperties", false));
|
||||
debugSampler.appendChild(createBoolProp(document, "displayJMeterVariables", true));
|
||||
debugSampler.appendChild(createBoolProp(document, "displaySystemProperties", false));
|
||||
onceOnlyHashTree.appendChild(debugSampler);
|
||||
// 添加空的 hashTree
|
||||
onceOnlyHashTree.appendChild(document.createElement(HASH_TREE_ELEMENT));
|
||||
tearDownHashTree.appendChild(onceOnlyHashTree);
|
||||
hashTree.appendChild(tearDownHashTree);
|
||||
// 添加backend listener
|
||||
processCheckoutBackendListener(tearDownElement);
|
||||
}
|
||||
|
||||
private Element createBoolProp(Document document, String name, boolean value) {
|
||||
Element tearDownSwitch = document.createElement("boolProp");
|
||||
tearDownSwitch.setAttribute("name", name);
|
||||
tearDownSwitch.appendChild(document.createTextNode(String.valueOf(value)));
|
||||
return tearDownSwitch;
|
||||
}
|
||||
|
||||
private void processBackendListener(Element backendListener) {
|
||||
KafkaProperties kafkaProperties = CommonBeanFactory.getBean(KafkaProperties.class);
|
||||
Document document = backendListener.getOwnerDocument();
|
||||
// 清空child
|
||||
removeChildren(backendListener);
|
||||
backendListener.appendChild(createStringProp(document, "classname", "io.github.rahulsinghai.jmeter.backendlistener.kafka.KafkaBackendClient"));
|
||||
backendListener.appendChild(createStringProp(document, "QUEUE_SIZE", "5000"));
|
||||
// elementProp
|
||||
Element elementProp = document.createElement("elementProp");
|
||||
elementProp.setAttribute("name", "arguments");
|
||||
|
@ -139,6 +233,9 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
collectionProp.appendChild(createKafkaProp(document, "kafka.batch.size", kafkaProperties.getBatchSize()));
|
||||
collectionProp.appendChild(createKafkaProp(document, "kafka.client.id", kafkaProperties.getClientId()));
|
||||
collectionProp.appendChild(createKafkaProp(document, "kafka.connections.max.idle.ms", kafkaProperties.getConnectionsMaxIdleMs()));
|
||||
// 添加关联关系 test.id test.name
|
||||
collectionProp.appendChild(createKafkaProp(document, "test.id", context.getTestId()));
|
||||
collectionProp.appendChild(createKafkaProp(document, "test.name", context.getTestName()));
|
||||
|
||||
elementProp.appendChild(collectionProp);
|
||||
// set elementProp
|
||||
|
@ -263,6 +360,8 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
collectionProp.appendChild(childCollectionProp);
|
||||
timer.appendChild(collectionProp);
|
||||
timerParent.appendChild(timer);
|
||||
// 添加一个空的hashTree
|
||||
timerParent.appendChild(document.createElement(HASH_TREE_ELEMENT));
|
||||
}
|
||||
|
||||
private Element createStringProp(Document document, String name, String value) {
|
||||
|
|
|
@ -93,7 +93,9 @@ public class FuctionalTestService {
|
|||
fileMetadata.setSize(file.getSize());
|
||||
fileMetadata.setCreateTime(System.currentTimeMillis());
|
||||
fileMetadata.setUpdateTime(System.currentTimeMillis());
|
||||
fileMetadata.setType(EngineType.JMX.name());
|
||||
fileMetadata.setType("jmx");
|
||||
// TODO engine 选择
|
||||
fileMetadata.setEngine(EngineType.DOCKER.name());
|
||||
fileMetadataMapper.insert(fileMetadata);
|
||||
|
||||
FileContent fileContent = new FileContent();
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.FileContentMapper;
|
||||
import io.metersphere.base.mapper.FileMetadataMapper;
|
||||
import io.metersphere.base.mapper.LoadTestFileMapper;
|
||||
import io.metersphere.base.mapper.LoadTestMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtLoadTestMapper;
|
||||
import io.metersphere.commons.constants.EngineType;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.controller.request.testplan.*;
|
||||
import io.metersphere.dto.LoadTestDTO;
|
||||
import io.metersphere.engine.Engine;
|
||||
|
@ -29,8 +33,6 @@ public class LoadTestService {
|
|||
@Resource
|
||||
private ExtLoadTestMapper extLoadTestMapper;
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private FileMetadataMapper fileMetadataMapper;
|
||||
@Resource
|
||||
private FileContentMapper fileContentMapper;
|
||||
|
@ -51,7 +53,7 @@ public class LoadTestService {
|
|||
|
||||
public String save(SaveTestPlanRequest request, MultipartFile file) {
|
||||
if (file == null) {
|
||||
throw new IllegalArgumentException("文件不能为空!");
|
||||
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
|
||||
}
|
||||
|
||||
final FileMetadata fileMetadata = saveFile(file);
|
||||
|
@ -95,7 +97,9 @@ public class LoadTestService {
|
|||
fileMetadata.setSize(file.getSize());
|
||||
fileMetadata.setCreateTime(System.currentTimeMillis());
|
||||
fileMetadata.setUpdateTime(System.currentTimeMillis());
|
||||
fileMetadata.setType(EngineType.JMX.name());
|
||||
fileMetadata.setType("JMX");
|
||||
// TODO engine 选择
|
||||
fileMetadata.setEngine(EngineType.DOCKER.name());
|
||||
fileMetadataMapper.insert(fileMetadata);
|
||||
|
||||
FileContent fileContent = new FileContent();
|
||||
|
@ -123,7 +127,7 @@ public class LoadTestService {
|
|||
|
||||
final LoadTestWithBLOBs loadTest = loadTestMapper.selectByPrimaryKey(request.getId());
|
||||
if (loadTest == null) {
|
||||
MSException.throwException("无法编辑测试,未找到测试:" + request.getId());
|
||||
MSException.throwException(Translator.get("edit_load_test_not_found") + request.getId());
|
||||
} else {
|
||||
loadTest.setName(request.getName());
|
||||
loadTest.setProjectId(request.getProjectId());
|
||||
|
@ -141,23 +145,25 @@ public class LoadTestService {
|
|||
public void run(RunTestPlanRequest request) {
|
||||
final LoadTestWithBLOBs loadTest = loadTestMapper.selectByPrimaryKey(request.getId());
|
||||
if (loadTest == null) {
|
||||
MSException.throwException("无法运行测试,未找到测试:" + request.getId());
|
||||
MSException.throwException(Translator.get("run_load_test_not_found") + request.getId());
|
||||
}
|
||||
|
||||
final FileMetadata fileMetadata = fileService.getFileMetadataByTestId(request.getId());
|
||||
if (fileMetadata == null) {
|
||||
MSException.throwException("无法运行测试,无法获取测试文件元信息,测试ID:" + request.getId());
|
||||
MSException.throwException(Translator.get("run_load_test_file_not_found") + request.getId());
|
||||
}
|
||||
|
||||
final FileContent fileContent = fileService.getFileContent(fileMetadata.getId());
|
||||
if (fileContent == null) {
|
||||
MSException.throwException("无法运行测试,无法获取测试文件内容,测试ID:" + request.getId());
|
||||
MSException.throwException(Translator.get("run_load_test_file_content_not_found") + request.getId());
|
||||
}
|
||||
|
||||
System.out.println("开始运行:" + loadTest.getName());
|
||||
final Engine engine = EngineFactory.createEngine(fileMetadata.getType());
|
||||
LogUtil.info("Load test started " + loadTest.getName());
|
||||
// engine type (DOCKER|KUBERNETES)
|
||||
// todo set type
|
||||
final Engine engine = EngineFactory.createEngine(fileMetadata.getEngine());
|
||||
if (engine == null) {
|
||||
MSException.throwException(String.format("无法运行测试,未识别测试文件类型,测试ID:%s,文件类型:%s",
|
||||
MSException.throwException(String.format("Test cannot be run,test ID:%s,file type:%s",
|
||||
request.getId(),
|
||||
fileMetadata.getType()));
|
||||
}
|
||||
|
@ -169,12 +175,12 @@ public class LoadTestService {
|
|||
MSException.throwException(e);
|
||||
}
|
||||
if (!init) {
|
||||
MSException.throwException(String.format("无法运行测试,初始化运行环境失败,测试ID:%s", request.getId()));
|
||||
MSException.throwException(Translator.get("run_load_test_file_init_error") + request.getId());
|
||||
}
|
||||
|
||||
engine.start();
|
||||
|
||||
/// todo:通过调用stop方法能够停止正在运行的engine,但是如果部署了多个backend实例,页面发送的停止请求如何定位到具体的engine
|
||||
// todo:通过调用stop方法能够停止正在运行的engine,但是如果部署了多个backend实例,页面发送的停止请求如何定位到具体的engine
|
||||
}
|
||||
|
||||
public List<LoadTestDTO> recentTestPlans(QueryTestPlanRequest request) {
|
||||
|
|
|
@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS `file_metadata` (
|
|||
`id` varchar(64) NOT NULL COMMENT 'File ID',
|
||||
`name` varchar(64) NOT NULL COMMENT 'File name',
|
||||
`type` varchar(64) DEFAULT NULL COMMENT 'File type',
|
||||
`engine` varchar(64) DEFAULT 'DOCKER' COMMENT 'engine type',
|
||||
`size` bigint(13) NOT NULL COMMENT 'File size',
|
||||
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
|
||||
`update_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
|
||||
|
|
|
@ -6,5 +6,11 @@
|
|||
"workspace_name_is_null": "Workspace name cannot be null",
|
||||
"workspace_name_already_exists": "The workspace name already exists",
|
||||
"workspace_does_not_belong_to_user": "The current workspace does not belong to the current user",
|
||||
"organization_does_not_belong_to_user": "The current organization does not belong to the current user"
|
||||
"organization_does_not_belong_to_user": "The current organization does not belong to the current user",
|
||||
"file_cannot_be_null": "File cannot be empty!",
|
||||
"edit_load_test_not_found": "Cannot edit test, test not found:",
|
||||
"run_load_test_not_found": "Cannot run test, test not found:",
|
||||
"run_load_test_file_not_found": "Unable to run test, unable to get test file meta information, test ID:",
|
||||
"run_load_test_file_content_not_found": "Cannot run test, cannot get test file content, test ID:",
|
||||
"run_load_test_file_init_error": "Failed to run test, failed to initialize run environment, test ID:"
|
||||
}
|
|
@ -6,5 +6,11 @@
|
|||
"workspace_name_is_null": "工作空间名不能为空",
|
||||
"workspace_name_already_exists": "工作空间名已存在",
|
||||
"workspace_does_not_belong_to_user": "当前工作空间不属于当前用户",
|
||||
"organization_does_not_belong_to_user": "当前组织不属于当前用户"
|
||||
"organization_does_not_belong_to_user": "当前组织不属于当前用户",
|
||||
"file_cannot_be_null": "文件不能为空!",
|
||||
"edit_load_test_not_found": "无法编辑测试,未找到测试:",
|
||||
"run_load_test_not_found": "无法运行测试,未找到测试:",
|
||||
"run_load_test_file_not_found": "无法运行测试,无法获取测试文件元信息,测试ID:",
|
||||
"run_load_test_file_content_not_found": "无法运行测试,无法获取测试文件内容,测试ID:",
|
||||
"run_load_test_file_init_error": "无法运行测试,初始化运行环境失败,测试ID:"
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package io.metersphere;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.api.model.PodList;
|
||||
import io.fabric8.kubernetes.client.ConfigBuilder;
|
||||
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BaseTest {
|
||||
protected KubernetesClient kubernetesClient;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
try {
|
||||
ConfigBuilder configBuilder = new ConfigBuilder();
|
||||
configBuilder.withMasterUrl("https://172.16.10.93:6443");
|
||||
kubernetesClient = new DefaultKubernetesClient(configBuilder.build());
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1() {
|
||||
PodList list = kubernetesClient.pods().list();
|
||||
for (Pod item : list.getItems()) {
|
||||
System.out.println(item);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import java.io.StringWriter;
|
|||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class JmxFileParseTest {
|
||||
private final static String HASH_TREE_ELEMENT = "hashTree";
|
||||
private final static String TEST_PLAN = "TestPlan";
|
||||
private final static String STRING_PROP = "stringProp";
|
||||
private final static String CONCURRENCY_THREAD_GROUP = "com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup";
|
||||
private final static String VARIABLE_THROUGHPUT_TIMER = "kg.apc.jmeter.timers.VariableThroughputTimer";
|
||||
|
@ -94,6 +95,8 @@ public class JmxFileParseTest {
|
|||
|
||||
if (nodeNameEquals(ele, HASH_TREE_ELEMENT)) {
|
||||
parseHashTree(ele);
|
||||
} else if (nodeNameEquals(ele, TEST_PLAN)) {
|
||||
processTearDownTestPlan(ele);
|
||||
} else if (nodeNameEquals(ele, CONCURRENCY_THREAD_GROUP)) {
|
||||
processConcurrencyThreadGroup(ele);
|
||||
processCheckoutTimer(ele);
|
||||
|
@ -113,11 +116,106 @@ public class JmxFileParseTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void processTearDownTestPlan(Element ele) {
|
||||
/*<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>*/
|
||||
Document document = ele.getOwnerDocument();
|
||||
Element tearDownSwitch = createBoolProp(document, "TestPlan.tearDown_on_shutdown", true);
|
||||
ele.appendChild(tearDownSwitch);
|
||||
|
||||
Node hashTree = ele.getNextSibling();
|
||||
while (!(hashTree instanceof Element)) {
|
||||
hashTree = hashTree.getNextSibling();
|
||||
}
|
||||
/*
|
||||
<PostThreadGroup guiclass="PostThreadGroupGui" testclass="PostThreadGroup" testname="tearDown Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">1</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">1</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
|
||||
</PostThreadGroup>
|
||||
*/
|
||||
Element tearDownElement = document.createElement("PostThreadGroup");
|
||||
tearDownElement.setAttribute("guiclass", "PostThreadGroupGui");
|
||||
tearDownElement.setAttribute("testclass", "PostThreadGroup");
|
||||
tearDownElement.setAttribute("testname", "tearDown Thread Group");
|
||||
tearDownElement.setAttribute("enabled", "true");
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "continue"));
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.num_threads", "1"));
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.ramp_time", "1"));
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.duration", ""));
|
||||
tearDownElement.appendChild(createStringProp(document, "ThreadGroup.delay", ""));
|
||||
tearDownElement.appendChild(createBoolProp(document, "ThreadGroup.scheduler", false));
|
||||
tearDownElement.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true));
|
||||
Element elementProp = document.createElement("elementProp");
|
||||
elementProp.setAttribute("name", "ThreadGroup.main_controller");
|
||||
elementProp.setAttribute("elementType", "LoopController");
|
||||
elementProp.setAttribute("guiclass", "LoopControlPanel");
|
||||
elementProp.setAttribute("testclass", "LoopController");
|
||||
elementProp.setAttribute("testname", "Loop Controller");
|
||||
elementProp.setAttribute("enabled", "true");
|
||||
elementProp.appendChild(createBoolProp(document, "LoopController.continue_forever", false));
|
||||
elementProp.appendChild(createStringProp(document, "LoopController.loops", "1"));
|
||||
tearDownElement.appendChild(elementProp);
|
||||
hashTree.appendChild(tearDownElement);
|
||||
|
||||
Element tearDownHashTree = document.createElement(HASH_TREE_ELEMENT);
|
||||
/*
|
||||
<OnceOnlyController guiclass="OnceOnlyControllerGui" testclass="OnceOnlyController" testname="Once Only Controller" enabled="true"/>
|
||||
*/
|
||||
Element onceOnlyController = document.createElement("OnceOnlyController");
|
||||
onceOnlyController.setAttribute("guiclass", "OnceOnlyControllerGui");
|
||||
onceOnlyController.setAttribute("testclass", "OnceOnlyController");
|
||||
onceOnlyController.setAttribute("testname", "Once Only Controller");
|
||||
onceOnlyController.setAttribute("enabled", "true");
|
||||
tearDownHashTree.appendChild(onceOnlyController);
|
||||
/*
|
||||
<hashTree>
|
||||
<DebugSampler guiclass="TestBeanGUI" testclass="DebugSampler" testname="Debug Sampler" enabled="true">
|
||||
<boolProp name="displayJMeterProperties">false</boolProp>
|
||||
<boolProp name="displayJMeterVariables">true</boolProp>
|
||||
<boolProp name="displaySystemProperties">false</boolProp>
|
||||
</DebugSampler>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
*/
|
||||
Element onceOnlyHashTree = document.createElement(HASH_TREE_ELEMENT);
|
||||
Element debugSampler = document.createElement("DebugSampler");
|
||||
debugSampler.setAttribute("guiclass", "TestBeanGUI");
|
||||
debugSampler.setAttribute("testclass", "DebugSampler");
|
||||
debugSampler.setAttribute("testname", "Debug Sampler");
|
||||
debugSampler.setAttribute("enabled", "true");
|
||||
debugSampler.appendChild(createBoolProp(document, "displayJMeterProperties", false));
|
||||
debugSampler.appendChild(createBoolProp(document, "displayJMeterVariables", true));
|
||||
debugSampler.appendChild(createBoolProp(document, "displaySystemProperties", false));
|
||||
onceOnlyHashTree.appendChild(debugSampler);
|
||||
// 添加空的 hashTree
|
||||
onceOnlyHashTree.appendChild(document.createElement(HASH_TREE_ELEMENT));
|
||||
tearDownHashTree.appendChild(onceOnlyHashTree);
|
||||
hashTree.appendChild(tearDownHashTree);
|
||||
// 添加backend listener
|
||||
processCheckoutBackendListener(tearDownElement);
|
||||
}
|
||||
|
||||
private Element createBoolProp(Document document, String name, boolean value) {
|
||||
Element tearDownSwitch = document.createElement("boolProp");
|
||||
tearDownSwitch.setAttribute("name", name);
|
||||
tearDownSwitch.appendChild(document.createTextNode(String.valueOf(value)));
|
||||
return tearDownSwitch;
|
||||
}
|
||||
|
||||
private void processBackendListener(Element backendListener) {
|
||||
Document document = backendListener.getOwnerDocument();
|
||||
// 清空child
|
||||
removeChildren(backendListener);
|
||||
backendListener.appendChild(createStringProp(document, "classname", "io.github.rahulsinghai.jmeter.backendlistener.kafka.KafkaBackendClient"));
|
||||
backendListener.appendChild(createStringProp(document, "QUEUE_SIZE", "5000"));
|
||||
// elementProp
|
||||
Element elementProp = document.createElement("elementProp");
|
||||
elementProp.setAttribute("name", "arguments");
|
||||
|
@ -151,6 +249,9 @@ public class JmxFileParseTest {
|
|||
collectionProp.appendChild(createKafkaProp(document, "kafka.batch.size", kafkaProperties.getBatchSize()));
|
||||
collectionProp.appendChild(createKafkaProp(document, "kafka.client.id", kafkaProperties.getClientId()));
|
||||
collectionProp.appendChild(createKafkaProp(document, "kafka.connections.max.idle.ms", kafkaProperties.getConnectionsMaxIdleMs()));
|
||||
// 添加关联关系 test.id test.name
|
||||
collectionProp.appendChild(createKafkaProp(document, "test.id", ""));
|
||||
collectionProp.appendChild(createKafkaProp(document, "test.name", ""));
|
||||
|
||||
elementProp.appendChild(collectionProp);
|
||||
// set elementProp
|
||||
|
|
Loading…
Reference in New Issue