### 创建自定义的提供者
在上一节,我们创建了一个新的名为 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
```
> ![注记](https://box.kancloud.cn/2016-05-12_5733eec619643.png)
> 注意:由于 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.html](http://docs.puppetlabs.com/guides/custom_types.html) 和 [http://projects.puppetlabs.com/projects/1/wiki/Development_Practical_Types](http://projects.puppetlabs.com/projects/1/wiki/Development_Practical_Types) 。 有关如何编写符合 Puppet Labs 标准的测试信息,请参考 [http://projects.puppetlabs.com/projects/1/wiki/Development_Writing_Tests](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/](http://www.kartar.net/2010/02/puppet-types-and-providers-are-easy/) 。
Dean Wilson 也提供了一个非常有启发性的例子,用于管理 APT 资源: [https://github.com/deanwilson/puppet-aptsourced](https://github.com/deanwilson/puppet-aptsourced) 。
- Puppet 2.7 Cookbook 中文版
- 中文翻译版
- 译者序
- 项目缘起
- 翻译方法
- 社区链接
- 社区建议
- 贡献者
- 原书版权页
- 关于作者
- 前言
- 本书内容
- 阅读前提
- 适用读者
- 格式约定
- 读者反馈
- 客户支持
- 下载案例代码
- 勘误表
- Puppet 基础设施
- 使用版本控制
- 使用提交钩子
- 使用 Rake 部署变更
- 配置 Puppet 的文件服务器
- 从 cron 运行 Puppet
- 使用自动签名
- 预签名证书
- 从 Puppet 的 filebucket 检索文件
- 使用 Passenger 扩展 Puppet 的部署规模
- 创建去中心化的分布式 Puppet 架构
- 监控、报告和排错
- 生成报告
- 通过 Email 发送包含特定标签的日志信息
- 创建图形化报告
- 自动生成 HTML 文档
- 绘制依赖关系图
- 测试你的 Puppet 配置清单
- 执行模拟运行
- 检测编译错误
- 理解 Puppet 的错误信息
- 显示命令的输出结果
- 输出调试信息
- 检查配置设置
- 使用标签
- 使用运行阶段
- 使用不同的环境
- Puppet 语言及其写作风格
- 使用 Puppet 社区规范
- 使用模块
- 使用标准的命名规范
- 使用嵌入式 Ruby 代码
- 使用纯 Ruby 代码书写配置清单
- 遍历多个项目
- 书写强大的条件语句
- 在 if 语句中使用正则表达式
- 使用选择器和 case 语句
- 检测字符串中是否包含指定的值
- 使用正则表达式替换
- 书写更优质的配置清单
- 使用资源的数组
- 使用 define 资源
- 指定资源的依赖关系
- 使用节点继承
- 使用类的继承和重载
- 给类传递参数
- 书写可重用的跨平台配置清单
- 获得系统的环境信息
- 导入动态信息
- 从 CSV 文件导入数据
- 给 Shell 命令传递参数
- 使用文件和软件包
- 为配置文件添加配置行
- 使用 Augeas 自动修改配置文件
- 使用配置片段构建配置文件
- 使用 ERB 模板
- 在模板中遍历数组
- 从第三方仓库安装软件包
- 配置 APT 软件仓库
- 配置 GEM 仓库
- 从源码包自动构建软件
- 比较软件包的版本
- 用户和虚拟资源
- 使用虚拟资源
- 使用虚拟资源管理用户
- 管理用户基于密钥的 SSH 访问
- 管理用户的自定义文件
- 有效地分发 cron 任务
- 当文件更新时运行命令
- 使用主机资源
- 为文件资源指定多个源
- 使用文件资源递归地分发整个目录树
- 清理过期的旧文件
- 使用日程表资源
- 资源的审计
- 临时禁用资源
- 管理时区
- 应用程序
- 管理 Apache 服务
- 创建 Apache 虚拟主机
- 创建 Nginx 虚拟主机
- 创建 MySQL 数据库及用户
- 管理 Drupal 站点
- 管理 Rails 应用程序
- 服务器和云基础设施
- 部署 Nagios 监控服务器
- 使用 Heartbeat 构建高可用服务
- 管理 NFS 服务和文件共享
- 使用 HAProxy 为多个 web 服务器实现负载均衡
- 使用 iptables 管理防火墙
- 管理 Amazon 的 EC2 实例
- 使用 Vagrant 管理虚拟机
- 外部工具和 Puppet 生态环境
- 创建 Facter 的自定义 fact
- 在运行 Puppet 之前和之后执行命令
- 从 Shell 会话生成 Puppet 配置清单
- 从运行的系统上生成 Puppet 配置清单
- 使用 Puppet Dashboard
- 使用 Foreman
- 使用 MCollective
- 使用公共模块
- 使用外部节点分类器
- 创建自定义的资源类型
- 创建自定义的提供者