

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 範例 4：新增流程控制
<a name="cookbooks-101-basics-ruby"></a>

**重要**  
 AWS OpsWorks Stacks 此服務已於 2024 年 5 月 26 日終止，並已針對新客戶和現有客戶停用。我們強烈建議客戶盡快將其工作負載遷移至其他解決方案。如果您對遷移有任何疑問，請透過 [AWS re：Post](https://repost.aws/) 或透過 [AWS Premium Support](https://aws.amazon.com/support) 聯絡 AWS 支援 團隊。

有些配方只是一系列的 Chef 資源。在這種情況下，當您執行配方時，它僅會依序執行每個資源提供者。不過，有更精密的執行路徑通常會很有幫助。下列是兩個常見情況：
+ 您想要某個配方以不同的屬性設定多次執行相同的資源。
+ 您想要在不同的作業系統上使用不同的屬性設定。

您可以將 Ruby 控制結構併入配方以處理類似的情況。本節示範如何修改[範例 3：建立目錄](cookbooks-101-basics-directories.md)中的配方來處理這兩種情況。

**Topics**
+ [重複](#cookbooks-101-basics-ruby-iteration)
+ [條件式邏輯](#cookbooks-101-basics-ruby-conditional)

## 重複
<a name="cookbooks-101-basics-ruby-iteration"></a>

[範例 3：建立目錄](cookbooks-101-basics-directories.md)示範如何使用 `directory` 資源來建立目錄或目錄鏈。不過，假設您要建立兩個不同的目錄 `/srv/www/config` 和 `/srv/www/shared`。您可以分別為每個目錄實作目錄資源，但如果您想要建立非常多的目錄，這種方法太過繁瑣。下列配方示範處理此任務的較簡單方式。

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

配方使用包含子目錄路徑的字串集合，而不是為每個子目錄分別使用目錄資源。Ruby `each` 方法從第一個元素開始，為每個集合元素執行一次資源。資源中以 `path` 變數表示元素，在本例中表示目錄路徑。您可以輕鬆地調整此範例建立任意數目的子目錄。

**執行配方**

1. 留在 `createdir` 目錄中，接下來數個範例都會使用該技術指南。

1. 若尚未完成，請執行 `kitchen destroy` 以從乾淨的執行個體開始。

1. 以範例取代 `default.rb` 中的程式碼並執行 `kitchen converge`。

1. 登入執行個體，您會在 `/srv` 下看到新建立的目錄。

您可以使用雜湊表為每次重複指定兩個值。下列配方建立 `/srv/www/config` 和 `/srv/www/shared`，每個都有不同的模式。

```
{ "/srv/www/config" => 0644, "/srv/www/shared" => 0755 }.each do |path, mode_value|
  directory path do
    mode mode_value
    owner 'root'
    group 'root'
    recursive true
    action :create
  end
end
```

**執行配方**

1. 若尚未完成，請執行 `kitchen destroy` 以從乾淨的執行個體開始。

1. 以範例取代 `default.rb` 中的程式碼並執行 `kitchen converge`。

1. 登入執行個體，您會在指定模式的 `/srv` 下看到新建立的目錄。

**注意**  
OpsWorks Stacks 配方通常會使用此方法來從[堆疊組態和部署 JSON](workingcookbook-json.md) 擷取值，基本上是大型雜湊資料表，並將其插入資源。如需範例，請參閱 [部署配方](create-custom-deploy.md)。

## 條件式邏輯
<a name="cookbooks-101-basics-ruby-conditional"></a>

您也可以使用 Ruby 條件式邏輯來建立多個執行分支。下列配方使用 `if-elsif-else` 邏輯來擴展前一個範例，以便建立名為 `/srv/www/shared` 的子目錄，但只限於 Debian 和 Ubuntu 系統。針對所有其他系統，其會記錄顯示在 Test Kitchen 輸出中的錯誤訊息。

```
if platform?("debian", "ubuntu")
  directory "/srv/www/shared" do
    mode 0755
    owner 'root'
    group 'root'
    recursive true
    action :create
  end
else
  log "Unsupported system"
end
```

**執行範例配方**

1. 如果您的執行個體仍在執行中，請執行 `kitchen destroy` 關閉它。

1. 以範例程式碼取代 `default.rb` 中的程式碼。

1. 編輯 `.kitchen.yml` 將 CentOS 6.4 系統新增到平台清單。檔案的 `platforms` 區段看起來應該如下：

   ```
   ...
   platforms:
     - name: ubuntu-12.04
     - name: centos-6.4
   ...
   ```

1. 執行 `kitchen converge`，這會建立執行個體，並依序為 `.kitchen.yml` 中的每個平台執行配方。
**注意**  
如果您想收歛成僅只一個執行個體，請將執行個體名稱新增為參數。例如，若要將配方收歛成僅在 Ubuntu 平台，請執行 `kitchen converge default-ubuntu-1204`。如果您忘記了平台名稱，只要執行 `kitchen list` 即可。

您應該會在 Test Kitchen 的 CentOS 部分看到您的日誌訊息，它看起來類似如下：

```
...
Converging 1 resources
Recipe: createdir::default
* log[Unsupported system] action write[2014-06-23T19:10:30+00:00] INFO: Processing log[Unsupported system] action write (createdir::default line 12)
[2014-06-23T19:10:30+00:00] INFO: Unsupported system
       
[2014-06-23T19:10:30+00:00] INFO: Chef Run complete in 0.004972162 seconds
```

您現在可以登入執行個體並驗證是否已建立目錄。不過，現在無法僅執行 `kitchen login`。您必須透過附加平台名稱來指定執行個體，例如 `kitchen login default-ubuntu-1204`。

**注意**  
如果 Test Kitchen 命令接受執行個體名稱，您就不需要輸入完整的名稱。Test Kitchen 會將執行個體名稱視為 Ruby 規則表達式，因此您只需要可提供唯一相符的足夠字元即可。例如，您可以透過執行 `kitchen converge ub` 收歛成僅 Ubuntu 執行個體，或透過執行 `kitchen login 64` 登入 CentOS 執行個體。

此時可能有的疑問，是配方如何知道它在哪個平台上執行。Chef 為收集系統資料 (包括平台) 的每個回合都執行名為 [Ohai](https://docs.chef.io/ohai.html) 的工具，在稱為「節點物件」**的結構中用一組屬性表示此資料。Chef `platform?` 方法使用括號來比較系統和 Ohai 平台值，如果其中一項符合即傳回 true。

您可以使用 `node['attribute_name']` 直接參考您程式碼中的節點屬性值。例如，平台值以 `node['platform']` 表示。例如，您可能將前述範例撰寫如下。

```
if node[:platform] == 'debian' or node[:platform] == 'ubuntu'
  directory "/srv/www/shared" do
    mode 0755
    owner 'root'
    group 'root'
    recursive true
    action :create
  end
else
  log "Unsupported system"
end
```

在配方中包含條件邏輯的常見原因，是為了因應不同的 Linux 系列有時使用不同的套件名稱、目錄等等。例如，Apache 套件名稱在 CentOS 系統為 `httpd`，在 Ubuntu 系統為 `apache2`。

如果只是因為不同的系統需要不同的字串，Chef [http://docs.chef.io/dsl_recipe.html#value-for-platform](http://docs.chef.io/dsl_recipe.html#value-for-platform) 方法是比 `if-elsif-else` 更簡單的解決方案。下列配方在 CentOS 系統中建立 `/srv/www/shared` 目錄，在 Ubuntu 系統中建立 `/srv/www/data` 目錄，在所有其他系統中建立 `/srv/www/config`。

```
data_dir = value_for_platform(
  "centos" => { "default" => "/srv/www/shared" },
  "ubuntu" => { "default" => "/srv/www/data" },
  "default" => "/srv/www/config"
)
directory data_dir do
  mode 0755
  owner 'root'
  group 'root'
  recursive true
  action :create
end
```

`value_for_platform` 將適當的路徑指派給 `data_dir`，而 `directory` 資源則使用該值建立目錄。

**執行範例配方**

1. 如果您的執行個體仍在執行中，請執行 `kitchen destroy` 關閉它。

1. 以範例程式碼取代 `default.rb` 中的程式碼。

1. 執行 `kitchen converge`，然後登入每個執行個體，以驗證是否有適當的目錄。