fogをやめてShrineにしてみた

長いこと、Ruby で単にストレージアクセスを抽象化するためなら fog が有力候補だと思っていたのですが、どうもそんなことねーな?と気づいた話。

fogとは

fog - The Ruby cloud services library

fog は storage に限らず cloud resource に対する API の wrapper として機能するもの。そこそこ歴史もある。

例えばこれを使うと理屈上 S3 へのアクセスも Google Cloud Storage へのアクセスも provider 設定の切り替えと認証情報のセット、プロジェクトのセット以外は全部同じように動く。はず。

Shrineとは

Shrine · File attachment toolkit for Ruby applications

Shrine は cloud 全般向けではなくて、Rails の model とかにファイルを添付する、その際に cloud storage も利用できるようにするためのもの。

やりたいこととfogの何で困ったのか

今回やりたかったことは Rails でもなければ Model とファイルの紐付けでもなんでもない、単にクラウドストレージのオブジェクトを読み書きしたい、それを local で手軽にテストしたい、という話だったのでファイル添付系の gem は最初から除外して考えていた。具体的には ActiveStorage とか CarrierWave とか Shrine とか Paperclip とかその辺。

で、じゃあ fog かと思って使い始めて fog-local でテストをがっつり動かしていざ fog-google に差し替えましたとなって急に雲行きがあやしくなった。

というのも、fog の API は

  • directories
    • directory
      • files
        • file

という階層を持つ形になっているんだけど、GCS 上で(権限不足が起きている際に)files がめちゃくちゃ遅くてtimeoutしてしまって何が起きているのか分からない

加えて、HTTP HEAD method を使ったオブジェクトの存在確認の API が GCS に存在しない。

ということでそもそも fog の API デザインが合わないという結論になった。

※ 実は GCS 側を S3 互換にして S3 の API を叩くという方法もあるんだけど、そうすると認証方法も変更になってしまうのでこれは避けた。具体的には GAE から GCS を叩く際に、イマドキは application default credentials というものがあるので、同一プロジェクト内の resource へアクセスするのであれば、アプリ側で認証情報に関して一切気にする必要がないんだけど、API を S3 互換にすると認証も S3 互換になるっぽいので、だいぶ面倒だなと思ってそこで考えるのをやめることにした。

[2020-10-29 追記] これについてはPermissionのエラーが確認できたのでfog固有の問題ではないようだ。ただしtimeoutでエラーの内容が確認できなくなってしまうという状態は非常に困るので、載せ替え自体は妥当な判断だったと言えるだろう。

Shrineは単なるオブジェクトアクセスでも便利

Shrine は Model へのファイル添付向けに Shrine::Attacher や Shrine::Attachment というものがあって、その例がトップページに掲載されているんだけど、実は単なるストレージアクセスを実現する Shrine::Storage という名前空間も存在している。この辺は fog などの他の gem に依存した実装になっているのかと思っていたがそうではなく、Shrine が独自に抽象化していて、この Shrine::Storage 以下の

  • upload
  • open
  • exists?

のメソッド群を使うと、File.write や File.exist? みたいなことが簡単に実現できるので、サクッと載せ替えてしまいました。とさ。

PlantUML始めました

最近いろいろ新しいものを試せているのは Emacs の環境が新しくなってパッケージ管理と設定に悩まなくなったからだなぁ。というわけで前々から VS Code では書けたけど Emacs で書けるようになったのでおさらい。

基本

シンプルなテキストファイルで UML が書ける、オープンソースのツール

PlantUML は UML をテキストで書くための文法であり処理系の名前でもある。UML については割愛。

ねらい

  • 画像エディタと画像ファイルと共同管理の組み合わせで発生する種々様々な課題を避けたい

必要なもの

PlantUML の処理系は以下に依存している

Java の話はまた後日。

VS Codeで

以下の extension を入れる。

PlantUML - Visual Studio Marketplace

動作確認しているのは 2.12.2

以前は plantuml.com のサーバにデータを投げる仕様だったのかな? 最新版では plantuml.server の設定はデフォルトではなくなったよ、と書かれている。

  • 文法のパーサを持っている
  • plantuml.jar が同梱されている

ので、あとは Java と Graphviz だけ。PATH の通っている場所に java or jar コマンドと dot コマンドがあればたぶん何も設定しなくても動く。

Preview ペインを持っていて、一度これを開いておくとファイルの保存を待たずに適当なタイミングで rerender が走るのでカジュアルに始めるのにオススメ。ただ、どうも内部で一回一回コマンドを叩いてゼロから JVM が起動しているようで、preview と言う割にそこそこ重くて逆に気になるかもしれない。

