• 作成:

AWSのS3にhaskellでアクセスするのに苦戦してます, flycheck-stackはもう不要になってました, optparse-applicativeがいい感じ

haskellのawsパッケージでS3にアクセス

今書いてるwebアプリケーションでは, S3上に置いているファイルへのアクセスにkahing/goofys: a high-performance, POSIX-ish Amazon S3 file system written in Goというファイルシステムにバケットをマウントできるものを使っています. s3fsの高速版です.

しかし, アプリケーションサーバが大量のファイルを送信しているのが負荷になっているのではないかと思ってS3から直接ダウンロードさせるように書き換えようとしています.

ライブラリにはaws :: Stackage Serverを使用しています.

とりあえずダウンロードより先にアップロードを実装しないとデバッグも出来ないなと思ってアップロードを実装していました. 本当はアップロードもブラウザが直接S3と通信する方が良さそうですが, その場合データベースとファイルの一貫性保証がかなり面倒くさいので, ひとまずアプリケーションサーバを経由してアップロードするように書いています.

Aws.simpleAwsを使うとS3なのにhttpsにアクセスしに行って証明書が存在しないというエラーでアクセスできなくて何もわからなくなっていました. そういう時はサンプルを見れば良いわけです. サンプルを見れば良いことに気がつくのに3日ぐらいかかった気がします. aws/PutObjectIA.hs at master · aristidb/awsを見るとプロトコルにHTTPを指定しているのがわかりますね.

READMEのサンプルコードではAws.defServiceConfigを使ってますが, putでは使えないのかな, awsのことがまだよくわかっていないのでよくわかりません. S3.s3 Aws.HTTP S3.s3EndpointApNorthEast FalseServiceConfigurationに利用すればとりあえず動きました.

間違いです. バケット名にドットを使っているせいでした. ドットが無ければAws.defServiceConfigで動きました.

awsのサンプルコードではwithManagerを使っているわけですが, この関数はhttp-conduit :: Stackage Serverというパッケージに2つあります. 以下2つのモジュールに同名の関数が存在するわけです.

awsパッケージのサンプルコードで使われているのはNetwork.HTTP.Conduitの方なのですが, これはDeprecatedになっているのでNetwork.HTTP.Client.Conduitのものに適時書き換えましょう. withManagerを使ってReaderT内でaskするか, newManagerを使うかですね. 私はnewManagerを使いました.

今はsimpleAwsを使っています.

Pre-Signed URLを生成する方法がよくわからない

putとdeleteは実装できました. 肝心のgetを実装する必要があります. S3にリダイレクトするように設定して, アプリケーションサーバへの負荷を減らしたいところです. しかし, これがよくわからない.

パブリックアクセス可能ではないのでPre-Signed URLというのを使いたいと思ってやっています. ファイル名もディレクトリトラバーサル防止のために固定文字列+数値ということになってるので実際のファイル名をresponse-content-dispositionで差し替えたいですし. S3から直接ダウンロードさせるときにファイル名を差し替える - Qiita

aws cliで生成すればリージョンの問題か何回か手動でリダイレクト(リダイレクトは自動転送だから手動でリダイレクトは矛盾していませんか?)が必要でしたがURLの生成自体は出来ていたので権限の問題がないことは確認済みです. 【小ネタ】AWS CLIでS3のPre-Signed URLを生成できるようになっていました! | Developers.IO

しかしawsパッケージでこれをやる方法がよくわからなくて困っていました. How do you generate pre-signed object URL (S3) with this library? · Issue #189 · aristidb/aws では他の人は解決してるみたいですが, このissueを見てawsUri関数を使ってもシグネチャとか全く無いURLが生成されるしどういうことなんでしょうか全くわからない.

queryToUrl, SignedQueryあたりを使えば出来るんでしょうか, わからない, 誰か教えて欲しい, 明日やります.

flycheck-stackが不要になっていたので削除しました

stackが登場し始めた頃は, stack無し環境が標準だったので, flycheckはstackで管理しているプロジェクトでは動きませんでした.

なので, chrisdone/flycheck-stack: A flycheck checker that uses stack ghciというものを使っていました. 最近これが動かなくなっていました. 今見てみたら2016年05月が最後のコミットでした.

それで削除してみたらflycheck本体flycheck/flycheck: On the fly syntax checking for GNU Emacshaskell-stack-ghcというソースが追加されててこれが普通に動きました.

というわけで役目を果たしたflycheck-stackはもう不要のようですね. 本体が単体で使えるようになっていてハッピー.

slackとのコマンドライン連携ツールを書いています

optparse-applicativeがいい感じ

2年ぶりぐらいにhaskellでコマンドラインツールを書いてます. 今回言うコマンドラインツールというのは, 単に実行するだけではなく, そこそこオプション指定などをするものを指します.

特に理由がない場合はLibrariesからライブラリを選択しようと思っていたので, コマンドラインオプション解析ツールにはoptparse-applicative :: Stackage Serverを選ぶことにしました. Command line options parsing - optparse-applicative library ドキュメントがすごい書き込まれてて, 昔やってた時より大分良くなってることを実感しました.

slackへのファイル投稿で少し悩んでいた

slackとの連携といえばwebhookを使うと思ってたらfiles.upload method | Slackはwebhookじゃなくてlegacy apiを使わないと出来ないんですね… jsonベースでファイルをやりとりするのは面倒という判断でしょうか.

slackのlegacy apiがhttpステータスでは200を返してきてbodyのjsonで{ "ok": false }を返す形式で, これよくtwitterで話題になるAPIじゃん, と笑いました.

addRequestHeaderでパラメータを設定しようとしていたので少しハマった, URLに付けなければいけない. setRequestQueryStringを使いましょう.

Network.HTTP.SimpleにはPOST bodyにフォームの形式で値を付加する方法がないっぽい? Network.HTTP.Conduit.urlEncodedBodyが必要ですね. しかし, 何故かslackに1回だけしかfileを使った形式での投稿が成功しなかったので, form形式の投稿はなかったことになり, contentを使うことにしました. contentではなくfileで投稿できないか30分ぐらい悩みましたがよく考えてみると別にfileに固執する理由がなかった. Network.HTTP.Simpleで済ませられるならそれのほうが良いですよね.