Promise Context Switch 之 Drone Plugin的愛恨情仇
最近在使用Drone的時候有用到同事寫的JavaScript Plugin
原本是想說利用Drone Secret就可以藏好專案敏感的訊息, 所以又自己改了一版有Secret的Plugin
但是遇到了一個問題, 就是這個套件在Rancher deploy失敗的時候並不會在Pipeline顯示Failed.
Drone pipeline要失敗主要是偵測這個Container是否是exit in 0以外的Code(非正常結束)
如果Container裡面的Process exit in 1, 那這個Container也會exit in 1, Pipeline也就會失敗
原本想的很簡單
只要在返回錯誤訊息的時候加process.exit(1)就好了
這是原本的Code:
1 | const workloadInfo = await axios.get(PLUGIN_PROJECT_API, { |
加了process.exit
1 | const workloadInfo = await axios.get(PLUGIN_PROJECT_API, { |
原本以為這樣就結束,打完收工.
但是歹擠沒有憨人所想的這麼簡單
pipeline還是return success
只是log會顯示process exit code 1
於是我試著拋出error
1 | const workloadInfo = await axios.get(PLUGIN_PROJECT_API, { |
原本以為終於可以結束,打完收工.
但是事實證明憨人就是憨人
於是我想這應該是Thread(執行緒)的問題,於是就去查NodeJs到底是不是單執行緒
被我翻出了這一篇蠻有教育意義的文章
然後我就在想,嗯嗯應該是因為axios在http call的時候啟動了I/O thread的關係吧!
所以到Catch的時候已經是另外一個Thread, main thread已經結束了, 也難怪pipeline沒辦法exit in code 1
那這樣也沒辦法,只能用別的語言來改寫這個Plugin了
(事後證明我真的是個憨人XD)
這是用Golang改寫過後的Plugin,
還因此開啟我的Golang學習之路…
套件本身是可以用的,也會在deploy失敗時正常顯示失敗,因為Proccess 確實 exit in code 1
原本以為事情就這麼結束了
但我還是覺得這個疑問沒有得到回答
所以我就跑去問我的大學大神同學@David Kuo大大
然後也在StackOverflow上找到這篇文章
這才知道,原來在Promise的Catch裡面不管丟出什麼錯誤或exit process code in 1
JavaScript預設你已經知道這邊可能會有錯而且你會好好處理它
照著上面文章的解法改寫了一下:
1 | const workloadInfo = await axios.get(PLUGIN_PROJECT_API, { |
Drone pipeline就正常Failed了!真是可喜可賀!
其實官方網站有寫到這是一個Catch的Trap, 詳見此
但是官方網站沒有寫到為啥這個可以Work, 但是StackOverflow那篇文章的解答有解釋到
在你setTimeout叫起另外一個function的時候,JavaScript就已經進入了一個新的Event Loop了
兩個function的context不同,也就不會預設你會好好處理這個Error
所以當你在function裡面拋出例外時,程式就會異常結束了
然而這邊我又有個疑問
如果是這樣寫呢?
1 | const workloadInfo = await axios.get(PLUGIN_PROJECT_API, { |
因為setTimeout()是非同步事件,為了防止這個事件耗時太久,所以JavaScript會把這些有非同步Callback的函式都丟進去Event Queue裡,等到目前手上的任務都執行完再執行
而上面寫的throwError並不是非同步的函式, JavaScript並不會為了這個函式進入新的Event loop, Error也就會被好好處理了。
打完收工。