ChefのKnife用プラグインを作ってみた
こちらの記事でも触れていますが、ChefはKnifeというコマンドラインツールを使用してリポジトリ操作を行います。
Knifeにはサブコマンドが多数用意されており、それらを使ってリポジトリの作成やクックブックの作成を行うわけですが、自分で新たにプラグインを作成することで、サブコマンドとして利用することもできます。
というわけで、今回は試しにKnifeプラグインを作成し、自前のサブコマンドを作成してみたいと思います。
Knifeプラグインの作成
Knifeプラグインの作成はとっても簡単。公式にベースとなるコードがあるので、それをベースに処理を記述していくだけでオッケーです。
今回は以下のようにしてみます。
module MakeNodeModule class Makenode < Chef::Knife deps do require 'json' #5行目 end banner "knife subcommand argument VALUE (options)" #8行目 option :node_file_path, #10行目 :short => "-f FILEPATH", :long => "--node-file-path FILEPATH", :description => "nodeオブジェクトファイルのパス", :default => "./node/sample.json" RECIPE_SCAFFOLD = "recipe[name]" def run #コマンド実行時の処理 # nodeオブジェクトファイルを読み込み json_file_path = config[:node_file_path] json_data = open(json_file_path) do |file| #20行目 JSON.load(file) end # site-cookbooksディレクトリ内のフォルダ名を配列に詰め込む recipe_json_ary = [] Dir.foreach("./site-cookbooks/") { |name| #26行目 unless name.start_with?(".") recipe_json_ary.push(RECIPE_SCAFFOLD.gsub('name', name)) end } # run_listにレシピを詰め込む json_data['run_list'] = recipe_json_ary #33行目 # nodeオブジェクトファイルに戻す open(json_file_path, 'w') do |io| JSON.dump(json_data, io) end end end end
適当ですが...
ポイントはまず2行目、クラス名を定義していますが、このクラス名でサブコマンド名が決まります。ここでは"Makenode"としているので、コマンドとしてはknife makenode 〜
となります。
もし"MakeNode"と"N"を大文字にすると、knife make node 〜
となるのでクラス名の大文字には注意します。
4〜6行目のブロックには依存関係のあるライブラリをrequireで定義します。今回はJSONを扱うのでrequire 'json'
を定義しています。
8行目のbannerはヘルプで表示される説明文(?)です。以下のようになります。
$ knife makenode -h nodeオブジェクトファイルのrun_listを自動で生成します。 -s, --server-url URL Chef Server URL --chef-zero-host HOST Host to start chef-zero on --chef-zero-port PORT Port (or port range) to start chef-zero on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works. -k, --key KEY API Client Key --[no-]color Use colored output, defaults to enabled -c, --config CONFIG The configuration file to use --defaults Accept default values for all questions -d, --disable-editing Do not open EDITOR, just accept the data as is -e, --editor EDITOR Set the editor to use for interactive commands -E, --environment ENVIRONMENT Set the Chef environment (except for in searches, where this will be flagrantly ignored) --[no-]fips Enable fips mode -F, --format FORMAT Which format to use for output --[no-]listen Whether a local mode (-z) server binds to a port -z, --local-mode Point knife commands at local repository instead of server -f, --node-file-path FILEPATH nodeオブジェクトファイルのパス #17行目 -u, --user USER API Client Username --print-after Show the data after a destructive operation -V, --verbose More verbose output. Use twice for max verbosity -v, --version Show chef version -y, --yes Say yes to all prompts for confirmation -h, --help Show this message
2行目に表示されているのがわかります。
10〜14行目ではコマンドラインオプションを定義しています。今回はnodeオブジェクトのファイルパスを受け取ることにします。
上記のヘルプをよく見てみると、17行目あたりに作成したオプションが表示されいているのがわかります。
17行目からのrunメソッド内に、実際の処理を記述しています。
nodeオブジェクトファイルを読み込んで、site-cookbooksディレクトリ内のフォルダ名をレシピ名として利用し、run_listを作成しています。完成したJSONを、最後に書き込んでいます。
実行してみる
さて、実際に実行してみるわけですが、まずは作成したプラグインファイルを以下のいずれかの方法で配置する必要があります。
・ホームディレクトリ ~/.chef/plugins/knife/ 配下
・<Chefリポジトリ>/.chef/plugins/knife/ 配下
・RubyGemsのパッケージとしてインストール
とりあえず、一つ目のホームディレクトリに配置しました。
で、実行してみると...
$ ls ./site-cookbooks/ apache certbot ssh $ knife makenode -f ./nodes/centos6-chef.json
centos6-chef.jsonファイルを見てみると、
{"run_list":["recipe[apache]","recipe[certbot]","recipe[ssh]"],"automatic":{"ipaddress":"uentseit2.com"}}
改行がなくなってしまいましたが...run_listは正しく構築されているようです。
というわけで、今回はこんなところです。