使用类的继承和重载

正如节点可以从其他节点继承一样,这可以为相似的节点复制很多代码, 同样的思想也可以用于类。

例如,假设你有一个管理 Apache Web 服务器的 apache 类, 你想要使用略有不同的配置文件设置一台新的 Apache 机器?—?也许是监听的端口不同。

你可以复制整个 apache 类,除了配置文件。另外,你可以从 apache 类中提取配置文件并创建两个新类,每个新类都包含 apache 基类并添加一个新版本的配置文件。

一个更简洁的做法是从 apache 类继承,而后仅覆盖其配置文件。

准备工作

  1. 为新的 apache 模块创建目录结构:

    # mkdir /etc/puppet/modules/apache
    # mkdir /etc/puppet/modules/apache/manifests
    # mkdir /etc/puppet/modules/apache/files
    
  2. 使用如下代码创建 /etc/puppet/modules/apache/manifests/init.pp 文件:

    class apache {
        package { "apache2-mpm-worker": ensure => installed }
    
        service { "apache2":
            enable  => true,
            ensure  => running,
            require => Package["apache2-mpm-worker"],
        }
    
        file { "/etc/apache2/ports.conf":
            source => "puppet:///modules/apache/port80.conf.apache",
            notify => Service["apache2"],
        }
    }
    
  3. 若 Apache 软件包还未安装,安装它,复制其包含的文件 ports.conf 到 Puppet:

    # apt-get install apache2-mpm-worker
    
    # cp /etc/apache2/ports.conf \
    /etc/puppet/modules/apache/files/port80.conf.apache
    
  4. 添加 apache 类到一个节点,例如:

    node cookbook {
        include apache
    }
    
  5. 运行 Puppet 验证配置清单是否正常工作。

操作步骤

  1. 创建 port80.conf.apache 文件的一个新版本 port8000.conf.apache,并做如下改动:

    NameVirtualHost *:8000
    Listen 8000
    
  2. 使用如下内容创建一个新文件 /etc/puppet/modules/apache/manifests/port8000.pp:

    class apache::port8000 inherits apache {
        File["/etc/apache2/ports.conf"] {
            source => "puppet:///modules/apache/port8000.conf.apache",
        }
    }
    
  3. 改变你的节点配置,使其包含 apache::port8000 类而不是 apache 类:

    node cookbook {
        include apache::port8000
    }
    
  4. 运行 Puppet 检查它是否会按照要求的那样发生改变:

    # puppet agent --test
    info: Retrieving plugin
    info: Caching catalog for cookbook.bitfieldconsulting.com
    info: Applying configuration version '1302970905'
    --- /etc/apache2/ports.conf 2010-11-18 14:16:23.000000000 -0700
    +++ /tmp/puppet-file20110416-6165-pzeivi-0 2011-04-16
    10:21:47.204294334 -0600
    @@ -5,8 +5,8 @@
    # Debian etch). See /usr/share/doc/apache2.2-common/NEWS.Debian.gz and
    # README.Debian.gz
    
    -NameVirtualHost *:80
    -Listen 80
    +NameVirtualHost *:8000
    +Listen 8000
    
    <IfModule mod_ssl.c>
    # If you add NameVirtualHost *:443 here, you will also have
    to change
    info: FileBucket adding /etc/apache2/ports.conf as {md5}38b31d2032
    6f3640a8dfbe1ff5d1c4ad
    info: /Stage[main]/Apache/File[/etc/apache2/ports.conf]:
    Filebucketed /etc/apache2/ports.conf to puppet with sum
    38b31d20326f3640a8dfbe1ff5d1c4ad
    notice: /Stage[main]/Apache/File[/etc/apache2/ports.conf]/content:
    content changed '{md5}38b31d20326f3640a8dfbe1ff5d1c4ad' to '{md5}4
    1d9d446f779c55f13c5fe5a7477d943'
    info: /Stage[main]/Apache/File[/etc/apache2/ports.conf]:
    Scheduling refresh of Service[apache2]
    notice: /Stage[main]/Apache/Service[apache2]: Triggered 'refresh'
    from 1 events
    notice: Finished catalog run in 4.85 seconds
    

工作原理

让我们再看看这个新类:

class apache::port8000 inherits apache {
    File["/etc/apache2/ports.conf"] {
        source => "puppet:///modules/apache/port8000.conf.apache",
    }
}

你可以从类名后看出,此类继承(inherits)自 apache 类。这将创建一个与 apache 类完全相同的副本,除了跟随其后的变化。

如下的代码片段:

File["/etc/apache2/ports.conf"] {

指定了我们想要改变父类中名为 /etc/apache2/ports.conf 的 file 资源 (注意 File 是首字母大写的,这意味着,我们指的是现有的资源,而不是定义一个新资源)。

如下的代码片段:

source => "puppet:///modules/apache/port8000.conf.apache",

意味着我们将使用一个新的值覆盖父类中 source 资源的参数值。 如果我们复制整个 apache 类的定义并改变资源 source 的值,那么结果将是完全一样的:

class apache {
    package { "apache2-mpm-worker": ensure => installed }

    service { "apache2":
        enable  => true,
        ensure  => running,
        require => Package["apache2-mpm-worker"],
    }

    file { "/etc/apache2/ports.conf":
        source => "puppet:///modules/apache/port8000.conf.apache",
        notify => Service["apache2"],
    }
}

更多用法

首先覆盖被继承的类看上去有些复杂。然而一旦你掌握了这种思想,就会发现这实际上很简单。 这是一种使你的配置清单更具可读性的强大方式,因为这样消除了大量的重复代码, 使你仅专注于编写不同的代码部分。下面给出几种使用覆盖的方法。

取消参数的定义

有时候你不想改变一个参数的值,只是想完全移除它的值。 为了实现这一点,可以使用 undef 值覆盖原有值。 其结果就像是此参数从未在先前定义过一样。

class apache::norestart inherits apache {
    File["/etc/apache2/ports.conf"] {
        notify => undef,
    }
}
使用 +> 操作符添加额外的值

与替换一个值类似,你可能想要在父类定义的基础上添加更多的值。 使用 plusignment 操作符 +> 可以实现这一功能:

class apache::ssl inherits apache {
    file { "/etc/ssl/certs/cookbook.pem":
        source => "puppet:///modules/apache/cookbook.pem",
    }

    Service["apache2"] {
        require +> File["/etc/ssl/certs/cookbook.pem"],
    }
}

操作符 +> 在父类定义的值的基础上添加一个值(或使用方括号括起来的一个数组)。 对于上面的例子,我们最终得到的代码相当于:

service { "apache2":
    enable  => true,
    ensure  => running,
    require => [ Package["apache2-mpm-worker"], File["/etc/ssl/certs/
 cookbook.pem"] ],
}
禁用资源

继承和覆盖最常见的用途之一就是禁用服务或其他资源:

class apache::disabled inherits apache {
    Service["apache2"] {
        enable => false,
        ensure => stopped,
    }
}

参见本书