Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2914

GAS+slack+GitLab Runners API+bash+LaunchtclでGitLab Runnerの監視プログラムを組む

$
0
0

GitLab Runnerが音もなく死んでいる(offline状態?)になっていることがたびたび起きたので、定期的にRunnerの状態をチェックして死んでたら教えてくれるslack appを作成した。

前提条件

GitLabはアクセスは社内LANからの物しか受け付けないようになっている。
したがってRunnerの状態のチェックだけは必ず社内のPCからしなくてはならない。

使用機材

iMac (OS ver : macOS mojave 10.14.4)

ざっくりとした構成

GAS : 監視対象のRunner一覧をspreadSheetで記録。スクリプトをウェブアプリケーションとして公開しておき、httpアクセスで追加・削除・登録されているもの一覧の確認をできるようにする

bash : GASから監視対象一覧を取得。ひとつひとつRunners APIをたたいてRunnerのstateを確認。死んでたらそれぞれのRunnerの管理人にslackでメッセージを送信。

mac : 上記のbashスクリプトをlaunchctlコマンドで15分ごとに定期実行

slack : appを作成。Runnerが死んでいたらbashがこのappとしてそのRunnerの管理人にメッセージを送る。また、スラッシュコマンドでGASにアクセスしてRunnerの追加・削除・登録されているもの一覧の取得ができるようにする。

それぞれのコード

GAS

A1に「id」B1に「管理人」とそれぞれ項目名を書いたspreadSheetを用意し、以下のようなスクリプトを作成し、ウェブアプリケーションとして公開。「管理人」はそのidを監視対象に追加した人と判断する。

