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.
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