美文网首页
Jenkins API+Pipeline深度实践之input的自

Jenkins API+Pipeline深度实践之input的自

作者: 小明同学9527 | 来源:发表于2021-03-24 09:21 被阅读0次

    目录

    • 1、pipeline input 概述

    • 2、背景概述

    • 3、推理及测试

    • 3.1 通过 Crumb 安全操作 Jenkins

    • 3.2 通过 Token 安全操作 Jenkins

    • 3.3 API 操作

    • 3.4 input 的改造

    • 4、自动化 input 的最终实现

    • 5、小结

    图片

    本文分享的小Tips是在我前面的文章DevOps建设之基于钉钉OA审批流的自动化上线[1]中提到的,当通过API自动触发Jenkins Pipeline流水线执行时,如果原来的流水线中定义了在构建正式开始后还需要接收用户input的步骤,想要自动绕过或自动执行input的方法

    1、pipeline input 概述

    首先回过头再来看看pipeline input的语法及功能,参考我之前总结的JenkinsPipeline语法概要[2]

    input指令stage允许使用input step提示输入。在stage将暂停任何后options已被应用,并在进入前agent块为stage或评估when的条件stage。如果input批准,stage则将继续。作为input提交的一部分提供的任何参数将在其余的环境中可用stage

    可选项

    • message 必需的,这将在用户提交时显示给用户input
    • id 可选标识符input,默认为stage名称
    • okinput表单上“确定”按钮的可选文本
    • submitter 可选的逗号分隔列表,这些列表允许用户提交此用户或外部组名input。默认为允许任何用户。
    • submitterParameter 环境变量的可选名称,用该submitter名称设置(如果存在)
    • parameters 提示提交者提供的可选参数列表。请参阅Pipeline parameters[3]以获取更多信息

    2、背景概述

    基于上面的语法描述,我这里线上发布流水线中input的功能仅仅是需要用户进行确认,所以没有传递任何参数,通过这种简单的input控制及timeout超时机制,实现了用户选择参数并点击开始构建后需要在 60 秒内二次确认的功能,流水线的部分内容如下

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">stage('Deploy to prod'){ when { environment name: 'PerformType', value: 'Deploy' } steps{ timeout(time:60, unit:'SECONDS') { input "确认要部署到线上环境吗?" script{ try { ... } catch (exc) { ... throw(exc) } } } } } </pre>

    到这里问题就产生了,input的过程是在流水线运行过程中动态出现的,如果是想要在钉钉OA审批通过后自动通过调用jenkins api并传入参数让整个流水线自动执行,并且自动进行input的确认操作或者绕过input,应该怎么做呢?

    3、推理及测试

    刚开始没有任何思路,唯一想到的办法就是把input的过程从pipeline中去除掉,这样就没有任何烦恼了

    但是为了保留原有pipeline设计的完整性,显然这种做法不够友好,只是避开了这个难点,是不可取的

    通过查找发现这方面的资料很少,最终有用的资料如下

    • input 官方说明[4]

    input语法中可选字段包含id,每个input步骤都有一个唯一的ID。在生成的URL中可以使用它来继续或中止

    例如,可以使用特定的ID来机械地响应来自某些外部过程/工具的输入

    这篇文章中讲到了如何通过Jenkins REST API恢复暂停的管道?作为参考起到了一定帮助

    为了完成整个自动化input的过程,具体的演进流程如下

    3.1 通过 Crumb 安全操作 Jenkins

    Crumb指的是JenkinsCSRF tokenJenkins服务器为了阻止不安全的跨域请求,默认开启了CSRF保护,参考Jenkins 远程 API 访问[5]

    Jenkins 的 CSRF 配置[6]可以在「系统管理」——> 「全局安全配置」——> 「CSRF Protection」相关配置中关闭此保护,跨站请求伪造这是一个很常见的安全问题,为了安全起见建议不关闭。如果关闭,这里的内容可以略过。

    Jenkins开启CSRF保护后,可以通过固定的接口获得一个安全的Crumb以便于通过API操作Jenkins,以curl请求为例,请求的可选方式一般是两种,如下

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`方法一:
    curl -u <username>:<password> 'https://jenkins.ssgeek.com/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)'
    Jenkins-Crumb:dc78dfb9615fb56bbf2001fb99c64dbd3331c5e14c8d4edd54722e7ca790529e%

    方法二:
    curl -u <username>:<password> 'https://jenkins.ssgeek.com/crumbIssuer/api/json'
    {"_class":"hudson.security.csrf.DefaultCrumbIssuer","crumb":"52d605f43328f15303c2e68eb492b9656e229ce124c2f5f2e39b6f552f54e4ac","crumbRequestField":"Jenkins-Crumb"}%` </pre>

    以上两种方式都可以获取一个Crumb,然后就能带着它去请求JenkinsAPI

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">curl -u <username>:<password> -X POST -H "Jenkins-Crumb:b220147dbdf3cfebbeba4c29048c2e33" -d <data> 'https://jenkins.ssgeek.com/<jenkins api url>' </pre>

    3.2 通过 Token 安全操作 Jenkins

    在官方文档的描述中有这样一句话:API tokens are preferred instead of crumbs for CSRF protection.

    意为在开启了CSRF的情况下,首选的是通过API token操作而不是crumb,这里的API token指的就是Jenkins中用户的API token

    可以通过「用户」——> 「设置」——> 「API Token」——> 「添加新 Token」来获得一个api token,有了这个Token之后,以curl请求为例操作JenkinsAPI方式如下

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">curl -u user_id:user_api_token -X POST -d <data> 'https://jenkins.ssgeek.com/<jenkins api url>' </pre>

    3.3 API 操作

    参考上面的文档资料使用 Jenkins REST API 恢复暂停的管道[7]

    对于input有这样的api接口地址可以使用,用于将输入发送到等待的输入步骤。url格式如下

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">http://<JenkinsURL>/<JobURL>/<Build#>/input/<InputID>/submit </pre>

    需要满足的条件

    • 如果Jenkins启用了CSRF保护,则您需要使用CrumbAPI Token

    • 请求通过POST方式发送

    • 需要提供参数名为proceed的值,并且以OK作为参数值

    • 为了提供数据,需要带有json格式的参数,这些参数就是在input阶段需要接收的参数,格式为

      <pre style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">{ "parameter":[ { "name":"param1", "value":"valueOfParam1" }, { "name":"param2", "value":"valueOfParam2" } ] } </pre>

      如果没有发送有效的json参数,则流水线也将继续进行,只是不会获得任何参数(这也可能导致流水线最终执行失败),如果成功则返回302状态码并重定向到用户界面

    • 必须填写input id,因此要从外部连接到的input步骤配置唯一的id

    • 也可以使用下面的 url,如果流水线成功,则返回状态码为200且响应为空

      <pre style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">http://<Jenkins URL>/job/<YOUR_PROJECT>/<BUILD_NUMBER>/wfapi/inputSubmit </pre>

    其他可用的api接口地址以及作用

    • 用于中止流水线

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">http://<Jenkins URL>/job/<YOUR_PROJECT>/<BUILD_NUMBER>/input/<INPUT_ID>/abort </pre>

    • 不传入任何参数并继续进行流水线

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">http://<Jenkins URL>/job/<YOUR_PROJECT>/<BUILD_NUMBER>/input/<INPUT_ID>/proceedEmpty </pre>

    对于本文中我的需求,只需要在input执行时自动确认且无需传入任何参数即可,因此使用的接口地址为上面的最后一种(其余接口地址未测试)

    3.4 input 的改造

    为了实现在input执行时自动确认,需要对流水线的input部分进行改造,加入一个固定的id即可

    由于定义的id都是固定的,因此可以利用脚本对所有的流水线涉及到这种input的部分批量更新,这里就不列出具体方法了

    最终我的流水线调整如下

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">stage('Deploy to prod'){ when { beforeInput true environment name: 'PerformType', value: 'Deploy' } options { timeout(time:60, unit:'SECONDS') } input { message "确认要部署到线上环境吗?" id "CustomId" } steps{ script{ try { ... } catch (exc) { ... throw(exc) } } } } </pre>

    4、自动化 input 的最终实现

    经过上面的推理和测试,解决了通过API自动执行input进行流水线确认的问题

    这里还剩下最后一个问题,通过测试发现,想要自动执行input过程,其接口对应的url地址并不是一直存在的,而是在流水线执行开始后到达input的时候才会出现,出现时通过浏览器访问查看如下

    图片

    而其余时间发送请求都会返回404状态码,此时是无法接收post请求的,因此想要自动化执行input并不只是简单的向接口发送POST请求了

    我这里的解决思路

    在发送流水线开始执行的请求后,立即通过代码循环请求并判断接口地址返回的状态码是否是200

    如果不是,那么表示流水线还没执行到这里;如果是,就可以完美的向这个地址发送自动执行的请求了

    python语言调用Jenkins api为例,用到了python-jenkins这个包,在触发构建时使用build_job这个方法,这个方法返回值刚好是job任务的build number,这恰好是接口地址组成中需要的一部分

    好了,上最终的部分代码

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(89, 89, 89); font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.75px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">def auto_job_input(self, server_url, job_name, build_number): """ 根据input阶段生成的url http状态码,判断当前job流水线运行的stage否进行到了input步骤 自动执行input or 继续判断 :param job_name: :param build_number: :return: """ # https://jenkins.ssgeek.com/job/input-demo/64/input get_url = server_url + "/job/" + job_name + "/" + str(build_number) + "/input" # https://jenkins.ssgeek.com/job/input-demo/64/input/CustomId/proceedEmpty post_url = get_url + "/CustomId/proceedEmpty" s = requests.Session() res_code_get = s.get(url=get_url, auth=('user_id', 'user_token')).status_code while res_code_get != 200: res_code_get = s.get(url=get_url, auth=('user_id', 'user_token')).status_code res_code_post = s.post(url=post_url, auth=('user_id', 'user_token'), data=None).status_code return res_code_post </pre>

    关键部分代码量很少,利用request并且携带认证参数进行请求,如果有大佬有更好的方案欢迎与我交流

    5、小结

    到这里,通过一步步推理演进,在流水线中input的自动化执行就完美实现了,最终既实现了调用api触发自动构建并执行input进行自动确认,同时也保留了原流水线的input设计,对原有流水线只需要做很小的调整。

    参考资料

    [1]

    DevOps建设之基于钉钉OA审批流的自动化上线: https://www.ssgeek.com/post/devops-jian-she-zhi-ji-yu-ding-ding-oa-shen-pi-liu-de-zi-dong-hua-shang-xian/ [2]

    JenkinsPipeline语法概要: https://www.ssgeek.com/post/jenkinspipeline-yu-fa-gai-yao/#310-input [3]

    Pipeline parameters: https://www.jenkins.io/doc/book/pipeline/syntax/#parameters [4]

    input官方说明: https://www.jenkins.io/doc/pipeline/steps/pipeline-input-step/ [5]

    Jenkins远程API访问: https://www.jenkins.io/doc/book/using/remote-access-api/ [6]

    Jenkins的CSRF配置: https://www.jenkins.io/doc/book/managing/security/#cross-site-request-forgery [7]

    使用Jenkins REST API恢复暂停的管道: https://www.thinbug.com/q/48799442

    本文摘选 地址————————————点击传送

    相关文章

      网友评论

          本文标题:Jenkins API+Pipeline深度实践之input的自

          本文链接:https://www.haomeiwen.com/subject/ipechltx.html