// スラッシュコマンド用// eとしては以下のような形のjsonが来る。//{//    "parameter": {//        "channel_name": "hogehoge",//        "user_id": "A0B1C2DE",//        "user_name": "kkkkan",//        "trigger_id": "012345678.abcdefg",//        "team_domain": "××-jp",//        "team_id": "ABCABC012012",//        "text": "0",//        "channel_id": "AAAAAAA",//        "command": "/add_id",//        "token": "abc456DEF0123",//        "response_url": "https://hooks.slack.com/commands/××/●●/▼▼"//    },//    "contextPath": "",//    "contentLength": 100,//    "queryString": "",//    "parameters": {//        "channel_name": [//            "hogehoge"//        ],//        "user_id": [//            "A0B1C2DE"//        ],//        "user_name": [//            "kkkkan"//        ],//        "trigger_id": [//            "012345678.abcdefg"//        ],//        "team_domain": [//            "××-jp"//        ],//        "team_id": [//            "ABCABC012012"//        ],//        "text": [//            "0"//        ],//        "channel_id": [//            "AAAAAAA"//        ],//        "command": [//            "/add_id"//        ],//        "token": [//            "abc456DEF0123"//        ],//        "response_url": [//            "https://hooks.slack.com/commands/××/●●/▼▼"//        ]//    },//    "postData": {//        "type": "application/x-www-form-urlencoded",//        "length": 357,//        "contents": "token=abc456DEF0123&team_id=ABCABC012012&team_domain=××-jp&channel_id=AAAAAAA&channel_name=hogehoge&user_id=U4TCA3K8S&user_name=A0B1C2DE&command=%2Fadd_id&text=0&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2××%2●●%2▼▼&trigger_id=ABCABC012012",//        "name": "postData"//    }//}functiondoPost(e){// jsonをdecodevarjson_str=JSON.stringify(e,null,"\t");//  return ContentService.createTextOutput(json_str).setMimeType(ContentService.MimeType.JSON);// jsonを再度encodevarjson=JSON.parse(json_str);// コマンドを打った人varuser_name=json.parameter.user_name;// コマンドに渡した文字列vartexts=json.parameter.text.split('');// コマンド名varcommand=json.parameter.command;// spread sheetvarspreadsheet=SpreadsheetApp.openById('【spreadSheetのID】');// 1枚目のシートvarsheet=spreadsheet.getSheets()[0];varbody="";if(command=="/add_runner"){// 追加コマンドだったらvarid=texts[0];if(texts.length==1&&id!=null&&id!=''&&!isNaN(id)){// コマンドが受け付けるメッセージは1つの数字だけvarstartrow=2;varstartcol=1;varlastrow=sheet.getLastRow();varlastcol=sheet.getLastColumn();//がさっと取得varsheetdata=sheet.getSheetValues(startrow,startcol,lastrow,lastcol);for(vari=0;i<lastrow-1;i++){if(sheetdata[i][0]==id&&sheetdata[i][1]==user_name){// もしすでにidも管理者も同じRunnerが追加済みだったらbody=body+user_name+"の管理する Id "+texts[0]+"のRunnerは既に監視対象です。\n/runner_listコマンドで現在登録されているRunnerの一覧が確認できます。";}}if(body==""){// 新規追加// 監視対象に追加vararrData=sheet.getDataRange().getValues();arrData.push([id,user_name]);varrows=arrData.length;varcols=arrData[0].length;sheet.getRange(1,1,rows,cols).setValues(arrData);// 表示するメッセージbody=body+"Runner Id "+texts[0]+"を監視対象に追加しました。";}}else{// メッセージ内容が間違っていたらbody=body+"メッセージが間違っています。/add_runner [Runner Id] で監視対象に追加できます。";}}elseif(command=="/runner_list"){// 登録してあるreunner一覧表示コマンドだったらvarlist_str=getList();// 見やすいよう整形body=JSON.stringify(JSON.parse(list_str),null,"\t");}elseif(command=="/remove_runner"){// 削除コマンドだったらvarid=texts[0];if(texts.length==1&&id!=null&&id!=''&&!isNaN(id)){// コマンドが受け付けるメッセージは1つの数字だけvarstartrow=2;varstartcol=1;varlastrow=sheet.getLastRow();varlastcol=sheet.getLastColumn();//がさっと取得varsheetdata=sheet.getSheetValues(startrow,startcol,lastrow,lastcol);varbody="";for(vari=0;i<lastrow-1;i++){if(sheetdata[i][0]==id&&sheetdata[i][1]==user_name){// そのidの、その人が管理するrunnerが監視対象に含まれていたら// 削除sheet.getRange(startrow+i,1,1,2).clear();// 空白になってしまった行を削除sheet.deleteRow(startrow+i)// 表示するメッセージbody=body+user_name+"の管理する Id "+texts[0]+"のRunnerを監視対象から削除しました。";}}// 表示するメッセージ// もし何も削除しなかったらif(body==""){body=body+user_name+"の管理する Id "+texts[0]+"のRunnerは監視対象に含まれていませんでした。\n/runner_listコマンドで現在登録されているRunnerの一覧が確認できます。";}}else{// メッセージ内容が間違っていたらbody=body+"メッセージが間違っています。/remove_runner [Runner Id] で監視対象から削除できます。";}}else{body=body+"false";}returnContentService.createTextOutput(body).setMimeType(ContentService.MimeType.JSON);}// 監視スクリプトが呼ぶ用のメソッド// 監視するべきrunnerのidと管理人を返すfunctiondoGet(){returnContentService.createTextOutput(getList()).setMimeType(ContentService.MimeType.JSON);}// 監視するべきrunnerのidと管理人を返すfunctiongetList(){// spread sheetvarspreadsheet=SpreadsheetApp.openById('【spreadSheetのID】');// 1枚目のシートvarsheet=spreadsheet.getSheets()[0];varstartrow=2;varstartcol=1;varlastrow=sheet.getLastRow();varlastcol=sheet.getLastColumn();//がさっと取得varsheetdata=sheet.getSheetValues(startrow,startcol,lastrow,lastcol);varbody="[";for(vari=0;i<lastrow-1;i++){body=body+"{ \"id\":\""+sheetdata[i][0]+"\", \"admin\":\""+sheetdata[i][1]+"\"}";if(i!=lastrow-2){body=body+",";}}// 返すデータがないもないときにも空配列を返せるように]はfor文の外で。body=body+"]";Logger.log(body);returnbody;}

bashスクリプト

