#manifest-file #class #printing #puppet #pretty-print #linter #utility

bin+lib shadowplay

检查 puppet 语法、puppet 清单代码检查器、美化打印器以及 Hiera 探索工具

3 个版本

0.16.3 2022年6月7日
0.16.1 2022年4月12日
0.16.0 2022年4月12日

#635 in 操作系统

MIT/Apache

575KB
14K SLoC

目录

  1. 关于
  2. 安装方法
    1. 通过 deb/rpm
    2. MaOS 二进制文件
    3. 通过 cargo
    4. Guix 清单
  3. 使用方法
    1. YAML 文件的正确性
    2. Hiera YAML 文件的有效性
    3. Puppet 清单文件检查器
    4. 美化打印清单文件
    5. 配置文件生成器
    6. Hiera 探索器
    7. *.pp AST 导出器
  4. 可用的 *.pp 检查
    1. ArgumentLooksSensitive
    2. ArgumentTyped
    3. ConstantExpressionInCondition
    4. DefaultCaseIsNotLast
    5. DoNotUseUnless
    6. DoubleNegation
    7. EmptyCasesList
    8. EnsureAttributeIsNotTheFirst
    9. ErbReferencesToUnknownVariable
    10. ExecAttributes
    11. ExpressionInSingleQuotes
    12. FileModeAttributeIsString
    13. InvalidResourceCollectionInvocation
    14. InvalidResourceSetInvocation
    15. InvalidStringEscape
    16. InvalidVariableAssignment
    17. LowerCaseArgumentName
    18. LowerCaseVariable
    19. MultipleDefaultCase
    20. MultipleResourcesWithoutDefault
    21. NegationOfEquation
    22. NoDefaultCase
    23. OptionalArgumentsGoesFirst
    24. PerExpressionResourceDefaults
    25. ReadableArgumentsName
    26. ReferenceToUndefinedValue
    27. RelationToTheLeft
    28. SelectorInAttributeValue
    29. SensitiveArgumentWithDefault
    30. StatementWithNoEffect
    31. UnconditionalExec
    32. UniqueArgumentsNames
    33. UniqueAttributeName
    34. UnusedVariables
    35. UpperCaseName
    36. UselessDoubleQuotes
    37. UselessParens
  5. YAML 文件检查器
  6. Hiera YAML 文件检查器
    1. 有语法错误的模块引用
    2. 未在 modules/ 中找到的类引用
    3. 未定义类参数中的引用
    4. 根映射键名的单列

关于

Shadowplay 是一个检查 puppet 语法、puppet 清单代码检查器、美化打印器以及 Hiera 探索工具的程序。

img

安装方法

通过 deb/rpm

最新版本可以在此下载:https://github.com/mailru/shadowplay/releases

MaOS 二进制文件

最新 macOS 二进制文件可以在此下载:https://github.com/mailru/shadowplay/releases

通过 cargo

cargo install shadowplay

Guix 清单

Guix 清单尚未合并到主仓库。可以从 Shadowplay 仓库中使用 etc/guix.scm。所有缺失的依赖也包含在清单文件中。

使用方法

YAML 文件的正确性

shadowplay check yaml hieradata/default.yaml [hieradata/another.yaml] ...

除了语法正确性外,还会检查映射中键的唯一性以及链接(锚点)的正确性。

Hiera YAML 文件的有效性

shadowplay check hiera hieradata/default.yaml ...

对于指定的文件,将检查YAML的正确性,以及Puppet类和类参数引用的正确性。例如,如果使用了未知的类参数,将生成错误。

作为副作用,它还会检查Hiera中引用的puppet manifest语法的正确性。

Puppet 清单文件检查器

shadowplay --repo-path ./ check pp modules/hammer/manifests/config.pp ...

指定的文件将由解析器处理,然后应用linter检查(如果解析成功的话)。

美化打印清单文件

shadowplay pretty-print-pp < /path/to/file.pp

配置文件生成器

可能需要禁用一些linter或进行自定义。它可以通过以下命令生成默认配置并在以后进行编辑

shadowplay generate-config >/etc/shadowplay.yaml

Hiera 探索器

Hiera是yaml文件的层次结构。在大型配置中,可能很难确定某些主机特定键的值。Shadowplay提供了一个简单的解决方案。

shadowplay get host123 sshd::install::version

命令尽可能打印出更多信息

