From 264331b3287067251c202c96ceb3a6d1f5039976 Mon Sep 17 00:00:00 2001 From: "Michael V. O'Brien" Date: Fri, 19 Oct 2007 13:47:17 -0500 Subject: Partial work done for ssl certificates. --- lib/puppet/indirector/ssl_rsa.rb | 5 +++++ lib/puppet/indirector/ssl_rsa/file.rb | 33 ++++++++++++++++++++++++++++++ lib/puppet/sslcertificates/monkey_patch.rb | 6 ++++++ 3 files changed, 44 insertions(+) create mode 100644 lib/puppet/indirector/ssl_rsa.rb create mode 100644 lib/puppet/indirector/ssl_rsa/file.rb create mode 100644 lib/puppet/sslcertificates/monkey_patch.rb (limited to 'lib/puppet') diff --git a/lib/puppet/indirector/ssl_rsa.rb b/lib/puppet/indirector/ssl_rsa.rb new file mode 100644 index 000000000..162d8200a --- /dev/null +++ b/lib/puppet/indirector/ssl_rsa.rb @@ -0,0 +1,5 @@ +# This is a stub class + +class Puppet::Indirector::SslRsa #:nodoc: +end + diff --git a/lib/puppet/indirector/ssl_rsa/file.rb b/lib/puppet/indirector/ssl_rsa/file.rb new file mode 100644 index 000000000..435aa8f86 --- /dev/null +++ b/lib/puppet/indirector/ssl_rsa/file.rb @@ -0,0 +1,33 @@ +require 'puppet/indirector/file' +require 'puppet/indirector/ssl_rsa' + +class Puppet::Indirector::SslRsa::File < Puppet::Indirector::File + desc "Store SSL keys on disk." + + def initialize + Puppet.settings.use(:ssl) + end + + def path(name) + if name == :ca + File.join Puppet.settings[:cadir], "ca_key.pem" + else + File.join Puppet.settings[:publickeydir], name.to_s + ".pem" + end + end + + def save(key) + File.open(path(key.name), "w") { |f| f.print key.to_pem } + end + + def find(name) + return nil unless FileTest.exists?(path(name)) + OpenSSL::PKey::RSA.new(File.read(path(name))) + end + + def destroy(name) + return nil unless FileTest.exists?(path(name)) + File.unlink(path(name)) and true + end + +end diff --git a/lib/puppet/sslcertificates/monkey_patch.rb b/lib/puppet/sslcertificates/monkey_patch.rb new file mode 100644 index 000000000..663b944c1 --- /dev/null +++ b/lib/puppet/sslcertificates/monkey_patch.rb @@ -0,0 +1,6 @@ +# This is the file that we use to add indirection to all the SSL Certificate classes. + +require 'puppet/indirector' + +OpenSSL::PKey::RSA.extend Puppet::Indirector +OpenSSL::PKey::RSA.indirects :ssl_rsa, :terminus_class => :file -- cgit From 8f827ffe4fa1aa25a2e3c7903967e87c55766996 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Sat, 20 Oct 2007 15:06:11 -0500 Subject: Renaming the 'mounts' terminus to 'file_server', and renaming tests accordingly. --- lib/puppet/file_serving/terminus_selector.rb | 6 +++--- lib/puppet/indirector/file_content/file_server.rb | 11 +++++++++++ lib/puppet/indirector/file_content/mounts.rb | 11 ----------- lib/puppet/indirector/file_metadata/file_server.rb | 11 +++++++++++ lib/puppet/indirector/file_metadata/mounts.rb | 11 ----------- 5 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 lib/puppet/indirector/file_content/file_server.rb delete mode 100644 lib/puppet/indirector/file_content/mounts.rb create mode 100644 lib/puppet/indirector/file_metadata/file_server.rb delete mode 100644 lib/puppet/indirector/file_metadata/mounts.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/terminus_selector.rb b/lib/puppet/file_serving/terminus_selector.rb index 08009cd2b..5952cfffa 100644 --- a/lib/puppet/file_serving/terminus_selector.rb +++ b/lib/puppet/file_serving/terminus_selector.rb @@ -9,7 +9,7 @@ require 'puppet/file_serving' # 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} + PROTOCOL_MAP = {"puppet" => :rest, "file" => :local, "puppetmounts" => :file_server} # Pick an appropriate terminus based on the protocol. def select_terminus(full_uri) @@ -26,10 +26,10 @@ module Puppet::FileServing::TerminusSelector # 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 + terminus = :file_server end - if uri.path =~ /^\/modules\b/ and terminus == :mounts + if uri.path =~ /^\/modules\b/ and terminus == :file_server terminus = :modules end diff --git a/lib/puppet/indirector/file_content/file_server.rb b/lib/puppet/indirector/file_content/file_server.rb new file mode 100644 index 000000000..2f50fcc23 --- /dev/null +++ b/lib/puppet/indirector/file_content/file_server.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::FileServer < Puppet::Indirector::FileServer + desc "Retrieve file contents using Puppet's fileserver." +end diff --git a/lib/puppet/indirector/file_content/mounts.rb b/lib/puppet/indirector/file_content/mounts.rb deleted file mode 100644 index b11fc628c..000000000 --- a/lib/puppet/indirector/file_content/mounts.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/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_metadata/file_server.rb b/lib/puppet/indirector/file_metadata/file_server.rb new file mode 100644 index 000000000..0b2e78908 --- /dev/null +++ b/lib/puppet/indirector/file_metadata/file_server.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::FileServer < Puppet::Indirector::FileServer + desc "Retrieve file metadata using Puppet's fileserver." +end diff --git a/lib/puppet/indirector/file_metadata/mounts.rb b/lib/puppet/indirector/file_metadata/mounts.rb deleted file mode 100644 index b1e3b32fd..000000000 --- a/lib/puppet/indirector/file_metadata/mounts.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/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 -- cgit From b2b8f756c813f7c9a59ac91b4099304b4be2db4c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Sun, 21 Oct 2007 19:37:17 -0500 Subject: Adding authorization hooks to the file_server and module_files indirection terminus types. Both hooks use the fileserver configuration, but the module_files hook only uses the 'modules' mount. Also moved all responsibility for knowing whether to use the 'modules' terminus type to the terminus selector; it was previously spread between that and the file_server terminus, which made some things annoyingly complicated. This normalizes the deprecation notices and the logic about how we make these decisions. --- lib/puppet/file_serving/configuration.rb | 9 +++++++++ lib/puppet/file_serving/terminus_selector.rb | 12 +++++++++--- lib/puppet/indirector/file_server.rb | 15 +++++++++------ lib/puppet/indirector/indirection.rb | 2 +- lib/puppet/indirector/module_files.rb | 28 +++++++++++++++++++++++----- 5 files changed, 51 insertions(+), 15 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb index 03be1b9dd..ccf0957d1 100644 --- a/lib/puppet/file_serving/configuration.rb +++ b/lib/puppet/file_serving/configuration.rb @@ -28,6 +28,14 @@ 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 + # Search for a file. def file_path(key, options = {}) mount, file_path = split_path(key, options[:node]) @@ -81,6 +89,7 @@ class Puppet::FileServing::Configuration return end + # Don't assign the mounts hash until we're sure the parsing succeeded. begin newmounts = @parser.parse @mounts = newmounts diff --git a/lib/puppet/file_serving/terminus_selector.rb b/lib/puppet/file_serving/terminus_selector.rb index 5952cfffa..aa08f087e 100644 --- a/lib/puppet/file_serving/terminus_selector.rb +++ b/lib/puppet/file_serving/terminus_selector.rb @@ -12,7 +12,7 @@ module Puppet::FileServing::TerminusSelector PROTOCOL_MAP = {"puppet" => :rest, "file" => :local, "puppetmounts" => :file_server} # Pick an appropriate terminus based on the protocol. - def select_terminus(full_uri) + def select_terminus(full_uri, options = {}) # Short-circuit to :local if it's a fully-qualified path. return PROTOCOL_MAP["file"] if full_uri =~ /^#{::File::SEPARATOR}/ begin @@ -29,8 +29,14 @@ module Puppet::FileServing::TerminusSelector terminus = :file_server end - if uri.path =~ /^\/modules\b/ and terminus == :file_server - terminus = :modules + if terminus == :file_server and uri.path =~ %r{^/([^/]+)\b} + modname = $1 + if modname == "modules" + terminus = :modules + elsif terminus(:modules).find_module(modname, options[:node]) + Puppet.warning "DEPRECATION NOTICE: Found file '%s' in module without using the 'modules' mount; please prefix path with '/modules'" % uri.path + terminus = :modules + end end return terminus diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb index 1b2e047e8..51e53d8c9 100644 --- a/lib/puppet/indirector/file_server.rb +++ b/lib/puppet/indirector/file_server.rb @@ -10,16 +10,19 @@ require 'puppet/indirector/terminus' class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus include Puppet::Util::URIHelper + # Is the client authorized to perform this action? + def authorized?(method, key, options = {}) + return false unless [:find, :search].include?(method) + + uri = key2uri(key) + + configuration.authorized?(uri.path, :node => options[:node], :ipaddress => options[:ipaddress]) + end + # 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) diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 313117b25..f464f846f 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -104,7 +104,7 @@ class Puppet::Indirector::Indirection # of URI that the indirection can use for routing to the appropriate # terminus. if respond_to?(:select_terminus) - terminus_name = select_terminus(key) + terminus_name = select_terminus(key, *args) else terminus_name = terminus_class end diff --git a/lib/puppet/indirector/module_files.rb b/lib/puppet/indirector/module_files.rb index e0374d7a4..739d7b7b5 100644 --- a/lib/puppet/indirector/module_files.rb +++ b/lib/puppet/indirector/module_files.rb @@ -4,11 +4,24 @@ require 'puppet/util/uri_helper' require 'puppet/indirector/terminus' +require 'puppet/file_serving/configuration' # Look files up in Puppet modules. class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus include Puppet::Util::URIHelper + # Is the client allowed access to this key with this method? + def authorized?(method, key, options = {}) + return false unless [:find, :search].include?(method) + + uri = key2uri(key) + + # Make sure our file path starts with /modules + path = uri.path =~ /^\/modules/ ? uri.path : "/modules" + uri.path + + configuration.authorized?(path, :node => options[:node], :ipaddress => options[:ipaddress]) + end + # Find our key in a module. def find(key, options = {}) uri = key2uri(key) @@ -27,7 +40,17 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus return model.new(path) end + # Try to find our module. + def find_module(module_name, node_name) + Puppet::Module::find(module_name, environment(node_name)) + 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) @@ -39,9 +62,4 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus 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 -- cgit From 393a3e8743f503543ad34a874e9296d684b0d49b Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 22 Oct 2007 15:24:50 -0500 Subject: Adding a Fileset class for managing sets of files. This is the new server-side for file recursion, and I'll next be hooking it to the fileserving 'search' methods. This is basically a mechanism for abstracting that search functionality into a single class. --- lib/puppet/file_serving/fileset.rb | 138 +++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 lib/puppet/file_serving/fileset.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb new file mode 100644 index 000000000..7f7b9fc2d --- /dev/null +++ b/lib/puppet/file_serving/fileset.rb @@ -0,0 +1,138 @@ +# +# Created by Luke Kanies on 2007-10-22. +# Copyright (c) 2007. All rights reserved. + +require 'find' +require 'puppet/file_serving' +require 'puppet/file_serving/metadata' + +# Operate recursively on a path, returning a set of file paths. +class Puppet::FileServing::Fileset + attr_reader :path, :ignore, :links + attr_accessor :recurse + + # Find our collection of files. This is different from the + # normal definition of find in that we support specific levels + # of recursion, which means we need to know when we're going another + # level deep, which Find doesn't do. + def find + files = perform_recursion + + # Now strip off the leading path, so each file becomes relative, and remove + # any slashes that might end up at the beginning of the path. + result = files.collect { |file| file.sub(%r{^#{@path}/*}, '') } + + # And add the path itself. + result.unshift(".") + + result + end + + # Should we ignore this path? + def ignore?(path) + # 'detect' normally returns the found result, whereas we just want true/false. + ! @ignore.detect { |pattern| File.fnmatch?(pattern, path) }.nil? + end + + def ignore=(values) + values = [values] unless values.is_a?(Array) + @ignore = values + end + + def initialize(path, options = {}) + raise ArgumentError.new("Fileset paths must be fully qualified") unless path =~ /^#{::File::SEPARATOR}/ + + @path = path + + # Set our defaults. + @ignore = [] + @links = :manage + @recurse = false + + options.each do |option, value| + method = option.to_s + "=" + begin + send(method, value) + rescue NoMethodError + raise ArgumentError, "Invalid option '%s'" % option + end + end + + raise ArgumentError.new("Fileset paths must exist") unless stat = stat(path) + end + + def links=(links) + links = links.intern if links.is_a?(String) + raise(ArgumentError, "Invalid :links value '%s'" % links) unless [:manage, :follow].include?(links) + @links = links + @stat_method = links == :manage ? :lstat : :stat + end + + # Should we recurse further? This is basically a single + # place for all of the logic around recursion. + def recurse?(depth) + # If recurse is true, just return true + return true if self.recurse == true + + # Return false if the value is false or zero. + return false if [false, 0].include?(self.recurse) + + # Return true if our current depth is less than the allowed recursion depth. + return true if self.recurse.is_a?(Fixnum) and depth <= self.recurse + + # Else, return false. + return false + end + + private + + # Pull the recursion logic into one place. It's moderately hairy, and this + # allows us to keep the hairiness apart from what we do with the files. + def perform_recursion + # Start out with just our base directory. + current_dirs = [@path] + + next_dirs = [] + + depth = 1 + + result = [] + return result unless recurse?(depth) + + while dir_path = current_dirs.shift or ((depth += 1) and recurse?(depth) and current_dirs = next_dirs and next_dirs = [] and dir_path = current_dirs.shift) + next unless stat = stat(dir_path) + next unless stat.directory? + + Dir.entries(dir_path).each do |file_path| + next if [".", ".."].include?(file_path) + + # Note that this also causes matching directories not + # to be recursed into. + next if ignore?(file_path) + + # Add it to our list of files to return + result << File.join(dir_path, file_path) + + # And to our list of files/directories to iterate over. + next_dirs << File.join(dir_path, file_path) + end + end + + return result + end + + # Stat a given file, using the links-appropriate method. + def stat(path) + unless defined?(@stat_method) + @stat_method = self.links == :manage ? :lstat : :stat + end + + begin + return File.send(@stat_method, path) + rescue + # If this happens, it is almost surely because we're + # trying to manage a link to a file that does not exist. + return nil + end + end +end -- cgit From 688fcdf11a685dfda297beff50de8d4c751494d9 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 22 Oct 2007 19:28:22 -0500 Subject: Adding searchability to the fileserving termini, using the new Fileset class. The tests aren't the cleanest, in that there is still a good bit of duplication in them, but it's what we got. --- lib/puppet/file_serving/fileset.rb | 4 +-- lib/puppet/file_serving/terminus_helper.rb | 15 ++++++++++ lib/puppet/indirector/file_content/local.rb | 11 +++++-- lib/puppet/indirector/file_metadata/local.rb | 8 ++++++ lib/puppet/indirector/file_server.rb | 23 +++++++++++++-- lib/puppet/indirector/module_files.rb | 43 +++++++++++++++++++--------- 6 files changed, 83 insertions(+), 21 deletions(-) create mode 100644 lib/puppet/file_serving/terminus_helper.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb index 7f7b9fc2d..fe54350b1 100644 --- a/lib/puppet/file_serving/fileset.rb +++ b/lib/puppet/file_serving/fileset.rb @@ -11,11 +11,11 @@ class Puppet::FileServing::Fileset attr_reader :path, :ignore, :links attr_accessor :recurse - # Find our collection of files. This is different from the + # Return a list of all files in our fileset. This is different from the # normal definition of find in that we support specific levels # of recursion, which means we need to know when we're going another # level deep, which Find doesn't do. - def find + def files files = perform_recursion # Now strip off the leading path, so each file becomes relative, and remove diff --git a/lib/puppet/file_serving/terminus_helper.rb b/lib/puppet/file_serving/terminus_helper.rb new file mode 100644 index 000000000..9542cbf84 --- /dev/null +++ b/lib/puppet/file_serving/terminus_helper.rb @@ -0,0 +1,15 @@ +# +# Created by Luke Kanies on 2007-10-22. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving' +require 'puppet/file_serving/fileset' + +# Define some common methods for FileServing termini. +module Puppet::FileServing::TerminusHelper + # Create model instances for all files in a fileset. + def path2instances(path, options = {}) + args = [:links, :ignore, :recurse].inject({}) { |hash, param| hash[param] = options[param] if options[param]; hash } + Puppet::FileServing::Fileset.new(path, args).files.collect { |file| model.new(file) } + end +end diff --git a/lib/puppet/indirector/file_content/local.rb b/lib/puppet/indirector/file_content/local.rb index e429c6c25..c2262c82d 100644 --- a/lib/puppet/indirector/file_content/local.rb +++ b/lib/puppet/indirector/file_content/local.rb @@ -3,6 +3,7 @@ # Copyright (c) 2007. All rights reserved. require 'puppet/file_serving/content' +require 'puppet/file_serving/terminus_helper' require 'puppet/util/uri_helper' require 'puppet/indirector/file_content' require 'puppet/indirector/file' @@ -11,13 +12,17 @@ class Puppet::Indirector::FileContent::Local < Puppet::Indirector::File desc "Retrieve file contents from disk." include Puppet::Util::URIHelper + include Puppet::FileServing::TerminusHelper def find(key, options = {}) uri = key2uri(key) - return nil unless FileTest.exists?(uri.path) - data = model.new(uri.path) + model.new(uri.path) + end - return data + def search(key, options = {}) + uri = key2uri(key) + return nil unless FileTest.exists?(uri.path) + path2instances(uri.path, options) end end diff --git a/lib/puppet/indirector/file_metadata/local.rb b/lib/puppet/indirector/file_metadata/local.rb index f40d4ce43..86e1172f9 100644 --- a/lib/puppet/indirector/file_metadata/local.rb +++ b/lib/puppet/indirector/file_metadata/local.rb @@ -3,6 +3,7 @@ # Copyright (c) 2007. All rights reserved. require 'puppet/file_serving/metadata' +require 'puppet/file_serving/terminus_helper' require 'puppet/indirector/file_metadata' require 'puppet/util/uri_helper' require 'puppet/indirector/code' @@ -11,6 +12,7 @@ class Puppet::Indirector::FileMetadata::Local < Puppet::Indirector::Code desc "Retrieve file metadata directly from the local filesystem." include Puppet::Util::URIHelper + include Puppet::FileServing::TerminusHelper def find(key) uri = key2uri(key) @@ -21,4 +23,10 @@ class Puppet::Indirector::FileMetadata::Local < Puppet::Indirector::Code return data end + + def search(key, options = {}) + uri = key2uri(key) + return nil unless FileTest.exists?(uri.path) + path2instances(uri.path, options).each { |instance| instance.get_attributes } + end end diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb index 51e53d8c9..46b0c40f2 100644 --- a/lib/puppet/indirector/file_server.rb +++ b/lib/puppet/indirector/file_server.rb @@ -4,11 +4,14 @@ require 'puppet/util/uri_helper' require 'puppet/file_serving/configuration' +require 'puppet/file_serving/fileset' +require 'puppet/file_serving/terminus_helper' 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?(method, key, options = {}) @@ -21,11 +24,16 @@ class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus # Find our key using the fileserver. def find(key, options = {}) - uri = key2uri(key) + return nil unless path = find_path(key, options) + return model.new(path) + end - return nil unless path = configuration.file_path(uri.path, :node => options[:node]) and FileTest.exists?(path) + # Search for files. This returns an array rather than a single + # file. + def search(key, options = {}) + return nil unless path = find_path(key, options) - return model.new(path) + path2instances(path, options) end private @@ -34,4 +42,13 @@ 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(key, options) + uri = key2uri(key) + + return nil unless path = configuration.file_path(uri.path, :node => options[:node]) + + return path + end end diff --git a/lib/puppet/indirector/module_files.rb b/lib/puppet/indirector/module_files.rb index 739d7b7b5..815da2efe 100644 --- a/lib/puppet/indirector/module_files.rb +++ b/lib/puppet/indirector/module_files.rb @@ -5,10 +5,13 @@ 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?(method, key, options = {}) @@ -16,7 +19,8 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus uri = key2uri(key) - # Make sure our file path starts with /modules + # 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 => options[:node], :ipaddress => options[:ipaddress]) @@ -24,18 +28,7 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus # 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 nil unless path = find_path(key, options) return model.new(path) end @@ -45,6 +38,12 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus Puppet::Module::find(module_name, environment(node_name)) end + # Search for a list of files. + def search(key, options = {}) + return nil unless path = find_path(key, options) + path2instances(path, options) + end + private # Our fileserver configuration, if needed. @@ -62,4 +61,22 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus nil end end + + # The abstracted method for turning a key into a path; used by both :find and :search. + def find_path(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 path + end end -- cgit From 7fa99b08f9aa3777fba82f24eb5bb391f3758f48 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 22 Oct 2007 22:33:06 -0500 Subject: Link handling is now in the file serving classes. This was done by putting all of the functionality in the Content and Metadata class (actually, in a new base class for them). There are still some issues, and there need to be integration tests between the :local (soon to be renamed :file) termini for these classes. --- lib/puppet/file_serving/content.rb | 18 +++++------ lib/puppet/file_serving/file_base.rb | 46 ++++++++++++++++++++++++++++ lib/puppet/file_serving/metadata.rb | 35 ++++++++++++--------- lib/puppet/indirector/file_content/local.rb | 2 +- lib/puppet/indirector/file_metadata/local.rb | 8 ++--- lib/puppet/indirector/file_server.rb | 2 +- lib/puppet/indirector/module_files.rb | 2 +- 7 files changed, 81 insertions(+), 32 deletions(-) create mode 100644 lib/puppet/file_serving/file_base.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb index 38ca80fb0..3cb428e63 100644 --- a/lib/puppet/file_serving/content.rb +++ b/lib/puppet/file_serving/content.rb @@ -4,30 +4,28 @@ require 'puppet/indirector' require 'puppet/file_serving' +require 'puppet/file_serving/file_base' 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 +class Puppet::FileServing::Content < Puppet::FileServing::FileBase 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) + # Read the content of our file in. + def content(base = nil) + # This stat can raise an exception, too. + raise(ArgumentError, "Cannot read the contents of links unless following links") if stat(base).ftype == "symlink" - @path = path + ::File.read(full_path(base)) 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 + # avoid escaping or any such thing. LAK:NOTE 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 diff --git a/lib/puppet/file_serving/file_base.rb b/lib/puppet/file_serving/file_base.rb new file mode 100644 index 000000000..b2e9a0656 --- /dev/null +++ b/lib/puppet/file_serving/file_base.rb @@ -0,0 +1,46 @@ +# +# Created by Luke Kanies on 2007-10-22. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving' + +# The base class for Content and Metadata; provides common +# functionality like the behaviour around links. +class Puppet::FileServing::FileBase + attr_accessor :path, :base_path + + def full_path(base = nil) + base ||= base_path || raise(ArgumentError, "You must set or provide a base path") + + full = File.join(base, self.path) + end + + def initialize(path, options = {}) + raise ArgumentError.new("Files must not be fully qualified") if path =~ /^#{::File::SEPARATOR}/ + + @path = path + @links = :manage + + options.each do |param, value| + begin + send param.to_s + "=", value + rescue NoMethodError + raise ArgumentError, "Invalid option %s for %s" % [param, self.class] + end + end + end + + attr_reader :links + def links=(value) + raise(ArgumentError, ":links can only be set to :manage or :follow") unless [:manage, :follow].include?(value) + @links = value + end + + # Stat our file, using the appropriate link-sensitive method. + def stat(base = nil) + unless defined?(@stat_method) + @stat_method = self.links == :manage ? :lstat : :stat + end + File.send(@stat_method, full_path(base)) + end +end diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 7adb66981..62ebccca9 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -5,17 +5,18 @@ require 'puppet' require 'puppet/indirector' require 'puppet/file_serving' +require 'puppet/file_serving/file_base' require 'puppet/util/checksums' require 'puppet/file_serving/terminus_selector' # A class that handles retrieving file metadata. -class Puppet::FileServing::Metadata +class Puppet::FileServing::Metadata < Puppet::FileServing::FileBase include Puppet::Util::Checksums extend Puppet::Indirector indirects :file_metadata, :extend => Puppet::FileServing::TerminusSelector - attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum + attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum, :ftype, :destination def checksum_type=(type) raise(ArgumentError, "Unsupported checksum type %s" % type) unless respond_to?("%s_file" % type) @@ -23,32 +24,36 @@ class Puppet::FileServing::Metadata @checksum_type = type end - def get_attributes - stat = File.stat(path) + # Retrieve the attributes for this file, relative to a base directory. + # Note that File.stat raises Errno::ENOENT if the file is absent and this + # method does not catch that exception. + def collect_attributes(base = nil) + real_path = full_path(base) + stat = stat(base) @owner = stat.uid @group = stat.gid + @ftype = stat.ftype + # 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 + if stat.ftype == "symlink" + @destination = File.readlink(real_path) + else + @checksum = get_checksum(real_path) end + end + def initialize(*args) @checksum_type = "md5" + super end private # Retrieve our checksum. - def get_checksum - ("{%s}" % @checksum_type) + send("%s_file" % @checksum_type, @path) + def get_checksum(path) + ("{%s}" % @checksum_type) + send("%s_file" % @checksum_type, path) end end diff --git a/lib/puppet/indirector/file_content/local.rb b/lib/puppet/indirector/file_content/local.rb index c2262c82d..a9c45d59e 100644 --- a/lib/puppet/indirector/file_content/local.rb +++ b/lib/puppet/indirector/file_content/local.rb @@ -17,7 +17,7 @@ class Puppet::Indirector::FileContent::Local < Puppet::Indirector::File def find(key, options = {}) uri = key2uri(key) return nil unless FileTest.exists?(uri.path) - model.new(uri.path) + model.new(uri.path, :links => options[:links]) end def search(key, options = {}) diff --git a/lib/puppet/indirector/file_metadata/local.rb b/lib/puppet/indirector/file_metadata/local.rb index 86e1172f9..d696bc769 100644 --- a/lib/puppet/indirector/file_metadata/local.rb +++ b/lib/puppet/indirector/file_metadata/local.rb @@ -14,12 +14,12 @@ class Puppet::Indirector::FileMetadata::Local < Puppet::Indirector::Code include Puppet::Util::URIHelper include Puppet::FileServing::TerminusHelper - def find(key) + def find(key, options = {}) uri = key2uri(key) return nil unless FileTest.exists?(uri.path) - data = model.new(uri.path) - data.get_attributes + data = model.new(uri.path, :links => options[:links]) + data.collect_attributes return data end @@ -27,6 +27,6 @@ class Puppet::Indirector::FileMetadata::Local < Puppet::Indirector::Code def search(key, options = {}) uri = key2uri(key) return nil unless FileTest.exists?(uri.path) - path2instances(uri.path, options).each { |instance| instance.get_attributes } + path2instances(uri.path, options).each { |instance| instance.collect_attributes } end end diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb index 46b0c40f2..de88bdc18 100644 --- a/lib/puppet/indirector/file_server.rb +++ b/lib/puppet/indirector/file_server.rb @@ -25,7 +25,7 @@ class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus # Find our key using the fileserver. def find(key, options = {}) return nil unless path = find_path(key, options) - return model.new(path) + return model.new(path, :links => options[:links]) end # Search for files. This returns an array rather than a single diff --git a/lib/puppet/indirector/module_files.rb b/lib/puppet/indirector/module_files.rb index 815da2efe..12794e4c7 100644 --- a/lib/puppet/indirector/module_files.rb +++ b/lib/puppet/indirector/module_files.rb @@ -30,7 +30,7 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus def find(key, options = {}) return nil unless path = find_path(key, options) - return model.new(path) + return model.new(path, :links => options[:links]) end # Try to find our module. -- cgit From de5d91e2036de2934a4eec79d35a714f3ed24b10 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 22 Oct 2007 22:37:52 -0500 Subject: Renaming the :local termini for metadata and content to :file. --- lib/puppet/file_serving/terminus_selector.rb | 4 ++-- lib/puppet/indirector/file_content/file.rb | 28 ++++++++++++++++++++++++ lib/puppet/indirector/file_content/local.rb | 28 ------------------------ lib/puppet/indirector/file_metadata/file.rb | 32 ++++++++++++++++++++++++++++ lib/puppet/indirector/file_metadata/local.rb | 32 ---------------------------- 5 files changed, 62 insertions(+), 62 deletions(-) create mode 100644 lib/puppet/indirector/file_content/file.rb delete mode 100644 lib/puppet/indirector/file_content/local.rb create mode 100644 lib/puppet/indirector/file_metadata/file.rb delete mode 100644 lib/puppet/indirector/file_metadata/local.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/terminus_selector.rb b/lib/puppet/file_serving/terminus_selector.rb index aa08f087e..06b53ddb1 100644 --- a/lib/puppet/file_serving/terminus_selector.rb +++ b/lib/puppet/file_serving/terminus_selector.rb @@ -9,11 +9,11 @@ require 'puppet/file_serving' # 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" => :file_server} + PROTOCOL_MAP = {"puppet" => :rest, "file" => :file, "puppetmounts" => :file_server} # Pick an appropriate terminus based on the protocol. def select_terminus(full_uri, options = {}) - # Short-circuit to :local if it's a fully-qualified path. + # Short-circuit to :file if it's a fully-qualified path. return PROTOCOL_MAP["file"] if full_uri =~ /^#{::File::SEPARATOR}/ begin uri = URI.parse(URI.escape(full_uri)) diff --git a/lib/puppet/indirector/file_content/file.rb b/lib/puppet/indirector/file_content/file.rb new file mode 100644 index 000000000..4503a7919 --- /dev/null +++ b/lib/puppet/indirector/file_content/file.rb @@ -0,0 +1,28 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/file_serving/terminus_helper' +require 'puppet/util/uri_helper' +require 'puppet/indirector/file_content' +require 'puppet/indirector/file' + +class Puppet::Indirector::FileContent::File < Puppet::Indirector::File + desc "Retrieve file contents from disk." + + include Puppet::Util::URIHelper + include Puppet::FileServing::TerminusHelper + + def find(key, options = {}) + uri = key2uri(key) + return nil unless FileTest.exists?(uri.path) + model.new(uri.path, :links => options[:links]) + end + + def search(key, options = {}) + uri = key2uri(key) + return nil unless FileTest.exists?(uri.path) + path2instances(uri.path, options) + end +end diff --git a/lib/puppet/indirector/file_content/local.rb b/lib/puppet/indirector/file_content/local.rb deleted file mode 100644 index a9c45d59e..000000000 --- a/lib/puppet/indirector/file_content/local.rb +++ /dev/null @@ -1,28 +0,0 @@ -# -# Created by Luke Kanies on 2007-10-16. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/file_serving/content' -require 'puppet/file_serving/terminus_helper' -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 - include Puppet::FileServing::TerminusHelper - - def find(key, options = {}) - uri = key2uri(key) - return nil unless FileTest.exists?(uri.path) - model.new(uri.path, :links => options[:links]) - end - - def search(key, options = {}) - uri = key2uri(key) - return nil unless FileTest.exists?(uri.path) - path2instances(uri.path, options) - end -end diff --git a/lib/puppet/indirector/file_metadata/file.rb b/lib/puppet/indirector/file_metadata/file.rb new file mode 100644 index 000000000..823c26c36 --- /dev/null +++ b/lib/puppet/indirector/file_metadata/file.rb @@ -0,0 +1,32 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/metadata' +require 'puppet/file_serving/terminus_helper' +require 'puppet/indirector/file_metadata' +require 'puppet/util/uri_helper' +require 'puppet/indirector/code' + +class Puppet::Indirector::FileMetadata::File < Puppet::Indirector::Code + desc "Retrieve file metadata directly from the local filesystem." + + include Puppet::Util::URIHelper + include Puppet::FileServing::TerminusHelper + + def find(key, options = {}) + uri = key2uri(key) + + return nil unless FileTest.exists?(uri.path) + data = model.new(uri.path, :links => options[:links]) + data.collect_attributes + + return data + end + + def search(key, options = {}) + uri = key2uri(key) + return nil unless FileTest.exists?(uri.path) + path2instances(uri.path, options).each { |instance| instance.collect_attributes } + end +end diff --git a/lib/puppet/indirector/file_metadata/local.rb b/lib/puppet/indirector/file_metadata/local.rb deleted file mode 100644 index d696bc769..000000000 --- a/lib/puppet/indirector/file_metadata/local.rb +++ /dev/null @@ -1,32 +0,0 @@ -# -# Created by Luke Kanies on 2007-10-16. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/file_serving/metadata' -require 'puppet/file_serving/terminus_helper' -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 - include Puppet::FileServing::TerminusHelper - - def find(key, options = {}) - uri = key2uri(key) - - return nil unless FileTest.exists?(uri.path) - data = model.new(uri.path, :links => options[:links]) - data.collect_attributes - - return data - end - - def search(key, options = {}) - uri = key2uri(key) - return nil unless FileTest.exists?(uri.path) - path2instances(uri.path, options).each { |instance| instance.collect_attributes } - end -end -- cgit