创建自定义的提供者

在上一节,我们创建了一个新的名为 gitrepo 的自定义资源类型并告诉 Puppet 此类型需要携带两个参数,分别为 source 和 path。 然而到目前为止,我们还没有告诉 Puppet 如何检出仓库,即如何创建这种类型的具体实例。 这正是提供者(provider)的用武之地。

正如我们之前看到的,一个类型经常会有几种可能的提供者。在本例中, 对一个 Git 仓库进行实例化仅有一种明智的方法,所以我们只需一个提供者:git。 如果你想要扩展这个自定义类型(将其称之为 repo 而非 gitrepo), 不难想象只要针对不同类型的仓库创建若干不同的提供者即可,例如:git、svn、cvs 等等。

准备工作

  1. 在你的 custom 模块的 lib/puppet 目录中, 创建一个名为 provider/gitrepo 的子目录:

    # mkdir -p lib/puppet/provider/gitrepo
    
  2. 在 gitrepo 目录中,使用如下内容创建一个名为 git.rb 的文件:

    require 'fileutils'
    
    Puppet::Type.type(:gitrepo).provide(:git) do
        commands :git => "git"
    
        def create
            git "clone", resource[:source], resource[:path]
        end
    
        def exists?
            File.directory? resource[:path]
        end
    end
    

操作步骤

  1. 在 Puppet 配置清单中添加如下代码为新的资源类型 gitrepo 创建一个实例:

    gitrepo { "https://github.com/puppetlabs/puppet.git":
        path   => "/tmp/puppet",
        ensure => present,
    }
    
  2. 现在运行 Puppet,你的新类型将被加载并进行了实例化:

    # puppet agent --test
    info: Retrieving plugin
    notice: /File[/var/lib/puppet/lib/puppet]/ensure: created
    notice: /File[/var/lib/puppet/lib/puppet/provider]/ensure: created
    
    notice: /File[/var/lib/puppet/lib/puppet/provider/gitrepo]/ensure:
    created
    
    notice: /File[/var/lib/puppet/lib/puppet/provider/gitrepo/git.rb]/
    ensure: defined content as '{md5}a12870d89a4b517e48fe417ce2e12ac2'
    
    notice: /File[/var/lib/puppet/lib/puppet/type]/ensure: created
    
    notice: /File[/var/lib/puppet/lib/puppet/type/gitrepo.rb]/ensure:
    defined content as '{md5}90d5809e1d01dc9953464e8d431c9639'
    
    info: Loading downloaded plugin /var/lib/puppet/lib/puppet/
    provider/gitrepo/git.rb
    
    info: Loading downloaded plugin /var/lib/puppet/lib/puppet/type/
    gitrepo.rb
    
    info: Redefining gitrepo in Puppet::Type
    info: Caching catalog for cookbook.bitfieldconsulting.com
    info: Applying configuration version '1299850325'
    
    notice: /Stage[main]//Node[cookbook]/Gitrepo[https://github.com/
    puppetlabs/puppet.git]/ensure: created
    
    notice: Finished catalog run in 74.43 seconds
    

注记

注意:由于 Puppet 的一个错误,当你首次创建新类型的实例时,可能需要两次运行 puppet agent:第一次加载类型的定义,第二次才真正创建实例。 如果你看到如下的信息:

err: /Stage[main]//Node[cookbook]/Gitrepo[https://
github.com/puppetlabs/puppet.git]: Could not
evaluate: No ability to determine if gitrepo exists

就意味着你正遭遇此错误带来的困扰?—?别急,再次运行 Puppet 即可正常工作。 当你读到本书的出版物时,这个错误很可能已经被修复。

工作原理

首先我们为 gitrepo 类型注册一个资源类型的提供者:

Puppet::Type.type(:gitrepo).provide(:git) do

当你在配置清单中声明此类型的一个实例时,Puppet 会先检查是否有已经存在的实例:

def exists?
    File.directory? resource[:path]
end

Puppet 会调用我们实现的 exists? 方法来做这种检查。 如果已有一个匹配实例 path 参数的目录存在,它返回 true。

如果 exists? 返回 true,那么 Puppet 将不会采取进一步的行动, 否则 Puppet 将通过调用 create 方法试图创建这个资源:

def create
    git "clone", resource[:source], resource[:path]
end

在这种情况下,create 方法会执行 git clone ,这会将原始仓库 (由 source 参数指定)克隆到由 path 参数指定的目录。

更多用法

你已经看到 Puppet 的自定义类型和提供者的强大之处。 实际上,他们可以做任何事情?—?至少是 Ruby 可以做的任何事情。 如果在你管理的某一部分基础设施中,使用了复杂的 define 和 exec 资源, 你就应该考虑将它们替换为自定义资源类型。 实际上,在你创建自定义类型之前可以先环顾一下周围是否已经有人实现了你需要的自定义资源类型。

此处我举的例子比较简单,你还有更多有关书写自定义类型的内容需要学习。 如果你要分发代码以供他人使用(或者,即使你不分发代码),在代码中包含必要的测试是一个好主意。

Puppet Labs 有一些有关开发自定义类型的有用页面: http://docs.puppetlabs.com/guides/custom_types.htmlhttp://projects.puppetlabs.com/projects/1/wiki/Development_Practical_Types 。 有关如何编写符合 Puppet Labs 标准的测试信息,请参考 http://projects.puppetlabs.com/projects/1/wiki/Development_Writing_Tests

James Turnbull 为自定义类型的开发撰写了一篇相当不错的易于遵循的介绍文章 “Creating Puppet types and providers is easy…”,其地址为: http://www.kartar.net/2010/02/puppet-types-and-providers-are-easy/

Dean Wilson 也提供了一个非常有启发性的例子,用于管理 APT 资源: https://github.com/deanwilson/puppet-aptsourced