SQL等価性検証ツールCosetteを、Dockerで立ててみた

# はじめに
[Cosette](http://cosette.cs.washington.edu/)という、SQL文が等価かどうかを検証するOSSのツールがあります。
個人的に面白いツールだと思い試そうとしたのですが、[オンラインデモ](https://demo.cosette.cs.washington.edu/)はユーザ登録が必要なのと、中身についてもある程度把握したいとの思いから、ローカルで立てようと決心しました。

しかし、[リポジトリ](https://github.com/uwdb/Cosette-Web)のREADMEを見ると、ローカルにPostgreSQLを立てたり手動でサーバを起動したりと、まどろっこしい手順になっています。なので、docker-compose一発で起動できるようにしてみました。

もしかしたら、私のようにローカルで動かしたいという奇特な人がいるかもと思ったので、手順を記事に残します。

# Cosetteとは？
次回、「Cosette使ってみた」編で紹介しますのでお楽しみに。

# 導入手順
## 前提条件
導入手順の前提は以下の通りです。

* OS：Linux （動作確認はUbuntu18.04で行ってます）
* 以下のSWがインストール・設定されていること
    * git
    * docker
    * docker-compose

## リポジトリのクローン＆必要なディレクトリ作成
```
git clone https://github.com/uwdb/Cosette-Web.git
cd Cosette-Web
mkdir docker-entrypoint-initdb.d
mkdir postgres-data
```
## Docker用のファイルを作成
### Dockerfile
オリジナルのDockerfileでも良いですが、aptのキャッシュを消していなかったりレイヤが多かったりしたので、修正版を作成しました。

```dockerfile:Dockerfile.mod
# Docker for prod site
FROM shumo/cosette-frontend

RUN set -x && \
    apt-get update && \
    apt-get install -yqq python-pip libpq-dev python-dev && \
    rm -rf /var/lib/apt/lists/*

RUN set -ex && \
    git clone --depth 1 https://github.com/uwdb/Cosette-Web.git && \
    pip install -r /Cosette-Web/requirements.txt && \
    cp -R /Cosette /Cosette-Web/backend/Cosette && \
    cd /Cosette-Web/backend/Cosette/dsl && \
    git pull && \
    cabal build

WORKDIR /Cosette-Web

EXPOSE 5000
ENV FLASK_APP main.py
CMD ["./run_server.sh"]
```

### Composeファイル
```:docker-compose.yml
version: '3'

services:
  cosette:
    build:
      context: .
      dockerfile: Dockerfile.mod
    ports:
      - "80:5000"
    environment:
      - COS_DB_DATABASE=cosette
      - COS_DB_USERNAME=cosette
      - COS_DB_PASSWORD=cosetteisawesome
      - COS_DB_HOST=postgres
      - TZ=Asia/Tokyo

  postgres:
    image: postgres:10.6
    volumes:
      - ./postgres-data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
      - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
      - PGPASSWORD=cosetteisawesome
      - TZ=Asia/Tokyo
```

### DB初期化スクリプト
docker-entrypoint-initdb.d以下にスクリプトを置いておくと、初回起動時に実行してくれます。

```bash:docker-entrypoint-initdb.d/init.sh
#!/bin/bash
set -xe

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
  CREATE ROLE cosette WITH LOGIN PASSWORD 'cosetteisawesome';
  CREATE DATABASE cosette;
EOSQL

psql -v ON_ERROR_STOP=1 --username "cosette" --dbname "cosette" <<-EOSQL
  create table users (user_id serial primary key, name varchar(30), email varchar(100), institution varchar(100), password varchar(100), api_key varchar(150));
  create table queries (id serial primary key, username varchar(20), email varchar(100), timestamp bigint, cosette_code text, result varchar(10), institution varchar(100), coq_result varchar(10), rosette_result varchar(10), coq_log text, rosette_log text, error_msg text, counterexamples text, coq_source text, rosette_source text, api_key varchar(150));
EOSQL
```

### ディレクトリ構成
上記手順実施後のディレクトリ構成は以下の通りです。

```
.
|-- backend/
|-- docker-compose.yml             <-追加
|-- docker-entrypoint-initdb.d/    <-追加
|   `-- init.sh                    <-追加
|-- Dockerfile.mod                 <-追加
|-- frontend/
|-- grader/
|-- postgres-data/                 <-追加
|-- README.md
|-- requirements.txt
|-- run_server.sh
`-- setup.sql
```

## ビルド＆起動
PostgreSQLは初期化処理があるので、先に起動しておきます。

```
docker-compose build
docker-compose up -d postgres
docker-compose up -d cosette
```

`docker-compose logs -f` でログをチェックし、特にエラーが出ていなければOKです。

# 使ってみる
エラーなく起動したら、80番ポートにブラウザからアクセスします。
以下の画面が表示されたら成功です。
（私はポートフォワーディングしてアクセスしているのでポート番号が違いますが、適宜読み替えてください）
![画面.png](https://qiita-image-store.s3.amazonaws.com/0/68336/a43dc3d6-758e-97ae-1503-ae1269f3bbe8.png)

あとは、適当にユーザ登録して使い倒しましょう。
ローカルなので、うっかり実システムのSQL文を張り付けてしまっても大丈夫ですね！
（もし何か事故が起きたとしても保証はできませんのであしからず）

# 次回予告
ちなみに、この記事は[NSSOL Advent Calendar 2018](https://qiita.com/advent-calendar/2018/nssol) 12/4分の準備回です。
上でも書きましたが、次回はCosetteを実際に使って、クエリの等価性検証がどこまで実用的に可能かを検証していきます。