diff options
| author | Luke Kanies <luke@madstop.com> | 2009-02-18 16:33:47 -0600 |
|---|---|---|
| committer | Luke Kanies <luke@madstop.com> | 2009-02-19 17:50:28 -0600 |
| commit | d3bc1e8279b6e1d372ab3624982788cde026461d (patch) | |
| tree | 549d4365b52e7aea1cddf7365368d34cdb980b93 /lib/puppet | |
| parent | 00726bac02211be3c269c23a564bdcc8fdd28c2b (diff) | |
Adding pluginsyncing support to the Indirector
This switches away from the use of terminii for
each type of fileserving - it goes back to the traditional
fileserving method, and is much cleaner and simpler
as a result.
Signed-off-by: Luke Kanies <luke@madstop.com>
Diffstat (limited to 'lib/puppet')
| -rw-r--r-- | lib/puppet/file_serving/configuration.rb | 106 | ||||
| -rw-r--r-- | lib/puppet/file_serving/configuration/parser.rb | 43 | ||||
| -rw-r--r-- | lib/puppet/file_serving/indirection_hooks.rb | 15 | ||||
| -rw-r--r-- | lib/puppet/file_serving/mount.rb | 163 | ||||
| -rw-r--r-- | lib/puppet/file_serving/mount/file.rb | 126 | ||||
| -rw-r--r-- | lib/puppet/file_serving/mount/modules.rb | 25 | ||||
| -rw-r--r-- | lib/puppet/file_serving/mount/plugins.rb | 27 | ||||
| -rw-r--r-- | lib/puppet/indirector/file_content/modules.rb | 11 | ||||
| -rw-r--r-- | lib/puppet/indirector/file_metadata/modules.rb | 17 | ||||
| -rw-r--r-- | lib/puppet/indirector/file_server.rb | 47 | ||||
| -rw-r--r-- | lib/puppet/indirector/module_files.rb | 81 | ||||
| -rw-r--r-- | lib/puppet/module.rb | 6 |
12 files changed, 302 insertions, 365 deletions
diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb index 907186ac3..de5995d7e 100644 --- a/lib/puppet/file_serving/configuration.rb +++ b/lib/puppet/file_serving/configuration.rb @@ -5,9 +5,14 @@ require 'puppet' require 'puppet/file_serving' require 'puppet/file_serving/mount' +require 'puppet/file_serving/mount/file' +require 'puppet/file_serving/mount/modules' +require 'puppet/file_serving/mount/plugins' require 'puppet/util/cacher' +require 'puppet/util/uri_helper' class Puppet::FileServing::Configuration + include Puppet::Util::URIHelper require 'puppet/file_serving/configuration/parser' class << self @@ -15,8 +20,6 @@ class Puppet::FileServing::Configuration cached_attr(:configuration) { new() } end - @config_fileuration = nil - Mount = Puppet::FileServing::Mount # Create our singleton configuration. @@ -26,23 +29,26 @@ class Puppet::FileServing::Configuration private_class_method :new - # Verify that the client is allowed access to this file. - def authorized?(file, options = {}) - mount, file_path = split_path(file, options[:node]) - # If we're not serving this mount, then access is denied. - return false unless mount - return mount.allowed?(options[:node], options[:ipaddress]) - end + attr_reader :mounts + #private :mounts + + # Find the right mount. Does some shenanigans to support old-style module + # mounts. + def find_mount(mount_name, node) + # Reparse the configuration if necessary. + readconfig - # Search for a file. - def file_path(key, options = {}) - mount, file_path = split_path(key, options[:node]) + if mount = mounts[mount_name] + return mount + end - return nil unless mount + if mounts["modules"].environment(node).module(mount_name) + Puppet.warning "DEPRECATION NOTICE: Found module '%s' without using the 'modules' mount; please prefix path with 'modules/'" % mount_name + return mounts["modules"] + end - # The mount checks to see if the file exists, and returns nil - # if not. - return mount.file(file_path, options) + # This can be nil. + mounts[mount_name] end def initialize @@ -59,22 +65,39 @@ class Puppet::FileServing::Configuration @mounts.include?(name) end + # Split the path into the separate mount point and path. + def split_path(request) + # Reparse the configuration if necessary. + readconfig + + uri = key2uri(request.key) + + mount_name, path = uri.path.sub(/^\//, '').split(File::Separator, 2) + + raise(ArgumentError, "Cannot find file: Invalid path '%s'" % mount_name) unless mount_name =~ %r{^[-\w]+$} + + return nil unless mount = find_mount(mount_name, request.options[:node]) + if mount.name == "modules" and mount_name != "modules" + # yay backward-compatibility + path = "%s/%s" % [mount_name, path] + end + + if path == "" + path = nil + elsif path + # Remove any double slashes that might have occurred + path = path.gsub(/\/+/, "/") + end + + return mount, path + 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] @@ -92,37 +115,8 @@ class Puppet::FileServing::Configuration newmounts = @parser.parse @mounts = newmounts rescue => detail + puts detail.backtrace if Puppet[:trace] 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.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 index cda6889d4..f36bef639 100644 --- a/lib/puppet/file_serving/configuration/parser.rb +++ b/lib/puppet/file_serving/configuration/parser.rb @@ -46,20 +46,15 @@ class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile } } + mk_default_mounts + + validate() + 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) # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] @@ -88,39 +83,47 @@ class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile } end + def mk_default_mounts + ["plugins", "modules"].each do |name| + newmount(name) unless @mounts[name] + 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) + case name + when "modules" + mount = Mount::Modules.new(name) + when "plugins" + mount = Mount::Plugins.new(name) + else + mount = Mount::File.new(name) + end @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 + if mount.respond_to?(:path=) begin mount.path = value rescue ArgumentError => detail - Puppet.err "Removing mount %s: %s" % - [mount.name, detail] + Puppet.err "Removing mount %s: %s" % [mount.name, detail] @mounts.delete(mount.name) end + else + Puppet.warning "The '#{mount.name}' module can not have a path. Ignoring attempt to set it" 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 - } + @mounts.each { |name, mount| mount.validate } end end diff --git a/lib/puppet/file_serving/indirection_hooks.rb b/lib/puppet/file_serving/indirection_hooks.rb index 15564cf3d..5fd7985e7 100644 --- a/lib/puppet/file_serving/indirection_hooks.rb +++ b/lib/puppet/file_serving/indirection_hooks.rb @@ -29,19 +29,6 @@ module Puppet::FileServing::IndirectionHooks end # If we're still here, we're using the file_server or modules. - - # This is the backward-compatible module terminus. - modname = request.key.split("/")[0] - - if modname == "modules" - terminus = :modules - elsif terminus(:modules).find_module(modname, request.options[:node]) - Puppet.warning "DEPRECATION NOTICE: Found file '%s' in module without using the 'modules' mount; please prefix path with 'modules/'" % request.key - terminus = :modules - else - terminus = :file_server - end - - return terminus + return :file_server end end diff --git a/lib/puppet/file_serving/mount.rb b/lib/puppet/file_serving/mount.rb index 552cf33f2..5b74ad218 100644 --- a/lib/puppet/file_serving/mount.rb +++ b/lib/puppet/file_serving/mount.rb @@ -14,171 +14,40 @@ require 'puppet/file_serving/content' class Puppet::FileServing::Mount < Puppet::Network::AuthStore include Puppet::Util::Logging - class << self - include Puppet::Util::Cacher - - cached_attr(:localmap) do - { "h" => Facter.value("hostname"), - "H" => [Facter.value("hostname"), - Facter.value("domain")].join("."), - "d" => Facter.value("domain") - } - end - 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 + + # Determine the environment to use, if any. + def environment(node_name) + if node_name and node = Puppet::Node.find(node_name) + Puppet::Node::Environment.new(node.environment) + else + Puppet::Node::Environment.new + end 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) + def find(path, options) + raise NotImplementedError end - # Create out object. It must have a name. - def initialize(name, path = nil) + # Create our object. It must have a name. + def initialize(name) 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] + def search(path, options) + raise NotImplementedError 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 - self.class.localmap + # A noop. + def validate end end diff --git a/lib/puppet/file_serving/mount/file.rb b/lib/puppet/file_serving/mount/file.rb new file mode 100644 index 000000000..15e343c57 --- /dev/null +++ b/lib/puppet/file_serving/mount/file.rb @@ -0,0 +1,126 @@ +require 'puppet/util/cacher' + +require 'puppet/file_serving/mount' + +class Puppet::FileServing::Mount::File < Puppet::FileServing::Mount + class << self + include Puppet::Util::Cacher + + cached_attr(:localmap) do + { "h" => Facter.value("hostname"), + "H" => [Facter.value("hostname"), + Facter.value("domain")].join("."), + "d" => Facter.value("domain") + } + end + end + + def complete_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 + + file = ::File.join(full_path, relative_path) + + return nil unless FileTest.exist?(file) + + return file + end + + # Return an instance of the appropriate class. + def find(short_file, options = {}) + complete_path(short_file, options[:node]) + 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 search(path, options = {}) + return nil unless path = complete_path(path, options[:node]) + return [path] + end + + # Verify our configuration is valid. This should really check to + # make sure at least someone will be allowed, but, eh. + def validate + raise ArgumentError.new("Mounts without paths are not usable") if @path.nil? + end + + private + + # 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 + self.class.localmap + end +end diff --git a/lib/puppet/file_serving/mount/modules.rb b/lib/puppet/file_serving/mount/modules.rb new file mode 100644 index 000000000..bf0bad02b --- /dev/null +++ b/lib/puppet/file_serving/mount/modules.rb @@ -0,0 +1,25 @@ +require 'puppet/file_serving/mount' + +# This is the modules-specific mount: it knows how to search through +# modules for files. Yay. +class Puppet::FileServing::Mount::Modules < Puppet::FileServing::Mount + # Return an instance of the appropriate class. + def find(path, options = {}) + module_name, relative_path = path.split("/", 2) + return nil unless mod = environment(options[:node]).module(module_name) + + mod.file(relative_path) + end + + def search(path, options = {}) + module_name, relative_path = path.split("/", 2) + return nil unless mod = environment(options[:node]).module(module_name) + + return nil unless path = mod.file(relative_path) + return [path] + end + + def valid? + true + end +end diff --git a/lib/puppet/file_serving/mount/plugins.rb b/lib/puppet/file_serving/mount/plugins.rb new file mode 100644 index 000000000..487bd043b --- /dev/null +++ b/lib/puppet/file_serving/mount/plugins.rb @@ -0,0 +1,27 @@ +require 'puppet/file_serving/mount' + +# Find files in the modules' plugins directories. +# This is a very strange mount because it merges +# many directories into one. +class Puppet::FileServing::Mount::Plugins < Puppet::FileServing::Mount + # Return an instance of the appropriate class. + def find(relative_path, options = {}) + return nil unless mod = environment(options[:node]).modules.find { |mod| mod.plugin(relative_path) } + + path = mod.plugin(relative_path) + + return path + end + + def search(relative_path, options = {}) + # We currently only support one kind of search on plugins - return + # them all. + paths = environment(options[:node]).modules.find_all { |mod| mod.plugins? }.collect { |mod| mod.plugins } + return nil if paths.empty? + return paths + end + + def valid? + true + end +end diff --git a/lib/puppet/indirector/file_content/modules.rb b/lib/puppet/indirector/file_content/modules.rb deleted file mode 100644 index 8563dacb4..000000000 --- a/lib/puppet/indirector/file_content/modules.rb +++ /dev/null @@ -1,11 +0,0 @@ -# -# 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_metadata/modules.rb b/lib/puppet/indirector/file_metadata/modules.rb deleted file mode 100644 index 4598c2175..000000000 --- a/lib/puppet/indirector/file_metadata/modules.rb +++ /dev/null @@ -1,17 +0,0 @@ -# -# 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.collect - instance - end -end diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb index 46a590f9c..e3bde1540 100644 --- a/lib/puppet/indirector/file_server.rb +++ b/lib/puppet/indirector/file_server.rb @@ -2,7 +2,6 @@ # 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/file_serving/fileset' require 'puppet/file_serving/terminus_helper' @@ -10,33 +9,54 @@ require 'puppet/indirector/terminus' # Look files up using the file server. class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus - include Puppet::Util::URIHelper include Puppet::FileServing::TerminusHelper # Is the client authorized to perform this action? def authorized?(request) return false unless [:find, :search].include?(request.method) - uri = key2uri(request.key) + mount, file_path = configuration.split_path(request) - configuration.authorized?(uri.path, :node => request.node, :ipaddress => request.ip) + # If we're not serving this mount, then access is denied. + return false unless mount + return mount.allowed?(request.options[:node], request.options[:ipaddress]) end # Find our key using the fileserver. def find(request) - return nil unless path = find_path(request) - result = model.new(path) + mount, relative_path = configuration.split_path(request) + + return nil unless mount + + # The mount checks to see if the file exists, and returns nil + # if not. + return nil unless path = mount.find(relative_path, request.options) + result = model.new(path) result.links = request.options[:links] if request.options[:links] result.collect - return result + result end # Search for files. This returns an array rather than a single # file. def search(request) - return nil unless path = find_path(request) + mount, relative_path = configuration.split_path(request) + + return nil unless mount + + return nil unless paths = mount.search(relative_path, :node => request.node) + + filesets = paths.collect do |path| + # Filesets support indirector requests as an options collection + Puppet::FileServing::Fileset.new(path, request) + end - path2instances(request, path) + Puppet::FileServing::Fileset.merge(*filesets).collect do |file, base_path| + inst = model.new(base_path, :relative_path => file) + inst.links = request.options[:links] if request.options[:links] + inst.collect + inst + end end private @@ -45,13 +65,4 @@ class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus def configuration Puppet::FileServing::Configuration.create end - - # Find our path; used by :find and :search. - def find_path(request) - uri = key2uri(request.key) - - return nil unless path = configuration.file_path(uri.path, :node => request.node) - - return path - end end diff --git a/lib/puppet/indirector/module_files.rb b/lib/puppet/indirector/module_files.rb deleted file mode 100644 index fbf40aa70..000000000 --- a/lib/puppet/indirector/module_files.rb +++ /dev/null @@ -1,81 +0,0 @@ -# -# Created by Luke Kanies on 2007-10-19. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/util/uri_helper' -require 'puppet/indirector/terminus' -require 'puppet/file_serving/configuration' -require 'puppet/file_serving/fileset' -require 'puppet/file_serving/terminus_helper' - -# Look files up in Puppet modules. -class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus - include Puppet::Util::URIHelper - include Puppet::FileServing::TerminusHelper - - # Is the client allowed access to this key with this method? - def authorized?(request) - return false unless [:find, :search].include?(request.method) - - uri = key2uri(request.key) - - # Make sure our file path starts with /modules, so that we authorize - # against the 'modules' mount. - path = uri.path =~ /^modules\// ? uri.path : "modules/" + uri.path - - configuration.authorized?(path, :node => request.node, :ipaddress => request.ip) - end - - # Find our key in a module. - def find(request) - return nil unless path = find_path(request) - - result = model.new(path) - result.links = request.options[:links] if request.options[:links] - return result - end - - # Try to find our module. - def find_module(module_name, node_name) - environment(node_name).module(module_name) - end - - # Search for a list of files. - def search(request) - return nil unless path = find_path(request) - path2instances(request, path) - end - - private - - # Our fileserver configuration, if needed. - def configuration - Puppet::FileServing::Configuration.create - end - - # Determine the environment to use, if any. - def environment(node_name) - if node_name and node = Puppet::Node.find(node_name) - Puppet::Node::Environment.new(node.environment) - else - Puppet::Node::Environment.new - end - end - - # The abstracted method for turning a key into a path; used by both :find and :search. - def find_path(request) - uri = key2uri(request.key) - - # Strip off modules/ if it's there -- that's how requests get routed to this terminus. - module_name, relative_path = uri.path.sub(/^modules\//, '').sub(%r{^/}, '').split(File::Separator, 2) - - # And use the environment to look up the module. - return nil unless mod = find_module(module_name, request.node) - - path = File.join(mod.files, relative_path) - - return nil unless FileTest.exists?(path) - - return path - end -end diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index 54eb4bd97..45b40698b 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -152,7 +152,11 @@ class Puppet::Module # Finally, a method for returning an individual file define_method(type.to_s.sub(/s$/, '')) do |file| - path = File.join(send(type), file) + if file + path = File.join(send(type), file) + else + path = send(type) + end return nil unless FileTest.exist?(path) return path end |
