1. 1. 用語
  2. 2. 目的
  3. 3. 基本方針
  4. 4. バージョン管理
  5. 5. 間隔、インデント、ホワイトスペース
    1. 5.1 配列とハッシュ
  6. 6. 引用符
    1. 6.1. エスケープ文字
  7. 7. コメント
    1. 7.1 ドキュメント化のためのコメント
  8. 8. モジュールメタデータ
    1. 8.1 依存関係
  9. 9. リソース
    1. 9.1. リソース名
    2. 9.2 矢印の位置合わせ
    3. 9.3. 属性の順序
    4. 9.4. リソースの配置
    5. 9.5. シンボリックリンク
    6. 9.6. ファイルモード
    7. 9.6.5. 複数のリソース
    8. 9.7. レガシースタイルのデフォルト
    9. 9.8. リソース属性のインデントと位置合わせ
  10. 10. クラスと定義型
    1. 10.1. 個別ファイル
    2. 10.2. クラスの内部構成と定義型
    3. 10.3. パブリックとプライベート
    4. 10.4. チェイニングアローの構文
    5. 10.5. ネストしたクラスまたは定義型
    6. 10.6. パラメータの表示順序
    7. 10.7. パラメータのデフォルト値
    8. 10.8. エクスポートリソース
    9. 10.9 パラメータのインデントと位置合わせ
  11. 11. クラス
    1. 11.1. クラスの継承
    2. 11.2. パブリック公開されているモジュールについて
  12. 12. 定義リソース型
  13. 13. 変数
    1. 13.1. factsの参照
    2. 13.2. 名前空間指定変数
    3. 13.3. 変数のフォーマット
  14. 14. 条件文
    1. 14.1. リソース宣言をシンプルに
    2. 14.2. caseステートメントとセレクタのデフォルト値
  15. 15. 関数
  16. 16. Hiera
  17. 17. モジュールの例
  18. 16. モジュールのドキュメント
    1. 18.1 README
    2. 18.2. Puppetコードのドキュメント化
    3. 18.3 CHANGELOG
  19. 19. 検証とテスト

Puppet言語スタイルガイド

このスタイルガイドは、Puppet 4以降に適用されます。 Puppet 3のサポートは廃止されていますが、以前のコードを維持する必要がある場合のために、このスタイルガイドにはPuppet 3向けのガイドラインが含まれています。

スタイルガイドのセクション15に新しい関数のセクションが追加されました。 そのため、同セクション以降のセクション番号が変わります。

1. 用語

明示的に記載のない限り、このドキュメント内の説明はすべて、明確にPuppet(Puppetモジュール、Puppetクラスなど)を対象としています。 すべてのトピック1つひとつに「Puppet」という名前を付記してはいません。

このドキュメント内のキーワード、「する必要がある(MUST)」、「してはならない(MUST NOT)」、「要求される(REQUIRED)」、「することになる(SHALL)」、「することはない(SHALL NOT)」、「する必要がある(SHOULD)」、「しない方が良い(SHOULD NOT)」、「推奨される(RECOMMENDED)」、「しても良い(MAY)」、「任意で(OPTIONAL)」は、RFC 2119に記載されたとおりに解釈されるものとします。

2. 目的

このスタイルガイドの目的は、Puppet言語(特にモジュール間)において一貫性のあるフォーマットを推進することで、Puppetモジュールのユーザおよび開発者に対して、参考になる共通のパターン、設計、スタイルを提供することです。 また、コードとモジュールの構造に一貫性があると、継続的な開発と貢献が容易になります。

モジュール内でpuppet-lintmetadata-json-lintを使用して、スタイルガイドに対する適合性をチェックすることを推奨します。

3. 基本方針

このガイドで、Puppetコードの開発中に発生しうるすべての状況を網羅することはできません。 何らかの判断を下す必要がある場合は、以下の原則に留意してください。

  1. 読みやすさが重要

    同じような選択肢から選ぶ必要がある場合は、より読みやすい方を選択します。 これは主観的な方法ですが、作成したコードが3か月後も判読可能であれば、非常に良いスタートと言えるでしょう。 特に推奨されるのは、読みやすい差分が生成されるコードです。

  2. 鍵となる範囲設定および単純性

    迷った場合は、より単純な書き方を選びます。 モジュールには、タスクを完了するために必要な関連リソースを含める必要があります。 モジュールの機能を記述するときに、「および(and)」という単語を使っている場合、モジュールの分割を検討してください。 すべてのクラスとパラメータは、1つの目標の達成だけに焦点を合わせることを推奨します。

  3. ソフトウェアの一部としてのモジュール

    少なくとも、モジュールをソフトウェアとして扱う必要があります。 意思決定を下す場合、長期的に見て保守しやすい方法を選択します。

4. バージョン管理

モジュールにはバージョン管理が必要です。 セマンティックバージョニングの使用を推奨します。

セマンティックバージョニング(SemVer)において、バージョン番号をx.y.zで指定した場合、以下の意味を持ちます。

  • ‘x’の増加は、下位互換性がない変更や完全な書き換えなどの大きな変更を示します。
  • ‘y’の増加は、下位互換性のある新機能の追加などの小さな変更を示します。
  • ‘z’の増加は、下位互換性のあるバグ修正などのパッチを示します。

5. 間隔、インデント、ホワイトスペース

