Class: OctofactsUpdater::CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/octofacts_updater/cli.rb

Instance Method Summary collapse

Constructor Details

#initialize(argv) ⇒ CLI

Constructor.

argv - The Array with command line arguments.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/octofacts_updater/cli.rb', line 10

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

Instance Method Details

#handle_action_bulkObject



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/octofacts_updater/cli.rb', line 158

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_factsObject



195
196
197
198
199
200
201
# File 'lib/octofacts_updater/cli.rb', line 195

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_bulkObject



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/octofacts_updater/cli.rb', line 132

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

#runObject

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



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/octofacts_updater/cli.rb', line 82

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) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/octofacts_updater/cli.rb', line 117

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

#usageObject



70
71
72
73
74
75
76
77
78
# File 'lib/octofacts_updater/cli.rb', line 70

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