ka
2015-11-14
このスライドは公開されています
このスライドのリポジトリはhttps://github.com/kaosf/20151114-osc-tokushimarbにあります
間違いなどを見つけた場合は
などしていただけるとありがたいです (※下に行くほど嬉しいです)
最近 NC を除いてもいいんじゃないかって気もしてる
これは BY-NC-SA で
一つのことを上手くやれなくても一つのことだけに集中しようという話
Ruby製のCLIツールを作る方法
net/httpライブラリをちゃんと自分の手で使ってみたという話
OpenStackのAPIを普通に扱う方法を諦めた話
ユーザが自分しか居なくても頑張れる話
などなど
VPSの操作をWebブラウザ上でやるの面倒くさいと思っている人
ConoHa VPSを料金抑えて使いたいと思っている人
CUIの世界に閉じこもりたい人
Rubyの話が何か聞きたい人
レンタルサーバなりVPSなりを使ったことがある人
Rubyの文法とかgemの使い方とか入門レベルは流石に終わっている人
ターミナルでの操作を説明されたり見せられたりしても「さっきから一体何をやってるんだ?」とはならない程度の人
AWSの天下布武に始まり
さくら GMO Kagoya その他諸々
の色んな業者がクラウドやVPSのサービスをやっている
そんな中でGMOがやってるConoHa VPSというのが2015年の5月だか6月だか(うろ覚え)の頃に
リニューアルをやって安いわ性能は良いわで私が今現在大変気に入っている
気に入っているのは以下の点
ノートPC(なるべくバッテリー長持ちするやつ)を常用する
開発はSSH越しに別マシン(VPSかデスクトップマシンであることが多い)で行う
さくらのVPSを使っていた
VPSの利用料金は最低でおおよそ月額1,000円程度なので開発に利用する環境の料金が年間10,000円ちょい必要
一番低いスペックで年間10,000円はかかるので次のスペックを開発用に使おうと思ったら
4年くらいでもっと良いノートPC買えたよね?みたいになりそう
VPSのコントロールパネル(VPSを作って壊してする画面)がWebブラウザで操作出来るものとして提供される
(これは別にどこもそれほど変わらない)
しかしAPIが提供された
時間単位で課金されるなら作っては壊し作っては壊しを出来れば利用料金を1/3くらいに抑えられる?
ならばコマンドラインで作って壊して出来るようにしてしまえ
これが一番最初の動機
例えば今回作ってみたツールでは
シェルスクリプトを実行するのが面倒なのを簡単にしたい
これに集中した
最初はConoHa APIをサンプルに則りcurlしまくるシェルスクリプト群を使ってた
AUTH_TOKEN=`head -1 authtoken`
TENANT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ROOT_PASS=xxxxxxxxxx
ADMIN_PASS=$ROOT_PASS
if [ $# -lt 1 ]; then
IMAGE_REF=fa67ec7b-b9b4-4633-9012-fc5a6303aba7 # CentOS 6.6
elif [ $1 = ubuntu ]; then
IMAGE_REF=2b03327f-d453-4c7d-91c9-8b9924b6ea88 # Ubuntu 14.04 amd64
elif [ $1 = centos7 ]; then
IMAGE_REF=edc9457e-e4a8-4974-8217-c254d215b460 # CentOS 7.1
fi
FLAVOR_REF=7eea7469-0d85-4f82-8050-6ae742394681 # RAM 1GB
curl -X POST \
-H "Accept: application/json" \
-H "X-Auth-Token: $AUTH_TOKEN" \
-d '{"server": {"adminPass": "'$ADMIN_PASS'","imageRef": "'$IMAGE_REF'", ... }}' \
https://compute.tyo1.conoha.io/v2/$TENANT_ID/servers 2> /dev/null | \
ruby -rjson -e 'puts JSON.parse(STDIN.read)["server"]["id"]'
これを 1-create-vps.sh
として用意してた
認証用のtokenは authtoken
というファイルの一行目に書いて
以下のシェルスクリプトで書き換えるようにしてた
curl -X POST -H "Accept: application/json" \
-d '{"auth":{"passwordCredentials":{"username":"xxxx","password": "xxxxxx"},"tenantId":"xxxxxxxxxxx"}}' \
https://identity.tyo1.conoha.io/v2.0/tokens 2> /dev/null | \
ruby -rjson -e 't=JSON.parse(STDIN.read)["access"]["token"]["id"];puts t;File.open("authtoken","w").puts t'
JSONのパースは面倒くさいのでRubyのワンライナーで済ませていた
これを 0-authenticate.sh
として用意してた
で,以上のようなシェルスクリプト(Rubyの力をJSONパースに利用させてもらう)群を
他にもたくさん作っていて,ひとまず
が出来るようになった
JSONをパースするツールとしては jq という有名ドコロがある
しかしこれをインストールするよりもRubyを普段使いする自分にとっては
ruby -rjson -e 'puts JSON.parse(STDIN.read)["key"][1]'
の方が楽かな?と思った
Rubyのワンライナーで STDIN.read
や puts
は省略出来るとか
何かそういうのがあったような気もしたので知ってたら教えて下さい
最近はあんまり見かけませんが
「それ○○で出来るよ?」
というマサカリに対しては意外と
「自分はこっちの方が楽だったので」
で普通に対応出来ることがある
※もちろんきちんと比較した上でないとダメ
net/httpライブラリをちゃんと自分の手で使ってみたという話
シェルスクリプトでcurl叩きまくりプログラムでもとりあえず自分のやりたいことは出来た
しかし一度何かが出来るようになるとすぐに不満が出てくる
Bashで JSON.parse(STDIN.read)
の代わりは難しいが
Rubyでcurlコマンドの代わりは出来そう
gemにしてしまえばコマンド一発でインストール出来る
認証は設定ファイルをホームディレクトリに置いてそれを勝手に書き換えてくれれば扱いやすい
ということで自分でcurlの代わりをすることになる
幸いRubyには(他の言語でもこの辺はまずサポートされていると思うが)標準で
HTTPの各種リクエスト,レスポンスを扱うライブラリが用意されている
便利メソッドがちょくちょくある(curlコマンド並の手軽さを実現?)
しかしリクエストヘッダを application/json
にしたかったり
https 通信にしたかったりするので出来るだけ細かく動かしたい
require 'uri'
uri = URI.parse 'https://hostname/a/b/c'
uri.class #=> URI::HTTPS
uri.host #=> "hostname"
uri.port #=> 443
uri.request_uri #=> "/a/b/c"
uri = URI.parse 'http://myhost.com/some/where.html'
uri.class #=> URI::HTTP
uri.host #=> "myhost.com"
uri.port #=> 80
uri.request_uri #=> "/some/where.html"
多分これが一番簡単で細かいと思います
GETリクエストの場合
require 'net/https'
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
req = Net::HTTP::Get.new(uri.path)
req['Content-Type'] = 'application/json'
req['Accept'] = 'application/json'
req['X-Auth-Token'] = "0123456789abcdef0123456789abcdef"
https.use_ssl = true
これでhttps通信出来る
各種リクエストヘッダの設定はHashを更新するかのように出来る
POSTリクエストの場合
require 'net/https'
require 'json'
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
req = Net::HTTP::Post.new(uri.request_uri)
req['Content-Type'] = 'application/json'
req['Accept'] = 'application/json'
req['X-Auth-Token'] = "0123456789abcdef0123456789abcdef"
req.body = payload.to_json
Net::HTTP::Get
クラスの代わりに Net::HTTP::Post
クラスのインスタンスを作る
payload
にリクエスト用のJSONの元になるHashを設定する
payload = {
server: {
adminPass: "very-secure-root-password",
imageRef: "01234567-89ab-cdef-0123-456789abcdef",
flavorRef: "01234567-89ab-cdef-0123-456789abcdef",
key_name: "registered-public-key-name",
security_groups: [
{name: 'default'},
{name: 'gncs-ipv4-all'}
]
}
}
数ページ前に出てきたcurlの例は以下の通り
curl -X POST \
-H "Accept: application/json" \
-H "X-Auth-Token: $AUTH_TOKEN" \
-d '{"server": {"adminPass": "'$ADMIN_PASS'","imageRef": "'$IMAGE_REF'", ... }}' \
https://compute.tyo1.conoha.io/v2/$TENANT_ID/servers 2> /dev/null | \
ruby -rjson -e 'puts JSON.parse(STDIN.read)["server"]["id"]'
長くなりすぎるので ... で省略しているが
-d '{"server": {"adminPass": "'$ADMIN_PASS'","imageRef": "'$IMAGE_REF'", ... }}'
ここでJSONを文字列として渡していた
DELETEリクエストの場合
Net::HTTP::Get
の代わりに Net::HTTP::Delete
を用いる
後はGETリクエストのときと変わらない
作っておいた Net::HTTP::Get
などのインスタンスを
res = https.request(req)
のようにこれまたあらかじめ作っておいた Net::HTTP
のインスタンスの request
メソッドに渡してやる
そうして受け取れる res
が Net::HTTPResponse
のインスタンスとなり
res.code #=> 200, 401 など
res.body # レスポンスの本体(今回はJSONの文字列が入ってくる)
のように扱える
これが出来るようになったのでひとまず今回のcurlコマンドの代わりとしては上等である
まずgemの作り方は分かっているものとする
分かってなくても3行で解説出来る
Bundlerをインストールして以下を実行
bundle gem your_library_name
コードを書いてコミットして以下を実行
rake release
普通に bundle gem foobar
を実行してgemを作ってもコマンドは作れない
exe
というディレクトリを作ってそこに実行可能スクリプトを用意する
ここでややこしいのが昔はデフォルトが exe
ディレクトリではなく
bin
ディレクトリだったこと
ある時期から変わった
とは言っても *.gemspec
ファイルにその指定はあるのでそこを好きに書き換えれば良い
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
もしあなたが今の自分の環境で同様のことを行っても
先に述べた状態になっていないかもしれない
その場合は Bundler 自体のバージョンが古いかもしれない
gem uninstall bundler
gem install bundler
古い Bundler は消してしまって新しいものを積極的に取り入れよう
私は現在 1.10.6 を使用中
OpenStackのAPIを普通に扱う方法を諦めた話
ConoHa VPSはOpenStackの技術を採用している
OpenStackは有名なのでそれを操作するRubyのgemくらい普通にあってもいいだろうと思う
調べると以下のものを見つける
READMEを読んで「自分がやりたいだけのことからかけ離れてる気がする」
そして何よりも「ボリュームがパない」
READMEが462行,28kBもある
READMEを読んでも自分がやりたいだけのことを知るのに時間かかりそう
ConoHaのAPIドキュメントのcurlのサンプルを試しまくって
それで積んでしまった実績があるのでそれを再利用出来るだけにする方が楽そう
なので不採用
これはOpenStackを操作するVagrant用のProviderからのforkであると分かる
Vagrant(というかVMwareないしVirtualBox)を使うのが
無理な人も居るからConoHa VPSを採用しているんだぞという節がある
実際に自分がやりたかったのは
vagrant up
vagrant ssh
の代わりとしての
conoha create
conoha ssh
的な操作である
なのでやっぱり作ってしまおうと思った
あと conoha
ってだけの名前の gem がまだなかったので儲けモンだとも思った(名前は早い者勝ち)
これは素直に面白い
自分が作ったものをネタに話出来るのは楽しい
tokushima.rbでもまた話したい
よく言われることだが「どこまで作っても完成は無い」
やりたいことが次から次に出てくる
しかしその度にconoha gemを肥大化させてはUNIX哲学に反するのでレイヤーをきちんと分けていく
そういう工夫も楽しい
単にものづくりは楽しいということ
個人の趣味レベルのプロダクトでソースコードを隠すメリットはそんなに無いと思う
(※今Vectorとか窓の杜とかクローズドソースで作者も連絡不能なプロダクトが大量にあるだけみたいな場になってません?)
OSSは「別に自分は損するわけじゃないし」ってだけの動機で出来る
(※ただし著作権や特許の侵害に対しては気を付けるように)
ひとときひとときの活動記録であるコミットの集合体のリポジトリを世界に公開してしまう
こんな素晴らしい日記としてツールがあるだろうか?
HTTPの実行を司る部分,CLIツールとしての機能,
をそれぞれ
conoha_api_client
, conoha
に分割したい
というのも複数のアカウントを切り替えなければならないことがあるので
CLIツールで
conoha userselect ka
conoha userselect other
のように切り替えが出来てその後の操作が全てそのユーザで行えるようにしたい
現状HTTPリクエスト出す部分とCLIツールとしての部分は最初から分離しようと思って
分離してあるが認証関連の部分がどっぷり前者に入っている
ConoHaのAPI用のアクセストークンは有効期限が1日だけなので24時間以上開くと
conoha authenticate
しなければならない
操作の度にアクセストークンを取得するか?
操作の度に401が帰ってきたらアクセストークン再取得を行うようにするか?
どちらかをやりたい
現状
conoha ssh UUID
するのがマニュアルの通りなのだが実用的にしんどいので
conoha ssh 0
とすれば 0 番目のVPSに対して操作が行えるようにしてある(READMEには書いてない)
というのも複数のVPSが存在する際に順番が保証されるのかどうか分からないので
なので Conohafile
(まるで Vagrantfile
のように) を用意して
それが存在するディレクトリで
conoha ssh
すればVPSが勝手に選ばれるようにするとかやってみたい
が現状2つのVPSを頻繁に操作することも無い(一回ログインしたらそのままになる)し
conoha ssh 0
で困ってないので可能性は薄い
もし良かったらGitHubでStar下さい
もし良かったら
gem install conoha
して下さい
(※ConoHa VPSはVPSを消しておけば維持費かかりませんよ)
おわり