Value: "present"
Found in "./hieradata/default_CentOS7.yaml" at lines 63:63
Value lookup path was: network/host123.yaml -> host123.yaml -> host.yaml -> default_CentOS7.yaml
===================================
Git information:
deadbeef1234 (Evgenii Lepikhin 2022-03-29 15:06:51 +0300 63) sshd::install::version:             'present'

*.pp AST 导出器

shadowplay dump modules/sshd/manifests/install.pp

以JSON格式输出AST。主要用于内部目的。

可用的 *.pp 检查

ArgumentLooksSensitive

如果参数名称看起来很敏感,但参数没有用类型Sensitive进行标记,则发出警告

不好

class some::class (
  $secret_token,
) { }

class some::class (
  Sensitive $secret_token,
) { }

ArgumentTyped

如果参数未标记,则发出警告

不好

class some::class (
  $config_path,
) { }

class some::class (
  Stdlib::Absolutepath $config_path,
) { }

ConstantExpressionInCondition

如果条件中使用常量表达式,则发出警告

不好

if 1 == 2 - 1 { notify('1=2-1') }

这种类型的条件总是评估为常量false或true,因此可以安全地删除。好

notify('1=2-1')

DefaultCaseIsNotLast

如果'default'情况不是最后一个,则发出警告

不好

case $value {
  'a': { }
  default: { }
  'b': { }
}

case $value {
  'a': { }
  'b': { }
  default: { }
}

DoNotUseUnless

如果使用'unless'条件语句,则发出警告

不好

unless $value { }

if !$value { }

DoubleNegation

如果使用双重否定,则发出警告

不好

if !(!$value) { }

if !($value != 1) { }

if $value { }

if $value == 1 { }

EmptyCasesList

如果case { … }没有cases,则发出警告

不好

case $value { }

EnsureAttributeIsNotTheFirst

如果资源参数的'ensure'不是第一个,则发出警告

不好

file { '/etc/passwd':
  user => root,
  ensure => file,
}

file { '/etc/passwd':
  ensure => file,
  user => root,
}

ErbReferencesToUnknownVariable

检查template()中指定的ERB模板中的未定义变量

不好

class some::class () {
  # here template_file.erb contains: <% @some_undefined_variable %>
  $value = template('some/template_file.erb')
}

ExecAttributes

检查exec { …} 参数

不好

# implicit 'command' attribute
exec { 'echo Hello' : }

exec {
  unknown_attribute => 1,
}

# invalid provider
exec {
  provider => 'unknown provider value'
}

# 'path' is not set, 'provider' is not 'shell', thus 'command' attribute of exec {} must start with absolute path
exec {
  command => 'echo Hello'
}

ExpressionInSingleQuotes

如果找到单引号字符串的插值表达式,则发出警告

不好

$value = 'Hello $world'

$value = '2 + 2 = ${2+2}'

FileModeAttributeIsString

如果'file'资源参数的'mode'不是4位字符串形式,则发出警告

不好

file { '/some/file':
  mode => '644',
}

file { '/some/file':
  mode => 644,
}

file { '/some/file':
  mode => '0644',
}

InvalidResourceCollectionInvocation

检查是否使用了现有的资源集,并且在该类中所有参数都是已知的

不好

# relation to unknown resource
Class['unknown_class'] -> Class['known_class']

InvalidResourceSetInvocation

检查是否使用了现有的资源,并且在该类中所有参数都是已知的

不好

class class1 (
  $known_arg,
) { }

class class2 {
  # Call to unknown class
  class { 'unknown_class': }

  # Call to known class with invalid argument
  class { 'class1':
    unknown_arg => 1
  }

  # Call to known class with invalid argument
  class1 { 'title':
    unknown_arg => 1,
  }

  # Call to internal resource with invalid argument
  file { '/some/file':
    uknown_arg => 1,
  }
}

InvalidStringEscape

检查字符串中是否只使用了允许的转义字符

不好

$value = '\s*\.'

$value = "\s*\."

$value = '\\s*\\.'

$value = "\\s*\\."

InvalidVariableAssignment

如果赋值号左侧不是变量或变量的数组,则发出警告

不好

lookup('some::value') = 1

LowerCaseArgumentName

如果参数名称不是小写,则根据Puppet的风格指南发出警告

不好

class some::class (
  $ArgumentInCamelCase
) {}

LowerCaseVariable

如果变量名称不是小写,则发出警告

不好

