From d0bd48cc50cf90440429569e748877ab6e23491f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Wed, 17 Oct 2007 11:44:03 -0500 Subject: Adding the first pass at modifying file serving to work with indirection. I've split the fileserver handler into four pieces: Mount (which so far I've just copied wholesale), Configuration (responsible for reading the configuration file and determining what's allowed), Metadata (retrieves information about the files), and Content (retrieves the actual file content). I haven't added the indirection tests yet, and the configuration tests are still all stubs. --- lib/puppet/file_serving.rb | 7 + lib/puppet/file_serving/configuration.rb | 261 +++++++++++++++++++++++++++++++ lib/puppet/file_serving/content.rb | 35 +++++ lib/puppet/file_serving/metadata.rb | 48 ++++++ lib/puppet/file_serving/mount.rb | 180 +++++++++++++++++++++ lib/puppet/indirector/content/file.rb | 10 ++ lib/puppet/indirector/metadata/ral.rb | 15 ++ lib/puppet/util/checksums.rb | 37 +++++ 8 files changed, 593 insertions(+) create mode 100644 lib/puppet/file_serving.rb create mode 100644 lib/puppet/file_serving/configuration.rb create mode 100644 lib/puppet/file_serving/content.rb create mode 100644 lib/puppet/file_serving/metadata.rb create mode 100644 lib/puppet/file_serving/mount.rb create mode 100644 lib/puppet/indirector/content/file.rb create mode 100644 lib/puppet/indirector/metadata/ral.rb create mode 100644 lib/puppet/util/checksums.rb (limited to 'lib/puppet') 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..234f66962 --- /dev/null +++ b/lib/puppet/file_serving/configuration.rb @@ -0,0 +1,261 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet' +require 'puppet/file_serving' + +class Puppet::FileServing::Configuration + + def self.create(options = {}) + unless defined?(@configuration) + @configuration = new(options) + end + @configuration + end + + def initialize(options = {}) + if options.include?(:Mount) + @passedconfig = true + unless options[:Mount].is_a?(Hash) + raise Puppet::DevError, "Invalid mount options %s" % + options[:Mount].inspect + end + + options[:Mount].each { |dir, name| + if FileTest.exists?(dir) + mount(dir, name) + end + } + mount(nil, MODULES) + else + @passedconfig = false + readconfig(false) # don't check the file the first time. + end + end + + private :initialize + + # Mount a new directory with a name. + def mount(path, name) + if @mounts.include?(name) + if @mounts[name] != path + raise FileServerError, "%s is already mounted at %s" % + [@mounts[name].path, name] + else + # it's already mounted; no problem + return + end + end + + # Let the mounts do their own error-checking. + @mounts[name] = Mount.new(name, path) + @mounts[name].info "Mounted %s" % path + + return @mounts[name] + end + + def umount(name) + @mounts.delete(name) if @mounts.include? name + end + + private + + def authcheck(file, mount, client, clientip) + # 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 + + def convert(url, client, clientip) + readconfig + + url = URI.unescape(url) + + mount, stub = splitpath(url, client) + + authcheck(url, mount, client, clientip) + + path = nil + unless path = mount.subdir(stub, client) + mount.notice "Could not find subdirectory %s" % + "//%s/%s" % [mount, stub] + return "" + end + + return mount, path + end + + # 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 + + # Return the mount for the Puppet modules; allows file copying from + # the modules. + def modules_mount(module_name, client) + # Find our environment, if we have one. + unless hostname = (client || Facter.value("hostname")) + raise ArgumentError, "Could not find hostname" + end + if node = Puppet::Node.find(hostname) + env = node.environment + else + env = nil + end + + # And use the environment to look up the module. + mod = Puppet::Module::find(module_name, env) + if mod + return @mounts[MODULES].copy(mod.name, mod.files) + else + return nil + end + end + + # Read the configuration file. + def readconfig(check = true) + return if @noreadconfig + + if check and ! @config.changed? + return + end + + newmounts = {} + begin + File.open(@config.file) { |f| + mount = nil + count = 1 + f.each { |line| + case line + when /^\s*#/: next # skip comments + when /^\s*$/: next # skip blank lines + when /\[([-\w]+)\]/: + name = $1 + if newmounts.include?(name) + raise FileServerError, "%s is already mounted at %s" % + [newmounts[name], name], count, @config.file + end + mount = Mount.new(name) + newmounts[name] = mount + when /^\s*(\w+)\s+(.+)$/: + var = $1 + value = $2 + case var + when "path": + 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 FileServerError => detail + Puppet.err "Removing mount %s: %s" % + [mount.name, detail] + newmounts.delete(mount.name) + end + end + when "allow": + value.split(/\s*,\s*/).each { |val| + begin + mount.info "allowing %s access" % val + mount.allow(val) + rescue AuthStoreError => detail + raise FileServerError.new(detail.to_s, + count, @config.file) + end + } + when "deny": + value.split(/\s*,\s*/).each { |val| + begin + mount.info "denying %s access" % val + mount.deny(val) + rescue AuthStoreError => detail + raise FileServerError.new(detail.to_s, + count, @config.file) + end + } + else + raise FileServerError.new("Invalid argument '%s'" % var, + count, @config.file) + end + else + raise FileServerError.new("Invalid line '%s'" % line.chomp, + count, @config.file) + end + count += 1 + } + } + rescue Errno::EACCES => detail + Puppet.err "FileServer error: Cannot read %s; cannot serve" % @config + #raise Puppet::Error, "Cannot read %s" % @config + rescue Errno::ENOENT => detail + Puppet.err "FileServer error: '%s' does not exist; cannot serve" % + @config + #raise Puppet::Error, "%s does not exit" % @config + #rescue FileServerError => detail + # Puppet.err "FileServer error: %s" % detail + end + + unless newmounts[MODULES] + mount = Mount.new(MODULES) + mount.allow("*") + newmounts[MODULES] = mount + end + + # Verify each of the mounts are valid. + # We let the check raise an error, so that it can raise an error + # pointing to the specific problem. + newmounts.each { |name, mount| + unless mount.valid? + raise FileServerError, "No path specified for mount %s" % + name + end + } + @mounts = newmounts + end + + # Split the path into the separate mount point and path. + def splitpath(dir, client) + # the dir is based on one of the mounts + # so first retrieve the mount path + mount = nil + path = nil + if dir =~ %r{/([-\w]+)/?} + # Strip off the mount name. + mount_name, path = dir.sub(%r{^/}, '').split(File::Separator, 2) + + unless mount = modules_mount(mount_name, client) + unless mount = @mounts[mount_name] + raise FileServerError, "Fileserver module '%s' not mounted" % mount_name + end + end + else + raise FileServerError, "Fileserver error: Invalid path '%s'" % dir + end + + 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/content.rb b/lib/puppet/file_serving/content.rb new file mode 100644 index 000000000..773ae89a5 --- /dev/null +++ b/lib/puppet/file_serving/content.rb @@ -0,0 +1,35 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/indirector' +require 'puppet/file_serving' + +# 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 :content, :terminus_class => :file + + 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..69211b2d0 --- /dev/null +++ b/lib/puppet/file_serving/metadata.rb @@ -0,0 +1,48 @@ +# +# 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' + +# A class that handles retrieving file metadata. +class Puppet::FileServing::Metadata + include Puppet::Util::Checksums + + extend Puppet::Indirector + indirects :metadata, :terminus_class => :ral + + 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 initialize(path, checksum_type = "md5") + raise ArgumentError.new("Files must be fully qualified") unless path =~ /^#{::File::SEPARATOR}/ + raise ArgumentError.new("Files must exist") unless FileTest.exists?(path) + + @path = path + + stat = File.stat(path) + @owner = stat.uid + @group = stat.gid + + # Set the octal mode, but as a string. + @mode = "%o" % (stat.mode & 007777) + + @checksum_type = checksum_type + @checksum = get_checksum + 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..719c30b16 --- /dev/null +++ b/lib/puppet/file_serving/mount.rb @@ -0,0 +1,180 @@ +# +# 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 + + attr_reader :name + + @@syncs = {} + + @@files = {} + + # 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 a content instance for a given file. + def content(short_file, client = nil) + file_instance(Puppet::FileServing::Content, short_file, client) + end + + # Return a fully qualified path, given a short path and + # possibly a client name. + def file_path(short, client = nil) + p = path(client) + raise ArgumentError.new("Mounts without paths are not usable") unless p + File.join(p, short) + 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 a metadata instance with the appropriate information provided. + def metadata(short_file, client = nil) + file_instance(Puppet::FileServing::Metadata, short_file, client) + end + + # Return the path as appropriate, expanding as necessary. + def path(client = nil) + if expandable? + return expand(@path, client) + 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.exists?(path) + raise ArgumentError, "%s does not exist" % path + end + unless FileTest.directory?(path) + raise ArgumentError, "%s 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? + if name == MODULES + return @path.nil? + else + return ! @path.nil? + end + end + + private + + # Create a map for a specific client. + def clientmap(client) + { + "h" => client.sub(/\..*$/, ""), + "H" => client, + "d" => client.sub(/[^.]+\./, "") # domain name + } + end + + # Replace % patterns as appropriate. + def expand(path, client = nil) + # This map should probably be moved into a method. + map = nil + + if client + map = clientmap(client) + 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 + + # Return an instance of the appropriate class. + def file_instance(klass, short_file, client = nil) + file = file_path(short_file, client) + + return nil unless FileTest.exists?(file) + + return klass.new(file) + end + + # Cache this manufactured map, since if it's used it's likely + # to get used a lot. + def localmap + unless defined? @@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/indirector/content/file.rb b/lib/puppet/indirector/content/file.rb new file mode 100644 index 000000000..1e0af4f12 --- /dev/null +++ b/lib/puppet/indirector/content/file.rb @@ -0,0 +1,10 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/indirector/file' + +class Puppet::FileServing::Content::File < Puppet::Indirector::File + desc "Retrieve file contents from disk." +end diff --git a/lib/puppet/indirector/metadata/ral.rb b/lib/puppet/indirector/metadata/ral.rb new file mode 100644 index 000000000..a06253315 --- /dev/null +++ b/lib/puppet/indirector/metadata/ral.rb @@ -0,0 +1,15 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/metadata' +require 'puppet/indirector/code' + +class Puppet::FileServing::Metadata::Ral < Puppet::Indirector::Code + desc "Retrieve file metadata using Puppet's Resource Abstraction Layer. + Returns everything about the file except its content." + + def find(file) + Puppet::Node::Facts.new(key, Facter.to_hash) + end +end 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 -- cgit From 8bf519681f3c464f87b853a1e9b07743fa26bace Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Wed, 17 Oct 2007 11:44:37 -0500 Subject: Oops, forgot this file in my last commit. I moved the checksum code to a separate module. --- lib/puppet/checksum.rb | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'lib/puppet') 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 -- cgit From 2718b638d1df7fe37941952e396d84d1eff1efc9 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 18 Oct 2007 11:56:45 -0500 Subject: This is the first mostly functional commit of the new file serving structure. The next step is to hook it up to the indirection so we can start writing integration tests to see if we can actually serve up files. --- lib/puppet/file_serving/configuration.rb | 230 +++++++----------------- lib/puppet/file_serving/configuration/parser.rb | 123 +++++++++++++ lib/puppet/file_serving/mount.rb | 80 +++++---- 3 files changed, 237 insertions(+), 196 deletions(-) create mode 100644 lib/puppet/file_serving/configuration/parser.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb index 234f66962..b1512e14e 100644 --- a/lib/puppet/file_serving/configuration.rb +++ b/lib/puppet/file_serving/configuration.rb @@ -4,37 +4,56 @@ require 'puppet' require 'puppet/file_serving' +require 'puppet/file_serving/mount' class Puppet::FileServing::Configuration + require 'puppet/file_serving/configuration/parser' - def self.create(options = {}) - unless defined?(@configuration) - @configuration = new(options) + @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 - @configuration + @config_fileuration end - def initialize(options = {}) - if options.include?(:Mount) - @passedconfig = true - unless options[:Mount].is_a?(Hash) - raise Puppet::DevError, "Invalid mount options %s" % - options[:Mount].inspect - end + private_class_method :new - options[:Mount].each { |dir, name| - if FileTest.exists?(dir) - mount(dir, name) - end - } - mount(nil, MODULES) - else - @passedconfig = false - readconfig(false) # don't check the file the first time. - end + # Return a content instance. + def content(path, options = {}) + mount, file_path = splitpath(path, options[:node]) + + return nil unless mount + + mount.file_instance :content, 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 - private :initialize + # Return a metadata instance. + def metadata(path, options = {}) + mount, file_path = splitpath(path, options[:node]) + + return nil unless mount + + mount.file_instance :metadata, file_path, options + end # Mount a new directory with a name. def mount(path, name) @@ -55,44 +74,17 @@ class Puppet::FileServing::Configuration return @mounts[name] 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 - def authcheck(file, mount, client, clientip) - # 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 - - def convert(url, client, clientip) - readconfig - - url = URI.unescape(url) - - mount, stub = splitpath(url, client) - - authcheck(url, mount, client, clientip) - - path = nil - unless path = mount.subdir(stub, client) - mount.notice "Could not find subdirectory %s" % - "//%s/%s" % [mount, stub] - return "" - end - - return mount, path - end - # Deal with ignore parameters. def handleignore(children, path, ignore) ignore.each { |ignore| @@ -106,7 +98,6 @@ class Puppet::FileServing::Configuration # Return the mount for the Puppet modules; allows file copying from # the modules. def modules_mount(module_name, client) - # Find our environment, if we have one. unless hostname = (client || Facter.value("hostname")) raise ArgumentError, "Could not find hostname" end @@ -119,7 +110,7 @@ class Puppet::FileServing::Configuration # And use the environment to look up the module. mod = Puppet::Module::find(module_name, env) if mod - return @mounts[MODULES].copy(mod.name, mod.files) + return @mounts[Mount::MODULES].copy(mod.name, mod.files) else return nil end @@ -127,123 +118,38 @@ class Puppet::FileServing::Configuration # Read the configuration file. def readconfig(check = true) - return if @noreadconfig + config = Puppet[:fileserverconfig] + + return unless FileTest.exists?(config) - if check and ! @config.changed? + @parser ||= Puppet::FileServing::Configuration::Parser.new(config) + + if check and ! @parser.changed? return end - newmounts = {} begin - File.open(@config.file) { |f| - mount = nil - count = 1 - f.each { |line| - case line - when /^\s*#/: next # skip comments - when /^\s*$/: next # skip blank lines - when /\[([-\w]+)\]/: - name = $1 - if newmounts.include?(name) - raise FileServerError, "%s is already mounted at %s" % - [newmounts[name], name], count, @config.file - end - mount = Mount.new(name) - newmounts[name] = mount - when /^\s*(\w+)\s+(.+)$/: - var = $1 - value = $2 - case var - when "path": - 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 FileServerError => detail - Puppet.err "Removing mount %s: %s" % - [mount.name, detail] - newmounts.delete(mount.name) - end - end - when "allow": - value.split(/\s*,\s*/).each { |val| - begin - mount.info "allowing %s access" % val - mount.allow(val) - rescue AuthStoreError => detail - raise FileServerError.new(detail.to_s, - count, @config.file) - end - } - when "deny": - value.split(/\s*,\s*/).each { |val| - begin - mount.info "denying %s access" % val - mount.deny(val) - rescue AuthStoreError => detail - raise FileServerError.new(detail.to_s, - count, @config.file) - end - } - else - raise FileServerError.new("Invalid argument '%s'" % var, - count, @config.file) - end - else - raise FileServerError.new("Invalid line '%s'" % line.chomp, - count, @config.file) - end - count += 1 - } - } - rescue Errno::EACCES => detail - Puppet.err "FileServer error: Cannot read %s; cannot serve" % @config - #raise Puppet::Error, "Cannot read %s" % @config - rescue Errno::ENOENT => detail - Puppet.err "FileServer error: '%s' does not exist; cannot serve" % - @config - #raise Puppet::Error, "%s does not exit" % @config - #rescue FileServerError => detail - # Puppet.err "FileServer error: %s" % detail - end - - unless newmounts[MODULES] - mount = Mount.new(MODULES) - mount.allow("*") - newmounts[MODULES] = mount + newmounts = @parser.parse + @mounts = newmounts + rescue => detail + Puppet.err "Error parsing fileserver configuration: %s; using old configuration" % detail end - - # Verify each of the mounts are valid. - # We let the check raise an error, so that it can raise an error - # pointing to the specific problem. - newmounts.each { |name, mount| - unless mount.valid? - raise FileServerError, "No path specified for mount %s" % - name - end - } - @mounts = newmounts end # Split the path into the separate mount point and path. - def splitpath(dir, client) + def splitpath(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 = nil - path = nil - if dir =~ %r{/([-\w]+)/?} - # Strip off the mount name. - mount_name, path = dir.sub(%r{^/}, '').split(File::Separator, 2) - - unless mount = modules_mount(mount_name, client) - unless mount = @mounts[mount_name] - raise FileServerError, "Fileserver module '%s' not mounted" % mount_name - end - end - else - raise FileServerError, "Fileserver error: Invalid path '%s'" % dir - end + mount = path = nil + # Strip off the mount name. + mount_name, path = uri.sub(%r{^/}, '').split(File::Separator, 2) + + return nil unless mount = modules_mount(mount_name, node) || @mounts[mount_name] if path == "" path = nil diff --git a/lib/puppet/file_serving/configuration/parser.rb b/lib/puppet/file_serving/configuration/parser.rb new file mode 100644 index 000000000..5b17d9801 --- /dev/null +++ b/lib/puppet/file_serving/configuration/parser.rb @@ -0,0 +1,123 @@ +require 'puppet/file_serving/configuration' +require 'puppet/util/loadedfile' + +class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile + Mount = Puppet::FileServing::Mount + + # 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[Mount::MODULES] + mount = Mount.new(Mount::MODULES) + mount.allow("*") + @mounts[Mount::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 == Mount::MODULES + Puppet.warning "The '#{Mount::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/mount.rb b/lib/puppet/file_serving/mount.rb index 719c30b16..be67e6d2b 100644 --- a/lib/puppet/file_serving/mount.rb +++ b/lib/puppet/file_serving/mount.rb @@ -13,11 +13,12 @@ require 'puppet/file_serving/content' class Puppet::FileServing::Mount < Puppet::Network::AuthStore include Puppet::Util::Logging - attr_reader :name + # A constant that defines how we refer to our modules mount. + MODULES = "modules" - @@syncs = {} + InstanceTypes = {:metadata => Puppet::FileServing::Metadata, :content => Puppet::FileServing::Content} - @@files = {} + attr_reader :name # Return a new mount with the same properties as +self+, except # with a different name and path. @@ -28,17 +29,27 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore return result end - # Return a content instance for a given file. - def content(short_file, client = nil) - file_instance(Puppet::FileServing::Content, short_file, client) + # Return an instance of the appropriate class. + def file_instance(return_type, short_file, options = {}) + raise(ArgumentError, "Invalid file type %s" % return_type) unless InstanceTypes.include?(return_type) + + file = file_path(short_file, options[:node]) + + return nil unless FileTest.exists?(file) + + return InstanceTypes[return_type].new(file) end # Return a fully qualified path, given a short path and # possibly a client name. - def file_path(short, client = nil) - p = path(client) - raise ArgumentError.new("Mounts without paths are not usable") unless p - File.join(p, short) + 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. @@ -57,15 +68,10 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore super() end - # Return a metadata instance with the appropriate information provided. - def metadata(short_file, client = nil) - file_instance(Puppet::FileServing::Metadata, short_file, client) - end - # Return the path as appropriate, expanding as necessary. - def path(client = nil) + def path(node = nil) if expandable? - return expand(@path, client) + return expand(@path, node) else return @path end @@ -114,22 +120,37 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore private - # Create a map for a specific client. - def clientmap(client) + # 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" => client.sub(/\..*$/, ""), - "H" => client, - "d" => client.sub(/[^.]+\./, "") # domain name + "h" => node.sub(/\..*$/, ""), + "H" => node, + "d" => node.sub(/[^.]+\./, "") # domain name } end # Replace % patterns as appropriate. - def expand(path, client = nil) + def expand(path, node = nil) # This map should probably be moved into a method. map = nil - if client - map = clientmap(client) + if node + map = clientmap(node) else Puppet.notice "No client; expanding '%s' with local host" % path @@ -155,15 +176,6 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore end end - # Return an instance of the appropriate class. - def file_instance(klass, short_file, client = nil) - file = file_path(short_file, client) - - return nil unless FileTest.exists?(file) - - return klass.new(file) - end - # Cache this manufactured map, since if it's used it's likely # to get used a lot. def localmap -- cgit From 815618568d88daf373fb057333c962e31a4b748b Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 18 Oct 2007 12:24:05 -0500 Subject: Renaming the file_serving/{content,metadata} indirections so that they make more sense in the REST API, and creating stub tests for the indirection termini. Now it's on to create the rest of the tests for them. --- lib/puppet/file_serving/content.rb | 2 +- lib/puppet/file_serving/metadata.rb | 2 +- lib/puppet/indirector/content/file.rb | 10 ---------- lib/puppet/indirector/file_content.rb | 5 +++++ lib/puppet/indirector/file_content/file.rb | 11 +++++++++++ lib/puppet/indirector/file_metadata.rb | 5 +++++ lib/puppet/indirector/file_metadata/ral.rb | 16 ++++++++++++++++ lib/puppet/indirector/metadata/ral.rb | 15 --------------- 8 files changed, 39 insertions(+), 27 deletions(-) delete mode 100644 lib/puppet/indirector/content/file.rb create mode 100644 lib/puppet/indirector/file_content.rb create mode 100644 lib/puppet/indirector/file_content/file.rb create mode 100644 lib/puppet/indirector/file_metadata.rb create mode 100644 lib/puppet/indirector/file_metadata/ral.rb delete mode 100644 lib/puppet/indirector/metadata/ral.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb index 773ae89a5..216072e49 100644 --- a/lib/puppet/file_serving/content.rb +++ b/lib/puppet/file_serving/content.rb @@ -10,7 +10,7 @@ require 'puppet/file_serving' # asked for. class Puppet::FileServing::Content extend Puppet::Indirector - indirects :content, :terminus_class => :file + indirects :file_content, :terminus_class => :file attr_reader :path diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 69211b2d0..29a395c6a 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -12,7 +12,7 @@ class Puppet::FileServing::Metadata include Puppet::Util::Checksums extend Puppet::Indirector - indirects :metadata, :terminus_class => :ral + indirects :file_metadata, :terminus_class => :ral attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum diff --git a/lib/puppet/indirector/content/file.rb b/lib/puppet/indirector/content/file.rb deleted file mode 100644 index 1e0af4f12..000000000 --- a/lib/puppet/indirector/content/file.rb +++ /dev/null @@ -1,10 +0,0 @@ -# -# Created by Luke Kanies on 2007-10-16. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/file_serving/content' -require 'puppet/indirector/file' - -class Puppet::FileServing::Content::File < Puppet::Indirector::File - desc "Retrieve file contents from disk." -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/file.rb b/lib/puppet/indirector/file_content/file.rb new file mode 100644 index 000000000..2723142af --- /dev/null +++ b/lib/puppet/indirector/file_content/file.rb @@ -0,0 +1,11 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/indirector/file_content' +require 'puppet/indirector/file' + +class Puppet::Indirector::FileContent::File < Puppet::Indirector::File + desc "Retrieve file contents from disk." +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/ral.rb b/lib/puppet/indirector/file_metadata/ral.rb new file mode 100644 index 000000000..5777b1774 --- /dev/null +++ b/lib/puppet/indirector/file_metadata/ral.rb @@ -0,0 +1,16 @@ +# +# 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/indirector/code' + +class Puppet::Indirector::FileMetadata::Ral < Puppet::Indirector::Code + desc "Retrieve file metadata using Puppet's Resource Abstraction Layer. + Returns everything about the file except its content." + + def find(file) + Puppet::Node::Facts.new(key, Facter.to_hash) + end +end diff --git a/lib/puppet/indirector/metadata/ral.rb b/lib/puppet/indirector/metadata/ral.rb deleted file mode 100644 index a06253315..000000000 --- a/lib/puppet/indirector/metadata/ral.rb +++ /dev/null @@ -1,15 +0,0 @@ -# -# Created by Luke Kanies on 2007-10-16. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/file_serving/metadata' -require 'puppet/indirector/code' - -class Puppet::FileServing::Metadata::Ral < Puppet::Indirector::Code - desc "Retrieve file metadata using Puppet's Resource Abstraction Layer. - Returns everything about the file except its content." - - def find(file) - Puppet::Node::Facts.new(key, Facter.to_hash) - end -end -- cgit From 33d7dc0141328dc9ae042c992404943ebaf78d0d Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 18 Oct 2007 14:36:38 -0500 Subject: I'm working on making file serving work in the indirector now, so I added two abilities to the indirections: Models can specify a module to extend the indirection instance with, and indirections will use a :select_terminus method, if it's available, to select the terminus to use for finding. (It's currently only used for finding, not destroying or saving.) The upshot is that a model can have a module that handles terminus selection for it, and then extend its indirection with that module. This will allow me to use the local terminus when the protocol is 'file' and the REST terminus when the protocol is 'puppet'. It should also open the door for other protocols if they become available. --- lib/puppet/file_serving/content.rb | 3 ++- lib/puppet/file_serving/terminus_selector.rb | 26 +++++++++++++++++++++ lib/puppet/indirector.rb | 12 +--------- lib/puppet/indirector/file_content/file.rb | 3 +++ lib/puppet/indirector/indirection.rb | 35 ++++++++++++++++++++++------ 5 files changed, 60 insertions(+), 19 deletions(-) create mode 100644 lib/puppet/file_serving/terminus_selector.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb index 216072e49..53e52cd2d 100644 --- a/lib/puppet/file_serving/content.rb +++ b/lib/puppet/file_serving/content.rb @@ -4,13 +4,14 @@ 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, :terminus_class => :file + indirects :file_content, :terminus_class => :file, :extend => Puppet::FileServing::TerminusSelector attr_reader :path diff --git a/lib/puppet/file_serving/terminus_selector.rb b/lib/puppet/file_serving/terminus_selector.rb new file mode 100644 index 000000000..0cec4bf98 --- /dev/null +++ b/lib/puppet/file_serving/terminus_selector.rb @@ -0,0 +1,26 @@ +# +# 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} + + # Pick an appropriate terminus based on the protocol. + def select_terminus(uri) + # Short-circuit to :local if it's a fully-qualified path. + return PROTOCOL_MAP["file"] if uri =~ /^#{::File::SEPARATOR}/ + begin + uri = URI.parse(URI.escape(uri)) + rescue => detail + raise ArgumentError, "Could not understand URI %s: %s" % [uri, detail.to_s] + end + + return PROTOCOL_MAP[uri.scheme] || raise(ArgumentError, "URI protocol '%s' is not supported for file serving" % uri.scheme) + 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/file.rb b/lib/puppet/indirector/file_content/file.rb index 2723142af..bb9dc6998 100644 --- a/lib/puppet/indirector/file_content/file.rb +++ b/lib/puppet/indirector/file_content/file.rb @@ -8,4 +8,7 @@ require 'puppet/indirector/file' class Puppet::Indirector::FileContent::File < Puppet::Indirector::File desc "Retrieve file contents from disk." + + def find(path) + end end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 0d814c5ef..313117b25 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -48,6 +48,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) @@ -55,11 +68,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. @@ -91,11 +99,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] -- cgit From 56b83fe1bd1b08ccd3cc8a857e3a993790d2a983 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 18 Oct 2007 14:38:12 -0500 Subject: Renaming the file serving indirection termini to match the standards I set in the TerminusSelector. --- lib/puppet/indirector/file_content/file.rb | 14 -------------- lib/puppet/indirector/file_content/local.rb | 14 ++++++++++++++ lib/puppet/indirector/file_metadata/local.rb | 16 ++++++++++++++++ lib/puppet/indirector/file_metadata/ral.rb | 16 ---------------- 4 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 lib/puppet/indirector/file_content/file.rb create mode 100644 lib/puppet/indirector/file_content/local.rb create mode 100644 lib/puppet/indirector/file_metadata/local.rb delete mode 100644 lib/puppet/indirector/file_metadata/ral.rb (limited to 'lib/puppet') diff --git a/lib/puppet/indirector/file_content/file.rb b/lib/puppet/indirector/file_content/file.rb deleted file mode 100644 index bb9dc6998..000000000 --- a/lib/puppet/indirector/file_content/file.rb +++ /dev/null @@ -1,14 +0,0 @@ -# -# Created by Luke Kanies on 2007-10-16. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/file_serving/content' -require 'puppet/indirector/file_content' -require 'puppet/indirector/file' - -class Puppet::Indirector::FileContent::File < Puppet::Indirector::File - desc "Retrieve file contents from disk." - - def find(path) - end -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..bb9dc6998 --- /dev/null +++ b/lib/puppet/indirector/file_content/local.rb @@ -0,0 +1,14 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/indirector/file_content' +require 'puppet/indirector/file' + +class Puppet::Indirector::FileContent::File < Puppet::Indirector::File + desc "Retrieve file contents from disk." + + def find(path) + end +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..5777b1774 --- /dev/null +++ b/lib/puppet/indirector/file_metadata/local.rb @@ -0,0 +1,16 @@ +# +# 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/indirector/code' + +class Puppet::Indirector::FileMetadata::Ral < Puppet::Indirector::Code + desc "Retrieve file metadata using Puppet's Resource Abstraction Layer. + Returns everything about the file except its content." + + def find(file) + Puppet::Node::Facts.new(key, Facter.to_hash) + end +end diff --git a/lib/puppet/indirector/file_metadata/ral.rb b/lib/puppet/indirector/file_metadata/ral.rb deleted file mode 100644 index 5777b1774..000000000 --- a/lib/puppet/indirector/file_metadata/ral.rb +++ /dev/null @@ -1,16 +0,0 @@ -# -# 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/indirector/code' - -class Puppet::Indirector::FileMetadata::Ral < Puppet::Indirector::Code - desc "Retrieve file metadata using Puppet's Resource Abstraction Layer. - Returns everything about the file except its content." - - def find(file) - Puppet::Node::Facts.new(key, Facter.to_hash) - end -end -- cgit From 64c6700bc7530f1213e124d248cc176a7cfe6180 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 18 Oct 2007 14:43:08 -0500 Subject: Fixing all of the classes that I just renamed, and adding the TerminusSelector module to the File Metadata indirection. --- lib/puppet/file_serving/content.rb | 2 +- lib/puppet/file_serving/metadata.rb | 3 ++- lib/puppet/indirector/file_content/local.rb | 2 +- lib/puppet/indirector/file_metadata/local.rb | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb index 53e52cd2d..38ca80fb0 100644 --- a/lib/puppet/file_serving/content.rb +++ b/lib/puppet/file_serving/content.rb @@ -11,7 +11,7 @@ require 'puppet/file_serving/terminus_selector' # asked for. class Puppet::FileServing::Content extend Puppet::Indirector - indirects :file_content, :terminus_class => :file, :extend => Puppet::FileServing::TerminusSelector + indirects :file_content, :extend => Puppet::FileServing::TerminusSelector attr_reader :path diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 29a395c6a..73e1f53d4 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -6,13 +6,14 @@ 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, :terminus_class => :ral + indirects :file_metadata, :extend => Puppet::FileServing::TerminusSelector attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum diff --git a/lib/puppet/indirector/file_content/local.rb b/lib/puppet/indirector/file_content/local.rb index bb9dc6998..34d1c7794 100644 --- a/lib/puppet/indirector/file_content/local.rb +++ b/lib/puppet/indirector/file_content/local.rb @@ -6,7 +6,7 @@ require 'puppet/file_serving/content' require 'puppet/indirector/file_content' require 'puppet/indirector/file' -class Puppet::Indirector::FileContent::File < Puppet::Indirector::File +class Puppet::Indirector::FileContent::Local < Puppet::Indirector::File desc "Retrieve file contents from disk." def find(path) diff --git a/lib/puppet/indirector/file_metadata/local.rb b/lib/puppet/indirector/file_metadata/local.rb index 5777b1774..f6cfa1f1c 100644 --- a/lib/puppet/indirector/file_metadata/local.rb +++ b/lib/puppet/indirector/file_metadata/local.rb @@ -6,7 +6,7 @@ require 'puppet/file_serving/metadata' require 'puppet/indirector/file_metadata' require 'puppet/indirector/code' -class Puppet::Indirector::FileMetadata::Ral < Puppet::Indirector::Code +class Puppet::Indirector::FileMetadata::Local < Puppet::Indirector::Code desc "Retrieve file metadata using Puppet's Resource Abstraction Layer. Returns everything about the file except its content." -- cgit From fc607513faa54d64186a674a36d81ea010745569 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 18 Oct 2007 20:47:56 -0500 Subject: I've now split the file-serving termini into two separate types (in addition to Rest): A local terminus that just uses direct file paths, and a mounts terminus that uses the file server to figure out what the path should be. It looks like it also makes sense to split the 'mounts' terminus further, so there is a 'modules' terminus used to look files up in the terminus. I've added some integration tests to verify that everything is hooked together correctly. Lastly, I added a directory for shared behaviours. There's a ton of duplication in this setup, because the Content and Metadata classes behave almost but not quite identically across the board. --- lib/puppet/file_serving/configuration.rb | 34 ++++++++++----------------- lib/puppet/file_serving/metadata.rb | 19 +++++++++------ lib/puppet/file_serving/terminus_helper.rb | 22 +++++++++++++++++ lib/puppet/file_serving/terminus_selector.rb | 20 +++++++++++----- lib/puppet/indirector/file_content/local.rb | 9 ++++++- lib/puppet/indirector/file_content/mounts.rb | 28 ++++++++++++++++++++++ lib/puppet/indirector/file_content/rest.rb | 12 ++++++++++ lib/puppet/indirector/file_metadata/local.rb | 16 +++++++++---- lib/puppet/indirector/file_metadata/mounts.rb | 28 ++++++++++++++++++++++ lib/puppet/indirector/file_metadata/rest.rb | 12 ++++++++++ 10 files changed, 160 insertions(+), 40 deletions(-) create mode 100644 lib/puppet/file_serving/terminus_helper.rb create mode 100644 lib/puppet/indirector/file_content/mounts.rb create mode 100644 lib/puppet/indirector/file_content/rest.rb create mode 100644 lib/puppet/indirector/file_metadata/mounts.rb create mode 100644 lib/puppet/indirector/file_metadata/rest.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb index b1512e14e..6cd99b267 100644 --- a/lib/puppet/file_serving/configuration.rb +++ b/lib/puppet/file_serving/configuration.rb @@ -30,13 +30,22 @@ class Puppet::FileServing::Configuration # Return a content instance. def content(path, options = {}) - mount, file_path = splitpath(path, options[:node]) + mount, file_path = split_path(path, options[:node]) return nil unless mount mount.file_instance :content, file_path, options end + # Search for a file. + def file_path(key, options = {}) + mount, file_path = split_path(key, options[:node]) + + return nil unless mount + + return mount.file(file_path, options) + end + def initialize @mounts = {} @config_file = nil @@ -48,32 +57,13 @@ class Puppet::FileServing::Configuration # Return a metadata instance. def metadata(path, options = {}) - mount, file_path = splitpath(path, options[:node]) + mount, file_path = split_path(path, options[:node]) return nil unless mount mount.file_instance :metadata, file_path, options end - # Mount a new directory with a name. - def mount(path, name) - if @mounts.include?(name) - if @mounts[name] != path - raise FileServerError, "%s is already mounted at %s" % - [@mounts[name].path, name] - else - # it's already mounted; no problem - return - end - end - - # Let the mounts do their own error-checking. - @mounts[name] = Mount.new(name, path) - @mounts[name].info "Mounted %s" % path - - return @mounts[name] - end - # Is a given mount available? def mounted?(name) @mounts.include?(name) @@ -137,7 +127,7 @@ class Puppet::FileServing::Configuration end # Split the path into the separate mount point and path. - def splitpath(uri, node) + def split_path(uri, node) # Reparse the configuration if necessary. readconfig diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 73e1f53d4..7adb66981 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -23,12 +23,7 @@ class Puppet::FileServing::Metadata @checksum_type = type end - def initialize(path, checksum_type = "md5") - raise ArgumentError.new("Files must be fully qualified") unless path =~ /^#{::File::SEPARATOR}/ - raise ArgumentError.new("Files must exist") unless FileTest.exists?(path) - - @path = path - + def get_attributes stat = File.stat(path) @owner = stat.uid @group = stat.gid @@ -36,10 +31,20 @@ class Puppet::FileServing::Metadata # Set the octal mode, but as a string. @mode = "%o" % (stat.mode & 007777) - @checksum_type = checksum_type @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. diff --git a/lib/puppet/file_serving/terminus_helper.rb b/lib/puppet/file_serving/terminus_helper.rb new file mode 100644 index 000000000..b7f560c57 --- /dev/null +++ b/lib/puppet/file_serving/terminus_helper.rb @@ -0,0 +1,22 @@ +# +# Created by Luke Kanies on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require 'uri' +require 'puppet/file_serving' +require 'puppet/file_serving/configuration' + +module Puppet::FileServing::TerminusHelper + 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 diff --git a/lib/puppet/file_serving/terminus_selector.rb b/lib/puppet/file_serving/terminus_selector.rb index 0cec4bf98..4525a8570 100644 --- a/lib/puppet/file_serving/terminus_selector.rb +++ b/lib/puppet/file_serving/terminus_selector.rb @@ -9,18 +9,26 @@ 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} + PROTOCOL_MAP = {"puppet" => :rest, "file" => :local, "puppetmounts" => :mounts} # Pick an appropriate terminus based on the protocol. - def select_terminus(uri) + def select_terminus(full_uri) # Short-circuit to :local if it's a fully-qualified path. - return PROTOCOL_MAP["file"] if uri =~ /^#{::File::SEPARATOR}/ + return PROTOCOL_MAP["file"] if full_uri =~ /^#{::File::SEPARATOR}/ begin - uri = URI.parse(URI.escape(uri)) + uri = URI.parse(URI.escape(full_uri)) rescue => detail - raise ArgumentError, "Could not understand URI %s: %s" % [uri, detail.to_s] + raise ArgumentError, "Could not understand URI %s: %s" % [full_uri, detail.to_s] end - return PROTOCOL_MAP[uri.scheme] || raise(ArgumentError, "URI protocol '%s' is not supported for file serving" % uri.scheme) + 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 + + return terminus end end diff --git a/lib/puppet/indirector/file_content/local.rb b/lib/puppet/indirector/file_content/local.rb index 34d1c7794..a597fea55 100644 --- a/lib/puppet/indirector/file_content/local.rb +++ b/lib/puppet/indirector/file_content/local.rb @@ -3,12 +3,19 @@ # Copyright (c) 2007. All rights reserved. require 'puppet/file_serving/content' +require 'puppet/file_serving/terminus_helper' require 'puppet/indirector/file_content' require 'puppet/indirector/file' class Puppet::Indirector::FileContent::Local < Puppet::Indirector::File desc "Retrieve file contents from disk." - def find(path) + include Puppet::FileServing::TerminusHelper + + def find(key) + uri = key2uri(key) + + return nil unless FileTest.exists?(uri.path) + Puppet::FileServing::Content.new uri.path end 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..3d147d65a --- /dev/null +++ b/lib/puppet/indirector/file_content/mounts.rb @@ -0,0 +1,28 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/content' +require 'puppet/file_serving/terminus_helper' +require 'puppet/indirector/file_content' +require 'puppet/indirector/code' + +class Puppet::Indirector::FileContent::Mounts < Puppet::Indirector::Code + desc "Retrieve file contents using Puppet's fileserver." + + include Puppet::FileServing::TerminusHelper + + # This way it can be cleared or whatever and we aren't retaining + # a reference to the old one. + def configuration + Puppet::FileServing::Configuration.create + end + + def find(key) + uri = key2uri(key) + + return nil unless path = configuration.file_path(uri.path) and FileTest.exists?(path) + + Puppet::FileServing::Content.new path + end +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..9e2de360c --- /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/file_serving/terminus_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/local.rb b/lib/puppet/indirector/file_metadata/local.rb index f6cfa1f1c..e1d774cc8 100644 --- a/lib/puppet/indirector/file_metadata/local.rb +++ b/lib/puppet/indirector/file_metadata/local.rb @@ -4,13 +4,21 @@ require 'puppet/file_serving/metadata' require 'puppet/indirector/file_metadata' +require 'puppet/file_serving/terminus_helper' require 'puppet/indirector/code' class Puppet::Indirector::FileMetadata::Local < Puppet::Indirector::Code - desc "Retrieve file metadata using Puppet's Resource Abstraction Layer. - Returns everything about the file except its content." + desc "Retrieve file metadata directly from the local filesystem." - def find(file) - Puppet::Node::Facts.new(key, Facter.to_hash) + include Puppet::FileServing::TerminusHelper + + def find(key) + uri = key2uri(key) + + return nil unless FileTest.exists?(uri.path) + data = Puppet::FileServing::Metadata.new uri.path + data.get_attributes + + return data 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..6d7fe15c6 --- /dev/null +++ b/lib/puppet/indirector/file_metadata/mounts.rb @@ -0,0 +1,28 @@ +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/file_serving/metadata' +require 'puppet/file_serving/terminus_helper' +require 'puppet/indirector/file_metadata' +require 'puppet/indirector/code' + +class Puppet::Indirector::FileMetadata::Mounts < Puppet::Indirector::Code + desc "Retrieve file metadata using Puppet's fileserver." + + include Puppet::FileServing::TerminusHelper + + # This way it can be cleared or whatever and we aren't retaining + # a reference to the old one. + def configuration + Puppet::FileServing::Configuration.create + end + + def find(key) + uri = key2uri(key) + + return nil unless path = configuration.file_path(uri.path) and FileTest.exists?(path) + + Puppet::FileServing::Metadata.new path + end +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..dcf875b25 --- /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/file_serving/terminus_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 -- cgit From 08099b7a383987e292357f285e05933e10205660 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 19 Oct 2007 17:35:40 -0500 Subject: File serving now works. I've tested a couple of ways to use it, and added integration tests at the most important hook points. This provides the final class structure for all of these classes, but a lot of the class names are pretty bad, so I'm planning on going through all of them (especially the file_server stuff) and renaming. The functionality is all here for finding files, though (finally). Once the classes are renamed, I'll be adding searching ability (which will enable the recursive file copies) and then adding the link management and enabling ignoring files. --- lib/puppet/file_serving/configuration.rb | 43 ++-------------------- lib/puppet/file_serving/configuration/parser.rb | 11 +++--- lib/puppet/file_serving/mount.rb | 28 ++++++--------- lib/puppet/file_serving/terminus_helper.rb | 22 ------------ lib/puppet/file_serving/terminus_selector.rb | 4 +++ lib/puppet/indirector/file_content/local.rb | 10 +++--- lib/puppet/indirector/file_content/modules.rb | 11 ++++++ lib/puppet/indirector/file_content/mounts.rb | 21 ++--------- lib/puppet/indirector/file_content/rest.rb | 2 +- lib/puppet/indirector/file_metadata/local.rb | 6 ++-- lib/puppet/indirector/file_metadata/modules.rb | 17 +++++++++ lib/puppet/indirector/file_metadata/mounts.rb | 21 ++--------- lib/puppet/indirector/file_metadata/rest.rb | 2 +- lib/puppet/indirector/file_server.rb | 34 ++++++++++++++++++ lib/puppet/indirector/module_files.rb | 47 +++++++++++++++++++++++++ lib/puppet/indirector/rest.rb | 2 +- lib/puppet/util/uri_helper.rb | 22 ++++++++++++ 17 files changed, 171 insertions(+), 132 deletions(-) delete mode 100644 lib/puppet/file_serving/terminus_helper.rb create mode 100644 lib/puppet/indirector/file_content/modules.rb create mode 100644 lib/puppet/indirector/file_metadata/modules.rb create mode 100644 lib/puppet/indirector/file_server.rb create mode 100644 lib/puppet/indirector/module_files.rb create mode 100644 lib/puppet/util/uri_helper.rb (limited to 'lib/puppet') diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb index 6cd99b267..03be1b9dd 100644 --- a/lib/puppet/file_serving/configuration.rb +++ b/lib/puppet/file_serving/configuration.rb @@ -28,21 +28,14 @@ class Puppet::FileServing::Configuration private_class_method :new - # Return a content instance. - def content(path, options = {}) - mount, file_path = split_path(path, options[:node]) - - return nil unless mount - - mount.file_instance :content, file_path, options - end - # 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 @@ -55,15 +48,6 @@ class Puppet::FileServing::Configuration readconfig(false) end - # Return a metadata instance. - def metadata(path, options = {}) - mount, file_path = split_path(path, options[:node]) - - return nil unless mount - - mount.file_instance :metadata, file_path, options - end - # Is a given mount available? def mounted?(name) @mounts.include?(name) @@ -85,27 +69,6 @@ class Puppet::FileServing::Configuration return children end - # Return the mount for the Puppet modules; allows file copying from - # the modules. - def modules_mount(module_name, client) - unless hostname = (client || Facter.value("hostname")) - raise ArgumentError, "Could not find hostname" - end - if node = Puppet::Node.find(hostname) - env = node.environment - else - env = nil - end - - # And use the environment to look up the module. - mod = Puppet::Module::find(module_name, env) - if mod - return @mounts[Mount::MODULES].copy(mod.name, mod.files) - else - return nil - end - end - # Read the configuration file. def readconfig(check = true) config = Puppet[:fileserverconfig] @@ -139,7 +102,7 @@ class Puppet::FileServing::Configuration # Strip off the mount name. mount_name, path = uri.sub(%r{^/}, '').split(File::Separator, 2) - return nil unless mount = modules_mount(mount_name, node) || @mounts[mount_name] + return nil unless mount = @mounts[mount_name] if path == "" path = nil diff --git a/lib/puppet/file_serving/configuration/parser.rb b/lib/puppet/file_serving/configuration/parser.rb index 5b17d9801..707c3f9b1 100644 --- a/lib/puppet/file_serving/configuration/parser.rb +++ b/lib/puppet/file_serving/configuration/parser.rb @@ -3,6 +3,7 @@ require 'puppet/util/loadedfile' class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile Mount = Puppet::FileServing::Mount + MODULES = 'modules' # Parse our configuration file. def parse @@ -52,10 +53,10 @@ class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile # Add the mount for getting files from modules. def add_module_mount - unless @mounts[Mount::MODULES] - mount = Mount.new(Mount::MODULES) + unless @mounts[MODULES] + mount = Mount.new(MODULES) mount.allow("*") - @mounts[Mount::MODULES] = mount + @mounts[MODULES] = mount end end @@ -98,8 +99,8 @@ class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile # Set the path for a mount. def path(mount, value) - if mount.name == Mount::MODULES - Puppet.warning "The '#{Mount::MODULES}' module can not have a path. Ignoring attempt to set it" + if mount.name == MODULES + Puppet.warning "The '#{MODULES}' module can not have a path. Ignoring attempt to set it" else begin mount.path = value diff --git a/lib/puppet/file_serving/mount.rb b/lib/puppet/file_serving/mount.rb index be67e6d2b..8e5bd03e8 100644 --- a/lib/puppet/file_serving/mount.rb +++ b/lib/puppet/file_serving/mount.rb @@ -13,10 +13,12 @@ require 'puppet/file_serving/content' class Puppet::FileServing::Mount < Puppet::Network::AuthStore include Puppet::Util::Logging - # A constant that defines how we refer to our modules mount. - MODULES = "modules" + @@localmap = nil - InstanceTypes = {:metadata => Puppet::FileServing::Metadata, :content => Puppet::FileServing::Content} + # Clear the cache. This is only ever used for testing. + def self.clear_cache + @@localmap = nil + end attr_reader :name @@ -30,14 +32,12 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore end # Return an instance of the appropriate class. - def file_instance(return_type, short_file, options = {}) - raise(ArgumentError, "Invalid file type %s" % return_type) unless InstanceTypes.include?(return_type) - + def file(short_file, options = {}) file = file_path(short_file, options[:node]) return nil unless FileTest.exists?(file) - return InstanceTypes[return_type].new(file) + return file end # Return a fully qualified path, given a short path and @@ -85,11 +85,8 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore # Mark that we're expandable. @expandable = true else - unless FileTest.exists?(path) - raise ArgumentError, "%s does not exist" % path - end unless FileTest.directory?(path) - raise ArgumentError, "%s is not a 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 @@ -111,11 +108,7 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore # Verify our configuration is valid. This should really check to # make sure at least someone will be allowed, but, eh. def valid? - if name == MODULES - return @path.nil? - else - return ! @path.nil? - end + return ! @path.nil? end private @@ -157,6 +150,7 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore # Else, use the local information map = localmap() end + path.gsub(/%(.)/) do |v| key = $1 if key == "%" @@ -179,7 +173,7 @@ class Puppet::FileServing::Mount < Puppet::Network::AuthStore # Cache this manufactured map, since if it's used it's likely # to get used a lot. def localmap - unless defined? @@localmap + unless @@localmap @@localmap = { "h" => Facter.value("hostname"), "H" => [Facter.value("hostname"), diff --git a/lib/puppet/file_serving/terminus_helper.rb b/lib/puppet/file_serving/terminus_helper.rb deleted file mode 100644 index b7f560c57..000000000 --- a/lib/puppet/file_serving/terminus_helper.rb +++ /dev/null @@ -1,22 +0,0 @@ -# -# Created by Luke Kanies on 2007-10-16. -# Copyright (c) 2007. All rights reserved. - -require 'uri' -require 'puppet/file_serving' -require 'puppet/file_serving/configuration' - -module Puppet::FileServing::TerminusHelper - 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 diff --git a/lib/puppet/file_serving/terminus_selector.rb b/lib/puppet/file_serving/terminus_selector.rb index 4525a8570..08009cd2b 100644 --- a/lib/puppet/file_serving/terminus_selector.rb +++ b/lib/puppet/file_serving/terminus_selector.rb @@ -29,6 +29,10 @@ module Puppet::FileServing::TerminusSelector terminus = :mounts end + if uri.path =~ /^\/modules\b/ and terminus == :mounts + terminus = :modules + end + return terminus end end diff --git a/lib/puppet/indirector/file_content/local.rb b/lib/puppet/indirector/file_content/local.rb index a597fea55..e429c6c25 100644 --- a/lib/puppet/indirector/file_content/local.rb +++ b/lib/puppet/indirector/file_content/local.rb @@ -3,19 +3,21 @@ # 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::FileServing::TerminusHelper + include Puppet::Util::URIHelper - def find(key) + def find(key, options = {}) uri = key2uri(key) return nil unless FileTest.exists?(uri.path) - Puppet::FileServing::Content.new 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 index 3d147d65a..b11fc628c 100644 --- a/lib/puppet/indirector/file_content/mounts.rb +++ b/lib/puppet/indirector/file_content/mounts.rb @@ -3,26 +3,9 @@ # Copyright (c) 2007. All rights reserved. require 'puppet/file_serving/content' -require 'puppet/file_serving/terminus_helper' require 'puppet/indirector/file_content' -require 'puppet/indirector/code' +require 'puppet/indirector/file_server' -class Puppet::Indirector::FileContent::Mounts < Puppet::Indirector::Code +class Puppet::Indirector::FileContent::Mounts < Puppet::Indirector::FileServer desc "Retrieve file contents using Puppet's fileserver." - - include Puppet::FileServing::TerminusHelper - - # This way it can be cleared or whatever and we aren't retaining - # a reference to the old one. - def configuration - Puppet::FileServing::Configuration.create - end - - def find(key) - uri = key2uri(key) - - return nil unless path = configuration.file_path(uri.path) and FileTest.exists?(path) - - Puppet::FileServing::Content.new path - end end diff --git a/lib/puppet/indirector/file_content/rest.rb b/lib/puppet/indirector/file_content/rest.rb index 9e2de360c..31df7626d 100644 --- a/lib/puppet/indirector/file_content/rest.rb +++ b/lib/puppet/indirector/file_content/rest.rb @@ -3,7 +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/rest' diff --git a/lib/puppet/indirector/file_metadata/local.rb b/lib/puppet/indirector/file_metadata/local.rb index e1d774cc8..f40d4ce43 100644 --- a/lib/puppet/indirector/file_metadata/local.rb +++ b/lib/puppet/indirector/file_metadata/local.rb @@ -4,19 +4,19 @@ require 'puppet/file_serving/metadata' require 'puppet/indirector/file_metadata' -require 'puppet/file_serving/terminus_helper' +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::FileServing::TerminusHelper + include Puppet::Util::URIHelper def find(key) uri = key2uri(key) return nil unless FileTest.exists?(uri.path) - data = Puppet::FileServing::Metadata.new uri.path + data = model.new(uri.path) data.get_attributes return data 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 index 6d7fe15c6..b1e3b32fd 100644 --- a/lib/puppet/indirector/file_metadata/mounts.rb +++ b/lib/puppet/indirector/file_metadata/mounts.rb @@ -3,26 +3,9 @@ # Copyright (c) 2007. All rights reserved. require 'puppet/file_serving/metadata' -require 'puppet/file_serving/terminus_helper' require 'puppet/indirector/file_metadata' -require 'puppet/indirector/code' +require 'puppet/indirector/file_server' -class Puppet::Indirector::FileMetadata::Mounts < Puppet::Indirector::Code +class Puppet::Indirector::FileMetadata::Mounts < Puppet::Indirector::FileServer desc "Retrieve file metadata using Puppet's fileserver." - - include Puppet::FileServing::TerminusHelper - - # This way it can be cleared or whatever and we aren't retaining - # a reference to the old one. - def configuration - Puppet::FileServing::Configuration.create - end - - def find(key) - uri = key2uri(key) - - return nil unless path = configuration.file_path(uri.path) and FileTest.exists?(path) - - Puppet::FileServing::Metadata.new path - end end diff --git a/lib/puppet/indirector/file_metadata/rest.rb b/lib/puppet/indirector/file_metadata/rest.rb index dcf875b25..0f3d9c6fd 100644 --- a/lib/puppet/indirector/file_metadata/rest.rb +++ b/lib/puppet/indirector/file_metadata/rest.rb @@ -3,7 +3,7 @@ # Copyright (c) 2007. All rights reserved. require 'puppet/file_serving/metadata' -require 'puppet/file_serving/terminus_helper' +require 'puppet/util/uri_helper' require 'puppet/indirector/file_metadata' require 'puppet/indirector/rest' 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/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/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 -- cgit