LambdaでGitHubのプライベートリポジトリをcloneする

# はじめに
LambdaでGitHubのプライベートリポジトリをcloneする。
GitHubの認証はsshを用いる。

# 環境

## 開発環境
- Ubuntu 14.04
- Python2.7

## 使用するライブラリ
- paramiko
- dulwich

# 手順

## プロジェクトディレクトリの作成
```
$ mkdir project
```

## SSH keysの作成と登録

鍵ペアを作成する。パスフレーズは入力しなくても良い。

```
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/(username)/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
```

公開鍵をGitHubに登録する。秘密鍵は `project/ssh` 以下に置く。

## 実装
Lambdaで実行するコードを実装する。コードは [こちら](http://ijin.github.io/blog/2016/02/18/ssh-and-git-on-aws-lambda/) から引用。

### git.py
```py
from __future__ import print_function
import os
import sys
import subprocess
import boto3

from botocore.client import Config

from dulwich.errors import (
    SendPackError,
    UpdateRefsError,
)
from dulwich.objectspec import (
    parse_object,
    parse_reftuples,
)
from contextlib import closing
from dulwich import porcelain
from dulwich.contrib.paramiko_vendor import ParamikoSSHVendor

import dulwich
import dulwich.repo
import paramiko
import paramiko.client


s3 = boto3.client('s3', config=Config(signature_version='s3v4'))


class KeyParamikoSSHVendor(object):

    def __init__(self):
        self.ssh_kwargs = {'key_filename': './ssh/id_rsa'}

    def run_command(self, host, command, username=None, port=None,
                    progress_stderr=None):
        if not isinstance(command, bytes):
            raise TypeError(command)
        if port is None:
            port = 22

        client = paramiko.SSHClient()

        policy = paramiko.client.MissingHostKeyPolicy()
        client.set_missing_host_key_policy(policy)
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(host, username=username, port=port,
                       **self.ssh_kwargs)

        # Open SSH session
        channel = client.get_transport().open_session()

        # Run commands
        channel.exec_command(command)

        from dulwich.contrib.paramiko_vendor import (
            _ParamikoWrapper as ParamikoWrapper)
        return ParamikoWrapper(
            client, channel, progress_stderr=progress_stderr)

```

### lambda_handler.py
```py
# coding: utf-8

from __future__ import print_function

import json

import dulwich
from dulwich import server
from dulwich import porcelain
from dulwich.repo import Repo

import git

print('Loading function')


def run(event, context):
    dulwich.client.get_ssh_vendor = git.KeyParamikoSSHVendor
    repo = porcelain.clone(
        "git@github.com:アカウント名/リポジトリ名.git",
        "/tmp/repo"
    )
    print(os.listdir("/tmp/repo"))
```

## デプロイパッケージの作成
必要なパッケージをインストールする。

```
$ sudo apt-get install python-dev libssl-dev libffi-dev
```

virtualenvで仮想環境を構築する。

```
$ virtualenv .
$ source bin/activate
```

pipでパッケージをインストールする。

```
$ pip install paramiko dulwich
```

コードやライブラリをzipに固める。これがデプロイパッケージになる。

コードをzipに固める。

```
$ zip project.zip git.py lambda_handler.py
```

ライブラリを追加する。

```
$ cd lib/python2.7/site-packages
$ zip ../../../project.zip ./* -r
```

共有ライブラリを追加する。

```
$ cp lib/python2.7/site-packages/.libs_cffi_backend/libffi-72499c49.so.6.0.4 .
$ cp /lib/x86_64-linux-gnu/libssl.so.1.0.0  .
$ cp /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 .
$ zip project.zip libffi-72499c49.so.6.0.4 libssl.so.1.0.0 libcrypto.so.1.0.0
```

鍵ペアを追加する。

```
$ chmod 777 ssh/*
$ zip project.zip ssh/*
```

## Lambda functionの作成
作成したデプロイパッケージ `project.zip` を使って、普通にLambda functionを作る。

# 動作確認
作成したLambda functionの実行結果は下記の通り。

```
START RequestId: 8edc1075-5fcd-11e6-97d6-afe1bfa01fbf Version: $LATEST
Counting objects: 3, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Checking out HEAD
['README.md', '.git']
END RequestId: 8edc1075-5fcd-11e6-97d6-afe1bfa01fbf
REPORT RequestId: 8edc1075-5fcd-11e6-97d6-afe1bfa01fbf	Duration: 3459.28 ms	Billed Duration: 3500 ms 	Memory Size: 128 MB	Max Memory Used: 40 MB	
```

正常にプライベートリポジトリがpullされている。

# 補足
[Lambda 実行環境と利用できるライブラリ](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/current-supported-versions.html) に以下のように書かれている。

> AWS Lambda の基盤実行環境は、以下で構成されます。
> 
- パブリック Amazon Linux AMI バージョン (AMI 名: amzn-ami-hvm-2015.09.1.x86_64-gp2):
    - AMI ID: 米国西部（オレゴン） リージョンの ami-f0091d91。
    - AMI ID: 米国東部（バージニア北部） リージョンの ami-60b6c60a。
    - AMI ID: 欧州（アイルランド） リージョンの ami-bff32ccc。
    - AMI ID: アジアパシフィック (東京) リージョンの ami-383c1956。
- AMI の使用に関する一般的な情報については、『Linux インスタンス用 Amazon EC2 ユーザーガイド』の「Amazon マシンイメージ (AMI)」を参照してください。
- Linux カーネルバージョン : 4.1.13-19.31.amzn1.x86_64
> 
コードにネイティブバイナリを使用している場合は、必ずこの環境でコンパイルしてください。

今回作成したデプロイパッケージは問題なく動いたが、Amazon Linuxで開発したほうが良さそう。

Amazon Linuxで開発する場合、今回の手順と下記の点が異なる。

- 以下のパッケージをインストールする

```
$ sudo yum -y install gcc gcc-c++ kernel-devel python-devel libxslt-devel libffi-devel openssl-devel
```

- 以下のライブラリをデプロイパッケージに追加する

```
$ cd lib/python2.7/site-packages
$ zip ../../../project.zip ./* -r
$ cd ../../../lib64/python2.7/site-packages
$ zip ../../../project.zip ./* -r
```

- 以下の共有ライブラリをデプロイパッケージに追加する

```
$ cp lib/python2.7/site-packages/.libs_cffi_backend/libffi-72499c49.so.6.0.4 .
$ zip project.zip libffi-72499c49.so.6.0.4 libssl.so.1.0.0 libcrypto.so.1.0.0
```

# おわりに
LambdaでGitHubのプライベートリポジトリをcloneできることを確認した。

# 参考
- [LambdaでSSHやGitを使ってみよう - @ijin](http://ijin.github.io/blog/2016/02/18/ssh-and-git-on-aws-lambda/)
- [AWS LambdaでPythonのparamikoを使う方法 ｜ Developers.IO](http://dev.classmethod.jp/etc/aws-lambda-paramiko/)
- [gitHubでssh接続する手順~公開鍵・秘密鍵の生成から~](http://qiita.com/shizuma/items/2b2f873a0034839e47ce)