techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

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は正しく構築されているようです。

というわけで、今回はこんなところです。