|
|
@@ -1,6 +1,11 @@
|
|
|
package cn.seecoder.build.server.service.facade.jenkins.impl;
|
|
|
|
|
|
+import cn.seecoder.build.model.vo.BuildDetailsVO;
|
|
|
+import cn.seecoder.build.model.vo.CoverageResult;
|
|
|
+import cn.seecoder.build.model.vo.MutationResult;
|
|
|
import cn.seecoder.build.server.service.facade.jenkins.JenkinsApi;
|
|
|
+import cn.seecoder.build.server.service.facade.jenkins.JenkinsInternalApi;
|
|
|
+import cn.seecoder.build.server.service.model.converter.TestResultConvertor;
|
|
|
import cn.seecoder.build.server.util.ApplicationProperties;
|
|
|
import cn.seecoder.build.server.util.LoggerUtil;
|
|
|
import cn.seecoder.build.server.util.ServiceException;
|
|
|
@@ -8,7 +13,10 @@ import com.alibaba.fastjson2.JSON;
|
|
|
import com.offbytwo.jenkins.JenkinsServer;
|
|
|
import com.offbytwo.jenkins.client.JenkinsHttpClient;
|
|
|
import com.offbytwo.jenkins.model.*;
|
|
|
+import org.apache.commons.io.output.ByteArrayOutputStream;
|
|
|
+import org.dom4j.*;
|
|
|
import org.slf4j.Logger;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import javax.annotation.PostConstruct;
|
|
|
@@ -16,6 +24,7 @@ import java.io.IOException;
|
|
|
import java.io.InputStream;
|
|
|
import java.net.URI;
|
|
|
import java.net.URISyntaxException;
|
|
|
+import java.util.List;
|
|
|
import java.util.Map;
|
|
|
|
|
|
@Service
|
|
|
@@ -26,19 +35,21 @@ public class JenkinsApiImpl implements JenkinsApi {
|
|
|
private final ApplicationProperties properties;
|
|
|
|
|
|
private JenkinsServer server;
|
|
|
- private JenkinsHttpClient client;
|
|
|
+ private JenkinsInternalApi jenkinsInternalApi;
|
|
|
|
|
|
- public JenkinsApiImpl(ApplicationProperties properties) {
|
|
|
+ @Autowired
|
|
|
+ public JenkinsApiImpl(ApplicationProperties properties, JenkinsInternalApi jenkinsInternalApi) {
|
|
|
this.properties = properties;
|
|
|
+ this.jenkinsInternalApi = jenkinsInternalApi;
|
|
|
}
|
|
|
|
|
|
@PostConstruct
|
|
|
- private void initialize() throws URISyntaxException, IOException {
|
|
|
+ private void initialize() throws URISyntaxException {
|
|
|
ApplicationProperties.Jenkins jenkins = properties.getJenkins();
|
|
|
- URI uri = new URI(jenkins.getHost());
|
|
|
+ URI internalURI = new URI(jenkins.getInternalHost());
|
|
|
String username = jenkins.getUsername();
|
|
|
String token = jenkins.getToken();
|
|
|
- client = new PoolingJenkinsHttpClient(uri, username, token);
|
|
|
+ JenkinsHttpClient client = new PoolingJenkinsHttpClient(internalURI, username, token);
|
|
|
server = new JenkinsServer(client);
|
|
|
}
|
|
|
|
|
|
@@ -54,22 +65,18 @@ public class JenkinsApiImpl implements JenkinsApi {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public QueueItem invokeBuild(String jobName, Map<String, String> params) throws ServiceException {
|
|
|
+ public Integer invokeBuild(String jobName, Map<String, String> params) throws ServiceException {
|
|
|
QueueReference reference;
|
|
|
try {
|
|
|
- JobWithDetails externalJob=server.getJob(jobName);
|
|
|
- InternalJob job = new InternalJob(client,externalJob.getName(),
|
|
|
- externalJob.getUrl().replace("build-jenkins.internal-paas.seec.seecoder.cn","environment-9-12.seec.svc.cluster.local:8080"),
|
|
|
- externalJob.getFullName());
|
|
|
- if (params == null) {
|
|
|
- reference = job.build();
|
|
|
- logger.info(reference.getQueueItemUrlPart());
|
|
|
- } else {
|
|
|
- // 0.3.7版本的Jenkins客户端库中build(Map<String, String> params)方法存在BUG,会触发两次构建
|
|
|
- reference = job.build(params, false);
|
|
|
- logger.info(reference.getQueueItemUrlPart());
|
|
|
- }
|
|
|
- reference= new QueueReference(reference.getQueueItemUrlPart().replace("build-jenkins.internal-paas.seec.seecoder.cn","environment-9-12.seec.svc.cluster.local:8080"));
|
|
|
+// if (params == null) {
|
|
|
+// reference = server.getJob(jobName).build();
|
|
|
+// } else {
|
|
|
+// // 0.3.7版本的Jenkins客户端库中build(Map<String, String> params)方法存在BUG,会触发两次构建
|
|
|
+// reference = server.getJob(jobName).build(params, false);
|
|
|
+// }
|
|
|
+
|
|
|
+ JobWithDetails jobWithDetails = server.getJob(jobName);
|
|
|
+ reference = jenkinsInternalApi.invokeBuild(jobWithDetails,params);
|
|
|
QueueItem item = server.getQueueItem(reference);
|
|
|
while (item.getExecutable() == null) {
|
|
|
try {
|
|
|
@@ -79,7 +86,8 @@ public class JenkinsApiImpl implements JenkinsApi {
|
|
|
}
|
|
|
item = server.getQueueItem(reference);
|
|
|
}
|
|
|
- return item;
|
|
|
+
|
|
|
+ return item.getExecutable().getNumber().intValue();
|
|
|
} catch (IOException e) {
|
|
|
logger.error("invokeBuild exception={}, jobName={}, params={}", e, jobName, params);
|
|
|
throw new ServiceException("101", e.getMessage());
|
|
|
@@ -96,59 +104,184 @@ public class JenkinsApiImpl implements JenkinsApi {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ public <T extends BaseModel> T get(BuildWithDetails build, String path, Class<T> cls) throws ServiceException {
|
|
|
+ try {
|
|
|
+ //return build.getClient().get(build.getUrl() + path, cls);
|
|
|
+ return jenkinsInternalApi.get(build, path, cls);
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.error("get exception={}, path={}", e, path);
|
|
|
+ throw new ServiceException("101", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ public String get(BuildWithDetails build, String path) throws ServiceException {
|
|
|
+ try {
|
|
|
+ //return build.getClient().get(build.getUrl() + path);
|
|
|
+ return jenkinsInternalApi.get(build, path);
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.error("get exception={}, path={}", e, path);
|
|
|
+ throw new ServiceException("101", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param jobName job名
|
|
|
+ * @param buildId 构建id
|
|
|
+ * @return BuildDetailsVO
|
|
|
+ * @throws ServiceException
|
|
|
+ * 通过jobName和buildID获取BuildDetailsVO
|
|
|
+ *
|
|
|
+ */
|
|
|
@Override
|
|
|
- public BuildWithDetails fetchBuildDetails(String jobName, int buildNumber, boolean waitFinished) throws ServiceException {
|
|
|
+ public BuildDetailsVO getBuildDetailsVO(String jobName, Integer buildId) throws ServiceException{
|
|
|
+
|
|
|
+ BuildWithDetails buildWithDetails=getBuildDetails(jobName, buildId, true);
|
|
|
+
|
|
|
+ if(buildWithDetails==null){
|
|
|
+ logger.error("buildWithDetails为空, jobName={}, buildId={}", jobName, buildId);
|
|
|
+ throw new ServiceException("101","buildWithDetails为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ BuildDetailsVO buildDetailsVO = new BuildDetailsVO();
|
|
|
+
|
|
|
+ buildDetailsVO.setBuildId(buildId);
|
|
|
+ buildDetailsVO.setJobName(jobName);
|
|
|
+ buildDetailsVO.setDuration(buildWithDetails.getDuration());
|
|
|
+ buildDetailsVO.setResult(buildWithDetails.getResult());
|
|
|
+ buildDetailsVO.setTimestamps(buildWithDetails.getTimestamp());
|
|
|
+ buildDetailsVO.setArtifacts(buildWithDetails.getArtifacts());
|
|
|
try {
|
|
|
- BuildWithDetails buildWithDetails;
|
|
|
- Build build =server.getJob(jobName).getBuildByNumber(buildNumber);
|
|
|
- buildWithDetails = build.getClient().get(build.getUrl().replace("build-jenkins.internal-paas.seec.seecoder.cn","environment-9-12.seec.svc.cluster.local:8080"), BuildWithDetails.class);
|
|
|
+ //buildDetailsVO.setConsoleOutput(buildWithDetails.getConsoleOutputText());
|
|
|
+ buildDetailsVO.setConsoleOutput(jenkinsInternalApi.getBuildConsoleOutputText(buildWithDetails));
|
|
|
+
|
|
|
+ setBuildResult(buildWithDetails,buildDetailsVO);
|
|
|
+
|
|
|
+ logger.info(JSON.toJSONString(buildDetailsVO));
|
|
|
+ return buildDetailsVO;
|
|
|
+ }catch (IOException e){
|
|
|
+ logger.error("getBuildDetailsVO exception={}, jobName={}, buildId={}", e, jobName, buildId);
|
|
|
+ throw new ServiceException("101", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public BuildWithDetails getBuildDetails(String jobName, int buildId, boolean waitFinished) throws ServiceException {
|
|
|
+ try {
|
|
|
+ // BuildWithDetails buildWithDetails=server.getJob(jobName).getBuildByNumber(buildNumber).details();
|
|
|
+ // 上面是Jenkins external地址没有https报错,不用使用k8s内部地址的情况
|
|
|
+ // 下面是需要使用k8s内部地址的情况
|
|
|
+ Build build = server.getJob(jobName).getBuildByNumber(buildId);
|
|
|
+ BuildWithDetails buildWithDetails = jenkinsInternalApi.getBuildDetails(build);
|
|
|
+
|
|
|
while (waitFinished && (buildWithDetails.getResult() == null || buildWithDetails.getDuration() == 0)) {
|
|
|
try {
|
|
|
Thread.sleep(500);
|
|
|
} catch (InterruptedException e) {
|
|
|
Thread.currentThread().interrupt();
|
|
|
}
|
|
|
- buildWithDetails = build.getClient().get(build.getUrl().replace("build-jenkins.internal-paas.seec.seecoder.cn","environment-9-12.seec.svc.cluster.local:8080"), BuildWithDetails.class);
|
|
|
+ buildWithDetails = jenkinsInternalApi.getBuildDetails(build);
|
|
|
}
|
|
|
return buildWithDetails;
|
|
|
} catch (IOException e) {
|
|
|
- logger.error("fetchBuildDetails exception={}, jobName={}, buildNumber={}, waitFinished", e.getMessage(), jobName, buildNumber, waitFinished);
|
|
|
+ logger.error("getBuildDetails exception={}, jobName={}, buildId={}, waitFinished={}", e, jobName, buildId, waitFinished);
|
|
|
throw new ServiceException("101", e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public <T extends BaseModel> T get(BuildWithDetails build, String path, Class<T> cls) throws ServiceException {
|
|
|
+ void setBuildResult(BuildWithDetails buildWithDetails,BuildDetailsVO buildDetailsVO) throws ServiceException {
|
|
|
+
|
|
|
+ TestResult testResult = getTestResult(buildWithDetails);
|
|
|
+
|
|
|
+ if (testResult != null) {
|
|
|
+ buildDetailsVO.setTestResultVO(TestResultConvertor.convertToVO(testResult));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ CoverageResult coverageResult = getCoverageResult(buildWithDetails);
|
|
|
+
|
|
|
+ if (coverageResult != null) {
|
|
|
+ buildDetailsVO.setCoverageResult(coverageResult);
|
|
|
+ } else {
|
|
|
+ try {
|
|
|
+ List<Artifact> artifactList = buildWithDetails.getArtifacts();
|
|
|
+ Artifact artifact = artifactList.stream().filter(a -> a.getFileName().equals("mutations.xml")).findFirst().orElse(null);
|
|
|
+ if (artifact != null) {
|
|
|
+ MutationResult mutationResult = new MutationResult();
|
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
|
+ outputStream.write(downloadArtifact(buildWithDetails, artifact.getRelativePath()));
|
|
|
+ String mutationReport = new String(outputStream.toByteArray());
|
|
|
+ outputStream.close();
|
|
|
+ Document document = DocumentHelper.parseText(mutationReport);
|
|
|
+ Element root = document.getRootElement();
|
|
|
+ for (Object element : root.elements("mutation")) {
|
|
|
+ if (element instanceof Element) {
|
|
|
+ Attribute attribute = ((Element) element).attribute("detected");
|
|
|
+ if (attribute != null) {
|
|
|
+ mutationResult.setMutations(mutationResult.getMutations() + 1);
|
|
|
+ if ("true".equals(attribute.getValue())) {
|
|
|
+ mutationResult.setDetectedMutations(mutationResult.getDetectedMutations() + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ buildDetailsVO.setMutationResult(mutationResult);
|
|
|
+ }
|
|
|
+ } catch (DocumentException e) {
|
|
|
+ logger.error("documentParseText exception="+e);
|
|
|
+ throw new ServiceException("101", e.getMessage());
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.error("writeArtifact exception="+e);
|
|
|
+ throw new ServiceException("101", e.getMessage());
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public InputStream downloadArtifact(BuildWithDetails build, String relativePath) throws ServiceException {
|
|
|
try {
|
|
|
- return build.getClient().get(build.getUrl().replace("build-jenkins.internal-paas.seec.seecoder.cn","environment-9-12.seec.svc.cluster.local:8080") + path, cls);
|
|
|
- } catch (IOException e) {
|
|
|
- logger.error("get exception={}, path={}", e, path);
|
|
|
+ //Artifact artifact = new Artifact();
|
|
|
+ //artifact.setRelativePath(relativePath);
|
|
|
+ //return build.downloadArtifact(artifact);
|
|
|
+ return jenkinsInternalApi.downloadArtifact(build,relativePath);
|
|
|
+ } catch (IOException | URISyntaxException e) {
|
|
|
+ logger.error("downloadArtifact exception={}, relativePath={}", e, relativePath);
|
|
|
throw new ServiceException("101", e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public String get(BuildWithDetails build, String path) throws ServiceException {
|
|
|
+ public TestResult getTestResult(BuildWithDetails buildWithDetails) throws ServiceException {
|
|
|
try {
|
|
|
- return build.getClient().get(build.getUrl().replace("build-jenkins.internal-paas.seec.seecoder.cn","environment-9-12.seec.svc.cluster.local:8080") + path);
|
|
|
- } catch (IOException e) {
|
|
|
- logger.error("get exception={}, path={}", e, path);
|
|
|
+ //return buildWithDetails.getTestResult();
|
|
|
+ return jenkinsInternalApi.getTestResult(buildWithDetails);
|
|
|
+ }catch (IOException e){
|
|
|
+ logger.error("getTestResult exception="+e);
|
|
|
throw new ServiceException("101", e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public InputStream downloadArtifact(BuildWithDetails build, String relativePath) throws ServiceException {
|
|
|
+ public CoverageResult getCoverageResult(BuildWithDetails buildWithDetails) throws ServiceException {
|
|
|
try {
|
|
|
- Artifact artifact = new Artifact();
|
|
|
- artifact.setRelativePath(relativePath);
|
|
|
- URI uri = new URI(build.getUrl().replace("build-jenkins.internal-paas.seec.seecoder.cn","environment-9-12.seec.svc.cluster.local:8080"));
|
|
|
- String artifactPath = uri.getPath() + "artifact/" + artifact.getRelativePath();
|
|
|
- URI artifactUri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), artifactPath, "", "");
|
|
|
- return build.getClient().getFile(artifactUri);
|
|
|
- } catch (IOException | URISyntaxException e) {
|
|
|
- logger.error("get exception={}, relativePath={}", e, relativePath);
|
|
|
+ //return buildWithDetails.getClient().get(buildWithDetails.getUrl() + "/cobertura/?depth=2", CoverageResult.class);
|
|
|
+ return jenkinsInternalApi.getCoverageResult(buildWithDetails);
|
|
|
+ }catch (IOException e){
|
|
|
+ logger.error("getCoverageResult exception="+e);
|
|
|
throw new ServiceException("101", e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getEntity(String jobName, Integer buildId, String path) throws ServiceException {
|
|
|
+ BuildWithDetails buildWithDetails = getBuildDetails(jobName, buildId, true);
|
|
|
+ return get(buildWithDetails, path);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public InputStream getArtifact(String jobName, Integer buildId, String path) throws ServiceException {
|
|
|
+ BuildWithDetails buildWithDetails = getBuildDetails(jobName, buildId, true);
|
|
|
+ return downloadArtifact(buildWithDetails, path);
|
|
|
+ }
|
|
|
}
|