Emacsで

plantuml-mode - MELPA

これも構成としては VS Code の場合と一緒。ただし、plantuml-mode は plantuml.jar を同梱していないのでその設定から行う必要がある。

plantuml-mode は 20191019.1309 の時点でデフォルトで plantuml.com のサーバに繋ぎにいくので注意が必要。

また、一度その動作をしないと M-x customize で設定項目が出てこない。この現象は以下のバージョンで確認済み。

  • Emacs 26.2
  • 20191019.1309

一度、何も内容のない UML で preview を叩いてみると plantuml.com に繋ぎに行くので、そこでキャンセルして

M-x customize

と叩くと設定できるようになっている。ここで

Plantuml Default Exec Mode

jar

にしておくとサーバに繋ぎに行かなくなる。あとは Java の設定やら Jar の設定やら必要かもしれないので頑張る。

plantuml を brew などなんらかのパッケージ管理ツールで入れている場合はその辺の設定の参考になると思う。

コマンドで

Homebrew を利用している場合は brew install plantuml すると plantuml コマンドというか sh script ができるのでこれを利用する。

この中身を見ると

  • 同じく brew で入れた dot
  • brew で入れた plantuml.jar

のパスが直書きされているのが分かる。

Emacs の plantuml-mode の設定の際にこれが必要になるかも。逆に plantuml-mode の設定を見ていると plantuml -help では分からない設定があって、それが

-headless

オプション。これがないと一瞬 Dock に Duke が現れて今使いたいアプリからフォーカスを奪って逃げていく。すごく腹がたつので絶対に設定しておきたい。

出力形式とビュワー

  • デフォルトは PNG を生成するので適当な画像ビュワーで開ける
  • 書き込みが細かくなってくるとベクター画像が欲しくなってくるが、そうなるといろいろ挙動があやしい。とりあえずでやってみたら PDF は生成できなかった。EPS と SVG はイケた。
    • ベクター画像が欲しければ SVG を生成してブラウザで開く、くらいがお手軽かも

本当は dot ファイルそのものが出力できるといいんじゃないかと思ったんだけど、どうやら PlantUML にそういう出力オプションを作るのは難しいらしい。(request はちょくちょく上がっている模様。)

SVG はブラウザが対応してるので各種 Web サービスに上がっていてもそのまま表示できるわけで、まぁまぁよさげ。ちょっと確認したところ OmniGraffle も draw.io も対応してるし、なんかいろいろ大丈夫じゃないかな。

Cloud KMS + Cloud Storageの利用には大きく分けて3種類の方法があった

エンベロープ暗号化や秘密情報の扱いはそもそもそんなに得意じゃないので大間違いしている可能性があります。間違ってたら指摘お願いします。

※ Cloud KMSについては Key Management Serviceについて勘違いしていたこと - あーありがち(2018-10-21) に一部メモがあります。

まとめ

Cloud KMS + Cloud Storage の組み合わせには大きく分けて以下の3つの方法がある。

  1. Bucket のデフォルト暗号化キーを KMS Key にする
  2. Object 保存時に KMS Key Name をオプションで渡す
  3. 独自に KMS Key で暗号化して通常通り保存する

基本的に Cloud KMS のドキュメント上で説明されているのは 3 の方法。

1 と 2 の方法については Cloud KMS の API を enable にして KMS と Storage に同じアカウントでアクセスできるようにする必要がある。暗号化、複合は透過的に行われ、意図的に暗号化済みのデータを取得する方法はない。KMS Key が無効化されたら Object そのものにアクセスできなくなる。

Storageの自動機能の利用には注意が必要

上の 1, 2 は Storage + KMS という Google の提供する技で、これは簡単に言うと KMS Key Name の指定さえできていれば暗号化/複合処理は完全に Google におまかせになる、つまり自動で透過的に適用されるということ1

この場合、Object のメタデータの一つに KMS Key Name が入るので、鍵とデータの組み合わせは気にする必要がない。

ただし、利用の際には以下のような制限が生まれる。

  • Bucket の Region と Key の Location の組み合わせには制限がある(例えば asia な Bucket に global な Key を組み合わせることはできない)
  • KMS と Storage の両方に同一のアカウントで認証を要求することになるので厳密なことを言うと権限がややルーズ
  • Key が無効化されたら Object にはメタデータ含めてアクセスできない(読み取りはできるが中身が分からない、ではなく、読み取りもできない。ただし Key Name は取得できる。)

