プログラマ行進曲第二章

主にソフトウェア関連の技術をネタにした記事を執筆するためのブログ

『エンタングル:ガール』を読み終えました

最近本を読めているので、この調子で書評記事を書いていきます。

今回扱うのは『エンタングル:ガール』です。

エンタングル:ガール (創元日本SF叢書)

エンタングル:ガール (創元日本SF叢書)

『エンタングル:ガール』は2006年に放送されたアニメ『ゼーガペイン』のスピンオフ小説で、舞浜南高校に通う映画監督志望の高校一年生、カミナギ・リョーコが主人公の青春小説です。

原作であるアニメ『ゼーガペイン』はロボットやSF要素が強いながらも切ない感情を呼び起こすストーリーを持ち、未だにコアなファン人気を誇る作品ですが、この『エンタングル:ガール』はアニメ『ゼーガペイン』が持っていた青春群像劇の要素を強調して押し出した作品となっています。

『エンタングル:ガール』の大まかなストーリーは、映画コンテストに出す映画を撮るため、主人公のカミナギ・リョーコが監督となり、舞浜南高校に伝わる七不思議を軸にした映画を舞浜南高校映研のメンバー+出演者で作るというもので、その中で徐々に明らかになる人間関係や世界の謎に触れるという流れになってます。

読んでみての感想

最近小説を読まなくなっている身としては、久し振りの真っ当な青春小説だったので、読後感は爽やかな気持ちになってよかったですね。

私が熱狂的な『ゼーガペイン』ファンということもあり、大分評価が甘い可能性はあるのですが、SF要素にあたる話や『ゼーガペイン』の要素を前提にした話は最後の方にしかないので、アニメ『ゼーガペイン』を知らない人でも読める作品になっているのでは?と思います。

個人的によかったポイントは、非常に読みやすい日本語だったという点です。

『三体』を読んだときの記事にも書いたのですが、小説に限らず、本を読んでいるときに読みにくい日本語で書かれていると、相当の本好きでも無い限り、限られた時間を使って読んでいるので、読み通すのに障害になってしまうので、この読みやすさはよかったです。

この読みやすさは著者の高島雄哉氏の持ち味なのかどうなのか、私は分からないですが、これだけ読みやすいならば氏の他の著作(『ランドスケープと夏の定理』など)を読んでみようかなという気になりました。

余談

この『エンタングル:ガール』の出版を記念したトークショー&サイン会が8/31に開かれたのですが、当初予定が無いから行こうかなとTwitterで呟いていたら、ありがたいことに著者からアドバイスをもらいました。

ただ、ここで残念だったのが、確認不足で8/31には用事があってそちらを優先したかったので、このトークショー&サイン会には行けず、上記ツイートの返信もしないまま今日まで来てしまいました。

放置したままなのもどうかと思いつつ、だからといって返信をするには随分日が経ってしまったので、返信の代わりに読書感想の記事をこうやって書いたわけです。

少しでも著者の手元に何か届けば幸いです。

Xcode 11が正式にリリースされたのでXcode 11のベータを消したらgit周りでエラーが出たため対処した

タイトルの通りで、Xoce 11が正式にリリースされたので、今まで入れていたXcode 11のベータを消した後、gitのコマンドを実行しようとしたら以下のようなエラーが出ました。

xcrun: error: active developer path ("/Applications/Xcode11-beta.app/Contents/Developer") does not exist, use xcode-select to change

出たエラーメッセージをちゃんと記録してなかったので細かい文言は違うと思いますが、こんな感じのエラーでした。

which git した結果は /usr/bin/git だったので、Xcode付属のgitを使っていて、Xcode11 ベータを消したことでだめになっている模様。

普段Xcodeをヘビーには使わないのでどうやって解決するのがいいのかとググったところ、以下の記事を見つけて同様の対処をすれば解決しました。

atsumo.hatenablog.com

根本原因を理解せずにやっているのはあまりよろしくないとは思いつつ、当面の問題を回避するのによさそうだったので実行しました。

