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. 【API进阶之路】用API打造一条自动化内容生产流水线

    摘要:搞定了内容审核之后,我又把抓取工具.内容审核API.文本摘要生成API串联在一起,从抓到审再到编,建立了一条自动化的内容生产流水线,编辑团队只需要做优质内容的推荐就可以了. 上周,运营部将官网上 ...

  2. 操作BOM对象

    操作BOM对象 目录 操作BOM对象 1. 浏览器介绍 2. window 3. Navigator(不建议使用) 4. screan 5. location(重要) 6. document(内容:D ...

  3. Ubuntu下开启/关闭防火墙及端口

    有管理员权限可省略sudo. 1.查看端口开启状态 sudo ufw status 2.开启某个端口(以8866为例) sudo ufw allow 8866 3.开启防火墙 sudo ufw ena ...

  4. 吴恩达《深度学习》-第五门课 序列模型(Sequence Models)-第三周 序列模型和注意力机制(Sequence models & Attention mechanism)-课程笔记

    第三周 序列模型和注意力机制(Sequence models & Attention mechanism) 3.1 序列结构的各种序列(Various sequence to sequence ...

  5. Hadoop入门学习整理(三)

    1.Hive 的安装 下载tar包,并且要注意和Hadoop版本兼容. 下载  ----->  传输 -------> 解压     即可. 然后配置环境变量和相关配置信息.   参考: ...

  6. 万字详解TDengine 2.0整体架构设计思路

    ​导读:涛思数据8月3日将TDengine 的集群功能开源,TDengine具有超强的性能和功能,为什么能做到?它到底有哪些技术创新?今将TDengine的整体设计文档分享出来. 1: 数据模型 物联 ...

  7. apache-apollo启动报错

    启动Apollo后,进入网页版管理中心后报错:500: Server Error https://127.0.0.1:61681/console/index.html 网页抓包 报错:{"c ...

  8. Element-UI:级联选择器:Cannot read property 'level' of null"

    当级联选择时如果其选择内容需要动态变化时,如果没有选择就不会报错的:而当做出选择后又要动态变化级联选择器内容时,就会报错/ 错误:这个错误的原因是当选择后,再更新内容时,选择器仍会关联原来的数据,导致 ...

  9. Redis中的订阅模式

    redis中的客户端可以订阅一个自定义的频道,接受来自该频道的消息 订阅 订阅指定频道-SUBSCRIBE SUBSCRIBE channel [channel2]... SUBSCRIBE 频道名 ...

  10. Codeforces Round #672 (Div. 2) A - C1题解

    [Codeforces Round #672 (Div. 2) A - C1 ] 题目链接# A. Cubes Sorting 思路: " If Wheatley needs more th ...