Custom Source能够理解为自定义的资源类型,必须使用ruby语言进行书写,Custom Source主要包含两个模块:type和provider。
type和provider是须要配合使用的,type定义了能作什么,provider定义了怎么作,type和provider是相互依赖的关系。html
注:学习开发type和provider最好的方法是阅读现有的type和provider,最好是puppet核心代码块。
假如你是初次接触type和provider的开发,建议阅读和参考Package和User资源,而不要使用File资源。正则表达式
阅读本文以前你应该掌握的基本技能:shell
当建立一个type资源时候,应该关注于这个资源能作什么,而不是如何去作ruby
一般建立type是经过调用Puppet::Type的newtype方法,示例以下:markdown
# lib/puppet/type/database.rb Puppet::Type.newtype(:database) do @doc = "Create a new database." # ... the code ... end
或者开头使用module Puppetapp
# lib/puppet/type/package.rb module Puppet Type.newtype(:package) do @doc = "Manage a Package." # ... the code ... end
notice: 当建立type时,能够在名字后面指定参数,而且只有一个参数是可用的:
:self_refresh => true --- 会形成这个资源类型的刷新(相似于这个资源类型经过nogify/subscribe等关联资源方法收到了刷新的指令),这个最经常使用的是用在mount类型上less
在代码块里建立property和provider以前,一般会对这个资源类型有一个说明,经过@doc方法。字符串使用markdown格式,示例以下:运维
Puppet::Type.newtype(:database) do @doc = %q{Creates a new database. Depending on the provider, this may create relational databases or NoSQL document stores. Example: database {'mydatabase': ensure => present, owner => root } } end
前面咱们说道一个新的type名称后面是代码块,一个代码块里的主要内容就是properties和parameters,它们都能生成资源的属性。ide
Properties和Parameters又是怎样的区别呢?它们的区别很微妙并且很重要学习
那么property是怎么工做的呢?
假如你定义了一个owner的property,首先property会调用provider的方法去检测并获取当前owner的状态,当当前资源状态与咱们设置的不一致时,property或调用相似于owner=methon的方法将资源状态改变为咱们所指望的。
一般咱们定义property是经过"def xxx { ... }"方式定义的,可是有一个特殊的property,由于是比较经常使用的,因此有了简化的写法,那就是ensure property。它能够简单定义为ensurable,示例以下:
Puppet::Type.newtype(:database) do ensurable ... end
这个property会调用provider的三个方法:create, destroy, and exists? . 顾名思义,create就是建立(更改)当前的资源状态,destory就是删除当前资源,exists?是检测当前资源是否存在。若是ensure属性定义为“不要求同步”,那么其余的properties将都不会执行。
其余property定义方式采用“newproperty(:xxx)do ... end”的形式,示例以下:
Puppet::Type.newtype(:database) do ensurable newproperty(:owner) do desc "The owner of the database." ... end end
其中desc是描述这个property的做用,相似于type类型的@doc方法。
puppet最开始发展时候,properties里面会有不少代码,然而通过优化与规范,properties里面只须要定义可用的参数或者设置validation和munging(后面的parameters讲解会说到这两个方法),若是你定义了一些参数,那么puppet只会接受这些你定义的参数。一般来说,只须要定义容许的参数,可是这些参数对其余的property也是生效的。
newproperty(:enable) do newvalue(:true) newvalue(:false) end
validate方法能够对property设置的参数进行处理,例如,当须要参数是字符串时,能够这样写:
newproperty(:owner) do validate do |value| unless value =~ /^\w+/ raise ArgumentError, "%s is not a valid user name" % value end end end
puppet根据property定义顺序执行,因此对一个目标系统资源进行检查和修复的顺序也是根据咱们定义property的顺序执行的。
日志输出是必不可少的,经过puppet日志你能够看到目标系统资源都进行了怎样的变化,经过以下三个方法能够实现日志输出功能:
示例:
def should_to_s(newvalue) if provider.respond_to?(:package_settings_should_to_s) provider.package_settings_should_to_s(should, newvalue) else super(newvalue) end end def is_to_s(currentvalue) if provider.respond_to?(:package_settings_is_to_s) provider.package_settings_is_to_s(should, currentvalue) else super(currentvalue) end end def change_to_s(currentvalue, newvalue) if provider.respond_to?(:package_settings_change_to_s) provider.package_settings_change_to_s(currentvalue, newvalue) else super(currentvalue,newvalue) end end
当建立一个资源时候,咱们指定的参数会被存储在每个property的@should方法中,咱们能够在资源中使用以下方式去调用这些参数:
myval = should(:color)
固然有时候也会调用parameters里的参数,parameters里的参数不能使用should方法了,而应该使用value方法。因此当你不肯定你调用的参数是properties里的仍是parameters里的时候,使用value方法确定是没错的:
myvalue = value(:color)
parameters方法的使用和property大致上是相同的,惟一的不一样是parameters不会调用providers的方法。
定义一个新的parameter使用newparam方法,后面的语法和property方法同样。示例以下:
newparam(:name) do desc "The name of the database." end
每一个type都应该有至少一个必有的parameter:namevar。这个parameter是该资源的惟一标识,例如:磁盘上的路径,用户名称,包的名称等等。
若是声明的资源里不指定namevar方法,那么这个资源的title将会是namevar的默认值。
有三个方法声明namevar:
newparam(:name) do desc "The name of the database." end
newparam(:path, :namevar => true) do ... end
newparam(:path) do isnamevar ... end
当没有namevar的定义时候会报错,puppet2.7和puppet3的报错信息以下:
puppet 2.7:
$ puppet apply -e "testing { h: }" Error: undefined method `merge' for []:Array
Puppet 3:
$ puppet apply -e "testing { h: }" Error: No set of title patterns matched the title "h".
一次声明多个变量的方法:
newparam(:color) do newvalues(:red, :green, :blue, :purple) end
声明变量也可使用正则表达式的形式:
newparam(:color) do desc "Your color, and stuff." newvalues(:blue, :red, /.+/) end
一些parameters是不必定义参数列表的,或者想要对传进来的参数进行一些处理,那么咱们可使用validate和munge方法:
newparam(:color) do desc "Your color, and stuff." newvalues(:blue, :red, /.+/) validate do |value| if value == "green" raise ArgumentError, "Everyone knows green databases don't have enough RAM" else super end end munge do |value| case value when :mauve, :violet # are these colors really any different? :purple else super end end end
validate和munge的区别:
validate只会处理使用newvalues定义过的参数,然而munge方法会处理全部传进来的参数,包括newvalues定义过的和其余被指定的参数。假如传递进来的参数都没有通过处理,那么必须使用调用super方法(也就是说else后面必须使用super方法),不然传进来的数值就因被丢失而失效。super方法的使用能够参考个人另外一篇博客ruby语法-super用法
通常来说,参数老是先通过validate方法再通过munged方法。
最后要说明的一点:validate和munge方法只会处理传进来的参数(该参数已是肯定咱们指望的值了),而不会处理方法里预约义的参数。
由于声明布尔类型的参数是很常见的,因此为了定义的方便,可使用下面的方式:
require 'puppet/parameter/boolean' # ... newparam(:force, :boolean => true, :parent => Puppet::Parameter::Boolean)
其中:parent => Puppet::Parameter::Boolean会配置parameter接受许多的名称(变量等),并判断其真假,返回true或false。:boolean => true部分会建立一个布尔方法并返回parameter的最终参数,在上面的例子中,等同于:boolean => true建立了一个force?的方法。
一个type资源能够指定要自动关联的资源,经过使用autorequire,autobefore,autonotify和autosubscribe方法,这些方法须要一个资源类型的名字做为参数,而且会返回一个资源列表,这个资源列表可能会与你定义的type方法有某些关联。示例:
autorequire(:user) do self[:user] end
须要注意的一点:当使用这四个方法时,即便调用的资源名称(例如上例中的user)并非一个有效的资源,也不会报错。
这是一个发生在agent端的事情,在agent端有时咱们须要设置一些先决条件,当先决条件不被知足时,就会跳出部署catalog。这个先决条件的定义就是pre_run_check方法了。
若是type资源里面定义了pre_run_check方法,在Puppet agent或puppet apply部署catalog以前,首先会运行全部资源里的pre_run_check方法,一旦有错误产生,就会将错误信息打印出来告知使用者并中止部署catalog。