class OctofactsUpdater::CLI

Attributes

config[R]
opts[R]

Public Class Methods

new(argv) click to toggle source

Constructor.

argv - The Array with command line arguments.

# File lib/octofacts_updater/cli.rb, line 9
def initialize(argv)
  @opts = {}
  OptionParser.new(argv) do |opts|
    opts.banner = "Usage: octofacts-updater [options]"

    opts.on("-a", "--action <action>", String, "Action to take") do |a|
      @opts[:action] = a
    end

    opts.on("-c", "--config <config_file>", String, "Path to configuration file") do |f|
      @opts[:config] = File.expand_path(f)
      raise "Invalid configuration file #{@opts[:config].inspect}" unless File.file?(@opts[:config])
    end

    opts.on("-H", "--hostname <hostname>", String, "FQDN of the host whose facts are to be gathered") do |h|
      @opts[:hostname] = h
    end

    opts.on("-o", "--output-file <filename>", String, "Path to output file to write") do |i|
      @opts[:output_file] = i
    end

    opts.on("-l", "--list <host1,host2,...>", Array, "List of hosts to update or index") do |l|
      @opts[:host_list] = l
    end

    opts.on("--[no-]quick", "Quick indexing: Use existing YAML fact fixtures when available") do |q|
      @opts[:quick] = q
    end

    opts.on("-p", "--path <directory>", "Path where to read/write host fixtures when working in bulk") do |path|
      @opts[:path] = path
    end

    opts.on("--github", "Push any changes to a branch on GitHub (requires --action=bulk)") do
      @opts[:github] ||= {}
      @opts[:github][:enabled] = true
    end

    opts.on("--datasource <datasource>", "Specify the data source to use when retrieving facts (localfile, puppetdb, ssh)") do |ds|
      unless %w{localfile puppetdb ssh}.include?(ds)
        raise ArgumentError, "Invalid datasource #{ds.inspect}. Acceptable values: localfile, puppetdb, ssh."
      end
      @opts[:datasource] = ds.to_sym
    end

    opts.on("--config-override <section:key=value>", Array, "Override a portion of the configuration") do |co_array|
      co_array.each do |co|
        if co =~ /\A(\w+):(\S+?)=(.+?)\z/
          @opts[Regexp.last_match(1).to_sym] ||= {}
          @opts[Regexp.last_match(1).to_sym][Regexp.last_match(2).to_sym] = Regexp.last_match(3)
        else
          raise ArgumentError, "Malformed argument: --config-override must be in the format section:key=value"
        end
      end
    end
  end.parse!
  validate_cli
end

Public Instance Methods

handle_action_bulk() click to toggle source
# File lib/octofacts_updater/cli.rb, line 157
def handle_action_bulk
  facts_to_index = @config.fetch("index", {})["indexed_facts"]
  unless facts_to_index.is_a?(Array)
    raise ArgumentError, "Must declare index:indexed_facts in configuration to use bulk update"
  end

  nodes = nodes_for_bulk
  if nodes.empty?
    raise ArgumentError, "Cannot run bulk update with no nodes to check"
  end

  path = opts[:path] || @config.fetch("index", {})["node_path"]
  paths = []

  fixtures = nodes.map do |hostname|
    if opts[:quick] && path && File.file?(File.join(path, "#{hostname}.yaml"))
      OctofactsUpdater::Fixture.load_file(hostname, File.join(path, "#{hostname}.yaml"))
    else
      fixture = OctofactsUpdater::Fixture.make(hostname, @config)
      if path && File.directory?(path)
        fixture.write_file(File.join(path, "#{hostname}.yaml"))
        paths << File.join(path, "#{hostname}.yaml")
      end
      fixture
    end
  end

  index = OctofactsUpdater::FactIndex.load_file(index_file)
  index.reindex(facts_to_index, fixtures)
  index.write_file
  paths << index_file

  if opts[:github] && opts[:github][:enabled]
    OctofactsUpdater::Service::GitHub.run(config["github"]["base_directory"], paths, @config)
  end
end
handle_action_facts() click to toggle source
# File lib/octofacts_updater/cli.rb, line 194
def handle_action_facts
  unless opts[:hostname]
    raise ArgumentError, "--hostname <hostname> must be specified to use --action facts"
  end

  facts_for_one_node
