diff --git a/backend/src/main/java/io/metersphere/commons/constants/ResourceTypeEnum.java b/backend/src/main/java/io/metersphere/commons/constants/ResourcePoolTypeEnum.java similarity index 80% rename from backend/src/main/java/io/metersphere/commons/constants/ResourceTypeEnum.java rename to backend/src/main/java/io/metersphere/commons/constants/ResourcePoolTypeEnum.java index 3544675660..1cc3f1a7c5 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/ResourceTypeEnum.java +++ b/backend/src/main/java/io/metersphere/commons/constants/ResourcePoolTypeEnum.java @@ -1,6 +1,6 @@ package io.metersphere.commons.constants; -public enum ResourceTypeEnum { +public enum ResourcePoolTypeEnum { /** * k8s 资源池 */ diff --git a/backend/src/main/java/io/metersphere/dto/KubernetesDTO.java b/backend/src/main/java/io/metersphere/dto/KubernetesDTO.java new file mode 100644 index 0000000000..2ad1f92de6 --- /dev/null +++ b/backend/src/main/java/io/metersphere/dto/KubernetesDTO.java @@ -0,0 +1,41 @@ +package io.metersphere.dto; + +public class KubernetesDTO { + + private String masterUrl; + private String token; + private Integer maxConcurrency; + private Boolean validate; + + 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; + } + + public Boolean getValidate() { + return validate; + } + + public void setValidate(Boolean validate) { + this.validate = validate; + } +} diff --git a/backend/src/main/java/io/metersphere/dto/NodeDTO.java b/backend/src/main/java/io/metersphere/dto/NodeDTO.java new file mode 100644 index 0000000000..cfc141885c --- /dev/null +++ b/backend/src/main/java/io/metersphere/dto/NodeDTO.java @@ -0,0 +1,40 @@ +package io.metersphere.dto; + +public class NodeDTO { + private String ip; + private Integer port; + private Integer maxConcurrency; + private Boolean validate; + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Integer getMaxConcurrency() { + return maxConcurrency; + } + + public void setMaxConcurrency(Integer maxConcurrency) { + this.maxConcurrency = maxConcurrency; + } + + public Boolean getValidate() { + return validate; + } + + public void setValidate(Boolean validate) { + this.validate = validate; + } +} diff --git a/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java b/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java index eaf4cb0838..d25e109a99 100644 --- a/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java +++ b/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java @@ -1,12 +1,23 @@ package io.metersphere.service; +import com.alibaba.fastjson.JSON; import io.metersphere.base.domain.TestResourcePool; import io.metersphere.base.domain.TestResourcePoolExample; import io.metersphere.base.mapper.TestResourcePoolMapper; +import io.metersphere.commons.constants.ResourcePoolTypeEnum; +import io.metersphere.commons.utils.BeanUtils; import io.metersphere.controller.request.resourcepool.QueryResourcePoolRequest; +import io.metersphere.dto.KubernetesDTO; +import io.metersphere.dto.NodeDTO; +import io.metersphere.engine.kubernetes.provider.ClientCredential; +import io.metersphere.engine.kubernetes.provider.KubernetesProvider; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.util.List; @@ -19,6 +30,8 @@ import java.util.UUID; @Transactional(rollbackFor = Exception.class) public class TestResourcePoolService { + private final static String nodeControllerUrl = "%s:%s/status"; + @Resource private TestResourcePoolMapper testResourcePoolMapper; @@ -27,6 +40,7 @@ public class TestResourcePoolService { testResourcePool.setCreateTime(System.currentTimeMillis()); testResourcePool.setUpdateTime(System.currentTimeMillis()); testResourcePool.setStatus("1"); + validateTestResourcePool(testResourcePool); testResourcePoolMapper.insertSelective(testResourcePool); return testResourcePool; } @@ -37,6 +51,7 @@ public class TestResourcePoolService { public void updateTestResourcePool(TestResourcePool testResourcePool) { testResourcePool.setUpdateTime(System.currentTimeMillis()); + validateTestResourcePool(testResourcePool); testResourcePoolMapper.updateByPrimaryKeySelective(testResourcePool); } @@ -47,4 +62,55 @@ public class TestResourcePoolService { } return testResourcePoolMapper.selectByExample(example); } + + private void validateTestResourcePool(TestResourcePool testResourcePool) { + if (StringUtils.equalsIgnoreCase(testResourcePool.getType(), ResourcePoolTypeEnum.K8S.name())) { + validateK8s(testResourcePool); + return; + } + validateNodes(testResourcePool); + } + + private void validateNodes(TestResourcePool testResourcePool) { + List nodes = JSON.parseArray(testResourcePool.getInfo(), NodeDTO.class); + + if (CollectionUtils.isEmpty(nodes)) { + throw new RuntimeException("没有节点信息"); + } + + for (NodeDTO node : nodes) { + boolean isValidate = validateNode(node); + if (!isValidate) { + testResourcePool.setStatus("0"); + } + node.setValidate(isValidate); + } + testResourcePool.setInfo(JSON.toJSONString(nodes)); + } + + private boolean validateNode(NodeDTO dto) { + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity entity = restTemplate.getForEntity(String.format(nodeControllerUrl, dto.getIp(), dto.getPort()), String.class); + return entity.getStatusCode().value() == HttpStatus.SC_OK; + } + + private void validateK8s(TestResourcePool testResourcePool) { + List dtos = JSON.parseArray(testResourcePool.getInfo(), KubernetesDTO.class); + + if (CollectionUtils.isEmpty(dtos) || dtos.size() != 1) { + throw new RuntimeException("只能添加一个 K8s"); + } + + ClientCredential clientCredential = new ClientCredential(); + BeanUtils.copyBean(clientCredential, dtos.get(0)); + try { + KubernetesProvider provider = new KubernetesProvider(JSON.toJSONString(clientCredential)); + provider.validateCredential(); + dtos.get(0).setValidate(true); + } catch (Exception e) { + dtos.get(0).setValidate(false); + testResourcePool.setStatus("0"); + } + testResourcePool.setInfo(JSON.toJSONString(dtos)); + } } diff --git a/frontend/src/business/components/settings/system/TestResourcePool.vue b/frontend/src/business/components/settings/system/TestResourcePool.vue index a5bd6ab574..9699ff915f 100644 --- a/frontend/src/business/components/settings/system/TestResourcePool.vue +++ b/frontend/src/business/components/settings/system/TestResourcePool.vue @@ -69,7 +69,8 @@ - + @@ -143,7 +144,8 @@ - + @@ -225,6 +227,7 @@ data() { return { loading: false, + testLoading: false, createVisible: false, infoList: [], updateVisible: false, @@ -363,6 +366,7 @@ if (valide) { let vri = this.validateResourceInfo(); if (vri.validate) { + this.testLoading = true; this.form.info = JSON.stringify(this.infoList); this.$post("/testresourcepool/add", this.form) .then(() => { @@ -371,13 +375,15 @@ message: '添加成功!' }, this.createVisible = false, - this.initTableData()) + this.initTableData()); + this.testLoading = false; }); } else { this.$message({ type: 'warning', message: vri.msg }); + this.testLoading = false; return false; } @@ -389,6 +395,7 @@ updateTestResourcePool(updateTestResourcePoolForm) { this.$refs[updateTestResourcePoolForm].validate(valide => { if (valide) { + this.testLoading = true; let vri = this.validateResourceInfo(); if (vri.validate) { this.form.info = JSON.stringify(this.infoList); @@ -400,13 +407,15 @@ }, this.updateVisible = false, this.initTableData(), - self.loading = false) + self.loading = false); + this.testLoading = false; }); } else { this.$message({ type: 'warning', message: vri.msg }); + this.testLoading = false; return false; } } else {