leoninew 原创,转载请注明来自博客园

BACKGROUND

使用 jenkins rest api 触发的 job 会先进入任务队列,然后异步执行,而无法直接获取到被触发的 job 构建记录编号。虽然 job 的描述信息中 lastBuild 字段告知了最后的构建记录,但无论是先获取 lastBuild,自增其编号作为下次构建 id,还是请求内等待 lastBuild 更新作为构建记录的做法,都存在若干问题:

  1. 由于构建任务延迟触发,先触发 job 构建再紧接着获取 lastBuild 的多数情况下将返回历史而非当前的构建记录,不可行。
  2. 以 lastBuild 编号自增作为下次 job 构建编号的做法不可靠,部分机制如插件可以修改自增步进,见 Changing Jenkins build number: If you have access to the script console (Manage Jenkins -> Script Console), then you can do this following:

Jenkins.instance.getItemByFullName("YourJobName").updateNextBuildNumber(45)

  1. 在多个调用方同时进行 job 构建时,无法判断谁的构建先触发,出现后续的状态/日志错位。

INVESTIGATION I

虽然 jenkins 在触发 job 构建的请求中仅返回了 201(No content),但 jenkins 提供了内置队列查询接口。另一方面阅读网页和查看 jenkins sdk 的 python 版本 python-jenkins 实现后,得知 jenkins 的 job 构建接口有以下实现细节,其中第 1、3 种情况下,返回的响应会携带 Location 字段,携带了队列编号。

  1. 无参:POST /job/:job-name/build,无 Content-Type 要求
$ curl -i -X POST http://localhost:8080/job/demo/build  -H 'Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw=='
HTTP/1.1 201 Created
Date: Wed, 14 Oct 2020 10:14:36 GMT
X-Content-Type-Options: nosniff
Location: http://localhost:8080/queue/item/110/
Content-Length: 0
Server: Jetty(9.4.30.v20200611)
  1. 有参:POST /job/:job-name/build,要求表单格式(application/x-www-form-urlencoded),请求消息体有特殊格式要求
  • 以 name+value 键值对集合作为请求参数,再进行序列化,形如 {"parameter":[{"name":"branch","value":"test"}]}
  • 将请求参数转义,以表单格式(application/x-www-form-urlencoded)发送,键为固定值 json

以 curl 形式调用的命令为

$ curl -i http://localhost:8080/job/rdc-pipline/build -H 'Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw==' -d "json=%7B%22parameter%22%3A%5B%7B%22name%22%3A%22branch%22%2C%22value%22%3A%22test%22%7D%5D%7D"
HTTP/1.1 302 Found
Date: Wed, 14 Oct 2020 09:23:36 GMT
X-Content-Type-Options: nosniff
Location: http://localhost:8080/job/rdc-pipline/
Content-Length: 0
Server: Jetty(9.4.30.v20200611)

贴出 fiddler 捕获结果

POST http://localhost:8080/job/rdc-pipline/build HTTP/1.1
Host: localhost:8080
User-Agent: python-requests/2.24.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Jenkins-Crumb: d5a1f46c7e02e9633a1d73741a264fa98bc3729e1e4ebdb4974f2a5b4004afb3
Cookie: JSESSIONID.0e0c708f=node018835f7lya5y821r3looclhze104.node0
Content-Length: 93
Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw==
Content-Type: application/x-www-form-urlencoded json=%7B%22parameter%22%3A%5B%7B%22name%22%3A%22branch%22%2C%22value%22%3A%22test%22%7D%5D%7D HTTP/1.1 201 Created
Date: Wed, 14 Oct 2020 10:17:18 GMT
X-Content-Type-Options: nosniff
Location: http://localhost:8080/job/rdc-pipline/
Content-Length: 0
Server: Jetty(9.4.30.v20200611)
  • 有参:POST /job/:job-name/buildWithParameters?...,无 Content-Type 要求,参数拼接到 QueryString 中,该形式对参数格式没有示例。

以 curl 形式调用的命令为

$ curl -i -X POST http://localhost:8080/job/rdc-pipline/buildWithParameters?branch=A  -H 'Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw=='
HTTP/1.1 201 Created
Date: Wed, 14 Oct 2020 09:26:09 GMT
X-Content-Type-Options: nosniff
Location: http://localhost:8080/queue/item/98/
Content-Length: 0
Server: Jetty(9.4.30.v20200611)

贴出 fiddler 捕获结果

