Skip to content

Instantly share code, notes, and snippets.

@taka512
Last active January 4, 2016 16:19
Show Gist options
  • Save taka512/8647015 to your computer and use it in GitHub Desktop.
Save taka512/8647015 to your computer and use it in GitHub Desktop.
puppet style guide

スタイルガイド

Version 1.1.2

1. 用語

キーワード “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, ”MAY”, and “OPTIONAL”は、本書ではRFC2119に記載されるように解釈されるべきである。

2. Puppet Version

このスタイルガイドは、バージョン2.6.xが対象です。バージョン2.6.0以降で利用できるようになったいくつかの機能に基づいています。

3. Why a Style Guide?

Puppet Labsは、お客様や地域社会のためのモジュールを開発しています。これらのモジュールは、モジュールのデザインとスタイルのためのベスト・プラクティスであるべきです。 これらのモジュールは組織を超えて多くの人々によって開発されているので一貫したパターン、デザイン・スタイルを確保する基準が必要でした。

4. 全般的な哲学

スタイルマニュアルは全ての状況はカバーできません。判断が必要なときには下記一般的な考え方に留意してください。

  • 可読性

もしあなたが効果的に等しい2つの案で選ぶ必要が有る場合はより読みやすい方を選択しましょう。これは、主観的ですが3月後に自分のコードを理解する事ができれば、それは素晴らしい事でしょう

  • 継承は避けるべき

一般的に、継承は読むのが大変なコードにつながる。 継承のほとんどのユースケースは、リソース属性を構成するために使用できるクラスパラメータを抽出することによって置き換えることができる。 詳細は、クラスの継承のセクションを参照してください。

  • モジュールには、1を必要とせずに、ENCと協力しなければならない

内部調査では、ENCは要求されるべきではないというコンセンサスの近くに生じた。 同時に、我々が書くすべてのモジュールは、ENCとうまく動作するはずです。

  • 一般的にクラスは他のクラスを宣言するべきではありません

できるだけノードスコープに閉じたクラスを宣言します。 他のクラスを必要とするクラスは、それらを直接宣言するべきではありませんし、他の手段で宣言されていない場合、エラーが発生するようにする必要があります。 (インクルード機能は、複数クラスの宣言が許可されていますがそれは親スコープによる非決定スコープがアサインされた事によりもたらされた結果です。今のこと宣言に関しては保守的ですが、今後、クラスのマルチ宣言は決定することができれば、この理念を再検討可能性があります。)

5. Module Metadata

すべてのモジュールは、メタデータはModulefileデータファイルで定義され、metadata.jsonファイルとして出力されている必要があります。次のメタデータは、すべてのモジュールのために提供されるべきである

name 'myuser-mymodule'
version '0.0.1'
author 'Author of the module - for shared modules this is Puppet Labs'
summary 'One line description of the module'
description 'Longer description of the module including an example'
license 'The license the module is release under - generally GPLv2 or Apache'
project_page 'The URL where the module source is located'
dependency 'otheruser/othermodule', '>= 1.2.3'

Modulefile形式のより完全なガイドでは、puppet-module-toolのREADMEに記載されています。

5.1. Style Versioning

このスタイルガイドはバージョン管理されるため、モジュールはスタイルガイドの特定のバージョンに準拠できるようになります。将来、puppet-module toolはmodulefileの中にメタデータを埋め込み関連するスタイルガイドのバージョンを可能となる。また、メタデータの自動チェックを使用する事ができるようになる。

6. Spacing, Indentation, & Whitespace

このスタイルガイドに準拠したモジュールマニフェストは以下の通り

  • 2つのスペースのソフトタブを使用する
  • リテラルにタブ文字を使用しない
  • 末尾の空白は含めない
  • 1行は80文字を超えないようにすべき
  • attributesのブロックの中にコンマ矢印(=>)を揃えるべき

7. Comments

