hieracles 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +30 -0
- data/Rakefile +14 -0
- data/bin/hc +32 -0
- data/bin/nodeinfo +41 -0
- data/hieracles.gemspec +30 -0
- data/lib/hieracles.rb +15 -0
- data/lib/hieracles/config.rb +56 -0
- data/lib/hieracles/format.rb +58 -0
- data/lib/hieracles/formats/console.rb +84 -0
- data/lib/hieracles/formats/csv.rb +66 -0
- data/lib/hieracles/formats/plain.rb +61 -0
- data/lib/hieracles/formats/yaml.rb +36 -0
- data/lib/hieracles/help.rb +27 -0
- data/lib/hieracles/hiera.rb +27 -0
- data/lib/hieracles/node.rb +108 -0
- data/lib/hieracles/optparse.rb +63 -0
- data/lib/hieracles/utils.rb +59 -0
- data/spec/files/config.yml +5 -0
- data/spec/files/enc/server.example.com.yaml +7 -0
- data/spec/files/enc/server2.example.com.yaml +7 -0
- data/spec/files/enc/server3.example.com.yaml +7 -0
- data/spec/files/farm_modules/dev.pp +5 -0
- data/spec/files/farm_modules/dev2.pp +5 -0
- data/spec/files/hiera.yaml +16 -0
- data/spec/files/hiera_no_yamlbackend.yaml +16 -0
- data/spec/files/hiera_yamlbackend_notfound.yaml +16 -0
- data/spec/files/modules/fake_module/manifests/init.pp +3 -0
- data/spec/files/modules/fake_module2/manifests/init.pp +3 -0
- data/spec/files/modules/fake_module3/manifests/init.pp +3 -0
- data/spec/files/params/common/common.yml +2 -0
- data/spec/files/params/farm/dev.yaml +1 -0
- data/spec/files/params/nodes/server.example.com.yaml +5 -0
- data/spec/lib/config_spec.rb +63 -0
- data/spec/lib/format_spec.rb +91 -0
- data/spec/lib/formats/console_spec.rb +107 -0
- data/spec/lib/formats/csv_spec.rb +89 -0
- data/spec/lib/formats/plain_spec.rb +94 -0
- data/spec/lib/formats/yaml_spec.rb +80 -0
- data/spec/lib/help_spec.rb +8 -0
- data/spec/lib/hiera_spec.rb +111 -0
- data/spec/lib/node_spec.rb +158 -0
- data/spec/lib/optparse_spec.rb +65 -0
- data/spec/lib/utils_spec.rb +61 -0
- data/spec/spec_helper.rb +26 -0
- metadata +223 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
module Hieracles
|
2
|
+
module Formats
|
3
|
+
# for db compatibility
|
4
|
+
class Csv < Hieracles::Format
|
5
|
+
CVS_DELIM = ';'
|
6
|
+
|
7
|
+
def info(_)
|
8
|
+
make_csv @node.info.values
|
9
|
+
end
|
10
|
+
|
11
|
+
def files(_)
|
12
|
+
make_csv @node.files
|
13
|
+
end
|
14
|
+
|
15
|
+
def paths(_)
|
16
|
+
make_csv @node.paths
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_head(without_common)
|
20
|
+
output = []
|
21
|
+
@node.files(without_common).each do |f|
|
22
|
+
output << f
|
23
|
+
end
|
24
|
+
output += %w(var value overriden)
|
25
|
+
make_csv output
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_params_line(key, value, filter)
|
29
|
+
output = ''
|
30
|
+
if !filter || Regexp.new(filter).match(key)
|
31
|
+
first = value.shift
|
32
|
+
output << make_csv(in_what_file(first[:file]) +
|
33
|
+
[key, first[:value].to_s, '0'])
|
34
|
+
while value.count > 0
|
35
|
+
overriden = value.shift
|
36
|
+
output << make_csv(in_what_file(overriden[:file]) +
|
37
|
+
[key, overriden[:value].to_s, '1'])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
output
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_modules_line(key, value)
|
44
|
+
make_csv [key, value]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def make_csv(array)
|
50
|
+
array.join(CVS_DELIM) + "\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
def in_what_file(file)
|
54
|
+
output = []
|
55
|
+
@node.files.each do |f|
|
56
|
+
if file == f
|
57
|
+
output << '1'
|
58
|
+
else
|
59
|
+
output << '0'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
output
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Hieracles
|
2
|
+
module Formats
|
3
|
+
# format accepting colors
|
4
|
+
# for display in the terminal
|
5
|
+
class Plain < Hieracles::Format
|
6
|
+
include Hieracles::Utils
|
7
|
+
|
8
|
+
def initialize(node)
|
9
|
+
@index = {}
|
10
|
+
super(node)
|
11
|
+
end
|
12
|
+
|
13
|
+
def info(_)
|
14
|
+
back = ''
|
15
|
+
length = max_key_length(@node.info) + 2
|
16
|
+
@node.info.each do |k, v|
|
17
|
+
back << format("%-#{length}s %s\n", k, v)
|
18
|
+
end
|
19
|
+
back
|
20
|
+
end
|
21
|
+
|
22
|
+
def files(_)
|
23
|
+
@node.files.join("\n") + "\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
def paths(_)
|
27
|
+
@node.paths.join("\n") + "\n"
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_head(without_common)
|
31
|
+
output = ''
|
32
|
+
@node.files(without_common).each_with_index do |f, i|
|
33
|
+
output << "[#{i}] #{f}\n"
|
34
|
+
@index[f] = i
|
35
|
+
end
|
36
|
+
"#{output}\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_params_line(key, value, filter)
|
40
|
+
output = ''
|
41
|
+
if !filter || Regexp.new(filter).match(key)
|
42
|
+
first = value.shift
|
43
|
+
filecolor_index = @index[first[:file]]
|
44
|
+
output << "[#{filecolor_index}] #{key} #{first[:value]}\n"
|
45
|
+
while value.count > 0
|
46
|
+
overriden = value.shift
|
47
|
+
filecolor_index = @index[overriden[:file]]
|
48
|
+
output << " [#{filecolor_index}] #{key} #{overriden[:value]}\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
output
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_modules_line(key, value)
|
55
|
+
length = max_key_length(@node.modules) + 3
|
56
|
+
format("%-#{length}s %s\n", key, value)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Hieracles
|
2
|
+
module Formats
|
3
|
+
# format mostly useful for re-integration in param files
|
4
|
+
class Yaml < Hieracles::Format
|
5
|
+
|
6
|
+
def info(_)
|
7
|
+
@node.info.to_yaml
|
8
|
+
end
|
9
|
+
|
10
|
+
def files(_)
|
11
|
+
@node.files.to_yaml
|
12
|
+
end
|
13
|
+
|
14
|
+
def paths(_)
|
15
|
+
@node.paths.to_yaml
|
16
|
+
end
|
17
|
+
|
18
|
+
def modules(_)
|
19
|
+
@node.modules.to_yaml
|
20
|
+
end
|
21
|
+
|
22
|
+
def params(args)
|
23
|
+
@node.params_tree(true).to_yaml
|
24
|
+
end
|
25
|
+
|
26
|
+
def allparams(args)
|
27
|
+
@node.params_tree(false).to_yaml
|
28
|
+
end
|
29
|
+
|
30
|
+
def modules(args)
|
31
|
+
@node.modules.to_yaml
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Hieracles
|
2
|
+
module Help
|
3
|
+
|
4
|
+
def self.usage
|
5
|
+
return <<-END
|
6
|
+
|
7
|
+
Usage: hc <fqdn> <command> [extra_args]
|
8
|
+
|
9
|
+
Available commands:
|
10
|
+
info provides the farm, datacenter, country
|
11
|
+
associated to the given fqdn
|
12
|
+
files list all files containing params affecting this fqdn
|
13
|
+
(in more than commons)
|
14
|
+
paths list all file paths for files with params
|
15
|
+
modules list modules included in the farm where the node is
|
16
|
+
params list params for the node matching the fqdn
|
17
|
+
An extra filter string can be added to limit the list
|
18
|
+
use ruby regexp without the enclosing slashes
|
19
|
+
eg. hc <fqdn> params postfix.*version
|
20
|
+
eg. hc <fqdn> params '^postfix'
|
21
|
+
eg. hc <fqdn> params 'version$'
|
22
|
+
allparams same as params but including the common.yaml params (huge)
|
23
|
+
Also accepts a search string
|
24
|
+
END
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Hieracles
|
2
|
+
|
3
|
+
class Hiera
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
raise IOError, "Hierafile #{Config.hierafile} not found." unless File.exist? Config.path('hierafile')
|
7
|
+
@hierafile = Config.path('hierafile')
|
8
|
+
@loaded = YAML.load_file(@hierafile)
|
9
|
+
end
|
10
|
+
|
11
|
+
def datadir
|
12
|
+
raise TypeError, "Sorry hieracles only knows yaml backend for now." unless @loaded[:yaml]
|
13
|
+
parampath = File.expand_path(File.join(Config.basepath, @loaded[:yaml][:datadir]))
|
14
|
+
raise IOError, "Params dir #{parampath} not found." unless Dir.exist? parampath
|
15
|
+
parampath
|
16
|
+
end
|
17
|
+
|
18
|
+
def hierarchy
|
19
|
+
@loaded[:hierarchy]
|
20
|
+
end
|
21
|
+
|
22
|
+
def params
|
23
|
+
hierarchy.join(',').scan(/%\{(?:::)?([^\}]*)\}/).flatten.uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "uri"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module Hieracles
|
6
|
+
class Node
|
7
|
+
include Hieracles::Utils
|
8
|
+
|
9
|
+
attr_reader :hiera_params, :hiera
|
10
|
+
|
11
|
+
def initialize(fqdn, options)
|
12
|
+
Config.load(options)
|
13
|
+
@hiera = Hieracles::Hiera.new
|
14
|
+
@hiera_params = { fqdn: fqdn }.
|
15
|
+
merge(get_hiera_params(fqdn)).
|
16
|
+
merge(Config.extraparams)
|
17
|
+
@fqdn = fqdn
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_hiera_params(fqdn)
|
21
|
+
if File.exist?(File.join(Config.path('encpath'), "#{fqdn}.yaml"))
|
22
|
+
load = YAML.load_file(File.join(Config.path('encpath'), "#{fqdn}.yaml"))
|
23
|
+
sym_keys(load['parameters'])
|
24
|
+
else
|
25
|
+
raise "Node not found"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def files(without_common = true)
|
30
|
+
@hiera.hierarchy.reduce([]) do |a, f|
|
31
|
+
file = format("#{f}.yaml", @hiera_params) rescue nil
|
32
|
+
if file &&
|
33
|
+
File.exist?(File.join(@hiera.datadir, file)) &&
|
34
|
+
(!without_common ||
|
35
|
+
!file[/common/])
|
36
|
+
a << file
|
37
|
+
end
|
38
|
+
a
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def paths(without_common = true)
|
43
|
+
files(without_common).map { |p| File.join(@hiera.datadir, p) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def params(without_common = true)
|
47
|
+
params = {}
|
48
|
+
files(without_common).each do |f|
|
49
|
+
data = YAML.load_file(File.join(@hiera.datadir, f))
|
50
|
+
s = to_shallow_hash(data)
|
51
|
+
s.each do |k,v|
|
52
|
+
params[k] ||= []
|
53
|
+
params[k] << { value: v, file: f}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
params.sort
|
57
|
+
end
|
58
|
+
|
59
|
+
def params_tree(without_common = true)
|
60
|
+
params = {}
|
61
|
+
paths(without_common).each do |f|
|
62
|
+
data = YAML.load_file(f)
|
63
|
+
deep_merge!(params, data)
|
64
|
+
end
|
65
|
+
deep_sort(params)
|
66
|
+
end
|
67
|
+
|
68
|
+
def modules
|
69
|
+
if File.exist?(classfile)
|
70
|
+
modules = {}
|
71
|
+
f = File.open(classfile, "r")
|
72
|
+
f.each_line do |line|
|
73
|
+
modules = add_modules(line, modules)
|
74
|
+
end
|
75
|
+
f.close
|
76
|
+
modules
|
77
|
+
else
|
78
|
+
raise "Class file #{classfile} not found."
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def info
|
83
|
+
@hiera_params
|
84
|
+
end
|
85
|
+
|
86
|
+
def classfile
|
87
|
+
format(Config.path('classpath'), @hiera_params[:farm])
|
88
|
+
end
|
89
|
+
|
90
|
+
def modulepath(path)
|
91
|
+
File.join(Config.path('modulepath'), path)
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_modules(line, modules)
|
95
|
+
if /^\s*include\s*([-_:a-zA-Z0-9]*)\s*/.match(line)
|
96
|
+
mod = $1
|
97
|
+
mainmod = mod[/^[^:]*/]
|
98
|
+
if Dir.exists? modulepath(mainmod)
|
99
|
+
modules[mod] = File.join(Config.modulepath, mainmod)
|
100
|
+
else
|
101
|
+
modules[mod] = nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
modules
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Hieracles
|
2
|
+
|
3
|
+
class Optparse
|
4
|
+
|
5
|
+
attr_reader :options, :payload
|
6
|
+
|
7
|
+
OPTIONS = {
|
8
|
+
config: {
|
9
|
+
has_arg: true,
|
10
|
+
aliases: ['c', 'conf', 'config']
|
11
|
+
},
|
12
|
+
format: {
|
13
|
+
has_arg: true,
|
14
|
+
aliases: ['f', 'format']
|
15
|
+
},
|
16
|
+
params: {
|
17
|
+
has_arg: true,
|
18
|
+
aliases: ['p', 'params']
|
19
|
+
},
|
20
|
+
hierafile: {
|
21
|
+
has_arg: true,
|
22
|
+
aliases: ['h', 'hierafile']
|
23
|
+
},
|
24
|
+
basepath: {
|
25
|
+
has_arg: true,
|
26
|
+
aliases: ['b', 'basepath']
|
27
|
+
},
|
28
|
+
encpath: {
|
29
|
+
has_arg: true,
|
30
|
+
aliases: ['e', 'encpath']
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
def initialize(array)
|
35
|
+
@options = {}
|
36
|
+
@payload = []
|
37
|
+
ok = optionkeys
|
38
|
+
while x = array.shift
|
39
|
+
if x[0] == '-'
|
40
|
+
if ok[x[/[a-z][-_a-z]*$/]]
|
41
|
+
@options[ok[x[/[a-z][-_a-z]*$/]]] = array.shift
|
42
|
+
else
|
43
|
+
array.shift
|
44
|
+
end
|
45
|
+
else
|
46
|
+
@payload << x
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def optionkeys
|
52
|
+
back = {}
|
53
|
+
OPTIONS.each do |k, v|
|
54
|
+
v[:aliases].each do |a|
|
55
|
+
back[a] = k
|
56
|
+
end
|
57
|
+
end
|
58
|
+
back
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Hieracles
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
def to_shallow_hash(hash)
|
5
|
+
hash.reduce({}) do |shallow_hash, (key, value)|
|
6
|
+
if value.is_a?(Hash)
|
7
|
+
to_shallow_hash(value).each do |sub_key, sub_value|
|
8
|
+
shallow_hash[[key, sub_key].join('.')] = sub_value
|
9
|
+
end
|
10
|
+
else
|
11
|
+
shallow_hash[key.to_s] = value
|
12
|
+
end
|
13
|
+
shallow_hash
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_deep_hash(hash)
|
18
|
+
hash.reduce({}) do |a, (key, value)|
|
19
|
+
keys = key.to_s.split('.').reverse
|
20
|
+
leaf_key = keys.shift
|
21
|
+
key_hash = keys.reduce(leaf_key.to_sym => value) { |h, k| { k.to_sym => h } }
|
22
|
+
deep_merge!(a, key_hash)
|
23
|
+
a
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def deep_merge!(hash1, hash2)
|
28
|
+
merger = proc { |key, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : v2 }
|
29
|
+
hash1.merge!(hash2, &merger)
|
30
|
+
end
|
31
|
+
|
32
|
+
def deep_sort(object)
|
33
|
+
if object.is_a?(Hash)
|
34
|
+
res = {}
|
35
|
+
object.each { |k, v| res[k] = deep_sort(v) }
|
36
|
+
Hash[res.sort { |a, b| a[0].to_s <=> b[0].to_s }]
|
37
|
+
elsif object.is_a?(Array)
|
38
|
+
if object[0].is_a?(Hash) || object[0].is_a?(Array)
|
39
|
+
array = []
|
40
|
+
object.each_with_index { |v, i| array[i] = deep_sort(v) }
|
41
|
+
array
|
42
|
+
else
|
43
|
+
object.sort
|
44
|
+
end
|
45
|
+
else
|
46
|
+
object
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def max_key_length(hash)
|
51
|
+
hash.keys.reduce(0) { |a, x| (x.length > a) ? x.length : a }
|
52
|
+
end
|
53
|
+
|
54
|
+
def sym_keys(hash)
|
55
|
+
hash.reduce({}) { |a, (k, v)| a[k.to_sym] = v ; a }
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|