メモです。
AWS LambdaでとあるPrivate Repositoryをcloneして利用したかっただけなのですが、 とても苦労した&確実に後日忘れるので自分用にメモ。
やりたかったこと
- AWS Lambdaで特定のPrivate Repositoryをcloneして利用したい。
- Pythonで組みたい
- cloneするためだけにGitHubのアカウントを新規に作りたくない
- ≒ GitHub Appsで生成するTokenでいい感じにやりたい
最初にやったこと
最初は、GitHubのアカウントを作成して、
参考にさせていただいたURL
上記を参考に下記のようにしてみた。 ただ、これだとGitHub側にユーザーを作らなければいけない(or自分のアカウントを利用しないといけない) ので後々困りそうだなと思い、GitHub Appsを利用した方法を模索してみることに。
import paramiko import dulwich import dulwich.client from dulwich import porcelain from dulwich.contrib.paramiko_vendor import _ParamikoWrapper def def_github_repository_clone(ssh_secret_key_path, repository_url, save_dir): #ssh_secret_key_path: SSH秘密鍵のローカルパス #repository_url: GitHubリポジトリのClone用URL(SSHパスで指定) #save_dir: Cloneしたリポジトリを保管するローカルパス class KeyParamikoSSHVendor(object): #参考URL #https://ijin.github.io/blog/2016/02/18/ssh-and-git-on-aws-lambda/ #https://qiita.com/yuu-eguci/items/c854856662336770decc def __init__(self): # 秘密鍵のパスを書きます。 self.ssh_kwargs = {'key_filename': ssh_secret_key_path} def run_command(self, host, command, username=None, port=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) return _ParamikoWrapper(client, channel) #SSH接続 dulwich.client.get_ssh_vendor = KeyParamikoSSHVendor #Clone result = dulwich.porcelain.clone(repository_url, save_dir) return result
試行錯誤
GitHub Appsなら個人ユーザーが必要無いので、「良さそう!」と思い、試行錯誤を開始。
まず、GitHub Appsを作成する。 Primissionは「Contents」を「Read-only」で設定して、clone対象のリポジトリを指定してInstallする。 (SSH方式と違って、対象リポジトリも柔軟に制限できるのでいい感じ)
そして、local環境でテストしてみるために、pipで"GitPython","pyjwt","cryptography"をインストール後に下記のようなコードでテストしてみる。 そしてlocalではうまくいった。 (GitHub AppsのAppIDや、InstallationsID、秘密鍵の生成方法はググれば他にいくらでも記載があるので説明割愛)
import time import jwt import requests import json import git from cryptography.hazmat.backends import default_backend from functions.def_get_ssm_parameter import def_get_ssm_parameter def def_clone_github_repository(github_repository_name, github_apps_app_id, github_apps_installations_id, github_apps_private_pem): #github_repository_name -> リポジトリ名 #github_apps_app_id -> GitHubAppsのAppID #github_apps_installations_id -> GitHubAppsのInstallationsID #github_apps_private_pem -> GitHubAppsで生成した秘密鍵の値 #jwt値を生成 #参考:https://gist.github.com/pelson/47c0c89a3522ed8da5cc305afc2562b0#file-example-ipynb github_apps_private_pem_bytes = github_apps_private_pem.encode() github_apps_private_key = default_backend().load_pem_private_key(github_apps_private_pem_bytes, None) time_since_epoch_in_seconds = int(time.time()) payload = { 'iat': time_since_epoch_in_seconds, # issued at time 'exp': time_since_epoch_in_seconds + (10 * 60), # JWT expiration time (10 minute maximum) 'iss': github_apps_app_id # GitHub App's identifier } jwt_value = jwt.encode(payload, github_apps_private_pem, algorithm='RS256') #jwtからheaderを生成 headers = { 'Authorization': 'Bearer {}'.format(jwt_value), 'Accept': 'application/vnd.github.v3+json', } #GitHub Tokenを生成 response = requests.post( 'https://api.github.com/app/installations/{}/access_tokens'.format(github_apps_installations_id), headers=headers ) if 'token' in response.json(): github_token = response.json()['token'] #ad_deployリポジトリをclone github_repository_remote_url = 'https://x-access-token:{}@github.com/{}/{}.git'.format(str(github_token), str(github_org_name), str(github_repository_name)) github_clone_local_dir = '/tmp/{}'.format(str(github_repository_name)) git.Git().clone(github_repository_remote_url, github_clone_local_dir)
Lambdaにのせてみる
上記がlocalでうまくいったのでLambdaで実装してみる。環境はPython3.8で。
標準のLambda環境ではいくつかモジュールが足りないので、
を参考にして"GitPython","pyjwt","cryptography"と"requests"をrequirements.txtに書いて、Lambda Layer用のZipを作成して、実装しようとしているLambda環境のLayerに追加。(もしかしたらGitPythonは要らなかったかも)
上記コードを実行してみる。
...が、うまくいかない
発生したエラー
git commandが無い!的なエラー(たぶん、「GitCommandNotFound」とかいうエラー)を吐いて、異常終了する。
後々、わかったがGitPythonはローカルのgitコマンドを利用しているだけのモジュールだったぽい。
ググると、下記のようなLayerを追加すればいいとのこと。自分の環境はap-northeast-1だったので、リージョンだけ
それに変えて、arn:aws:lambda:ap-northeast-1:553035198032:layer:git:6
というLayerを追加したらGit commandが無い!的なエラーは出なくなった。それでもうまくいかない
参考にしたURL stackoverflow.com
memo:もしかしたら上記Layerに頼らずとも、下記のように自前でgit関連コマンドを抽出すれば自己解決できるかもだが未検証。
発生したエラー2
今度は error while loading shared libraries: libcurl.so.4: cannot open shared object file: No such file or directory
というエラーが発生。
どうやらcURL関係のライブラリが無いらしい。
というわけで今度はcURL関係のライブラリを公開してくれている人がいるのでありがたく拝借して、Layerに追加してみる。 下記URLにある通り、GitHubからZipダウンロードしたままではパス(階層)の関係でうまく利用できないので、ダウンロード後にunzipして、解凍ディレクトリに移動してzip化してAWSにアップロードするとちゃんと読み込んでくれる。
参考にしたURL
再実行
git command関連のLayerとcURL関連のライブラリのLayerを追加したら無事、AWS Lambda上でもPrivate Repositoryのcloneに成功!