thenから始めないといけないキモいオブジェクトで、stub/mockとか頑張る

Swagger Clientの設計がキモい

swagger-client 3.9.5 で確認。

swagger-api/swagger-js: Javascript library to connect to swagger-enabled APIs via browser or nodejs

いろいろあるけどいちばんキモいのはコレ。

const Swagger = require('swagger-client')

const s = new Swagger()
s
.then(client => {
  ..
})
.catch(e => {
  ..
})

これ、インスタンスが Promise そのものなので、まず then を書かないと実際の Swagger Client を取得できずに何も実行することができない。何らかのメソッドが Promise を返すのは分かるけどインスタンスそのものって、さすがにどうなの。

これって単に「自由な callback メソッドだと引数の順番のルールが統一されていないので、とりあえず Promise の仕様に合わせたよ」以上の意味がないように見えるんだけど、どうなんだろうなぁ。

PromiseになってしまったオブジェクトのメソッドをwrapするPromiseを作ってawaitできるように

上のような Promise は何も動作し始めていないので async/await できない。無限に wait して Timeout する。これは困る。then / catch の世界は結局 callback であり1、scope が切れて扱いにくい。

そこで何らかのメソッドを呼ぶための wrapper を書いて、そいつが Promise を返すようにすると async/await で書けるようになる。上の例で言うと以下のような感じ。

async foo (retry) {
  return new Promise((resolve, reject) => {
    s
    .then(async (client) => {
      const r = await client.execute()
      resolve(r)
    })
    .catch(async (err) => {
      if (retry > 0) {
        return await foo(--retry).then(resolve).catch(reject)
      } else {
        return reject(err)
      }
    })
  })
}

ここではあえて古式ゆかしい Promise の書き方をしている。.then().catch() で数珠つなぎにしてる場合はそのまま値を return することはできないので async/await のように楽することができない。しっかり return new Promise() で resolve, reject してあげよう。こうしておかないと var を使って破壊的に値を代入したうえでその値をチェックして return するか throw するか、みたいな余計なロジックが必要になってしまう。

上のような Promise を作ると async/await の書き方とマッチする。

プロパティに収まった何もしないPromiseに対してstub, spyを作る

上の書き方で扱いにくい Promise を async/await で書けるようになったわけだが、まだ問題がある。テストコードである。

例えば c というオブジェクトの中に s という property で Swagger インスタンスを持っているとする。

class C {
  constructor () {
    this.s = new Swagger()
  }
}
const c = new C()

sinon で stub out する場合、then の方はこんな感じ。

sinon.stub(c, 's').get(() => {
  return Promise.resolve({
    async execute () {
      return {
        ..
      }
    }
  })
})

Promise.resolve() で client に相当するオブジェクトを作って返してやる。例えば client.execute() の実行が必要ならそれに対して適当な値が返ってくるように仕立ててやるとその周辺のコードのテストができる。async/await 時代はこういうコードがだいぶ書きやすくなってて助かる。

catch の方はこんな感じになる。

sinon.stub(c, 's').get(() => {
  return Promise.reject(..)
})

考え方は同様。Promise.reject() で Error オブジェクトを作って返してやる。

ポイントは sinon.stub().get() で、これは getter メソッドの stub out に使える記法。

少しまとめると、

  • そもそも stub out はメソッドに対して行うものだが、どうしても property に対して行いたいということであれば、property を getter メソッドと看做して stub().get() を使うことで対応できる
  • then() と catch() それぞれのテストは stub().get() で Promise.resolve() か Promise.reject() を return してあげることで kick できる

sinon の部分の続きは Sinon.JSにもう少し詳しくなったのでちょっとまとめ - あーありがち(2019-10-22) へ。

他の test double も同様の考え方を適用できるんだと思う。試してない。悪しからず。

execute を実環境に対して実行するテストはいろいろ重いので、平時は execute() に対して何らかの固定値を返すものを用意しておいて、その後の動作をテストする形になると思う。

  1. 深くなりにくいだけ 