上記記事と同じことの繰り返しになりますが、やったことは以下の通りです。

まず、 xcode-select --print-path を実行して、現在のxcode-selectのパスを確認。

xcode-select --print-path
/Applications/Xcode11-beta.app/Contents/Developer

実際に使いたいのは Xcode.app なので、それを戻す。

sudo xcode-select --reset

再度パスを確認する。

$ xcode-select --print-path
/Applications/Xcode.app/Contents/Developer

確認した後、gitコマンドを使ってみたらエラーが出なくなったのでこれでひとまず解決。

別のことを調べていたらGo言語のx/crypto/sshパッケージがOpenSSH形式のパスフレーズ付き秘密鍵にまだ対応できてないことに気づいたという話

業務中に調べていたら発見したことなのですが、発見した後調べてみたら既にクラスメソッドさんの記事でも触れられていたことだったのでまとめる意味あるかなと思いつつ、自分で調べて突き止めたことなので記録に残しておいた方が自分の実力の証明になるかと思い、久々に技術系の記事を書いてみます。

dev.classmethod.jp

dev.classmethod.jp

TL;DR

この問題に気づいたきっかけ

もともとgolangのアプリを書いて気づいたわけではなく、KubernetesのCI/CD周りのエコシステムを調べている中でArgoCDの調査をしており、ArgoCDにSSH private keyを使ってgithub等のprivate repositoryを登録するのを試していたところ、ArgoCDのCLIがエラーを出したので、まずArgoCDのrepositoryにissue登録をしました。

github.com

issueにも書きましたが、出たエラーはこんな感じです。

$ argocd repo add git@github.com:<my private repository> --ssh-private-key-path ~/.ssh/id_rsa
FATA[0000] ssh: cannot decode encrypted private keys

そしてissue登録をした後にdelveでデバッグをしながらArgoCDのソースコードを見たところ、まずこの箇所で引っかかっていることが分かり、step inしながら調べたところ、ssh.ParsePrivateKey関数にしか対応していないことが分かったので、単にArgoCDの問題かと思って一旦放置していました。

ただ、少し経った後「別に技術調査中なんだから、雑にこの箇所をssh.ParsePrivateKeyWithPassphrase関数を使うように書き換えたCLIをビルドして試すのはありなんじゃないか?」と思ったので、雑に書き直した後ビルドして試したところ、またエラーが出てきて「あれ?」と問題に気づいたという流れです。

x/crypto/sshパッケージ自体に問題があると確信するためにした検証の仕方

上記の流れでArgoCDのCLIの挙動をdelveを使って追ったり、x/crypto/sshパッケージのソースコードを読んだりして、細かい検証をする前にx/crypto/sshパッケージ自体に問題がありそうだと思ってはいたのですが、VSCode経由でArgoCDのCLIをdelveでデバッグしていたときの変数の中身の表示がおかしかったりして、本当にx/crypto/sshパッケージの問題なのか他者に証明しづらかったので、ssh.ParsePrivateKeyWithPassphrase関数の挙動がおかしいことを示すコードをgistに残し、挙動を確かめたところ、やはりssh.ParsePrivateKeyWithPassphrase関数自体が手元のssh private key(パスフレーズ付き)をパース出来ないことが分かりました。

確認するのに使ったコードなどは上記のgistに全て書いているのですが、リンク先に飛ぶのも面倒なので以下に同内容のコードを説明付きで書いていきます。

使用したパスフレーズ付き秘密鍵を生成したOpenSSHのバージョン、秘密鍵の状態、Go言語のバージョンは以下の通りです。

$ ssh -V
OpenSSH_7.9p1, LibreSSL 2.7.3

$ ssh-keygen -l -f ~/.ssh/id_rsa
2048 SHA256:/<masked> taku@altair.local (RSA)

$ go version
go version go1.12.6 darwin/amd64

この環境で以下のGoのコードを実行します。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "os"

    "golang.org/x/crypto/ssh"
)