check_gitlab_runners.sh
GitLabのRunners API(https://docs.gitlab.com/ee/api/runners.html)とSlackのmessage API(https://api.slack.com/methods/chat.postMessage)を使用する。

#!/usr/bin/env bashcd /Users/kkkkan/Desktop/gitlab_runner_watcher/
# 監視するgitlab runnerのidと管理人の配列を取得check_list=$(curl -L【公開したGASウェブアプリケーションのURL】 | jq )list_len=$(echo$check_list | jq length)# tokenと投稿するchannelslack_token="【xoxb-から始まる、slack appのOAuth Access Token】"at_mark="@"# 監視する状態offline='"offline"'paused='"paused"'#echo ${status}#set -xfor i in$(seq 0 $(($list_len-1)))do
    id=$(echo$check_list | jq -r .[$i].id)admin=$(echo$check_list | jq -r .[$i].admin)#for id in "${check_runner_ids[@]}"#doecho${id}http_result=$(curl -s--header"PRIVATE-TOKEN: 【GitLabのtoken】""http://【監視したいGitLabインスタンスのドメイン】/api/v4/runners/${id}" | jq )#echo "$http_result"touch result.json
    echo"$http_result"> result.json
    #cat result.json# messageを取得# 権限などで正常に取れない場合だけmessgeがあるっぽいmessage=$(jq ".message" result.json)#set -x#echo "$message"if["$message"!="null"]then
      payload="GitLab Runner $_runner_name (ID : $id)の状態は取得できません。/remove_runnerコマンドで監視対象から外すか、このRunnerをkkkkanがみれるようにGitLab上から権限を変更してください。"
      curl -X POST -d"token="$slack_token"&channel="$at_mark""$admin"&text=""$payload""https://slack.com/api/chat.postMessage"fi# statusを取得status=$(jq ".status" result.json)_runner_name=$(jq ".description" result.json)runner_name=$(eval echo${_runner_name})if["$status"="$offline"]then#echo "offline$id"payload="GitLab Runner $_runner_name (ID : $id)のステータスがofflineになっています。"#set -x# Runnerの管理人にメッセージをポスト
       curl -X POST -d"token="$slack_token"&channel="$at_mark""$admin"&text=""$payload""https://slack.com/api/chat.postMessage"fi

    if["$status"="$paused"]then#echo "pause$id"payload="GitLab Runner $_runner_name (ID : $id)のステータスがpauseになっています。"#set -x# Runnerの管理人にメッセージをポスト
       curl -X POST -d"token="$slack_token"&channel="$at_mark""$admin"&text=""$payload""https://slack.com/api/chat.postMessage"fi
done

Launchctl

launchctlのplistファイル。~/Library/LaunchAgents/以下に置く。(15分ごとに実行するようにしている。)

gitlabrunner.watcher.plist
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plistversion="1.0"><dict><key>Label</key><string>gitlabrunner.watcher</string><!--フルパスで指定--><key>ProgramArguments</key><array><string>/Users/kkkkan/Desktop/gitlab_runner_watcher/check_gitlab_runners.sh</string></array><key>EnvironmentVariables</key><dict><key>PATH</key><string>/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin</string></dict><key>StandardErrorPath</key><string>/Users/kkkkan/Desktop/error.log</string><key>StandardOutPath</key><string>/Users/kkkkan/Desktop/out.log</string><key>StartInterval</key><!--15分毎--><integer>900</integer><key>RunAtLoad</key><true/><key>ExitTimeout</key><integer>300</integer></dict></plist>

slack

https://api.slack.com/apps
から新しくappを作って、slash commandsを導入し、/add_runner,/remove_runner,/runner_listコマンドを作成。全てRequest URLは先ほど作って公開したGAS webアプリケーションのURLにする。

ハマったところ

  • launchtclが全然実行されない! →
<key>EnvironmentVariables</key><dict><key>PATH</key><string>/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin</string></dict>

最初plistにこれを追加していなかったのだが、そうしたらpathが一部上手く通っていなかったみたいでjqコマンドが見つからなくてエラーしていた。今回ここが一番ドはまりして時間がかかった。

感想など

  • GitLab Runnerが死んだら通知が来るような機構はGitLab CIが備えていても良いようなものだが、見つけられなかったので自前で実装した。が、自分の探し方が下手で見つけられなかっただけの可能性が多分にあるのでもし見つけた方がいたら教えてほしいです。
  • 前提条件に書いたように、GitLabアクセスが社内PCからしかできない環境のためこの構成にしたが、その制約がないならslackへのメッセージ送信もGASスクリプト内で行い、GASウェブアプリケーションの定期実行機能を用いることでGAS+slack+GitLab Runners APIだけで十分実現可能だと思う。

参考にしたもの


Viewing all articles
Browse latest Browse all 2914

Trending Articles