Ruby
chef

Chefのレシピでsed的な事を実施

More than 3 years have passed since last update.

この記事は最終更新から1年以上経過しています。 気をつけてね。

Infrastructure as Codeに感化されていざChefを使うとなった時、ちょっと既存のファイルを弄りたいだけなのにcookbookfileとかtemplateとかメンドクセって思うじゃないですか。

『ほなexecuteリソースでちょっとsedコマンドしたら…』と思う前にちょっとChef::Utilを検討しませんか。

対象ファイルとレシピ

サンプルとしてこんな2行で出来たファイルを2つ用意しました。

tmp1,tmp2
hogehoge
mogemoge

では 'tmp1'に一致した行の置換、'tmp2'では文字列の置換を実施してみます。

apply.rb
file './tmp1' do
  _file = Chef::Util::FileEdit.new(path)
  _file.search_file_replace_line('^hoge', "piyopiyo\n")
  content _file.send(:contents).join
end

file './tmp2' do
  _file = Chef::Util::FileEdit.new(path)
  _file.search_file_replace('ge', "ga")
  content _file.send(:contents).join
end

追記中:Chef-Clientが11.12以降では FileEditの@contentsがなくなり、@editor.linesに変更。

apply2.rb
## 11.12.0以降のクライアント
file './tmp1' do
  _file = Chef::Util::FileEdit.new(path)
  _file.search_file_replace_line('^hoge', "piyopiyo\n")
  content _file.send(:editor).lines.join
end

file './tmp2' do
  _file = Chef::Util::FileEdit.new(path)
  _file.search_file_replace('ge', "ga")
  content _file.send(:editor).lines.join
end

Chef::Util::FileEdit.new(path)pathについて。
レシピのリソース定義内では自分(self)のattributesを利用することができるので、path(※省略時はnameと同じ)を使いまわしてChef::Util::FileEdit読み込み対象にしてるんですね。

参考:file — Chef Docs

#contentsがprivate_methodなのでちょっと:sendしましたがまあいいっしょ。

Result

:dry_runです、きっと目論見通りですね。

chef-apply(dry_run)
$ chef-apply appry.rb -w
Recipe: (chef-apply cookbook)::(chef-apply recipe)
  * file[./tmp1] action create
    - Would update content in file ./tmp1 from 639ed8 to 55d5ca
        --- ./tmp1  2013-08-20 00:45:11.000000000 +0900
        +++ /var/folders/s9/kwcs_8ln0d32v_n4hdfb7td00000gn/T/.tmp120130820-24879-gemfn5 2013-08-20 00:45:19.000000000 +0900
        @@ -1,2 +1,2 @@
        -hogehoge
        +piyopiyo
         mogemoge
  * file[./tmp2] action create
    - Would update content in file ./tmp2 from 639ed8 to 93260c
        --- ./tmp2  2013-08-20 00:45:03.000000000 +0900
        +++ /var/folders/s9/kwcs_8ln0d32v_n4hdfb7td00000gn/T/.tmp220130820-24879-ieq0kc 2013-08-20 00:45:19.000000000 +0900
        @@ -1,2 +1,2 @@
        -hogehoge
        -mogemoge
        +hogahoga
        +mogamoga

実施します、まあ:dry_runと変わらず。

chef-apply(conversion)
$ chef-apply appry.rb 
Recipe: (chef-apply cookbook)::(chef-apply recipe)
  * file[./tmp1] action create
    - update content in file ./tmp1 from 639ed8 to 55d5ca
        --- ./tmp1  2013-08-20 00:45:11.000000000 +0900
        +++ /var/folders/s9/kwcs_8ln0d32v_n4hdfb7td00000gn/T/.tmp120130820-24938-1bwvf6g    2013-08-20 00:45:41.000000000 +0900
        @@ -1,2 +1,2 @@
        -hogehoge
        +piyopiyo
         mogemoge
  * file[./tmp2] action create
    - update content in file ./tmp2 from 639ed8 to 93260c
        --- ./tmp2  2013-08-20 00:45:03.000000000 +0900
        +++ /var/folders/s9/kwcs_8ln0d32v_n4hdfb7td00000gn/T/.tmp220130820-24938-h5bq4k 2013-08-20 00:45:41.000000000 +0900
        @@ -1,2 +1,2 @@
        -hogehoge
        -mogemoge
        +hogahoga
        +mogamoga

続いてchef-apply実施、もうファイルに対して残す変更はありません。

chef-apply(up_to_date)
$ chef-apply appry.rb 
Recipe: (chef-apply cookbook)::(chef-apply recipe)
  * file[./tmp1] action create (up to date)
  * file[./tmp2] action create (up to date)

よし、本日も冪等である。

その他FileEditのメソッド

その他sedでよく使うマッチ行の次に挿入や、sedでやるのは多分面倒くさい'無かったら追記'がメソッドとして用意されています。

Chef
pry(main)> Chef::Util::FileEdit.instance_methods(false)
=> [:search_file_replace_line,
 :search_file_replace,
 :search_file_delete_line,
 :search_file_delete,
 :insert_line_after_match,
 :insert_line_if_no_match,
 :write_file]

Chef::UtilのあたりChefの中でもかなり古いコードです、初期はファイルをちょろっといじるようなツールだったのでしょうかね。


追記:
念のため書いておくとChef::Util::FileEdit本来の使い方は#write_fileで、Chef::Util::Backupなどと組み合わせて主にLibraryやLWRP方面で利用します。
ただレシピ内でやるのは芸が無いと思うのです。

追記2:

普通の使い方のサンプルも引用しておきます。

timezone cookbook(fork版)より。

https://github.com/pearj/cookbooks/blob/master/timezone/recipes/default.rb

  clock = Chef::Util::FileEdit.new("/etc/sysconfig/clock")
  clock.search_file_replace_line(/^ZONE=.*$/, "ZONE=\"#{node[:tz]}\"")
  clock.write_file