diff options
author | Luke Kanies <luke@madstop.com> | 2007-09-23 19:04:31 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2007-09-23 19:04:31 -0500 |
commit | cdc8ea6e81c1b5eba5ea784bb7079c4c1f3965a4 (patch) | |
tree | 6dab031628ee4c8269c93cd96ed646bdd2874cc4 /lib | |
parent | c40da335123ee839294b37134d1e6361000bf216 (diff) | |
download | puppet-cdc8ea6e81c1b5eba5ea784bb7079c4c1f3965a4.tar.gz puppet-cdc8ea6e81c1b5eba5ea784bb7079c4c1f3965a4.tar.xz puppet-cdc8ea6e81c1b5eba5ea784bb7079c4c1f3965a4.zip |
Taking a first stab at moving configuration compiling
into the indirection system. There are still quite
a few unanswered questions, the two most notable
being embodied in unimplemented tests in the Configuration
Code terminus.
This also requires changing the behaviour in a few places.
In particular, 'puppet' and the 'module_puppet' cfengine
module need to store a Node object in memory with the appropriate
classes, since that's now the only way to communicate with
the compiler. That integration work has not yet been done,
partially because the old configuration handler (which the
soon-to-be-deprecated master handler now uses) still exists.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/puppet/indirector.rb | 2 | ||||
-rw-r--r-- | lib/puppet/indirector/code.rb | 6 | ||||
-rw-r--r-- | lib/puppet/indirector/code/configuration.rb | 171 | ||||
-rw-r--r-- | lib/puppet/node/configuration.rb | 4 | ||||
-rw-r--r-- | lib/puppet/util/settings.rb | 20 |
5 files changed, 193 insertions, 10 deletions
diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index bd2487e33..6ff2de1b4 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -16,7 +16,7 @@ module Puppet::Indirector # evaluated at parse time, which is before the user has had a chance # to override it. def indirects(indirection) - raise(ArgumentError, "Already handling indirection for %s; cannot also handle %s" % [@indirection.name, indirection]) if defined?(@indirection) and indirection + raise(ArgumentError, "Already handling indirection for %s; cannot also handle %s" % [@indirection.name, indirection]) if defined?(@indirection) and @indirection # populate this class with the various new methods extend ClassMethods include InstanceMethods diff --git a/lib/puppet/indirector/code.rb b/lib/puppet/indirector/code.rb new file mode 100644 index 000000000..0c0ee146b --- /dev/null +++ b/lib/puppet/indirector/code.rb @@ -0,0 +1,6 @@ +require 'puppet/indirector/terminus' + +# Do nothing, requiring that the back-end terminus do all +# of the work. +class Puppet::Indirector::Code < Puppet::Indirector::Terminus +end diff --git a/lib/puppet/indirector/code/configuration.rb b/lib/puppet/indirector/code/configuration.rb new file mode 100644 index 000000000..6d0317204 --- /dev/null +++ b/lib/puppet/indirector/code/configuration.rb @@ -0,0 +1,171 @@ +require 'puppet/node' +require 'puppet/node/configuration' +require 'puppet/indirector/code' +require 'puppet/parser/interpreter' +require 'yaml' + +class Puppet::Indirector::Code::Configuration < Puppet::Indirector::Code + desc "Puppet's configuration compilation interface. Passed a node name + or other key, retrieves information about the node (using the ``node_source``) + and returns a compiled configuration." + + include Puppet::Util + + attr_accessor :code + + # Compile a node's configuration. + def find(key, client = nil, clientip = nil) + # If we want to use the cert name as our key + if Puppet[:node_name] == 'cert' and client + key = client + end + + # Note that this is reasonable, because either their node source should actually + # know about the node, or they should be using the ``none`` node source, which + # will always return data. + unless node = Puppet::Node.search(key) + raise Puppet::Error, "Could not find node '%s'" % key + end + + # Add any external data to the node. + add_node_data(node) + + configuration = compile(node) + + return configuration + end + + def initialize + set_server_facts + end + + # Create/return our interpreter. + def interpreter + unless defined?(@interpreter) and @interpreter + @interpreter = create_interpreter + end + @interpreter + end + + # Return the configuration version. + def version(client = nil, clientip = nil) + if client and node = Puppet::Node.search(client) + update_node_check(node) + return interpreter.configuration_version(node) + else + # Just return something that will always result in a recompile, because + # this is local. + return (Time.now + 1000).to_i + end + end + + private + + # Add any extra data necessary to the node. + def add_node_data(node) + # Merge in our server-side facts, so they can be used during compilation. + node.merge(@server_facts) + end + + # Compile the actual configuration. + def compile(node) + # Ask the interpreter to compile the configuration. + str = "Compiled configuration for %s" % node.name + if node.environment + str += " in environment %s" % node.environment + end + config = nil + + # LAK:FIXME This should log at :none when our client is + # local, since we don't want 'puppet' (vs. puppetmasterd) to + # log compile times. + benchmark(:notice, "Compiled configuration for %s" % node.name) do + begin + config = interpreter.compile(node) + rescue Puppet::Error => detail + if Puppet[:trace] + puts detail.backtrace + end + unless local? + Puppet.err detail.to_s + end + raise XMLRPC::FaultException.new( + 1, detail.to_s + ) + end + end + + return config + end + + # Create our interpreter object. + def create_interpreter + args = {} + + # Allow specification of a code snippet or of a file + if self.code + args[:Code] = self.code + end + + # LAK:FIXME This needs to be handled somehow. + #if options.include?(:UseNodes) + # args[:UseNodes] = options[:UseNodes] + #elsif @local + # args[:UseNodes] = false + #end + + return Puppet::Parser::Interpreter.new(args) + end + + # Initialize our server fact hash; we add these to each client, and they + # won't change while we're running, so it's safe to cache the values. + def set_server_facts + @server_facts = {} + + # Add our server version to the fact list + @server_facts["serverversion"] = Puppet.version.to_s + + # And then add the server name and IP + {"servername" => "fqdn", + "serverip" => "ipaddress" + }.each do |var, fact| + if value = Facter.value(fact) + @server_facts[var] = value + else + Puppet.warning "Could not retrieve fact %s" % fact + end + end + + if @server_facts["servername"].nil? + host = Facter.value(:hostname) + if domain = Facter.value(:domain) + @server_facts["servername"] = [host, domain].join(".") + else + @server_facts["servername"] = host + end + end + end + + # Translate our configuration appropriately for sending back to a client. + # LAK:FIXME This method should probably be part of the protocol, but it + # shouldn't be here. + def translate(config) + if local? + config + else + CGI.escape(config.to_yaml(:UseBlock => true)) + end + end + + # Mark that the node has checked in. LAK:FIXME this needs to be moved into + # the Node class, or somewhere that's got abstract backends. + def update_node_check(node) + if Puppet.features.rails? and Puppet[:storeconfigs] + Puppet::Rails.connect + + host = Puppet::Rails::Host.find_or_create_by_name(node.name) + host.last_freshcheck = Time.now + host.save + end + end +end diff --git a/lib/puppet/node/configuration.rb b/lib/puppet/node/configuration.rb index 0ae03a651..53f63d003 100644 --- a/lib/puppet/node/configuration.rb +++ b/lib/puppet/node/configuration.rb @@ -1,3 +1,4 @@ +require 'puppet/indirector' require 'puppet/external/gratr/digraph' # This class models a node configuration. It is the thing @@ -5,6 +6,9 @@ require 'puppet/external/gratr/digraph' # of the information in the configuration, including the resources # and the relationships between them. class Puppet::Node::Configuration < Puppet::PGraph + extend Puppet::Indirector + indirects :configuration + # The host name this is a configuration for. attr_accessor :name diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index f2af13dc2..1478cd8a5 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -45,15 +45,17 @@ class Puppet::Util::Settings end # A simplified equality operator. - def ==(other) - self.each { |myname, myobj| - unless other[myname] == value(myname) - return false - end - } - - return true - end + # LAK: For some reason, this causes mocha to not be able to mock + # the 'value' method, and it's not used anywhere. +# def ==(other) +# self.each { |myname, myobj| +# unless other[myname] == value(myname) +# return false +# end +# } +# +# return true +# end # Generate the list of valid arguments, in a format that GetoptLong can # understand, and add them to the passed option list. |