diff options
author | elijah <elijah@riseup.net> | 2012-10-09 00:01:55 -0700 |
---|---|---|
committer | elijah <elijah@riseup.net> | 2012-10-09 00:01:55 -0700 |
commit | 578ac2f5dc7432317d7a022bed9d869ab89ee45c (patch) | |
tree | 3cf8d27c2e8059df33e33e9c39146e0b3db29599 /cli/lib/leap_cli/config_manager.rb | |
parent | 105fe22c2de06310e48c4827b47d38d18750ed32 (diff) |
initial code commit
Diffstat (limited to 'cli/lib/leap_cli/config_manager.rb')
-rw-r--r-- | cli/lib/leap_cli/config_manager.rb | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/cli/lib/leap_cli/config_manager.rb b/cli/lib/leap_cli/config_manager.rb new file mode 100644 index 0000000..d383cc1 --- /dev/null +++ b/cli/lib/leap_cli/config_manager.rb @@ -0,0 +1,200 @@ +require 'oj' +require 'yaml' + +module LeapCli + + class ConfigManager + + attr_reader :services, :tags, :nodes + + ## + ## IMPORT EXPORT + ## + + # + # load .json configuration files + # + def load(dir) + @services = load_all_json("#{dir}/services/*.json") + @tags = load_all_json("#{dir}/tags/*.json") + @common = load_all_json("#{dir}/common.json")['common'] + @nodes = load_all_json("#{dir}/nodes/*.json", :node) + @nodes.each do |name, node| + apply_inheritance(node) + end + @nodes.each do |name, node| + node.each {|key,value| node[key] } # force evaluation of dynamic values + end + end + + # + # save compiled hiera .yaml files + # + def export(dir) + Dir.glob(dir + '/*.yaml').each do |f| + File.unlink(f) + end + @nodes.each do |name, node| + File.open("#{dir}/#{name}.#{node.domain_internal}.yaml", 'w') do |f| + f.write node.to_yaml + end + end + end + + ## + ## FILTERING + ## + + # + # returns a node list consisting only of nodes that satisfy the filter criteria. + # + # filter: condition [condition] [condition] [+condition] + # condition: [node_name | service_name | tag_name] + # + # if conditions is prefixed with +, then it works like an AND. Otherwise, it works like an OR. + # + def filter(filters) + if filters.empty? + return nodes + end + if filters[0] =~ /^\+/ + # don't let the first filter have a + prefix + filters[0] = filters[0][1..-1] + end + + node_list = ConfigList.new + filters.each do |filter| + if filter =~ /^\+/ + keep_list = nodes_for_filter(filter[1..-1]) + node_list.delete_if do |name, node| + if keep_list[name] + false + else + true + end + end + else + node_list << nodes_for_filter(filter) + end + end + return node_list + end + + ## + ## CLASS METHODS + ## + + def self.manager + @manager ||= begin + manager = ConfigManager.new + manager.load(Path.provider) + manager + end + end + + def self.filter(filters); manager.filter(filters); end + def self.nodes; manager.nodes; end + def self.services; manager.services; end + def self.tags; manager.tags; end + + private + + def load_all_json(pattern, config_type = :class) + results = ConfigList.new + Dir.glob(pattern).each do |filename| + obj = load_json(filename, config_type) + if obj + name = File.basename(filename).sub(/\.json$/,'') + obj['name'] = name + results[name] = obj + end + end + results + end + + def load_json(filename, config_type) + log2 { filename.sub(/^#{Regexp.escape(Path.root)}/,'') } + + # + # read file, strip out comments + # (File.read(filename) would be faster, but we like ability to have comments) + # + buffer = StringIO.new + File.open(filename) do |f| + while (line = f.gets) + next if line =~ /^\s*#/ + buffer << line + end + end + + # parse json, and flatten hash + begin + hash = Oj.load(buffer.string) || {} + rescue SyntaxError => exc + log0 'Error in file "%s":' % filename + log0 exc.to_s + return nil + end + return flatten_hash(hash, Config.new(config_type, self)) + end + + # + # remove all the nesting from a hash. + # + def flatten_hash(input = {}, output = {}, options = {}) + input.each do |key, value| + key = options[:prefix].nil? ? "#{key}" : "#{options[:prefix]}#{options[:delimiter]||"_"}#{key}" + if value.is_a? Hash + flatten_hash(value, output, :prefix => key, :delimiter => options[:delimiter]) + else + output[key] = value + end + end + output + end + + # + # makes this node inherit options from the common, service, and tag json files. + # + def apply_inheritance(node) + new_node = Config.new(:node, self) + new_node.merge!(@common) + if node['services'] + node['services'].sort.each do |node_service| + service = @services[node_service] + if service.nil? + log0('Error in node "%s": the service "%s" does not exist.' % [node['name'], node_service]) + else + new_node.merge!(service) + service.nodes << node # this is odd, but we want the node pointer, not new_node pointer. + end + end + end + if node['tags'] + node['tags'].sort.each do |node_tag| + tag = @tags[node_tag] + if tag.nil? + log0('Error in node "%s": the tag "%s" does not exist.' % [node['name'], node_tag]) + else + new_node.merge!(tag) + tag.nodes << node + end + end + end + new_node.merge!(node) + node.replace(new_node) + end + + def nodes_for_filter(filter) + if node = self.nodes[filter] + ConfigList.new(node) + elsif service = self.services[filter] + service.nodes + elsif tag = self.tags[filter] + tag.nodes + end + end + + end + +end |