JenkinsのジョブをAWS Lambdaを使ってリモート実行する

この記事は、[ケーシーエスキャロット Advent Calendar 2017](https://qiita.com/advent-calendar/2017/kcscarrot)の4日目の記事です。

エンジニア2年目のonoriorioriです。
今まで技術ブログを書いたことがなかったのですが、折角のアウトプットの機会なので参加します。
まだまだ未熟なところがあるかと思いますがよろしくお願いします。

# 概要
AWSのEC2に構築したJenkinsのジョブをリモートで実行できるようにします。
リモートの実行にはAWS Lambdaを使用します。ランタイムはpython3.6です。
Jenkins(ver 2.73.3)の初期設定(Adminの登録)が完了済みの状態からスタートします。

## リモートで実行するジョブを作成する

###・パラメータを使わない場合の設定
ジョブの設定にあるビルド・トリガから、`リモートからビルド`にチェックをし、
`認証トークン`欄に任意のトークン名を入力します。
ここでは`test-job`を作成し、認証トークンに`sample_token`を設定しました。

![スクリーンショット 2017-11-24 17.08.26.png](https://qiita-image-store.s3.amazonaws.com/0/169832/cef1d3d8-da9c-00e8-cd4f-097df607ed80.png)

ジョブ内容はシェルスクリプトでechoする簡単なものにします。

![スクリーンショット 2017-11-24 17.32.18.png](https://qiita-image-store.s3.amazonaws.com/0/169832/8864adb4-d9cc-709d-26c2-e68b64dcae85.png)

###・パラメータを使う場合の設定
パラメータを使う場合もジョブの設定についてはほとんど変わりません。
今回はパラメータを渡すので、パラメータで受け取った文字列をシェルスクリプトでechoするようにします。

![スクリーンショット 2017-11-24 17.40.01.png](https://qiita-image-store.s3.amazonaws.com/0/169832/e61d8cf0-b183-ff75-e190-c21d9c0d8e6d.png)

![スクリーンショット 2017-11-24 17.40.46.png](https://qiita-image-store.s3.amazonaws.com/0/169832/36527a5c-91a7-5a8a-1ee4-35d3ede330f4.png)

###・APIトークンの確認
Jenkinsのホーム画面から、開発者→adminのユーザーID→設定を選択し、
`APIトークンの表示...`をクリック。
User IDとAPIトークンを控えておきます。

###・CSRF対策を外す
Jenkinsのホーム画面から、Jenkinsの管理→グローバルセキュリティの設定を選択し、
`CSRF対策`のチェックボックスを外します。

これでJenkinsの設定は完了です。

## ジョブをリモートで実行するLambda関数の作成
Jenkinsの設定が完了したので、次は作成したJobを実行するLambda関数を作成します。
AWSのコンソール上から、Lambda→関数の作成→一から作成を選択し、基本的な情報を入力して作成してください。
ロールについてはざっくりとAdministratorAccessを与えておきます。

さて、肝心な関数の中身ですが、Requestsというライブラリを使用します。
http://docs.python-requests.org/
`import requests`と記述することで使用することができます。
しかし、Lambdaのデフォルトで入っているライブラリにRequestsは存在しないので、
そのままimport requestsと記述しても使用出来ません。
自分でRequestsをインストールし、Lambda関数と共にzipファイルにしてアップロードして使用出来るようにします。
ディレクトリ構成としては以下のようになります。

workspace/
　　　├─ lambda_function.py
　　　├─ certifi/
　　　│　　├─　\_\_init\_\_.py
　　　│　　︙
　　　├─ chardet/
　　　│　　├─　\_\_init\_\_.py
　　　│　　︙
　　　├─ idna/
　　　│　　├─　\_\_init\_\_.py
　　　│　　︙
　　　├─ requests/
　　　│　　├─　\_\_init\_\_.py
　　　│　　︙
　　　└─ urllib3/
　　　　　　├─　\_\_init\_\_.py
　　　　　　︙

zipファイルの作成は、EC2で作成してFileZillaでローカルに移動してアップロード・・・としていましたが、
修正がかなり面倒なので、ローカルで作成するようにしています。(もっと簡単な方法があれば教えてください)

まず、Requestをインストールします。
Requestはpipで取得出来るので、pipを用います。
ただ、今回は指定した場所に配置しないといけないので、インストール先を指定して取得します。
`pip install requests -t . `　(workspace下で)
これを実行すると、上に記載したディレクトリとdist-infoのディレクトリが作成されるはずです。
dist-infoのディレクトリは不要なので、削除して問題ありません。

続いて関数の中身です。
コンソールを見ると、ハンドラは`lambda_function.lambda_handler`となっているので、
lambda_function.pyのlambda_handlerという関数を作成します。
ソースコードは、アップロードしたzipファイルのサイズが大き過ぎなければコンソールから編集出来るので、
ここで全て書かなくても大丈夫です。

```py3:lambda_function.py
import requests

def lambda_handler(event, context):
    # TODO implement
    return 'Hello from Lambda'
```
※ デフォルトの関数にimport requestsを追加したものになります。

以上が作成出来たらworkspace下をzipファイルにまとめ、アップロードします。
`zip -r sample.zip *`
アップロードして、保存を押すと以下のような状態になっていると思います。

![スクリーンショット 2017-11-24 19.35.21.png](https://qiita-image-store.s3.amazonaws.com/0/169832/b9fbca3b-7070-16c3-89d9-1ee38a8c38d5.png)

関数を実行して成功するか確認してみましょう。

![スクリーンショット 2017-11-24 21.37.52.png](https://qiita-image-store.s3.amazonaws.com/0/169832/04ea4187-8a79-29bb-0526-23660af4e17b.png)

無事成功しました。


###・ようやく本題
無事Requestsが使えるようになったので、Jenkinsのジョブをリモートで実行してみましょう。
コンソールからソースコードを編集します。

・まずはパラメータ無しの場合

```py3:lambda_function.py
import requests

USER_ID   = 'hogehoge'
API_TOKEN = '1234567890abcdefghijklmnopqrstuvwxyz'
URL = 'http://JENKINS_URL/job/test-job/build?token=sample_token'

def lambda_handler(event, context):
    
    auth = (USER_ID, API_TOKEN)

    response = requests.post(url=URL, auth=auth)

    return response.status_code
```
USER_IDとAPI_TOKENにはAPIトークンの確認で控えておいたUser IDとAPIトークンを、
URLには`http://JENKINS_URL/job/ジョブ名/build?token=認証トークン名`を記載します。

実行結果
![スクリーンショット 2017-11-25 0.02.52.png](https://qiita-image-store.s3.amazonaws.com/0/169832/c99543fb-2c97-90ed-aca6-384315c5a6c9.png)
無事実行されました。

・続いてパラメータ有りの場合

```py3:lambda_function.py
import requests

USER_ID   = 'hogehoge'
API_TOKEN = '1234567890abcdefghijklmnopqrstuvwxyz'
URL = 'http://JENKINS_URL/job/test-param-job/buildWithParameters?token=sample_token'

def lambda_handler(event, context):
    
    auth = (USER_ID, API_TOKEN)

    data = {
        'SAMPLE_PARAMETER' : 'リモートからパラメータ有りのジョブ実行'
    }

    response = requests.post(url=URL, data=data, auth=auth)

    return response.status_code

```
USER_IDとAPI_TOKENには先程と同じように、APIトークンの確認で控えておいたUser IDとAPIトークンを、
URLには`http://JENKINS_URL/job/ジョブ名/buildWithParameters?token=認証トークン名`を記載します。
また、dataにはジョブの設定で作成したパラメータをJSON形式で与えることで、パラメータ有りのジョブが実行出来ます。

実行結果
![スクリーンショット 2017-11-30 23.25.53.png](https://qiita-image-store.s3.amazonaws.com/0/169832/0926e298-07eb-5d17-ac80-2e67fa8fa5b4.png)
こちらも無事実行されました。

今回は`return response.status_code`として、ステータスコードを返すようにしています。
成功時のLambdaの実行結果は以下のようになります。
![スクリーンショット 2017-11-30 23.39.37.png](https://qiita-image-store.s3.amazonaws.com/0/169832/695bd8d1-ee4f-b1e7-4632-01787037de31.png)
ステータスコードとして201が返ってきました。

## まとめ
Lambdaのトリガーの設定次第で、Jenkinsだけでは設定出来なかった不定期な処理の実行なども出来るようになるのではないでしょうか。
間違い、不明点等ありましたらご指摘頂けると幸いです。