func main() {
    sshPrivateKeyPath := os.Args[1]
    keyData, err := ioutil.ReadFile(sshPrivateKeyPath)
    if err != nil {
        log.Fatal(err)
    }
    signer, err := ssh.ParsePrivateKey([]byte(keyData))
    if err == nil {
        fmt.Println(signer)
        os.Exit(0)
    }

    passPhrase := os.Getenv("PASSPHRASE")
    if passPhrase == "" {
        fmt.Println("Please configure `PASSPHRASE` environment variable for your passphrase")
        os.Exit(1)
    }

    signer, err = ssh.ParsePrivateKeyWithPassphrase(keyData, []byte(passPhrase))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Parsed private key with passphrase!!")
    fmt.Println(signer)
}

雑なコードですが、PASSPHRASE環境変数が正しく設定されていて、実行時にパスフレーズ付き秘密鍵のパスを指定していればssh.ParsePrivateKeyWithPassphrase関数を実行するようになっていて、signerが表示されるのが期待する動作というコードです。

これで意図通りに動くのか以下のように確かめました。

# ~/.ssh/id_rsa がパスフレーズが無い秘密鍵の時
$ go run main.go ~/.ssh/id_rsa
&{0xc0000ac120 0xc0000ac120}
# 事前にファイルを使って export PASSPHRASE="<~/.ssh/id_rsa のパスフレーズ>" と下準備して、パスフレーズ付き秘密鍵を使用する時
$ go run main.go ~/.ssh/id_rsa
2019/07/11 23:25:03 ssh: cannot decode encrypted private keys
exit status 1

上記のように、パスフレーズ付き秘密鍵の時にはエラーとなり、期待通りに動いていないことが分かりました。

このエラーが「SSH private key側の問題」なのか、「ssh.ParsePrivateKeyWithPassphrase関数の問題」なのかを調べるため、delveを使って上記Goのソースコード実行時の状態を見たところ、以下の箇所でエラーが出ているところまで突き止めました。

詳しくはgistに残したコメントを参照して欲しいのですが、ここまで調べた段階で個人的には「エラーハンドリングが甘い(=ssh.ParsePrivateKeyWithPassphrase関数の問題)のでは?」とほぼ確信はしていたのですが、普段扱っているわけではないgolangの話だったので、社内Slackの #golang チャンネルにgistのコードを添付して質問して他の人にもチェックしてもらったところ、やはりssh.ParsePrivateKeyWithPassphrase関数の問題だと言われました。

自分で調べているときにも見つけてはいましたが、上記Slackチャンネルでも「以下issueに関係ある話なのでは?」と指摘され、ほぼ間違いなくx/crypto/sshパッケージ自体の問題だと判明。

github.com

何が問題だったかというと、OpenSSH 7.8からssh-keygenで生成される秘密鍵のフォーマットがOpenSSH形式になったのに、x/crypto/sshパッケージではまだそのOpenSSH形式のフォーマットのパスフレーズ付き秘密鍵をサポートできていなかったことがこのエラーを引き起こしていたのです。

自分自身ではこのOpenSSHのバージョン違いによる秘密鍵のフォーマットの差には気がつかなかったので、教えてもらえたことで最終的な原因に気がつけてよかったです。

教えてもらったときに参考に貼ってもらった記事は以下のものがありました。

qiita.com

検証後に気づいたことと感想

こういう風に色々調べきった後に改めて調べ直していたら、本記事の頭に書いたように既にクラスメソッドさんの記事でこの問題に言及されていたことに気づきました。

最初の方で見つけられていれば原因特定が早くなったと思いますが、今回自力で大半の原因を特定でき、分からない所は分かりそうな人に質問して特定するといったフローを実際に実行でき、経験が積めたので、今回はこれでよかったのかなと思います。

余談になりますが、このOpenSSH形式の秘密鍵をgolangで扱いたい場合は、x/crypto/sshに関するissueでコメントされているように、ScaleFT/sshkeysという3rd party製パッケージを使えば出来るみたいです。試したことはないので保証は出来ませんが。

