This file is indexed.

/usr/bin/hiera is in hiera 3.2.0-2.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 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
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
157
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#!/usr/bin/ruby

# CLI client for Hiera.
#
# To lookup the 'release' key for a node given Puppet YAML facts:
#
# $ hiera release 'rel/%{location}' --yaml some.node.yaml
#
# If the node yaml had a location fact the default would match that
# else you can supply scope values on the command line
#
# $ hiera release 'rel/%{location}' location=dc2 --yaml some.node.yaml

# Bundler and rubygems maintain a set of directories from which to
# load gems. If Bundler is loaded, let it determine what can be
# loaded. If it's not loaded, then use rubygems. But do this before
# loading any hiera code, so that our gem loading system is sane.
if not defined? ::Bundler
  begin
    require 'rubygems'
  rescue LoadError
  end
end
require 'hiera'
require 'hiera/util'
require 'optparse'
require 'pp'

options = {
  :default => nil,
  :config  => nil,
  :scope   => {},
  :key     => nil,
  :verbose => false,
  :resolution_type => :priority,
  :format => :ruby
}

initial_scopes = Array.new

# Loads the scope from YAML or JSON files
def load_scope(source, type=:yaml)
  case type
  when :mcollective
    begin
      require 'mcollective'

      include MCollective::RPC

      util = rpcclient("rpcutil")
      util.progress = false
      nodestats = util.custom_request("inventory", {}, source, {"identity" => source}).first

      raise "Failed to retrieve facts for node #{source}: #{nodestats[:statusmsg]}" unless nodestats[:statuscode] == 0

      scope = nodestats[:data][:facts]
    rescue Exception => e
      STDERR.puts "MCollective lookup failed: #{e.class}: #{e}"
      exit 1
    end

  when :yaml
    raise "Cannot find scope #{type} file #{source}" unless File.exist?(source)

    require 'yaml'

    # Attempt to load puppet in case we're going to be fed
    # Puppet yaml files
    begin
        require 'puppet'
    rescue
    end

    scope = YAML.load_file(source)

    # Puppet makes dumb yaml files that do not promote data reuse.
    scope = scope.values if scope.is_a?(Puppet::Node::Facts)

  when :json
    raise "Cannot find scope #{type} file #{source}" unless File.exist?(source)

    require 'json'

    scope = JSON.load(File.read(source))

  when :inventory_service
    # For this to work the machine running the hiera command needs access to
    # /facts REST endpoint on your inventory server.  This access is
    # controlled in auth.conf and identification is by the certname of the
    # machine running hiera commands.
    #
    # Another caveat is that if your inventory server isn't at the short dns
    # name of 'puppet' you will need to set the inventory_sever option in
    # your puppet.conf.  Set it in either the master or main sections.  It
    # is fine to have the inventory_server option set even if the config
    # doesn't have the fact_terminus set to rest.
    begin
      require 'puppet/util/run_mode'
      $puppet_application_mode = Puppet::Util::RunMode[:master]
      require 'puppet'
      Puppet.settings.parse
      Puppet::Node::Facts.indirection.terminus_class = :rest
      scope = YAML.load(Puppet::Node::Facts.indirection.find(source).to_yaml)
      # Puppet makes dumb yaml files that do not promote data reuse.
      scope = scope.values if scope.is_a?(Puppet::Node::Facts)
    rescue Exception => e
        STDERR.puts "Puppet inventory service lookup failed: #{e.class}: #{e}"
        exit 1
    end
  else
    raise "Don't know how to load data type #{type}"
  end

  raise "Scope from #{type} file #{source} should be a Hash" unless scope.is_a?(Hash)

  scope
end

def output_answer(ans, format)
  case format
  when :json
    require 'json'
    puts JSON.dump(ans)
  when :ruby
    if ans.is_a?(String)
      puts ans
    else
      pp ans
    end
  when :yaml
    require 'yaml'
    puts ans.to_yaml
  else
    STDERR.puts "Unknown output format: #{v}"
    exit 1
  end
end

OptionParser.new do |opts|
  opts.banner = "Usage: hiera [options] key [default value] [variable='text'...]\n\nThe default value will be used if no value is found for the key. Scope variables\nwill be interpolated into %{variable} placeholders in the hierarchy and in\nreturned values.\n\n"

  opts.on("--version", "-V", "Version information") do
    puts Hiera.version
    exit
  end

  opts.on("--debug", "-d", "Show debugging information") do
    options[:verbose] = true
  end

  opts.on("--array", "-a", "Return all values as an array") do
    options[:resolution_type] = :array
  end

  opts.on("--hash", "-h", "Return all values as a hash") do
    options[:resolution_type] = :hash
  end

  opts.on("--config CONFIG", "-c", "Configuration file") do |v|
    if File.exist?(v)
      options[:config] = v
    else
      STDERR.puts "Cannot find config file: #{v}"
      exit 1
    end
  end

  opts.on("--json SCOPE", "-j", "JSON format file to load scope from") do |v|
    initial_scopes << { :type => :json, :value => v, :name => "JSON" }
  end

  opts.on("--yaml SCOPE", "-y", "YAML format file to load scope from") do |v|
    initial_scopes << { :type => :yaml, :value => v, :name => "YAML" }
  end

  opts.on("--mcollective IDENTITY", "-m", "Use facts from a node (via mcollective) as scope") do |v|
    initial_scopes << { :type => :mcollective, :value => v, :name => "Mcollective" }
  end

  opts.on("--inventory_service IDENTITY", "-i", "Use facts from a node (via Puppet's inventory service) as scope") do |v|
    initial_scopes << { :type => :inventory_service, :value => v, :name => "Puppet inventory service" }
  end

  opts.on("--format TYPE", "-f", "Output the result in a specific format (ruby, yaml or json); default is 'ruby'") do |v|
    options[:format] = case v
    when 'json', 'ruby', 'yaml'
      v.to_sym
    else
      STDERR.puts "Unknown output format: #{v}"
      exit 1
    end
  end
end.parse!

unless initial_scopes.empty?
  initial_scopes.each { |this_scope|
    # Load initial scope
    begin
      options[:scope] = load_scope(this_scope[:value], this_scope[:type])
    rescue Exception => e
      STDERR.puts "Could not load #{this_scope[:name]} scope: #{e.class}: #{e}"
      exit 1
    end
  }
end

# arguments can be:
#
# key default var=val another=val
#
# The var=val's assign scope
unless ARGV.empty?
  options[:key] = ARGV.delete_at(0)

  ARGV.each do |arg|
    if arg =~ /^(.+?)=(.+?)$/
      options[:scope][$1] = $2
    else
      unless options[:default]
        options[:default] = arg.dup
      else
        STDERR.puts "Don't know how to parse scope argument: #{arg}"
      end
    end
  end
else
  STDERR.puts "Please supply a data item to look up"
  exit 1
end

begin
  hiera = Hiera.new(:config => options[:config])
rescue Exception => e
  if options[:verbose]
    raise
  else
    STDERR.puts "Failed to start Hiera: #{e.class}: #{e}"
    exit 1
  end
end

unless options[:verbose]
  Hiera.logger = "noop"
end

ans = hiera.lookup(options[:key], options[:default], options[:scope], nil, options[:resolution_type])

output_answer(ans, options[:format])