aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2012-12-07 15:53:41 -0800
committerelijah <elijah@riseup.net>2012-12-07 15:53:41 -0800
commitf511984870bd372170de3a5547f5bb5cf31788e3 (patch)
treee7b6b7e2ecfa1628c467160bdd448de811d0a175
parent5a5879c51afc128ea723443de26458ebdc645c6a (diff)
downloadleap_cli-f511984870bd372170de3a5547f5bb5cf31788e3.tar.gz
leap_cli-f511984870bd372170de3a5547f5bb5cf31788e3.tar.bz2
added late evaluation of some keys, and hostname tracking (node tracks all the hostnames it has encountered).
-rw-r--r--lib/leap_cli/config/object.rb148
1 files changed, 116 insertions, 32 deletions
diff --git a/lib/leap_cli/config/object.rb b/lib/leap_cli/config/object.rb
index 6f0342e..bbaa6f4 100644
--- a/lib/leap_cli/config/object.rb
+++ b/lib/leap_cli/config/object.rb
@@ -32,11 +32,14 @@ module LeapCli
@node_list = Config::ObjectList.new
end
+ #
# We use pure ruby yaml exporter ya2yaml instead of SYCK or PSYCH because it
# allows us greater compatibility regardless of installed ruby version and
- # greater control over how the yaml is exported.
+ # greater control over how the yaml is exported (sorted keys, in particular).
#
def dump
+ self.evaluate_everything
+ self.late_evaluate_everything
self.ya2yaml(:syck_compatible => true)
end
@@ -87,7 +90,7 @@ module LeapCli
value
end
elsif self.has_key?(key)
- evaluate_value(key)
+ evaluate_key(key)
else
raise NoMethodError.new(key, "No method '#{key}' for #{self.class}")
end
@@ -269,49 +272,130 @@ module LeapCli
"SHA256: " + X509.fingerprint("SHA256", Path.named_path(filename))
end
+ #
+ # records the list of hosts that are encountered for this node
+ #
+ def hostnames(nodes)
+ @referenced_nodes ||= ObjectList.new
+ if nodes.is_a? Config::Object
+ nodes = ObjectList.new nodes
+ end
+ nodes.each_node do |node|
+ @referenced_nodes[node.name] = node
+ end
+ nodes.values.collect do |node|
+ node.domain.name
+ end
+ end
+
+ #
+ # generates entries needed for updating /etc/hosts on a node, but only including the IPs of the
+ # other nodes we have encountered.
+ #
+ def hosts_file
+ return nil unless @referenced_nodes
+ entries = []
+ @referenced_nodes.each_node do |node|
+ entries << "#{node.ip_address} #{node.name} #{node.domain.internal} #{node.domain.full}"
+ end
+ {'leap_hosts' => entries}
+ end
+
+ def known_hosts_file
+ return nil unless @referenced_nodes
+ entries = []
+ @referenced_nodes.each_node do |node|
+ hostnames = [node.name, node.domain.internal, node.domain.full, node.ip_address].join(',')
+ pub_key = Util::read_file([:node_ssh_pub_key,node.name])
+ if pub_key
+ entries << [hostnames, pub_key].join(' ')
+ end
+ end
+ entries.join("\n")
+ end
+
+ protected
+
+ #
+ # walks the object tree, eval'ing all the attributes that are dynamic ruby (e.g. value starts with '= ')
+ #
+ def evaluate_everything
+ keys.each do |key|
+ obj = evaluate_key(key)
+ if obj.is_a? Config::Object
+ obj.evaluate_everything
+ end
+ end
+ end
+
+ #
+ # some keys need to be evaluated 'late', after all the other keys have been evaluated.
+ #
+ def late_evaluate_everything
+ if @late_eval_list
+ @late_eval_list.each do |key, value|
+ self[key] = evaluate_now(key, value)
+ end
+ end
+ values.each do |obj|
+ if obj.is_a? Config::Object
+ obj.late_evaluate_everything
+ end
+ end
+ end
+
private
#
# fetches the value for the key, evaluating the value as ruby if it begins with '='
#
- def evaluate_value(key)
+ def evaluate_key(key)
value = fetch(key, nil)
- if value.is_a? Array
+ if !value.is_a?(String)
value
- elsif value.nil?
- nil
else
- if value =~ /^= (.*)$/
- begin
- value = @node.instance_eval($1) #, @node.send(:binding))
- self[key] = value
- rescue SystemStackError => exc
- log 0, :error, "while evaluating node '#{@node.name}'"
- log 0, "offending string: #{$1}", :indent => 1
- log 0, "STACK OVERFLOW, BAILING OUT. There must be an eval loop of death (variables with circular dependencies).", :indent => 1
- raise SystemExit.new()
- rescue FileMissing => exc
- Util::bail! do
- if exc.options[:missing]
- log :missing, exc.options[:missing].gsub('$node', @node.name)
- else
- log :error, "while evaluating node '#{@node.name}'"
- log "offending string: #{$1}", :indent => 1
- log "error message: no file '#{exc}'", :indent => 1
- end
- end
- rescue SyntaxError, StandardError => exc
- Util::bail! do
- log :error, "while evaluating node '#{@node.name}'"
- log "offending string: #{$1}", :indent => 1
- log "error message: #{exc}", :indent => 1
- end
- end
+ if value =~ /^=> (.*)$/
+ value = evaluate_later(key, $1)
+ elsif value =~ /^= (.*)$/
+ value = evaluate_now(key, $1)
end
+ self[key] = value
value
end
end
+ def evaluate_later(key, value)
+ @late_eval_list ||= []
+ @late_eval_list << [key, value]
+ '<evaluate later>'
+ end
+
+ def evaluate_now(key, value)
+ return @node.instance_eval(value)
+ rescue SystemStackError => exc
+ Util::log 0, :error, "while evaluating node '#{@node.name}'"
+ Util::log 0, "offending string: #{$1}", :indent => 1
+ Util::log 0, "STACK OVERFLOW, BAILING OUT. There must be an eval loop of death (variables with circular dependencies).", :indent => 1
+ raise SystemExit.new()
+ rescue FileMissing => exc
+ Util::bail! do
+ if exc.options[:missing]
+ Util::log :missing, exc.options[:missing].gsub('$node', @node.name)
+ else
+ Util::log :error, "while evaluating node '#{@node.name}'"
+ Util::log "offending string: #{$1}", :indent => 1
+ Util::log "error message: no file '#{exc}'", :indent => 1
+ end
+ end
+ rescue SyntaxError, StandardError => exc
+ Util::bail! do
+ Util::log exc.inspect
+ Util::log :error, "while evaluating node '#{@node.name}'"
+ Util::log "offending string: #{$1}", :indent => 1
+ Util::log "error message: #{exc}", :indent => 1
+ end
+ end
+
#
# Output json from ruby objects in such a manner that all the hashes and arrays are output in alphanumeric sorted order.
# This is required so that our generated configs don't throw puppet or git for a tizzy fit.