hieracles 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|