diff options
author | Rick Bradley <rick@rickbradley.com> | 2007-10-22 09:35:34 -0500 |
---|---|---|
committer | Rick Bradley <rick@rickbradley.com> | 2007-10-22 09:35:34 -0500 |
commit | b134f0ce465923a6b0b7f2855850e38599f0f176 (patch) | |
tree | 3d917856225c904f3496c2be145ddf84d04db666 /lib/puppet | |
parent | e69a50afbe0c031833c4247a962cfda0b5996d78 (diff) | |
parent | 08099b7a383987e292357f285e05933e10205660 (diff) | |
download | puppet-b134f0ce465923a6b0b7f2855850e38599f0f176.tar.gz puppet-b134f0ce465923a6b0b7f2855850e38599f0f176.tar.xz puppet-b134f0ce465923a6b0b7f2855850e38599f0f176.zip |
Merge branch 'master' of git://reductivelabs.com/puppet into routing
Conflicts:
spec/unit/indirector/indirection.rb
Diffstat (limited to 'lib/puppet')
26 files changed, 873 insertions, 33 deletions
diff --git a/lib/puppet/checksum.rb b/lib/puppet/checksum.rb index c607953c1..27f08aa99 100644 --- a/lib/puppet/checksum.rb +++ b/lib/puppet/checksum.rb @@ -3,11 +3,14 @@ # Copyright (c) 2007. All rights reserved. require 'puppet' +require 'puppet/util/checksums' require 'puppet/indirector' # A checksum class to model translating checksums to file paths. This # is the new filebucket. class Puppet::Checksum + include Puppet::Util::Checksums + extend Puppet::Indirector indirects :checksum @@ -27,25 +30,20 @@ class Puppet::Checksum # Calculate (if necessary) and return the checksum def checksum unless @checksum - @checksum = send(algorithm) + @checksum = send(algorithm, content) end @checksum end - def initialize(content, algorithm = nil) + def initialize(content, algorithm = "md5") raise ArgumentError.new("You must specify the content") unless content @content = content - self.algorithm = algorithm || "md5" # Init to avoid warnings. @checksum = nil - end - # This can't be private, else respond_to? returns false. - def md5 - require 'digest/md5' - Digest::MD5.hexdigest(content) + self.algorithm = algorithm end # This is here so the Indirector::File terminus works correctly. @@ -53,11 +51,6 @@ class Puppet::Checksum checksum end - def sha1 - require 'digest/sha1' - Digest::SHA1.hexdigest(content) - end - def to_s "Checksum<{%s}%s>" % [algorithm, checksum] end diff --git a/lib/puppet/file_serving.rb b/lib/puppet/file_serving.rb new file mode 100644 index 000000000..e7e2b898e --- /dev/null +++ b/lib/puppet/file_serving.rb @@ -0,0 +1,7 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +# Just a stub class. +class Puppet::FileServing # :nodoc: +end diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb new file mode 100644 index 000000000..03be1b9dd --- /dev/null +++ b/lib/puppet/file_serving/configuration.rb @@ -0,0 +1,120 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet' +require 'puppet/file_serving' +require 'puppet/file_serving/mount' + +class Puppet::FileServing::Configuration + require 'puppet/file_serving/configuration/parser' + + @config_fileuration = nil + + Mount = Puppet::FileServing::Mount + + # Remove our singleton instance. + def self.clear_cache + @config_fileuration = nil + end + + # Create our singleton configuration. + def self.create + unless @config_fileuration + @config_fileuration = new() + end + @config_fileuration + end + + private_class_method :new + + # Search for a file. + def file_path(key, options = {}) + mount, file_path = split_path(key, options[:node]) + + return nil unless mount + + # The mount checks to see if the file exists, and returns nil + # if not. + return mount.file(file_path, options) + end + + def initialize + @mounts = {} + @config_file = nil + + # We don't check to see if the file is modified the first time, + # because we always want to parse at first. + readconfig(false) + end + + # Is a given mount available? + def mounted?(name) + @mounts.include?(name) + end + + def umount(name) + @mounts.delete(name) if @mounts.include? name + end + + private + + # Deal with ignore parameters. + def handleignore(children, path, ignore) + ignore.each { |ignore| + Dir.glob(File.join(path,ignore), File::FNM_DOTMATCH) { |match| + children.delete(File.basename(match)) + } + } + return children + end + + # Read the configuration file. + def readconfig(check = true) + config = Puppet[:fileserverconfig] + + return unless FileTest.exists?(config) + + @parser ||= Puppet::FileServing::Configuration::Parser.new(config) + + if check and ! @parser.changed? + return + end + + begin + newmounts = @parser.parse + @mounts = newmounts + rescue => detail + Puppet.err "Error parsing fileserver configuration: %s; using old configuration" % detail + end + end + + # Split the path into the separate mount point and path. + def split_path(uri, node) + # Reparse the configuration if necessary. + readconfig + + raise(ArgumentError, "Cannot find file: Invalid path '%s'" % uri) unless uri =~ %r{/([-\w]+)/?} + + # the dir is based on one of the mounts + # so first retrieve the mount path + mount = path = nil + # Strip off the mount name. + mount_name, path = uri.sub(%r{^/}, '').split(File::Separator, 2) + + return nil unless mount = @mounts[mount_name] + + if path == "" + path = nil + elsif path + # Remove any double slashes that might have occurred + path = URI.unescape(path.gsub(/\/\//, "/")) + end + + return mount, path + end + + def to_s + "fileserver" + end +end diff --git a/lib/puppet/file_serving/configuration/parser.rb b/lib/puppet/file_serving/configuration/parser.rb new file mode 100644 index 000000000..707c3f9b1 --- /dev/null +++ b/lib/puppet/file_serving/configuration/parser.rb @@ -0,0 +1,124 @@ +require 'puppet/file_serving/configuration' +require 'puppet/util/loadedfile' + +class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile + Mount = Puppet::FileServing::Mount + MODULES = 'modules' + + # Parse our configuration file. + def parse + raise("File server configuration %s does not exist" % self.file) unless FileTest.exists?(self.file) + raise("Cannot read file server configuration %s" % self.file) unless FileTest.readable?(self.file) + + @mounts = {} + @count = 0 + + File.open(self.file) { |f| + mount = nil + f.each { |line| + # Have the count increment at the top, in case we throw exceptions. + @count += 1 + + case line + when /^\s*#/: next # skip comments + when /^\s*$/: next # skip blank lines + when /\[([-\w]+)\]/: + mount = newmount($1) + when /^\s*(\w+)\s+(.+)$/: + var = $1 + value = $2 + raise(ArgumentError, "Fileserver configuration file does not use '=' as a separator") if value =~ /^=/ + case var + when "path": + path(mount, value) + when "allow": + allow(mount, value) + when "deny": + deny(mount, value) + else + raise ArgumentError.new("Invalid argument '%s'" % var, + @count, file) + end + else + raise ArgumentError.new("Invalid line '%s'" % line.chomp, + @count, file) + end + } + } + + return @mounts + end + + private + + # Add the mount for getting files from modules. + def add_module_mount + unless @mounts[MODULES] + mount = Mount.new(MODULES) + mount.allow("*") + @mounts[MODULES] = mount + end + end + + # Allow a given pattern access to a mount. + def allow(mount, value) + value.split(/\s*,\s*/).each { |val| + begin + mount.info "allowing %s access" % val + mount.allow(val) + rescue AuthStoreError => detail + raise ArgumentError.new(detail.to_s, + @count, file) + end + } + end + + # Deny a given pattern access to a mount. + def deny(mount, value) + value.split(/\s*,\s*/).each { |val| + begin + mount.info "denying %s access" % val + mount.deny(val) + rescue AuthStoreError => detail + raise ArgumentError.new(detail.to_s, + @count, file) + end + } + end + + # Create a new mount. + def newmount(name) + if @mounts.include?(name) + raise ArgumentError, "%s is already mounted at %s" % + [@mounts[name], name], @count, file + end + mount = Mount.new(name) + @mounts[name] = mount + return mount + end + + # Set the path for a mount. + def path(mount, value) + if mount.name == MODULES + Puppet.warning "The '#{MODULES}' module can not have a path. Ignoring attempt to set it" + else + begin + mount.path = value + rescue ArgumentError => detail + Puppet.err "Removing mount %s: %s" % + [mount.name, detail] + @mounts.delete(mount.name) + end + end + end + + # Make sure all of our mounts are valid. We have to do this after the fact + # because details are added over time as the file is parsed. + def validate + @mounts.each { |name, mount| + unless mount.valid? + raise ArgumentError, "No path specified for mount %s" % name + end + } + end +end diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb new file mode 100644 index 000000000..38ca80fb0 --- /dev/null +++ b/lib/puppet/file_serving/content.rb @@ -0,0 +1,36 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/indirector' +require 'puppet/file_serving' +require 'puppet/file_serving/terminus_selector' + +# A class that handles retrieving file contents. +# It only reads the file when its content is specifically +# asked for. +class Puppet::FileServing::Content + extend Puppet::Indirector + indirects :file_content, :extend => Puppet::FileServing::TerminusSelector + + attr_reader :path + + def content + ::File.read(@path) + end + + def initialize(path) + raise ArgumentError.new("Files must be fully qualified") unless path =~ /^#{::File::SEPARATOR}/ + raise ArgumentError.new("Files must exist") unless FileTest.exists?(path) + + @path = path + end + + # Just return the file contents as the yaml. This allows us to + # avoid escaping or any such thing. LAK:FIXME Not really sure how + # this will behave if the file contains yaml... I think the far + # side needs to understand that it's a plain string. + def to_yaml + content + end +end diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb new file mode 100644 index 000000000..7adb66981 --- /dev/null +++ b/lib/puppet/file_serving/metadata.rb @@ -0,0 +1,54 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet' +require 'puppet/indirector' +require 'puppet/file_serving' +require 'puppet/util/checksums' +require 'puppet/file_serving/terminus_selector' + +# A class that handles retrieving file metadata. +class Puppet::FileServing::Metadata + include Puppet::Util::Checksums + + extend Puppet::Indirector + indirects :file_metadata, :extend => Puppet::FileServing::TerminusSelector + + attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum + + def checksum_type=(type) + raise(ArgumentError, "Unsupported checksum type %s" % type) unless respond_to?("%s_file" % type) + + @checksum_type = type + end + + def get_attributes + stat = File.stat(path) + @owner = stat.uid + @group = stat.gid + + # Set the octal mode, but as a string. + @mode = "%o" % (stat.mode & 007777) + + @checksum = get_checksum + end + + def initialize(path = nil) + if path + raise ArgumentError.new("Files must be fully qualified") unless path =~ /^#{::File::SEPARATOR}/ + raise ArgumentError.new("Files must exist") unless FileTest.exists?(path) + + @path = path + end + + @checksum_type = "md5" + end + + private + + # Retrieve our checksum. + def get_checksum + ("{%s}" % @checksum_type) + send("%s_file" % @checksum_type, @path) + end +end diff --git a/lib/puppet/file_serving/mount.rb b/lib/puppet/file_serving/mount.rb new file mode 100644 index 000000000..8e5bd03e8 --- /dev/null +++ b/lib/puppet/file_serving/mount.rb @@ -0,0 +1,186 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/network/authstore' +require 'puppet/util/logging' +require 'puppet/file_serving' +require 'puppet/file_serving/metadata' +require 'puppet/file_serving/content' + +# Broker access to the filesystem, converting local URIs into metadata +# or content objects. +class Puppet::FileServing::Mount < Puppet::Network::AuthStore + include Puppet::Util::Logging + + @@localmap = nil + + # Clear the cache. This is only ever used for testing. + def self.clear_cache + @@localmap = nil + end + + attr_reader :name + + # Return a new mount with the same properties as +self+, except + # with a different name and path. + def copy(name, path) + result = self.clone + result.path = path + result.instance_variable_set(:@name, name) + return result + end + + # Return an instance of the appropriate class. + def file(short_file, options = {}) + file = file_path(short_file, options[:node]) + + return nil unless FileTest.exists?(file) + + return file + end + + # Return a fully qualified path, given a short path and + # possibly a client name. + def file_path(relative_path, node = nil) + full_path = path(node) + raise ArgumentError.new("Mounts without paths are not usable") unless full_path + + # If there's no relative path name, then we're serving the mount itself. + return full_path unless relative_path + + return File.join(full_path, relative_path) + end + + # Create out object. It must have a name. + def initialize(name, path = nil) + unless name =~ %r{^[-\w]+$} + raise ArgumentError, "Invalid mount name format '%s'" % name + end + @name = name + + if path + self.path = path + else + @path = nil + end + + super() + end + + # Return the path as appropriate, expanding as necessary. + def path(node = nil) + if expandable? + return expand(@path, node) + else + return @path + end + end + + # Set the path. + def path=(path) + # FIXME: For now, just don't validate paths with replacement + # patterns in them. + if path =~ /%./ + # Mark that we're expandable. + @expandable = true + else + unless FileTest.directory?(path) + raise ArgumentError, "%s does not exist or is not a directory" % path + end + unless FileTest.readable?(path) + raise ArgumentError, "%s is not readable" % path + end + @expandable = false + end + @path = path + end + + def sync(path) + @@syncs[path] ||= Sync.new + @@syncs[path] + end + + def to_s + "mount[%s]" % @name + end + + # Verify our configuration is valid. This should really check to + # make sure at least someone will be allowed, but, eh. + def valid? + return ! @path.nil? + end + + private + + # LAK:FIXME Move this method to the REST terminus hook. + def authcheck(file, client, clientip) + raise "This method should be replaced by a REST/terminus hook" + # If we're local, don't bother passing in information. + if local? + client = nil + clientip = nil + end + unless mount.allowed?(client, clientip) + mount.warning "%s cannot access %s" % + [client, file] + raise Puppet::AuthorizationError, "Cannot access %s" % mount + end + end + + # Create a map for a specific node. + def clientmap(node) + { + "h" => node.sub(/\..*$/, ""), + "H" => node, + "d" => node.sub(/[^.]+\./, "") # domain name + } + end + + # Replace % patterns as appropriate. + def expand(path, node = nil) + # This map should probably be moved into a method. + map = nil + + if node + map = clientmap(node) + else + Puppet.notice "No client; expanding '%s' with local host" % + path + # Else, use the local information + map = localmap() + end + + path.gsub(/%(.)/) do |v| + key = $1 + if key == "%" + "%" + else + map[key] || v + end + end + end + + # Do we have any patterns in our path, yo? + def expandable? + if defined? @expandable + @expandable + else + false + end + end + + # Cache this manufactured map, since if it's used it's likely + # to get used a lot. + def localmap + unless @@localmap + @@localmap = { + "h" => Facter.value("hostname"), + "H" => [Facter.value("hostname"), + Facter.value("domain")].join("."), + "d" => Facter.value("domain") + } + end + @@localmap + end +end diff --git a/lib/puppet/file_serving/terminus_selector.rb b/lib/puppet/file_serving/terminus_selector.rb new file mode 100644 index 000000000..08009cd2b --- /dev/null +++ b/lib/puppet/file_serving/terminus_selector.rb @@ -0,0 +1,38 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'uri' +require 'puppet/file_serving' + +# This module is used to pick the appropriate terminus +# in file-serving indirections. This is necessary because +# the terminus varies based on the URI asked for. +module Puppet::FileServing::TerminusSelector + PROTOCOL_MAP = {"puppet" => :rest, "file" => :local, "puppetmounts" => :mounts} + + # Pick an appropriate terminus based on the protocol. + def select_terminus(full_uri) + # Short-circuit to :local if it's a fully-qualified path. + return PROTOCOL_MAP["file"] if full_uri =~ /^#{::File::SEPARATOR}/ + begin + uri = URI.parse(URI.escape(full_uri)) + rescue => detail + raise ArgumentError, "Could not understand URI %s: %s" % [full_uri, detail.to_s] + end + + terminus = PROTOCOL_MAP[uri.scheme] || raise(ArgumentError, "URI protocol '%s' is not supported for file serving" % uri.scheme) + + # This provides a convenient mechanism for people to write configurations work + # well in both a networked and local setting. + if uri.host.nil? and uri.scheme == "puppet" and Puppet.settings[:name] == "puppet" + terminus = :mounts + end + + if uri.path =~ /^\/modules\b/ and terminus == :mounts + terminus = :modules + end + + return terminus + end +end diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index 6009a7ba7..c30c097b2 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -23,17 +23,7 @@ module Puppet::Indirector # instantiate the actual Terminus for that type and this name (:ldap, w/ args :node) # & hook the instantiated Terminus into this class (Node: @indirection = terminus) - @indirection = Puppet::Indirector::Indirection.new(self, indirection) - - unless options.empty? - options.each do |param, value| - case param - when :terminus_class: @indirection.terminus_class = value - else - raise ArgumenError, "Invalid option '%s' to 'indirects'" % param - end - end - end + @indirection = Puppet::Indirector::Indirection.new(self, indirection, options) @indirection end diff --git a/lib/puppet/indirector/file_content.rb b/lib/puppet/indirector/file_content.rb new file mode 100644 index 000000000..5261ddc05 --- /dev/null +++ b/lib/puppet/indirector/file_content.rb @@ -0,0 +1,5 @@ +require 'puppet/file_serving/content' + +# A stub class, so our constants work. +class Puppet::Indirector::FileContent # :nodoc: +end diff --git a/lib/puppet/indirector/file_content/local.rb b/lib/puppet/indirector/file_content/local.rb new file mode 100644 index 000000000..e429c6c25 --- /dev/null +++ b/lib/puppet/indirector/file_content/local.rb @@ -0,0 +1,23 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/util/uri_helper' +require 'puppet/indirector/file_content' +require 'puppet/indirector/file' + +class Puppet::Indirector::FileContent::Local < Puppet::Indirector::File + desc "Retrieve file contents from disk." + + include Puppet::Util::URIHelper + + def find(key, options = {}) + uri = key2uri(key) + + return nil unless FileTest.exists?(uri.path) + data = model.new(uri.path) + + return data + end +end diff --git a/lib/puppet/indirector/file_content/modules.rb b/lib/puppet/indirector/file_content/modules.rb new file mode 100644 index 000000000..8563dacb4 --- /dev/null +++ b/lib/puppet/indirector/file_content/modules.rb @@ -0,0 +1,11 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/indirector/file_content' +require 'puppet/indirector/module_files' + +class Puppet::Indirector::FileContent::Modules < Puppet::Indirector::ModuleFiles + desc "Retrieve file contents from modules." +end diff --git a/lib/puppet/indirector/file_content/mounts.rb b/lib/puppet/indirector/file_content/mounts.rb new file mode 100644 index 000000000..b11fc628c --- /dev/null +++ b/lib/puppet/indirector/file_content/mounts.rb @@ -0,0 +1,11 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/indirector/file_content' +require 'puppet/indirector/file_server' + +class Puppet::Indirector::FileContent::Mounts < Puppet::Indirector::FileServer + desc "Retrieve file contents using Puppet's fileserver." +end diff --git a/lib/puppet/indirector/file_content/rest.rb b/lib/puppet/indirector/file_content/rest.rb new file mode 100644 index 000000000..31df7626d --- /dev/null +++ b/lib/puppet/indirector/file_content/rest.rb @@ -0,0 +1,12 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/util/uri_helper' +require 'puppet/indirector/file_content' +require 'puppet/indirector/rest' + +class Puppet::Indirector::FileContent::Rest < Puppet::Indirector::REST + desc "Retrieve file contents via a REST HTTP interface." +end diff --git a/lib/puppet/indirector/file_metadata.rb b/lib/puppet/indirector/file_metadata.rb new file mode 100644 index 000000000..c43579d24 --- /dev/null +++ b/lib/puppet/indirector/file_metadata.rb @@ -0,0 +1,5 @@ +require 'puppet/file_serving/metadata' + +# A stub class, so our constants work. +class Puppet::Indirector::FileMetadata # :nodoc: +end diff --git a/lib/puppet/indirector/file_metadata/local.rb b/lib/puppet/indirector/file_metadata/local.rb new file mode 100644 index 000000000..f40d4ce43 --- /dev/null +++ b/lib/puppet/indirector/file_metadata/local.rb @@ -0,0 +1,24 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/metadata' +require 'puppet/indirector/file_metadata' +require 'puppet/util/uri_helper' +require 'puppet/indirector/code' + +class Puppet::Indirector::FileMetadata::Local < Puppet::Indirector::Code + desc "Retrieve file metadata directly from the local filesystem." + + include Puppet::Util::URIHelper + + def find(key) + uri = key2uri(key) + + return nil unless FileTest.exists?(uri.path) + data = model.new(uri.path) + data.get_attributes + + return data + end +end diff --git a/lib/puppet/indirector/file_metadata/modules.rb b/lib/puppet/indirector/file_metadata/modules.rb new file mode 100644 index 000000000..739c40fca --- /dev/null +++ b/lib/puppet/indirector/file_metadata/modules.rb @@ -0,0 +1,17 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/metadata' +require 'puppet/indirector/file_metadata' +require 'puppet/indirector/module_files' + +class Puppet::Indirector::FileMetadata::Modules < Puppet::Indirector::ModuleFiles + desc "Retrieve file metadata from modules." + + def find(*args) + return unless instance = super + instance.get_attributes + instance + end +end diff --git a/lib/puppet/indirector/file_metadata/mounts.rb b/lib/puppet/indirector/file_metadata/mounts.rb new file mode 100644 index 000000000..b1e3b32fd --- /dev/null +++ b/lib/puppet/indirector/file_metadata/mounts.rb @@ -0,0 +1,11 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/metadata' +require 'puppet/indirector/file_metadata' +require 'puppet/indirector/file_server' + +class Puppet::Indirector::FileMetadata::Mounts < Puppet::Indirector::FileServer + desc "Retrieve file metadata using Puppet's fileserver." +end diff --git a/lib/puppet/indirector/file_metadata/rest.rb b/lib/puppet/indirector/file_metadata/rest.rb new file mode 100644 index 000000000..0f3d9c6fd --- /dev/null +++ b/lib/puppet/indirector/file_metadata/rest.rb @@ -0,0 +1,12 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/metadata' +require 'puppet/util/uri_helper' +require 'puppet/indirector/file_metadata' +require 'puppet/indirector/rest' + +class Puppet::Indirector::FileMetadata::Rest < Puppet::Indirector::REST + desc "Retrieve file metadata via a REST HTTP interface." +end diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb new file mode 100644 index 000000000..1b2e047e8 --- /dev/null +++ b/lib/puppet/indirector/file_server.rb @@ -0,0 +1,34 @@ +# +# Created by Luke Kanies on 2007-10-19. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/util/uri_helper' +require 'puppet/file_serving/configuration' +require 'puppet/indirector/terminus' + +# Look files up using the file server. +class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus + include Puppet::Util::URIHelper + + # Find our key using the fileserver. + def find(key, options = {}) + uri = key2uri(key) + + # First try the modules mount, at least for now. + if instance = indirection.terminus(:modules).find(key, options) + Puppet.warning "DEPRECATION NOTICE: Found file in module without using the 'modules' mount; please fix" + return instance + end + + return nil unless path = configuration.file_path(uri.path, :node => options[:node]) and FileTest.exists?(path) + + return model.new(path) + end + + private + + # Our fileserver configuration, if needed. + def configuration + Puppet::FileServing::Configuration.create + end +end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 6930af494..81d960fbd 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -56,6 +56,19 @@ class Puppet::Indirector::Indirection def initialize(model, name, options = {}) @model = model @name = name + + @termini = {} + @cache_class = nil + + raise(ArgumentError, "Indirection %s is already defined" % @name) if @@indirections.find { |i| i.name == @name } + @@indirections << self + + if mod = options[:extend] + extend(mod) + options.delete(:extend) + end + + # This is currently only used for cache_class and terminus_class. options.each do |name, value| begin send(name.to_s + "=", value) @@ -63,11 +76,6 @@ class Puppet::Indirector::Indirection raise ArgumentError, "%s is not a valid Indirection parameter" % name end end - @termini = {} - @terminus_types = {} - @cache_class = nil - raise(ArgumentError, "Indirection %s is already defined" % @name) if @@indirections.find { |i| i.name == @name } - @@indirections << self end # Return the singleton terminus for this indirection. @@ -99,11 +107,24 @@ class Puppet::Indirector::Indirection end def find(key, *args) - if cache? and cache.has_most_recent?(key, terminus.version(key)) + # Select the appropriate terminus if there's a hook + # for doing so. This allows the caller to pass in some kind + # of URI that the indirection can use for routing to the appropriate + # terminus. + if respond_to?(:select_terminus) + terminus_name = select_terminus(key) + else + terminus_name = terminus_class + end + + # See if our instance is in the cache and up to date. + if cache? and cache.has_most_recent?(key, terminus(terminus_name).version(key)) Puppet.info "Using cached %s %s" % [self.name, key] return cache.find(key, *args) end - if result = terminus.find(key, *args) + + # Otherwise, return the result from the terminus, caching if appropriate. + if result = terminus(terminus_name).find(key, *args) result.version ||= Time.now.utc if cache? Puppet.info "Caching %s %s" % [self.name, key] diff --git a/lib/puppet/indirector/module_files.rb b/lib/puppet/indirector/module_files.rb new file mode 100644 index 000000000..e0374d7a4 --- /dev/null +++ b/lib/puppet/indirector/module_files.rb @@ -0,0 +1,47 @@ +# +# Created by Luke Kanies on 2007-10-19. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/util/uri_helper' +require 'puppet/indirector/terminus' + +# Look files up in Puppet modules. +class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus + include Puppet::Util::URIHelper + + # Find our key in a module. + def find(key, options = {}) + uri = key2uri(key) + + # Strip off /modules if it's there -- that's how requests get routed to this terminus. + # Also, strip off the leading slash if present. + module_name, relative_path = uri.path.sub(/^\/modules\b/, '').sub(%r{^/}, '').split(File::Separator, 2) + + # And use the environment to look up the module. + return nil unless mod = find_module(module_name, options[:node]) + + path = File.join(mod.files, relative_path) + + return nil unless FileTest.exists?(path) + + return model.new(path) + end + + private + + # Determine the environment to use, if any. + def environment(node_name) + if node_name and node = Puppet::Node.find(node_name) + node.environment + elsif env = Puppet.settings[:environment] and env != "" + env + else + nil + end + end + + # Try to find our module. + def find_module(module_name, node_name) + Puppet::Module::find(module_name, environment(node_name)) + end +end diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb index 8d51aff09..7b7c932c4 100644 --- a/lib/puppet/indirector/rest.rb +++ b/lib/puppet/indirector/rest.rb @@ -2,7 +2,7 @@ require 'puppet/indirector/rest' # Access objects via REST class Puppet::Indirector::REST < Puppet::Indirector::Terminus - def find(name) + def find(name, options = {}) indirection.model.new(name) end end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 0d4be3a8d..4365fffca 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -6,7 +6,7 @@ class Puppet::Network::HTTP::Handler register_handler end - # handle an HTTP request coming from Mongrel + # handle an HTTP request def process(request, response) return do_find(request, response) if get?(request) and singular?(request) return do_search(request, response) if get?(request) and plural?(request) diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb new file mode 100644 index 000000000..6f6ea59b5 --- /dev/null +++ b/lib/puppet/util/checksums.rb @@ -0,0 +1,37 @@ +module Puppet::Util::Checksums + def md5(content) + require 'digest/md5' + Digest::MD5.hexdigest(content) + end + + def md5_file(filename) + require 'digest/md5' + + incr_digest = Digest::MD5.new() + File.open(filename, 'r') do |file| + file.each_line do |line| + incr_digest << line + end + end + + return incr_digest.hexdigest + end + + def sha1(content) + require 'digest/sha1' + Digest::SHA1.hexdigest(content) + end + + def sha1_file(filename) + require 'digest/sha1' + + incr_digest = Digest::SHA1.new() + File.open(filename, 'r') do |file| + file.each_line do |line| + incr_digest << line + end + end + + return incr_digest.hexdigest + end +end diff --git a/lib/puppet/util/uri_helper.rb b/lib/puppet/util/uri_helper.rb new file mode 100644 index 000000000..cb9320387 --- /dev/null +++ b/lib/puppet/util/uri_helper.rb @@ -0,0 +1,22 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'uri' +require 'puppet/util' + +# Helper methods for dealing with URIs. +module Puppet::Util::URIHelper + def key2uri(key) + # Return it directly if it's fully qualified. + if key =~ /^#{::File::SEPARATOR}/ + key = "file://" + key + end + + begin + uri = URI.parse(URI.escape(key)) + rescue => detail + raise ArgumentError, "Could not understand URI %s: %s" % [key, detail.to_s] + end + end +end |