lib/resources/json.rb in inspec-2.0.32 vs lib/resources/json.rb in inspec-2.0.45

- old
+ new

@@ -1,117 +1,117 @@ -# encoding: utf-8 - -require 'utils/object_traversal' - -module Inspec::Resources - class JsonConfig < Inspec.resource(1) - name 'json' - supports platform: 'unix' - supports platform: 'windows' - supports platform: 'esx' - supports platform: 'cisco' - desc 'Use the json InSpec audit resource to test data in a JSON file.' - example " - describe json('policyfile.lock.json') do - its(['cookbook_locks','omnibus','version']) { should eq('2.2.0') } - end - - describe json({ command: 'retrieve_data.py --json' }) do - its('state') { should eq('open') } - end - - describe json({ content: '{\"item1\": { \"status\": \"available\" } }' }) do - its(['item1', 'status']) { should cmp 'available' } - end - - " - - include ObjectTraverser - - # make params readable - attr_reader :params, :raw_content - - def initialize(opts) - # pre-initialize @params to an empty hash. In the event that reading/parsing the data - # throws an exception, this allows the resource to still be called outside of a - # describe/test and not throw errors when a caller attempts to fetch a value from the params. - @params = {} - - # load the raw content from the source, and then parse it - @raw_content = load_raw_content(opts) - @params = parse(@raw_content) - end - - # Shorthand to retrieve a parameter name via `#its`. - # Example: describe json('file') { its('paramX') { should eq 'Y' } } - # - # @param [String] name name of the field to retrieve - # @return [Object] the value stored at this position - def method_missing(*keys) - # catch bahavior of rspec its implementation - # @see https://github.com/rspec/rspec-its/blob/master/lib/rspec/its.rb#L110 - keys.shift if keys.is_a?(Array) && keys[0] == :[] - value(keys) - end - - def value(key) - # uses ObjectTraverser.extract_value to walk the hash looking for the key, - # which may be an Array of keys for a nested Hash. - extract_value(key, params) - end - - def to_s - "#{resource_base_name} #{@resource_name_supplement || 'content'}" - end - - private - - def parse(content) - require 'json' - JSON.parse(content) - rescue => e - raise Inspec::Exceptions::ResourceFailed, "Unable to parse JSON: #{e.message}" - end - - def load_raw_content(opts) - # if the opts isn't a hash, we assume it's a path to a file - unless opts.is_a?(Hash) - @resource_name_supplement = opts - return load_raw_from_file(opts) - end - - if opts.key?(:command) - @resource_name_supplement = "from command: #{opts[:command]}" - load_raw_from_command(opts[:command]) - elsif opts.key?(:content) - opts[:content] - else - raise Inspec::Exceptions::ResourceFailed, 'No JSON content; must specify a file, command, or raw JSON content' - end - end - - def load_raw_from_file(path) - file = inspec.file(path) - - # these are currently ResourceSkipped to maintain consistency with the resource - # pre-refactor (which used skip_resource). These should likely be changed to - # ResourceFailed during a major version bump. - raise Inspec::Exceptions::ResourceSkipped, "No such file: #{path}" unless file.file? - raise Inspec::Exceptions::ResourceSkipped, "File #{path} is empty or is not readable by current user" if file.content.nil? || file.content.empty? - - file.content - end - - def load_raw_from_command(command) - command_output = inspec.command(command).stdout - raise Inspec::Exceptions::ResourceSkipped, "No output from command: #{command}" if command_output.nil? || command_output.empty? - - command_output - end - - # for resources the subclass JsonConfig, this allows specification of the resource - # base name in each subclass so we can build a good to_s method - def resource_base_name - 'JSON' - end - end -end +# encoding: utf-8 + +require 'utils/object_traversal' + +module Inspec::Resources + class JsonConfig < Inspec.resource(1) + name 'json' + supports platform: 'unix' + supports platform: 'windows' + supports platform: 'esx' + supports platform: 'cisco' + desc 'Use the json InSpec audit resource to test data in a JSON file.' + example " + describe json('policyfile.lock.json') do + its(['cookbook_locks','omnibus','version']) { should eq('2.2.0') } + end + + describe json({ command: 'retrieve_data.py --json' }) do + its('state') { should eq('open') } + end + + describe json({ content: '{\"item1\": { \"status\": \"available\" } }' }) do + its(['item1', 'status']) { should cmp 'available' } + end + + " + + include ObjectTraverser + + # make params readable + attr_reader :params, :raw_content + + def initialize(opts) + # pre-initialize @params to an empty hash. In the event that reading/parsing the data + # throws an exception, this allows the resource to still be called outside of a + # describe/test and not throw errors when a caller attempts to fetch a value from the params. + @params = {} + + # load the raw content from the source, and then parse it + @raw_content = load_raw_content(opts) + @params = parse(@raw_content) + end + + # Shorthand to retrieve a parameter name via `#its`. + # Example: describe json('file') { its('paramX') { should eq 'Y' } } + # + # @param [String] name name of the field to retrieve + # @return [Object] the value stored at this position + def method_missing(*keys) + # catch bahavior of rspec its implementation + # @see https://github.com/rspec/rspec-its/blob/master/lib/rspec/its.rb#L110 + keys.shift if keys.is_a?(Array) && keys[0] == :[] + value(keys) + end + + def value(key) + # uses ObjectTraverser.extract_value to walk the hash looking for the key, + # which may be an Array of keys for a nested Hash. + extract_value(key, params) + end + + def to_s + "#{resource_base_name} #{@resource_name_supplement || 'content'}" + end + + private + + def parse(content) + require 'json' + JSON.parse(content) + rescue => e + raise Inspec::Exceptions::ResourceFailed, "Unable to parse JSON: #{e.message}" + end + + def load_raw_content(opts) + # if the opts isn't a hash, we assume it's a path to a file + unless opts.is_a?(Hash) + @resource_name_supplement = opts + return load_raw_from_file(opts) + end + + if opts.key?(:command) + @resource_name_supplement = "from command: #{opts[:command]}" + load_raw_from_command(opts[:command]) + elsif opts.key?(:content) + opts[:content] + else + raise Inspec::Exceptions::ResourceFailed, 'No JSON content; must specify a file, command, or raw JSON content' + end + end + + def load_raw_from_file(path) + file = inspec.file(path) + + # these are currently ResourceSkipped to maintain consistency with the resource + # pre-refactor (which used skip_resource). These should likely be changed to + # ResourceFailed during a major version bump. + raise Inspec::Exceptions::ResourceSkipped, "No such file: #{path}" unless file.file? + raise Inspec::Exceptions::ResourceSkipped, "File #{path} is empty or is not readable by current user" if file.content.nil? || file.content.empty? + + file.content + end + + def load_raw_from_command(command) + command_output = inspec.command(command).stdout + raise Inspec::Exceptions::ResourceSkipped, "No output from command: #{command}" if command_output.nil? || command_output.empty? + + command_output + end + + # for resources the subclass JsonConfig, this allows specification of the resource + # base name in each subclass so we can build a good to_s method + def resource_base_name + 'JSON' + end + end +end