golangの場合、delve(+VSCode)によるデバッグが非常に強力だというのも実感しました。またgolangを使ってデバッグをすることになったら今回の経験を活かしたいと思います。

*1:使ったことがないので憶測です

話題になっているSF小説『三体』を読み終えました

表題の通りで、中国のSF小説『三体』を読み終えました。

評判に違わぬ面白さで、「面白い小説を読みたい。でも自分にとって面白い小説見つけるのは難しい」と思っていた私にとっては、評判に釣られて読んでみてよかったと思います。

そもそも『三体』とは何か?

『三体』とは中国で爆発的にヒットし、海外でも人気となったSF小説です。

三体

三体

私がダラダラと書くより、版元の早川書房さんが書いている以下の記事を読んだ方が分かりやすいですね。というか私も購入前に以下の記事を読んで購入するかどうかの参考にしました。

www.hayakawabooks.com

『三体』を読み始めたきっかけ

会社のSlackのチャンネルに読書好きな人向けのチャンネルがあり、そこで話題になっていたというのが一番のきっかけでした。ツイートした時期を見ると7/10には購入して読み始めたようですね。覚えてないですが。

上記のSlackチャンネルはあまり活発というわけではないのですが、普段から本を読んでいる人が面白かった本を書き込んだりするので、最近情報収集する手間をかけてない私としては積ん読本の候補選びの参考にしています。

今回の『三体』の場合、1人だけでなく、複数人が読み始めて話題にしていたので、その流れに乗ろうと私もAmazonのKindle版購入ボタンをポチッとして読み始めました。

あと、多分に早川書房さんとか書籍関係者、読者の人がこぞって『三体』に関する投稿をネットに投げ込んでいて話題になっているのを見かけたのもきっかけの一つです。

私のようにネットでの話題に影響された人も多かったためか、発売してから一ヶ月も経たずに10万部を突破していたようです。現時点だとさらにどれくらい売れているのか調べていませんが、SFという枠を超えて日本の市場でベストセラーの仲間入りしているのは間違いないでしょう。

www.hayakawabooks.com

『三体』を読んでの感想

私が『三体』を読み終わったのはつい先日なのですが、その時の感想ツイートがこちらです。

読み始めたのが7/10で、途中時間をかけられなくて読み終えたのが9/4と、読了まで2ヶ月近くかかっていることになりますが、最近の私の小説を読み進めるスピードと勢いを考慮すると、これはかなり速いスピードで読み終えていて、それだけこの『三体』という本が面白かったんだなと改めて思いました。

普段練習もしていないので『三体』の面白さを紹介するにはどうしたらいいかよく分からないですが、もしここまでの文章を読んでみてまだ『三体』を読んでない & 梗概レベルの展開を知っても問題ないという人は、『三体』の翻訳に関わった大森望氏のあとがきを読んでみると、『三体』の面白さとその話題性・立ち位置などが見て取れるので、以下の記事に目を通してみるといいと思います。

www.hayakawabooks.com

上記引用したあとがきで大森望氏が記されているように、日本語版『三体』は翻訳に非常に力が入っていて、どれくらい入っているかというと、「原文が中国語で書かれて翻訳されたとは思えない。最初から日本語で書いてある小説みたい」という感想「しか」出てこないくらい自然な日本語訳になっていて、読みやすさが翻訳物なのに段違いにいいというのが個人的に一番好印象なポイントでした。

学生時代英米文学専攻の学科にいて、英語限定でしたが原文と翻訳をそれなりに嗜んできた経験からすると、だいたい翻訳された作品は文体が自然な日本語であってもどことなく「翻訳しました」という匂いが抜け切れてない箇所が発生し、そこが読書の際に引っかかるということが多かったのですが、この『三体』の場合はそれが全くないのが驚きでした。

これには『三体』がSF小説で、たとえ舞台が中国だったとしても内容が越境的になっていたということと、上記引用した大森望氏のあとがきで触れられているように、大森望氏が「翻訳原稿をもとにして、現代SFらしくリライトするというフィニッシュ・ワーク」するという工程があったことの産物なのだと思います。