POST http://localhost:8080/job/rdc-pipline/buildWithParameters?branch=A HTTP/1.1
Authorization: basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw==
User-Agent: PostmanRuntime/7.26.5
Accept: */*
Postman-Token: 41dda8ba-a376-44f5-b3e5-f59e8fa2fca7
Host: localhost:8080
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: JSESSIONID.0e0c708f=node06gr4lsp9c2wg2dg1vlgm9er496.node0
Content-Length: 0 HTTP/1.1 201 Created
Date: Wed, 14 Oct 2020 10:23:11 GMT
X-Content-Type-Options: nosniff
Location: http://localhost:8080/queue/item/118/
Content-Length: 0
Server: Jetty(9.4.30.v20200611)

对比和测试两种有参请求,有如下区别

URL 状态码 其他
/job/:job-name/buildWithParameters 201 Location 携带队列编号,形如 http://localhost:8080/queue/item/118/
/job/:job-name/build 201 或 302,规律未知 Location 仅为 Job 地址,形如 http://localhost:8080/job/rdc-pipline/

同时观察第一种方式 jenkins 返回的响应,可以看到出现了相同的队列编号,这表示同时提交的构建任务不会重复入队。编排一系列请求如下,以对入队不同 job 及参数变化的情况进行观察。

curl -i -X POST http://localhost:8080/job/demo/build -H 'Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw=='
curl -i -X POST http://localhost:8080/job/rdc-pipline/buildWithParameters?branch=A -H 'Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw=='
curl -i -X POST http://localhost:8080/job/demo/build -H 'Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw=='
curl -i -X POST http://localhost:8080/job/rdc-pipline/buildWithParameters?branch=A -H 'Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw=='
curl -i -X POST http://localhost:8080/job/rdc-pipline/buildWithParameters?branch=B -H 'Authorization: Basic YWRtaW46MTFiNDY0NGYwMDRkYWM3MWU0YjI4ODMyNmQwZWUwNmFhMw=='

5条 curl 命令执行以下操作

  1. 触发名为 demo 的 job 构建
  2. 触发名为 rdc-pipline 的 job 构建,使用参数 branch=A
  3. 触发名为 demo 的 job 构建
  4. 触发名为 rdc-pipline 的 job 构建,使用参数 branch=A
  5. 触发名为 rdc-pipline 的 job 构建,变更参数,使用参数 branch=B

可以看到分别返回了以下 Location

可以看到最后的参数变化生成了两条队列记录,目前为止我们有以下结论:

1. 连续触发的相同 job 构建不会重复入队

job 构建的成本因内内容不同差异很大,但触发 job 构建的成本很小,我们可以轻易地提交大量构建请求,基于以下认知可以理解 jenkins 不进行重复构建的意义:

  1. 虽然代码可能触发前后的短时间内变化,但这是小概率事件;
  2. 常规情况下我们并不需要获取多份各自独立的编译结果,将请求入队以节流方式构建是可行的;

所以 jenkins 只会为短时间内重复触发的 job 构建一次。

2. 连续触发的不同 job 构建会各自入队

虽然 jenkins 不会重复构建相同 job,但在多个 job 同时触发构建的时候,执行构建仍是必须的,入队只是前置。

为什么 jenkins 不在队列为空、资源可用的情况下避免入队环节直接构建?这里没有深入调查。

3. 参数变动的相同 job 构建将分别入队

虽然有避免重复构建相同 job 的必要,但是当参数变化时,构建仍是必须的,否则就丢失了请求,这是不能容忍的。

目前所用 sdk 只实现了基于 /job/:job-name/build 的构建接口,因入队后队列编号的意义重大,后续的讨论基于实现和使用 /job/:job-name/buildWithParameters 之上。

4. 允许同时触发构建将有数据错乱的可能

虽然参数相同的构建会被 jenkins 以排重形式处理,但参数不同时,没有区分策略可言

  1. user1 和 user2 准备发起相同名称但参数不同的 job 构建;
  2. 无论当前的构建队列是否为空,user1 和 user2 触发的构建都会入队
  3. 检查 job 信息,无法知晓 lastBuild 或 nextBuildNumber 的归属
sequenceDiagram
participant user1
participant user2
participant jenkins
user1-->>jenkins: {...}
user2-->>jenkins: {...}
jenkins->>jenkins: enqueue and dequeue
user1->>jenkins: which job?
user2->>jenkins: which job?

5. 分布式锁强制使得入队或构建触发串行化不可行

  1. 强制使构建过程串行化,在计算资源及时间成本上不可接受;
  2. 强制使入队串行化,意味着 jenkins 的排重机制不生效,将出现构建过程串行化;

这类做法违背了 jenkins 的设计意图,加剧了服务器负载,使得响应时间延长,有导致服务器不可用的风险。

INVESTIGATION II

看起来无法把希望寄托于目前 sdk 提供的 job 信息中 lastBuild 信息上,应在队列上继续调查。阅读到 Check Jenkins job status after triggering a build remotely 后,找到可行方法,其步骤如下:

  1. 提交 job 构建请求到地址 /job/:job-name/buildWithParameters ,并解析响应中 Location 字段得到队列编号
  2. 从 /queue/item/:queue-id/api/json 检查队列信息,此时 executable 可能为空
  3. 间断发起轮询,直到返回的 job 携带非空的 executable
  4. 使用 job.executable.number 作为构建编号,从 /job/:job-name/:job-id/api/json 获取到 job 状态等。
sequenceDiagram
participant user1
participant user2
participant jenkins
user1->>+jenkins: {...}
jenkins->>-user1: /queue/item/1
user2->>+jenkins: {...}
jenkins->>-user2: /queue/item/2
jenkins->>jenkins: enqueue and dequeue
user1->>+jenkins: which job for /queue/item/1?
jenkins->>-user1: /job/101
user2->>+jenkins: which job for /queue/item/2?
jenkins->>-user2: /job/102

LINQPad 的执行结果如下:

拿到的 job 即使已经出队,也可能没有第1时间被运行,至此状态字段 Result 可能为空,仍然需要刷新其任务状态的后续工作。

FUTHER MORE

虽然可以使用轮询获取到构建编号,但因不是原生手段可能有少许延迟。jenkins 提供了名为 JENKINS-CLI 的交互工具,见管理页 "Tools and Actions" 下的 Jenkins CLI 项,我的本地地址是 http://localhost:8080/cli/

$ java -jar jenkins-cli.jar -s http://localhost:8080/ -webSocket -auth admin:admin build --help
java -jar jenkins-cli.jar build JOB [-c] [-f] [-p] [-r N] [-s] [-v] [-w]
-w : Wait until the start of the command (default: false)

其中 job 构建命令支持 -w 参数等待构建触发

If you use the -w option, the command will not return until the build starts and then it will print "Started build #N"

$ java -jar jenkins-cli.jar -s http://localhost:8080/ -webSocket -auth admin:admin build demo

$ java -jar jenkins-cli.jar -s http://localhost:8080/ -webSocket -auth admin:admin build demo -w
Started demo #53

可以看到返回了 job 编号,然而以 http 形式调用并使用抓包工具检查。

发现请求并未使用其约定的地址 /job/:job-name/build 或 /job/:job-name/buildWithParameters,而是向地址 /cli?remoting=false 发起了请求,正文应是预先约定的特定格式

c
00000007000005build
b
00000006000004demo
9
00000004000002-w
a
00000005020003GBK
c
00000007010005en_US
5
0000000003

在远期开发中,该 sdk 值得深入挖掘。

SUMMARY

这里梳理了本人调查和使用 jenkins rest api 构建 job 并获取状态的过程,并试用了 jenkins cli 发现其并未调用常规 endpoints。到目前为止,jenkins 的 job 状态和日志查询已没有巨大的障碍,但许多实现路径有着各种差别,另行讨论。

leoninew 原创,转载请注明来自博客园

jenkins 构建 job 并获取其状态的实现的更多相关文章

  1. jenkins构建结果企业微信提醒

    每当jenkin在构建之后我们想把构建结果SUCCESS/FAILURE或者其他信息通知给其他人,也许有人会说,不是有邮件提醒吗?但是我这里的环境邮件提醒的话所被通知者并不会第一时间去阅读,所以我们用 ...

  2. Jenkins获取编译状态

    背景:在通过python的API调用Jenkins,启动Jenkins的job任务时,是需要知道Jenkins的编译状态,获取编译状态为 status=server.get_build_info(jo ...

  3. python回调函数应用-获取jenkins构建结果

    需求背景: 现在用jenkins构建自动化测试(2个job),公司现将自动化纳入到发布系统 要求每次构建成功之后,把测试结果发送给发布系统.这就需要先获取jenkins构建的结果,如果构建结束,才能发 ...

  4. jira 改变issue状态触发jenkins构建/发布

    目录 jira中issue状态的改变触发Jenkins构建 jira中定制新的workflow,作为jenkins发布使用流程 大家可以参考我的这个workflow 设置workflow 使用Tran ...

  5. jenkins+sonar发送结果邮件的状态问题修复

    在我的这篇博文中:使用jenkins+sonar进行代码扫描,并发送自定义邮件 邮件的配置为默认的$PROJECT_DEFAULT_SUBJECT 所以发送的邮件标题中的状态是jenkins构建的状态 ...

  6. 通过jenkins-Python在后台操作Jenkins构建job

    最近要开发1个接口,接收到1个指令后自动触发自动化测试,虽然也可以通过shell命令做这一步,但因为目前所有构建自动化的的动作都通过jenkins完成,所以想要尝试能不能用python去控制jenki ...

  7. Jenkins 实现Gitlab事件自动触发Jenkins构建及钉钉消息推送

    实现Gitlab事件自动触发Jenkins构建及钉钉消息推送 实践环境 GitLab Community Edition 12.6.4 Jenkins 2.284 Post build task 1. ...

  8. Jenkins构建时报错:No Space left on device

    Jenkins在自动化构建服务的同时也在消耗服务器的磁盘空间,如果构建的项目个数很多,而Jenkins 服务器磁盘空间又不是非常大的话,每隔一段时间磁盘空间就会爆满导致,就会出现磁盘空间不足无法构建的 ...

  9. Jenkins构建Android项目持续集成之findbugs的使用

    Findbugs简介 关于findbugs的介绍,可以自行百度下,这里贴下百度百科的介绍.findbugs是一个静态分析工具,它检查类或者 JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题 ...

随机推荐

  1. SpringMVC-Controller&RestFul

    Controller与RestFul 目录 Controller与RestFul 1. Controller 1. 控制器Controller 2. 利用接口定义控制器 1. 实现Controller ...

  2. webstorm单标签设置成双标签展开解决iview中col展开问题

    大家好!我是木瓜太香,今天给大家带来一个 webstorm 小技巧 场景:有使用过 vue 框架并且使用 iview 做 ui webstorm 做 ide 的同学,可能会遇到一个比较奇怪的问题,iv ...

  3. 安卓自动化测试工具Monkey简单使用

    一.首先安装adb 地址:http://www.downza.cn/soft/219906.html安装到D盘下,安装的过程中自己注意下不要安装上全家桶.找到这个压缩包:解压到当前文件夹: 二.将ad ...

  4. Django进入监听端口就自动打开指定页面,无需导航栏手动添加(Django六)

    在我们进入监听端口时画面如下:而因为在urls.py中写过如下语句 我们在监听端口后加上/login就会跳转到login.html页面,如下图 那么如何一打开监听端口就可以走动跳转到login.htm ...

  5. Linux:less and Aix:more

    在运维工作中,经常要查询应用日志,有Linux和Aix系统,个人感觉,Linux查询日志用less命令比较方便,Aix查询日志用more命令比较方便,在此总结一下两个命令的使用方法 AIX more命 ...

  6. linux 信号机制

    文章目录 1. 实时信号非实时信号 2. 信号状态: 3. 信号生命周期: 4. 信号的执行和注销 信号掩码和信号处理函数的继承 信号处理函数的继承 信号掩码的继承 sigwait 与多线程 sigw ...

  7. Springboot定时任务@Scheduled注解形式,参数详解

    参数详解 1.占位符 1 秒 是 0-59 , - * / 2 分 是 0-59 , - * / 3 时 是 0-23 , - * / 4 日 是 1-31 , - * ? / L W 5 月 是 1 ...

  8. FFmpeg开发笔记(五):ffmpeg解码的基本流程详解(ffmpeg3新解码api)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  9. yum管理——ningx部署私有repo源(4)

    一.前言: 为了加快安装效率,或者日后服务器处于内网环境,本次特写一片搭建的是一个属于个人私有repo源仓库,思路如下: 1.首先到mirrors.ustc.edu.cn下载用到的源的仓库 2.然后安 ...

  10. 11.深入k8s:kubelet工作原理及源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 源码版本是1.19 kubelet信息量是很大的,通过我这一篇文章肯定是讲不全的,大家可 ...