From 47062a50e9bba238191838a6625b81793afa8472 Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 10 Oct 2012 00:09:31 -0700 Subject: hierarchical yaml output. --- lib/core_ext/boolean.rb | 14 +++ lib/core_ext/hash.rb | 74 +++++++++++++ lib/core_ext/nil.rb | 5 + lib/leap_cli.rb | 17 ++- lib/leap_cli/commands/compile.rb | 1 - lib/leap_cli/commands/deploy.rb | 2 +- lib/leap_cli/commands/list.rb | 8 +- lib/leap_cli/config.rb | 119 --------------------- lib/leap_cli/config/base.rb | 149 ++++++++++++++++++++++++++ lib/leap_cli/config/list.rb | 81 +++++++++++++++ lib/leap_cli/config/manager.rb | 219 +++++++++++++++++++++++++++++++++++++++ lib/leap_cli/config/node.rb | 19 ++++ lib/leap_cli/config/tag.rb | 19 ++++ lib/leap_cli/config_list.rb | 77 -------------- lib/leap_cli/config_manager.rb | 200 ----------------------------------- 15 files changed, 598 insertions(+), 406 deletions(-) create mode 100644 lib/core_ext/boolean.rb create mode 100644 lib/core_ext/hash.rb create mode 100644 lib/core_ext/nil.rb delete mode 100644 lib/leap_cli/config.rb create mode 100644 lib/leap_cli/config/base.rb create mode 100644 lib/leap_cli/config/list.rb create mode 100644 lib/leap_cli/config/manager.rb create mode 100644 lib/leap_cli/config/node.rb create mode 100644 lib/leap_cli/config/tag.rb delete mode 100644 lib/leap_cli/config_list.rb delete mode 100644 lib/leap_cli/config_manager.rb (limited to 'lib') diff --git a/lib/core_ext/boolean.rb b/lib/core_ext/boolean.rb new file mode 100644 index 0000000..9b617b2 --- /dev/null +++ b/lib/core_ext/boolean.rb @@ -0,0 +1,14 @@ +# +# make is_a?(Boolean) possible. +# + +module Boolean +end + +class TrueClass + include Boolean +end + +class FalseClass + include Boolean +end \ No newline at end of file diff --git a/lib/core_ext/hash.rb b/lib/core_ext/hash.rb new file mode 100644 index 0000000..a9e5c9e --- /dev/null +++ b/lib/core_ext/hash.rb @@ -0,0 +1,74 @@ +# +# +# We modify Hash to add a few features we need: +# +# * sorted output of keys to in yaml. +# * reference values either with hsh[key] or hsh.key +# * deep merge +# * select fields +# +# Because the json parsing code we use doesn't support setting a custom class, it is easier for us to just modify Hash. +# + +require 'yaml' + +class Hash + + ## + ## YAML + ## + + # + # make the type appear to be a normal Hash in yaml, even for subclasses. + # + def to_yaml_type + "!map" + end + + # + # just like Hash#to_yaml, but sorted + # + def to_yaml(opts = {}) + YAML::quick_emit(self, opts) do |out| + out.map(taguri, to_yaml_style) do |map| + keys.sort.each do |k| + v = self[k] + map.add(k, v) + end + end + end + end + + ## + ## CONVERTING + ## + + # + # convert self into a plain hash, but only include the specified keys + # + def pick(*keys) + keys.map(&:to_s).inject({}) do |hsh, key| + if has_key?(key) + hsh[key] = self[key] + end + hsh + end + end + + # + # recursive merging (aka deep merge) + # taken from ActiveSupport::CoreExtensions::Hash::DeepMerge + # + # def deep_merge(other_hash) + # self.merge(other_hash) do |key, oldval, newval| + # oldval = oldval.to_hash if oldval.respond_to?(:to_hash) + # newval = newval.to_hash if newval.respond_to?(:to_hash) + # oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval + # end + # end + + # def deep_merge!(other_hash) + # replace(deep_merge(other_hash)) + # end + +end diff --git a/lib/core_ext/nil.rb b/lib/core_ext/nil.rb new file mode 100644 index 0000000..05ca98f --- /dev/null +++ b/lib/core_ext/nil.rb @@ -0,0 +1,5 @@ +class NilClass + def any? + false + end +end \ No newline at end of file diff --git a/lib/leap_cli.rb b/lib/leap_cli.rb index b935e35..6fb91a2 100644 --- a/lib/leap_cli.rb +++ b/lib/leap_cli.rb @@ -5,15 +5,24 @@ unless defined?(LeapCli::VERSION) require 'leap_cli/version.rb' end +require 'core_ext/hash' +require 'core_ext/boolean' +require 'core_ext/nil' + require 'leap_cli/init' require 'leap_cli/path' require 'leap_cli/log' -require 'leap_cli/config' -require 'leap_cli/config_list' -require 'leap_cli/config_manager' +require 'leap_cli/config/base' +require 'leap_cli/config/node' +require 'leap_cli/config/tag' +require 'leap_cli/config/manager' +require 'leap_cli/config/list' +# +# make 1.8 act like ruby 1.9 +# unless String.method_defined?(:to_a) class String def to_a; [self]; end end -end \ No newline at end of file +end diff --git a/lib/leap_cli/commands/compile.rb b/lib/leap_cli/commands/compile.rb index 6b38de5..8764e52 100644 --- a/lib/leap_cli/commands/compile.rb +++ b/lib/leap_cli/commands/compile.rb @@ -4,7 +4,6 @@ module LeapCli desc 'Compile json files to hiera configs' command :compile do |c| c.action do |global_options,options,args| - manager = ConfigManager.new manager.load(Path.provider) Path.ensure_dir(Path.hiera) manager.export(Path.hiera) diff --git a/lib/leap_cli/commands/deploy.rb b/lib/leap_cli/commands/deploy.rb index 3694a38..9ec984c 100644 --- a/lib/leap_cli/commands/deploy.rb +++ b/lib/leap_cli/commands/deploy.rb @@ -6,7 +6,7 @@ module LeapCli arg_name '' command :deploy do |c| c.action do |global_options,options,args| - nodes = ConfigManager.filter(args) + nodes = manager.filter(args) say "Deploying to these nodes: #{nodes.keys.join(', ')}" if agree "Continue? " say "deploy not yet implemented" diff --git a/lib/leap_cli/commands/list.rb b/lib/leap_cli/commands/list.rb index a186049..166ed2a 100644 --- a/lib/leap_cli/commands/list.rb +++ b/lib/leap_cli/commands/list.rb @@ -48,11 +48,11 @@ module LeapCli command :list do |c| c.action do |global_options,options,args| if args.any? - print_config_table(:nodes, ConfigManager.filter(args)) + print_config_table(:nodes, manager.filter(args)) else - print_config_table(:services, ConfigManager.services) - print_config_table(:tags, ConfigManager.tags) - print_config_table(:nodes, ConfigManager.nodes) + print_config_table(:services, manager.services) + print_config_table(:tags, manager.tags) + print_config_table(:nodes, manager.nodes) end end end diff --git a/lib/leap_cli/config.rb b/lib/leap_cli/config.rb deleted file mode 100644 index 44e66be..0000000 --- a/lib/leap_cli/config.rb +++ /dev/null @@ -1,119 +0,0 @@ -module LeapCli - # - # This class represents the configuration for a single node, service, or tag. - # - class Config < Hash - - def initialize(config_type, manager) - @manager = manager - @type = config_type - end - - # - # lazily eval dynamic values when we encounter them. - # - def [](key) - value = fetch(key, nil) - if value.is_a? Array - value - elsif value.nil? - nil - else - if value =~ /^= (.*)$/ - value = eval($1) - self[key] = value - end - value - end - end - - # - # make the type appear to be a normal Hash in yaml. - # - def to_yaml_type - "!map" - end - - # - # just like Hash#to_yaml, but sorted - # - def to_yaml(opts = {}) - YAML::quick_emit(self, opts) do |out| - out.map(taguri, to_yaml_style) do |map| - keys.sort.each do |k| - v = self.fetch(k) - map.add(k, v) - end - end - end - end - - # - # make obj['name'] available as obj.name - # - def method_missing(method, *args, &block) - if has_key?(method.to_s) - self[method.to_s] - else - super - end - end - - # - # convert self into a plain hash, but only include the specified keys - # - def to_h(*keys) - keys.map(&:to_s).inject({}) do |hsh, key| - if has_key?(key) - hsh[key] = self[key] - end - hsh - end - end - - def nodes - if @type == :node - @manager.nodes - else - @nodes ||= ConfigList.new - end - end - - def services - if @type == :node - self['services'] || [] - else - @manager.services - end - end - - def tags - if @type == :node - self['tags'] || [] - else - @manager.tags - end - end - - private - - ## - ## MACROS - ## these are methods used when eval'ing a value in the .json configuration - ## - - # - # inserts the contents of a file - # - def file(filename) - filepath = Path.find_file(name, filename) - if filepath - File.read(filepath) - else - log0('no such file, "%s"' % filename) - "" - end - end - - end # class -end # module \ No newline at end of file diff --git a/lib/leap_cli/config/base.rb b/lib/leap_cli/config/base.rb new file mode 100644 index 0000000..c7f4bc9 --- /dev/null +++ b/lib/leap_cli/config/base.rb @@ -0,0 +1,149 @@ +module LeapCli + module Config + # + # This class represents the configuration for a single node, service, or tag. + # + class Base < Hash + + def initialize(manager=nil, node=nil) + @manager = manager + @node = node || self + end + + ## + ## FETCHING VALUES + ## + + # + # lazily eval dynamic values when we encounter them. + # + def [](key) + value = fetch(key, nil) + if value.is_a? Array + value + elsif value.nil? + nil + else + if value =~ /^= (.*)$/ + begin + value = eval($1) + self[key] = value + rescue Exception => exc + puts "Eval error in '#{name}'" + puts " string: #{$1}" + puts " error: #{exc}" + end + end + value + end + end + + def name + @node['name'] + end + + # + # make hash addressable like an object (e.g. obj['name'] available as obj.name) + # + def method_missing(method, *args, &block) + method = method.to_s + if self.has_key?(method) + self[method] + elsif @node != self + @node.send(method) # send call up the tree... + else + raise NoMethodError.new(method) + end + end + + # + # a deep (recursive) merge with another hash or node. + # + def deep_merge!(hsh) + hsh.each do |key,new_value| + old_value = self[key] + if old_value.is_a?(Hash) || new_value.is_a?(Hash) + # merge hashes + value = Base.new(@manager, @node) + old_value.is_a?(Hash) ? value.deep_merge!(old_value) : (value[key] = old_value if old_value.any?) + new_value.is_a?(Hash) ? value.deep_merge!(new_value) : (value[key] = new_value if new_value.any?) + elsif old_value.is_a?(Array) || new_value.is_a?(Array) + # merge arrays + value = [] + old_value.is_a?(Array) ? value += old_value : value << old_value + new_value.is_a?(Array) ? value += new_value : value << new_value + value.compact! + elsif new_value.nil? + value = old_value + elsif old_value.nil? + value = new_value + elsif old_value.is_a?(Boolean) && new_value.is_a?(Boolean) + value = new_value + elsif old_value.class != new_value.class + raise 'Type mismatch. Cannot merge %s with %s. Key value is %s, name is %s.' % [old_value.class, new_value.class, key, name] + else + value = new_value + end + self[key] = value + end + self + end + + #def deep_merge!(new_node) + # new_node.each do |key, value| + # if value.is_a? self.class + # value = Base.new(@manager, @node).deep_merge!(value) + # self[key] = new_node[key] + # end + # self + #end + + # + # like a normal deep_merge, but replace any hash it encounters with a Config::Base + # + #def deep_merge(other_hash) + # p [self['name'], other_hash['name']] + # self.merge(other_hash) do |key, oldval, newval| + # oldval = oldval.to_hash if oldval.respond_to?(:to_hash) + # newval = newval.to_hash if newval.respond_to?(:to_hash) + # p key + # p oldval.class + # p newval.class + # if oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' + # oldval.deep_merge(newval) + # elsif newval.class.to_s == 'Hash' + # p key + # Base.new(@manager, node).replace(newval) + # else + # newval + # end + # end + #end + # + #def deep_merge!(other_hash) + # replace(deep_merge(other_hash)) + #end + + private + + ## + ## MACROS + ## these are methods used when eval'ing a value in the .json configuration + ## + + # + # inserts the contents of a file + # + def file(filename) + filepath = Path.find_file(name, filename) + if filepath + File.read(filepath) + else + log0('no such file, "%s"' % filename) + "" + end + end + + end # class + end # module +end # module \ No newline at end of file diff --git a/lib/leap_cli/config/list.rb b/lib/leap_cli/config/list.rb new file mode 100644 index 0000000..28ef499 --- /dev/null +++ b/lib/leap_cli/config/list.rb @@ -0,0 +1,81 @@ +module LeapCli + module Config + class List < Hash + + def initialize(config=nil) + if config + self << config + end + end + + # + # if the key is a hash, we treat it as a condition and filter all the configs using the condition + # + # for example: + # + # nodes[:public_dns => true] + # + # will return a ConfigList with node configs that have public_dns set to true + # + def [](key) + if key.is_a? Hash + results = List.new + field, match_value = key.to_a.first + field = field.is_a?(Symbol) ? field.to_s : field + match_value = match_value.is_a?(Symbol) ? match_value.to_s : match_value + each do |name, config| + value = config[field] + if !value.nil? + if value.is_a? Array + if value.includes?(match_value) + results[name] = config + end + else + if value == match_value + results[name] = config + end + end + end + end + results + else + super + end + end + + def <<(config) + if config.is_a? Config::List + self.deep_merge!(config) + elsif config['name'] + self[config['name']] = config + else + raise ArgumentError.new('argument must be a Config::Base or a Config::List') + end + end + + # + # converts the hash of configs into an array of hashes, with ONLY the specified fields + # + def fields(*fields) + result = [] + keys.sort.each do |name| + result << self[name].pick(*fields) + end + result + end + + # + # like fields(), but returns an array of values instead of an array of hashes. + # + def field(field) + field = field.to_s + result = [] + keys.sort.each do |name| + result << self[name][field] + end + result + end + + end + end +end diff --git a/lib/leap_cli/config/manager.rb b/lib/leap_cli/config/manager.rb new file mode 100644 index 0000000..b1ea68c --- /dev/null +++ b/lib/leap_cli/config/manager.rb @@ -0,0 +1,219 @@ +require 'oj' +require 'yaml' + +module LeapCli + module Config + class Manager + + attr_reader :services, :tags, :nodes + + ## + ## IMPORT EXPORT + ## + + # + # load .json configuration files + # + def load(dir) + @services = load_all_json("#{dir}/services/*.json", :tag) + @tags = load_all_json("#{dir}/tags/*.json", :tag) + @common = load_all_json("#{dir}/common.json", :tag)['common'] + @nodes = load_all_json("#{dir}/nodes/*.json", :node) + @nodes.each do |name, node| + @nodes[name] = apply_inheritance(node) + 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 = Config::List.new + filters.each do |filter| + if filter =~ /^\+/ + keep_list = nodes_for_name(filter[1..-1]) + node_list.delete_if do |name, node| + if keep_list[name] + false + else + true + end + end + else + node_list << nodes_for_name(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 = Config::List.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 + config = config_type == :node ? Node.new(self) : Tag.new(self) + config.deep_merge!(hash) + return config + 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.replace(input) + # output + # end + + # + # makes this node inherit options from the common, service, and tag json files. + # + # - takes a hash + # - returns a Node object. + # + def apply_inheritance(node) + new_hash = Node.new(self) + #new_node = Node.new(self) + + # inherit from common + new_hash.deep_merge!(@common) + + # inherit from services + 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_hash.deep_merge!(service) + service.nodes << new_hash + end + end + end + + # inherit from tags + 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_hash.deep_merge!(tag) + tag.nodes << new_hash + end + end + end + + # inherit from node + new_hash.deep_merge!(node) + + # typecast full hash tree to type Node + #new_node.clone_from_plain_hash!(new_hash) + + return new_hash + end + + # + # returns a set of nodes corresponding to a single name, where name could be a node name, service name, or tag name. + # + def nodes_for_name(name) + if node = self.nodes[name] + Config::List.new(node) + elsif service = self.services[name] + service.nodes + elsif tag = self.tags[name] + tag.nodes + end + end + + end + end +end diff --git a/lib/leap_cli/config/node.rb b/lib/leap_cli/config/node.rb new file mode 100644 index 0000000..5389b44 --- /dev/null +++ b/lib/leap_cli/config/node.rb @@ -0,0 +1,19 @@ +module LeapCli + module Config + class Node < Base + + def nodes + @manager.nodes + end + + def services + self['services'] || [] + end + + def tags + self['tags'] || [] + end + + end + end +end diff --git a/lib/leap_cli/config/tag.rb b/lib/leap_cli/config/tag.rb new file mode 100644 index 0000000..25c7246 --- /dev/null +++ b/lib/leap_cli/config/tag.rb @@ -0,0 +1,19 @@ +module LeapCli + module Config + class Tag < Base + + def nodes + @nodes ||= Config::List.new + end + + def services + @manager.services + end + + def tags + @manager.tags + end + + end + end +end diff --git a/lib/leap_cli/config_list.rb b/lib/leap_cli/config_list.rb deleted file mode 100644 index c8ff23b..0000000 --- a/lib/leap_cli/config_list.rb +++ /dev/null @@ -1,77 +0,0 @@ -module LeapCli - class ConfigList < Hash - - def initialize(config=nil) - if config - self << config - end - end - - # - # if the key is a hash, we treat it as a condition and filter all the configs using the condition - # - # for example: - # - # nodes[:public_dns => true] - # - # will return a ConfigList with node configs that have public_dns set to true - # - def [](key) - if key.is_a? Hash - results = ConfigList.new - field, match_value = key.to_a.first - field = field.is_a?(Symbol) ? field.to_s : field - match_value = match_value.is_a?(Symbol) ? match_value.to_s : match_value - each do |name, config| - value = config[field] - if !value.nil? - if value.is_a? Array - if value.includes?(match_value) - results[name] = config - end - else - if value == match_value - results[name] = config - end - end - end - end - results - else - super - end - end - - def <<(config) - if config.is_a? ConfigList - self.merge!(config) - else - self[config['name']] = config - end - end - - # - # converts the hash of configs into an array of hashes, with ONLY the specified fields - # - def fields(*fields) - result = [] - keys.sort.each do |name| - result << self[name].to_h(*fields) - end - result - end - - # - # like fields(), but returns an array of values instead of an array of hashes. - # - def field(field) - field = field.to_s - result = [] - keys.sort.each do |name| - result << self[name][field] - end - result - end - - end -end diff --git a/lib/leap_cli/config_manager.rb b/lib/leap_cli/config_manager.rb deleted file mode 100644 index d383cc1..0000000 --- a/lib/leap_cli/config_manager.rb +++ /dev/null @@ -1,200 +0,0 @@ -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 -- cgit v1.2.3