Puppet4.xではじめるサーバ設定自動化 10（コマンド実行）

Puppet4.xではじめるサーバ設定自動化の、コマンド実行についてです。
全体的な目次は、[ここ](http://qiita.com/kijibato/items/ae510dfa91884bdd9f61)を参照してください。
主にCentOS 7を例に紹介しているので、その他OSについては読み替えてください。

# Puppetでコマンド実行

Puppetでコマンドを実行するには、execリソースを使います。
コマンドを使わないと設定できないものに使えますが、複雑なことをしようとするとメンテナンス性が悪くなるため、次の点に注意が必要です。

- 既存のリソースタイプでできることは、既存のリソースタイプを使う
- 何度実行しても問題がないようにする（コマンド実行不要なときに実行されないように）
- 複雑すぎることはしない（場合によっては、カスタムリソースを作ることを検討する）

# コマンド実行の定義

## 基本
コマンドの実行は、Puppetのマニフェストではexecリソースを使います。

```puppet:exec01.pp
exec { 'tar -xf /Volumes/nfs02/important.tar':
  cwd     => '/var/tmp',
  path    => '/usr/bin:/usr/sbin',
}

```

- execの右側のタイトルに、実行するコマンドを記載する
- cwd属性で、コマンドを実行するカレントディレクトリを指定
- path属性で、コマンドがあるパスを指定。複数ある場合:でつなぎます。上記例では、tarコマンドは、/usr/binか、/usr/sbinにあるものが使われます。

# 実行ユーザーの指定(user)

コマンドを特定のユーザーで実行するには、user属性を指定します。例えば、hogeというユーザーで実行する必要がある場合、次のように書きます。

```puppet:exec02.pp
exec { 'tar -xf /Volumes/nfs02/important.tar':
  cwd     => '/var/tmp',
  path    => '/usr/bin:/usr/sbin',
  user    => 'hoge',
}

```

インストール手順の制約などで、特定のユーザーでコマンドを実行する必要がある場合などに使います。

# タイムアウト設定(timeout)

execリソースでのコマンド実行は、デフォルト300秒でタイムアウト（一定時間で終わらなければ失敗）するようになっています。いつまで待っても終わらなくなるのを防止するためですが、常に300秒以上かかるコマンドには、timeout属性を指定しましょう。

```puppet:exec03.pp
exec { 'tar -xf /Volumes/nfs02/important.tar':
  cwd     => '/var/tmp',
  timeout => '600',
  path    => '/usr/bin:/usr/sbin',
}

```

なお、0に設定すると、タイムアウトしなくなりますが、コマンド実行で問題が起きてコマンドが終わらない場合、puppetの実行も終わらくなってしまうため適切な値を指定しておく方がよいです。

# コマンド実行不要ときに実行しないようにする

Puppetを実行するたびに、コマンドを実行しないようにする方法についてです。

## ファイルがなければ実行する(creates)

ファイルがないときに、コマンドを実行するようにしたい場合、creates属性を指定します。/var/tmp/myfileがない場合のみ、tarコマンドを実行する例です。

```puppet:exec04.pp
exec { 'tar -xf /Volumes/nfs02/important.tar':
  cwd     => '/var/tmp',
  creates => '/var/tmp/myfile',
  path    => '/usr/bin:/usr/sbin',
}

```

属性名が個人的には微妙と思っていますが、creates属性に指定したものがコマンド実行後にできるわけではないのに注意です。例えば、解凍すればそのファイルがあるとか、インストール後にそのファイルがあるなどで適切なファイルを指定する必要があります。
たまにパッケージのバージョン変更とかで、インストール後にそのファイルができなくなった場合などは、ファイルがないため何度も実行されることになってしまうので特に注意です。

## あるコマンド結果が0だったときに実行する(onlyif)

あるコマンドの結果が0だったときに、コマンドを実行したい場合、onlyif属性を指定します。リファレンスの例では、/var/log/messagesが100000バイト以上の場合、logrotateコマンドを実行する例です。（ログローテをPuppetでやるべきかは、別ですが…）

```puppet:exec05.pp
exec { 'logrotate':
  path   => '/usr/bin:/usr/sbin:/bin',
  onlyif => 'test `du /var/log/messages | cut -f1` -gt 100000',
}

```

## onlyifの逆(unless)

あるコマンドの結果が0 **でない** ときに、コマンドを実行したい場合、unless属性を指定します。リファレンスの例では、/usr/lib/cron/cron.allowにrootがなければ、/usr/lib/cron/cron.allowにrootを追記する例です。

```puppet:exec06.pp
exec { '/bin/echo root >> /usr/lib/cron/cron.allow':
  path   => '/usr/bin:/usr/sbin:/bin',
  unless => 'grep root /usr/lib/cron/cron.allow 2>/dev/null',
}

```

## 別のリソースが変更されたときだけ実行する(refreshonly)

別のリソースが変更されたときだけ、コマンドを実行する場合はrefreshonly属性にtrueを指定します。リファレンスの例では、/etc/aliasesが変更された場合、newaliasesを実行する例です。

```puppet:exec07.pp
# Pull down the main aliases file
file { '/etc/aliases':
  source => 'puppet://server/module/aliases',
}

# Rebuild the database, but only when the file changes
exec { newaliases:
  path        => ['/usr/bin', '/usr/sbin'],
  subscribe   => File['/etc/aliases'],
  refreshonly => true,
}
```

# 実行順序に注意！

Puppet 4.0以降は、マニフェストの反映順序が上から順番になっていますが、バージョン3.xでは上から順ではないため実行順序を指定する必要があります。なお、4.0以降でも、Puppetの設定変更で順序を変更できるため、execリソースの順序性があるような場合は注意が必要です。

実行順序自体の設定はここ参照

- https://docs.puppet.com/puppet/latest/configuration.html#ordering

実行順序の指定はここ参照

- https://docs.puppet.com/puppet/latest/lang_relationships.html

# 参考
execリソースの詳しい情報は、下記参照です。今回は、ほぼここの抜粋です。

- https://docs.puppet.com/puppet/latest/reference/type.html#exec