本章介绍基于 Jenkins API 调用的跨平台 API 对接。
基于Jenkins实现跨平台API对接
Jenkins 提供了远程访问应用编程接口(Remote Access API),能够通过 Http 协议远程调用相关命令操作 Jenkins 进行 Jenkins 视图、任务、插件、构建信息、任务日志信息、统计信息等,非常容易与其配合更好的完成 CI/CD 工作。
Jenkins API 总共有三种格式,分别为:
- XML API
可以使用 xml 方式进行 API 的使用,这种方式的优势在于可以使用强大的 xpath 特性进行相关的访问控制。如我们下文将要介绍的 Jenkins 客户端底层就是基于 XML API 实现的。
- JSON API
使用 JSON 方式进行操作,因为 json 基本上已经是应用之间数据交换的准标准格式之一,这种方式比较方便 Javascript 或者和其他应用的集成。
- Python API 可以通过 python-jenkins 库对 Jenkins 进行控制操作。此库对 Jenkins 的 API 进行了进一步的包装,使用起来更加方便,但是一般需要安装 python-jenkins,并通过 python 脚本的执行来达到集成的方式。
为什么基于Jenkins API对接
- 频繁创建 Job 时,降低手工错误的概率
在工作中,如果需要创建的 Jenkins 的 Job 非常多,而大多又呈现有规律的方式时,Job 的创建成为了一个繁琐而又需要频繁操作的任务。在这种场景下,使用 API 结合脚本进行自动化可以提高效率,降低手工错误的几率。
- 满足特定条件时自动触发 Jenkins
如果需要动态的创建 Jenkins Job ,如根据中间结果在某个触发点自动生成,再如生成的 Job 需要使用的参数也是动态运行阶段才能取到值的场景下。
- 基于 Jenkins 自研产品或工具
如果产品或工具相关的功能,需要基于 Jenkins 进行研发,而且不希望用户直接使用 Jenkins,仅将 Jenkins 作为背后的执行引擎的场景,这种情况下也需要使用 Jenkins API 才能完成。
快速开始
下面我们通过实战学习下如何将 Spring Boot 和 Jenkins 进行集成,实现跨平台 API 对接。
Maven 依赖
<dependency> <groupId>com.offbytwo.jenkins</groupId> <artifactId>jenkins-client</artifactId> <version>0.3.8</version> </dependency>
我们先引入 Jenkins-client 用于和 Jenkins 进行交互。而 Jenkins-client 的底层实现其实就是调用 Jenkins XML API 来完成操作 Jenkins 的,如下表部分示例所示。
操作 | HTTP动作 | API | jenkins-client 方法 | URI 使用示例 |
创建 Job | POST | /createItem | jenkinsServer.createJob() | /createItem?name=Job 名称 |
起用 Job | POST | /job/job 名称/enable | jenkinsServer.enableJob() | /job/job 名称/enable |
禁用 Job | POST | /job/job 名称/disable | jenkinsServer.disableJob | /job/job 名称/disable |
删除 Job | POST | /job/job 名称/doDelete | jenkinsServer.deleteJob() | /job/job 名称/doDelete |
执行 Job | POST | /job/job 名称/build | job.build() | /job/job 名称/build |
停止执行中的 Job | POST | /job/Job 名称/构建序号/stop | build.Stop() | /job/Job 名称/构建序号/stop |
常用类和方法
- JenkinsHttpClient:封装了调用 JenkinsAPI 的底层方法
JenkinsHttpClient(URI uri, String username, String password)
操作 API 方法示例
方法名 | 说明 |
get(String path) | 根据请求路径获取 Jenkins 的文本内容 |
getFile(URI path) | 根据请求路径获取 Jenkins 的文件内容 |
post(String path, boolean crumbFlag) | 根据请求路径向 Jenkins 发送 post 请求 |
getJenkinsVersion() | 获取 Jenkins 版本 |
post(String path, D data, Class cls, boolean crumbFlag) | 根据请求路径向 Jenkins 发送 post 请求数据 |
post_xml(String path, String xml_data, boolean crumbFlag) | 根据请求路径向 Jenkins 发送 post 请求 xml 数据 |
- JenkinsServer:封装了调用 JenkinsAPI 的语义级别的方法,其本质调用的是 JenkinsHttpClient 类中的方法,只是根据操作 Jenkins 的功能进行了语义级别的封装
JenkinsServer(JenkinsHttpConnection client)
操作 API 方法示例
方法名 | 说明 |
isRunning() | 通过 ping 得到 Jenkins 端点的当前状态 |
getVersion() | 获取 Jenkins 的版本信息 |
getJobs() | 获取 Jenkins 服务器上所有已定义作业的列表(仅摘要信息) |
getViews() | 获取 Jenkins 服务器上所有已定义视图的列表(仅摘要信息) |
getView(String name) | 从 Jenkins 服务器获取单个视图对象 |
getJob(String jobName) | 从 Jenkins 服务器获取单个 Job |
getJobXml(String jobName) | 获取现有 Job 的 xml 描述 |
createJob(String jobName, String jobXml, Boolean crumbFlag) | 使用提供的 xml 在服务器上创建 Job,且需要权限认证 |
updateJob(String jobName, String jobXml, boolean crumbFlag) | 更新现有 Job 的 xml 描述,且需要权限认证 |
createView(String viewName, String viewXml) | 使用提供的 xml 在服务器上创建一个视图 |
updateView(String viewName, String viewXml) | 更新 Jenkins 服务器现有视图的 xml 描述 |
createFolder(String folderName) | 在 Jenkins 服务器上创建一个文件夹(在根目录下) |
getJobXml(String jobName) | 获取现有 Job 的 xml 描述 |
getLabel(String labelName) | 获取现有标签的描述 |
getComputers() | 获取 Jenkins 服务器上所有计算机的列表(仅摘要信息) |
getPluginManager() | 获取 Jenkins 插件管理器的信息 |
deleteJob(String jobName, boolean crumbFlag) | 从 jenkins 删除一个 Job,且需要权限认证 |
disableJob(String jobName) | 从 jenkins 禁用一个 Job |
enableJob(String jobName) | 从 jenkins 启用一个 Job |
runScript(String script, boolean crumbFlag) | 在服务器上运行提供的 groovy 脚本并返回结果。这类似于使用脚本控制台运行 groovy 脚本。 |
方法名 | 说明 |
renameJob(String oldJobName, String newJobName) | 重命名一个 Job |
close() | 关闭底层资源。关闭的实例不应该再被使用,且关闭一个已经关闭的实例没有副作用 |
restart(Boolean crumbFlag) | 在不等待任何现有构建完成的情况下重新启动 Jenkins |
safeRestart(Boolean crumbFlag) | 将 Jenkins 设置为安静模式,等待已存在的构建待完成,然后重新启动 Jenkins |
exit(Boolean crumbFlag) | 在不等待任何现有构建完成的情况下关闭 Jenkins |
safeExit(Boolean crumbFlag) | 让 Jenkins 进入安静模式,等待现有的构建完成,然后关闭 Jenkins |
- Job:Jenkins 中 job 对应的实体类,有很多实用的语义级别的方法,如构建等。
Job(String name, String url)
方法名 | 说明 |
getName() | 获取 Job 名称 |
getUrl() | 获取 Job 地址 |
getFileFromWorkspace(String fileName) | 从工作区获取一个文件 |
build() | 触发一个没有参数的构建 |
build(boolean crumbFlag) | 触发一个没有参数的构建,需权限校验 |
build(Map params) | 仅使用字符串参数触发参数化构建 |
build(Map params, boolean crumbFlag) | 使用字符串参数触发参数化构建,需权限校验 |
如何获取创建(更新) Jenkins Job 的请求参数数据
- 创建新 Job
- 进入 Job 配置
- 将 Job/Job 名称/ Configure 改为 Job/Job 名称/ Config.xml 并回车
请求参数数据
- 在 resources 目录下,jenkinsDir ,并添加 hogwarts_test_mini_start_test.xml 文件,此时我们为了测试命令可以正常被执行,需要在测试命令前加 eval 关键字,并将${testCommand}放在英文双引号括内部,如图中红框部分。
- JenkinsUtil 示例代码
import com.offbytwo.jenkins.JenkinsServer; import com.offbytwo.jenkins.client.JenkinsHttpClient; import com.offbytwo.jenkins.model.Job; import org.springframework.core.io.ClassPathResource; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; /** * @Author tlibn * @Date 2020/8/11 15:21 **/ public class JenkinsUtil { //调试使用 public static void main(String[] args) throws IOException, URISyntaxException { build("hogwarts_test_mini_start_test_100","12","token","pwd"); } public static void build(String jobName, String userId, String remark,String testCommand) throws IOException, URISyntaxException { System.out.println("========== 执行开始 ==========="); // 1. 通过 ClassPathResource 获取 Jenkins 的 JenkinsJob 请求参数数据 ClassPathResource classPathResource = new ClassPathResource("JenkinsConfigDir/hogwarts_jenkins_test_start.xml"); InputStream inputStream = classPathResource.getInputStream(); String jobConfigXml = FileUtil.getText(inputStream); // 2. 获取 Jenkins 信息 String baseUrl = JenkinsInfo.baseUrl; String userName = JenkinsInfo.userName; String password = JenkinsInfo.password; // 3. 根据 Jenkins 信息创建 JenkinsHttpClient 对象 JenkinsHttpClient jenkinsHttpClient = new JenkinsHttpClient(new URI(baseUrl),userName,password); // 4. 根据 Jenkins 客户端创建 JenkinsServer 对象 JenkinsServer jenkinsServer = new JenkinsServer(jenkinsHttpClient); // 5. 创建 Job,如果Job已经存在,可以改为更新 Job 方法 jenkinsServer.createJob(jobName,jobConfigXml,true); // 6. 获取 Jenkins 服务器中所有的 Job 信息 Map<String, Job> jobMap = jenkinsServer.getJobs(); // 7. 获取 Jenkins 服务器中我们创建的单个 Job 信息 Job job = jobMap.get(jobName); // 8. 组装 Jenkins 服务器的构建参数 Map<String,String> map = new HashMap<>(); map.put("userId",userId); map.put("remark",remark); map.put("testCommand",testCommand); // 9. 构建 Jenkins Job job.build(map,true); System.out.println("========== 执行完毕 ==========="); } }
- 创建新 Job 并构建成功
- 构建参数页面查看参数数据
- 构建日志页面查看 pwd 命令执行情况
数据持久化技术就先讲到这里啦,下面留两个思考给大家,希望大家能用心练习一下哦~
- 尝试更新 Jenkins Job 配置信息,并在构建参数中新增用户名称字段
- 将 Jenkins API 调用和 Spring Boot 结合在一起,通过 postman 发送以下数据进行 Jenkins Job 的创建和更新操作
{ "jobName": "hogwarts_test_mini_start_test_100", "testCommand": "pwd", "remark": "token", "userId": "12" }