Crates.io | shadowplay |
lib.rs | shadowplay |
version | 0.16.3 |
source | src |
created_at | 2022-04-12 20:18:11.471137 |
updated_at | 2022-06-07 21:14:35.380964 |
description | Utility for checking puppet syntax, a puppet manifest linter, a pretty printer, and a utility for exploring the Hiera. |
homepage | |
repository | https://github.com/mailru/shadowplay/ |
max_upload_size | |
id | 566608 |
size | 586,059 |
Shadowplay is a utility for checking puppet syntax, a puppet manifest linter, a pretty printer, and a utility for exploring the Hiera.
Latest releases can be downloaded here: https://github.com/mailru/shadowplay/releases
Lastest binaries for MacOS can be downloaded here: https://github.com/mailru/shadowplay/releases
cargo install shadowplay
Guix manifest is not merged into main repository yet. One can use etc/guix.scm from Shadowplay repo. All missing dependencies are also included into manifest file.
shadowplay check yaml hieradata/default.yaml [hieradata/another.yaml] ...
In addition to the correctness of the syntax, the uniqueness of the keys in maps will be checked, as well as the correctness of the links (anchors).
shadowplay check hiera hieradata/default.yaml ...
For the specified files, YAML correctness will be checked, as well as the correctness of references to Puppet classes and class arguments. For example, there will be an error generated if an unknown class argument is used.
As a side effect, it also checks the correctness of syntax of pappet manifests referenced by values in Hiera.
shadowplay --repo-path ./ check pp modules/hammer/manifests/config.pp ...
The specified files will be processed by the parser, then linter checks will be applied to the resulting AST (if parsing is successful).
shadowplay pretty-print-pp < /path/to/file.pp
Use may want to disable some lints or customize it. She can generate default config and edit it later with the command:
shadowplay generate-config >/etc/shadowplay.yaml
Hiera is hierarchy of yaml files. In huge configurations it may be difficult to determine value of specific key for some host. Shadowplay provides easy solution.
shadowplay get host123 sshd::install::version
Command prints as much information as possible:
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'
shadowplay dump modules/sshd/manifests/install.pp
Outputs AST in JSON format. Mainly for internal purposes.
Warns if argument name looks like sensitive, but argument is not typed with type Sensitive
Bad:
class some::class (
$secret_token,
) { }
Good:
class some::class (
Sensitive $secret_token,
) { }
Warns if argument is not typed
Bad:
class some::class (
$config_path,
) { }
Good:
class some::class (
Stdlib::Absolutepath $config_path,
) { }
Warns if constant expression is used in condition
Bad:
if 1 == 2 - 1 { notify('1=2-1') }
Such type of conditions always evaluated into constant false or true, thus can be safely removed. Good:
notify('1=2-1')
Warns if 'default' case is not the last
Bad:
case $value {
'a': { }
default: { }
'b': { }
}
Good:
case $value {
'a': { }
'b': { }
default: { }
}
Warns if 'unless' conditional statement is used
Bad:
unless $value { }
Good:
if !$value { }
Warns if double negation is used
Bad:
if !(!$value) { }
if !($value != 1) { }
Good:
if $value { }
if $value == 1 { }
Warns if case { … } has no cases
Bad:
case $value { }
Warns if 'ensure' argument of resource is not the first
Bad:
file { '/etc/passwd':
user => root,
ensure => file,
}
Good:
file { '/etc/passwd':
ensure => file,
user => root,
}
Checks ERB templates specified in template() for undefined variables
Bad:
class some::class () {
# here template_file.erb contains: <% @some_undefined_variable %>
$value = template('some/template_file.erb')
}
Checks exec { …} arguments
Bad:
# 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'
}
Warns if interpolated expression found single-qouted string
Bad:
$value = 'Hello $world'
$value = '2 + 2 = ${2+2}'
Warns if argument 'mode' of 'file' resource is not in 4-digit string form
Bad:
file { '/some/file':
mode => '644',
}
file { '/some/file':
mode => 644,
}
Good:
file { '/some/file':
mode => '0644',
}
Checks if existing resource set is used and all arguments are known in it's class
Bad:
# relation to unknown resource
Class['unknown_class'] -> Class['known_class']
Checks if existing resource is used and all arguments are known in it's class
Bad:
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,
}
}
Checks if only allowed characters are escaped in strings
Bad:
$value = '\s*\.'
$value = "\s*\."
Good:
$value = '\\s*\\.'
$value = "\\s*\\."
Warns if left part of assignment is not a variable or array of variables
Bad:
lookup('some::value') = 1
Warns if argument name is not lowercase, as suggested by Puppet's style guide
Bad:
class some::class (
$ArgumentInCamelCase
) {}
Warns if variable name is not lowercase
Bad:
class some::class () {
$VariableIsNOTInLowercase = 1
Warns if case statement has multiple 'default' cases
Bad:
case $val {
1: {}
default: {}
default: {}
}
Warns if resource set contains multiple resources and no defaults specified
Bad:
file {
'/etc/passwd':
ensure => file,
user => root,
'/etc/group':
ensure => file,
user => root,
group => wheel,
}
Good:
file {
default:
ensure => file,
user => root,
'/etc/passwd':
'/etc/group':
group => wheel,
}
Warns on negation of equation
Bad:
if !($a == 1) { }
if !($a =~ /./) { }
Good:
if $a != 1 { }
if $a !~ /./ { }
Warns if case statement has no default case
Bad:
case $val {
1, 2: { }
3: { }
}
Good:
case $val {
1, 2: { }
3: { }
default: { }
}
Warns if optional argument specified before required
class some::class (
$optional_arg = 1,
$required_arg,
) { }
Good:
class some::class (
$required_arg,
$optional_arg = 1,
) { }
Warns if local resource defaults are used
Bad:
Exec {
provider => shell,
}
exec { 'run command':
command => 'echo Hello',
}
Warns if argument name is not readable enough
Bad:
class some::class (
String $c = '/etc/config',
) { }
Good:
class some::class (
String $config = '/etc/config',
) { }
Warns if variable is not defined in current context
Bad:
if $some_undefined_variable { }
Checks for left-directed relations
Bad:
Class['c'] <- Class['b'] <~ Class['a']
Good:
Class['a'] ~> Class['b'] -> Class['c']
Warns if selector (… ? … : …) used in resource attribute
Bad:
file { '/etc/shadow':
mode => $is_secure ? '0600' : '0644',
}
Good:
$file_mode = $is_secure ? '0600' : '0644'
file { '/etc/shadow':
mode => $file_mode,
}
Warns if argument typed with Sensitive contains default value
Bad:
class some::class (
Sensitive $password = 'admin',
)
Public available default value for sensitive data is nonsense. Good:
class some::class (
Sensitive $password,
)
Checks for statements without side effects
Bad:
if $a {
if $b {
2 + 2
}
}
Warns if exec { … } is specified without unless, onlyif, creates or refreshonly attributes
Bad:
exec { 'run command':
command => '/bin/rm -rf /var/cache/myapp',
}
Good:
exec { 'run command':
command => '/bin/rm -rf /var/cache/myapp',
onlyif => 'test -e /var/cache/myapp',
}
Checks for class/definition/plan arguments uniqueness
Bad:
class some::class (
$arg,
$arg,
$arg,
) { }
Resource attributes must be unique
Bad:
service { 'sshd':
ensure => running,
ensure => stopped,
}
Checks for unused variables. Experimental lint false-positives are possible.
Bad:
class some::class (
$unused_argument,
) {
service { 'sshd':
ensure => running,
}
}
Warns if resource set used with uppercase letters
Bad:
Service { 'sshd':
ensure => running,
}
Good:
service { 'sshd':
ensure => running,
}
Warns if double quoted string has no interpolated expressions and no escaped single quotes
Bad:
$var = "simple literal"
Good:
$var = 'simple literal'
Checks for extra parens
Bad:
if (($var1) or ($var2)) { }
Good:
if $var1 or $var2 { }
Some basic checks are implemented:
File is not executable
File is empty (no root value available)
File parsed without syntax errors
Maps does not contain duplicate keys
Attempt to merge anchor which type is not array nor map
All lints of YAML files plus:
Linter will fail if someclass was unable to parse:
some_class::argument: 1
Linter will fail if modules/someclass/init.pp does not exists:
some_class::argument: 1
Linter will fail if someclass does not accept argument $argumentname:
some_class::argument_name: 1
Linter protects agains typos like:
some_class:argument_name: 1