Edited at

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