inspec/lib/resources/nginx_conf.rb
Dominik Richter 56549aed82 add nginx_conf resource (#1889)
The resource itself only offers contents and params right now. It resolved
all include calls it can find and creates the aggregated config object.

This is limited in functionality. One last (set of) PR(s) is needed to
add an interface that makes querying this config file easier. It is due
to the file's inherent complexity that I want to explore which methods
are needed to be effective. In the meantime, this resource offers accessors
to the underlying data that are stable.

Signed-off-by: Dominik Richter <dominik.richter@gmail.com>
2017-06-26 06:37:41 -07:00

95 lines
3.2 KiB
Ruby

# encoding: utf-8
# author: Dominik Richter
# author: Christoph Hartmann
require 'utils/nginx_parser'
# STABILITY: Experimental
# This resouce needs a proper interace to the underlying data, which is currently missing.
# Until it is added, we will keep it experimental.
#
# TODO: Support it on Windows. To do so, we need to recognize the base os and how
# it combines the file path. Calling `File.join` or similar methods may lead to errors
# when running remotely.
module Inspec::Resources
class NginxConf < Inspec.resource(1)
name 'nginx_conf'
desc 'Use the nginx_conf InSpec resource to test configuration data '\
'for the NginX web server located in /etc/nginx/nginx.conf on '\
'Linux and UNIX platforms.'
example "
describe nginx_conf.params ...
describe nginx_conf('/path/to/my/nginx.conf').params ...
"
attr_reader :contents
def initialize(conf_path = nil)
@conf_path = conf_path || '/etc/nginx/nginx.conf'
@contents = {}
return skip_resource 'The `nginx_conf` resource is currently not supported on Windows.' if inspec.os.windows?
end
def params
@params ||= parse_nginx(@conf_path)
rescue StandardError => e
skip_resource e.message
@params = {}
end
def to_s
"nginx_conf #{@conf_path}"
end
private
def read_content(path)
return @contents[path] if @contents.key?(path)
file = inspec.file(path)
if !file.file?
return skip_resource "Can't find file \"#{path}\""
end
@contents[path] = file.content
end
def parse_nginx(path)
return nil if inspec.os.windows?
content = read_content(path)
data = NginxConfig.parse(content)
resolve_references(data, File.dirname(path))
rescue StandardError => _
raise "Cannot parse NginX config in #{path}."
end
# Cycle through the complete parsed data structure and try to find any
# calls to `include`. In NginX, this is used to embed data from other
# files into the current data structure.
#
# The method steps through the object structure that is passed in to
# find any calls to 'include' and returns the object structure with the
# included data merged in.
#
# @param data [Hash] data structure from NginxConfig.parse
# @param rel_path [String] the relative path from which this config is read
# @return [Hash] data structure with references included
def resolve_references(data, rel_path)
# Walk through all array entries to find more references
return data.map { |x| resolve_references(x, rel_path) } if data.is_a?(Array)
# Return any data that we cannot step into to find more `include` calls
return data unless data.is_a?(Hash)
# Any call to `include` gets its data read, parsed, and merged back
# into the current data structure
if data.key?('include')
data.delete('include').flatten
.map { |x| File.expand_path(x, rel_path) }
.map { |path| parse_nginx(path) }
.map { |e| data.merge!(e) }
end
# Walk through the remaining hash fields to find more references
Hash[data.map { |k, v| [k, resolve_references(v, rel_path)] }]
end
end
end