あさりのリゾット
あさりのリゾットを作ったらめっちゃ美味しかったのでメモを残します。
材料(2人分)
材料 | 数量 |
---|---|
米 | 1合 |
あさり | 1パック |
たまねぎ | 半玉 |
にんにく | 適量 |
じゃがいも | 1玉 |
白ワイン | 200ml |
水 | 100ml |
バター | 適量 |
塩 | 適量 |
黒胡椒 | 適量 |
オリーブオイル | 大量 |
作り方
まず、あさりを酒蒸しにします。
- あさりは2時間ぐらい砂抜きしておきます。
- にんにくをみじん切りにします。
- フライパンにあさり、にんにく、白ワイン150ml、塩、バター、オリーブオイル、黒胡椒を入れて、蓋をして5分ほど蒸します。
- ざるで具材とスープに分けます。
- スープに水を加えて、弱火で保温しておきます。
それから、じゃがいもを蒸します。
- じゃがいもを適当な大きさに切ります。
- ラップで包んで電子レンジで蒸します。800Wで2分程度です。
じゃがいもと並行して米を炒めていきます。
- フライパンにオリーブオイルを引いて、塩を加えて、にんにくを炒めます。
- 香りがついてきたら、たまねぎを炒めます。
- 米を洗わずに入れて炒めます。オリーブオイルをたっぷり加えます。
- 米が透明になってきたら、スープを半分ぐらい加えます。蓋をして5分ほど蒸します。
- スープを1/4ぐらい加えて、さらに5分ほど蒸します。
- 残りのスープ、白ワイン50ml、あさり、じゃがいもを加えて、さらに5分ほど蒸します。
- 少し芯が残る程度(アルデンテ)になったら、黒胡椒を加えて完成です。
ポイント
米を蒸すときはフライパンが干上がらないように気をつけましょう。少しずつスープを足すとよいです。あと、蒸しすぎると味が落ちてしまうので、少し芯が残る程度がおすすめです。オリーブオイルはたっぷり入れましょう。
Swagger自動生成スタブの継続的デプロイ
SwaggerにはYAMLからAPIクライアントやAPIサーバのコードを自動生成する機能があります。コードの自動生成を利用することでドメインの実装に集中でき、また、スタブを利用することでチーム開発を円滑に進められるといったメリットがあります。
しかし、コードの自動生成を利用すると、API仕様を更新したのにスタブは古いままといったライフサイクルのズレが起こりがちです。そこで、CIで継続的に自動生成を実行することで、常に最新のスタブを共有しながらチーム開発を行えるようになります。
本稿では、CIとGradleを利用してスタブコードを自動生成する方法を説明します。具体的には、CIで以下を実行します。
- 開発者がSwagger YAMLを変更する。
- Gradleでスタブコードを自動生成する。
- GradleでスタブコードからJARをビルドする。
- JARをデプロイする。
前提
以下のマルチプロジェクト構成を前提に説明します。
- ルートプロジェクト
Swaggerによるスタブコードの自動生成
ビルドスクリプトからswagger-codegen-cliを実行します。
// codegen/build.gradle configurations { swagger } dependencies { swagger 'io.swagger:swagger-codegen-cli:2.2.1' } task generateSwaggerServer(type: JavaExec) { description 'Generate source code for API server' main = 'io.swagger.codegen.SwaggerCodegen' classpath = configurations.swagger args 'generate', '-i', file("${project(':api').projectDir}/src/main/swagger/api.yaml"), '-o', file("$buildDir/swagger-server"), '-l', 'spring', '--additional-properties', 'dateLibrary=java8' }
swagger-codegen-cliに引数を渡すことで、自動生成されるコードをカスタマイズできます。上記の例では、Springのコードを生成する、Java 8の日付クラスを使うといったことを指定しています。
テンプレートのカスタマイズ
自動生成のテンプレートをカスタマイズするには、swagger-codegenリポジトリにあるテンプレートフォルダを使います。例えば、Springのテンプレートをカスタマイズするには以下のフォルダをローカルに配置します。
ビルドスクリプトではテンプレートの場所をswagger-codegen-cliの引数に渡します。
task generateSwaggerServer(type: JavaExec) { description 'Generate source code for API server' main = 'io.swagger.codegen.SwaggerCodegen' classpath = configurations.swagger args 'generate', '-i', file("${project(':api').projectDir}/src/main/swagger/api.yaml"), '-o', file("$buildDir/swagger-server"), '-t', file('template'), // テンプレートの場所 '-l', 'spring', '--additional-properties', 'dateLibrary=java8' }
スタブのビルド
自動生成されたコードをビルドします。ここではSpring BootでJARを生成します。
// stub/build.gradle buildscript { ext { springBootVersion = '1.4.0.RELEASE' } repositories { jcenter() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } plugins { id 'java' id 'spring-boot' } sourceCompatibility = 1.8 targetCompatibility = 1.8 sourceSets.main.java.srcDir file("${project(':codegen').buildDir}/swagger-server/src/main/java") sourceSets.main.resources.srcDir file("${project(':codegen').buildDir}/swagger-server/src/main/resources") repositories { jcenter() } dependencies { compile 'org.springframework.boot:spring-boot-starter-web' compile 'io.springfox:springfox-swagger2:2.5.0' compile 'io.springfox:springfox-swagger-ui:2.5.0' } compileJava.dependsOn project(':codegen').generateSwaggerServer
buildタスクを実行すると、JARファイルが出力されます。
./gradlew :stub:build
スタブのデプロイ
/stub/build/libs/stub.jar
に出力されたJARをサーバにデプロイします。Dockerを利用すると簡単に実行できるので便利です。
# stub/Dockerfile FROM java:8 EXPOSE 8080 COPY build/libs/stub.jar / ENTRYPOINT ["java", "-jar", "/stub.jar"]
# stub/docker-compose.yml version: "2" services: app: build: . ports: - "8080:8080"
以下のコマンドを実行すると、スタブが実行されます。古いスタブが実行されている場合は新しいものに置き換わります。
docker-compose build
docker-compose up -d
サーバの /v1
にアクセスしたらSwagger UIが表示されます。また、YAMLに定義済みのパスにアクセスしたら空のJSONが返ってきます。
今後の課題
swagger-codegen-cliが自動生成するコードは空の値を返す実装になっています。これではスタブの利用側に不親切なので、 example
セクションに書いたデータを返す実装にしてほしいと思います。
Gradleからswagger-codegenを利用するプラグインはすでにありますが、swagger-codegen-cliの最新版に追いついていないので、今回はJARを直接実行することにしました。本当はプラグインを利用する方がビルドスクリプトの可読性が高くてよいと思います。
Gradle SSH Plugin 2.6.0 released
Gradle SSH Plugin 2.6.0、Groovy SSH 2.6.0をリリースしました。
2.6.0の変更点
New feature
- Add executeScript method for script execution (thanks to @matthiasbalke)
- Add inputStream setting for command or shell (thanks to @matthiasbalke)
- Automatically add host key to known_hosts
Bug fixes
- Fix executeSudo works if error message is changed from default
Add executeScript method for script execution
リモートでスクリプトを実行するメソッド executeScript
を追加しました。これまではファイルを転送してから実行する必要がありましたが、 executeScript
では文字列で指定したスクリプトを直接実行できるようになりました。
executeScript '''#!/bin/bash -xe echo 1 echo 2 '''
executeScript
は文字列からShebangを読み取ってインタープリタを実行します。上記の例では /bin/bash -xe
を実行します。そして、標準入力に文字列を流し込むことでスクリプトを実行します。
Add inputStream setting for command or shell
execute
、executeBackground
、executeSudo
メソッドに標準入力を渡せるようになりました。前項の executeScript
はこの仕組みで実現しています。
execute '/bin/sh', inputStream: '''#!/bin/sh echo 1 echo 2 '''
executeSudo
メソッドはsudoプロンプトとのやり取りがあるので実装が難しかったです。sudoプロンプトにパスワードを渡した後に標準入力にデータを渡します。ただし、パスワード認証に失敗した場合は再度プロンプトが現れることを考慮する必要があります。
Automatically add host key to known_hosts
2.6.0の目玉ともいえる機能です。これまではsshコマンドなどでknown_hostsを生成する必要がありましたが、Gradle SSH Plugin単体でknown_hostsに自動的にホストキーを追加できるようになりました。sshコマンドでいう StrictHostKeyChecking=ask
ですね。
knownHosts = addHostKey(file('.ssh/known_hosts'))
これまでHost Key Checkingに起因する問題が多く寄せられていたため、この機能を追加することにしました。
実装にはだいぶ苦戦しました。苦労したのは以下の2点です。
UserInfo
を指定するとHost Key CheckingだけでなくUser Authenticationにも副作用がある。- ゲートウェイ(踏み台)を経由する場合、トンネルのローカルポートがknown_hostsに書かれてしまう。
当初の実装では StrictHostKeyChecking=ask
を指定して、JSchの UserInfo
でHost Key Checkingのプロンプトにtrueを返すことでknown_hostsを更新する仕組みにしていました。しかし、この実装ではユーザ認証失敗時の例外のメッセージが Auth cancel
に変わってしまいます。どうやら UserInfo
がnullを返すと認証がキャンセルされたことになるようです。
また、ゲートウェイを経由する場合はトンネルのローカルポートを読み替える必要があります。 通常の方法では、known_hostsに書き出すホストを変えることは不可能です。
そこで、以下の流れに実装を変えることにしました。
StrictHostKeyChecking=ask
を指定してリモートホストに接続する。- Host Key Checkingに成功した場合は、そのまま処理を続ける。
- Host Key Checkingに失敗した場合は、
StrictHostKeyChecking=ask
を指定してリモートホストに再接続する。そして、Host Key Repositoryに追加されたホストキーをknown_hostsに書き出す。もしゲートウェイ経由の場合はホスト名を読み替える。
テストを通っているので問題ないはずですが、もしバグを見つけたら教えてください。
Bump to Groovy 2.4.7
traitに10個以上のプロパティを含めるとコンパイルエラーになるバグが2.4.4以前に含まれるため、2.4.7に上げました。
振り返り
StrictHostKeyCheckingやHostKeyRepositoryの挙動にだいぶ悩まされました。Server Integration Testを書いていたおかげでいろいろ試行錯誤してたどり着けたと思います。
少しずつCIの時間短縮を進めていて、5分台に乗るようになりました。Gradle 1.xサポートを廃止すればもっと短くなりそうです。