hiera 0.1.0

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/bin/hiera ADDED
@@ -0,0 +1,143 @@
1
+ #!/bin/env ruby
2
+
3
+ # CLI client for Hiera.
4
+ #
5
+ # To lookup the 'release' key for a node given Puppet YAML facts:
6
+ #
7
+ # $ hiera release 'rel/%{location}' --yaml some.node.yaml
8
+ #
9
+ # If the node yaml had a location fact the default would match that
10
+ # else you can supply scope values on the command line
11
+ #
12
+ # $ hiera release 'rel/%{location}' location=dc2 --yaml some.node.yaml
13
+
14
+ require 'rubygems'
15
+ require 'hiera'
16
+ require 'optparse'
17
+
18
+
19
+ options = {:default => nil, :config => "/etc/hiera.yaml", :scope => {}, :key => nil, :verbose => false}
20
+
21
+ class Hiera::Noop_logger
22
+ class << self
23
+ def warn(msg);end
24
+ def debug(msg);end
25
+ end
26
+ end
27
+
28
+ # Loads the scope from YAML or JSON files
29
+ def load_scope(file, type=:yaml)
30
+ raise "Cannot find scope #{type} file #{file}" unless File.exist?(file)
31
+
32
+ case type
33
+ when :yaml
34
+ require 'yaml'
35
+
36
+ # Attempt to load puppet in case we're going to be fed
37
+ # Puppet yaml files
38
+ begin
39
+ require 'puppet'
40
+ rescue
41
+ end
42
+
43
+ scope = YAML.load_file(file)
44
+
45
+ # Puppet makes dumb yaml files that do not promote data reuse.
46
+ scope = scope.values if scope.is_a?(Puppet::Node::Facts)
47
+ when :json
48
+ require 'json'
49
+
50
+ scope = JSON.load(File.read(file))
51
+ else
52
+ raise "Don't know how to load data type #{type}"
53
+ end
54
+
55
+ raise "Scope from #{type} file #{file} should be a Hash" unless scope.is_a?(Hash)
56
+
57
+ scope
58
+ end
59
+
60
+ OptionParser.new do |opts|
61
+ opts.on("--version", "-V", "Version information") do
62
+ puts Hiera.version
63
+ exit
64
+ end
65
+
66
+ opts.on("--debug", "-d", "Show debugging information") do
67
+ options[:verbose] = true
68
+ end
69
+
70
+ opts.on("--config CONFIG", "-c", "Configuration file") do |v|
71
+ if File.exist?(v)
72
+ options[:config] = v
73
+ else
74
+ STDERR.puts "Cannot find config file: #{v}"
75
+ exit 1
76
+ end
77
+ end
78
+
79
+ opts.on("--json SCOPE", "-j", "JSON format file to load scope from") do |v|
80
+ begin
81
+ options[:scope] = load_scope(v, :json)
82
+ rescue Exception => e
83
+ STDERR.puts "Could not load JSON scope: #{e.class}: #{e}"
84
+ exit 1
85
+ end
86
+ end
87
+
88
+ opts.on("--yaml SCOPE", "-y", "YAML format file to load scope from") do |v|
89
+ begin
90
+ options[:scope] = load_scope(v)
91
+ rescue Exception => e
92
+ STDERR.puts "Could not load YAML scope: #{e.class}: #{e}"
93
+ exit 1
94
+ end
95
+ end
96
+ end.parse!
97
+
98
+ # arguments can be:
99
+ #
100
+ # key default var=val another=val
101
+ #
102
+ # The var=val's assign scope
103
+ unless ARGV.empty?
104
+ options[:key] = ARGV.delete_at(0)
105
+
106
+ ARGV.each do |arg|
107
+ if arg =~ /^(.+?)=(.+?)$/
108
+ options[:scope][$1] = $2
109
+ else
110
+ unless options[:default]
111
+ options[:default] = arg.dup
112
+ else
113
+ STDERR.puts "Don't know how to parse scope argument: #{arg}"
114
+ end
115
+ end
116
+ end
117
+ else
118
+ STDERR.puts "Please supply a data item to look up"
119
+ exit 1
120
+ end
121
+
122
+ begin
123
+ hiera = Hiera.new(:config => options[:config])
124
+ rescue Exception => e
125
+ if options[:verbose]
126
+ raise
127
+ else
128
+ STDERR.puts "Failed to start Hiera: #{e.class}: #{e}"
129
+ exit 1
130
+ end
131
+ end
132
+
133
+ unless options[:verbose]
134
+ Hiera.logger = "noop"
135
+ end
136
+
137
+ ans = hiera.lookup(options[:key], options[:default], options[:scope])
138
+
139
+ if ans.is_a?(String)
140
+ puts ans
141
+ else
142
+ p ans
143
+ end
data/lib/hiera.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+
4
+ class Hiera
5
+ VERSION = "0.1.0"
6
+
7
+ autoload :Config, "hiera/config"
8
+ autoload :Backend, "hiera/backend"
9
+ autoload :Console_logger, "hiera/console_logger"
10
+
11
+ class << self
12
+ def version
13
+ VERSION
14
+ end
15
+
16
+ # Loggers are pluggable, just provide a class called
17
+ # Hiera::Foo_logger and respond to :warn and :debug
18
+ #
19
+ # See hiera-puppet for an example that uses the Puppet
20
+ # loging system instead of our own
21
+ def logger=(logger)
22
+ loggerclass = "#{logger.capitalize}_logger"
23
+
24
+ require "hiera/#{logger}_logger" unless constants.include?(loggerclass)
25
+
26
+ @logger = const_get(loggerclass)
27
+ rescue Exception => e
28
+ @logger = Console_logger
29
+ warn("Failed to load #{logger} logger: #{e.class}: #{e}")
30
+ end
31
+
32
+ def warn(msg); @logger.warn(msg); end
33
+ def debug(msg); @logger.debug(msg); end
34
+ end
35
+
36
+ attr_reader :options, :config
37
+
38
+ # If the config option is a string its assumed to be a filename,
39
+ # else a hash of what would have been in the YAML config file
40
+ def initialize(options={})
41
+ options[:config] ||= "/etc/hiera.yaml"
42
+
43
+ @config = Config.load(options[:config])
44
+
45
+ Config.load_backends
46
+ end
47
+
48
+ # Calls the backends to do the actual lookup.
49
+ #
50
+ # The scope can be anything that responds to [], if you have input
51
+ # data like a Puppet Scope that does not you can wrap that data in a
52
+ # class that has a [] method that fetches the data from your source.
53
+ # See hiera-puppet for an example of this.
54
+ #
55
+ # The order-override will insert as first in the hierarchy a data source
56
+ # of your choice.
57
+ #
58
+ # TODO: resolution_type is to eventually support top down priority based
59
+ # lookups or bottom up merging type lookups like an ENC might need
60
+ def lookup(key, default, scope, order_override=nil, resolution_type=:priority)
61
+ Backend.lookup(key, default, scope, order_override, resolution_type)
62
+ end
63
+ end
@@ -0,0 +1,102 @@
1
+ class Hiera
2
+ module Backend
3
+ class << self
4
+ # Data lives in /var/lib/hiera by default. If a backend
5
+ # supplies a datadir in the config it will be used and
6
+ # subject to variable expansion based on scope
7
+ def datadir(backend, scope)
8
+ backend = backend.to_sym
9
+ default = "/var/lib/hiera"
10
+
11
+ if Config.include?(backend)
12
+ parse_string(Config[backend][:datadir] || default, scope)
13
+ else
14
+ parse_string(default, scope)
15
+ end
16
+ end
17
+
18
+ # Constructs a list of data sources to search
19
+ #
20
+ # If you give it a specific hierarchy it will just use that
21
+ # else it will use the global configured one, failing that
22
+ # it will just look in the 'common' data source.
23
+ #
24
+ # An override can be supplied that will be pre-pended to the
25
+ # hierarchy.
26
+ #
27
+ # The source names will be subject to variable expansion based
28
+ # on scope
29
+ def datasources(scope, override=nil, hierarchy=nil)
30
+ if hierarchy
31
+ hierarchy = [hierarchy]
32
+ elsif Config.include?(:hierarchy)
33
+ hierarchy = [Config[:hierarchy]].flatten
34
+ else
35
+ hierarchy = ["common"]
36
+ end
37
+
38
+ hierarchy.insert(0, override) if override
39
+
40
+ hierarchy.flatten.map do |source|
41
+ source = parse_string(source, scope)
42
+ yield(source) unless source == ""
43
+ end
44
+ end
45
+
46
+ # Parse a string like '%{foo}' against a supplied
47
+ # scope and additional scope. If either scope or
48
+ # extra_scope includes the varaible 'foo' it will
49
+ # be replaced else an empty string will be placed.
50
+ #
51
+ # If both scope and extra_data has "foo" scope
52
+ # will win. See hiera-puppet for an example of
53
+ # this to make hiera aware of additional non scope
54
+ # variables
55
+ def parse_string(data, scope, extra_data={})
56
+ return nil unless data
57
+
58
+ tdata = data.clone
59
+
60
+ if tdata.is_a?(String)
61
+ while tdata =~ /%\{(.+?)\}/
62
+ var = $1
63
+ val = scope[var] || extra_data[var] || ""
64
+
65
+ tdata.gsub!(/%\{#{var}\}/, val)
66
+ end
67
+ end
68
+
69
+ return tdata
70
+ end
71
+
72
+ # Calls out to all configured backends in the order they
73
+ # were specified. The first one to answer will win.
74
+ #
75
+ # This lets you declare multiple backends, a possible
76
+ # use case might be in Puppet where a Puppet module declares
77
+ # default data using in-module data while users can override
78
+ # using JSON/YAML etc. By layering the backends and putting
79
+ # the Puppet one last you can override module author data
80
+ # easily.
81
+ #
82
+ # Backend instances are cached so if you need to connect to any
83
+ # databases then do so in your constructor, future calls to your
84
+ # backend will not create new instances
85
+ def lookup(key, default, scope, order_override, resolution_type)
86
+ @backends ||= {}
87
+ answer = nil
88
+
89
+ Config[:backends].each do |backend|
90
+ if constants.include?("#{backend.capitalize}_backend")
91
+ @backends[backend] ||= Backend.const_get("#{backend.capitalize}_backend").new
92
+ answer = @backends[backend].lookup(key, scope, order_override, resolution_type)
93
+
94
+ break if answer
95
+ end
96
+ end
97
+
98
+ answer || parse_string(default, scope)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,45 @@
1
+ class Hiera
2
+ module Backend
3
+ class Yaml_backend
4
+ def initialize
5
+ require 'yaml'
6
+
7
+ Hiera.debug("Hiera YAML backend starting")
8
+ end
9
+
10
+ def lookup(key, scope, order_override, resolution_type)
11
+ answer = nil
12
+
13
+ Hiera.debug("Looking up #{key} in YAML backend")
14
+
15
+ datadir = Backend.datadir(:yaml, scope)
16
+
17
+ raise "Cannot find data directory #{datadir}" unless File.directory?(datadir)
18
+
19
+ Backend.datasources(scope, order_override) do |source|
20
+ unless answer
21
+ Hiera.debug("Looking for data source #{source}")
22
+
23
+ datafile = File.join([datadir, "#{source}.yaml"])
24
+
25
+ unless File.exist?(datafile)
26
+ Hiera.warn("Cannot find datafile #{datafile}, skipping")
27
+ next
28
+ end
29
+
30
+ data = YAML.load_file(datafile)
31
+
32
+ next if data.empty?
33
+ next unless data.include?(key)
34
+
35
+ answer = Backend.parse_string(data[key], scope)
36
+ else
37
+ break
38
+ end
39
+ end
40
+
41
+ answer
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,50 @@
1
+ class Hiera::Config
2
+ class << self
3
+ # Takes a string or hash as input, strings are treated as filenames
4
+ # hashes are stored as data that would have been in the config file
5
+ #
6
+ # Unless specified it will only use YAML as backend with a single
7
+ # 'common' hierarchy and console logger
8
+ def load(source)
9
+ @config = {:backends => "yaml",
10
+ :hierarchy => "common"}
11
+
12
+ if source.is_a?(String)
13
+ raise "Config file #{source} not found" unless File.exist?(source)
14
+
15
+ @config.merge! YAML.load_file(source)
16
+ elsif source.is_a?(Hash)
17
+ @config.merge! source
18
+ end
19
+
20
+ @config[:backends] = [ @config[:backends] ].flatten
21
+
22
+ if @config.include?(:logger)
23
+ Hiera.logger = @config[:logger].to_s
24
+ else
25
+ @config[:logger] = "console"
26
+ Hiera.logger = "console"
27
+ end
28
+
29
+ @config
30
+ end
31
+
32
+ def load_backends
33
+ @config[:backends].each do |backend|
34
+ begin
35
+ require "hiera/backend/#{backend.downcase}_backend"
36
+ rescue LoadError => e
37
+ Hiera.warn "Cannot load backend #{backend}: #{e}"
38
+ end
39
+ end
40
+ end
41
+
42
+ def include?(key)
43
+ @config.include?(key)
44
+ end
45
+
46
+ def [](key)
47
+ @config[key]
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ class Hiera
2
+ module Console_logger
3
+ class << self
4
+ def warn(msg)
5
+ STDERR.puts("WARN: %s: %s" % [Time.now.to_s, msg])
6
+ end
7
+
8
+ def debug(msg)
9
+ STDERR.puts("DEBUG: %s: %s" % [Time.now.to_s, msg])
10
+ end
11
+ end
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hiera
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - R.I.Pienaar
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-06-05 00:00:00 +01:00
18
+ default_executable: hiera
19
+ dependencies: []
20
+
21
+ description: A pluggable data store for hierarcical data
22
+ email: rip@devco.net
23
+ executables:
24
+ - hiera
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - bin/hiera
31
+ - lib/hiera.rb
32
+ - lib/hiera/backend.rb
33
+ - lib/hiera/config.rb
34
+ - lib/hiera/console_logger.rb
35
+ - lib/hiera/backend/yaml_backend.rb
36
+ has_rdoc: true
37
+ homepage: http://devco.net/
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ segments:
50
+ - 0
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.3.6
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Light weight hierarcical data store
66
+ test_files: []
67
+