自分用のBottle用テンプレート。
Bitbucket、GithubのHook用APIは、pullした後、Upstartに登録してるAPIサーバー(gunicorn)を再起動してる。
Gitレポジトリの操作用に、GitPythonを入れてる。
時間周りの処理で、arrowも入れてる。
server.py
# -*- coding: utf-8 -*-
import commands
import os.path
import traceback
from decorator import decorator
import logging
import subprocess
import getpass
from bottle import get, post, put, delete, run, default_app, request, response, HTTPError, redirect, local, abort
from bottle import static_file, route
import arrow
from git import Repo
# Decorator
@decorator
def check_db_connection(func, *args, **kwargs):
"""
Peewee用。dbは、Peeweeのデータベースのインスタンス。
"""
try:
db.get_conn().ping(True)
except:
logging.error(traceback.format_exc())
return func(*args, **kwargs)
@decorator
def check_login(func, *args, **kwargs):
"""
認証処理を書いて、ログインしたユーザーのインスタンスをlocal.meとかに入れとく。
"""
response_json = func(*args, **kwargs)
return response_json
@decorator
def error_handling(func, *args, **kwargs):
"""
エラーをJSON形式で返す。デバッグ用。本公開の時は、error_classとかerror_traceは消す。
"""
try:
return func(*args, **kwargs)
except Exception as exception:
if issubclass(exception.__class__, HTTPError):
raise exception
else:
logging.error(traceback.format_exc())
return {
"error_class": type(exception).__name__,
"error_trace": traceback.format_exc(),
}
@decorator
def measurement(func, *args, **kwargs):
"""
APIの処理時間を計測する。
"""
start_time = arrow.now()
result = func(*args, **kwargs)
print "Transaction time: {0} secs".format(arrow.now().float_timestamp - start_time.float_timestamp)
return result
# API
# Static files
@route('/')
@route('/<filepath:path>')
def server_static(filepath = None):
if filepath is None:
filepath = 'index.html'
root_path = u"Webのルートパス"
response = static_file(filepath, root = root_path)
return response
# Pull from Bitbucket
@post('/__pull_from_bitbucket')
@error_handling
def pull_from_bitbucket():
"""
Bitbucket連携用。
IPで、このAPIへのアクセスをBitbucketからだけに絞っとく。
"""
repository_path = "{0}/../../..".format(os.path.dirname(__file__))
repository = Repo(repository_path)
payload = request.json
logging.info("payload: {0}".format(payload))
branch = payload['push']['changes'][0]['new']['name']
logging.info("Pull from Bitbucket: {0}: {1}".format(
branch, repository.active_branch.path))
if repository.active_branch.path.endswith(branch):
repository.remotes[0].pull()
reboot_gunicorn_command = ["/usr/bin/sudo", "/sbin/restart", "api-server"]
output = subprocess.check_output(reboot_gunicorn_command)
logging.info("Reboot API server: {0}: {1}".format(
reboot_gunicorn_command, output))
return {
}
# Pull from GitHub
GITHUB_HOOK_SECRET = os.environ.get('GITHUB_HOOK_SECRET')
@post('/__pull_from_github')
@error_handling
def pull_from_github():
"""
GitHub連携用。
GitHub Hookのシークレットキーを、環境変数に入れとく。
"""
sent_signature = request.headers.get('X-Hub-Signature')[5:]
raw_payload = request.body.read()
generated_signature = hmac.new(GITHUB_HOOK_SECRET, raw_payload, hashlib.sha1).hexdigest()
if sent_signature == generated_signature:
repository_path = "{0}/../../..".format(os.path.dirname(__file__))
repository = Repo(repository_path)
payload = request.json
if payload['ref'] == repository.active_branch.path:
logging.info("Pull from GitHub: {0}: {1}".format(payload['ref'], payload['head_commit']['id']))
repository.remotes[0].pull()
reboot_gunicorn_command = ["/bin/sudo", "/usr/bin/systemctl", "restart", "api_server"]
logging.info("Reboot API server: {0}".format(reboot_gunicorn_command))
return_code = subprocess.call(reboot_gunicorn_command)
return {
}
application = default_app()
if __name__ == '__main__':
run(host = 'localhost', port = 8000, debug = True)