Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(PDK-421) Validate EPP syntax #680

Merged
merged 5 commits into from
Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions lib/pdk/validate/puppet/puppet_epp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
require 'pdk'
require 'pdk/cli/exec'
require 'pdk/validate/base_validator'
require 'fileutils'
require 'tmpdir'

module PDK
module Validate
class PuppetEPP < BaseValidator
# In Puppet >= 5.3.4, the error context formatting was changed to facilitate localization
ERROR_CONTEXT = %r{(?:file:\s(?<file>.+?)|line:\s(?<line>.+?)|column:\s(?<column>.+?))}
# In Puppet < 5.3.3, the error context was formatted in these variations:
# - "at file_path:line_num:col_num"
# - "at file_path:line_num"
# - "at line line_num"
# - "in file_path"
ERROR_CONTEXT_LEGACY = %r{(?:at\sline\s(?<line>\d+)|at\s(?<file>.+?):(?<line>\d+):(?<column>\d+)|at\s(?<file>.+?):(?<line>\d+)|in\s(?<file>.+?))}

PUPPET_LOGGER_PREFIX = %r{^(debug|info|notice|warning|error|alert|critical):\s.+?$}i
PUPPET_SYNTAX_PATTERN = %r{^
(?<severity>.+?):\s
(?<message>.+?)
(?:
\s\(#{ERROR_CONTEXT}(,\s#{ERROR_CONTEXT})*\)| # attempt to match the new localisation friendly location
\s#{ERROR_CONTEXT_LEGACY}| # attempt to match the old " at file:line:column" location
$ # handle cases where the output has no location
)
$}x

def self.name
'puppet-epp'
end

def self.cmd
'puppet'
end

def self.pattern
'**/**.epp'
end

def self.pattern_ignore
''
end

def self.spinner_text(_targets = nil)
_('Checking Puppet EPP syntax (%{pattern}).') % { pattern: pattern }
end

def self.parse_options(_options, targets)
# Due to PDK-1266 we need to run `puppet parser validate` with an empty
# modulepath. On *nix, Ruby treats `/dev/null` as an empty directory
# however it doesn't do so with `NUL` on Windows. The workaround for
# this to ensure consistent behaviour is to create an empty temporary
# directory and use that as the modulepath.
['epp', 'validate', '--config', null_file, '--modulepath', validate_tmpdir].concat(targets)
end

def self.invoke(report, options)
super
ensure
remove_validate_tmpdir
end

def self.validate_tmpdir
@validate_tmpdir ||= Dir.mktmpdir('puppet-epp-validate')
end

def self.remove_validate_tmpdir
return unless @validate_tmpdir
return unless File.directory?(@validate_tmpdir)

FileUtils.remove_entry_secure(@validate_tmpdir)
@validate_tmpdir = nil
end

def self.null_file
Gem.win_platform? ? 'NUL' : '/dev/null'
end

def self.parse_output(report, result, targets)
# Due to PUP-7504, we will have to programmatically construct the json
# object from the text output for now.
output = result[:stderr].split("\n").reject { |entry| entry.empty? }

results_data = []
output.each do |offense|
offense_data = parse_offense(offense)
results_data << offense_data
end

# puppet parser validate does not include files without problems in its
# output, so we need to go through the list of targets and add passing
# events to the report for any target not listed in the output.
targets.reject { |target| results_data.any? { |j| j[:file] =~ %r{#{target}} } }.each do |target|
report.add_event(
file: target,
source: name,
severity: :ok,
state: :passed,
)
end

results_data.each do |offense|
report.add_event(offense)
end
end

def self.parse_offense(offense)
sanitize_console_output(offense)

offense_data = {
source: name,
state: :failure,
}

if offense.match(PUPPET_LOGGER_PREFIX)
attributes = offense.match(PUPPET_SYNTAX_PATTERN)

unless attributes.nil?
attributes.names.each do |name|
offense_data[name.to_sym] = attributes[name] unless attributes[name].nil?
end
end
else
offense_data[:message] = offense
end

offense_data
end

def self.sanitize_console_output(line)
line.gsub!(%r{\e\[([;\d]+)?m}, '')
end
end
end
end
3 changes: 2 additions & 1 deletion lib/pdk/validate/puppet_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'pdk/validate/base_validator'
require 'pdk/validate/puppet/puppet_lint'
require 'pdk/validate/puppet/puppet_syntax'
require 'pdk/validate/puppet/puppet_epp'

module PDK
module Validate
Expand All @@ -12,7 +13,7 @@ def self.name
end

def self.puppet_validators
[PuppetSyntax, PuppetLint]
[PuppetSyntax, PuppetLint, PuppetEPP]
end

def self.invoke(report, options = {})
Expand Down
8 changes: 4 additions & 4 deletions spec/acceptance/validate_puppet_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class foo {
its(:exit_status) { is_expected.to eq(0) }
its(:stderr) { is_expected.to match(syntax_spinner_text) }
its(:stderr) { is_expected.to match(lint_spinner_text) }
its(:stdout) { is_expected.to have_no_output }
its(:stdout) { is_expected.to match(%r{Target does not contain any files to validate}) }
glennsarti marked this conversation as resolved.
Show resolved Hide resolved

describe file('report.xml') do
its(:content) { is_expected.to contain_valid_junit_xml }
Expand Down Expand Up @@ -167,7 +167,7 @@ class foo {

describe command('pdk validate puppet') do
its(:exit_status) { is_expected.to eq(0) }
its(:stdout) { is_expected.to have_no_output }
its(:stdout) { is_expected.to match(%r{Target does not contain any files to validate}) }
end
end

Expand Down Expand Up @@ -333,7 +333,7 @@ class foo {

describe command("pdk validate puppet --format text:stdout --format junit:report.xml #{clean_pp}") do
its(:exit_status) { is_expected.to eq(0) }
its(:stdout) { is_expected.to have_no_output }
its(:stdout) { is_expected.to match(%r{Target does not contain any files to validate}) }
its(:stderr) { is_expected.to match(syntax_spinner_text) }
its(:stderr) { is_expected.to match(lint_spinner_text) }

Expand Down Expand Up @@ -439,7 +439,7 @@ class foo {

describe command('pdk validate puppet') do
its(:exit_status) { is_expected.to eq(0) }
its(:stdout) { is_expected.to have_no_output }
its(:stdout) { is_expected.to match(%r{Target does not contain any files to validate}) }
end
end
end
Expand Down