發表文章

目前顯示的是有「Jenkins」標籤的文章

將 Jenkins Job 的歷史結果整理出視覺化的 Daily Report mail (三)

圖片
為了寄出統整 Jenkins 上游 Job 的 daily report mail, 第一篇 說明了 Jenkins Daily Report Job 的設定, 第二篇 說明了如何用 Python script 統計 Job result 以及產生視覺化的圖表。本篇說明如何用 jelly script 產生 html 格式的 mail,email 裡面除了顯示第二篇文章中產生的視覺畫圖表之外,也會包含前一天每個小時的 Job 結果,如下圖: Email-exit Plugin 會預設 jelly script 的位置在 $JENKINS_HOME/email-templates 目錄底下,但是安裝完畢時並沒有這個目錄,也就是說要自己在 Jenkins 安裝目錄下新建一個 email-templates 的 folder,然後把 jelly scripts 放在這個 folder 之下,以我們的例子來說就是 load-monitor-dailyreport.jelly 詳細說明可以參考 Email-ext plugin 的網頁。 這裡可以找到一些 email-ext 提供的 jelly script 的範例 ,建議要快速上手,可以 copy 一個來改,ex: html.jelly 。這個 html.jelly script 預設會包含 change set, maven, junit 等相關的內容,但是因為我們的上游 Job 都是自訂的,因此需要自己修改 script 內容,後文只說明與 daily report 有關的部分。 Jelly Script 的架構如下,後續說明主要都是 BODY 裡面的內容。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?jelly escape-by-default='true'?> <!DOCTYPE html [ <!ENTITY nbsp "&#38;#38;nbsp&#59;"> ]> <j:jelly xmlns:j= "jelly:core" xmlns:st= "jelly:stapler" x...

將 Jenkins Job 的歷史結果整理出視覺化的 Daily Report mail (一)

圖片
當初在處理這個問題的時候,大部分都是參考國外的網站,使用 Jenkins 內建功能送 email 的文章很多,但是 像是要自訂 mail 內容,蒐集統整不同 Job 的狀態以及包含視覺化報表 的部分倒是很少看到,所以就做個紀錄與分享,一方面也是拋轉引玉,歡迎交流。 這裡的做法受限於 "Free-Style" 形式的 Jenkins Job,所以過程會比較繁瑣;但是換句話說,如果只想用 "Free-Style" 形式來創建 Jenkins Job,可以從這篇文章得到一些靈感。 這個主題為了盡量寫得詳細點,會分成三個部分: Jenkins Daily Report Job 的相關設定。( 本篇 ) 使用 Python/Pandas/matplotlib 做資料分析及處理 。 說明使用 jelly script 來客製化郵件內容 。 最後目標是每天自動送出視覺化統計 Jenkins Job 歷史狀態的 email,如下圖。 首先,本篇會說明以下主題: 設定 Jenkins 郵件相關 (gmail) SMTP 設定 需要安裝的 Jenkins Plugin 新增/設定一個 Jenkins "Daily Report" Job 這個系列 沒有 說明的部分如下: (我想這些在網路上應該都很容易可以找到相關資料) 安裝 Jenkins 設定 slave node,並在 slave node 上執行 Jenkins Job 設定 Jenkins 郵件相關 SMTP 設定 讓 Jenkins 寄出郵件需要設定 SMTP server,這邊以使用 gmail SMTP為範例。 Jenkins mail 設定在 "管理 Jenkins" => "設定系統"   http://localhost:8080/configure 詳細設定如下圖,設定完成可以勾選"寄測試信",先試試看設定是否正確。 如果看到 "電子郵件成功寄出"就代表設定正確了。 已知目前 google 認為 Jenkins Plugin登入SMTP的方式安全性較低,使用者必須要自行 "啟用安全性較低的應...

從 Python script 停止 Jenkins Job

之前分享過如何 使用 Jenkins API 取得 Job result ,這次來記錄一下如何 停止 執行中的 Job。 這次直接使用 requests ,如果沒有安裝可以先執行 pip install requests 安裝。 這個是比較簡潔的寫法,其實也可以直接用  python-jenkins 這類的 python wrapper 來控制 Jenkins server,通常如果有很多 jenkins server 控制的行為,就建議直接使用 python-jenkins 了。 以下是使用 requests 的範例: 1 2 3 4 5 6 7 8 9 10 11 import requests jenkinsMaster = "http://jenkinshost:8080" userID = "myaccount" APIToken = "1234567890" def stopJenkinsJob(job_name): url = jenkinsMaster + "/job/" + jobName + "/lastBuild/stop" auth = (userID, APIToken) r = requests . post(url, auth = auth) return r . status_code

在 Jenkins 環境下使用 adb

因為 Jenkins 在結束工作的時候,會將所有 fork 出來的子進程全部砍掉,來確保不會有 leakage 的情況發生,讓 Jenkins 可以長時間運行。通常這個行為是沒有問題的,有時候也可以仰賴這個行為來讓 Job Script 寫起來簡潔一些,省一些功夫。 但是如果某個 Jenkins Job 會啟動 server or daemon 性質的 process,而我們希望當 Job 結束後, daemon process 可以保留,這個時候需要在一開始啟動這個 daemon 的時候,加上 "dontKillMe" 的環境變數,來告知 Jenkins 不要回收這個 process。 詳細說明可以參考 這裡 adb 就是一個很好的例子。 adb daemon 可以同時間處理很多 connection,通常在大規模的自動化測試中,一台 PC 會接著很多 device,如果 adb daemon 被砍掉,其他 device connection 就會中斷,這並不是我們希望看到的。 以下是針對指定 adb server 不要被 Jenkins 回收的範例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 jenkins_env = os . environ . copy() jenkins_env[ "BUILD_ID" ] = "dontKillMe" start_time = time . clock() event = Event() is_device_timeout = False cmd = "adb -s %s wait-for-device" % (deviceNo) try : adb_pid = subprocess . Popen(cmd, env = jenkins_env) while adb_pid . poll() is None : event . wait(timeout = 1.0 ) if (time . clock() -...

如何用 Jenkins API 取得 Job Build Result

當需要建構網頁取得 Jenkins Job 的結果,或是需要整理 Job 狀態並發送通知或郵件的時候,通常會需要透過 API 取得 Job Result Jenkins API 的 URL :  http://jenkinshost:8080/job/[jobName]/[buildNum]/api/json/ 但是如果打開 Jenkins Security 設定,使用 API 必須要登入 Jenkins 並有合法的權限 這個時候可以填上 APIToken,便可以登入並使用 API user 的 APIToken 可以從這個網址找到: http://jenkinshost:8080/user/[myaccount]/configure 以下是完整的實作方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import json , urllib , urllib2 from threading import Event jenkinsMaster = "http://jenkinshost:8080" userID = "myaccount" APIToken = "1234567890" class HTTPBasic403AuthHandler (urllib2 . HTTPBasicAuthHandler): # retry with basic auth when facing a 403 forbidden def http_error_403( self , req, fp, code, msg, headers): host = req . get_host() realm = None return self . retry_http_basic_auth(host, req, realm) def get...