end
nodes_for_bulk() click to toggle source
# File lib/octofacts_updater/cli.rb, line 131
def nodes_for_bulk
  if opts[:action] == "reindex"
    @opts[:quick] = true

    path = if opts[:path]
      File.expand_path(opts[:path])
    elsif @config.fetch("index", {})["node_path"]
      File.expand_path(@config.fetch("index", {})["node_path"], File.dirname(opts[:config]))
    else
      raise ArgumentError, "Must set --path, or define index:node_path to a valid directory in configuration"
    end

    unless File.directory?(path)
      raise Errno::ENOENT, "--path must be a directory (#{path.inspect} is not)"
    end

    Dir.glob("#{path}/*.yaml").map { |f| File.basename(f, ".yaml") }
  elsif opts[:host_list]
    opts[:host_list]
  elsif opts[:hostname]
    [opts[:hostname]]
  else
    OctofactsUpdater::FactIndex.load_file(index_file).nodes(true)
  end
end
run() click to toggle source

Run method. Call this to run the octofacts updater with the object that was previously construcuted.

# File lib/octofacts_updater/cli.rb, line 81
def run
  unless opts[:action]
    usage
    exit 255
  end

  @config = {}

  if opts[:config]
    @config = YAML.load_file(opts[:config])
    substitute_relative_paths!(@config, File.dirname(opts[:config]))
    load_plugins(@config["plugins"]) if @config.key?("plugins")
  end

  @config[:options] = {}
  opts.each do |k, v|
    if v.is_a?(Hash)
      @config[k.to_s] ||= {}
      v.each do |v_key, v_val|
        @config[k.to_s][v_key.to_s] = v_val
        @config[k.to_s].delete(v_key.to_s) if v_val.nil?
      end
    else
      @config[:options][k] = v
    end
  end

  return handle_action_bulk if opts[:action] == "bulk"
  return handle_action_facts if opts[:action] == "facts"
  return handle_action_bulk if opts[:action] == "reindex"

  usage
  exit 255
end
substitute_relative_paths!(object_in, basedir) click to toggle source
# File lib/octofacts_updater/cli.rb, line 116
def substitute_relative_paths!(object_in, basedir)
  if object_in.is_a?(Hash)
    object_in.each { |k, v| object_in[k] = substitute_relative_paths!(v, basedir) }
  elsif object_in.is_a?(Array)
    object_in.map! { |v| substitute_relative_paths!(v, basedir) }
  elsif object_in.is_a?(String)
    if object_in =~ %r{^\.\.?(/|\z)}
      object_in = File.expand_path(object_in, basedir)
    end
    object_in
  else
    object_in
  end
end
usage() click to toggle source
# File lib/octofacts_updater/cli.rb, line 69
def usage
  puts "Usage: octofacts-updater --action <action> [--config /path/to/config.yaml] [other options]"
  puts ""
  puts "Available actions:"
  puts "  bulk:    Update fixtures and index in bulk"
  puts "  facts:   Obtain facts for one node (requires --hostname <hostname>)"
  puts "  reindex: Build a new index from the existing fact fixtures"
  puts ""
end

Private Instance Methods

facts_for_one_node() click to toggle source

Determine the facts for one node and print to the console or write to the specified file.

# File lib/octofacts_updater/cli.rb, line 207
def facts_for_one_node
  fixture = OctofactsUpdater::Fixture.make(opts[:hostname], @config)
  print_or_write(fixture.to_yaml)
end
index_file() click to toggle source

Get the index file from the options or configuration file. Raise error if it does not exist or was not specified.

# File lib/octofacts_updater/cli.rb, line 214
def index_file
  @index_file ||= begin
    if config.fetch("index", {})["file"]
      return config["index"]["file"] if File.file?(config["index"]["file"])
      raise Errno::ENOENT, "Index file (#{config['index']['file'].inspect}) does not exist"
    end
    raise ArgumentError, "No index file specified on command line (--index-file) or in configuration file"
  end
end
load_plugins(plugins) click to toggle source

Load plugins as per configuration file. Note: all plugins embedded in this gem are automatically loaded. This is just for user-specified plugins.

plugins - An Array of file names to load

# File lib/octofacts_updater/cli.rb, line 228
def load_plugins(plugins)
  unless plugins.is_a?(Array)
    raise ArgumentError, "load_plugins expects an array, got #{plugins.inspect}"
  end

  plugins.each do |plugin|
    plugin_file = plugin.start_with?("/") ? plugin : File.expand_path("../../#{plugin}", File.dirname(__FILE__))
    unless File.file?(plugin_file)
      raise Errno::ENOENT, "Failed to find plugin #{plugin.inspect} at #{plugin_file}"
    end
    require plugin_file
  end
end
print_or_write(data) click to toggle source

Print or write to file depending on whether or not the output file was set.

data - Data to print or write.

validate_cli() click to toggle source

Validate command line options. Kick out invalid combinations of options immediately.

# File lib/octofacts_updater/cli.rb, line 254
def validate_cli
  if opts[:path] && !File.directory?(opts[:path])
    raise Errno::ENOENT, "An existing directory must be specified with -p/--path"
  end
end