hiera 0.3.0 → 1.0.0rc4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of hiera might be problematic. Click here for more details.
- data/CHANGELOG +112 -0
- data/COPYING +202 -0
- data/README.md +237 -0
- data/bin/hiera +160 -156
- data/lib/hiera.rb +58 -50
- data/lib/hiera/backend.rb +174 -161
- data/lib/hiera/backend/yaml_backend.rb +67 -45
- data/lib/hiera/config.rb +43 -41
- data/lib/hiera/console_logger.rb +9 -9
- data/lib/hiera/noop_logger.rb +8 -0
- data/lib/hiera/puppet_logger.rb +10 -5
- data/lib/hiera/util.rb +47 -0
- data/spec/spec_helper.rb +42 -6
- data/spec/unit/backend/yaml_backend_spec.rb +153 -104
- data/spec/unit/backend_spec.rb +309 -255
- data/spec/unit/config_spec.rb +85 -79
- data/spec/unit/console_logger_spec.rb +13 -13
- data/spec/unit/hiera_spec.rb +57 -54
- data/spec/unit/util_spec.rb +49 -0
- metadata +21 -10
data/bin/hiera
CHANGED
@@ -11,159 +11,163 @@
|
|
11
11
|
#
|
12
12
|
# $ hiera release 'rel/%{location}' location=dc2 --yaml some.node.yaml
|
13
13
|
|
14
|
-
|
14
|
+
begin
|
15
|
+
require 'rubygems'
|
16
|
+
rescue LoadError
|
17
|
+
end
|
15
18
|
require 'hiera'
|
19
|
+
require 'hiera/util'
|
16
20
|
require 'optparse'
|
17
21
|
|
18
|
-
options = {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
options = {
|
23
|
+
:default => nil,
|
24
|
+
:config => File.join(Hiera::Util.config_dir, 'hiera.yaml'),
|
25
|
+
:scope => {},
|
26
|
+
:key => nil,
|
27
|
+
:verbose => false,
|
28
|
+
:resolution_type => :priority
|
29
|
+
}
|
26
30
|
|
27
31
|
# Loads the scope from YAML or JSON files
|
28
32
|
def load_scope(source, type=:yaml)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
include MCollective::RPC
|
35
|
-
|
36
|
-
util = rpcclient("rpcutil")
|
37
|
-
util.progress = false
|
38
|
-
nodestats = util.custom_request("inventory", {}, source, {"identity" => source}).first
|
39
|
-
|
40
|
-
raise "Failed to retrieve facts for node #{source}: #{nodestats[:statusmsg]}" unless nodestats[:statuscode] == 0
|
41
|
-
|
42
|
-
scope = nodestats[:data][:facts]
|
43
|
-
rescue Exception => e
|
44
|
-
STDERR.puts "MCollective lookup failed: #{e.class}: #{e}"
|
45
|
-
exit 1
|
46
|
-
end
|
47
|
-
|
48
|
-
when :yaml
|
49
|
-
raise "Cannot find scope #{type} file #{source}" unless File.exist?(source)
|
50
|
-
|
51
|
-
require 'yaml'
|
52
|
-
|
53
|
-
# Attempt to load puppet in case we're going to be fed
|
54
|
-
# Puppet yaml files
|
55
|
-
begin
|
56
|
-
require 'puppet'
|
57
|
-
rescue
|
58
|
-
end
|
59
|
-
|
60
|
-
scope = YAML.load_file(source)
|
61
|
-
|
62
|
-
# Puppet makes dumb yaml files that do not promote data reuse.
|
63
|
-
scope = scope.values if scope.is_a?(Puppet::Node::Facts)
|
64
|
-
|
65
|
-
when :json
|
66
|
-
raise "Cannot find scope #{type} file #{source}" unless File.exist?(source)
|
67
|
-
|
68
|
-
require 'json'
|
69
|
-
|
70
|
-
scope = JSON.load(File.read(source))
|
71
|
-
|
72
|
-
when :inventory_service
|
73
|
-
# For this to work the machine running the hiera command needs access to
|
74
|
-
# /facts REST endpoint on your inventory server. This access is
|
75
|
-
# controlled in auth.conf and identification is by the certname of the
|
76
|
-
# machine running hiera commands.
|
77
|
-
#
|
78
|
-
# Another caveat is that if your inventory server isn't at the short dns
|
79
|
-
# name of 'puppet' you will need to set the inventory_sever option in
|
80
|
-
# your puppet.conf. Set it in either the master or main sections. It
|
81
|
-
# is fine to have the inventory_server option set even if the config
|
82
|
-
# doesn't have the fact_terminus set to rest.
|
83
|
-
begin
|
84
|
-
require 'puppet/util/run_mode'
|
85
|
-
$puppet_application_mode = Puppet::Util::RunMode[:master]
|
86
|
-
require 'puppet'
|
87
|
-
Puppet.settings.parse
|
88
|
-
Puppet::Node::Facts.indirection.terminus_class = :rest
|
89
|
-
scope = YAML.load(Puppet::Node::Facts.indirection.find(source).to_yaml)
|
90
|
-
# Puppet makes dumb yaml files that do not promote data reuse.
|
91
|
-
scope = scope.values if scope.is_a?(Puppet::Node::Facts)
|
92
|
-
rescue Exception => e
|
93
|
-
STDERR.puts "Puppet inventory service lookup failed: #{e.class}: #{e}"
|
94
|
-
exit 1
|
95
|
-
end
|
96
|
-
else
|
97
|
-
raise "Don't know how to load data type #{type}"
|
98
|
-
end
|
33
|
+
case type
|
34
|
+
when :mcollective
|
35
|
+
begin
|
36
|
+
require 'mcollective'
|
99
37
|
|
100
|
-
|
38
|
+
include MCollective::RPC
|
101
39
|
|
102
|
-
|
103
|
-
|
40
|
+
util = rpcclient("rpcutil")
|
41
|
+
util.progress = false
|
42
|
+
nodestats = util.custom_request("inventory", {}, source, {"identity" => source}).first
|
104
43
|
|
105
|
-
|
106
|
-
opts.on("--version", "-V", "Version information") do
|
107
|
-
puts Hiera.version
|
108
|
-
exit
|
109
|
-
end
|
44
|
+
raise "Failed to retrieve facts for node #{source}: #{nodestats[:statusmsg]}" unless nodestats[:statuscode] == 0
|
110
45
|
|
111
|
-
|
112
|
-
|
46
|
+
scope = nodestats[:data][:facts]
|
47
|
+
rescue Exception => e
|
48
|
+
STDERR.puts "MCollective lookup failed: #{e.class}: #{e}"
|
49
|
+
exit 1
|
113
50
|
end
|
114
51
|
|
115
|
-
|
116
|
-
|
117
|
-
end
|
52
|
+
when :yaml
|
53
|
+
raise "Cannot find scope #{type} file #{source}" unless File.exist?(source)
|
118
54
|
|
119
|
-
|
120
|
-
options[:resolution_type] = :hash
|
121
|
-
end
|
55
|
+
require 'yaml'
|
122
56
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
exit 1
|
129
|
-
end
|
57
|
+
# Attempt to load puppet in case we're going to be fed
|
58
|
+
# Puppet yaml files
|
59
|
+
begin
|
60
|
+
require 'puppet'
|
61
|
+
rescue
|
130
62
|
end
|
131
63
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
64
|
+
scope = YAML.load_file(source)
|
65
|
+
|
66
|
+
# Puppet makes dumb yaml files that do not promote data reuse.
|
67
|
+
scope = scope.values if scope.is_a?(Puppet::Node::Facts)
|
68
|
+
|
69
|
+
when :json
|
70
|
+
raise "Cannot find scope #{type} file #{source}" unless File.exist?(source)
|
71
|
+
|
72
|
+
require 'json'
|
73
|
+
|
74
|
+
scope = JSON.load(File.read(source))
|
75
|
+
|
76
|
+
when :inventory_service
|
77
|
+
# For this to work the machine running the hiera command needs access to
|
78
|
+
# /facts REST endpoint on your inventory server. This access is
|
79
|
+
# controlled in auth.conf and identification is by the certname of the
|
80
|
+
# machine running hiera commands.
|
81
|
+
#
|
82
|
+
# Another caveat is that if your inventory server isn't at the short dns
|
83
|
+
# name of 'puppet' you will need to set the inventory_sever option in
|
84
|
+
# your puppet.conf. Set it in either the master or main sections. It
|
85
|
+
# is fine to have the inventory_server option set even if the config
|
86
|
+
# doesn't have the fact_terminus set to rest.
|
87
|
+
begin
|
88
|
+
require 'puppet/util/run_mode'
|
89
|
+
$puppet_application_mode = Puppet::Util::RunMode[:master]
|
90
|
+
require 'puppet'
|
91
|
+
Puppet.settings.parse
|
92
|
+
Puppet::Node::Facts.indirection.terminus_class = :rest
|
93
|
+
scope = YAML.load(Puppet::Node::Facts.indirection.find(source).to_yaml)
|
94
|
+
# Puppet makes dumb yaml files that do not promote data reuse.
|
95
|
+
scope = scope.values if scope.is_a?(Puppet::Node::Facts)
|
96
|
+
rescue Exception => e
|
97
|
+
STDERR.puts "Puppet inventory service lookup failed: #{e.class}: #{e}"
|
98
|
+
exit 1
|
139
99
|
end
|
100
|
+
else
|
101
|
+
raise "Don't know how to load data type #{type}"
|
102
|
+
end
|
140
103
|
|
141
|
-
|
142
|
-
begin
|
143
|
-
options[:scope] = load_scope(v)
|
144
|
-
rescue Exception => e
|
145
|
-
STDERR.puts "Could not load YAML scope: #{e.class}: #{e}"
|
146
|
-
exit 1
|
147
|
-
end
|
148
|
-
end
|
104
|
+
raise "Scope from #{type} file #{source} should be a Hash" unless scope.is_a?(Hash)
|
149
105
|
|
150
|
-
|
151
|
-
|
152
|
-
options[:scope] = load_scope(v, :mcollective)
|
153
|
-
rescue Exception => e
|
154
|
-
STDERR.puts "Could not load MCollective scope: #{e.class}: #{e}"
|
155
|
-
exit 1
|
156
|
-
end
|
157
|
-
end
|
106
|
+
scope
|
107
|
+
end
|
158
108
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
109
|
+
OptionParser.new do |opts|
|
110
|
+
opts.on("--version", "-V", "Version information") do
|
111
|
+
puts Hiera.version
|
112
|
+
exit
|
113
|
+
end
|
114
|
+
|
115
|
+
opts.on("--debug", "-d", "Show debugging information") do
|
116
|
+
options[:verbose] = true
|
117
|
+
end
|
118
|
+
|
119
|
+
opts.on("--array", "-a", "Array search") do
|
120
|
+
options[:resolution_type] = :array
|
121
|
+
end
|
122
|
+
|
123
|
+
opts.on("--hash", "-h", "Hash search") do
|
124
|
+
options[:resolution_type] = :hash
|
125
|
+
end
|
126
|
+
|
127
|
+
opts.on("--config CONFIG", "-c", "Configuration file") do |v|
|
128
|
+
if File.exist?(v)
|
129
|
+
options[:config] = v
|
130
|
+
else
|
131
|
+
STDERR.puts "Cannot find config file: #{v}"
|
132
|
+
exit 1
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
opts.on("--json SCOPE", "-j", "JSON format file to load scope from") do |v|
|
137
|
+
begin
|
138
|
+
options[:scope] = load_scope(v, :json)
|
139
|
+
rescue Exception => e
|
140
|
+
STDERR.puts "Could not load JSON scope: #{e.class}: #{e}"
|
141
|
+
exit 1
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
opts.on("--yaml SCOPE", "-y", "YAML format file to load scope from") do |v|
|
146
|
+
begin
|
147
|
+
options[:scope] = load_scope(v)
|
148
|
+
rescue Exception => e
|
149
|
+
STDERR.puts "Could not load YAML scope: #{e.class}: #{e}"
|
150
|
+
exit 1
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
opts.on("--mcollective IDENTITY", "-m", "Retrieve facts from a node via mcollective as scope") do |v|
|
155
|
+
begin
|
156
|
+
options[:scope] = load_scope(v, :mcollective)
|
157
|
+
rescue Exception => e
|
158
|
+
STDERR.puts "Could not load MCollective scope: #{e.class}: #{e}"
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
opts.on("--inventory_service IDENTITY", "-i", "Retrieve facts for a node via Puppet's inventory service as scope") do |v|
|
164
|
+
begin
|
165
|
+
options[:scope] = load_scope(v, :inventory_service)
|
166
|
+
rescue Exception => e
|
167
|
+
STDERR.puts "Could not load Puppet inventory service scope: #{e.class}: #{e}"
|
168
|
+
exit 1
|
166
169
|
end
|
170
|
+
end
|
167
171
|
end.parse!
|
168
172
|
|
169
173
|
# arguments can be:
|
@@ -172,43 +176,43 @@ end.parse!
|
|
172
176
|
#
|
173
177
|
# The var=val's assign scope
|
174
178
|
unless ARGV.empty?
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
end
|
179
|
+
options[:key] = ARGV.delete_at(0)
|
180
|
+
|
181
|
+
ARGV.each do |arg|
|
182
|
+
if arg =~ /^(.+?)=(.+?)$/
|
183
|
+
options[:scope][$1] = $2
|
184
|
+
else
|
185
|
+
unless options[:default]
|
186
|
+
options[:default] = arg.dup
|
187
|
+
else
|
188
|
+
STDERR.puts "Don't know how to parse scope argument: #{arg}"
|
189
|
+
end
|
187
190
|
end
|
191
|
+
end
|
188
192
|
else
|
189
|
-
|
190
|
-
|
193
|
+
STDERR.puts "Please supply a data item to look up"
|
194
|
+
exit 1
|
191
195
|
end
|
192
196
|
|
193
197
|
begin
|
194
|
-
|
198
|
+
hiera = Hiera.new(:config => options[:config])
|
195
199
|
rescue Exception => e
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
200
|
+
if options[:verbose]
|
201
|
+
raise
|
202
|
+
else
|
203
|
+
STDERR.puts "Failed to start Hiera: #{e.class}: #{e}"
|
204
|
+
exit 1
|
205
|
+
end
|
202
206
|
end
|
203
207
|
|
204
208
|
unless options[:verbose]
|
205
|
-
|
209
|
+
Hiera.logger = "noop"
|
206
210
|
end
|
207
211
|
|
208
212
|
ans = hiera.lookup(options[:key], options[:default], options[:scope], nil, options[:resolution_type])
|
209
213
|
|
210
214
|
if ans.is_a?(String)
|
211
|
-
|
215
|
+
puts ans
|
212
216
|
else
|
213
|
-
|
217
|
+
p ans
|
214
218
|
end
|
data/lib/hiera.rb
CHANGED
@@ -1,61 +1,69 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
2
5
|
require 'yaml'
|
3
6
|
|
4
7
|
class Hiera
|
5
|
-
|
6
|
-
|
7
|
-
autoload :Config, "hiera/config"
|
8
|
-
autoload :Backend, "hiera/backend"
|
9
|
-
autoload :Console_logger, "hiera/console_logger"
|
10
|
-
autoload :Puppet_logger, "hiera/puppet_logger"
|
11
|
-
|
12
|
-
class << self
|
13
|
-
def version
|
14
|
-
VERSION
|
15
|
-
end
|
16
|
-
|
17
|
-
# Loggers are pluggable, just provide a class called
|
18
|
-
# Hiera::Foo_logger and respond to :warn and :debug
|
19
|
-
#
|
20
|
-
# See hiera-puppet for an example that uses the Puppet
|
21
|
-
# loging system instead of our own
|
22
|
-
def logger=(logger)
|
23
|
-
loggerclass = "#{logger.capitalize}_logger"
|
24
|
-
|
25
|
-
require "hiera/#{logger}_logger" unless constants.include?(loggerclass)
|
26
|
-
|
27
|
-
@logger = const_get(loggerclass)
|
28
|
-
rescue Exception => e
|
29
|
-
@logger = Console_logger
|
30
|
-
warn("Failed to load #{logger} logger: #{e.class}: #{e}")
|
31
|
-
end
|
32
|
-
|
33
|
-
def warn(msg); @logger.warn(msg); end
|
34
|
-
def debug(msg); @logger.debug(msg); end
|
35
|
-
end
|
8
|
+
VERSION = "1.0.0"
|
36
9
|
|
37
|
-
|
10
|
+
autoload :Config, "hiera/config"
|
11
|
+
autoload :Util, "hiera/util"
|
12
|
+
autoload :Backend, "hiera/backend"
|
13
|
+
autoload :Console_logger, "hiera/console_logger"
|
14
|
+
autoload :Puppet_logger, "hiera/puppet_logger"
|
15
|
+
autoload :Noop_logger, "hiera/noop_logger"
|
38
16
|
|
39
|
-
|
40
|
-
|
41
|
-
def initialize(options={})
|
42
|
-
options[:config] ||= "/etc/hiera.yaml"
|
17
|
+
class << self
|
18
|
+
attr_reader :logger
|
43
19
|
|
44
|
-
|
45
|
-
|
46
|
-
Config.load_backends
|
20
|
+
def version
|
21
|
+
VERSION
|
47
22
|
end
|
48
23
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
# The scope can be anything that responds to [], if you have input
|
52
|
-
# data like a Puppet Scope that does not you can wrap that data in a
|
53
|
-
# class that has a [] method that fetches the data from your source.
|
54
|
-
# See hiera-puppet for an example of this.
|
24
|
+
# Loggers are pluggable, just provide a class called
|
25
|
+
# Hiera::Foo_logger and respond to :warn and :debug
|
55
26
|
#
|
56
|
-
#
|
57
|
-
# of
|
58
|
-
def
|
59
|
-
|
27
|
+
# See hiera-puppet for an example that uses the Puppet
|
28
|
+
# loging system instead of our own
|
29
|
+
def logger=(logger)
|
30
|
+
loggerclass = "#{logger.capitalize}_logger"
|
31
|
+
|
32
|
+
require "hiera/#{logger}_logger" unless constants.include?(loggerclass)
|
33
|
+
|
34
|
+
@logger = const_get(loggerclass)
|
35
|
+
rescue Exception => e
|
36
|
+
@logger = Console_logger
|
37
|
+
warn("Failed to load #{logger} logger: #{e.class}: #{e}")
|
60
38
|
end
|
39
|
+
|
40
|
+
def warn(msg); @logger.warn(msg); end
|
41
|
+
def debug(msg); @logger.debug(msg); end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :options, :config
|
45
|
+
|
46
|
+
# If the config option is a string its assumed to be a filename,
|
47
|
+
# else a hash of what would have been in the YAML config file
|
48
|
+
def initialize(options={})
|
49
|
+
options[:config] ||= File.join(Util.config_dir, 'hiera.yaml')
|
50
|
+
|
51
|
+
@config = Config.load(options[:config])
|
52
|
+
|
53
|
+
Config.load_backends
|
54
|
+
end
|
55
|
+
|
56
|
+
# Calls the backends to do the actual lookup.
|
57
|
+
#
|
58
|
+
# The scope can be anything that responds to [], if you have input
|
59
|
+
# data like a Puppet Scope that does not you can wrap that data in a
|
60
|
+
# class that has a [] method that fetches the data from your source.
|
61
|
+
# See hiera-puppet for an example of this.
|
62
|
+
#
|
63
|
+
# The order-override will insert as first in the hierarchy a data source
|
64
|
+
# of your choice.
|
65
|
+
def lookup(key, default, scope, order_override=nil, resolution_type=:priority)
|
66
|
+
Backend.lookup(key, default, scope, order_override, resolution_type)
|
67
|
+
end
|
61
68
|
end
|
69
|
+
|