hieracles 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +15 -0
  4. data/.rspec +2 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/CHANGELOG.md +8 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +30 -0
  11. data/Rakefile +14 -0
  12. data/bin/hc +32 -0
  13. data/bin/nodeinfo +41 -0
  14. data/hieracles.gemspec +30 -0
  15. data/lib/hieracles.rb +15 -0
  16. data/lib/hieracles/config.rb +56 -0
  17. data/lib/hieracles/format.rb +58 -0
  18. data/lib/hieracles/formats/console.rb +84 -0
  19. data/lib/hieracles/formats/csv.rb +66 -0
  20. data/lib/hieracles/formats/plain.rb +61 -0
  21. data/lib/hieracles/formats/yaml.rb +36 -0
  22. data/lib/hieracles/help.rb +27 -0
  23. data/lib/hieracles/hiera.rb +27 -0
  24. data/lib/hieracles/node.rb +108 -0
  25. data/lib/hieracles/optparse.rb +63 -0
  26. data/lib/hieracles/utils.rb +59 -0
  27. data/spec/files/config.yml +5 -0
  28. data/spec/files/enc/server.example.com.yaml +7 -0
  29. data/spec/files/enc/server2.example.com.yaml +7 -0
  30. data/spec/files/enc/server3.example.com.yaml +7 -0
  31. data/spec/files/farm_modules/dev.pp +5 -0
  32. data/spec/files/farm_modules/dev2.pp +5 -0
  33. data/spec/files/hiera.yaml +16 -0
  34. data/spec/files/hiera_no_yamlbackend.yaml +16 -0
  35. data/spec/files/hiera_yamlbackend_notfound.yaml +16 -0
  36. data/spec/files/modules/fake_module/manifests/init.pp +3 -0
  37. data/spec/files/modules/fake_module2/manifests/init.pp +3 -0
  38. data/spec/files/modules/fake_module3/manifests/init.pp +3 -0
  39. data/spec/files/params/common/common.yml +2 -0
  40. data/spec/files/params/farm/dev.yaml +1 -0
  41. data/spec/files/params/nodes/server.example.com.yaml +5 -0
  42. data/spec/lib/config_spec.rb +63 -0
  43. data/spec/lib/format_spec.rb +91 -0
  44. data/spec/lib/formats/console_spec.rb +107 -0
  45. data/spec/lib/formats/csv_spec.rb +89 -0
  46. data/spec/lib/formats/plain_spec.rb +94 -0
  47. data/spec/lib/formats/yaml_spec.rb +80 -0
  48. data/spec/lib/help_spec.rb +8 -0
  49. data/spec/lib/hiera_spec.rb +111 -0
  50. data/spec/lib/node_spec.rb +158 -0
  51. data/spec/lib/optparse_spec.rb +65 -0
  52. data/spec/lib/utils_spec.rb +61 -0
  53. data/spec/spec_helper.rb +26 -0
  54. 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