Key Management Serviceについて勘違いしていたこと

あるいは Kanazawa.rb meetup #74 - Kanazawarb に行ってきたよ。

最近 Google Cloud Key Management Service について調べていた。

というのも、Cloud Functions で小さいツールを業務的に扱うことがたぶん今後増えるので、サービスアカウントが増える、ということはこのアカウントのキー管理が増える、ということを予想していた。

結論から言うとこの二つのうち両方を勘違いしていたのだが、その過程で面白いことが分かったのでまとめておこうと思う。

Cloud KMSは自分の管理したい鍵を直接管理してくれるものではなかった

Cloud Key Management Service | Cloud KMS | Google Cloud

Cloud KMS は確かに鍵を管理してくれるのだが、今自分が管理したいと思っている、すでに存在している鍵を収める金庫のような役割を果たしてくれるわけではなかった。

Cloud KMS は決してクラウドから取り出すことのできない鍵を生成、管理してくれるものだったのだ。

「え? 取り出せない鍵に意味あるの?」

あるんです。

「鍵に対して名前が振ってあって、その名前に対して暗号化/複合したいデータを送りつけてやる」

という使い方をすれば「取り出せないけど使える鍵」を実現できる。そしてこの方式には明確なメリットがある。

  • 鍵を利用したいレベルの秘密情報の利用の痕跡をすべてクラウド側に残せる
  • 特定の名前の鍵を破棄すれば、その鍵を利用するデータの利用も無効化できる
    • (鍵にアクセスできるユーザーなどで管理できるかどうかは未確認)

そう。単なる金庫ではなく監査が行えるし、暗号化したデータの利用も制御可能になる。

この鍵はどう使うのがよいのか?

この鍵はあくまでクラウド側で管理しやすい形をしているもので、直接何かに使えるわけではない。まぁ、暗号化/複合はデータを送りつけてやれば可能だが、

暗号化/複合のたびに通信が発生するので、ありとあらゆる暗号利用シーンに使うのは現実的でないし、なんなら暗号化/複合できるデータの容量には制限がある1

ということで、

  • データを暗号化して保存する鍵を暗号化する
  • 何かの認証に利用する鍵を暗号化する

など、鍵を暗号化する鍵 ( Key Encryption Key ) として利用することを想定しているらしい。

そこで新たな疑問

  1. Cloud KMSへアクセスする権限の認証、認可に利用するキーの管理はどうなるのか?
  2. 暗号化したキーはどこに保存するのがよいのか?

これについては、まだ完全に解決していないが、

  • Cloud KMS へアクセスするマスターキーの管理は頑張ってね
  • Cloud Storage の読み書きの際には実は Cloud KMS の key name を利用したアクセスが可能

という辺りにヒントがありそう2。少なくとも大量の鍵を大量のまま頑張って管理するよりは頑張らずに管理することは可能そうだ。

Cloud FunctionsからCloud Storageへのアクセスは鍵不要だった

Function の動作しているサービスアカウントの Default Credentials で同一プロジェクトの Storage は読み書き可能だった。

サーバー間での本番環境アプリケーションの認証の設定 | 認証 | Google Cloud

環境変数が設定されていない場合、ADC では、サービスで実行されているアプリケーションに応じて、Compute Engine、Kubernetes Engine、App Engine、または Cloud Functions によって提供されているデフォルトのサービス アカウントを使用します。

※ もしかしたら beta から GA になったタイミングでこの仕様は変わっているかもしれない

しかしpkgcloudはそれを許さなかった

pkgcloud/google.md at master · pkgcloud/pkgcloud

Using the Google Cloud provider requires:

  1. A project id
  2. A JSON key file

ということでFunction実行時の鍵を減らすには

  • pkgcloud に pull-req を送って default credentials を利用できるようにする
  • pkgcloud の利用を諦めて独自に Google API SDK の wrapper を作って file system へのアクセスと透過的に切り替える何かを作る
  • local の file system での開発を諦める

