ずっとSSL証明書面倒くさいなーと思ってたんですよ。まぁ今だと Let' Encrypt とか Heroku もそうだけど無料で自動更新みたいなのはあるにはあるんだけど、伝統的なやつをもうちょっと効率化できないかなとずっと思っていたわけです。1
ありました。
cfsslを知る
cloudflare/cfssl: CFSSL: Cloudflare's PKI and TLS toolkit
cfssl を使ってるのを最近よく見ます。https://t.co/VgDlANo6gZ
— Yuanying (@yuanying) January 5, 2018
本当は openssl + expect ? とか openssl -config <template> ? みたいなことを考えていたんだけど、swiss army knife が見つかっちゃいました。
cfsslの使い方
とっても簡単。
Creating a new CSR · cloudflare/cfssl Wiki
にあるように JSON で
{
"CN": <CommonName>,
"key": {
...
},
"names": [
{
"C": "JP",
...
}
]
}
のように定義を準備しておいて、
cfssl genkey <json>
てやると
- パスフレーズなしのprivate key
- CSR
が JSON で出力されます。
このね、 names に当たる部分て基本的に同じ組織なら同じになっちゃうわけですよ。違うのは CN くらいなわけです。これを openssl で interactive にいくつも作るのは超だるいわけです。これが cfssl ならラクショー、いや、いくつも JSON 作るのもダルいので、そこはあとで工夫します。
cfssljsonでファイルを作る
cfssl の出力はあくまで JSON なんだけど、欲しいのは秘密鍵のファイルと CSR のファイルなわけです。
そこで登場するのは cfssljson です。使い方は上の続きで
cfssl genkey <json> | cfssljson -bare <prefix>
ってやると以下の2つのファイルが生成されます。
- <prefix>.csr
- <prefix>-key.pem
そうそう、これが欲しかった。
あとは CSR を出して、戻ってきた cert / intermediate cert と private key をサーバにインストールすればオッケー。
定義をYAMLで書いてrakeで叩く
JSON で定義を書ける、まぁイマドキなわけですが、もっと楽したい。具体的には上の names に当たる部分が共通の SSL 証明書が複数必要、という場合は
YAML で定義を作ってやればアンカーとエイリアスで楽できます
アンカーとエイリアスってのは
Rubyist Magazine - プログラマーのための YAML 入門 (初級編)
こういうやつです。で、例えば Ruby だとこんな感じに
hosts = YAML.load_file(<yaml>)
で定義を取得できるので、この key なんかをもとに
hosts.keys.each {|host|
desc host
task host do
generate_json(host)
sh "cfssl genkey #{jsonfile} | cfssljson -bare #{prefix(host)}"
end
}
みたいなことをやってあげると host ごとに private key と csr を作成する rake task がズラッと揃います。(main のレベルで書かない場合は適宜 Rake::DSL を include / extend してください)
こいつをリポジトリに突っ込んであげれば少なくとも CSR 準備作業はきれいに忘れてしまってもオッケーだと思います。
証明書のインストールとかその辺はまた今度
まずはくそ面倒だった openssl + interactive な key / csr 作成とさよならできたのでよしとします。
ACMはメール + Webでacceptの操作が要るので全自動ではないですね ↩
twitter で TDDBC Hokuriku (2010) のレガシーコード改善を Coding Dojo で行った際の Ruby チームは比較的うまくいってたけど、あれって○○な流れだっけ的な話をしているうちに気になってることをまとめておこうと思い立ったので、できるだけ書き出してみる。
何かのきっかけになれば嬉しい。
素材(レガシーコード)のポイント
- まず動くこと
- 触ったことがあること1
- ある程度でいいので機能別に書かれていること
- オブジェクト指向であるとなお良い(使える技が増える)
- 小規模であること
- ただし完全に単機能だと余地が少ないのでテストを足しにくい
- 外部 API 依存しまくりの場合は単なるレガシーコード改善とはまた別なテクニックの習得に繋がってよいかも
- 自動実行できるテストがないこと :-)
1 については「えっ」て思うかもしれないけど、放置してるものは依存ライブラリの関係や、そもそも動かし方がよく分からない(覚えてない)など、動かないことがよくあるので、素材を事前に用意できるならこの部分をチェックしてあるとだいぶ話が早い。
文字列処理、数値処理などテストしやすい機能が独立していると改善は早いけど、あまりきれいに分離されていると分離するまでに使える技をおみまいできないので、悩ましいところかも。
ただライブで扱う際はどうしても時間的制約が大きいのであまりに難しい題材を扱うのはまず無理だと思う。
事前準備のポイント
素材を用意する人は
- コードの読み込み
- 動作テスト
を入念に行っておくのがオススメ。
特に自分で書いたわけではない(オープンソースなどの)レガシーコードを相手にするのは難易度高めなので事前準備が大切になってくる。
この部分は TDD の知識の有無、経験値は関係なく準備できる。古典的な「読ん」で「動かす」でよい。できれば「こうしたい」という要求が挙げられるとよいけれど、それがイベント内で達成可能なゴールになるとは限らない。
これはそういうもんだと思っておくとあまりガッカリしないで済む。なんでもそうだと思うけど、達成可能なゴールの設定にはある程度経験が必要。
レガシーコード改善手順を復習
レガシーコードをレガシーコードでなくすこと自体が目的ということはそれほど多くないと思う。継続してメンテナンスが必要だけど修正ポイントを見つるのが大変、リグレッションテストのコストが大きすぎて大変、てゆーか影響範囲が判断不能、といった事情があるはず。
そこでテストを付加していくわけだけど、その手順を書き起こすと大きく三つ、
- テスティングフレームワークに載せる
- characterization test から徐々に TDD を回す
- 修正、機能追加やリファクタリングを目指す
に分けられると思う。
サイボウズの記事のようにユニットテストよりも Selenium レベルのテストを重視するという方針ならそもそも話が変わってきてしまうんだけど、基本的には TDD を回すと言った場合はある程度「ユニットテスト可能」な状態を指すと思うし、Selenium 重視になるとテストデータの投入の自動化とかそもそも Web アプリじゃなかったらどうするのとか、クリアすべき課題がさらに増えてしまうので、ユニットテストを付加していく方向で考える方がイベント時の手順として汎用的に使えるだろう。
レガシーコード改善チーム編成のポイント
レガシーコードの改善をイベントでやろうとする場合、
- ペア以上の人数で一つの素材に取り組むチームを作る2。
そのチーム内に
- TDD に慣れている人3
- コードに習熟している人
の両方を置く
という形がベストだと思う。どちらが欠けてもイベントの限られた時間内でゴールに到達するのは難しくなるだろう。
理想的には上に挙げた人が一人ずつのペアを作れると最も速度が出て効果的に学習できるのだが、そんなに大量に TDD エキスパートが揃うとは思えないので、チーム内に一人ずつを目指すのがよいと思う。
ゴールは素材とイベントの規模とチーム編成次第
さて準備が整ったところでレガシーコード改善に実際に取り組むわけだけど、個人的には先に挙げた 3つのどのステップをゴールに設定してもよいのではないかと考えている。
どこをテスティングフレームワークに載せるのか
レガシーコードすべてを一度にテストに載せるのは多くの場合で無理なので、特にどの部分をテストして、修正、機能追加、リファクタリングに結びつけるかを考える。この際、ややこしい依存がいくつもある部分を選んだ場合はテストハーネスに載せるまでの苦労を実践的に味わえる。
現実の「どうにかしたい」レガシーコードの中にはそういうとてもややこしいことになってしまっているものも数多くあると思う。とにかく片っ端からそうしたコードをやっつけていきたいので、まずはここを強化したいと考え、あえてテスト開始の難しい素材でやるという選択もアリだと思う。
というかそういう素材しか見つからない場合だってあり得る。その場合はテスト可能な状態を作るだけでも良しする判断もあるんじゃないか。2〜3時間から半日程度の規模ならここまでで終えてしまうケースも増えると思う。
例えばコンストラクタが頑張りすぎてて依存オブジェクトが多い場合はまず依存オブジェクトを初期化する処理をそれぞれ別メソッドに分ける、外部のライブラリをあちこちで呼んでいる場合は wrapper にまとめる4、本当に果たすべき一つの責務を見つけ出す、継承してテスト用クラスで override して依存を分離する、など身につけたいテクニックはたくさんあるわけだが、多くの場合、一時的にはコードが膨らんでしまうし、この作業に掛かる時間を事前に読み切るのは難しい。
逆にすべてを体験したい場合には
ややこしい依存のないコード、あるいは依存を分離しやすいコードを選ぶ
ことになる。
もう一つ、テスティングフレームワークに載せる部分は修正、機能追加、リファクタリングを行う部分なので、2番目以降のステップに繋げやすいという要素も重要になってくる。
例えば機能としては独立しているのでテスティングフレームワークに載せるのは比較的容易だが、外部あるいはブラックボックスの API 呼び出しだらけとかメソッドがちゃんと入出力を持つ関数として機能せずに副作用だらけで動くようなものは TDD の入門素材としてはあまり適していない。(無理じゃないけど。)
TDDBC Hokuriku 2日目の Coding Dojo Ruby チーム は比較的うまくレガシーコードの改善が進んだのだが、このときの決め手の一つは t_wada の
「パーサがありますね! パーサいいですね! パーサにしましょう!」
だったと思う。この決断は TDD への習熟から生まれてきたものだろう。
characterization test から徐々に TDD を回す
レガシーコードはテストコードのないコードなので、当然機械的に確認可能な仕様が存在しない。仕様書はあるかもしれないが、それが現在の動作と合っている保証はない。そこでまず現状把握のためのテストを書く。これを Characterization Test と呼ぶ(『レガシーコード改善ガイド』第13章)。テストするのはもちろん変更を加えたい部分とその周辺。
これがすんなりうまく行くようならしめたもの。その後の修正、機能追加、リファクタリングに集中できる。
しかしたぶんそんなにうまくいかない。そのメソッドが想定通り動く条件をすぐに満たせるかどうかが分からない。インスタンス生成はうまくいってもメソッドの呼び出し時にはいきなり例外が上がる可能性もある。また、目的のメソッドは副作用を基本にしたメソッドかもしれない。そうなると検出用変数の追加など、テストなしで加えなければいけない変更が出てくるかもしれない。
目的のメソッドをテストできるようになったらようやくそこがスタート地点。境界値など一般的なテストを書いて仕様を確認していく。
「現場」においてはこの段階は次のステップへの繋ぎなのでゴールにはならないのだけれど、これもイベントにおいては状況次第かなと思う。次のステップへ進むために遠回り(目的とは異なるメソッドのテストや stub out など)が必要な場合も考えられる。そうなってしまうとどこでその遠回りが終わるかの予想が難しいこともあり得る。
修正、機能追加、リファクタリングを目指す
修正、機能追加を行うためにはその部分をテストでガードしましょうというのが TDD の基本である。(それ以外の部分もガードできないと本当はダメだけど、そこは置いておく。)ということでガードできたところからプロダクトコードをいじり始める。ただし、レガシーコードをいじる場合、
影響範囲が閉じていて簡単に変更できるなんて平和なことは少なく、
- 影響範囲は小さいがメソッドは巨大で if や case の嵐
- 一つのメソッドで多くのインスタンス変数やメソッドを呼び出している
なんてことがザラにある。要するに
レガシーコードはクラスやメソッドの仕事が多すぎる傾向にある。
方針としては
仕事(責務)を一つに減らすことが理想
と言えるが、これは恐らくそれほど簡単には実現できない。つまり、リファクタリングをゴールにする場合は結構時間が掛かることを覚悟しなければいけない。
だから修正や機能追加をゴールにする方が時間設計上は楽だと思う。
それでもリファクタリングしたいという場合、「リファクタリングのための準備」くらいで我慢しなければいけないこともあり得ると覚悟しておこう。例えば
- 不要なコードは削除する
- メソッド丸ごと別クラスに分離する
- if, case の嵐は実際の処理を別メソッドに分けて、override できるようにする
など。
上の文字だけを見ると単純な話のように見えるけど、これはこれでちゃんと準備して施術する必要があり、かつ効果は予想以上に大きい。場合分けすると複雑で長くなってしまうコードも、override を前提にすれば個々の処理はそれほどでもない場合は多いし、独立クラスに追い出してしまうと結果的にコードの長さが長くなったとしても、可読性が高く短い処理に分割しやすくなる。
また試行リファクタリングも効果的。これは実際には捨ててしまうことを前提に理想的なコードを組んでみることで、クラスやメソッドが本来果たすべき責務を明らかにするために行う。ただしこれを踏まえて作業できるとは限らない。非常に根の深い問題を抱えている場合は試行リファクタリングで見つけたような気がした理想的な状態をいったん捨てて5、地道に取りかかれるところだけを作業しなければいけないこともあり得る。実際にできあがったコードだけを成果として捉えてしまうとこれはとても寂しいが
できないことが分かる
ことも成果である。
まとめ
個人的には TDD のキモの一つは依存の分離だと思う。テストしにくい依存はどんどん分離してテスト対象の動作に集中して回転を上げていく。これができるからこそ TDD はリズムが良く、小さなミスの発見、小さな設計変更、小さな達成をくり返すことができる。
これはレガシーコードを相手にする際により大きな意味を持つ。レガシーコード特有の問題ではないが、レガシーコードは往々にしてブロック6が大きすぎ、それぞれの責務が曖昧で名前もぼんやりしている傾向にあり、依存ライブラリを含めて本番と同じ動かし方しか想定していないので分離の難しい傾向にある。だから TDD で書き始めたコードよりも
大胆な依存分離テクニックと発想、そのための調査
が大事になってくる。
自分にはそれほど多くのレガシーコード改善経験があるわけではないが、レガシーコードとの戦いに有用な依存分離テクニックは伝統的なオブジェクト指向の教科書的な方針とぶつかることがあることは知っている。しかし多くの場合で教科書を補足する原則には反していない。つまり、実はオブジェクト指向の周辺知識がとても有効な武器になる。
レガシーコードとの戦いはつらい。だがレガシーコードを切り刻んで TDD 可能なコードに改善する作業も楽ではない。これは総力戦だと思う。レガシーコード改善のためには TDD への習熟も必要だし虫食いの知識を補強する必要もある。そしてこれをイベントで扱う場合は「楽しかった!」「勉強になった!」と簡単に言えないケースも想定しておくことが大切だと思う。
最後のまとめの発表では「ここまでしかできなかった」という気持ちにならないこと。「この課題はここが難しかったがここまで進むことができた」と締めくくろう。
あ、『レガシーコード改善ガイド』はオススメですよ。これを読むと
勇気が手に入る。
諦めていたレガシーコードともう一度戦ってみようと思える。同時に、何が大事なのか、捨ててよいものは何かを考える契機にもなるし、戦って負けてもそれはそれで別な方法で再戦しようと思えるようになる。
自分が段階的なゴール、段階的な成果を前提にこのエントリを書いているのもこの本と TDDBC Hokuriku (2010) の影響だと思う。
ありがとう。
参考
例によってまだ実験段階ですが、背景から整理しておきます。
- サーバの仮想化が、新しいサーバを新しい OS でいくつもホストすることではなく、migration をより少ない物理コストで実現するために必要
- 例えばゲストOS には RedHat 7.3 などの古いものがある
- 利用できる物理ホストは x86系
- 利用できる物理ホストのプロセッサは仮想化支援(IntelVT とか)を行ってくれない
ものとします。現段階で情報が十分揃っていてそれなりの速度で動き、手軽に始められそうなのは
- Xen
- VMware Server
なわけですが、今回は
- プロセッサの仮想化支援もなく、古い OS を利用する1関係上、Xen は手間が増える
- 内部向けのサーバなので VMware のライセンスで十分
ということで VMware を採用しようと考えています。
VMware のライセンスは日本語訳されたものを探して右往左往してしまいましたが、どうやらまったく存在しない模様。しょうがないので飛ばし読みかつ斜め読みしてみたところ
- 内部向けの利用なら ok
- 外部のネットワークを通じていても内部向けなら大丈夫そう
- プロセッサパワーやアプリケーションサービスを売ったりリースしたりしちゃダメ
- ベンチマークを公開したけりゃ VMware のレビューを受けてね、連絡待ってるわん
ということらしい。これなら今回の目的に利用するのは問題ない。
cf. リモートから利用可能なサーバー向け仮想化ソフトウエア:ITpro
幸いカスタマイズ不能なプロプラ OS はないが ↩
某blog へのコメントに自分はこう書いた。
ただ自分はコミュニケーションはこうしたコメントなどより ML の方が好きですね。記事とコメントという区別がないので全部フラットに検索できますし、込み入ってくるとコメントやトラックバックでは煩雑ですから。
普段感じていたことなんだけど、改めて言葉にすると自分の言葉なのにあぁなるほどと思う。ということでちょっと ML の特徴だと思っていることを挙げてみよう。
- 内容がメールで届くので、すべて手元のコンピュータ上で完結している。
- Web サイトを見に行く必要がないし、まして TrackBack は複数のサイトを見ていかないと行けないので煩雑だし、議論を振り返るのも大変。
- その点 ML はすべてローカルのコンピュータだけで処理できるので楽。
- (パソコン通信の頃は ML も BBS も情報の保存されている場所が違うだけで大きな違いはなかったんだけどね。)
- ML では記事、コメントという区別がなくすべてのメールは平等なので検索するときに偏りがない。(普通、blog の記事検索にコメントは含まれない。)
- ML は実際に使うメールアドレスを登録する必要があり、お互いのメールアドレスが丸分かりになる。以前は実名やメールアドレスをオープンにしたコミュニケーションは当たり前だったが、今はそういう時代ではない1ので、心情的な面で現代的ではないような気がする。
- 行儀の悪いメールソフト、ちゃんとスレッドを追いかけられないメールソフトがあるのでみんなが便利に使えるとは限らない。
- そうしたソフトのシェアが高いのでそもそもスレッドを追いかけられることを知らない人がかなりたくさん居る。
- おかげで本来とても便利な機能なのにまったく役に立たないことが多くなる。スレッドがぶちぶち切れたり、とんでもないところにリプライしてメールを書き始める人が出てきて困る。
また ML とは限らないけど最近のメールの特徴として
- メールを勝手に携帯に転送しておいて夜中に ML にメールを投げるなとか、緊急でないならメールを投げるなとかいう勘違いした人が居る
のは困りものですな。無知は罪です。
- 全文引用をやめられない人たち、全文引用が正しいと教育されている人たちが居る
これが不思議なんだけど、どうもスキルのないビジネスパーソンの間では「全文引用をしてあげることで議論の流れを振り返りやすくする」とか「やりとりの証拠にする」という意味があるようです。これ困るんだなぁ。軽く反論すると
- ちゃんとスレッドが活用できていれば全文引用なんか不要だし、全文引用があちこちのメールについていると検索するときにノイズが増えるので逆効果2
- 全文引用したあとに「はい」とか「反対です」とか単語を書く人が中に居る。そういうことをしている人は自覚がないだろうし、上のように全文引用した方がよいと考えている人には分からないだろうけど、こっちはちゃんとメールは最後まで読むもんだと思ってるし、全文引用で終わっているのかその後何かあるのかとても心配になる3ので勘弁してください。全文引用は罪です。
- 引用なんか恣意的に変更加えられるんだから、全文引用したって証拠にはならない。あくまで「補助」。補助なのに全文引用が本文より長いなんてことがザラになるのはおかしいやろ。つーか著作権的にもまずいんちゃいますかとか別な心配してしまうよ。
こうして振り返るとメールは Web に比べて多少スキルが必要なようです。PC の時計を正しく合わせることすらできない人も居るわけで、たったそれだけのことで受信メールが整理しにくくなるってのはやっぱちょっとシステムとして完成度低すぎるというか人間に頼り過ぎなんでしょうねぇ。まぁ最近は ML + Web の連動システムを比較的容易に構築できたり、無料のサービスもいくつかあるので、自信のない人は Web からやってくれってことにすればいいのかもしれませんが。4
ほんとはとても便利なんですけどねぇ < ML
先日、「blog ってなんですか?」「2ch ってなんですか?」という人と飲む機会があった。(別にその説明をするために飲んだわけじゃないけど。)そのとき自分がどういう風に説明したかを思い出してみる。
2ch
インターネット(= WWW)そのものである。どこの誰だか分からない人が自由に1書き込みできる大量の分野別の掲示板群の総称なので、中にはとても良い情報もかなり有害な情報もあり、それはインターネットそのものの性格を凝縮したようなものだ。
blog
「サイトを見ててもどれが blog でどれがそうでないのか分からない」という話だったので、「分かる必要はない」と答えておいた。しかし blog は 2ch のように「掲示板」という予備知識を利用できないので説明が難しい。
- 仕組み的には「更新を楽にしたもの」なので見た目で分かるものではない2
- 内容的にはいろいろなものがあり、日記だろうが趣味の話だろうが blog は blog
- 酔った勢いで海外ではマスコミよりも良質なジャーナリズムがうんたらって話があったとか日本ではその前に日記ブームがあったとか、JBA が痛かったとかって話をまくしたてて、最終的には「気にするものではない」とひどいことを言ってお茶を濁してしまった。
難しいなぁ。確かに流行ってはいるが、ただ見る分にはどうでもいいことだもんな。書き手になると気づく面白いことってのもあるんだけど、ただの読者への説明には使えない。
あと、自分の趣味がそうだからだろうけど、Web日記も blog も技術ドリブンだなぁと思った。RSS とか TrackBak とか、知ってれば楽できたり面白いと感じる部分もあるけど、別に知ってる必要は全然ない。じゃー blog って何? と言われると、、、うーん。今度から「別に普通のホームページだよ。」と言っておくかな。
ちなみにその人は
それと分かっているかどうか不明だけど Wiki を利用して情報のストックを行っている。(お膳立てはこっちがやったけど。)ハタから見ると blog だの Wiki だのってのはどうでもいいんだなぁ、と感じる出来事であった。
『美しき日々』の BGM を聞くと「クリーンビーチいしかわキャンペーン」を思い出してしまう。。。
cygwin を使う場合、へなちょこ cmd.exe を避けて賢い telnet や ssh クライアントで作業するってのは、たぶんイマドキの cygwin 環境としてはかなり常識の部類に入る話でしょう。
ところで、この telnet / ssh 経由で cygwin を使っている状態で、Win32 GUI アプリを起動することはできないのでしょうか? 試してみたところ、プロセスは起きるが、正常に Win32 アプリとして動作してくれていません。はて?
もちろん Win32 アプリは他の Win32 ネイティブなランチャなどで起動するってのが行儀がいいんでしょうが、リモートの、 Terminal Server じゃない Windows を、cygwin を利用することでコマンドラインで操作するってのは無理なんですかね? 使えたら嬉しい人、案外いるんじゃないかなぁ。
借り物の OS X マシンを返しちゃったので、もう試す環境がないのです。そしたら Safari は出るわ、X11 の Public Beta は出るわ、なんだか OS X 周りが賑やかじゃん!
ちくしょー。
試しました。確かに従来の OS X 上のどのブラウザよりも劇的に速いです。あの Chimera もトロく感じます。こりゃすごい。
…が。やっぱ予想通りそこかしこで CSS 周りのバグがあります。まー今は Public beta だから、、、とも思うけど、フォント関係の設定項目などが増えたり使い勝手の部分で改善が入ってもエンジンの改善はあまり行なわれないんじゃないのかなぁ。落ちたりするわけじゃないし。CSS厨以外にはそれほど致命的なことじゃなさそうだ。
Mozilla 1.0.1 にそこかしこで不満が出てきたので Netscape 7.0.1 と Mozilla 1.2.1 を両方入れることにしました。
ところがこいつらは同じユーザー情報を参照にいくのです。いやね、完全に互換性があるなら同じプロファイルを使えた方が便利なのですが、Netscape 7.0.1 のベースは Mozilla 1.0.1 + αで、1.2.1 とはけっこう大きく食い違います。見た目に顕著な例では適用できる Theme が異なります。そうすっと、Netscape 用と Mozilla 用で異なるプロファイルを用意しないといけません。
異なるプロファイルがあるってことは、起動時にいちいちプロファイルを選択せにゃいかんくなってしまいます。実際、これまでずっと起動時にプロファイルを選択していて「面倒くせぇなぁ。でもしゃーないか。」と思っていたのです。
が、よー考えたら起動時にコマンドラインオプションでプロファイル指定しておけばいいだけじゃん! …こんなことに気づかず放置し続けてたなんて。