目錄
1律适、pipeline input 概述
2腔长、背景概述
3袭祟、推理及測試
3.1 通過 Crumb 安全操作 Jenkins
3.2 通過 Token 安全操作 Jenkins
3.3 API 操作
3.4 input 的改造
4、自動化 input 的最終實現(xiàn)
5捞附、小結(jié)
本文分享的小
Tips
是在我前面的文章DevOps建設(shè)之基于釘釘OA審批流的自動化上線[1]中提到的巾乳,當通過API
自動觸發(fā)Jenkins Pipeline
流水線執(zhí)行時,如果原來的流水線中定義了在構(gòu)建正式開始后還需要接收用戶input
的步驟故俐,想要自動繞過或自動執(zhí)行input
的方法
1想鹰、pipeline input 概述
首先回過頭再來看看pipeline input
的語法及功能,參考我之前總結(jié)的JenkinsPipeline語法概要[2]
input
指令stage
允許使用input step
提示輸入药版。在stage
將暫停任何后options
已被應用辑舷,并在進入前agent
塊為stage
或評估when
的條件stage
。如果input
批準槽片,stage
則將繼續(xù)何缓。作為input
提交的一部分提供的任何參數(shù)將在其余的環(huán)境中可用stage
。
可選項
- message 必需的还栓,這將在用戶提交時顯示給用戶
input
- id 可選標識符
input
碌廓,默認為stage
名稱 - ok
input
表單上“確定”按鈕的可選文本 - submitter 可選的逗號分隔列表,這些列表允許用戶提交此用戶或外部組名
input
剩盒。默認為允許任何用戶谷婆。 - submitterParameter 環(huán)境變量的可選名稱,用該
submitter
名稱設(shè)置(如果存在) - parameters 提示提交者提供的可選參數(shù)列表辽聊。請參閱Pipeline parameters[3]以獲取更多信息
2纪挎、背景概述
基于上面的語法描述,我這里線上發(fā)布流水線中input
的功能僅僅是需要用戶進行確認跟匆,所以沒有傳遞任何參數(shù)异袄,通過這種簡單的input
控制及timeout
超時機制,實現(xiàn)了用戶選擇參數(shù)并點擊開始構(gòu)建后需要在 60 秒內(nèi)二次確認的功能玛臂,流水線的部分內(nèi)容如下
<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 "確認要部署到線上環(huán)境嗎烤蜕?" script{ try { ... } catch (exc) { ... throw(exc) } } } } }
</pre>
到這里問題就產(chǎn)生了封孙,input
的過程是在流水線運行過程中動態(tài)出現(xiàn)的,如果是想要在釘釘OA
審批通過后自動通過調(diào)用jenkins api
并傳入?yún)?shù)讓整個流水線自動執(zhí)行讽营,并且自動進行input
的確認操作或者繞過input
虎忌,應該怎么做呢?
3斑匪、推理及測試
剛開始沒有任何思路呐籽,唯一想到的辦法就是把input
的過程從pipeline
中去除掉,這樣就沒有任何煩惱了
但是為了保留原有pipeline
設(shè)計的完整性蚀瘸,顯然這種做法不夠友好狡蝶,只是避開了這個難點,是不可取的
通過查找發(fā)現(xiàn)這方面的資料很少贮勃,最終有用的資料如下
- input 官方說明[4]
input
語法中可選字段包含id
贪惹,每個input
步驟都有一個唯一的ID
。在生成的URL
中可以使用它來繼續(xù)或中止
例如寂嘉,可以使用特定的ID
來機械地響應來自某些外部過程/工具的輸入
這篇文章中講到了如何通過Jenkins REST API
恢復暫停的管道奏瞬?作為參考起到了一定幫助
為了完成整個自動化input
的過程,具體的演進流程如下
3.1 通過 Crumb 安全操作 Jenkins
Crumb
指的是Jenkins
的CSRF token
泉孩,Jenkins
服務(wù)器為了阻止不安全的跨域請求硼端,默認開啟了CSRF
保護,參考Jenkins 遠程 API 訪問[5]
Jenkins 的 CSRF 配置[6]可以在「系統(tǒng)管理」——> 「全局安全配置」——> 「CSRF Protection」相關(guān)配置中關(guān)閉此保護寓搬,跨站請求偽造這是一個很常見的安全問題珍昨,為了安全起見建議不關(guān)閉。如果關(guān)閉句喷,這里的內(nèi)容可以略過镣典。
當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
锡溯,然后就能帶著它去請求Jenkins
的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;">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
可以通過「用戶」——> 「設(shè)置」——> 「API Token」——> 「添加新 Token」來獲得一個api token
祭饭,有了這個Token
之后涌乳,以curl
請求為例操作Jenkins
的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;">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
接口地址可以使用,用于將輸入發(fā)送到等待的輸入步驟甜癞。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
保護,則您需要使用Crumb
或API Token
請求通過
POST
方式發(fā)送需要提供參數(shù)名為
proceed
的值宛乃,并且以OK
作為參數(shù)值-
為了提供數(shù)據(jù)悠咱,需要帶有
json
格式的參數(shù)蒸辆,這些參數(shù)就是在input
階段需要接收的參數(shù),格式為<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>如果沒有發(fā)送有效的
json
參數(shù)析既,則流水線也將繼續(xù)進行躬贡,只是不會獲得任何參數(shù)(這也可能導致流水線最終執(zhí)行失敗)眼坏,如果成功則返回302
狀態(tài)碼并重定向到用戶界面 必須填寫
input id
拂玻,因此要從外部連接到的input
步驟配置唯一的id
-
也可以使用下面的 url,如果流水線成功宰译,則返回狀態(tài)碼為
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>
- 不傳入任何參數(shù)并繼續(xù)進行流水線
<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
執(zhí)行時自動確認且無需傳入任何參數(shù)即可,因此使用的接口地址為上面的最后一種(其余接口地址未測試)
3.4 input 的改造
為了實現(xiàn)在input
執(zhí)行時自動確認沿侈,需要對流水線的input
部分進行改造闯第,加入一個固定的id
即可
由于定義的id
都是固定的,因此可以利用腳本對所有的流水線涉及到這種input
的部分批量更新缀拭,這里就不列出具體方法了
最終我的流水線調(diào)整如下
<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 "確認要部署到線上環(huán)境嗎咳短?" id "CustomId" } steps{ script{ try { ... } catch (exc) { ... throw(exc) } } } }
</pre>
4、自動化 input 的最終實現(xiàn)
經(jīng)過上面的推理和測試蛛淋,解決了通過API
自動執(zhí)行input
進行流水線確認的問題
這里還剩下最后一個問題咙好,通過測試發(fā)現(xiàn),想要自動執(zhí)行input
過程褐荷,其接口對應的url
地址并不是一直存在的勾效,而是在流水線執(zhí)行開始后到達input
的時候才會出現(xiàn),出現(xiàn)時通過瀏覽器訪問查看如下
而其余時間發(fā)送請求都會返回404
狀態(tài)碼诚卸,此時是無法接收post
請求的葵第,因此想要自動化執(zhí)行input
并不只是簡單的向接口發(fā)送POST
請求了
我這里的解決思路:
在發(fā)送流水線開始執(zhí)行的請求后,立即通過代碼循環(huán)請求并判斷接口地址返回的狀態(tài)碼是否是200
如果不是合溺,那么表示流水線還沒執(zhí)行到這里卒密;如果是,就可以完美的向這個地址發(fā)送自動執(zhí)行的請求了
以python
語言調(diào)用Jenkins api
為例棠赛,用到了python-jenkins
這個包哮奇,在觸發(fā)構(gòu)建時使用build_job
這個方法,這個方法返回值剛好是job
任務(wù)的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): """ 根據(jù)input階段生成的url http狀態(tài)碼,判斷當前job流水線運行的stage否進行到了input步驟 自動執(zhí)行input or 繼續(xù)判斷 :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>
關(guān)鍵部分代碼量很少辩涝,利用request
并且攜帶認證參數(shù)進行請求贸伐,如果有大佬有更好的方案歡迎與我交流
5、小結(jié)
到這里怔揩,通過一步步推理演進捉邢,在流水線中input
的自動化執(zhí)行就完美實現(xiàn)了脯丝,最終既實現(xiàn)了調(diào)用api
觸發(fā)自動構(gòu)建并執(zhí)行input
進行自動確認,同時也保留了原流水線的input
設(shè)計伏伐,對原有流水線只需要做很小的調(diào)整宠进。
參考資料
[1]
DevOps建設(shè)之基于釘釘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
本文摘選 地址————————————點擊傳送