Merge branch 'dev' of https://github.com/metersphere/server into dev
This commit is contained in:
commit
5ab8c54f93
|
@ -18,16 +18,9 @@
|
||||||
<shiro.version>1.5.1</shiro.version>
|
<shiro.version>1.5.1</shiro.version>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<jmeter.version>5.2.1</jmeter.version>
|
<jmeter.version>5.2.1</jmeter.version>
|
||||||
<kubernetes-client.version>4.9.0</kubernetes-client.version>
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- kubernetes client-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.fabric8</groupId>
|
|
||||||
<artifactId>kubernetes-client</artifactId>
|
|
||||||
<version>${kubernetes-client.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
package io.metersphere.commons.constants;
|
package io.metersphere.commons.constants;
|
||||||
|
|
||||||
public enum ResourcePoolTypeEnum {
|
public enum ResourcePoolTypeEnum {
|
||||||
/**
|
|
||||||
* k8s 资源池
|
|
||||||
*/
|
|
||||||
K8S,
|
|
||||||
/**
|
/**
|
||||||
* node controller 资源池
|
* node controller 资源池
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -57,7 +57,7 @@ public abstract class AbstractEngine implements Engine {
|
||||||
if (resourcePool == null) {
|
if (resourcePool == null) {
|
||||||
MSException.throwException("Resource Pool is empty");
|
MSException.throwException("Resource Pool is empty");
|
||||||
}
|
}
|
||||||
if (!ResourcePoolTypeEnum.K8S.name().equals(resourcePool.getType()) && !ResourcePoolTypeEnum.NODE.name().equals(resourcePool.getType())) {
|
if (!ResourcePoolTypeEnum.NODE.name().equals(resourcePool.getType())) {
|
||||||
MSException.throwException("Invalid Resource Pool type.");
|
MSException.throwException("Invalid Resource Pool type.");
|
||||||
}
|
}
|
||||||
this.resourceList = testResourceService.getResourcesByPoolId(resourcePool.getId());
|
this.resourceList = testResourceService.getResourcesByPoolId(resourcePool.getId());
|
||||||
|
|
|
@ -12,7 +12,6 @@ import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.config.KafkaProperties;
|
import io.metersphere.config.KafkaProperties;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.performance.engine.docker.DockerTestEngine;
|
import io.metersphere.performance.engine.docker.DockerTestEngine;
|
||||||
import io.metersphere.performance.engine.kubernetes.KubernetesTestEngine;
|
|
||||||
import io.metersphere.performance.parse.EngineSourceParser;
|
import io.metersphere.performance.parse.EngineSourceParser;
|
||||||
import io.metersphere.performance.parse.EngineSourceParserFactory;
|
import io.metersphere.performance.parse.EngineSourceParserFactory;
|
||||||
import io.metersphere.service.FileService;
|
import io.metersphere.service.FileService;
|
||||||
|
@ -47,11 +46,8 @@ public class EngineFactory {
|
||||||
|
|
||||||
final ResourcePoolTypeEnum type = ResourcePoolTypeEnum.valueOf(resourcePool.getType());
|
final ResourcePoolTypeEnum type = ResourcePoolTypeEnum.valueOf(resourcePool.getType());
|
||||||
|
|
||||||
switch (type) {
|
if (type == ResourcePoolTypeEnum.NODE) {
|
||||||
case NODE:
|
|
||||||
return new DockerTestEngine(loadTest);
|
return new DockerTestEngine(loadTest);
|
||||||
case K8S:
|
|
||||||
return new KubernetesTestEngine(loadTest);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
package io.metersphere.performance.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.base.domain.LoadTestWithBLOBs;
|
|
||||||
import io.metersphere.commons.constants.FileType;
|
|
||||||
import io.metersphere.commons.exception.MSException;
|
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
|
||||||
import io.metersphere.i18n.Translator;
|
|
||||||
import io.metersphere.performance.engine.AbstractEngine;
|
|
||||||
import io.metersphere.performance.engine.EngineContext;
|
|
||||||
import io.metersphere.performance.engine.EngineFactory;
|
|
||||||
import io.metersphere.performance.engine.kubernetes.crds.jmeter.Jmeter;
|
|
||||||
import io.metersphere.performance.engine.kubernetes.crds.jmeter.JmeterSpec;
|
|
||||||
import io.metersphere.performance.engine.kubernetes.provider.ClientCredential;
|
|
||||||
import io.metersphere.performance.engine.kubernetes.provider.KubernetesProvider;
|
|
||||||
import org.apache.commons.collections.MapUtils;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class KubernetesTestEngine extends AbstractEngine {
|
|
||||||
|
|
||||||
|
|
||||||
public KubernetesTestEngine(LoadTestWithBLOBs loadTest) {
|
|
||||||
this.init(loadTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(LoadTestWithBLOBs loadTest) {
|
|
||||||
super.init(loadTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
Integer sumThreadNum = getRunningThreadNum();
|
|
||||||
// resourceList size 1
|
|
||||||
resourceList.forEach(r -> {
|
|
||||||
String configuration = r.getConfiguration();
|
|
||||||
ClientCredential clientCredential = JSON.parseObject(configuration, ClientCredential.class);
|
|
||||||
// 最大并发数
|
|
||||||
Integer maxConcurrency = clientCredential.getMaxConcurrency();
|
|
||||||
// 当前测试需要的并发数大于剩余的并发数报错
|
|
||||||
if (threadNum > maxConcurrency - sumThreadNum) {
|
|
||||||
MSException.throwException(Translator.get("max_thread_insufficient"));
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
EngineContext context = EngineFactory.createContext(loadTest, r.getId(), threadNum, this.getStartTime(), this.getReportId());
|
|
||||||
runTest(context, clientCredential);
|
|
||||||
} catch (Exception e) {
|
|
||||||
MSException.throwException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runTest(EngineContext context, ClientCredential credential) {
|
|
||||||
KubernetesProvider kubernetesProvider = new KubernetesProvider(JSON.toJSONString(credential));
|
|
||||||
|
|
||||||
// create namespace
|
|
||||||
kubernetesProvider.confirmNamespace(context.getNamespace());
|
|
||||||
// create cm
|
|
||||||
try (KubernetesClient client = kubernetesProvider.getKubernetesClient()) {
|
|
||||||
String configMapName = "jmeter-" + 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() + FileType.JMX.suffix(), context.getContent());
|
|
||||||
if (MapUtils.isNotEmpty(context.getTestData())) {
|
|
||||||
putAll(context.getTestData());
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
client.configMaps().inNamespace(context.getNamespace()).create(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// create jmeter
|
|
||||||
// todo image
|
|
||||||
try {
|
|
||||||
Jmeter jmeter = new Jmeter();
|
|
||||||
jmeter.setMetadata(new ObjectMeta() {{
|
|
||||||
setNamespace(context.getNamespace());
|
|
||||||
setName(context.getTestId());
|
|
||||||
}});
|
|
||||||
jmeter.setSpec(new JmeterSpec() {{
|
|
||||||
setReplicas(1);
|
|
||||||
setImage(JMETER_IMAGE);
|
|
||||||
setEnv(context.getEnv());
|
|
||||||
}});
|
|
||||||
LogUtil.info("Load test started. " + context.getTestId());
|
|
||||||
kubernetesProvider.applyCustomResource(jmeter);
|
|
||||||
} catch (Exception e) {
|
|
||||||
MSException.throwException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() {
|
|
||||||
resourceList.forEach(r -> {
|
|
||||||
try {
|
|
||||||
String configuration = r.getConfiguration();
|
|
||||||
ClientCredential clientCredential = JSON.parseObject(configuration, ClientCredential.class);
|
|
||||||
KubernetesProvider provider = new KubernetesProvider(JSON.toJSONString(clientCredential));
|
|
||||||
provider.confirmNamespace(loadTest.getProjectId());
|
|
||||||
Jmeter jmeter = new Jmeter();
|
|
||||||
jmeter.setMetadata(new ObjectMeta() {{
|
|
||||||
setName(loadTest.getId());
|
|
||||||
setNamespace(loadTest.getProjectId());
|
|
||||||
}});
|
|
||||||
jmeter.setSpec(new JmeterSpec() {{
|
|
||||||
setReplicas(1);
|
|
||||||
setImage(JMETER_IMAGE);
|
|
||||||
}});
|
|
||||||
provider.deleteCustomResource(jmeter);
|
|
||||||
} catch (Exception e) {
|
|
||||||
MSException.throwException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, String> log() {
|
|
||||||
Map<String, String> logs = new HashMap<>();
|
|
||||||
resourceList.forEach(r -> {
|
|
||||||
try {
|
|
||||||
String configuration = r.getConfiguration();
|
|
||||||
ClientCredential clientCredential = JSON.parseObject(configuration, ClientCredential.class);
|
|
||||||
KubernetesProvider provider = new KubernetesProvider(JSON.toJSONString(clientCredential));
|
|
||||||
provider.confirmNamespace(loadTest.getProjectId());
|
|
||||||
try (KubernetesClient client = provider.getKubernetesClient()) {
|
|
||||||
String joblog = client.batch().jobs().inNamespace(loadTest.getProjectId()).withName("job-" + loadTest.getId()).getLog();
|
|
||||||
logs.put(clientCredential.getMasterUrl(), joblog);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
MSException.throwException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
return logs;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package io.metersphere.performance.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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package io.metersphere.performance.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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package io.metersphere.performance.engine.kubernetes.crds;
|
|
||||||
|
|
||||||
import io.fabric8.kubernetes.client.CustomResourceList;
|
|
||||||
|
|
||||||
public class MeterSphereCustomResourceList<T extends MeterSphereCustomResource> extends CustomResourceList<T> {
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package io.metersphere.performance.engine.kubernetes.crds.jmeter;
|
|
||||||
|
|
||||||
import io.metersphere.performance.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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package io.metersphere.performance.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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package io.metersphere.performance.engine.kubernetes.crds.jmeter;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
|
||||||
import io.fabric8.kubernetes.api.model.KubernetesResource;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@JsonDeserialize
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
|
||||||
public class JmeterSpec implements KubernetesResource {
|
|
||||||
private int replicas = 1;
|
|
||||||
private String image;
|
|
||||||
private Map<String, String> env = new HashMap<>();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getEnv() {
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnv(Map<String, String> env) {
|
|
||||||
this.env = env;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package io.metersphere.performance.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 + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,267 +0,0 @@
|
||||||
package io.metersphere.performance.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.performance.engine.kubernetes.crds.MeterSphereCustomResource;
|
|
||||||
import io.metersphere.performance.engine.kubernetes.crds.MeterSphereCustomResourceDoneable;
|
|
||||||
import io.metersphere.performance.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package io.metersphere.performance.engine.kubernetes.provider;
|
|
||||||
|
|
||||||
public class ClientCredential {
|
|
||||||
|
|
||||||
private String masterUrl;
|
|
||||||
private String token;
|
|
||||||
private Integer maxConcurrency;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getMaxConcurrency() {
|
|
||||||
return maxConcurrency;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxConcurrency(Integer maxConcurrency) {
|
|
||||||
this.maxConcurrency = maxConcurrency;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package io.metersphere.performance.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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package io.metersphere.performance.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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package io.metersphere.performance.engine.kubernetes.provider;
|
|
||||||
|
|
||||||
public class KubernetesProvider extends AbstractClientProvider {
|
|
||||||
public KubernetesProvider(String credential) {
|
|
||||||
super(credential);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -199,7 +199,7 @@ public class PerformanceTestService {
|
||||||
}
|
}
|
||||||
|
|
||||||
LogUtil.info("Load test started " + loadTest.getName());
|
LogUtil.info("Load test started " + loadTest.getName());
|
||||||
// engine type (NODE|K8S)
|
// engine type (NODE)
|
||||||
final Engine engine = EngineFactory.createEngine(loadTest);
|
final Engine engine = EngineFactory.createEngine(loadTest);
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
MSException.throwException(String.format("Test cannot be run,test ID:%s", request.getId()));
|
MSException.throwException(String.format("Test cannot be run,test ID:%s", request.getId()));
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package io.metersphere.service;
|
package io.metersphere.service;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import io.metersphere.base.domain.*;
|
import io.metersphere.base.domain.TestResource;
|
||||||
|
import io.metersphere.base.domain.TestResourceExample;
|
||||||
|
import io.metersphere.base.domain.TestResourcePool;
|
||||||
|
import io.metersphere.base.domain.TestResourcePoolExample;
|
||||||
import io.metersphere.base.mapper.TestResourceMapper;
|
import io.metersphere.base.mapper.TestResourceMapper;
|
||||||
import io.metersphere.base.mapper.TestResourcePoolMapper;
|
import io.metersphere.base.mapper.TestResourcePoolMapper;
|
||||||
import io.metersphere.commons.constants.ResourcePoolTypeEnum;
|
|
||||||
import io.metersphere.commons.constants.ResourceStatusEnum;
|
import io.metersphere.commons.constants.ResourceStatusEnum;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
@ -12,7 +14,6 @@ import io.metersphere.controller.request.resourcepool.QueryResourcePoolRequest;
|
||||||
import io.metersphere.dto.NodeDTO;
|
import io.metersphere.dto.NodeDTO;
|
||||||
import io.metersphere.dto.TestResourcePoolDTO;
|
import io.metersphere.dto.TestResourcePoolDTO;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.performance.engine.kubernetes.provider.KubernetesProvider;
|
|
||||||
import org.apache.commons.beanutils.BeanUtils;
|
import org.apache.commons.beanutils.BeanUtils;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -145,9 +146,6 @@ public class TestResourcePoolService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateTestResourcePool(TestResourcePoolDTO testResourcePool) {
|
private boolean validateTestResourcePool(TestResourcePoolDTO testResourcePool) {
|
||||||
if (StringUtils.equalsIgnoreCase(testResourcePool.getType(), ResourcePoolTypeEnum.K8S.name())) {
|
|
||||||
return validateK8s(testResourcePool);
|
|
||||||
}
|
|
||||||
return validateNodes(testResourcePool);
|
return validateNodes(testResourcePool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,31 +192,6 @@ public class TestResourcePoolService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateK8s(TestResourcePoolDTO testResourcePool) {
|
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(testResourcePool.getResources()) || testResourcePool.getResources().size() != 1) {
|
|
||||||
throw new RuntimeException(Translator.get("only_one_k8s"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TestResource testResource = testResourcePool.getResources().get(0);
|
|
||||||
testResource.setTestResourcePoolId(testResourcePool.getId());
|
|
||||||
boolean isValid;
|
|
||||||
try {
|
|
||||||
KubernetesProvider provider = new KubernetesProvider(testResource.getConfiguration());
|
|
||||||
provider.validateCredential();
|
|
||||||
testResource.setStatus(VALID.name());
|
|
||||||
testResourcePool.setStatus(VALID.name());
|
|
||||||
isValid = true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
testResource.setStatus(ResourceStatusEnum.INVALID.name());
|
|
||||||
testResourcePool.setStatus(ResourceStatusEnum.INVALID.name());
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
deleteTestResource(testResourcePool.getId());
|
|
||||||
updateTestResource(testResource);
|
|
||||||
return isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTestResource(TestResource testResource) {
|
private void updateTestResource(TestResource testResource) {
|
||||||
testResource.setUpdateTime(System.currentTimeMillis());
|
testResource.setUpdateTime(System.currentTimeMillis());
|
||||||
testResource.setCreateTime(System.currentTimeMillis());
|
testResource.setCreateTime(System.currentTimeMillis());
|
||||||
|
|
|
@ -10,10 +10,10 @@ import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
||||||
import io.metersphere.commons.constants.TestCaseConstants;
|
import io.metersphere.commons.constants.TestCaseConstants;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.BeanUtils;
|
import io.metersphere.commons.utils.BeanUtils;
|
||||||
|
import io.metersphere.exception.ExcelException;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.track.dto.TestCaseDTO;
|
import io.metersphere.track.dto.TestCaseDTO;
|
||||||
import io.metersphere.track.dto.TestCaseNodeDTO;
|
import io.metersphere.track.dto.TestCaseNodeDTO;
|
||||||
import io.metersphere.exception.ExcelException;
|
|
||||||
import io.metersphere.track.request.testcase.DragNodeRequest;
|
import io.metersphere.track.request.testcase.DragNodeRequest;
|
||||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -22,7 +22,6 @@ import org.apache.ibatis.session.SqlSession;
|
||||||
import org.apache.ibatis.session.SqlSessionFactory;
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.yaml.snakeyaml.nodes.NodeId;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
|
@ -37,7 +37,6 @@ test_not_running=Test is not running
|
||||||
load_test_already_exists=Duplicate load test name
|
load_test_already_exists=Duplicate load test name
|
||||||
no_nodes_message=No node message
|
no_nodes_message=No node message
|
||||||
duplicate_node_ip=Duplicate IPs
|
duplicate_node_ip=Duplicate IPs
|
||||||
only_one_k8s=Only one K8s can be added
|
|
||||||
max_thread_insufficient=The number of concurrent users exceeds
|
max_thread_insufficient=The number of concurrent users exceeds
|
||||||
#workspace
|
#workspace
|
||||||
workspace_name_is_null=Workspace name cannot be null
|
workspace_name_is_null=Workspace name cannot be null
|
||||||
|
|
|
@ -37,7 +37,6 @@ test_not_running=测试未运行
|
||||||
load_test_already_exists=测试名称不能重复
|
load_test_already_exists=测试名称不能重复
|
||||||
no_nodes_message=没有节点信息
|
no_nodes_message=没有节点信息
|
||||||
duplicate_node_ip=节点 IP 重复
|
duplicate_node_ip=节点 IP 重复
|
||||||
only_one_k8s=只能添加一个 K8s
|
|
||||||
max_thread_insufficient=并发用户数超额
|
max_thread_insufficient=并发用户数超额
|
||||||
#workspace
|
#workspace
|
||||||
workspace_name_is_null=工作空间名不能为空
|
workspace_name_is_null=工作空间名不能为空
|
||||||
|
|
|
@ -37,7 +37,6 @@ test_not_running=測試未運行
|
||||||
load_test_already_exists=測試名稱不能重復
|
load_test_already_exists=測試名稱不能重復
|
||||||
no_nodes_message=沒有節點信息
|
no_nodes_message=沒有節點信息
|
||||||
duplicate_node_ip=節點 IP 重復
|
duplicate_node_ip=節點 IP 重復
|
||||||
only_one_k8s=只能添加壹個 K8s
|
|
||||||
max_thread_insufficient=並發用戶數超額
|
max_thread_insufficient=並發用戶數超額
|
||||||
#workspace
|
#workspace
|
||||||
workspace_name_is_null=工作空間名不能為空
|
workspace_name_is_null=工作空間名不能為空
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
<el-table-column prop="type" :label="$t('test_resource_pool.type')">
|
<el-table-column prop="type" :label="$t('test_resource_pool.type')">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<span v-if="scope.row.type === 'NODE'">Node</span>
|
<span v-if="scope.row.type === 'NODE'">Node</span>
|
||||||
<span v-if="scope.row.type === 'K8S'">Kubernetes</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="status" :label="$t('test_resource_pool.enable_disable')">
|
<el-table-column prop="status" :label="$t('test_resource_pool.enable_disable')">
|
||||||
|
@ -61,31 +60,10 @@
|
||||||
<el-form-item :label="$t('test_resource_pool.type')" prop="type">
|
<el-form-item :label="$t('test_resource_pool.type')" prop="type">
|
||||||
<el-select v-model="form.type" :placeholder="$t('test_resource_pool.select_pool_type')"
|
<el-select v-model="form.type" :placeholder="$t('test_resource_pool.select_pool_type')"
|
||||||
@change="changeResourceType()">
|
@change="changeResourceType()">
|
||||||
<el-option key="K8S" value="K8S" label="Kubernetes">Kubernetes</el-option>
|
|
||||||
<el-option key="NODE" value="NODE" label="Node">Node</el-option>
|
<el-option key="NODE" value="NODE" label="Node">Node</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div v-for="(item,index) in infoList " :key="index">
|
<div v-for="(item,index) in infoList " :key="index">
|
||||||
<div class="node-line" v-if="form.type === 'K8S'">
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="9">
|
|
||||||
<el-form-item prop="masterUrl" label="Master URL">
|
|
||||||
<el-input v-model="item.masterUrl" autocomplete="new-password"/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="9">
|
|
||||||
<el-form-item prop="password" label="Token" style="padding-left: 20px">
|
|
||||||
<el-input v-model="item.token" autocomplete="new-password"/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="6">
|
|
||||||
<el-form-item prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')"
|
|
||||||
style="padding-left: 20px">
|
|
||||||
<el-input-number v-model="item.maxConcurrency" :min="1" :max="9999"></el-input-number>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
<div class="node-line" v-if="form.type === 'NODE'">
|
<div class="node-line" v-if="form.type === 'NODE'">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
|
@ -144,31 +122,10 @@
|
||||||
<el-form-item :label="$t('test_resource_pool.type')" prop="type">
|
<el-form-item :label="$t('test_resource_pool.type')" prop="type">
|
||||||
<el-select v-model="form.type" :placeholder="$t('test_resource_pool.select_pool_type')"
|
<el-select v-model="form.type" :placeholder="$t('test_resource_pool.select_pool_type')"
|
||||||
@change="changeResourceType()">
|
@change="changeResourceType()">
|
||||||
<el-option key="K8S" value="K8S" label="Kubernetes">Kubernetes</el-option>
|
|
||||||
<el-option key="NODE" value="NODE" label="Node">Node</el-option>
|
<el-option key="NODE" value="NODE" label="Node">Node</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div v-for="(item,index) in infoList " :key="index">
|
<div v-for="(item,index) in infoList " :key="index">
|
||||||
<div class="node-line" v-if="form.type === 'K8S'">
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="9">
|
|
||||||
<el-form-item prop="masterUrl" label="Master URL">
|
|
||||||
<el-input v-model="item.masterUrl" autocomplete="off"/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="9">
|
|
||||||
<el-form-item prop="password" label="Token" style="padding-left: 20px">
|
|
||||||
<el-input v-model="item.token" show-password autocomplete="off"/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="6">
|
|
||||||
<el-form-item prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')"
|
|
||||||
style="padding-left: 20px">
|
|
||||||
<el-input-number v-model="item.maxConcurrency" :min="1" :max="9999"></el-input-number>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
<div class="node-line" v-if="form.type === 'NODE'">
|
<div class="node-line" v-if="form.type === 'NODE'">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
|
|
Loading…
Reference in New Issue