以上から決断が必要そう、ということが分かった。

CI/CDなどからKMSを利用する方法を確立するのが次の課題

Function 実行時に必要な鍵は(将来的に)なくせることが分かった。もう一つの鍵は CI/CD サービスから deploy 時に利用するサービスアカウントの鍵である。

これについては CI/CD サービスが KMS を提供するクラウド3の外にあるCircleCI を今のところ使っているので、どうしても鍵を外部に保存する必要があり、その鍵自体を KMS で暗号化することが肝要になってくる。

そこで CI/CD サービスから KMS を利用して安全に暗号化/複合できるようにする手法を確立しておくことで、今後 deploy の必要な Function が増えても管理コストが極力増えないようにできるはずだ。

以上、あーなるほどね、すっきりした。

  1. 2018-10-21現在でGoogle KMSは64KiB, AWS KMSは4KB 

  2. Cloud StorageのドキュメントではなくSDKのコードを読んでいて気がついた。Google Cloudのドキュメントから情報を探すの難しすぎるよ。 

  3. 今回の私の場合は Google 

Rails + Jasmine gem 1.2のsrc_filesの与え方

Rails + Jasmine gem 1.2 の環境を新しく作ったところ、なんか変だなと思うところがあったのでメモ。

Comedy is Hard - Jasmine 1.2 released

Jasmine は 1.2 から assets pipeline 環境の自動判別機能が入って、reporter のデザインも変わってなかなか格好よくなりました。

Jasmine gem 1.2でrails g jasmine:installしたときのjasmine.yml

src_files:
  - assets/application.js

って書いてある。なんかこれおかしくないですか。こんなパスは存在しない。

ということで試してみた。

assets環境Rails

OKassets/application.js
OKapplication.js
NGapp/assets/javascripts/application.js
NGassets/javascripts/application.js
NGjavascripts/application.js
NGblank

上記の NG はテストを実行して fail になる。

NG*.js
NG**/*.js

そもそもテストが実行されない。ということで個人的には

application.jsのみ

がいいように思う。

いずれにせよ assets 環境下では SprocketsHelper などの働きによって application.js を指定するだけで依存している js すべて読み込んでテスト可能になった。

非assets環境Rails

  • Rails 3.0.x + Jasmine 1.2 は従来通り public/**/*.js で ok
  • assets.enabled = false も従来通り

こっちは簡単ですね。

assets環境下で何が起きてるのか

こっから先はあの assets/application.js という気持ち悪い書き方の話。これは jasmine gem の中の

lib/jasmine/asset_pipeline_mapper.rb

