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>
|
||||
<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>
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum ResourcePoolTypeEnum {
|
||||
/**
|
||||
* k8s 资源池
|
||||
*/
|
||||
K8S,
|
||||
/**
|
||||
* node controller 资源池
|
||||
*/
|
||||
|
|
|
@ -57,7 +57,7 @@ public abstract class AbstractEngine implements Engine {
|
|||
if (resourcePool == null) {
|
||||
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.");
|
||||
}
|
||||
this.resourceList = testResourceService.getResourcesByPoolId(resourcePool.getId());
|
||||
|
|
|
@ -12,7 +12,6 @@ import io.metersphere.commons.exception.MSException;
|
|||
import io.metersphere.config.KafkaProperties;
|
||||
import io.metersphere.i18n.Translator;
|
||||
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.EngineSourceParserFactory;
|
||||
import io.metersphere.service.FileService;
|
||||
|
@ -47,11 +46,8 @@ public class EngineFactory {
|
|||
|
||||
final ResourcePoolTypeEnum type = ResourcePoolTypeEnum.valueOf(resourcePool.getType());
|
||||
|
||||
switch (type) {
|
||||
case NODE:
|
||||
if (type == ResourcePoolTypeEnum.NODE) {
|
||||
return new DockerTestEngine(loadTest);
|
||||
case K8S:
|
||||
return new KubernetesTestEngine(loadTest);
|
||||
}
|
||||
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());
|
||||
// engine type (NODE|K8S)
|
||||
// engine type (NODE)
|
||||
final Engine engine = EngineFactory.createEngine(loadTest);
|
||||
if (engine == null) {
|
||||
MSException.throwException(String.format("Test cannot be run,test ID:%s", request.getId()));
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
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.TestResourcePoolMapper;
|
||||
import io.metersphere.commons.constants.ResourcePoolTypeEnum;
|
||||
import io.metersphere.commons.constants.ResourceStatusEnum;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
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.TestResourcePoolDTO;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.performance.engine.kubernetes.provider.KubernetesProvider;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -145,9 +146,6 @@ public class TestResourcePoolService {
|
|||
}
|
||||
|
||||
private boolean validateTestResourcePool(TestResourcePoolDTO testResourcePool) {
|
||||
if (StringUtils.equalsIgnoreCase(testResourcePool.getType(), ResourcePoolTypeEnum.K8S.name())) {
|
||||
return validateK8s(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) {
|
||||
testResource.setUpdateTime(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.exception.MSException;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.exception.ExcelException;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.dto.TestCaseNodeDTO;
|
||||
import io.metersphere.exception.ExcelException;
|
||||
import io.metersphere.track.request.testcase.DragNodeRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -22,7 +22,6 @@ import org.apache.ibatis.session.SqlSession;
|
|||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.yaml.snakeyaml.nodes.NodeId;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
|
|
@ -37,7 +37,6 @@ test_not_running=Test is not running
|
|||
load_test_already_exists=Duplicate load test name
|
||||
no_nodes_message=No node message
|
||||
duplicate_node_ip=Duplicate IPs
|
||||
only_one_k8s=Only one K8s can be added
|
||||
max_thread_insufficient=The number of concurrent users exceeds
|
||||
#workspace
|
||||
workspace_name_is_null=Workspace name cannot be null
|
||||
|
|
|
@ -37,7 +37,6 @@ test_not_running=测试未运行
|
|||
load_test_already_exists=测试名称不能重复
|
||||
no_nodes_message=没有节点信息
|
||||
duplicate_node_ip=节点 IP 重复
|
||||
only_one_k8s=只能添加一个 K8s
|
||||
max_thread_insufficient=并发用户数超额
|
||||
#workspace
|
||||
workspace_name_is_null=工作空间名不能为空
|
||||
|
|
|
@ -37,7 +37,6 @@ test_not_running=測試未運行
|
|||
load_test_already_exists=測試名稱不能重復
|
||||
no_nodes_message=沒有節點信息
|
||||
duplicate_node_ip=節點 IP 重復
|
||||
only_one_k8s=只能添加壹個 K8s
|
||||
max_thread_insufficient=並發用戶數超額
|
||||
#workspace
|
||||
workspace_name_is_null=工作空間名不能為空
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
<el-table-column prop="type" :label="$t('test_resource_pool.type')">
|
||||
<template v-slot:default="scope">
|
||||
<span v-if="scope.row.type === 'NODE'">Node</span>
|
||||
<span v-if="scope.row.type === 'K8S'">Kubernetes</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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-select v-model="form.type" :placeholder="$t('test_resource_pool.select_pool_type')"
|
||||
@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-select>
|
||||
</el-form-item>
|
||||
<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'">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
|
@ -144,31 +122,10 @@
|
|||
<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')"
|
||||
@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-select>
|
||||
</el-form-item>
|
||||
<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'">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
|
|
Loading…
Reference in New Issue