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