class Inspec::Plugin::V2::ConfigFile

Represents the plugin config file on disk.

Attributes

path[R]

Public Class Methods

default_path() click to toggle source

Returns the defaut path for a config file. This respects ENV.

# File lib/inspec/plugin/v2/config_file.rb, line 19
def self.default_path
  File.join(Inspec.config_dir, "plugins.json")
end
new(path = nil) click to toggle source
# File lib/inspec/plugin/v2/config_file.rb, line 10
def initialize(path = nil)
  @path = path || self.class.default_path
  @data = blank_structure

  read_and_validate_file if File.exist?(@path)
end

Public Instance Methods

add_entry(proposed_entry) click to toggle source

Add an entry with full validation.

# File lib/inspec/plugin/v2/config_file.rb, line 41
def add_entry(proposed_entry)
  unless proposed_entry.keys.all? { |field| field.is_a? Symbol }
    raise Inspec::Plugin::V2::ConfigError, "All keys to ConfigFile#add_entry must be symbols"
  end

  validate_entry(proposed_entry)

  if existing_entry?(proposed_entry[:name])
    raise Inspec::Plugin::V2::ConfigError, "Duplicate plugin name in call to ConfigFile#add_entry: '#{proposed_entry[:name]}'"
  end

  @data[:plugins] << proposed_entry
end
each(&block) click to toggle source

Implement Enumerable. All Enumerable methds act on the plugins list, and yield Hashes that represent an entry.

# File lib/inspec/plugin/v2/config_file.rb, line 26
def each(&block)
  @data[:plugins].each(&block)
end
existing_entry?(name) click to toggle source

Check for a plugin

# File lib/inspec/plugin/v2/config_file.rb, line 36
def existing_entry?(name)
  !plugin_by_name(name).nil?
end
plugin_by_name(name) click to toggle source

Look for a plugin by name.

# File lib/inspec/plugin/v2/config_file.rb, line 31
def plugin_by_name(name)
  detect { |entry| entry[:name] == name.to_sym }
end
remove_entry(name) click to toggle source

Removes an entry specified by plugin name.

# File lib/inspec/plugin/v2/config_file.rb, line 56
def remove_entry(name)
  unless existing_entry?(name)
    raise Inspec::Plugin::V2::ConfigError, "No such entry with plugin name '#{name}'"
  end

  @data[:plugins].delete_if { |entry| entry[:name] == name.to_sym }
end
save() click to toggle source

Save the file to disk as a JSON structure at the path.

# File lib/inspec/plugin/v2/config_file.rb, line 65
def save
  dir = File.dirname(path)
  FileUtils.mkdir_p(dir)
  File.write(path, JSON.pretty_generate(@data))
end

Private Instance Methods

blank_structure() click to toggle source
# File lib/inspec/plugin/v2/config_file.rb, line 73
def blank_structure
  {
    plugins_config_version: "1.0.0",
    plugins: [],
  }
end
read_and_validate_file() click to toggle source
# File lib/inspec/plugin/v2/config_file.rb, line 80
def read_and_validate_file
  @data = JSON.parse(File.read(path), symbolize_names: true)
  validate_file
rescue JSON::ParserError => e
  raise Inspec::Plugin::V2::ConfigError, "Failed to load plugins JSON configuration from #{path}:\n#{e}"
end
validate_entry(plugin_entry) click to toggle source
# File lib/inspec/plugin/v2/config_file.rb, line 125
def validate_entry(plugin_entry)
  unless plugin_entry.is_a? Hash
    raise Inspec::Plugin::V2::ConfigError, "each 'plugins' entry should be a Hash / JSON object"
  end

  unless plugin_entry.key? :name
    raise Inspec::Plugin::V2::ConfigError, "'plugins' entry missing 'name' field"
  end

  # Symbolize the name.
  plugin_entry[:name] = plugin_entry[:name].to_sym

  if plugin_entry.key? :installation_type
    seen_type = plugin_entry[:installation_type]
    unless %i{gem path}.include? seen_type.to_sym
      raise Inspec::Plugin::V2::ConfigError, "'plugins' entry with unrecognized installation_type (must be one of 'gem' or 'path')"
    end

    plugin_entry[:installation_type] = seen_type.to_sym

    if plugin_entry[:installation_type] == :path && !plugin_entry.key?(:installation_path)
      raise Inspec::Plugin::V2::ConfigError, "'plugins' entry with a 'path' installation_type missing installation path"
    end
  end
end
validate_file() click to toggle source
# File lib/inspec/plugin/v2/config_file.rb, line 87
def validate_file # rubocop: disable Metrics/AbcSize
  unless @data[:plugins_config_version]
    raise Inspec::Plugin::V2::ConfigError, "Missing 'plugins_config_version' entry at #{path} - currently support versions: 1.0.0"
  end

  unless @data[:plugins_config_version] == "1.0.0"
    raise Inspec::Plugin::V2::ConfigError, "Unsupported plugins.json file version #{@data[:plugins_config_version]} at #{path} - currently support versions: 1.0.0"
  end

  plugin_entries = @data[:plugins]
  if plugin_entries.nil?
    raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file at #{path} - missing top-level key named 'plugins', whose value should be an array"
  end

  unless plugin_entries.is_a?(Array)
    raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file at #{path} - top-level key named 'plugins' should be an array"
  end

  plugin_entries.each_with_index do |plugin_entry, idx|
    begin
      validate_entry(plugin_entry)
    rescue Inspec::Plugin::V2::ConfigError => ex
      # append some context to the message
      raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - " + ex.message + " at index #{idx}"
    end

    # Check for duplicates
    plugin_entries.each_with_index do |other_entry, other_idx|
      next if idx == other_idx
      next unless other_entry.is_a? Hash # We'll catch that invalid entry later
      next if plugin_entry[:name] != other_entry[:name]

      indices = [idx, other_idx].sort
      raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - duplicate plugin entry '#{plugin_entry[:name]}' detected at index #{indices[0]} and #{indices[1]}"
    end
  end
end