使用Jenkins CLI进行二次开发
使用背景
公司自研CI/DI平台,借助Jenkins+SonarQube进行代码质量管理。
对接版本
Jenkins版本为:Version 2.428
SonarQube版本为:Community EditionVersion 10.2.1 (build 78527)
技术选型
Java对接Jenkins有第三方组件,比如jenkins-rest、jenkins-client,但是考虑到第三方组件会引入其他jar包,而且会存在漏洞问题。
到时候升级组件时可能会和项目框架本身使用的第三方jar起冲突。因此,使用jenkins-cli来实现自己的需求。
注意:这里不是单纯的使用java -jar jenkins-cli.jar -http xxxx来实现接口调用,
而且提取并修改jenkins-cli.jar中的源码来达到自身需求开发的目的。
比如创建View时,使用jenkins-cli.jar时需要传入xml内容(标准输入流),
所以我们进行了改造,可以支持传入byte数组的形式,使用体验更符合大众。
jenkins-cli.jar的源码修改
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 ;
@Slf4j
public class JenkinsUtil {
private static final String PROTOCOL_HTTP = "-http" ;
private static final String PROTOCOL_WEB_SOCKET = "-webSocket" ;
private static final String END_SYMBOL = "\\r\\n" ;
private static final String LINE_BREAK_SYMBOL = "\\n" ;
public static class Credentials {
private static final String SYSTEM_STORE_ID = "system::system::jenkins" ;
private static final String GLOBAL_DOMAIN = "(global)" ;
private static final String CREDENTIAL_NOT_FOUND = "No such credential" ;
enum Operation {
LIST_CREDENTIALS ( "list-credentials" , "查询Jenkins Credentials集合" ) ,
LIST_CREDENTIALS_AS_XML ( "list-credentials-as-xml" , "查询Jenkins Credentials集合,返回xml" ) ,
CREATE_CREDENTIALS ( "create-credentials-by-xml" , "创建Jenkins Credentials" ) ,
UPDATE_CREDENTIALS ( "update-credentials-by-xml" , "更新Jenkins Credentials" ) ,
DELETE_CREDENTIALS ( "delete-credentials" , "删除Jenkins Credentials" ) ,
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) ;
}
}
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) ;
}
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) ;
}
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) ;
}
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) ;
}
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) ;
}
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) ;
}
}
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 {
CREATE ( "create-job" , "创建Jenkins Job" ) ,
COPY ( "copy-job" , "复制Jenkins Job" ) ,
UPDATE ( "update-job" , "更新Jenkins Job" ) ,
DELETE ( "delete-job" , "删除Jenkins Job" ) ,
BUILD ( "build" , "构建Jenkins Job" ) ,
CONSOLE ( "console" , "Jenkins Job构建日志输出" ) ,
LIST ( "list-jobs" , "查询Jenkins Job集合" ) ,
GET ( "get-job" , "查询Jenkins Job" ) ,
ADD_TO_VIEW ( "add-job-to-view" , "将Jenkins Job添加到视图中" ) ,
REMOVE_FROM_VIEW ( "remove-job-from-view" , "将Jenkins Job从视图中移除" ) ,
ENABLE ( "enable-job" , "启用Jenkins Job" ) ,
DISABLE ( "disable-job" , "禁用Jenkins Job" ) ,
RELOAD ( "reload-job" , "重新加载Jenkins Job" ) ,
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) ;
}
}
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) ;
}
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) ;
}
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) ;
}
public static ResultDto updateJob ( String host, String auth, JobDto job) {
return null ;
}
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) ;
}
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) ;
}
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) ;
String msg = ObjectUtils . isEmpty ( resultDto. getData ( ) ) ? "" : resultDto. getData ( ) . toString ( ) ;
if ( StringUtils . hasText ( msg) ) {
jobDto. parseXmlStr2Dto ( msg) ;
}
return resultDto. data ( jobDto) ;
}
public static ResultDto buildJob ( String host, String auth, String jobName) {
return operation ( host, auth, new String [ ] { Operation . BUILD . op, jobName} ) ;
}
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) ;
}
public static ResultDto disableJob ( String host, String auth, String jobName) {
return operation ( host, auth, new String [ ] { Operation . DISABLE . op, jobName} ) ;
}
public static ResultDto enableJob ( String host, String auth, String jobName) {
return operation ( host, auth, new String [ ] { Operation . ENABLE . op, jobName} ) ;
}
public static ResultDto reloadJob ( String host, String auth, String jobName) {
return operation ( host, auth, new String [ ] { Operation . RELOAD . op, jobName} ) ;
}
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) ;
}
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 ) ;
}
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 ( ) ;
}
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) ;
}
private static String buildGit ( JobDto job) {
String xml = "" ;
return xml;
}
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;
}
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 {
CREATE ( "create-view" , "创建Jenkins视图" ) ,
UPDATE ( "update-view" , "更新Jenkins视图" ) ,
DELETE ( "delete-view" , "删除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) ;
}
}
public static ResultDto createView ( String host, String auth, ViewDto viewDto) {
return operation ( host, auth, viewDto, Operation . CREATE ) ;
}
public static ResultDto updateView ( String host, String auth, ViewDto viewDto) {
return operation ( host, auth, viewDto, Operation . UPDATE ) ;
}
public static ResultDto deleteView ( String host, String auth, ViewDto viewDto) {
return operation ( host, auth, viewDto, Operation . DELETE ) ;
}
public static ResultDto rename ( String host, String auth, String oldViewName, String newViewName) {
return copyOrRenameView ( host, auth, oldViewName, newViewName, true ) ;
}
public static ResultDto copyView ( String host, String auth, String oldViewName, String newViewName) {
return copyOrRenameView ( host, auth, oldViewName, newViewName, false ) ;
}
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) ;
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) ;
}
private static ResultDto copyOrRenameView ( String host, String auth, String oldViewName, String newViewName, boolean rename) {
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) ;
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) {
return operation ( host, auth, oldViewInfo, Operation . DELETE ) ;
} else {
return ResultDto . buildSuccessDto ( ) ;
}
}
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 {
HELP ( "help" , "获取Jenkins支持的命令" ) ,
VERSION ( "version" , "获取Jenkins版本信息" ) ,
RELOAD_CONFIGURATION ( "reload-configuration" , "更新Jenkins全局配置信息" ) ,
RESTART ( "restart" , "重启Jenkins服务" ) ,
SAFE_RESTART ( "safe-restart" , "重启Jenkins服务" ) ,
SHUTDOWN ( "shutdown" , "停止Jenkins服务" ) ,
SAFE_SHUTDOWN ( "safe-shutdown" , "安全停止Jenkins服务" ) ,
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) ;
}
}
public static ResultDto help ( String host, String auth) {
return operation ( host, auth, Operation . HELP ) ;
}
public static ResultDto getVersion ( String host, String auth) {
return operation ( host, auth, Operation . VERSION ) ;
}
public static ResultDto restart ( String host, String auth) {
return operation ( host, auth, Operation . RESTART ) ;
}
public static ResultDto safeRestart ( String host, String auth) {
return operation ( host, auth, Operation . SAFE_RESTART ) ;
}
public static ResultDto shutdown ( String host, String auth) {
return operation ( host, auth, Operation . SHUTDOWN ) ;
}
public static ResultDto safeShutdown ( String host, String auth) {
return operation ( host, auth, Operation . SAFE_SHUTDOWN ) ;
}
public static ResultDto clearQueue ( String host, String auth) {
return operation ( host, auth, Operation . CLEAR_QUEUE ) ;
}
public static ResultDto reloadConfiguration ( String host, String auth) {
return operation ( host, auth, Operation . RELOAD_CONFIGURATION ) ;
}
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) ;
}
}
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) ;
}
}
}