class some::class () {
  $VariableIsNOTInLowercase = 1

MultipleDefaultCase

如果case语句有多个'default'情况,则发出警告

不好

case $val {
  1: {}
  default: {}
  default: {}
}

MultipleResourcesWithoutDefault

如果资源集包含多个资源且未指定默认值,则发出警告

不好

file {
  '/etc/passwd':
    ensure => file,
    user => root,
  '/etc/group':
    ensure => file,
    user => root,
    group => wheel,
}

file {
  default:
    ensure => file,
    user => root,
  '/etc/passwd':
  '/etc/group':
    group => wheel,
}

NegationOfEquation

如果否定方程式,则发出警告

不好

if !($a == 1) { }

if !($a =~ /./) { }

if $a != 1 { }

if $a !~ /./ { }

NoDefaultCase

如果case语句没有默认情况,则发出警告

不好

case $val {
  1, 2: {  }
  3: { }
}

case $val {
  1, 2: {  }
  3: { }
  default: { }
}

OptionalArgumentsGoesFirst

如果将可选参数指定在必需参数之前,则发出警告

class some::class (
  $optional_arg = 1,
  $required_arg,
) { }

class some::class (
  $required_arg,
  $optional_arg = 1,
) { }

PerExpressionResourceDefaults

如果使用局部资源默认值,则发出警告

不好

Exec {
  provider => shell,
}

exec { 'run command':
  command => 'echo Hello',
}

ReadableArgumentsName

如果参数名称不够可读,则发出警告

不好

class some::class (
  String $c = '/etc/config',
) { }

class some::class (
  String $config = '/etc/config',
) { }

ReferenceToUndefinedValue

如果变量在当前上下文中未定义,则发出警告

不好

if $some_undefined_variable { }

RelationToTheLeft

检查左向关系

不好

Class['c'] <- Class['b'] <~ Class['a']

Class['a'] ~> Class['b'] -> Class['c']

SelectorInAttributeValue

如果资源属性中使用选择器(… ? … : …),则发出警告

不好

file { '/etc/shadow':
  mode => $is_secure ? '0600' : '0644',
}

$file_mode = $is_secure ? '0600' : '0644'

file { '/etc/shadow':
  mode => $file_mode,
}

SensitiveArgumentWithDefault

如果使用类型Sensitive标记的参数包含默认值,则发出警告

不好

class some::class (
  Sensitive $password = 'admin',
)

敏感数据的公共可用默认值是没有意义的。好

class some::class (
  Sensitive $password,
)

StatementWithNoEffect

检查无副作用的语句

不好

if $a {
  if $b {
    2 + 2
  }
}

UnconditionalExec

如果没有指定unless、onlyif、creates或refreshonly属性,则发出警告exec { … }

不好

exec { 'run command':
  command => '/bin/rm -rf /var/cache/myapp',
}

exec { 'run command':
  command => '/bin/rm -rf /var/cache/myapp',
  onlyif => 'test -e /var/cache/myapp',
}

UniqueArgumentsNames

检查类/定义/计划参数的唯一性

不好

class some::class (
  $arg,
  $arg,
  $arg,
) { }

UniqueAttributeName

资源属性必须是唯一的

不好

service { 'sshd':
  ensure => running,
  ensure => stopped,
}

UnusedVariables

检查未使用的变量。实验性lint假阳性是可能的。

不好

class some::class (
  $unused_argument,
) {
  service { 'sshd':
    ensure => running,
  }
}

UpperCaseName

如果使用大写字母使用资源集,则发出警告

不好

Service { 'sshd':
  ensure => running,
}

service { 'sshd':
  ensure => running,
}

UselessDoubleQuotes

如果双引号字符串没有插值表达式和转义的单引号,则发出警告

不好

$var = "simple literal"

$var = 'simple literal'

UselessParens

检查多余的括号

不好

if (($var1) or ($var2)) { }

if $var1 or $var2 { }

YAML 文件检查器

实现了一些基本的检查

  • 文件不可执行
  • 文件为空(没有根值可用)
  • 文件解析没有语法错误
  • 映射不包含重复的键
  • 尝试合并类型不是数组或映射的锚点

Hiera YAML 文件检查器

所有YAML文件的lints加上

有语法错误的模块引用

如果某些class无法解析,linter将失败

some_class::argument: 1

未在 modules/ 中找到的类引用

如果modules/someclass/init.pp不存在,linter将失败

some_class::argument: 1

未定义类参数中的引用

如果某些class不接受参数$argumentname,linter将失败

some_class::argument_name: 1

根映射键名的单列

Linter可以防止像

some_class:argument_name: 1

依赖项

~7–17MB
~215K SLoC