puppet-lint 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 780fb18c627fc4498cfbd056762846a70b2240619c52a23121d522b56aaec062
4
- data.tar.gz: 59068f03812ef0d9b622fa6b9440567ba4e12f33a5349c1ddc9c642ac7dd51a5
3
+ metadata.gz: 4fce16244bedd61b8ca330f4dd9c2947e8111382830ebd63b22d9f2af8d2a50b
4
+ data.tar.gz: 727de95b4de5c2c2ee24172eea4f9e2551c816e31f3898bcc20e498e4ee7d614
5
5
  SHA512:
6
- metadata.gz: 32fb18937f47636f80535bcc8db5884aa63a6af9a86f148ee25a37b65890d13f275094ab2a81012a5ef7385d696195c59632d234eca805c94a2bc2d3b4d77905
7
- data.tar.gz: 124a921b4ad50b3f923f7180d330a89f313f70c5b77d1dcf8060611a124171e1bf3ef06f8b3d4b27421cad4157c69699c12bc8acc8dd63461a61fb1aecacc14c
6
+ metadata.gz: cb7870bfd9fd0a5bc5a616c651b0d614e6134761b15149c2db34f8796358b23dee66ae465c47f8361f37c9066286d49a619e764277acd6a49aa9ba7c68e2842c
7
+ data.tar.gz: fc2ea5694a8dd0660018d2a8cd1fbee55bb813fd68b052c06127a3857ba00b99e6e67970e9fb58ae1681f34cb4ddb8d6fda8c74690b9bf7c403f37ba80cc024e
data/README.md CHANGED
@@ -9,7 +9,9 @@ guide](http://puppet.com/docs/puppet/latest/style_guide.html). Puppet Lint valid
9
9
 
10
10
  ## Compatibility warning
11
11
 
12
- Puppet Lint version 2 is the last planned version with support for Puppet 3 and Ruby 1.8.7. The next major version of Puppet Lint will drop support for these versions.
12
+ Version 3.0.0 and above will no longer support Puppet 6 environments.
13
+
14
+ In cases where Puppet Lint is required in an environment with Puppet 6, we recommend pinning to version 2.5.2.
13
15
 
14
16
  ## Installation
15
17
 
@@ -155,6 +155,18 @@ class PuppetLint::Data
155
155
  end
156
156
  end
157
157
 
158
+ # Internal: Determine if the given token contains a CLASSREF in
159
+ # the token chain..
160
+ #
161
+ # Returns a Boolean.
162
+ def classref?(token)
163
+ current_token = token
164
+ while (current_token = current_token.prev_code_token)
165
+ return true if current_token.type == :CLASSREF
166
+ return false if current_token.type == :NAME
167
+ end
168
+ end
169
+
158
170
  # Internal: Calculate the positions of all resource declarations within the
159
171
  # tokenised manifest. These positions only point to the content of the
160
172
  # resource declarations, they do not include resource types or titles.
@@ -170,6 +182,7 @@ class PuppetLint::Data
170
182
  result = []
171
183
  tokens.select { |t| t.type == :COLON }.each do |colon_token|
172
184
  next unless colon_token.next_code_token && colon_token.next_code_token.type != :LBRACE
185
+ next if classref?(colon_token)
173
186
 
174
187
  rel_start_idx = tokens[marker..-1].index(colon_token)
175
188
  break if rel_start_idx.nil?
@@ -30,7 +30,7 @@ PuppetLint.new_check(:trailing_whitespace) do
30
30
 
31
31
  prev_token = problem[:token].prev_token
32
32
  next_token = problem[:token].next_token
33
- prev_token.next_token = next_token
33
+ prev_token.next_token = next_token unless prev_token.nil?
34
34
  next_token.prev_token = prev_token unless next_token.nil?
35
35
  tokens.delete(problem[:token])
36
36
  end
@@ -0,0 +1,189 @@
1
+ # Public: A puppet-lint custom check to detect legacy facts.
2
+ #
3
+ # This check will optionally convert from legacy facts like $::operatingsystem
4
+ # or legacy hashed facts like $facts['operatingsystem'] to the
5
+ # new structured facts like $facts['os']['name'].
6
+ #
7
+ # This plugin was adopted in to puppet-lint from https://github.com/mmckinst/puppet-lint-legacy_facts-check
8
+ # Thanks to @mmckinst, @seanmil, @rodjek, @baurmatt, @bart2 and @joshcooper for the original work.
9
+ PuppetLint.new_check(:legacy_facts) do
10
+ LEGACY_FACTS_VAR_TYPES = Set[:VARIABLE, :UNENC_VARIABLE]
11
+
12
+ # These facts that can't be converted to new facts.
13
+ UNCONVERTIBLE_FACTS = ['memoryfree_mb', 'memorysize_mb', 'swapfree_mb',
14
+ 'swapsize_mb', 'blockdevices', 'interfaces', 'zones',
15
+ 'sshfp_dsa', 'sshfp_ecdsa', 'sshfp_ed25519',
16
+ 'sshfp_rsa'].freeze
17
+
18
+ # These facts will depend on how a system is set up and can't just be
19
+ # enumerated like the EASY_FACTS below.
20
+ #
21
+ # For example a server might have two block devices named 'sda' and 'sdb' so
22
+ # there would be a $blockdeivce_sda_vendor and $blockdeivce_sdb_vendor fact
23
+ # for each device. Or it could have 26 block devices going all the way up to
24
+ # 'sdz'. There is no way to know what the possibilities are so we have to use
25
+ # a regex to match them.
26
+ REGEX_FACTS = [%r{^blockdevice_(?<devicename>.*)_(?<attribute>model|size|vendor)$},
27
+ %r{^(?<attribute>ipaddress|ipaddress6|macaddress|mtu|netmask|netmask6|network|network6)_(?<interface>.*)$},
28
+ %r{^processor(?<id>[0-9]+)$},
29
+ %r{^sp_(?<name>.*)$},
30
+ %r{^ssh(?<algorithm>dsa|ecdsa|ed25519|rsa)key$},
31
+ %r{^ldom_(?<name>.*)$},
32
+ %r{^zone_(?<name>.*)_(?<attribute>brand|iptype|name|uuid|id|path|status)$}].freeze
33
+
34
+ # These facts have a one to one correlation between a legacy fact and a new
35
+ # structured fact.
36
+ EASY_FACTS = {
37
+ 'architecture' => "facts['os']['architecture']",
38
+ 'augeasversion' => "facts['augeas']['version']",
39
+ 'bios_release_date' => "facts['dmi']['bios']['release_date']",
40
+ 'bios_vendor' => "facts['dmi']['bios']['vendor']",
41
+ 'bios_version' => "facts['dmi']['bios']['version']",
42
+ 'boardassettag' => "facts['dmi']['board']['asset_tag']",
43
+ 'boardmanufacturer' => "facts['dmi']['board']['manufacturer']",
44
+ 'boardproductname' => "facts['dmi']['board']['product']",
45
+ 'boardserialnumber' => "facts['dmi']['board']['serial_number']",
46
+ 'chassisassettag' => "facts['dmi']['chassis']['asset_tag']",
47
+ 'chassistype' => "facts['dmi']['chassis']['type']",
48
+ 'domain' => "facts['networking']['domain']",
49
+ 'fqdn' => "facts['networking']['fqdn']",
50
+ 'gid' => "facts['identity']['group']",
51
+ 'hardwareisa' => "facts['processors']['isa']",
52
+ 'hardwaremodel' => "facts['os']['hardware']",
53
+ 'hostname' => "facts['networking']['hostname']",
54
+ 'id' => "facts['identity']['user']",
55
+ 'ipaddress' => "facts['networking']['ip']",
56
+ 'ipaddress6' => "facts['networking']['ip6']",
57
+ 'lsbdistcodename' => "facts['os']['distro']['codename']",
58
+ 'lsbdistdescription' => "facts['os']['distro']['description']",
59
+ 'lsbdistid' => "facts['os']['distro']['id']",
60
+ 'lsbdistrelease' => "facts['os']['distro']['release']['full']",
61
+ 'lsbmajdistrelease' => "facts['os']['distro']['release']['major']",
62
+ 'lsbminordistrelease' => "facts['os']['distro']['release']['minor']",
63
+ 'lsbrelease' => "facts['os']['distro']['release']['specification']",
64
+ 'macaddress' => "facts['networking']['mac']",
65
+ 'macosx_buildversion' => "facts['os']['build']",
66
+ 'macosx_productname' => "facts['os']['product']",
67
+ 'macosx_productversion' => "facts['os']['version']['full']",
68
+ 'macosx_productversion_major' => "facts['os']['version']['major']",
69
+ 'macosx_productversion_minor' => "facts['os']['version']['minor']",
70
+ 'manufacturer' => "facts['dmi']['manufacturer']",
71
+ 'memoryfree' => "facts['memory']['system']['available']",
72
+ 'memorysize' => "facts['memory']['system']['total']",
73
+ 'netmask' => "facts['networking']['netmask']",
74
+ 'netmask6' => "facts['networking']['netmask6']",
75
+ 'network' => "facts['networking']['network']",
76
+ 'network6' => "facts['networking']['network6']",
77
+ 'operatingsystem' => "facts['os']['name']",
78
+ 'operatingsystemmajrelease' => "facts['os']['release']['major']",
79
+ 'operatingsystemrelease' => "facts['os']['release']['full']",
80
+ 'osfamily' => "facts['os']['family']",
81
+ 'physicalprocessorcount' => "facts['processors']['physicalcount']",
82
+ 'processorcount' => "facts['processors']['count']",
83
+ 'productname' => "facts['dmi']['product']['name']",
84
+ 'rubyplatform' => "facts['ruby']['platform']",
85
+ 'rubysitedir' => "facts['ruby']['sitedir']",
86
+ 'rubyversion' => "facts['ruby']['version']",
87
+ 'selinux' => "facts['os']['selinux']['enabled']",
88
+ 'selinux_config_mode' => "facts['os']['selinux']['config_mode']",
89
+ 'selinux_config_policy' => "facts['os']['selinux']['config_policy']",
90
+ 'selinux_current_mode' => "facts['os']['selinux']['current_mode']",
91
+ 'selinux_enforced' => "facts['os']['selinux']['enforced']",
92
+ 'selinux_policyversion' => "facts['os']['selinux']['policy_version']",
93
+ 'serialnumber' => "facts['dmi']['product']['serial_number']",
94
+ 'swapencrypted' => "facts['memory']['swap']['encrypted']",
95
+ 'swapfree' => "facts['memory']['swap']['available']",
96
+ 'swapsize' => "facts['memory']['swap']['total']",
97
+ 'system32' => "facts['os']['windows']['system32']",
98
+ 'uptime' => "facts['system_uptime']['uptime']",
99
+ 'uptime_days' => "facts['system_uptime']['days']",
100
+ 'uptime_hours' => "facts['system_uptime']['hours']",
101
+ 'uptime_seconds' => "facts['system_uptime']['seconds']",
102
+ 'uuid' => "facts['dmi']['product']['uuid']",
103
+ 'xendomains' => "facts['xen']['domains']",
104
+ 'zonename' => "facts['solaris_zones']['current']",
105
+ }.freeze
106
+
107
+ # A list of valid hash key token types
108
+ HASH_KEY_TYPES = Set[
109
+ :STRING, # Double quoted string
110
+ :SSTRING, # Single quoted string
111
+ :NAME, # Unquoted single word
112
+ ].freeze
113
+
114
+ def check
115
+ tokens.select { |x| LEGACY_FACTS_VAR_TYPES.include?(x.type) }.each do |token|
116
+ fact_name = ''
117
+
118
+ # Get rid of the top scope before we do our work. We don't need to
119
+ # preserve it because it won't work with the new structured facts.
120
+ if token.value.start_with?('::')
121
+ fact_name = token.value.sub(%r{^::}, '')
122
+
123
+ # This matches using legacy facts in a the new structured fact. For
124
+ # example this would match 'uuid' in $facts['uuid'] so it can be converted
125
+ # to facts['dmi']['product']['uuid']"
126
+ elsif token.value == 'facts'
127
+ fact_name = hash_key_for(token)
128
+
129
+ elsif token.value.start_with?("facts['")
130
+ fact_name = token.value.match(%r{facts\['(.*)'\]})[1]
131
+ end
132
+
133
+ next unless EASY_FACTS.include?(fact_name) || UNCONVERTIBLE_FACTS.include?(fact_name) || fact_name.match(Regexp.union(REGEX_FACTS))
134
+ notify :warning, {
135
+ message: "legacy fact '#{fact_name}'",
136
+ line: token.line,
137
+ column: token.column,
138
+ token: token,
139
+ fact_name: fact_name,
140
+ }
141
+ end
142
+ end
143
+
144
+ # If the variable is using the $facts hash represented internally by multiple
145
+ # tokens, this helper simplifies accessing the hash key.
146
+ def hash_key_for(token)
147
+ lbrack_token = token.next_code_token
148
+ return '' unless lbrack_token && lbrack_token.type == :LBRACK
149
+
150
+ key_token = lbrack_token.next_code_token
151
+ return '' unless key_token && HASH_KEY_TYPES.include?(key_token.type)
152
+
153
+ key_token.value
154
+ end
155
+
156
+ def fix(problem)
157
+ fact_name = problem[:fact_name]
158
+
159
+ # Check if the variable is using the $facts hash represented internally by
160
+ # multiple tokens and remove the tokens for the old legacy key if so.
161
+ if problem[:token].value == 'facts'
162
+ loop do
163
+ t = problem[:token].next_token
164
+ remove_token(t)
165
+ break if t.type == :RBRACK
166
+ end
167
+ end
168
+
169
+ if EASY_FACTS.include?(fact_name)
170
+ problem[:token].value = EASY_FACTS[fact_name]
171
+ elsif fact_name.match(Regexp.union(REGEX_FACTS))
172
+ if (m = fact_name.match(%r{^blockdevice_(?<devicename>.*)_(?<attribute>model|size|vendor)$}))
173
+ problem[:token].value = "facts['disks']['" << m['devicename'] << "']['" << m['attribute'] << "']"
174
+ elsif (m = fact_name.match(%r{^(?<attribute>ipaddress|ipaddress6|macaddress|mtu|netmask|netmask6|network|network6)_(?<interface>.*)$}))
175
+ problem[:token].value = "facts['networking']['interfaces']['" << m['interface'] << "']['" << m['attribute'].sub('address', '') << "']"
176
+ elsif (m = fact_name.match(%r{^processor(?<id>[0-9]+)$}))
177
+ problem[:token].value = "facts['processors']['models'][" << m['id'] << ']'
178
+ elsif (m = fact_name.match(%r{^sp_(?<name>.*)$}))
179
+ problem[:token].value = "facts['system_profiler']['" << m['name'] << "']"
180
+ elsif (m = fact_name.match(%r{^ssh(?<algorithm>dsa|ecdsa|ed25519|rsa)key$}))
181
+ problem[:token].value = "facts['ssh']['" << m['algorithm'] << "']['key']"
182
+ elsif (m = fact_name.match(%r{^ldom_(?<name>.*)$}))
183
+ problem[:token].value = "facts['ldom']['" << m['name'] << "']"
184
+ elsif (m = fact_name.match(%r{^zone_(?<name>.*)_(?<attribute>brand|iptype|name|uuid|id|path|status)$}))
185
+ problem[:token].value = "facts['solaris_zones']['zones']['" << m['name'] << "']['" << m['attribute'] << "']"
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,38 @@
1
+ # Public: A puppet-lint plugin that will check for the use of top scope facts.
2
+ # For example, the fact `$facts['kernel']` should be used over
3
+ # `$::kernel`.
4
+ #
5
+ # The check only finds facts using the top-scope: ie it will find $::operatingsystem
6
+ # but not $operatingsystem. It also all top scope variables are facts.
7
+ # If you have top scope variables that aren't facts you should configure the
8
+ # linter to ignore them.
9
+ #
10
+ # You can whitelist top scope variables to ignore via the Rake task.
11
+ # You should insert the following line to your Rakefile.
12
+ # `PuppetLint.configuration.top_scope_variables = ['location', 'role']`
13
+ #
14
+ # This plugin was adopted in to puppet-lint from https://github.com/mmckinst/puppet-lint-top_scope_facts-check
15
+ # Thanks to @mmckinst, @seanmil and @alexjfisher for the original work.
16
+ PuppetLint.new_check(:top_scope_facts) do
17
+ TOP_SCOPE_FACTS_VAR_TYPES = Set[:VARIABLE, :UNENC_VARIABLE]
18
+ def check
19
+ whitelist = ['trusted', 'facts'] + (PuppetLint.configuration.top_scope_variables || [])
20
+ whitelist = whitelist.join('|')
21
+ tokens.select { |x| TOP_SCOPE_FACTS_VAR_TYPES.include?(x.type) }.each do |token|
22
+ next unless %r{^::}.match?(token.value)
23
+ next if %r{^::(#{whitelist})\[?}.match?(token.value)
24
+ next if %r{^::[a-z0-9_][a-zA-Z0-9_]+::}.match?(token.value)
25
+
26
+ notify :warning, {
27
+ message: 'top scope fact instead of facts hash',
28
+ line: token.line,
29
+ column: token.column,
30
+ token: token,
31
+ }
32
+ end
33
+ end
34
+
35
+ def fix(problem)
36
+ problem[:token].value = "facts['" + problem[:token].value.sub(%r{^::}, '') + "']"
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  class PuppetLint
2
- VERSION = '3.0.1'.freeze
2
+ VERSION = '3.1.0'.freeze
3
3
  end
@@ -1,24 +1,22 @@
1
1
  ---
2
2
  require:
3
- - rubocop-performance
4
- - rubocop-rspec
3
+ - rubocop-performance
4
+ - rubocop-rspec
5
5
  AllCops:
6
+ TargetRubyVersion: '2.7'
6
7
  DisplayCopNames: true
7
- TargetRubyVersion: '2.6'
8
8
  SuggestExtensions: false
9
- Include:
10
- - "**/*.rb"
11
9
  Exclude:
12
- - bin/*
13
- - ".vendor/**/*"
14
- - "**/Gemfile"
15
- - "**/Rakefile"
16
- - pkg/**/*
17
- - spec/fixtures/**/*
18
- - vendor/**/*
19
- - "**/Puppetfile"
20
- - "**/Vagrantfile"
21
- - "**/Guardfile"
10
+ - "bin/*"
11
+ - ".vendor/**/*"
12
+ - "**/Gemfile"
13
+ - "**/Rakefile"
14
+ - "pkg/**/*"
15
+ - "spec/fixtures/**/*"
16
+ - "vendor/**/*"
17
+ - "**/Puppetfile"
18
+ - "**/Vagrantfile"
19
+ - "**/Guardfile"
22
20
  Layout/LineLength:
23
21
  Description: People have wide screens, use them.
24
22
  Max: 200
@@ -378,7 +378,7 @@ describe PuppetLint::Bin do
378
378
  if respond_to?(:include_json)
379
379
  is_expected.to include_json([[{ 'KIND' => 'WARNING' }]])
380
380
  else
381
- is_expected.to match(%r{\[\n \{})
381
+ is_expected.to match(%r{\[\n \[\n \{})
382
382
  end
383
383
  end
384
384
  end
@@ -397,7 +397,7 @@ describe PuppetLint::Bin do
397
397
  if respond_to?(:include_json)
398
398
  is_expected.to include_json([[{ 'KIND' => 'ERROR' }], [{ 'KIND' => 'WARNING' }]])
399
399
  else
400
- is_expected.to match(%r{\[\n \{})
400
+ is_expected.to match(%r{\[\n \[\n \{})
401
401
  end
402
402
  end
403
403
  end
@@ -33,6 +33,42 @@ describe PuppetLint::Data do
33
33
  }
34
34
  end
35
35
  end
36
+
37
+ context 'when given a defaults declaration' do
38
+ let(:manifest) { "Service { 'foo': }" }
39
+
40
+ it 'returns an empty array' do
41
+ expect(data.resource_indexes).to eq([])
42
+ end
43
+ end
44
+
45
+ context 'when given a set of resource declarations' do
46
+ let(:manifest) { <<-MANIFEST }
47
+ service {
48
+ 'foo':
49
+ ensure => running,
50
+ }
51
+
52
+ service {
53
+ 'bar':
54
+ ensure => running;
55
+ 'foobar':
56
+ ensure => stopped;
57
+ }
58
+
59
+ service { ['first', 'second']:
60
+ ensure => running,
61
+ }
62
+
63
+ service { 'third':
64
+ ensure => running,
65
+ }
66
+ MANIFEST
67
+
68
+ it 'returns an array of resource indexes' do
69
+ expect(data.resource_indexes.length).to eq(5)
70
+ end
71
+ end
36
72
  end
37
73
 
38
74
  describe '.insert' do
@@ -105,5 +105,17 @@ describe 'trailing_whitespace' do
105
105
  expect(manifest).to eq(fixed)
106
106
  end
107
107
  end
108
+
109
+ context 'empty lines with nothing but whitespace' do
110
+ let(:code) { " \n " }
111
+
112
+ it 'detects problems with both empty lines' do
113
+ expect(problems).to have(2).problem
114
+ end
115
+
116
+ it 'fixes the manifest' do
117
+ expect(manifest).to eq("\n")
118
+ end
119
+ end
108
120
  end
109
121
  end
@@ -0,0 +1,414 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'legacy_facts' do
4
+ context 'with fix disabled' do
5
+ context "fact variable using modern $facts['os']['family'] hash" do
6
+ let(:code) { "$facts['os']['family']" }
7
+
8
+ it 'does not detect any problems' do
9
+ expect(problems).to have(0).problem
10
+ end
11
+ end
12
+
13
+ context "fact variable using modern $facts['ssh']['rsa']['key'] hash" do
14
+ let(:code) { "$facts['ssh']['rsa']['key']" }
15
+
16
+ it 'does not detect any problems' do
17
+ expect(problems).to have(0).problem
18
+ end
19
+ end
20
+
21
+ context 'fact variable using legacy $osfamily' do
22
+ let(:code) { '$osfamily' }
23
+
24
+ it 'does not detect any problems' do
25
+ expect(problems).to have(0).problem
26
+ end
27
+ end
28
+
29
+ context "fact variable using legacy $facts['osfamily']" do
30
+ let(:code) { "$facts['osfamily']" }
31
+
32
+ it 'onlies detect a single problem' do
33
+ expect(problems).to have(1).problem
34
+ end
35
+ end
36
+
37
+ context 'fact variable using legacy $::osfamily' do
38
+ let(:code) { '$::osfamily' }
39
+
40
+ it 'onlies detect a single problem' do
41
+ expect(problems).to have(1).problem
42
+ end
43
+ end
44
+
45
+ context 'fact variable using legacy $::blockdevice_sda_model' do
46
+ let(:code) { '$::blockdevice_sda_model' }
47
+
48
+ it 'onlies detect a single problem' do
49
+ expect(problems).to have(1).problem
50
+ end
51
+ end
52
+
53
+ context "fact variable using legacy $facts['ipaddress6_em2']" do
54
+ let(:code) { "$facts['ipaddress6_em2']" }
55
+
56
+ it 'onlies detect a single problem' do
57
+ expect(problems).to have(1).problem
58
+ end
59
+ end
60
+
61
+ context 'fact variable using legacy $::zone_foobar_uuid' do
62
+ let(:code) { '$::zone_foobar_uuid' }
63
+
64
+ it 'onlies detect a single problem' do
65
+ expect(problems).to have(1).problem
66
+ end
67
+ end
68
+
69
+ context 'fact variable using legacy $::processor314' do
70
+ let(:code) { '$::processor314' }
71
+
72
+ it 'onlies detect a single problem' do
73
+ expect(problems).to have(1).problem
74
+ end
75
+ end
76
+
77
+ context 'fact variable using legacy $::sp_l3_cache' do
78
+ let(:code) { '$::sp_l3_cache' }
79
+
80
+ it 'onlies detect a single problem' do
81
+ expect(problems).to have(1).problem
82
+ end
83
+ end
84
+
85
+ context 'fact variable using legacy $::sshrsakey' do
86
+ let(:code) { '$::sshrsakey' }
87
+
88
+ it 'onlies detect a single problem' do
89
+ expect(problems).to have(1).problem
90
+ end
91
+ end
92
+
93
+ context 'fact variable in interpolated string "${::osfamily}"' do
94
+ let(:code) { '"start ${::osfamily} end"' }
95
+
96
+ it 'onlies detect a single problem' do
97
+ expect(problems).to have(1).problem
98
+ end
99
+ end
100
+
101
+ context 'fact variable using legacy variable in double quotes "$::osfamily"' do
102
+ let(:code) { '"$::osfamily"' }
103
+
104
+ it 'onlies detect a single problem' do
105
+ expect(problems).to have(1).problem
106
+ end
107
+ end
108
+
109
+ context 'fact variable using legacy facts hash variable in interpolation' do
110
+ let(:code) { %("${facts['osfamily']}") }
111
+
112
+ it 'detects a single problem' do
113
+ expect(problems).to have(1).problem
114
+ end
115
+ end
116
+ end
117
+
118
+ context 'with fix enabled' do
119
+ before(:each) do
120
+ PuppetLint.configuration.fix = true
121
+ end
122
+
123
+ after(:each) do
124
+ PuppetLint.configuration.fix = false
125
+ end
126
+
127
+ context "fact variable using modern $facts['os']['family'] hash" do
128
+ let(:code) { "$facts['os']['family']" }
129
+
130
+ it 'does not detect any problems' do
131
+ expect(problems).to have(0).problem
132
+ end
133
+ end
134
+
135
+ context "fact variable using modern $facts['ssh']['rsa']['key'] hash" do
136
+ let(:code) { "$facts['ssh']['rsa']['key']" }
137
+
138
+ it 'does not detect any problems' do
139
+ expect(problems).to have(0).problem
140
+ end
141
+ end
142
+
143
+ context 'fact variable using legacy $osfamily' do
144
+ let(:code) { '$osfamily' }
145
+
146
+ it 'does not detect any problems' do
147
+ expect(problems).to have(0).problem
148
+ end
149
+ end
150
+
151
+ context "fact variable using legacy $facts['osfamily']" do
152
+ let(:code) { "$facts['osfamily']" }
153
+ let(:msg) { "legacy fact 'osfamily'" }
154
+
155
+ it 'onlies detect a single problem' do
156
+ expect(problems).to have(1).problem
157
+ end
158
+
159
+ it 'fixes the problem' do
160
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(1)
161
+ end
162
+
163
+ it 'uses the facts hash' do
164
+ expect(manifest).to eq("$facts['os']['family']")
165
+ end
166
+ end
167
+
168
+ context 'fact variable using legacy $::osfamily' do
169
+ let(:code) { '$::osfamily' }
170
+ let(:msg) { "legacy fact 'osfamily'" }
171
+
172
+ it 'onlies detect a single problem' do
173
+ expect(problems).to have(1).problem
174
+ end
175
+
176
+ it 'fixes the problem' do
177
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(1)
178
+ end
179
+
180
+ it 'uses the facts hash' do
181
+ expect(manifest).to eq("$facts['os']['family']")
182
+ end
183
+ end
184
+
185
+ context 'fact variable using legacy $::sshrsakey' do
186
+ let(:code) { '$::sshrsakey' }
187
+ let(:msg) { "legacy fact 'sshrsakey'" }
188
+
189
+ it 'onlies detect a single problem' do
190
+ expect(problems).to have(1).problem
191
+ end
192
+
193
+ it 'fixes the problem' do
194
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(1)
195
+ end
196
+
197
+ it 'uses the facts hash' do
198
+ expect(manifest).to eq("$facts['ssh']['rsa']['key']")
199
+ end
200
+ end
201
+
202
+ context 'fact variable using legacy $::memoryfree_mb' do
203
+ let(:code) { '$::memoryfree_mb' }
204
+
205
+ it 'onlies detect a single problem' do
206
+ expect(problems).to have(1).problem
207
+ end
208
+
209
+ it 'continues to use the legacy fact' do
210
+ expect(manifest).to eq('$::memoryfree_mb')
211
+ end
212
+ end
213
+
214
+ context 'fact variable using legacy $::blockdevice_sda_model' do
215
+ let(:code) { '$::blockdevice_sda_model' }
216
+
217
+ it 'onlies detect a single problem' do
218
+ expect(problems).to have(1).problem
219
+ end
220
+ it 'uses the facts hash' do
221
+ expect(manifest).to eq("$facts['disks']['sda']['model']")
222
+ end
223
+ end
224
+
225
+ context "fact variable using legacy $facts['ipaddress6_em2']" do
226
+ let(:code) { "$facts['ipaddress6_em2']" }
227
+
228
+ it 'onlies detect a single problem' do
229
+ expect(problems).to have(1).problem
230
+ end
231
+ it 'uses the facts hash' do
232
+ expect(manifest).to eq("$facts['networking']['interfaces']['em2']['ip6']")
233
+ end
234
+ end
235
+
236
+ context 'fact variable using legacy $::zone_foobar_uuid' do
237
+ let(:code) { '$::zone_foobar_uuid' }
238
+
239
+ it 'onlies detect a single problem' do
240
+ expect(problems).to have(1).problem
241
+ end
242
+ it 'uses the facts hash' do
243
+ expect(manifest).to eq("$facts['solaris_zones']['zones']['foobar']['uuid']")
244
+ end
245
+ end
246
+
247
+ context 'fact variable using legacy $::processor314' do
248
+ let(:code) { '$::processor314' }
249
+
250
+ it 'onlies detect a single problem' do
251
+ expect(problems).to have(1).problem
252
+ end
253
+ it 'uses the facts hash' do
254
+ expect(manifest).to eq("$facts['processors']['models'][314]")
255
+ end
256
+ end
257
+
258
+ context 'fact variable using legacy $::sp_l3_cache' do
259
+ let(:code) { '$::sp_l3_cache' }
260
+
261
+ it 'onlies detect a single problem' do
262
+ expect(problems).to have(1).problem
263
+ end
264
+ it 'uses the facts hash' do
265
+ expect(manifest).to eq("$facts['system_profiler']['l3_cache']")
266
+ end
267
+ end
268
+
269
+ context 'fact variable using legacy $::sshrsakey' do
270
+ let(:code) { '$::sshrsakey' }
271
+
272
+ it 'onlies detect a single problem' do
273
+ expect(problems).to have(1).problem
274
+ end
275
+ it 'uses the facts hash' do
276
+ expect(manifest).to eq("$facts['ssh']['rsa']['key']")
277
+ end
278
+ end
279
+
280
+ context 'fact variable in interpolated string "${::osfamily}"' do
281
+ let(:code) { '"start ${::osfamily} end"' }
282
+
283
+ it 'onlies detect a single problem' do
284
+ expect(problems).to have(1).problem
285
+ end
286
+
287
+ it 'uses the facts hash' do
288
+ expect(manifest).to eq('"start '"${facts['os']['family']}"' end"') # rubocop:disable Lint/ImplicitStringConcatenation
289
+ end
290
+ end
291
+
292
+ context 'fact variable using legacy variable in double quotes "$::osfamily"' do
293
+ let(:code) { '"$::osfamily"' }
294
+
295
+ it 'onlies detect a single problem' do
296
+ expect(problems).to have(1).problem
297
+ end
298
+
299
+ it 'uses the facts hash' do
300
+ expect(manifest).to eq("\"$facts['os']['family']\"")
301
+ end
302
+ end
303
+ context 'fact variable using legacy variable in double quotes "$::gid"' do
304
+ let(:code) { '"$::gid"' }
305
+
306
+ it 'onlies detect a single problem' do
307
+ expect(problems).to have(1).problem
308
+ end
309
+
310
+ it 'uses the facts hash' do
311
+ expect(manifest).to eq("\"$facts['identity']['group']\"")
312
+ end
313
+ end
314
+ context 'fact variable using legacy variable in double quotes "$::id"' do
315
+ let(:code) { '"$::id"' }
316
+
317
+ it 'onlies detect a single problem' do
318
+ expect(problems).to have(1).problem
319
+ end
320
+
321
+ it 'uses the facts hash' do
322
+ expect(manifest).to eq("\"$facts['identity']['user']\"")
323
+ end
324
+ end
325
+ context 'fact variable using legacy variable in double quotes "$::lsbdistcodename"' do
326
+ let(:code) { '"$::lsbdistcodename"' }
327
+
328
+ it 'onlies detect a single problem' do
329
+ expect(problems).to have(1).problem
330
+ end
331
+
332
+ it 'uses the facts hash' do
333
+ expect(manifest).to eq("\"$facts['os']['distro']['codename']\"")
334
+ end
335
+ end
336
+ context 'fact variable using legacy variable in double quotes "$::lsbdistdescription"' do
337
+ let(:code) { '"$::lsbdistdescription"' }
338
+
339
+ it 'onlies detect a single problem' do
340
+ expect(problems).to have(1).problem
341
+ end
342
+
343
+ it 'uses the facts hash' do
344
+ expect(manifest).to eq("\"$facts['os']['distro']['description']\"")
345
+ end
346
+ end
347
+ context 'fact variable using legacy variable in double quotes "$::lsbdistid"' do
348
+ let(:code) { '"$::lsbdistid"' }
349
+
350
+ it 'onlies detect a single problem' do
351
+ expect(problems).to have(1).problem
352
+ end
353
+
354
+ it 'uses the facts hash' do
355
+ expect(manifest).to eq("\"$facts['os']['distro']['id']\"")
356
+ end
357
+ end
358
+ context 'fact variable using legacy variable in double quotes "$::lsbdistrelease"' do
359
+ let(:code) { '"$::lsbdistrelease"' }
360
+
361
+ it 'onlies detect a single problem' do
362
+ expect(problems).to have(1).problem
363
+ end
364
+
365
+ it 'uses the facts hash' do
366
+ expect(manifest).to eq("\"$facts['os']['distro']['release']['full']\"")
367
+ end
368
+ end
369
+ context 'fact variable using legacy variable in double quotes "$::lsbmajdistrelease"' do
370
+ let(:code) { '"$::lsbmajdistrelease"' }
371
+
372
+ it 'onlies detect a single problem' do
373
+ expect(problems).to have(1).problem
374
+ end
375
+
376
+ it 'uses the facts hash' do
377
+ expect(manifest).to eq("\"$facts['os']['distro']['release']['major']\"")
378
+ end
379
+ end
380
+ context 'fact variable using legacy variable in double quotes "$::lsbminordistrelease"' do
381
+ let(:code) { '"$::lsbminordistrelease"' }
382
+
383
+ it 'onlies detect a single problem' do
384
+ expect(problems).to have(1).problem
385
+ end
386
+
387
+ it 'uses the facts hash' do
388
+ expect(manifest).to eq("\"$facts['os']['distro']['release']['minor']\"")
389
+ end
390
+ end
391
+ context 'fact variable using legacy variable in double quotes "$::lsbrelease"' do
392
+ let(:code) { '"$::lsbrelease"' }
393
+
394
+ it 'onlies detect a single problem' do
395
+ expect(problems).to have(1).problem
396
+ end
397
+
398
+ it 'uses the facts hash' do
399
+ expect(manifest).to eq("\"$facts['os']['distro']['release']['specification']\"")
400
+ end
401
+ end
402
+ context "fact variable using facts hash in double quotes \"$facts['lsbrelease']\"" do
403
+ let(:code) { "\"${facts['lsbrelease']}\"" }
404
+
405
+ it 'onlies detect a single problem' do
406
+ expect(problems).to have(1).problem
407
+ end
408
+
409
+ it 'uses the facts hash' do
410
+ expect(manifest).to eq("\"${facts['os']['distro']['release']['specification']}\"")
411
+ end
412
+ end
413
+ end
414
+ end
@@ -0,0 +1,194 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'top_scope_facts' do
4
+ let(:msg) { 'top scope fact instead of facts hash' }
5
+
6
+ context 'with fix disabled' do
7
+ context 'fact variable using $facts hash' do
8
+ let(:code) { "$facts['operatingsystem']" }
9
+
10
+ it 'does not detect any problems' do
11
+ expect(problems).to have(0).problem
12
+ end
13
+ end
14
+ context 'non-fact variable with two colons' do
15
+ let(:code) { '$foo::bar' }
16
+
17
+ it 'does not detect any problems' do
18
+ expect(problems).to have(0).problem
19
+ end
20
+ end
21
+
22
+ context 'top scope $::facts hash' do
23
+ let(:code) { "$::facts['os']['family']" }
24
+
25
+ it 'does not detect any problems' do
26
+ expect(problems).to have(0).problem
27
+ end
28
+ end
29
+
30
+ context 'top scope $::trusted hash' do
31
+ let(:code) { "$::trusted['certname']" }
32
+
33
+ it 'does not detect any problems' do
34
+ expect(problems).to have(0).problem
35
+ end
36
+ end
37
+
38
+ context 'fact variable using top scope' do
39
+ let(:code) { '$::operatingsystem' }
40
+
41
+ it 'onlies detect a single problem' do
42
+ expect(problems).to have(1).problem
43
+ end
44
+
45
+ it 'creates a warning' do
46
+ expect(problems).to contain_warning(msg).on_line(1).in_column(1)
47
+ end
48
+ end
49
+
50
+ context 'fact variable using top scope with curly braces in double quote' do
51
+ let(:code) { '"${::operatingsystem}"' }
52
+
53
+ it 'onlies detect a single problem' do
54
+ expect(problems).to have(1).problem
55
+ end
56
+
57
+ it 'creates a warning' do
58
+ expect(problems).to contain_warning(msg).on_line(1).in_column(4)
59
+ end
60
+ end
61
+
62
+ context 'out of scope namespaced variable with leading ::' do
63
+ let(:code) { '$::profile::foo::bar' }
64
+
65
+ it 'does not detect any problems' do
66
+ expect(problems).to have(0).problem
67
+ end
68
+
69
+ context 'inside double quotes' do
70
+ let(:code) { '"$::profile::foo::bar"' }
71
+
72
+ it 'does not detect any problems' do
73
+ expect(problems).to have(0).problem
74
+ end
75
+ end
76
+
77
+ context 'with curly braces in double quote' do
78
+ let(:code) { '"${::profile::foo::bar}"' }
79
+
80
+ it 'does not detect any problems' do
81
+ expect(problems).to have(0).problem
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'with fix enabled' do
88
+ before(:each) do
89
+ PuppetLint.configuration.fix = true
90
+ end
91
+
92
+ after(:each) do
93
+ PuppetLint.configuration.fix = false
94
+ end
95
+
96
+ context 'fact variable using $facts hash' do
97
+ let(:code) { "$facts['operatingsystem']" }
98
+
99
+ it 'does not detect any problems' do
100
+ expect(problems).to have(0).problem
101
+ end
102
+ end
103
+
104
+ context 'non-fact variable with two colons' do
105
+ let(:code) { '$foo::bar' }
106
+
107
+ it 'does not detect any problems' do
108
+ expect(problems).to have(0).problem
109
+ end
110
+ end
111
+
112
+ context 'top scope $::facts hash' do
113
+ let(:code) { "$::facts['os']['family']" }
114
+
115
+ it 'does not detect any problems' do
116
+ expect(problems).to have(0).problem
117
+ end
118
+ end
119
+
120
+ context 'top scope $::trusted hash' do
121
+ let(:code) { "$::trusted['certname']" }
122
+
123
+ it 'does not detect any problems' do
124
+ expect(problems).to have(0).problem
125
+ end
126
+ end
127
+
128
+ context 'fact variable using top scope' do
129
+ let(:code) { '$::operatingsystem' }
130
+
131
+ it 'onlies detect a single problem' do
132
+ expect(problems).to have(1).problem
133
+ end
134
+
135
+ it 'fixes the problem' do
136
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(1)
137
+ end
138
+
139
+ it 'shoulds use the facts hash' do
140
+ expect(manifest).to eq("$facts['operatingsystem']")
141
+ end
142
+ end
143
+
144
+ context 'fact variable using top scope with curly braces in double quote' do
145
+ let(:code) { '"${::operatingsystem}"' }
146
+
147
+ it 'fixes the problem' do
148
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(4)
149
+ end
150
+
151
+ it 'shoulds use the facts hash' do
152
+ expect(manifest).to eq('"${facts[\'operatingsystem\']}"')
153
+ end
154
+ end
155
+
156
+ context 'with custom top scope fact variables' do
157
+ before(:each) do
158
+ PuppetLint.configuration.top_scope_variables = ['location', 'role']
159
+ end
160
+
161
+ context 'fact variable using $facts hash' do
162
+ let(:code) { "$facts['operatingsystem']" }
163
+
164
+ it 'does not detect any problems' do
165
+ expect(problems).to have(0).problem
166
+ end
167
+ end
168
+
169
+ context 'fact variable using $trusted hash' do
170
+ let(:code) { "$trusted['certname']" }
171
+
172
+ it 'does not detect any problems' do
173
+ expect(problems).to have(0).problem
174
+ end
175
+ end
176
+
177
+ context 'whitelisted top scope variable $::location' do
178
+ let(:code) { '$::location' }
179
+
180
+ it 'does not detect any problems' do
181
+ expect(problems).to have(0).problem
182
+ end
183
+ end
184
+
185
+ context 'non-whitelisted top scope variable $::application' do
186
+ let(:code) { '$::application' }
187
+
188
+ it 'does not detect any problems' do
189
+ expect(problems).to have(1).problem
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet-lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Sharpe
@@ -10,10 +10,10 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-10-20 00:00:00.000000000 Z
13
+ date: 2023-02-28 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: " Checks your Puppet manifests against the Puppetlabs style guide
16
- and alerts you to any discrepancies.'\n"
16
+ and alerts you to any discrepancies.\n"
17
17
  email:
18
18
  - tim@sharpe.id.au
19
19
  - modules-team@puppet.com
@@ -22,7 +22,6 @@ executables:
22
22
  extensions: []
23
23
  extra_rdoc_files: []
24
24
  files:
25
- - ".rubocop.yml"
26
25
  - LICENSE
27
26
  - README.md
28
27
  - bin/puppet-lint
@@ -76,12 +75,15 @@ files:
76
75
  - lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb
77
76
  - lib/puppet-lint/plugins/check_whitespace/line_length.rb
78
77
  - lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb
78
+ - lib/puppet-lint/plugins/legacy_facts/legacy_facts.rb
79
+ - lib/puppet-lint/plugins/top_scope_facts/top_scope_facts.rb
79
80
  - lib/puppet-lint/report/github.rb
80
81
  - lib/puppet-lint/report/sarif_template.json
81
82
  - lib/puppet-lint/tasks/gemfile_rewrite.rb
82
83
  - lib/puppet-lint/tasks/puppet-lint.rb
83
84
  - lib/puppet-lint/tasks/release_test.rb
84
85
  - lib/puppet-lint/version.rb
86
+ - rubocop_baseline.yml
85
87
  - spec/acceptance/puppet_lint_spec.rb
86
88
  - spec/fixtures/test/manifests/fail.pp
87
89
  - spec/fixtures/test/manifests/ignore.pp
@@ -143,6 +145,8 @@ files:
143
145
  - spec/unit/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb
144
146
  - spec/unit/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb
145
147
  - spec/unit/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb
148
+ - spec/unit/puppet-lint/plugins/legacy_facts/legacy_facts_spec.rb
149
+ - spec/unit/puppet-lint/plugins/top_scope_facts/top_scope_facts_spec.rb
146
150
  - spec/unit/puppet-lint/puppet-lint_spec.rb
147
151
  homepage: https://github.com/puppetlabs/puppet-lint/
148
152
  licenses:
@@ -156,7 +160,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
156
160
  requirements:
157
161
  - - ">="
158
162
  - !ruby/object:Gem::Version
159
- version: '2.7'
163
+ version: '2.5'
160
164
  required_rubygems_version: !ruby/object:Gem::Requirement
161
165
  requirements:
162
166
  - - ">="