の中で

 def files
   @src_files.map do |src_file|
   filename = src_file.gsub(/^assets\//, '').gsub(/\.js$/, '')
   @context.asset_paths.asset_for(filename, 'js').to_a.map { |p| @context.asset_path(p).gsub(/^\//, '') + "?body=true" }
   end.flatten.uniq
 end

でやってる、

   filename = src_file.gsub(/^assets\//, '').gsub(/\.js$/, '')

が理由。そのあとの

   @context.asset_paths.asset_for(filename, 'js') ...

で js の filename に該当する asset の情報を取得するんだけど、その前になぜか

gsub(/^assets\//, '')

が入っているから、

たまたま assets/application.js は application.js と解釈される

ようになっている。これたぶんなんか勘違いして jasmine.yml に assets/ って書いちゃって、無理矢理動くようにしちゃったんじゃないかなぁ…。まぁこのコードはもう取り除くの難しいけど、

せめてデフォルトは application.js に直してほしい

とは言え、この微妙な話題を英語で pull-req する自信はないなぁ…。

iPod touch 4thでボリュームの扱いが変わったのに気づいた

古い話ですが。

iPod touch を 2nd -> 4th に変えて地味に不便だなと思っていたのは

  • 「通知音&着信」と「本体ボリューム」が分離していること

だったんだけど、「設定」の「サウンド」にボタンでの変更を可能にするスイッチがあった…。デフォルトの動作が変わったのか。

Gnome の環境を Mac に合わせるのを諦めた

Ubuntu に引っ越したけど、細かいところで Mac のクセをそのまま活かせたらと思い、以下の設定をした。

  • alt + space で SCIM on/off
    • しかしアプリによってはこれでメニューが開いてしまうので、とりあえずかなりの時間使うであろう gnome-terminal は設定でこの動作をオフに
  • gnome-termial はショートカットキーを覚えて設定が終わればメニューは不要なので非表示に
    • 行数確保

他に

  • option1 を meta として使う

のはやりたかったんだけど、どうしても gnome-terminal でうまくいかなかったので諦めた。そのとき以下のことを教わった。

Twitter / Takeru Naito: @wtnabe [Win] は通常 [Super] …

ls のオプションはこんな感じ。

Twitter / wtnabe: BSD系の ls -wAFG は GNU では ls …

ちゃんと環境読み取って if else で alias 定義すればいいんだろうけど、いつも面倒でそのまま書き換えちゃうな。良くない、良くないよ。


最後に、ハマったのはこれ。

Ubuntu日本語フォーラム / Emacs にてAlt+<, Alt+>が効かない

かなりハマった。キーボードのレイアウトがいきなり変わる意味分からなす。

  1. Realforce上では左Winに割り当てていた 

PLUTO読み始める

うーん。失敗だったかなぁ。

気になってしかたがない。この人の序盤のストーリーテリングのうまさにはどうしても敵わないな。途中からオチにかけてはなんか満足感がイマイチなこともままあるけど。1

というか連載追っかけ作品は増やしちゃダメだって。

  1. MONSTERとか21世紀少年とかはそのクチで、終盤、個人的には惰性で読んだ感が残った。 

Spket IDE 試してみた

Spket IDE

けど。

  • .kon ファイルでは補完は効くけど .js では効かない
  • そもそも Konfabulator 独自のオブジェクト(system とか)の中身について補完が効かない

これじゃあ使えないなぁ。

別にプロジェクト管理とかパッケージングとかできなくていいから、独自オブジェクトの inspect ができると嬉しいんだよね。MozRepl みたいな。あぁいうのだけあれば十分なんだけどな。

特定のフォントだけ適用除外ってできないかな

いやね、具体的には Osaka なんですけど。

これね、肉厚すぎて本文が丸ごと Osaka だと目が疲れるんですよ。平成でもヒラギノでもいい、Osaka にはしたくない。でもこれ、実現する方法がないような?1

ユーザー CSS で上書き? でも「このフォントの指定だけを無視する」なんて使い方はできないような? 「このフォント指定で上書きする」っていうのは可能だけど、いつも必ずヒラギノで読みたいとかいう要求でもないしなぁ。

あ、そういえば昔 Windows でなんかフォントを一時的に on/off するソフトを使ってたな。2あんなんが Mac にもあればいいのかな。めでたく Terminal の常用フォントから Osaka 外れたし。

試しに FontBook で使用停止にしてみたけど、やっぱ太いなぁ。うーん、この辺さっぱり分かってないからな。とりあえず @-moz-document で該当サイトの font 指定を上書きしておくか。ニュース系なので今後もまず見るしね。

  1. 太字は好きじゃないんだなぁ。たまに全部ボールドとかいうページに出くわすとどんだけ有用な情報が書いてあっても作者への感謝の念はぶっとびます。 

  2. フォントをたくさんインストールすると重くなるので使うときだけ on にすることでこれを回避することが目的だったと思う。 

会員限定サイトのホスティングってのは成り立たないのかな

はてなブックマークにおけるリンクの考え方について

ずいぶんこなれたというか、この辺の対処はもうさすがにずいぶん手慣れたものという感じがする。少なくとも技術的に対処できるところはあれとこれとそれ、という具合にリストアップは済んでいて、情報も開示する。で、最後の砦の人間が対処しなきゃいけない部分も、協力はやぶさかでないですよ、と。

しかしあれだな。リンクされて困る人たちのため1にも、CMS ホスティングが安価に提供されたらいいのかも。ID を取得したらすぐに会員制サイトが構築できます!っての。デザインもテンプレートになってて、必要なモジュールもインストール済み。どのページは公開して、どのページは非公開なのか、自由に管理できるので安心です!みたいな。

価格と利用者数の見極めが難しそうなのと、インターフェイスがどれくらいド素人向けにできるのかってところがポイントなのかな。今は CMS をカスタマイズから請け負ってサービスするってのはあるけど、blog や Wiki のホスティングみたいに大規模で、ある程度のカスタマイズを自分でやってね、っていう形にもそろそろ持っていけるんじゃないかなぁ。Yahoo! Groups なんかだと画一的すぎていやだけど、自分で一から構築するスキルも頼む金も(あまり)ない、みたいな層は結構いると思うんだよな。そういう人たちを現実的にくすぐる設定2の勘所がつかめてきてる人たちも、それなりにいるんじゃなかろうか。

その前に『完全無料!会員制サイトの作り方』とか、本が出るか?3 ワンストップは難しくても、複数のサービスを組み合わせれば十分可能なはず。ちょっとユーザーは不便だけど4

  1. ってだけじゃないけど 

  2. 価格も機能も 

  3. ひょっとしてもうある? 

  4. 場合によっては複数のサービスの ID を新たに取得する必要があるため。あ。OpenID 前提の時代がきたらそれも問題なくなる? 

知るには痛みもあるってことか?

新しいことを知ろうとしない人や知っても感動のない人が居る。どーもこれが理解できないのだが、新しいことを知ることで自分の中に生じる変化がいやだってことなのかな?

これは個人にも組織にも言える。というか組織に当てはめた方が分かりやすいかな。新しくより良いものを取り込もう、生み出そうという動きをつぶしてしまうことは組織になると割とよくある。ルーチンワークの負荷が高いからそっちを優先する、という判断はもちろんあるだろうけど、負荷の判断やルーチンワークの設計そのものが間違っていることも往々にしてあり得る。

っていうか。

刺激があった方が面白いじゃん、と考えるような輩はダメなんだろうか。ときどき自分の方が間違っているのか?とも思うが、面白くないことを淡々と続けられる人の存在をありがたいと思いながら、そういう人たちが生き生きしていないように見える現実からも目をそむけることができんのです。

まー自分みたいな人間ばかりが周りにたくさん居たら真っ先に自分がムカツクんだろうけど :-P

物欲バシバシと Free Unix

ここんとこずっと金を使わずに地味な生活(その割にそこそこ食道楽ではあったが)を続けてきたが、急激に物欲指数が上がってきている。

  • デジカメ
  • Mac
  • iPod

くらいの順でほしい。ただ、デジカメは今あるやつの修理見積もりくらいは取ってもいいような気がしてきた。新しいのに乗り換えてしまうほどには酷使していない

Mac は当然 Panther が欲しいが、一年前くらいにも試してみようと思った Mac On Linux にもかなり興味が。今なら Gentoo の完成度の方も気になるし、旧型 Mac を最前線に復活させるいちばんいい方法が LinuxPPC + MacOS9 だと思うし。

Debian PPC じゃないのは Debian は「バージョンアップが全然ない = オフィシャルインストーラが古いまま」なので新規インストール時に若干気を使う。これがどうもあんまり好きになれない。(入ってしまえばどうってことはないと言えるけど。)あと、細かい点で FreeBSD に比べて練れてない感じがする。プロジェクトとして同じ 10周年だけど、どうにも GNU/Linux システムに対する完成度の低さという、自分の(イメージ先行なんだろうけど)実感を払拭することはまだできない。ま、そういう意味じゃ Gentoo はさらに若いんだけども。

その前に説教講座の改修しなきゃ。

About

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