特に Bucket レベルの制限はバツンと一律アクセスできなくなってしまうので、いろいろな利用方法の混ざった暗号化キーやシークレットに対しておおもとの KMS KEK Key で有効/無効を切り替えるといった使い方は難しそう。

となると、あくまで

  • KMS へのアクセスを外部に許可するわけではなく、管理が容易
  • シークレットの利用方法は同一
  • キーのローテーションはするが無効化は原則しない

以上のような運用に向いている気がする。

例えばマルチテナントのデータをテナントごとに暗号化するキーを分けたうえでその暗号化キーの暗号化に使う KEK がローテートされる、みたいな使い方をすると開発環境の準備も楽そうだし、よさそうに思える。2

自前で暗号化する場合は自前で対応表に基づく自動化を行うべし

Storage の自動暗号化の恩恵に与らない場合、

  • KMS Key Name
  • 暗号化されたシークレット / DEK

の間を紐づける情報は Google Cloud のどこにも残らない。あくまで KMS Key とデータを呼び出したプログラムだったり紐づけて作業を行う作業者の頭の中だったり terminal の履歴の中だけに残るものとなる。

そこでおそらく基本的にはこのデータの暗号化/複合の処理はコードで閉じ込めてしまうわないと維持が難しくなってしまうので、まずは対応表とそれを利用した暗号化/複合処理のコードの準備が必要になる。

上に挙げた例の反対、

  • KMS へアクセスするユーザー(プログラム)の場所は様々
  • シークレットの利用方法が同一でない
  • キー単位で無効化するなどシークレットの利用を制御したい

といった要件が入る場合はこのような準備が必要と考えるのがよさそうだ。

  1. Bucket レベルの場合は指定はリソース ID になるが、同じこと。その場合はバージョンまで含めたリソース ID にならないように注意 

  2. データの暗号化キーが KMS と自動連携する Storage で管理されるイメージ。キーは DB に入っていない方が安心な感じもする。 

git で svn status のようなもの

git ls-files にいろいろオプションがある。

-m(–modified)
修正されているファイル。svn では M と出てくるやつ。
-o(–others)
管理対象外のファイル。svn では ? と出てくるやつ。–directory を付けると、丸ごと others なディレクトリは DIRECTORY/ までの表示になる。必要以上に出てこないのでうざくない。こっちがデフォルトの方が親切なような気もする。
-t
管理されているファイルの status が取れる。これこそが本当に svn status 相当。のはずなんだけど、見てる情報が違うらしく、上の二つの情報を合わせたものにはなぜかならない。どーもこの辺、git のマニュアルが不備なのか自分の理解が足りないのかよく分からんのだよな。

あと svn info 相当の情報も取り方がよく分からない。git remote -v でずいぶんあっさりした情報は取れるんだけどねぇ。

MacOSX の open を GNOME で

もうそのまんま gnome-open だった。

Twitter / UI: alias open='gnome-open' はあ …

ただ alias って Emacs の Dired では解釈されないらしく、どうしても標準で入ってる open1 の方が立ち上がっちゃって悲しい。

ちなみに OSX と同じく標準状態で PDF が手軽に作成できてるのは嬉しいんだけど、~/PDF/ に入るのはなんかもうちょっと分かりやすく GUI から設定できてもいいんじゃないかと思った。

Twitter / Takeru Naito: @wtnabe /etc/cups/cups-pdf …

  1. 全然ベツモノ 

WCLはもう一手間加えないとダメだな

しつこく Yahoo! Widgets の Widget Class Library ですが。

とりあえずライブラリの存在をチェックして include してグローバルな lib にオブジェクトを作ってやるなんつー単純な作業を毎回書くのはいやなので、それだけをやるコードを書いた。

ついでにどんなコンポーネントがあるのかだけでもライブラリ自身にリストアップしてもらおうと components(), subcomponents() を勝手に追加。(要はファイルシステムをなめて .js ファイルかディレクトリかチェックして配列を返すだけ。)

