java
复制代码
package com.infosec.autobuild.util;
import com.cdancy.jenkins.rest.JenkinsClient;
import com.cdancy.jenkins.rest.domain.job.BuildInfo;
import com.cdancy.jenkins.rest.domain.queue.QueueItem;
import com.infosec.autobuild.dto.*;
import com.infosec.autobuild.dto.jenkins.BuildInfoDto;
import com.infosec.autobuild.dto.jenkins.CredentialDto;
import com.infosec.autobuild.dto.jenkins.JobDto;
import com.infosec.autobuild.dto.jenkins.ViewDto;
import com.infosec.autobuild.hudson.cli.CLI;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Jenkins cli工具类
*/
@Slf4j
public class JenkinsUtil {
private static final String PROTOCOL_HTTP = "-http";
private static final String PROTOCOL_WEB_SOCKET = "-webSocket";
/**
* 结束符:\r\n
*/
private static final String END_SYMBOL = "\\r\\n";
/**
* 换行符:\n
*/
private static final String LINE_BREAK_SYMBOL = "\\n";
/**
* Jenkins 凭证相关操作(暂不包括domain相关操作)
*/
public static class Credentials {
/**
* system::system::jenkins等同于SystemCredentialsProvider::SystemContextResolver::jenkins
* Provider可以通过java -jar jenkins-cli.jar -auth admin:admin -s http://xxx:8080/ -http list-credentials-providers获取
* Resolver可以通过java -jar jenkins-cli.jar -auth admin:admin -s http://xxx:8080/ -http list-credentials-context-resolvers获取
*/
private static final String SYSTEM_STORE_ID = "system::system::jenkins";
/**
* 默认全局domain
*/
private static final String GLOBAL_DOMAIN = "(global)";
private static final String CREDENTIAL_NOT_FOUND = "No such credential";
enum Operation {
/**
* 查询Jenkins Credentials集合:list-credentials
*/
LIST_CREDENTIALS("list-credentials", "查询Jenkins Credentials集合"),
/**
* 查询Jenkins Credentials集合,返回xml:list-credentials-as-xml
*/
LIST_CREDENTIALS_AS_XML("list-credentials-as-xml", "查询Jenkins Credentials集合,返回xml"),
/**
* 创建Jenkins Credentials:create-credentials-by-xml
*/
CREATE_CREDENTIALS("create-credentials-by-xml", "创建Jenkins Credentials"),
/**
* 更新Jenkins Credentials:create-credentials-by-xml
*/
UPDATE_CREDENTIALS("update-credentials-by-xml", "更新Jenkins Credentials"),
/**
* 删除Jenkins Credentials:delete-credentials
*/
DELETE_CREDENTIALS("delete-credentials", "删除Jenkins Credentials"),
/**
* 查询Jenkins Credentials:get-credentials-as-xml
*/
GET_CREDENTIALS("get-credentials-as-xml", "查询Jenkins Credentials"),
;
@Setter
@Getter
private String op;
@Setter
@Getter
private String desc;
Operation(String op, String desc) {
this.setOp(op);
this.setDesc(desc);
}
}
/**
* 创建Jenkins 凭证(凭证id为空时,默认使用36位UUID)
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param credentialDto 凭证对象
* @return ResultDto
*/
public static ResultDto createCredential(String host, String auth, CredentialDto credentialDto) {
if (!StringUtils.hasText(credentialDto.getId())) {
credentialDto.setId(UUID.randomUUID().toString());
}
String xml = CredentialDto.parseDto2Xml(credentialDto);
CLI cli = doWork(host, auth, new String[]{Operation.CREATE_CREDENTIALS.op, SYSTEM_STORE_ID, ""}, PROTOCOL_HTTP, xml);
int code = cli.code;
String msg = cli.msg;
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
}
/**
* 删除Jenkins 凭证
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param credentialId 凭证id
* @return ResultDto
*/
public static ResultDto deleteCredential(String host, String auth, String credentialId) {
CLI cli = doWork(host, auth,
new String[]{Operation.DELETE_CREDENTIALS.op, SYSTEM_STORE_ID, "", credentialId},
PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
if (StringUtils.hasText(msg) && msg.contains(CREDENTIAL_NOT_FOUND)) {
return ResultDto.buildSuccessDto();
}
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
}
/**
* 更新Jenkins 凭证(凭证id无法修改)
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param credentialDto 凭证对象
* @return ResultDto
*/
public static ResultDto updateCredential(String host, String auth, CredentialDto credentialDto) {
if (!StringUtils.hasText(credentialDto.getId())) {
return ResultDto.buildErrorDto().msg("凭证id不能为空");
}
String xml = CredentialDto.parseDto2Xml(credentialDto);
CLI cli = doWork(host, auth, new String[]{Operation.UPDATE_CREDENTIALS.op, SYSTEM_STORE_ID,
GLOBAL_DOMAIN, credentialDto.getId()}, PROTOCOL_HTTP, xml);
int code = cli.code;
String msg = cli.msg;
if (StringUtils.hasText(msg) && msg.contains(CREDENTIAL_NOT_FOUND)) {
msg = "根据凭证id未找到对应记录";
return ResultDto.buildErrorDto().msg(msg);
}
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
}
/**
* 查询Jenkins 凭证
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param credentialId 凭证id
* @return ResultDto
*/
public static ResultDto<CredentialDto> getCredential(String host, String auth, String credentialId) {
CLI cli = doWork(host, auth,
new String[]{Operation.GET_CREDENTIALS.op, SYSTEM_STORE_ID, "", credentialId},
PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
if (StringUtils.hasText(msg) && msg.contains(CREDENTIAL_NOT_FOUND)) {
msg = "根据凭证id未找到对应记录";
return ResultDto.buildErrorDto().msg(msg);
}
boolean ifSuccess = ResultDto.SUCCESS.equals(String.valueOf(code));
CredentialDto credentialDto = new CredentialDto();
if (ifSuccess) {
if (StringUtils.hasText(msg)) {
CredentialDto.parseXmlStr2Dto(credentialDto, msg, null);
}
}
return ifSuccess ? ResultDto.buildSuccess(credentialDto) : ResultDto.buildError(credentialDto).msg(msg);
}
/**
* 查询Jenkins上的凭证列表
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto<List<CredentialDto>> listCredentials(String host, String auth) {
CLI cli = doWork(host, auth, new String[]{Operation.LIST_CREDENTIALS_AS_XML.op, SYSTEM_STORE_ID},
PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
List<CredentialDto> credentialDtoList = new ArrayList<>(10);
boolean ifSuccess = ResultDto.SUCCESS.equals(String.valueOf(code));
if (ifSuccess) {
CredentialDto.parseXmlStr2List(credentialDtoList, msg);
}
return ifSuccess ? ResultDto.buildSuccess(credentialDtoList) : ResultDto.buildError(credentialDtoList).msg(msg);
}
/**
* 查询Jenkins上的凭证列表
*
* @param ip Jenkins服务IP
* @param port Jenkins服务端口
* @param username Jenkins服务登录用户名
* @param password Jenkins服务登录密码
* @return ResultDto
*/
public static ResultDto listCredentials(String ip, String port, String username, String password) {
String host = "http://" + ip + ":" + port;
String auth = username + ":" + password;
return listCredentials(host, auth);
}
}
/**
* Jenkins Job相关操作(即Item)
*/
public static class Jobs {
private static String JOB_BUILD_DETAIL_URL = "@host/job/@jobName/@buildId/api/json";
private static String QUEUE_LIST_URL = "@host/job/@jobName/@buildId/api/json";
enum Operation {
/**
* 创建Jenkins Job
*/
CREATE("create-job", "创建Jenkins Job"),
/**
* 复制Jenkins Job
*/
COPY("copy-job", "复制Jenkins Job"),
/**
* 更新Jenkins Job
*/
UPDATE("update-job", "更新Jenkins Job"),
/**
* 删除Jenkins Job
*/
DELETE("delete-job", "删除Jenkins Job"),
/**
* 构建Jenkins Job
*/
BUILD("build", "构建Jenkins Job"),
/**
* Jenkins Job构建日志输出
*/
CONSOLE("console", "Jenkins Job构建日志输出"),
/**
* 查询Jenkins Job集合
*/
LIST("list-jobs", "查询Jenkins Job集合"),
/**
* 查询Jenkins Job
*/
GET("get-job", "查询Jenkins Job"),
/**
* 将Jenkins Job添加到视图中
*/
ADD_TO_VIEW("add-job-to-view", "将Jenkins Job添加到视图中"),
/**
* 将Jenkins Job从视图中移除
*/
REMOVE_FROM_VIEW("remove-job-from-view", "将Jenkins Job从视图中移除"),
/**
* 启用Jenkins Job
*/
ENABLE("enable-job", "启用Jenkins Job"),
/**
* 禁用Jenkins Job
*/
DISABLE("disable-job", "禁用Jenkins Job"),
/**
* 重新加载Jenkins Job
*/
RELOAD("reload-job", "重新加载Jenkins Job"),
/**
* 停止构建Jenkins Jobs
*/
STOP_BUILDS("stop-builds", "停止构建Jenkins Job");
@Setter
@Getter
private String op;
@Setter
@Getter
private String desc;
Operation(String op, String desc) {
this.setOp(op);
this.setDesc(desc);
}
}
/**
* 查询Jenkins上的Job列表
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param viewName Jenkins视图名称,为空时,查询全部Jobs
* @return ResultDto
*/
public static ResultDto listJobs(String host, String auth, String viewName) {
String[] baseArgs = new String[]{Operation.LIST.op};
String[] finalArgs = StringUtils.hasText(viewName) ? Stream.concat(Arrays.stream(baseArgs),
Arrays.stream(new String[]{viewName})).toArray(String[]::new) : baseArgs;
CLI cli = doWork(host, auth, finalArgs, PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
List<JobDto> jobDtoList = new ArrayList<>(10);
boolean ifSuccess = ResultDto.SUCCESS.equals(String.valueOf(code));
boolean listAllJobs = StringUtils.hasText(viewName) ? true : false;
if (ifSuccess) {
if (StringUtils.hasText(msg)) {
String[] arr = msg.split(END_SYMBOL);
for (String tmp : arr) {
JobDto jobDto = new JobDto();
jobDto.setJobName(tmp);
jobDto.setViewName(listAllJobs ? viewName : "");
jobDtoList.add(jobDto);
}
}
}
return ifSuccess ? ResultDto.buildSuccess(jobDtoList) : ResultDto.buildError(jobDtoList).msg(msg);
}
/**
* 查询Jenkins上的凭证列表
*
* @param ip Jenkins服务IP
* @param port Jenkins服务端口
* @param username Jenkins服务登录用户名
* @param password Jenkins服务登录密码
* @param viewName Jenkins视图名称,为空时,查询全部Jobs
* @return ResultDto
*/
public static ResultDto listJobs(String ip, String port, String username, String password, String viewName) {
String host = "http://" + ip + ":" + port;
String auth = username + ":" + password;
return listJobs(host, auth, viewName);
}
public static ResultDto addJob2View(String host, String auth, String viewName, String[] jobNames) {
String[] baseArgs = new String[]{Operation.ADD_TO_VIEW.op};
String[] finalArgs = Stream.of(baseArgs, new String[]{viewName}, jobNames).flatMap(Arrays::stream)
.toArray(String[]::new);
CLI cli = doWork(host, auth, finalArgs, PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
List<JobDto> jobDtoList = new ArrayList<>(10);
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccess(jobDtoList) : ResultDto.buildError(jobDtoList).msg(msg);
}
public static ResultDto addJob2View(String ip, String port, String username, String password,
String viewName, String[] jobNames) {
String host = "http://" + ip + ":" + port;
String auth = username + ":" + password;
return addJob2View(host, auth, viewName, jobNames);
}
public static ResultDto removeJobFromView(String host, String auth, String viewName, String[] jobNames) {
String[] baseArgs = new String[]{Operation.REMOVE_FROM_VIEW.op};
String[] finalArgs = Stream.of(baseArgs, new String[]{viewName}, jobNames).flatMap(Arrays::stream)
.toArray(String[]::new);
CLI cli = doWork(host, auth, finalArgs, PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
List<JobDto> jobDtoList = new ArrayList<>(10);
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccess(jobDtoList) : ResultDto.buildError(jobDtoList).msg(msg);
}
/**
* 创建Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param job Jenkins Job
* @return {@link ResultDto}
*/
public static ResultDto createJob(String host, String auth, JobDto job) {
List<JobDto.SvnInfoDto> svnInfoDtoList = job.getSvnInfoList();
List<JobDto.GitInfoDto> gitInfoDtoList = job.getGitInfoList();
boolean scmCheck = CollectionUtils.isEmpty(svnInfoDtoList) && CollectionUtils.isEmpty(gitInfoDtoList);
if (scmCheck) {
return ResultDto.buildErrorDto().msg("源码管理地址信息不能为空");
}
String xml = "<?xml version='1.1' encoding='UTF-8'?>\n" +
"<project>\n" +
" <actions/>\n" +
" <description>" + job.getDescription() + "</description>\n" +
" <keepDependencies>false</keepDependencies>\n" +
" <properties/>\n";
if (!CollectionUtils.isEmpty(svnInfoDtoList)) {
xml += buildSvn(job);
}
if (!CollectionUtils.isEmpty(gitInfoDtoList)) {
xml += buildGit(job);
}
xml += " </locations>\n" +
" <excludedRegions></excludedRegions>\n" +
" <includedRegions></includedRegions>\n" +
" <excludedUsers></excludedUsers>\n" +
" <excludedRevprop></excludedRevprop>\n" +
" <excludedCommitMessages></excludedCommitMessages>\n" +
" <workspaceUpdater class=\"hudson.scm.subversion.UpdateUpdater\"/>\n" +
" <ignoreDirPropChanges>false</ignoreDirPropChanges>\n" +
" <filterChangelog>false</filterChangelog>\n" +
" <quietOperation>true</quietOperation>\n" +
" </scm>\n" +
" <canRoam>true</canRoam>\n" +
" <disabled>" + job.getDisabled() + "</disabled>\n" +
" <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>\n" +
" <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>\n" +
" <triggers/>\n" +
" <concurrentBuild>false</concurrentBuild>\n";
xml += buildBuilders(job);
xml += " <publishers/>\n" +
" <buildWrappers/>\n" +
"</project>";
CLI cli = doWork(host, auth, new String[]{Operation.CREATE.op, job.getJobName()}, PROTOCOL_HTTP, xml);
int code = cli.code;
String msg = cli.msg;
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
}
/**
* 更新Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param job Jenkins Job
* @return {@link ResultDto}
*/
public static ResultDto updateJob(String host, String auth, JobDto job) {
//TODO:待补充完善
return null;
}
/**
* 删除单个或多个Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param jobNames Jenkins Job名称集
* @return {@link ResultDto}
*/
public static ResultDto deleteJob(String host, String auth, String[] jobNames) {
String[] finalArgs = Stream.of(new String[]{Operation.DELETE.op}, jobNames)
.flatMap(Arrays::stream).toArray(String[]::new);
return operation(host, auth, finalArgs);
}
/**
* 复制Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param srcJobName 需要被复制的Jenkins Job名称
* @param destJobName 新Jenkins Job名称
* @return {@link ResultDto}
*/
public static ResultDto copyJob(String host, String auth, String srcJobName, String destJobName) {
String[] finalArgs = new String[]{Operation.COPY.op, srcJobName, destJobName};
return operation(host, auth, finalArgs);
}
/**
* 查询单个Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param jobName Jenkins Job名称
* @return {@link ResultDto}
*/
public static ResultDto<JobDto> getJob(String host, String auth, String jobName) {
String[] finalArgs = new String[]{Operation.GET.op, jobName};
ResultDto resultDto = operation(host, auth, finalArgs);
JobDto jobDto = new JobDto();
jobDto.setJobName(jobName);
//解析xml,获取名称、描述等信息
String msg = ObjectUtils.isEmpty(resultDto.getData()) ? "" : resultDto.getData().toString();
if (StringUtils.hasText(msg)) {
jobDto.parseXmlStr2Dto(msg);
}
return resultDto.data(jobDto);
}
/**
* 构建单个Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param jobName Jenkins Job名称
* @return {@link ResultDto}
*/
public static ResultDto buildJob(String host, String auth, String jobName) {
return operation(host, auth, new String[]{Operation.BUILD.op, jobName});
}
/**
* 输出单个Job的构建日志信息
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param jobName Jenkins Job名称
* @param buildId 构建id,为空时,查询最后一次构建的信息
* @return {@link ResultDto}
*/
public static ResultDto console(String host, String auth, String jobName, Integer buildId) {
String[] baseArgs = new String[]{Operation.CONSOLE.op, jobName};
String[] finalArgs = ObjectUtils.isEmpty(buildId) ? baseArgs : Stream.of(baseArgs, new String[]{buildId.toString()})
.flatMap(Arrays::stream).toArray(String[]::new);
return operation(host, auth, finalArgs);
}
/**
* 禁用Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param jobName Jenkins Job名称
* @return {@link ResultDto}
*/
public static ResultDto disableJob(String host, String auth, String jobName) {
return operation(host, auth, new String[]{Operation.DISABLE.op, jobName});
}
/**
* 启用Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param jobName Jenkins Job名称
* @return {@link ResultDto}
*/
public static ResultDto enableJob(String host, String auth, String jobName) {
return operation(host, auth, new String[]{Operation.ENABLE.op, jobName});
}
/**
* 重新加载Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param jobName Jenkins Job名称
* @return {@link ResultDto}
*/
public static ResultDto reloadJob(String host, String auth, String jobName) {
return operation(host, auth, new String[]{Operation.RELOAD.op, jobName});
}
/**
* 停止构建Job
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param jobNames Jenkins Job名称集合
* @return {@link ResultDto}
*/
public static ResultDto stopBuildJob(String host, String auth, String[] jobNames) {
String[] finalArgs = Stream.of(new String[]{Operation.STOP_BUILDS.op}, jobNames)
.flatMap(Arrays::stream).toArray(String[]::new);
return operation(host, auth, finalArgs);
}
/**
* 获取Jenkins Job的构建信息
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param username Jenkins登录用户名
* @param password Jenkins登录用户密码
* @param jobName Jenkins Job名称
* @param buildId Jenkins Job构建id
* @return {@link ResultDto}
*/
public static ResultDto<BuildInfoDto> getJobBuildInfoDetail(String host, String username, String password,
String jobName, Integer buildId) {
if (ObjectUtils.isEmpty(buildId)) {
return ResultDto.buildErrorDto().msg("buildId can not empty.");
}
JenkinsClient client = JenkinsClient.builder().endPoint(host).credentials(username + ":" + password).build();
List<QueueItem> list = client.api().queueApi().queue();
for (QueueItem item : list) {
System.out.println(item.why() + " " + item.task().name());
}
System.out.println(JsonUtil.toJSONString(client.api().systemApi().systemInfo()));
BuildInfo data = client.api().jobsApi().buildInfo(null, jobName, buildId);
System.out.println(data.building());
String url = JOB_BUILD_DETAIL_URL.replace("@host", host).replace("@jobName", jobName)
.replace("@buildId", buildId.toString());
return doGet(url, username, password, BuildInfoDto.class);
}
/**
* 获取Jenkins Job的构建信息
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param username Jenkins登录用户名
* @param password Jenkins登录用户密码
* @param jobName Jenkins Job名称
* @param buildId Jenkins Job构建id
* @return {@link ResultDto}
*/
public static boolean getJobBuildStatus(String host, String username, String password,
String jobName, Integer buildId) {
if (ObjectUtils.isEmpty(buildId)) {
throw new IllegalArgumentException("buildId can not empty.");
}
String url = JOB_BUILD_DETAIL_URL.replace("@host", host).replace("@jobName", jobName)
.replace("@buildId", buildId.toString());
BuildInfoDto infoDto = getJobBuildInfoDetail(url, username, password, jobName, buildId).getData();
return infoDto.getBuilding();
}
/**
* 操作单个Job(查询、启用、禁用、删除)
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param args Jenkins CLI参数
* @return {@link ResultDto}
*/
private static ResultDto operation(String host, String auth, String[] args) {
CLI cli = doWork(host, auth, args, PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccess(msg) : ResultDto.buildError(msg).msg(msg);
}
/**
* 组织Job的git xml章节内容
*
* @param job JobDto
* @return {@link String}
*/
private static String buildGit(JobDto job) {
//TODO:待补充组装git相关xml配置
String xml = "";
return xml;
}
/**
* 组织Job的svn xml章节内容
*
* @param job JobDto
* @return {@link String}
*/
private static String buildSvn(JobDto job) {
List<JobDto.SvnInfoDto> svnInfoDtoList = job.getSvnInfoList();
String xml = " <scm class=\"hudson.scm.SubversionSCM\" >\n" +
" <locations>\n";
StringBuilder sb = new StringBuilder();
for (JobDto.SvnInfoDto svnInfoDto : svnInfoDtoList) {
sb.append(" <hudson.scm.SubversionSCM_-ModuleLocation>\n")
.append(" <remote>" + svnInfoDto.getRemote() + "</remote>\n")
.append(" <credentialsId>" + svnInfoDto.getCredentialsId() + "</credentialsId>\n")
.append(" <local>" + svnInfoDto.getLocal() + "</local>\n")
.append(" <depthOption>" + svnInfoDto.getDepthOption() + "</depthOption>\n")
.append(" <ignoreExternalsOption>" + svnInfoDto.getIgnoreExternalsOption() + "</ignoreExternalsOption>\n")
.append(" <cancelProcessOnExternalsFail>" + svnInfoDto.getCancelProcessOnExternalsFail() + "</cancelProcessOnExternalsFail>\n")
.append(" </hudson.scm.SubversionSCM_-ModuleLocation>\n");
}
xml += sb.toString();
return xml;
}
/**
* 组织Job的builders xml章节内容
*
* @param job JobDto
* @return {@link String}
*/
private static String buildBuilders(JobDto job) {
String xml = "";
List<String> shellList = job.getBuildCmdList();
List<JobDto.SonarRunnerBuilderDto> sonarRunnerBuilderDtoList = job.getSonarRunnerBuilderDtoList();
boolean checkBuilders = CollectionUtils.isEmpty(shellList) && CollectionUtils.isEmpty(sonarRunnerBuilderDtoList);
if (checkBuilders) {
xml += "<builders/>";
} else {
xml += " <builders>\n";
if (!CollectionUtils.isEmpty(shellList)) {
String shells = shellList.stream().collect(Collectors.joining("\n"));
xml += " <hudson.tasks.Shell>\n" +
" <command>" + shells +
" </command>\n" +
" <configuredLocalRules/>\n" +
" </hudson.tasks.Shell>\n";
}
if (!CollectionUtils.isEmpty(sonarRunnerBuilderDtoList)) {
JobDto.ProjectInfo projectInfo = job.getProjectInfo();
String keyInfo = projectInfo.getProjectName() + "_" + job.getJobName() + "_" + projectInfo.getBuildVersion();
StringBuilder sb = new StringBuilder();
for (JobDto.SonarRunnerBuilderDto sonar : sonarRunnerBuilderDtoList) {
sb.append(" <hudson.plugins.sonar.SonarRunnerBuilder>\n")
.append(" <project></project>\n")
.append(" <properties>sonar.projectKey=" + keyInfo + "\n")
.append("sonar.projectName=" + keyInfo + "\n")
.append("sonar.projectVersion=" + projectInfo.getBuildVersion() + "\n")
.append("sonar.language=" + sonar.getSonarScannerLanguage() + "\n")
.append("sonar.sourceEncoding=UTF-8\n")
.append("sonar.java.binaries=target/classes\n")
.append("sonar.sources=.\n")
.append("sonar.login=" + job.getSonarLogin() + "\n")
.append("sonar.password=" + job.getSonarPassword() + "\n")
.append("sonar.scm.disabled=" + sonar.getSonarScmDisabled())
.append(" </properties>\n")
.append(" <javaOpts>" + sonar.getJavaOpts() + "</javaOpts>\n")
.append(" <additionalArguments>" + sonar.getAdditionalArguments() + "</additionalArguments>\n")
.append(" <jdk>" + sonar.getJdk() + "</jdk>\n")
.append(" <task>" + sonar.getTask() + "</task>\n")
.append(" </hudson.plugins.sonar.SonarRunnerBuilder>\n");
}
xml += sb.toString();
}
xml += " </builders>\n";
}
return xml;
}
}
public static class Views {
public enum Operation {
/**
* 创建Jenkins视图
*/
CREATE("create-view", "创建Jenkins视图"),
/**
* 更新Jenkins视图
*/
UPDATE("update-view", "更新Jenkins视图"),
/**
* 删除Jenkins视图
*/
DELETE("delete-view", "删除Jenkins视图"),
/**
* 查询Jenkins视图
*/
GET("get-view", "查询Jenkins视图");
@Setter
@Getter
private String op;
@Setter
@Getter
private String desc;
Operation(String op, String desc) {
this.setOp(op);
this.setDesc(desc);
}
}
/**
* 创建View
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param viewDto ViewDto
* @return {@link ResultDto}
*/
public static ResultDto createView(String host, String auth, ViewDto viewDto) {
return operation(host, auth, viewDto, Operation.CREATE);
}
/**
* 更新View
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param viewDto ViewDto
* @return {@link ResultDto}
*/
public static ResultDto updateView(String host, String auth, ViewDto viewDto) {
return operation(host, auth, viewDto, Operation.UPDATE);
}
/**
* 删除View
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param viewDto ViewDto
* @return {@link ResultDto}
*/
public static ResultDto deleteView(String host, String auth, ViewDto viewDto) {
return operation(host, auth, viewDto, Operation.DELETE);
}
/**
* 重命名View
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param oldViewName Jenkins旧视图名称
* @param newViewName Jenkins新视图名称
* @return ResultDto
*/
public static ResultDto rename(String host, String auth, String oldViewName, String newViewName) {
return copyOrRenameView(host, auth, oldViewName, newViewName, true);
}
/**
* 拷贝View
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param oldViewName Jenkins旧视图名称
* @param newViewName Jenkins新视图名称
* @return ResultDto
*/
public static ResultDto copyView(String host, String auth, String oldViewName, String newViewName) {
return copyOrRenameView(host, auth, oldViewName, newViewName, false);
}
/**
* 根据视图名称查询视图信息
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param viewName Jenkins视图名称
* @param returnJobDetails 是否返回Job详细信息,默认不返回。
* @return {@link ResultDto}
*/
public static ResultDto<ViewDto> getView(String host, String auth, String viewName, Boolean returnJobDetails) {
if (!StringUtils.hasText(viewName)) {
return ResultDto.buildErrorDto().msg("视图名称不能为空");
}
CLI cli = doWork(host, auth, new String[]{Operation.GET.op, viewName}, PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
boolean ifSuccess = ResultDto.SUCCESS.equals(String.valueOf(code));
ViewDto viewDto = new ViewDto();
viewDto.setViewName(viewName);
//解析xml,获取view名称、描述、关联的job等信息
if (StringUtils.hasText(msg)) {
viewDto.parseXmlStr2Dto(msg);
}
if (ObjectUtils.isEmpty(returnJobDetails)) {
returnJobDetails = false;
}
if (returnJobDetails) {
List<JobDto> jobDtoList = viewDto.getJobList();
for (JobDto jobDto : jobDtoList) {
String jobName = jobDto.getJobName();
JobDto dto = Jobs.getJob(host, auth, jobName).getData();
BeanUtils.copyProperties(dto, jobDto);
}
}
return ifSuccess ? ResultDto.buildSuccess(viewDto) : ResultDto.buildError(viewDto).msg(msg);
}
/**
* 拷贝或重命名View
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param oldViewName Jenkins旧视图名称
* @param newViewName Jenkins新视图名称
* @return ResultDto
*/
private static ResultDto copyOrRenameView(String host, String auth, String oldViewName, String newViewName, boolean rename) {
//1、先查询目标View
ResultDto<ViewDto> oldViewInfoResult = getView(host, auth, oldViewName, true);
if (!oldViewInfoResult.ifSuccess()) {
return oldViewInfoResult;
}
ViewDto newViewInfo = new ViewDto();
ViewDto oldViewInfo = oldViewInfoResult.getData();
BeanUtils.copyProperties(oldViewInfo, newViewInfo);
newViewInfo.setViewName(newViewName);
//2、拷贝目标View内容,并重命名
ResultDto createResult = operation(host, auth, newViewInfo, Operation.CREATE);
if (!createResult.ifSuccess()) {
return createResult;
}
List<JobDto> jobDtoList = oldViewInfo.getJobList();
if (CollectionUtils.isEmpty(jobDtoList)) {
return createResult;
}
String[] jobNames = new String[jobDtoList.size()];
for (int i = 0; i < jobDtoList.size(); i++) {
jobNames[i] = jobDtoList.get(i).getJobName();
}
ResultDto addJob2ViewResult = Jobs.addJob2View(host, auth, newViewName, jobNames);
if (!addJob2ViewResult.ifSuccess()) {
return addJob2ViewResult;
}
if (rename) {
//3、再删除旧View
return operation(host, auth, oldViewInfo, Operation.DELETE);
} else {
return ResultDto.buildSuccessDto();
}
}
/**
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param viewDto ViewDto
* @param op 操作:create-view、update-view、delete-view
* @return {@link ResultDto}
*/
private static ResultDto operation(String host, String auth, ViewDto viewDto, Operation op) {
String operation = op.op;
String xml = "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n" +
"<hudson.model.ListView>\n" +
" <name>" + viewDto.getViewName() + "</name>\n" +
" <description>" + viewDto.getDescription() + "</description>\n" +
" <filterExecutors>" + viewDto.isFilterExecutors() + "</filterExecutors>\n" +
" <filterQueue>" + viewDto.isFilterQueue() + "</filterQueue>\n" +
" <properties class=\"hudson.model.View$PropertyList\"/>\n" +
" <jobNames>\n" +
" <comparator class=\"java.lang.String$CaseInsensitiveComparator\"/>\n" +
" </jobNames>\n" +
" <jobFilters>\n" +
" </jobFilters>\n" +
" <columns>\n" +
" <hudson.views.StatusColumn/>\n" +
" <hudson.views.WeatherColumn/>\n" +
" <hudson.views.JobColumn/>\n" +
" <hudson.views.LastSuccessColumn/>\n" +
" <hudson.views.LastFailureColumn/>\n" +
" <hudson.views.LastStableColumn/>\n" +
" <hudson.views.LastDurationColumn/>\n" +
" <hudson.views.BuildButtonColumn/>\n" +
" <jenkins.branch.DescriptionColumn />\n" +
" </columns>\n";
if (StringUtils.hasText(viewDto.getIncludeRegex())) {
xml += " <includeRegex>" + viewDto.getIncludeRegex() + "</includeRegex>\n";
}
xml += " <recurse>" + viewDto.isRecurse() + "</recurse>\n" +
"</hudson.model.ListView>";
boolean isUpdateOrDelete = Operation.DELETE.equals(op) || Operation.UPDATE.equals(op);
String[] finalArgs = isUpdateOrDelete ?
new String[]{operation, viewDto.getViewName()} : new String[]{operation};
CLI cli = doWork(host, auth, finalArgs, PROTOCOL_HTTP, xml);
int code = cli.code;
String msg = cli.msg;
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
}
}
public static class JenkinsSystem {
public enum Operation {
/**
* 获取Jenkins支持的命令
*/
HELP("help", "获取Jenkins支持的命令"),
/**
* 获取Jenkins版本信息
*/
VERSION("version", "获取Jenkins版本信息"),
/**
* 更新Jenkins全局配置信息
*/
RELOAD_CONFIGURATION("reload-configuration", "更新Jenkins全局配置信息"),
/**
* 重启Jenkins服务
*/
RESTART("restart", "重启Jenkins服务"),
/**
* 安全重启Jenkins服务(Safe Restart Jenkins. Don't start any builds.)
*/
SAFE_RESTART("safe-restart", "重启Jenkins服务"),
/**
* 停止Jenkins服务
*/
SHUTDOWN("shutdown", "停止Jenkins服务"),
/**
* 安全停止Jenkins服务(Puts Jenkins into the quiet mode, wait for existing builds to be completed,
* and then shut down Jenkins.)
*/
SAFE_SHUTDOWN("safe-shutdown", "安全停止Jenkins服务"),
/**
* 清除Jenkins中的构建队列(Clears the build queue.)
*/
CLEAR_QUEUE("clear-queue", "清除Jenkins中的构建队列"),
;
@Setter
@Getter
private String op;
@Setter
@Getter
private String desc;
Operation(String op, String desc) {
this.setOp(op);
this.setDesc(desc);
}
}
/**
* 获取Jenkins支持的命令
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto help(String host, String auth) {
return operation(host, auth, Operation.HELP);
}
/**
* 获取Jenkins版本信息
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto getVersion(String host, String auth) {
return operation(host, auth, Operation.VERSION);
}
/**
* 重启Jenkins服务
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto restart(String host, String auth) {
return operation(host, auth, Operation.RESTART);
}
/**
* 安全重启Jenkins服务(Safe Restart Jenkins. Don't start any builds.)
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto safeRestart(String host, String auth) {
return operation(host, auth, Operation.SAFE_RESTART);
}
/**
* 停止Jenkins服务
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto shutdown(String host, String auth) {
return operation(host, auth, Operation.SHUTDOWN);
}
/**
* 安全停止Jenkins服务(Puts Jenkins into the quiet mode, wait for existing builds to be completed,
* and then shut down Jenkins.)
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto safeShutdown(String host, String auth) {
return operation(host, auth, Operation.SAFE_SHUTDOWN);
}
/**
* 清除Jenkins中的构建队列(Clears the build queue.)
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto clearQueue(String host, String auth) {
return operation(host, auth, Operation.CLEAR_QUEUE);
}
/**
* 重新加载Jenkins配置信息
* Discard all the loaded data in memory and reload everything from file system.
* Useful when you modified config files directly on disk.
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @return ResultDto
*/
public static ResultDto reloadConfiguration(String host, String auth) {
return operation(host, auth, Operation.RELOAD_CONFIGURATION);
}
/**
* 基本操作
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param op 操作
* @return {@link ResultDto}
*/
private static ResultDto operation(String host, String auth, Operation op) {
CLI cli = doWork(host, auth, new String[]{op.op}, PROTOCOL_HTTP, null);
int code = cli.code;
String msg = cli.msg;
String successMsg = StringUtils.hasText(msg) ? msg.replaceAll(END_SYMBOL, "") : "";
return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
ResultDto.buildSuccessDto(successMsg) : ResultDto.buildErrorDto().msg(msg);
}
}
/**
* 调用Jenkins CLI
* 注意:查询类时xml为空;涉及到使用xml内容创建、更新操作的,xml不能为空!!!
*
* @param host Jenkins地址,比如:http://10.100.57.156:8080
* @param auth Jenkins登录用户名:密码,比如:admin:admin
* @param args 请求参数
* @param protocol 请求协议,比如http,webSocket,默认http
* @param xml 处理XML相关,不使用标准输入流,此时使用HTTP协议调用
* @return CLI
*/
private static CLI doWork(String host, String auth, String[] args, String protocol, String xml) {
if (null == args || args.length == 0) {
throw new IllegalArgumentException("args cannot be empty");
}
if (!StringUtils.hasText(protocol)) {
protocol = PROTOCOL_HTTP;
}
byte[] xmlData = new byte[]{};
if (StringUtils.hasText(xml)) {
xmlData = xml.getBytes(StandardCharsets.UTF_8);
}
CLI cli = new CLI();
String[] baseArgs = new String[]{"-auth", auth, "-s", host, protocol};
String[] finalArgs = Stream.concat(Arrays.stream(baseArgs), Arrays.stream(args)).toArray(String[]::new);
log.info("executing command: {}", JsonUtil.toJSONString(finalArgs));
try {
cli._main(finalArgs, xmlData);
} catch (Exception e) {
cli.code = -1;
cli.msg = e.getMessage();
log.error("executing command: {} cause error ", JsonUtil.toJSONString(finalArgs), e);
}
return cli;
}
private static <T> ResultDto<T> doGet(String urlString, String username, String password, Class clz) {
URI uri = URI.create(urlString);
HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(uri.getHost(), uri.getPort()), new UsernamePasswordCredentials(username, password));
AuthCache authCache = new BasicAuthCache();
BasicScheme basicAuth = new BasicScheme();
authCache.put(host, basicAuth);
CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
HttpGet httpGet = new HttpGet(uri);
HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
if (ObjectUtils.isEmpty(clz)) {
clz = String.class;
}
T data = (T) new Object();
try {
CloseableHttpResponse response = httpClient.execute(host, httpGet, localContext);
String returnMsg = EntityUtils.toString(response.getEntity());
System.out.println(returnMsg);
if (StringUtils.hasText(returnMsg)) {
data = (T) JsonUtil.string2Obj(returnMsg, clz);
return ResultDto.buildSuccessDto().data(data);
}
return ResultDto.buildSuccessDto().data(returnMsg);
} catch (Exception e) {
log.error("call {} failed", urlString, e);
return ResultDto.buildErrorDto().data(data);
}
}
}