個人的な推測を語ると、この翻訳物とは思えない読みやすさが『三体』の爆発的なヒットの下支えになっていたのだと思います。

相当の本好きでも無い限り、普段の生活の忙しさに巻き込まれると、いくら読んでいる本が面白くてもちょっとした障害に引っかかるだけで読み通すのが不可能になると私は思っていて、翻訳物特有の「ここ、翻訳した箇所です」といった読みにくさはそういった障害になって読書を妨げることが私の場合多かったので、その読みにくさがないことは『三体』を最後まで読み通す助力となり、その結果ネットに感想を書きやすくなるといった流れが生まれたのかな?なんて思ったりしました。

とまあ、ここまで読みやすさについて書いてしまいましたが、中身は普通に面白く、私みたいなにわかSF好きな人間でも楽しめる間口の広さはありつつ、(おそらく)本格的なSF(なのだろうと思える)描写がふんだんに話を彩っており、早く続きが読みたくなる。『三体』とはそういう作品でした。

今回翻訳された『三体』は3部作のうちの1部のようで、原語である中国語と翻訳である英語版では既に3部まで出版されているとのこと。日本語訳は第2部が2020年に発売予定とあとがきで書かれていたので、今から楽しみ…というか待ちきれなくて中国語版か英語版で先に読み始めようかなと思うくらいです。

『三体』、オススメです。

『ホモ・デウス』上巻を読み終えました

前回の書評記事から大分期間が経ってしまいましたが、読書自体は細々と続けているので、久し振りに書評記事でも書きたいと思います。技術ネタで書ける記事はたくさんあっても正確に書こうとすると労力が必要で疲れてしまうので。

今回扱う書籍はこちらです。

ホモ・デウス 上: テクノロジーとサピエンスの未来

ホモ・デウス 上: テクノロジーとサピエンスの未来

前回の記事で紹介した『FACTFULNESS(ファクトフルネス)』と同じく、こちらも話題の書籍なので、普段読書を嗜まれている方はもう既に読んだ人も多いかと思います。

私はこの著者、ユヴァル・ノア・ハラリ氏の前作である『サピエンス全史』は読もうと思っていながら読むタイミングを失っていたのですが、今作は良いタイミングだと思って読み始めることが出来ました。あと、「『サピエンス全史』は人間発祥の時代から今までを語った書籍、『ホモ・デウス』は今の時代からこれから先のことを語った書籍」という話を聞いていたので、「どうせ読む時間もそんなに捻出できないのなら、過去のことを学ぶよりもこれから先の話を知る方を優先してみるか」と思い、『ホモ・デウス』を読み始めました。

読んでみての感想

内容が大分骨太なので読み進めるのに苦労しましたが、ビジネス書ではない教養的な書籍でこんなにワクワクする書籍はここ数年無かったので、面白く読めましたし、下巻も楽しく読んでます。

私の理解する限り、『ホモ・デウス』上巻に書かれていることの骨子を箇条書きにすると以下のようになります。

  • 人類は有史以来、3つの問題に悩まされてきた。その3つとは飢饉と疫病と戦争である。
  • 人類は有史以来、この3つの問題を解決することが出来ていなかったが、現在に到達し、ようやくこの3つの問題を克服しつつある。
  • 人類の歴史において、飢饉と疫病と戦争が取り組むべき問題のリスト上位にいたが、これらを克服しつつあるということになると、次は何に取り組めばいいのか?

まず最初にこの切り口から入っていく書籍なのですが、筆致の巧みさなのか、それとも論理的な筋書きのなせる技なのか、とにかく筆者が主張する流れが理解しやすいように書かれていて、著者のユヴァル・ノア・ハラリ氏はすごい頭のいい人物なんだなと思いながら読んでました。

極論してしまえば『ホモ・デウス』は著者のユヴァル・ノア・ハラリ氏が考える未来予測にしか過ぎないのですが、彼が出す主張をサポートするデータなり歴史を踏まえて提出される主張を見ると、「この未来予測は本当にそうなりそうな予測だ」という強い予感を感じさせるものが多く、ついつい納得してしまいます。

