

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 示例 5：使用属性
<a name="cookbooks-101-basics-attributes"></a>

**重要**  
该 AWS OpsWorks Stacks 服务于 2024 年 5 月 26 日终止，新客户和现有客户均已禁用。我们强烈建议客户尽快将其工作负载迁移到其他解决方案。如果您对迁移有疑问，请通过 re [AWS : Post 或通过 Pre](https://repost.aws/) mium Su [AWS pp](https://aws.amazon.com/support) ort 与 AWS 支持 团队联系。

前面几节中的配方对除平台之外的一切内容均使用了硬编码值。如果您要在多个配方中使用相同的值，则此方法可能不太方便。您可以通过在说明书中包含属性文件，独立于配方单独定义值。

属性文件是一个 Ruby 应用程序，该应用程序可将值分配给一个或多个属性。它必须位于说明书的 `attributes` 文件夹中。Chef 将属性纳入到节点对象中，任何配方均可通过引用相应属性来使用属性值。本主题介绍了如何从[迭代](cookbooks-101-basics-ruby.md#cookbooks-101-basics-ruby-iteration)中修改配方以使用属性。以下是原始配方，供您参考。

```
[ "/srv/www/config", "/srv/www/shared" ].each do |path|
  directory path do
    mode 0755
    owner 'root'
    group 'root'
    recursive true
    action :create
  end
end
```

以下内容定义了子目录名称、模式、所有者和组值的属性。

```
default['createdir']['shared_dir'] = 'shared'
default['createdir']['config_dir'] = 'config'
default['createdir']['mode'] = 0755
default['createdir']['owner'] = 'root'
default['createdir']['group'] = 'root'
```

注意以下几点：
+ 每个定义均以*属性类型*开头。

  如果某个属性被定义多次 (可能在不同的属性文件中)，则属性类型会指定该属性的优先级，这样可确定将哪个定义纳入到节点对象中。有关更多信息，请参阅 [属性优先顺序](workingcookbook-attributes-precedence.md)。此示例中的所有定义都具有 `default` 属性类型，该属性类型是用于此目的的通用类型。
+ 属性具有嵌套名称。

  节点对象基本上是可具有任意嵌套深度的哈希表，因此属性名称可以且通常是嵌套的。该属性文件遵循使用包含说明书名称 `createdir` 的嵌套名称作为第一个元素的标准做法。

使用 createdir 作为属性的第一个元素的原因是，当您运行 Chef 时，Chef 会将来自每个说明书的属性纳入到节点对象中。在 OpsWorks Stacks 中，节点对象除了您定义的任何属性外，还包括[内置食谱](https://github.com/aws/opsworks-cookbooks)中的大量属性。在属性名称中包含说明书名称可以降低与来自其他说明书的属性名称相冲突的风险，特别是在您的属性具有诸如 `port` 或 `user` 等名称的情况下更是如此。除非您希望覆盖属性的值，否则请勿将属性命名为诸如 [`[:apache2][:user]`](attributes-recipes-apache.md#attributes-recipes-apache-user) 等名称。有关更多信息，请参阅 [使用自定义说明书属性](workingcookbook-cookbook-attributes.md)。

以下示例显示了使用属性而不是硬编码值的原始配方。

```
[ "/srv/www/#{node['createdir']['shared_dir']}", "/srv/www/#{node['createdir']['config_dir']}" ].each do |path|
  directory path do
    mode node['createdir']['mode']
    owner node['createdir']['owner']
    group node['createdir']['group']
    recursive true
    action :create
  end
end
```

**注意**  
如果要将属性值纳入到字符串中，请将其包含在 `#{}` 中。在前面的示例中，`#{node['createdir']['shared_dir']}` 将“shared”附加到“/srv/www/”。

**运行配方**

1. 运行 `kitchen destroy`，以便从全新的实例开始。

1. 将 `recipes/default.rb` 中的代码替换为前面的配方示例。

1. 创建一个名为 `createdir` 的 `attributes` 子目录，并添加一个名为 `default.rb` 的文件，其中包含属性定义。

1. 编辑 `.kitchen.yml`，从平台列表中删除 CentOS。

1. 运行 `kitchen converge`，然后登录到实例并验证存在 `/srv/www/shared` 和 `/srv/www/config`。

**注意**  
使用 OpsWorks Stacks，将值定义为属性还有其他好处；您可以使用[自定义 JSON](workingstacks-json.md) 在每个堆栈甚至每个部署的基础上覆盖这些值。它可用于各种目的，其中包括：  
您可以自定义配方的行为，例如配置设置或用户名，而无需修改说明书。  
例如，您可以对不同的堆栈使用同一说明书，并使用自定义 JSON 来指定特定堆栈的关键配置设置。这样可以节省您修改说明书或对每个堆栈使用不同说明书所需的时间和精力。
您不必将潜在敏感信息 (如数据库密码) 置于您的说明书存储库中。  
您可以使用属性来定义默认值，然后采用自定义 JSON 用实际值来覆盖该值。
有关如何使用自定义 JSON 覆盖属性的更多信息，请参阅[覆盖属性](workingcookbook-attributes.md)。

属性文件名为 `default.rb`，因为它是一个 Ruby 应用程序，但是该应用程序相当简单。这意味着您可以使用条件逻辑来根据操作系统指定属性值。在[条件逻辑](cookbooks-101-basics-ruby.md#cookbooks-101-basics-ruby-conditional)中，您在配方中为不同 Linux 系列指定了不同的子目录名称。使用属性文件，您可以将条件逻辑放在属性文件中。

以下属性文件使用 `value_for_platform` 指定不同的 `['shared_dir']` 属性值，具体取决于操作系统。对于其他条件，您可以使用 Ruby `if-elsif-else` 逻辑或 `case` 语句。

```
data_dir = value_for_platform(
  "centos" => { "default" => "shared" },
  "ubuntu" => { "default" => "data" },
  "default" => "user_data"
)
default['createdir']['shared_dir'] = data_dir
default['createdir']['config_dir'] = "config"
default['createdir']['mode'] = 0755
default['createdir']['owner'] = 'root'
default['createdir']['group'] = 'root'
```

**运行配方**

1. 运行 `kitchen destroy`，以便从全新的实例开始。

1. 将 `attributes/default.rb` 中的代码替换为前面的示例。

1. 编辑 `.kitchen.yml`，将 CentOS 平台添加至平台部分，如 [条件逻辑](cookbooks-101-basics-ruby.md#cookbooks-101-basics-ruby-conditional) 中所述。

1. 运行 `kitchen converge`，然后登录到实例，以验证目录是否存在。

完成后，运行 `kitchen destroy` 以终止实例。下一个示例将使用新说明书。