雰囲気がつかめてきて機嫌がよくなってくる。しかしその先へ行こうとサンプルを眺めてげんなり。

  • まず Yahoo.Controls.Theme コンストラクタに Theme の存在するパスを教えてやる(えぇっ!?)
    • しかもそのためには Yahoo.Controls クラスのリソースのパスを getResourcePath で取得したうえで、その中の Themes ディレクトリを指定する必要がある(マジっすか)
    • つまりこういうこと new Yahoo.Controls.Theme( lib.getResourcePath( 'Yahoo.Controls' ) + '/Themes' )
    • こんなのグローバルな lib を自分たちで汚してるんだし、オブジェクトの中で解決してくれよ
  • できたオブジェクトに theme 名を与える
  • 各コントロールのコンストラクタにはすべてこの theme オブジェクトを与える
  • 各コントロールのパラメータはできあがったオブジェクトに対して一つ一つ丁寧にセットしてやる
frmMain = new Yahoo.Controls.Form(theme);
frmMain.text = "Widget Class Library Control Sample";
frmMain.width = 350;
frmMain.height = 400;
...
lblPrompt1 = new Yahoo.Controls.Label(frmMain.theme);
lblPrompt1.autoSize = true;
lblPrompt1.left = 8;
...

以下コントロール分くり返し。

……。

いやぁ。自分で画像作らなくていいのはありがたいよ? ありがたいんだけど、なんでこんなめんどくさいんだよぅ。なーんつーかこう、バラバラな感じ。かといって粗結合で扱いやすいよ!とかでもない。あくまで縦横無尽に共依存。

どないやねん。どないやねーん。

なんかパラメータとかもさ、JSON でドーンとセットしたいじゃんよ。まぁそういうの書けばいいんだと思うけどさ。ぐるぐる回して setter があったらそれに放り込むようなやつ? まぁ自分に書けるかどうか分かんないけどさ1。しかもこのライブラリの setter, getter の書き方って deprecated ですよ? orz

cf. Core JavaScript 1.5 Guide:Creating New Objects:Defining Getters and Setters - MDC

他にもリファレンスを眺めてみると obosolete なのに Widget Class Library のサンプルにはバンバン出てくる書き方とかあったりして、そういうのどうなのよ?とかね。気になるところがボロボロと出てきますね、これ。

たぶんその辺の気持ち悪さが解消されればめっちゃ便利になるんじゃないかと期待はしてるんだけどなぁ。自分で書かなきゃいけないんですかねぇ。WCL そのもののバージョンアップなんてそんなすぐにはないですよね…。

  1. setter 一覧とか getter 一覧て取れそうもないですなぁ。Ecmascript 4 ではできそうな気がするんだけど。 

emacs-w3m で referer は吐けないのか?

そんなにヘビーに使っているわけでもなかったので今まで w3m + Emacs で textarea を編集する際は普通に w3m から Emacs を起動して利用していたんだけど、ふと思い立って w3m-el を入れて全部 Emacs から作業するようにしてみた。

tDiary の CSRF 対策に引っかかって書いた内容全部飛ばしちまったorz

あとで分かったことだけど、どうも w3m から Emacs を起動するか、Emacs から w3m を起動するかの違いでしかないっぽいので、21.3.5 なら起動も速いし今まで通りの使い方でいいような気がしてきた。この方がマウスも普通に使えるし。

それより w3m.org がソースをそのまま吐いているのは何が起きているんだろうか。


ついでに以前 mule-ucs を組み込んで激重になった Debian 上の Emacs でも w3m-el なら快適だーと思っていたが、今度は textarea のレンダリングがおかしい。おうおうおう。そこが目的なんじゃい、emacs-w3m さんよぉ1

ということで強権発動して、システムワイドでいきなり mule-ucs を読み込もうとする startup の elisp を外した。必要なときに自分で require することにして起動を軽くし、こっちも w3m から Emacs を起動する方式に変更。同時にあれこれ起動を重くする要素を見直して少しでも快適に作業できるようにしてみた。

結局、元通りってことなんだな。

※ tDiary の設定をいじればいいじゃんとも思うんだけど、まぁそもそも emacs-w3m の操作性がちょっと趣味に合わないので、無理に使わなくてもいいやと思ったのでした。そうこう言っててまたそっちに傾くかもしれないんだけど。

  1. たぶんバージョンがちょっと合ってないんだろな。 

久しぶりに完全オフ

まー毎週ちゃんと休日はあるんですが、試験とか試験勉強とかなんだかんだでうまいこと身体を休めることができずにしばらくきてました。やっと完全休養日。たった一日。おいこら。

なんかいろいろやりたいことがあったような気がするんだけど、なんだったかもう思い出せないしやる気も起きない。

まーそんな日も必要だなってこった。なんか fink とか ports とか地味にアップデートしながらボー。

About

例によって個人のなんちゃらです