上記の切り口から語られるにつれ、「老化と死を克服した人類は、次に神性を求めるようになり、自身をホモ・サピエンスからホモ・デウスに変えることを目指すだろう」といった、一見すると突飛に見える内容も思わず納得させられてしまうくらいに説得的ですし、個人的には刺激的な内容が溢れていて読んでいて面白いです。

下巻も早いところ読了して、記事にしたいと思ってます。

Ansibleの変数の優先順位を理解してなくて詰まってしまったという話

TL;DR

基本的に以下の記事の方と同じハマり方をした、という話しです。

qiita.com

詰まった箇所

具体的に言うと、既存のansible roleを使ったplaybookを修正する必要があり、その際デフォルトで以下のようなフラグ変数を用意して、JenkinsのLTSを通常使用するようにしようとしました。

jenkins_lts_use: true

そして、LTSを使用しないサイトでは jenkins_lts_use: false とやって、この変数の値に応じて別の変数(yum repositoryのURLなど)を分岐させるという作りにして動作検証していたところ、 jenkins_lts_use: false とhost_varsに書いたのに上書きされない状態で「何故だ!?」と時間を消費することに。

ちゃんとdebugモジュールを使って変数の値が変わったかどうか確認したところ、私の意図とは違い、 jenkins_lts_use: true という値のままでした。

原因

「これはすっかり忘れてたけど、多分ansibleの変数が使われる優先順位の問題だろうな」と思ったのでインターネットでググったとこ ろ、この記事冒頭で紹介した記事を見つけ、ビンゴでした。

公式ドキュメントでは以下の箇所にあたります。

docs.ansible.com

Here is the order of precedence from least to greatest (the last listed variables winning prioritization):

  • command line values (eg “-u user”)
  • role defaults [1]
  • inventory file or script group vars [2]
  • inventory group_vars/all [3]
  • playbook group_vars/all [3]
  • inventory group_vars/* [3]
  • playbook group_vars/* [3]
  • inventory file or script host vars [2]
  • inventory host_vars/* [3]
  • playbook host_vars/* [3]
  • host facts / cached set_facts [4]
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (defined in role/vars/main.yml)
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • include_vars
  • set_facts / registered vars
  • role (and include_role) params
  • include params
  • extra vars (always win precedence)

冒頭の記事とこの公式ドキュメントの記述を見た後、私が追加したデフォルト変数はどこに追加したものか確認したところ、role defaults ではなく role vars (defined in role/vars/main.yml) でした。

jenkins_lts_use: false を追加したのは playbook host_vars/* だったので role vars (defined in role/vars/main.yml) より優先順位が低く、この影響で変数が上書きされない状態だったと言うことが分かりました。

jenkins_lts_use: truerole defaults に書くようにして問題解決。

一応対処出来てよかったです。

Ansibleのwhen節で複数行に複数の条件を書くやり方に迷ったので調べた

結論から書くと、以下のように書ける。

when: >
  ('redhat-stable' in repo_url) or
  ('redhat-stable' in repo_key_url) or
  ('redhat-stable' in package_url)

ポイントは以下の通り。以下の説明は細かい検証はしていない or メモしていないので細かい部分で間違っているかもしれないです。

> とか | を使って複数行に書けるようになる

上記のwhenの場合、条件をそれぞれカッコでくくる

そうしないとエラーになった。原因までは調べてない。

when内では変数はそのまま、文字列はクオートでくくる

repo_url がansibleの変数(中身は文字列)の時、 'redhat-stable' in repo_url は問題ないが、 redhat-stable in repo_url はエラーになるということ。

Ansible難しいなと思ったところ

yamlファイル内に書くとき、「こう書きたい」と思った表現が書けるかどうか、以下の知識を持っていないと完全に判断が付けられないところ。

  • Ansible固有の知識
    • when節に書ける法則 etc.
  • Jinja2の知識
  • YAMLの仕様・知識