[和訳] Chef 12におけるResourceとProviderの動的解決 #getchef
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
本稿は Chef-12 Dynamic Resource and Provider Resolution (2015/02/10) の和訳です。
背景
Chef 12では、'lib/chef/platform/provider_mapping.rb'にあった古いChef::Platformのハッシュマップは非推奨となりました。これにより、ProviderとResolverの動的解決の機構が推奨され、ResourceとProviderのDSLメソッドを通して操作できるようになりました。Chef 11では、プラットフォームのための機能を追加するための共通事項として、Chef::Platformハッシュマップは次のようになっていました。
class Chef
class Platform
class << self
attr_writer :platforms
def platforms
@platforms ||= begin
require 'chef/providers'
{
:mac_os_x => {
:default => {
:package => Chef::Provider::Package::Macports,
:service => Chef::Provider::Service::Macosx,
:user => Chef::Provider::User::Dscl,
:group => Chef::Provider::Group::Dscl
}
},
:mac_os_x_server => {
:default => {
:package => Chef::Provider::Package::Macports,
:service => Chef::Provider::Service::Macosx,
:user => Chef::Provider::User::Dscl,
:group => Chef::Provider::Group::Dscl
}
},
:freebsd => {
:default => {
:group => Chef::Provider::Group::Pw,
:service => Chef::Provider::Service::Freebsd,
:user => Chef::Provider::User::Pw,
:cron => Chef::Provider::Cron
}
},
[...etc for 400 lines...]
}
end
end
[...etc...]
新しい書式の例
Chef 12では、ProviderとResourceのクラスで'provides'メソッドを通して、ProviderとResolverを結びつけるようにしました。いくつか例を上げていきます。
すべてのプラットフォームにResourceを結びつける
ユーザがRecipeに'cookbook_file'と書いたら、すべてのプラットフォームが同じcookbook_file Resourceを得るという最も平凡な例です。
class Chef
class Resource
class CookbookFile < Chef::Resource::File
provides :cookbook_file
[...etc...]
end
end
end
あるOSにResourceを結びつける
node['os'] Attributeが'solaris2'ならば、ips_package Resourceのみに結びつける例です。
class Chef
class Resource
class IpsPackage < ::Chef::Resource::Package
provides :ips_package, os: "solaris2"
end
end
end
複数のplatform_familyにResourceを結びつける
より複雑な例として、provides行がnode['platform_family']をサポートし、値の配列が用いられているものを紹介します。どのようなプラットフォームのRecipeに'yum_package'に書いたとしてもyum_package Resourceに結びつけられる (SolarisでRecipeに'yum_package'に書いたとしても、その種類のResourceを得られる) だけでなく、Red Hat系のplatform_familyでユーザが'package'と書いたとしても'yum_package' Resourceに結びつけられて解決されます。これは、Red Hatで'package "foo"'と書いたら、素のパッケージ検証を行ってあらゆるyum特有のオプションを拒否するような素のChef::Resource::Packageオブジェクトを得るという、Chef 11からのちょっとした変更です。Chef 12では、Red HatではYumPackage Providerのための正しい検証を行うChef::Resource::YumPackageオブジェクトを得られます。
class Chef
class Resource
class YumPackage < Chef::Resource::Package
provides :yum_package
provides :package, os: "linux", platform_family: [ "rhel", "fedora" ]
end
end
end
任意のNode Attributeに基いてResourceを結びつける
Solaris2において、platform_versionが5.10以下ならばsolaris_packageを使う必要があり、platform_versionが5.11以上ならips_packageを使う必要があるならば、provides行は次のようになります。
class Chef
class Resource
class SolarisPackage < Chef::Resource::Package
provides :solaris_package
provides :package, os: "solaris2", platform_family: "nexentacore"
provides :package, os: "solaris2", platform_family: "solaris2" do |node|
# on >= Solaris 11 we default to IPS packages instead
node[:platform_version].to_f <= 5.10
end
end
end
end
ResourceとProviderのprovides行
Resourceファイルのすべてのprovides行について、通常はProviderファイル内の対応するprovides行であるべきです。Resourceは、Resourceのコンストラクタにもはや明示的にProviderを設定すべきではありません。ResourceでのProviderの明示的な定義はまだ動作しますが、Providerの動的解決ではこれは無視されます。Providerファイルでのprovides行がなくても今は動作し、Resource名に基くProviderの決定を壊したりはしませんが、これは非推奨となります。近いうちにChefは警告するようになり、いずれResourceとProviderの両方でprovides行が一致しなければ失敗するようになります。
サポートするprovidesの書式
provides行は、配列か文字列のどちらかに一致する'os'、'platform'、'platform_family'オプションを持ちます。また、Node Objectに渡されて、Nodeに結びつけることができたら真を返すことを期待されるブロックも持ちます。複数のマッチャがある場合、条件がすべて真でなければいけません。複数のprovides行は複数の条件に用いられ、配列の書式は配列の要素のいずれかにもマッチします。
provides :cookbook_file
provides :package, os: "windows"
provides :rpm_package, os: [ "linux", "aix" ]
provides :package, os: "solaris2", platform_family: "smartos"
provides :package, platform: "freebsd"
provides :package, os: "linux", platform_family: [ "rhel", "fedora" ]
provides :package, os: "solaris2", platform_family: "solaris2" do |node|
node[:platform_version].to_f <= 5.10
end
この書式の実装はlib/chef/node_map.rbファイルに含まれています。Chef::NodeMapオブジェクトはNode Objectに基いた条件を挿入されうる値を持つキーバリューストアです(そして、Node Objectがマッチするときのみそれらは回収されます)。
Providerの動的解決
Providerも動的解決を行います。プラットフォームが与えられたProviderをサポートしているかどうか(例:initシステムがsystemdかどうか?)、Providerのprovides?が与えられたResourceをサポートしているかどうか(例:service 'foo'はsysvinitスクリプトかupstartで管理されているかどうか?)を決定するための、Chef::Provider.provides? メソッドとChef::Provider.supports? メソッドを実装する、上書き可能な追加メソッドを持ちます。これはLinuxのinit scriptシステムのようなものを動的に取り扱うためにおおよそ設計されたようなものです。詳細は本稿で取り扱う範囲を越えているので、興味のある方はservice Providerを見てみてください。
LWRPの取り扱い
LWRPを任意の名前に結びつけることができるようになりました! もはやデフォルトの'[cookbook_name]_[provider_filename]'にとらわれず、もし望むなら独自のLWRPをpackage Providerに結びつけることさえできます (たとえそれがドラゴンだとしても----あるプラットフォーム用のpackage ProviderをChef本体に実装し、独自のpackage Providerが新しいChef本体のものと衝突するという、APIの破壊的な変更ではなく、API拡張というマイナーリリースで壊されるような場合を考えてみてください)。次は簡単な例です。
resources/default.rb
actions :run
default_action :run
provides :foo_bar
attribute :thing, kind_of: String, name_attribute: true
providers/default.rb
use_inline_resources
provides :foo_bar
action :run do
Chef::Log.warn new_resource.thing
end
recipes/default.rb
foo_bar "baz"
LWRP Chef 11 後方互換性
Chef 11はResourceにおける'provides'の書式をサポートしているので、コミュニティCookbookやChef 11後方互換性が依然として重要な場所で用いられる機能となるでしょう。Chef 12はResourceのために既に存在していたAPIを改善しただけです。Providerでの'provides'の書式を許可せず、"on_platform:"引数を取るだけです (Chef 12は後方互換のために"on_platform"を"platform"の別名としてサポートします)。LWRPを改名しChef 11後方互換性を維持することは、単純にProviderから'provides'行を削除するか、理想的には'if respond_to?(:provides)'という判定を用いて保護します。
状況
動的なProviderとResolverの機能は依然として開発中で、Chef 12では完成していません。Chef::Platform platform_mapには動的解決に変換して空にする必要があるエントリがまだ残っています。いずれにしろハッシュテーブルは完全に削除する必要があります。
ResourceとProviderの両方で起きている名前からクラスへ変換する謎の魔法は削除されます。実際に用いられるinitシステムに基いて挙動を変更する必要があるCookbookコードを書くための助けとなるDSLヘルパーメソッドとして提供される必要がある、ホストで使われているinitシステムを決定するための有用なヘルパーモジュールがあります。
これらのAPIについての文章は docs.chef.io には現時点では存在していません(もしこれを読んでいるのがJames Scottなら、修正のための連絡をください)。