puppetの言語は複数のコメントの種類が許可されていますが、シャープ記号(#)を推奨します。 なぜなら一般的に最もテストエディタやコードの字句解析でわかりやすいからです。

  • 「 # ... 」をコメントとして使うべき
  • 「// ... 」や「 /* ... */」をコメントとして使うべきでない

8. Quoting

変数が含まれていないすべての文字列はシングルクオートで囲む必要があります。変数の展開が必要とされる場合、ダブルクオートを使用する必要があります。シングルクオートが含まれている場合、ダブルクオートは文字列を読みやすくするために用いることができる。文字列が英数字や単語リソースのタイトルで無い場合はクオートはオブションです。

文字列で補間する時、すべての変数は括弧で囲む必要があります。

良い例

"/etc/${file}.conf"
"${::operatingsystem} is not supported by ${module_name}"

悪い例

"/etc/$file.conf"
"$::operatingsystem is not supported by $module_name"

自身のみで成り立っている変数は、引用符で囲む必要はありません。

良い例

mode => $my_mode

悪い例

mode => "$my_mode"
mode => "${my_mode}"

9. Resources

9.1. Resource Names

すべてのリソースのタイトルは引用符で囲む必要があります。 (puppetはスペースはハイフンが含まれていない場合は引用符で囲まれていないリソースタイトルをサポートしています。しかし一貫した見た目をまもるため避ける必要があります。)

良い例

package { 'openssh': ensure => present }

悪い例

package { openssh: ensure => present }

9.2. Arrow Alignment

リソースの属性/値リストの全てのカンマ矢印(=>)が揃っている必要があります。 矢印は、最も長い属性名の前に1スペース配置してください

良い例

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

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

悪い例

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

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

9.3. Attribute Ordering

リソース宣言にensure属性が含まれている場合は、最初に指定される属性でなければなりません。

良い例

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

(この推奨事項は、読みやすさのためだけにあり、puppetはリソースを同期する際の属性の順序を無視する)

9.4. Compression

マニュフェストの中でリソースタイプでなく論理的な関係によりリソースをグループ化すべきである。中括弧の中で複数リソースを宣言するためのセミコロンの使用は、読みやすさが改善するまれなケースを除いて推奨されません。

良い例

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

exec { 'change contents of a':
  command => 'sed -i.bak s/a/A/g /tmp/a',
}

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

exec { 'change contents of b':
  command => 'sed -i.bak s/b/B/g /tmp/b',
}

悪い例

file {
  "/tmp/a":
    content => "a";
  "/tmp/b":
    content => "b";
}

exec {
  "change contents of a":
    command => "sed -i.bak s/b/B/g /tmp/a";
  "change contents of b":
    command => "sed -i.bak s/b/B/g /tmp/b";
}

9.5. Symbolic Links

わかりやすくするためにシンボリックリンクは「ensure => link」を使用し、明示的にtarget属性で値を指定する必要があります。ensureの値としてターゲットへのパスを使用することは推奨されません。

良い例

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

悪い例

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

9.6. File Modes

ファイルモードは、3桁でなく4桁の数字として表現されるべきである。 さらに、ファイルのモードは数値のかわりにシングルクオートで囲まれた文字列として指定する必要があります。

良い例

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

悪い例

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

9.7. Resource Defaults

リソースのデフォルトは、制御された方法で使用されるべきである マニュフェストのエコシステムの先頭でのみ宣言される必要があります。具体的には次の通りです。

  • site.ppのトップスコープ
  • 他のクラスによって継承されることがなく、別のクラスで宣言することもないクラス。

リソースのデフォルトの伝搬がダイナミックスコープを介して、デフォルトが宣言された場所から遠いところで予測できない影響を与える事に起因している。

良い例

# /etc/puppetlabs/puppet/manifests/site.pp:
File {
  mode  => '0644',
  owner => 'root',
  group => 'root',
}

悪い例

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

class {'ssh::client':
  ensure => present,
}

10. Conditionals

10.1. Keep Resource Declarations Simple

リソース宣言で条件文を混在しないでください。 データ割り当てのための条件文を使用するときは、リソース宣言から条件付きコードを分離する必要があります。

良い例

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

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

悪い例

file { '/tmp/readme.txt':
  mode => $::operatingsystem ? {
    debian => '0777',
    redhat => '0776',
    fedora => '0007',
  }
}

10.2. Defaults for Case Statements and Selectors

caseステートメントは、デフォルトのケースを持っている必要があります。 また、プラットフォームの予測できない場合にモジュールがで使用されたデフォルトケースでは カタログのコンパイルに失敗する必要があります もしデフォルトケースで何もしたくない場合、明確化のため「default: {}」を明示する

明示的に値が一致しないときにfailし、コンパイルをカタログするセレクタの場合、デフォルトの選択は省略されるべきである

次の例では、推奨スタイルに従っています。

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

11. Classes

11.1. Separate Files

すべてのクラスとリソースタイプの定義は、そのモジュールのマニフェストディレクトリ内の別々のファイルである必要があります。

例:

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

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

これはinit.pp内ですべてのクラスと定義を宣言する場合と機能的に同じですが、モジュールの構造を強調し、すべてが読みやすくなります。

11.2. Internal Organization of a Class

クラスは、一貫性のある構造とスタイルで構成される必要があります。 下のリストにこれらの項目のそれぞれについて、「こうであるべき」という暗黙の声明があります。 「may」は「Xの場合は、こうあるべき」と解釈されるべきである

  • クラスとパラメータを定義すべき
  • 任意のクラスのパラメータを検証し、パラメータが無効な場合のカタログのコンパイルに失敗すべき
  • 最も一般的な検証されたパラメータをデフォルトとすべき
  • ローカル変数を宣言する(may)
  • 「Class['apache'] -> Class['local_yum']」で他のクラスとの関係を宣言する(may)
  • リソースをオーバーライドする(may)
  • リソースのデフォルトを宣言する(may)
  • カスタムタイプは、コアタイプの前に定義し、リソース宣言する(may)
  • 条件文の内側にリソース関係を宣言(may)

次の例では、推奨スタイルに従っています。

class myservice($ensure='running') {

  if $ensure in [ running, stopped ] {
    $_ensure = $ensure
  } else {
    fail('ensure parameter must be running or stopped')
  }
 
  case $::operatingsystem {
    centos: {
      $package_list = 'openssh-server'
    }
    solaris: {
      $package_list = [ SUNWsshr, SUNWsshu ]
    }
    default: {
      fail("Module ${module_name} does not support ${::operatingsystem}")
    }
  }
 
  $variable = 'something'
 
  Package { ensure => present, }
 
  File { owner => '0', group => '0', mode => '0644' }
 
  package { $package_list: }
 
  file { "/tmp/${variable}":
    ensure => present,
  }
  
  service { 'myservice':
    ensure    => $_ensure,
    hasstatus => true,
  }
}

11.3. Relationship Declarations

チェーン構文とのリレーションの宣言は「左から右」方向に使用されるべきである。

良い例

Package['httpd'] -> Service['httpd']

悪い例

Service['httpd'] <- Package['httpd']

可能な場合には、リレーションシップ宣言にmetaparametersを選ぶべきです。 サブクラス化するときは、動作をオーバーライドすることが必要であり、metaparametersは望ましくない一例である。 この状況では、条件文の内側に関係の宣言を使用する必要があります。

11.4. Classes and Defined Resource Types Within Classes

クラスと定義されたリソースタイプは、他のクラス内で定義されてはならない。

悪い例

class apache {
  class ssl { ... }
}

悪い例

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

11.5. Class Inheritance

継承は、モジュール内で使用することができるが、モジュールの名前空間を超えて使用することはできません。モジュール間の依存関係がモジュール方式の概念に違反していない、ステートメントやリレーションの宣言等含まれた移植性のある形で満たされる必要があります

良い例

class ssh { ... }

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

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

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

悪い例

class ssh inherits server { ... }

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

class wordpress inherits apache { ... }

一般的に代替案が実行可能であるとき継承は避けるべきである。 たとえば、サービスを停止する際に既存のクラスの関係をオーバライドするのに継承を使用する代わりに、 パラメータと条件式の宣言と単一のクラスを使って考えてみましょう。

class bluetooth($ensure=present, $autoupgrade=false) {
   # Validate class parameter inputs. (Fail early and fail hard)

   if ! ($ensure in [ "present", "absent" ]) {
     fail("bluetooth ensure parameter must be absent or present")
   }

   if ! ($autoupgrade in [ true, false ]) {
     fail("bluetooth autoupgrade parameter must be true or false")
   }

   # Set local variables based on the desired state

   if $ensure == "present" {
     $service_enable = true
     $service_ensure = running
     if $autoupgrade == true {
       $package_ensure = latest
     } else {
       $package_ensure = present
     }
   } else {
     $service_enable = false
     $service_ensure = stopped
     $package_ensure = absent
   }

   # Declare resources without any relationships in this section

   package { [ "bluez-libs", "bluez-utils"]:
     ensure => $package_ensure,
   }

   service { hidd:
     enable         => $service_enable,
     ensure         => $service_ensure,
     status         => "source /etc/init.d/functions; status hidd",
     hasstatus      => true,
     hasrestart     => true,
  }

  # Finally, declare relations based on desired behavior

  if $ensure == "present" {
    Package["bluez-libs"]  -> Package["bluez-utils"]
    Package["bluez-libs"]  ~> Service[hidd]
    Package["bluez-utils"] ~> Service[hidd]
  } else {
    Service["hidd"]        -> Package["bluez-utils"]
    Package["bluez-utils"] -> Package["bluez-libs"]
  }
}

(この例は、いくつかの仮定でブルートゥースを管理するためのPuppet Masterのトレーニングに提供された例に基づいています.)

要約:

  • クラスの継承は、リソースの属性をオーバーライドするためにのみ有用である。他のユースケースは他のより良い方法で実施される
  • metaparametersの関係をオーバーライドする必要がある場合は、 継承のかわりに条件式の宣言を持つ単一のクラスを使用すべき
  • 多くの場合、さらには他の属性(例えばensure、enable)の挙動は変数と条件ロジックを利用して切り替えるべきです

11.6. Namespacing Variables

トップスコープ変数を使用する場合、パペット·モジュールは、明示的、偶発的スコープの問題を防ぐために、空の名前空間を指定する必要があります。

良い例

$::operatingsystem

悪い例

$operatingsystem

11.7. Variable format

変数を定義するときには、文字、数字、アンダースコアを使用する必要があります。特にダッシュは利用するべきではありません。

良い例

$foo_bar123

悪い例

$foo-bar123

11.8. Display Order of Class Parameters

パラメータ化されたクラスと定義されているリソースタイプの宣言では、指定が必須なパラメータはオプションのパラメータ(デフォルトパラメータ)の前に表示される必要があります。

良い例

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

悪い例

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

11.9 Class parameter defaults

クラスのパラメータを受け入れるモジュールを記述する場合、デフォルト値はクラスの宣言時にパラメータでオプション指定する手段が提供されるべき

例:

class ntp(
  $server = 'UNSET'
) {

  include ntp::params

  $_server = $server ? {
    'UNSET' => $::ntp::params::server,
    default => $server,
  }

  notify { 'ntp':
    message => "server=[$_server]",
  }

}

クラスがこのように宣言されている理由は、Puppet の2.6.xバージョンと完全に互換性を保つためです。以下の方法はPuppet 2.6.2およびそれ以前と互換性がないため、使用しないべき。

class ntp(
  $server = $ntp::params::server
) inherits ntp::params {

    notify { 'ntp':
      message => "server=[$server]",
    }
}

他に留意すべき事:

  • メンテナンス性のためローカルスコープの変数は「_ 」プレフィックスを使用すべきである
  • 名前空間の衝突を避けるために、モジュールのparamsクラスの値を引いたときに、完全修飾名前空間の変数を使用すべき
  • エンドユーザーはモジュールが正しく機能するためにする必要はありませんので、paramsのクラスを宣言すべき

puppet2.7がより広く採用されて、2.6.Xのような多くのバージョンとモジュールの互換性が最大の関心事でなくなった場合はこの推奨されるパターンが緩和されることがある

この差分は、2つの一般的に使用されるパターンがどのように他への切り替えるかの変更を示している。

diff --git a/manifests/init.pp b/manifests/init.pp
index c16c3a0..7923ccb 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -12,9 +12,14 @@
 #
 class paramstest (
   $mandatory,
-  $param = $paramstest::params::param
-) inherits paramstest::params {
+  $param = 'UNSET'
+) {
+  include paramstest::params
+  $\_param = $param ? {
+    'UNSET' => $::paramstest::params::param,
+    default => $param,
+  }
   notify { 'TEST':
-    message => " param=[$param] mandatory=[$mandatory]",
+    message => " param=[$\_param] mandatory=[$mandatory]",
   }
 }

12. Tests

すべてのマニフェストは、testsディレクトリ内に対応するテストマニュフェストを持っている必要があります。

modulepath/apache/manifests/{init,ssl}.pp
modulepath/apache/tests/{init,ssl}.pp

テストマニフェストは、クラスまたは定義されたリソースタイプを宣言する方法の明確な例を提供する必要があります。また、テストマニフェストはスタンドアロンの制限された環境で動作を確認するため必要なクラスを宣言する必要があります。

13. Puppet Doc

クラスと定義されているリソースタイプはRDocのマークアップを使用してインラインで文書化する必要があります。puppetのdocコマンドを使用してオンラインドキュメントを生成することができるので、これらのインラインドキュメントコメントは重要である。

クラスの場合:

# == Class: example_class
#
# Full description of class example_class here.
#
# === Parameters
#
# Document parameters here.
#
# [*ntp_servers*]
#   Explanation of what this parameter affects and what it defaults to.
#   e.g. "Specify one or more upstream ntp servers as an array."
#
# === Variables
#
# Here you should define a list of variables that this module would require.
#
# [*enc_ntp_servers*]
#   Explanation of how this variable affects the funtion of this class and if it
#   has a default. e.g. "The parameter enc_ntp_servers must be set by the
#   External Node Classifier as a comma separated list of hostnames." (Note,
#   global variables should not be used in preference to class parameters  as of
#   Puppet 2.6.)
#
# === Examples
#
#  class { 'example_class':
#    ntp_servers => [ 'pool.ntp.org', 'ntp.local.company.com' ]
#  }
#
# === Authors
#
# Author Name <author@example.com>
#
# === Copyright
#
# Copyright 2011 Your name here, unless otherwise noted.
#
class example_class {

}

定義されたリソースの場合:

# == Define: example_resource
#
# Full description of defined resource type example_resource here.
#
# === Parameters
#
# Document parameters here
#
# [*namevar*]
#   If there is a parameter that defaults to the value of the title string
#   when not explicitly set, you must always say so.  This parameter can be
#   referred to as a "namevar," since it's functionally equivalent to the
#   namevar of a core resource type.
#
# [*basedir*]
#   Description of this variable.  For example, "This parameter sets the
#   base directory for this resource type.  It should not contain a trailing
#   slash."
#
# === Examples
#
# Provide some examples on how to use this type:
#
#   example_class::example_resource { 'namevar':
#     basedir => '/tmp/src',
#   }
#
# === Authors
#
# Author Name <author@example.com>
#
# === Copyright
#
# Copyright 2011 Your name here, unless otherwise noted.
#
define example_class::example_resource($basedir) {

}

これで自動的にpuppet docツールでドキュメントが抽出できるようになります。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment