puppet-lint 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="