モジュールのマニフェストは、 間隔、インデント、ホワイトスペースについてベストプラクティスに従う必要があります。

マニフェスト:

  • 2スペースのソフトタブを使用すること
  • 「Tab」キーを使用しないこと
  • 末尾にホワイトスペースを使用しないこと
  • すべてのリソース属性およびパラメータの定義の後に、カンマを付加すること
  • 最後の行の末尾に改行を付加すること
  • リソース型と左中括弧の間にスペースを1つ、左中括弧とタイトルの間にスペースを1つ使用し、タイトルとコロンの間にはスペースを使用しないこと

適切な例

file { '/tmp/foo':

不適切な例

# space between title and colon
file { '/tmp/foo' :

# no spaces
file{'/tmp/foo':

# too many spaces
file     { '/tmp/foo':
  • 実用的でない場合を除いて、1行を140字以内にすること
  • 依存チェインを使用する場合を除いて、リソース同士の間に空白行を1行入れること
  • 属性ブロック内でハッシュロケット(=>)を使用する場合、読みやすさを重視して、最も長いリソースキーの後にスペースを1つ入れてハッシュを揃えること

5.1 配列とハッシュ

配列とハッシュを読みやすくするには、ほとんどの場合、各要素を別々の行に分けると良いでしょう。

非常に短いケースなどで構造体が全体的に読みやすくなる場合のみ、1行に複数の要素を記述します。 配列とハッシュを分割する場合、以下の要素を含む必要があります。

  • それぞれを個別の行に記述した要素
  • 1レベルインデントされた新しい要素行
  • そのデータ型の構文に対してのみ使用される最初の行と最後の行

適切な例

# array with multiple elements on multiple lines
service { 'foo':
  require => [
    File['foo_config'],
    File['foo_sysconfig'],
  ],
}

# hash with multiple elements on multiple lines
$myhash = {
  key       => 'some value',
  other_key => 'some other value',
}

不適切な例

# array with multiple elements on same line
service { 'foo':
  require => [ File['foo_config'], File['foo_sysconfig'], ],
}

# hash with multiple elements on same line
$myhash = { key => 'some value', other_key => 'some other value', }

# array with multiple elements on different lines, but syntax and element share a line
service { 'foo':
  require => [ File['foo_config'],
    File['foo_sysconfig'],
  ],
}

# hash with multiple elements on different lines, but syntax and element share a line
$myhash = { key => 'some value',
  other_key     => 'some other value',
}

# array with indention of elements past two spaces
service { 'foo':
  require => [
              File['foo_config'],
              File['foo_sysconfig'],
  ],
}

6. 引用符

すべての文字列は一重引用符で囲む必要があります。ただし、一部の例外を除きます。

以下の条件に合致する場合、文字列を一重引用符で囲む必要はありません。

  • 文字列に変数が含まれる。
  • 文字列に一重引用符が含まれる。
  • 文字列に、一重引用符で囲まれた文字列ではサポートされていないエスケープ文字が含まれる。
  • 文字列に、present/absentなどの列挙可能な一連のオプションが含まれる。この場合、一重引用符の使用は任意になります。
  • 変数を文字列の中に挿入する場合はすべて、中括弧で囲む必要があります。 以下に例を示します。

    適切な例:

    "/etc/${file}.conf"
    "${facts['operatingsystem']} is not supported by ${module_name}"
    

    不適切な例:

    "/etc/$file.conf"
    "$facts['operatingsystem'] is not supported by $module_name"
    
  • 文字列に一重引用符が含まれる場合、エスケープ文字ではなく二重引用符を使用します。ただし、追加で不適切な量のエスケープ処理が必要になる場合を除きます。

    適切な例:

    warning("Class['apache'] parameter purge_vdir is deprecated in favor of purge_configs")
    

    不適切な例:

    warning('Class[\'apache\'] parameter purge_vdir is deprecated in favor of purge_configs')
    

6.1. エスケープ文字

Puppetでは、エスケープ文字としてバックスラッシュを使用します。

文字列が一重引用符またニ重引用符で囲まれている場合、バックスラッシュをエスケープ処理すると、この特別な意味が解除されます(\\)。つまり、結果の文字列に含めるバックスラッシュ1つに対して2つのバックスラッシュを使用します。 例を挙げると、2つのバックスラッシュをリテラルとして文字列に含めるには、合計4つのバックスラッシュを使用します。

バックスラッシュなどの後続文字を含めるために、正式に認められていないエスケープ文字を使用することは避けてください。

4桁に満たない16進数(例:\u040)を使ってUnicode文字をエスケープ処理すると、バックスラッシュの後にその文字列(例:u040)が付加されます (同時に、認識されないエスケープ文字に対する警告が発生します)。 4以外の16進数を使用する場合は、長いフォーマットのu{digits}を使用します。

7. コメント

コメントにはハッシュコメント(# This is a comment)を使用します。 コメントの内容は、そのコードの 動作方法(how)ではなく、理由(why)を示すものにします。

Puppetコードでは/* */コメントは使用しないでください。

適切な例:

# Configures NTP
file { '/etc/ntp.conf': ... }

不適切な例:

/* Creates file /etc/ntp.conf */
file { '/etc/ntp.conf': ... }

7.1 ドキュメント化のためのコメント

それぞれのクラス、定義型、関数、リソース型およびプロバイダには、Puppet Strings向けのドキュメント化コメントを含める必要があります。

ドキュメント化に関する推奨事項の一覧については、このガイドのドキュメント化セクションを参照してください。 ドキュメント化コメントを使用する場合、要素名の前にコメントを付加します。

8. モジュールメタデータ

すべてのモジュールに対して、メタデータをmetadata.jsonファイル内に定義する必要があります。

メタデータは以下のフォーマットに従います。

{
  "name": "examplecorp-mymodule",
  "version": "0.1.0",
  "author": "Pat",
  "license": "Apache-2.0",
  "summary": "A module for a thing",
  "source": "https://github.com/examplecorp/examplecorp-mymodule",
  "project_page": "https://github.com/examplecorp/examplecorp-mymodule",
  "issues_url": "https://github.com/examplecorp/examplecorp-mymodules/issues",
  "tags": ["things", "stuff"],
  "operatingsystem_support": [
    {
      "operatingsystem":"RedHat",
      "operatingsystemrelease": [
        "5.0",
        "6.0"
      ]
    },
    {
      "operatingsystem": "Ubuntu",
      "operatingsystemrelease": [ 
        "12.04",
        "10.04"
     ]
    }
  ],
  "dependencies": [
    { "name": "puppetlabs/stdlib", "version_requirement": ">= 3.2.0 <5.0.0" },
    { "name": "puppetlabs/firewall", "version_requirement": ">= 0.4.0 <5.0.0" },
  ]
}

metadata.jsonのフォーマットに関する詳細なガイドは、こちらのドキュメントを参照してください。

8.1 依存関係

ハード依存関係を、モジュールのmetadata.jsonファイル内に明示的に宣言する必要があります。

ソフト依存関係はREADME.mdファイルに記述し、metadata.jsonファイル内のハード要件としては適用しません。 ソフト依存関係とは、特定のユースケースでのみ要求される依存関係です (例:rabbitmqモジュール )

ハード依存関係は、制限なしで宣言しないでください。

9. リソース

リソースは、システム構成をモデル化するための基本的単位です。 リソース宣言には利用可能な機能が多くあります。そのため、コードの可読性は極めて重要です。

9.1. リソース名

すべてのリソース名またはタイトルは、引用符で囲む必要があります。 タイトルが配列になっている場合、配列自体を囲むのではなく、配列内の各タイトルを引用符で囲みます。

適切な例:

package { 'openssh': ensure => present }

不適切な例:

package { openssh: ensure => present }

引用符に関するこの要件は、文字列として評価される式には適用されません。

9.2 矢印の位置合わせ

リソースの属性/値リスト内で、ハッシュロケット(=>)の位置を揃えることができます。 ハッシュロケットは、もっとも長い属性名の後にスペースを1つ空けて配置します。 ネストしたブロックは2スペース分インデントしますが、ネストブロック内でハッシュロケットの位置を揃えることができます(もっとも長い属性名の後にスペースを1つ空けて配置)。

各属性は別々の行に配置する必要があります。ただし、リソース宣言が非常に短い場合や単目的の場合は、1行で宣言しても構いません。

適切な例:

exec { 'hambone':
  path => '/usr/bin',
  cwd  => '/tmp',
}

exec { 'test':
  subscribe   => File['/etc/test'],
  refreshonly => true,
}

myresource { 'test':
  ensure => present,
  myhash => {
    'myhash_key1' => 'value1',
    'key2'        => 'value2',
  },
}

notify { 'warning': message => 'This is an example warning' }

不適切な例:

exec { 'hambone':
path  => '/usr/bin',
cwd => '/tmp',
}

file { "/path/to/my-filename.txt":
  ensure => file, mode => $mode, owner => $owner, group => $group,
  source => 'puppet:///modules/my-module/productions/my-filename.txt'
}

9.3. 属性の順序

リソース宣言にensure属性が含まれる場合、この属性を最初に指定する必要があります。こうすることで、ユーザは、リソースが作成されているのか、または削除されているのかを素早く特定できます。

適切な例:

file { '/tmp/readme.txt':
  ensure => file,
  owner  => '0',
  group  => '0',
  mode   => '0644',
}

特別な属性*(アスタリスク文字)をその他の属性と一緒に使用する場合、見やすいように最後に配置します。 同一ボディ内に複数のアスタリスクを含めることはできません。

適切な例

$file_ownership = {
  'owner' => 'root',
  'group' => 'wheel',
  'mode'  => '0644',
}

file { '/etc/passwd':
  ensure => file,
  *      => $file_ownership,
}

9.4. リソースの配置

マニフェスト内では、リソース型ではなく、リソース同士の論理関係に基づいてリソースをグループ化します。

適切な例:

file { '/tmp/dir':
  ensure => directory,
}

file { '/tmp/dir/a':
  content => 'a',
}

file { '/tmp/dir2':
  ensure => directory,
}

file { '/tmp/dir2/b':
  content => 'b',
}

不適切な例:

file { '/tmp/dir':
  ensure => directory,
}

file { '/tmp/dir2':
  ensure => directory,
}

file { '/tmp/dir/a':
  content => 'a',
}

file { '/tmp/dir2/b':
  content => 'b',
}

セミコロンで区切られた複数のリソースボディを使用できるのは、ローカルのdefaultボディと組み合わせる場合のみです。

適切な例:

$defaults = { < hash of defaults > }

file {
  default: 
    * => $defaults,;

  '/tmp/foo':
    content => 'foos content',
}

適切な例:defaultを含む繰り返しパターン:

$defaults = { < hash of defaults > }

file {
  default: 
    * => $defaults,;

  '/tmp/motd':
    content => 'message of the day',;

  '/tmp/motd_tomorrow':
    content => 'tomorrows message of the day',;
}

不適切な例:関係のないリソースのグループ化:

file {
  '/tmp/foo':
    owner    => 'admin',
    mode     => '0644',
    contents => 'this is the content',;

  '/opt/myapp':
    owner  => 'myapp-admin',
    mode   => '0644',
    source => 'puppet://<someurl>',;

  # etc
}

特定のリソースに対して同じ属性を2回以上設定することはできません。設定しようとすると、Puppetでコンパイルエラーが発生します。 以下に、具体的な説明を示します。

  • ハッシュを使用してリソースの属性を設定する場合、これらの属性のいずれに対しても、別の明示的な値を設定することはできません(例:ハッシュ内にmodeが含まれる場合、このリソースボディ内でmode => “0644”を設定することはできません)。
  • *自体が属性としてふるまうので、1つのリソースボディ内で*属性を複数回使用することはできません。
  • ハッシュ内の一部の属性を使用し、残りをオーバーライドする場合、ハッシュを使用して式ごとにデフォルトを設定するか、または+(結合)演算子を使用して2つのハッシュの属性を組み合わせます(右側のハッシュが左側のハッシュをオーバーライドします)。

9.5. シンボリックリンク

シンボリックリンクを宣言する場合、ensure => linkというensure値を使用し、target属性に明示的に値を指定します。 こうすることで、リンクの作成がより明確に分かります。

適切な例:

file { '/var/log/syslog':
  ensure => link,
  target => '/var/log/messages',
}

不適切な例:

file { '/var/log/syslog':
  ensure => '/var/log/messages',
}

9.6. ファイルモード

  • POSIXの数字表記は4桁で指定します。
  • POSIXのシンボリック表記は文字列で指定します。
  • Windowsのファイルモードは使用しません。代わりにaclモジュールを使用します。
  • 可能な限り、数字表記を使用します。
  • ファイルモード属性には、常に文字列を引用符で囲んで指定します。整数は使用しません。

適切な例:

file { '/var/log/syslog':
  ensure => file,
  mode   => '0644',
}

不適切な例:

file { '/var/log/syslog':
  ensure => present,
  mode   => 644,
}

9.6.5. 複数のリソース

同一ブロック内に複数のリソースを宣言できるのは、このリソース型に対するデフォルトのオプションセットがある場合のみです。

適切な例

file {
  default:
    ensure => 'file',
    mode   => '0666',;

  '/foo':
    user => 'root',;

  '/bar':
    user => 'staff',;
}

# Give the defaults a name if used several times
$our_default_file_attributes = { 
  'ensure' => 'file', 
  'mode'   => '0666', 
}
 
file {
  default:
    * => $our_default_file_attributes,;

  '/foo':
    user => 'root',;

  '/bar':
    user => 'staff',;
}


# spell out "magic" iteration
['/foo', '/bar'].each |$path| {
  file { $path:
    ensure => 'file',
  }
}

# spell out "magic" iteration
$array_of_paths.each |$path| {
  file { $path:
    ensure => 'file',
  }
}

不適切な例

file {
  '/foo':
    ensure => 'file',
    user   => root,
    mode   => '0666',;

  '/bar':
    ensure => 'file',
    user   => staff,
    mode   => '0774',;
}

file { ['/foo', '/bar']:
  ensure => 'file',
}
 
file { $array_of_paths:
  ensure => 'file',
}

9.7. レガシースタイルのデフォルト

レガシースタイルのデフォルトは使用を避けてください。 どうしても使用する場合、サイトマニフェストのグローバルスコープでのみ指定します。 これは、リソースのデフォルトが動的スコープを介して適用されると、デフォルトの宣言箇所とは離れた場所で予測のつかない作用を及ぼす可能性があるためです。

許容可能な例

# site.pp:
 
Package {
  provider => 'zypper',
}

不適切な例

# /etc/puppetlabs/puppet/modules/apache/manifests/init.pp
File {
  owner => 'nobody',
  group => 'nogroup',
  mode  => '0600',
}

concat { $config_file_path:
  notify  => Class['Apache::Service'],
  require => Package['httpd'],
}

9.8. リソース属性のインデントと位置合わせ

リソース属性は、タイトルから均一に2スペース分インデントする必要があります。

適切な例

file { '/foo':
  ensure => 'file',
  owner  => 'root',
}

不適切な例

# too many levels of indentation
file { '/foo':
    ensure => 'file',
    owner  => 'root',
}

# no indentation
file { '/foo':
ensure => 'file',
owner  => 'root',
}

# improper and non-uniform indentation
file { '/foo':
  ensure => 'file',
   owner => 'root',
}

# indented the wrong direction
  file { '/foo':
ensure => 'file',
owner  => 'root',
  }

ボディが複数ある場合、タイトルごとに個別の行に記述してインデントします。 複数ボディ間ですべての矢印の位置を合わせても構いませんが、ボディごとに位置を合わせた方が読みやすい場合、全体で位置を揃える必要はありません。

file {
  default:
    * => $local_defaults,;
 
  '/foo':
    ensure => 'file',
    owner  => 'root',
}

10. クラスと定義型

クラスおよびユーザ定義のタイプは、スコープおよび組織のガイドラインに従う必要があります。

10.1. 個別ファイル

すべてのクラスとリソース型定義(定義型)を、モジュールのmanifestsディレクトリ内の個別ファイルに指定します。 モジュールのmanifestディレクトリ内にある個々のファイルには、クラスまたはリソース型の定義以外は何も含めないでください。

適切な例:

# /etc/puppetlabs/puppet/modules/apache/manifests

# init.pp
class apache { }
# ssl.pp
class apache::ssl { }
# virtual_host.pp
define apache::virtual_host () { }

クラスと定義型を別々のファイルに指定する場合、init.pp内での宣言と機能上はまったく同じ効果がありますが、モジュール構造がハイライトされ、関数と構造が読みやすくなるというメリットがあります。

クラス、ノード定義、または定義型の外部に、リソースまたはincludeステートメントを配置した場合、これらはすべてのカタログに含まれます。 これには望ましくない影響が生じる可能性があり、必ずしも検出しやすいとは限りません。

適切な例:

#manifests/init.pp:
class { 'foo':
  include bar
}

不適切な例:

#manifests/init.pp:
class { 'foo':
  #...
}
include bar

10.2. クラスの内部構成と定義型

クラスと定義型は、1つのタスクを遂行するように構成する必要があります。 以下に、コード行の配置に関する一般的なレイアウトを、1行目から順番に1行ずつ示します。

すべてのクラスと定義型には、Puppet Strings向けのドキュメント化コメントを含める必要があります。 ドキュメント化に関する推奨事項の一覧については、このガイドのドキュメント化セクションを参照してください。 ドキュメント化コメントを使用する場合、要素名の前にコメントを付加します。

  1. 1行目:クラスまたは型の名前。
  2. 後続の行(該当する場合):パラメータの定義。パラメータは型付けする必要があります。
  3. 後続の行:パラメータ定義の後にincludeとバリデーションを指定します。includeはバリデーションの前後どちらにも配置できますが、それぞれをグループ化する必要があります。すべてのincludeとrequireを1グループにまとめて、すべてのバリデーションを別のグループにまとめます。
    • バリデーションですべてのパラメータが検証され、無効なパラメータが見つかった場合はカタログコンパイルが失敗します(例:ntp )。
  4. 後続の行(該当する場合):ローカル変数を宣言して変数のマンジングを実行します。
  5. 後続の行:リソースのデフォルトを宣言します。
  6. 後続の行:必要に応じて、リソースをオーバーライドします。

推奨されたスタイルに従った例を以下に示します。

init.pp内:

# The `myservice` class installs packages, ensures the state of 'myservice', and creates 
# a tempfile with given content. If the tempfile contents contains digits,
# they are filtered out.
#
# @param service_ensure the wanted state of services
# @param package_list the list of packages to install, at least one must be given, or an 
# error of unsupported 'os' is raised
# @param tempfile_contents the text to be included in the tempfile, all digits are 
# filtered out if present
#
class myservice (
  Enum['running', 'stopped'] $service_ensure,
  String                     $tempfile_contents,
  Optional[Array[String[1]]] $package_list = undef,
) {
  # Example of additional assertion with a better error message than just saying that
  # there was a type mismatch for $package_list.
  #
  # The list can be "not given", or have an empty list of packages to install
  # Here an assertion is made that the list is an Array of at least one String, and that 
  # the String is at least one character long.
  #
  assert_type(Array[String[1], 1], $package_list) |$expected, $actual| {
    fail("Module ${module_name} does not support ${facts['os']['name']} as the list of packages is of type ${actual}")
  }

  package { $package_list:
    ensure => present,
  }

  file { "/tmp/${variable}":
    ensure   => present,
    contents => regsubst($tempfile_contents, '\d', '', 'G'),
    owner    => '0',
    group    => '0',
    mode     => '0644',
  }

  service { 'myservice':
    ensure    => $service_ensure,
    hasstatus => true,
  }
 
  Package[$package_list] -> Service['myservice']
}

モジュールhiera.yaml内:

---
version: 5
defaults:
  data_hash: yaml_data
 
# The default values can be merged if you want to extend with additional packages
# If not, use 'default_hierarchy' instead of 'hierarchy'
#
hierarchy:
- name: 'Per Operating System'
  path: "os/%{os.name}.yaml"
- name: 'Common'
  path: 'common.yaml'

モジュールdata/common.yaml内:

myservice::service_ensure: running

モジュールdata/os/centos.yaml内:

myservice::package_list:
- 'myservice-centos-package'

モジュールdata/os/solaris.yaml内:

myservice::package_list:
- 'myservice-solaris-package1'
- 'myservice-solaris-package2'

10.3. パブリックとプライベート

可能な限り、モジュールのクラスと定義型をパブリックとプライベートに分割することを推奨します。 パブリッククラスとパブリック定義型には、ユーザによる設定やカスタマイズの対象となることを意図したモジュール部分を含めます。一方、プライベートクラスには、ユーザによるパラメータを介した変更を想定しない要素を含めます。 クラスおよび定義型をパブリックまたはプライベートとして区別することで、再利用可能で読みやすいコードを作成できます。

ユーザがクラスの内容を把握しやすくなるように、すべてのパブリッククラスに必ず全面的なコメントを記述し、ドキュメント内にパブリッククラスとプライベートクラスを明記します。 ドキュメント化タグ「@api private」および「@api public」を使用すると、より明確になります。

10.4. チェイニングアローの構文

ほとんどのケースで、チェイニングアローではなく、関係メタパラメータを使用します。 ただし、多数の相互依存項目または順序固定項目がある場合、チェイニング構文を使用しても構いません。 チェイン演算子は右側にあるオペランドと同じ行に記述します。 チェイニングアローは左から右に向けて使用します。

適切な例:

# Points left to right
Package['httpd'] -> Service['httpd']
# On the line of the right-hand operand
Package['httpd']
-> Service['httpd']

不適切な例:

# arrows are not all pointing to the right
Service['httpd'] <- Package['httpd']
 
# Must be on the right-hand operand's line
Package['httpd'] ->
Service['httpd']

10.5. ネストしたクラスまたは定義型

クラスまたは定義リソース型を、別のクラスや定義型内に定義してはいけません。 クラスおよび定義型は、できるだけノードスコープの近くで宣言します。 クラスまたはユーザ定義のタイプが別のクラスまたはユーザ定義のタイプを必要とするときに、該当するクラスまたはタイプがない場合、モジュールに完全な失敗が発生しないよう確認してください。

非常に不適切な例:

class apache {
  class ssl { ... }
}

非常に不適切な例:

class apache {
  define config() { ... }
}

10.6. パラメータの表示順序

パラメータ化クラスと定義型の宣言では、必須パラメータをオプションパラメータ(デフォルト付きパラメータ)の前に配置する必要があります。 必須パラメータは、undefを含むいずれの値も設定されていないパラメータです。 たとえば、パスワードやIPアドレスなどのパラメータには妥当なデフォルト値がない場合があります。

パラメータをnamevarのように扱い、$titleまたは$nameをデフォルトにしても、それが必須パラメータになるわけではないことに注意してください。 ここに推奨する順序に従って、リストする必要があります。

適切な例:

class dhcp (
  $dnsdomain,
  $nameservers,
  $default_lease_time = 3600,
  $max_lease_time     = 86400,
) {}

不適切な例:

class ntp (
  $options   = "iburst",
  $servers,
  $multicast = false,
) {}

10.7. パラメータのデフォルト値

クラスおよび定義型のパラメータにデフォルト値を追加すると、モジュールが使いやすくなります。 Puppet 4.9.0では、Hieraデータをモジュールで使用して、クラスパラメータの自動探索を利用することができます。 詳細については、パラメータ自動探索についてのマニュアルを参照してください。

Puppet 4.9.0より前のバージョンでは、「params.pp」パターンを使用します。 単純なケースでは、クラスまたは定義型内で直接デフォルト値を指定することもできます。

パラメータのデータ型を宣言する場合、型の自動アサーションが提供されるので注意が必要です。

適切な例:

# parameter defaults provided via APL > puppet 4.9.0
class my_module (
  String $source,
  String $config,
) {
  # body of class
}

モジュールのルートでhiera.yamlを使用する例:

---
version: 5
default_hierarchy: 
- name: 'defaults'
  path: 'defaults.yaml'
  data_hash: yaml_data

data/defaults.yamlファイルを使用する例:

mymodule::source: 'default source value'
mymodule::config: 'default config value'

この場合、デフォルト階層に値が指定されるので、デフォルト値はオーバーライド値に結合されません。 デフォルト値をオーバーライド値に結合するには、default_hierarchyhierarchyに変更します。

Puppet 4.8以前

# using params.pp pattern < Puppet 4.9.0
class my_module (
  String $source = $mymodule::params::source,
  String $config = $mymodule::params::config,
) {
  # body of class
}

10.8. エクスポートリソース

エクスポートリソースは、オプトアウトではなくオプトインすることを推奨します。 明確に要求されない限り、エクスポートリソースの機能をデフォルトで使用するようにモジュールを作成するべきではありません。

エクスポートリソースを使用するときは、collect_exportedプロパティを指定します。

エクスポートリソースは、検索式を使用して、個別にエクスポートおよび収集することを推奨します。理想的には、ユーザ定義タグをパラメータとして使用すると、タグを使用した環境またはカスタムfactsによる個別の収集が可能になります。

適切な例:

define haproxy::frontend (
  $ports            = undef,
  $ipaddress        = [$::ipaddress],
  $bind             = undef,
  $mode             = undef,
  $collect_exported = false,
  $options          = {
    'option' => [
      'tcplog',
    ],
  },
) {
  # body of define
}

10.9 パラメータのインデントと位置合わせ

クラスまたはユーザ定義のタイプに対するパラメータは、タイトルから均一に2スペース分インデントする必要があります。 等号の位置は揃えなければなりません。

適切な例

class profile::myclass (
  $var1    = 'default',
  $var2    = 'something else',
  $another = 'another default value',
) {
# body of class
}

不適切な例

# too many levels of indentation
class profile::myclass (
      $var1    = 'default',
      $var2    = 'something else',
      $another = 'another default value',
) {
# body of class
}

# no indentation
class profile::myclass (
$var1    = 'default',
$var2    = 'something else',
$another = 'another default value',
) {
# body of class
}

# misaligned equals sign
class profile::myclass (
  $var1 = 'default',
  $var2  = 'something else',
  $another = 'another default value',
) {
# body of class
}

11. クラス

スコープおよび組織のガイドラインに加えて、モジュール内のクラスの取り扱いについては追加のガイドラインがいくつかあります。

11.1. クラスの継承

クラスの継承は使用しないでください。 params.ppパターンの代わりに、データバインディングを使用します。 継承の使用が適切なのはparams.ppに対してのみですが、これはPuppet 4では推奨されていません。

古いモジュールを維持するために継承を使用することはできますが、複数のモジュール名前空間をまたがって使用しないでください。 モジュール間の依存関係は、includeステートメントや関係の宣言など、より移植性の高い方法で満たすことを推奨します。 クラスの継承は、myclass::paramsパラメータのデフォルト値に対してのみ使用します。 その他のユースケースは、パラメータまたは条件付きロジックを追加することで実現できます。

適切な例:

class ssh { ... }

class ssh::client inherits ssh { ... }

class ssh::server inherits ssh { ... }

不適切な例:

class ssh inherits server { ... }

class ssh::client inherits workstation { ... }

class wordpress inherits apache { ... }

11.2. パブリック公開されているモジュールについて

パブリック公開されているモジュール内のクラスを宣言する場合、クラスのリソース宣言ではなく、includecontainrequireのいずれかを使用します。 こうすることで、クラス宣言の重複とベンダー固定を回避できます。

12. 定義リソース型

同じ定義リソース型に対して複数のインスタンスを使用できるので、宣言の重複を避けるには、リソース名に一意の変数を使用する必要があります。

適切な例:

define apache::listen {
  $listen_addr_port = $name

  # Template uses: $listen_addr_port
  concat::fragment { "Listen ${listen_addr_port}":
    ensure  => present,
    target  => $::apache::ports_file,
    content => template('apache/listen.erb'),
  }
}

不適切な例:

define apache::listen {
  # Template uses: $name
  concat::fragment { 'Listen port':
    ensure  => present,
    target  => $::apache::ports_file,
    content => template('apache/listen.erb'),
  }
}

13. 変数

Puppetスタイルと一致する、明確かつ一義的な方法で変数を参照します。

13.1. factsの参照

factsを参照する場合、単純なグローバルスコープの変数($::operatingsystemなど)ではなく、$factsハッシュを使用します。

単純なグローバルスコープ変数の方が記述は簡単ですが、$factsハッシュの方が明白で読みやすく、factsをグローバルスコープ変数から区別できます。

13.2. 名前空間指定変数

factsではなくグローバルスコープ変数を参照する場合、明白で読みやすいようにするため、明示的に絶対名前空間を指定します。 これには、メインのマニフェスト内でノード分類子によって設定されたグローバルスコープ変数を含みます。

ただし、以下のハッシュには必要ありません。

  • $factsハッシュ
  • $trustedハッシュ
  • $server_factsハッシュ

これらの特別な変数名は保護されており、ローカル変数にこれらの名前を付けることはできないので、常にグローバルスコープ変数が参照されます。

適切な例:

$facts['operatingsystem']

不適切な例:

$::operatingsystem

非常に不適切な例

$operatingsystem

13.3. 変数のフォーマット

変数の定義で使用できるのは、数字、小文字、アンダースコアのみです。 「CamelCase」などのように単語内で大文字を使うと、スタイルの一貫性が損なわれるので、大文字は使用しないでください。 また、ダッシュは構文上無効なので、使用しないでください。

適切な例:

$foo_bar
$some_long_variable
$foo_bar123

不適切な例:

$fooBar
$someLongVariable
$foo-bar123

14. 条件文

条件文はPuppetコードのガイドラインに従う必要があります。

14.1. リソース宣言をシンプルに

リソース宣言に条件文を組み合わせることは推奨しません。 条件文を使ってデータを割り当てる場合、条件付きのコードをリソース宣言から切り離す必要があります。

適切な例:

$file_mode = $::operatingsystem ? {
  'debian' => '0007',
  'redhat' => '0776',
   default => '0700',
}

file { '/tmp/readme.txt':
  ensure  => file,
  content => "Hello World\n",
  mode    => $file_mode,
}

不適切な例:

file { '/tmp/readme.txt':
  ensure  => file,
  content => "Hello World\n",
  mode    => $::operatingsystem ? {
    'debian' => '0777',
    'redhat' => '0776',
    default  => '0700',
  }
}

14.2. caseステートメントとセレクタのデフォルト値

caseステートメントにはデフォルトケースを指定する必要があります。 デフォルトケースに何も設定しない場合も、はっきり分かるようにするため、default: {}と明示的に指定する必要があります。

ケースおよびセレクタ値は引用符で囲みます。

セレクタのデフォルト値を省略して良いのは、値がマッチしないときにカタログコンパイルを明示的に失敗させる場合だけです。

適切な例:

case $::operatingsystem {
  'centos': {
    $version = '1.2.3'
  }
  'solaris': {
    $version = '3.2.1'
  }
  default: {
    fail("Module ${module_name} is not supported on ${::operatingsystem}")
  }
}

デフォルトケースを設定するときは、次の点に注意してください。デフォルトケースを設定すると、構築するモジュールが使用されるプラットフォームで結果の動作が予測できない場合、カタログコンパイルが失敗します。

15. 関数

複数の行をもつテンプレートでは、関数inline_template()inline_epp()を使用しないでください。これらの関数ではテンプレートの検証が許可されません。 モジュールからテンプレートを読み込むには、代わりに関数template()epp()を使用してください。 この方法では、構文の検証が許可されます。

16. Hiera

すべてのユーザがHieraを実装しているとは限らないので、公開使用を意図したモジュールでは、Hiera関数の呼び出しは使用しないでください。 その代わりに、Hieraでオーバーライドできるパラメータの使用を推奨します。

17. モジュールの例

モジュールのおもなユースケースに対して、そのモジュールの/examplesディレクトリ内に対応するサンプルマニフェストが用意されています。

modulepath/apache/examples/{usecase}.pp

サンプルマニフェストは、クラスまたは定義リソース型の宣言方法を明確に示す例となります。 制約のあるスタンドアロン方式でもpuppet applyが正しく実行されるようにするため、サンプルマニフェストには対応するクラスに必要なすべてのクラスの宣言が含まれています。

16. モジュールのドキュメント

一般に公開されるモジュールには、以下に記載するドキュメントを含める必要があります。

18.1 README

READMEファイルは、.md (または.markdown)フォーマットで記述します。 READMEを使用することで、モジュールのユーザは全体的なメリットを把握しやすくなります。 Puppet READMEテンプレートに含まれる基本フォーマットを参考にできます。 Puppet開発キットまたはpuppet module generateコマンドを使用してモジュールを作成すると、生成されたREADMEにテンプレートが含まれます。 .md/.markdownフォーマットを使用すると、Puppet Strings、GitHub、およびPuppet ForgeでREADMEを解析して表示することができます。

READMEの正しい記述方法については詳細なガイドが提供されていますが、全般的には以下の方針に従います。

  • モジュールの動作をまとめたものであること
  • 設定要件や制約事項がある場合は、これらをすべて記載すること(「このモジュールはpuppetlabs-apacheモジュールを必要とし、Ubuntu上でのみ実行できます」など)
  • ユーザのシステムにモジュールの影響を受ける部分がある場合、その内容を記載すること(「このモジュールは、animportantfile.confの内容をすべて上書きします」など)
  • モジュールのカスタマイズ方法と設定方法を説明すること
  • モジュールの一般的なユースケースの使用例とコードサンプルを含めること

18.2. Puppetコードのドキュメント化

Puppet Stringsのコードコメントを使用すると、Puppetクラス、定義型、関数、リソース型、プロバイダをドキュメント化することができます。 Puppet StringsはREADMEとコードのコメントを処理して、HTMLまたはJSONフォーマットのドキュメントを生成します。 これにより、モジュールの詳細なドキュメントを作成できます。

モジュールに含まれる要素(クラス、関数、定義型、パラメータなど)ごとにコメントを記述します。 コメントを使用する場合は、その要素のコードの前にコメントを記述します。 コメントには、次に示す情報が、以下の順番で含まれます。

  • 要素の動作をおおまかに示す説明
  • データ型からは明らかでない有効な値に関する追加情報(例:データ型は文字列であるが、値はパスでなければならない)
  • 要素に対するデフォルト値がある場合はその値

説明が複数行にわたる場合、少なくとも1つのスペースによって均一にインデントする必要があります。

以下に例を示します。

# @param config_epp Specifies a file to act as a EPP template for the config file.
#  Valid options: a path (absolute, or relative to the module path). Example value: 
#  'ntp/ntp.conf.epp'. A validation error is thrown if you supply both this param **and**
#  the `config_template` param.

文字列を使用してモジュールをドキュメント化する場合、ドキュメントを生成する方法をユーザがわかるよう、READMEのReferenceセクションに文字列に関する情報を含めます。 使用法、インストール、正しいドキュメント化コメントの記述方法について、詳しくはPuppet Stringsドキュメントを参照してください。

Puppet Stringsのコードコメントを使用しない場合、READMEにReferenceセクションを含めて、設定できるすべてのクラス、型、プロバイダ、定義型、パラメータの一覧を記述する必要があります。 簡単な説明、有効な選択肢、デフォルト値(ある場合)も含めます。 たとえば、以下に示すのはntpモジュールのntpクラスのパラメータです。

#### `package_ensure`

Data type: String.

Whether to install the NTP package, and what version to install. Values: 'present', 'latest', or a specific version number.

Default value: 'present'.

詳細および例については、モジュールのドキュメント化に関するガイドを参照してください。

18.3 CHANGELOG

CHANGELOGを.md (または.markdown)フォーマットで記述します。 CHANGELOGには以下の情報を含める必要があります。

  • リリースごとのエントリ
  • このリリースに含まれるバグ修正と機能のリスト
  • 下位互換性が無い変更がある場合は、これを明記

19. 検証とテスト

コードおよびスタイルのテストについては、いくつかのコミュニティツールを推奨します。

  • puppet-lintは、スタイルガイドラインへの準拠についてコードをテストします。
  • metadata-json-lintは、スタイルガイドラインへの準拠についてmetadata.jsonをテストします。
  • モジュールをテストするには、rspecを推奨します。rspec-puppetは、Puppet用のrspecテストを記述するのに役立ちます。
Back to top
The page rank or the 1 our of 5 rating a user has given the page.
The email address of the user submitting feedback.
The URL of the page being ranked/rated.