summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-09-23 23:50:43 -0500
committerLuke Kanies <luke@madstop.com>2008-09-23 23:50:43 -0500
commitbb23861e334e617b544c11bc75a35c40b36185a2 (patch)
tree18da91858e4fded78a56d673fc69014fdf266676
parente31df2f7f5e98c524b68cd724cfaa3e308e7b9a1 (diff)
parentac5db5ec115455e54090542870847820357739a2 (diff)
downloadpuppet-bb23861e334e617b544c11bc75a35c40b36185a2.tar.gz
puppet-bb23861e334e617b544c11bc75a35c40b36185a2.tar.xz
puppet-bb23861e334e617b544c11bc75a35c40b36185a2.zip
Merge branch 'feature/master/1481'
This merges in the new fileserving code -- we're now using REST to do fileserving, rather than xmlrpc. Conflicts: lib/puppet/parameter.rb lib/puppet/type/file.rb spec/unit/type/file.rb
-rwxr-xr-xbin/puppetd1
-rw-r--r--lib/puppet/file_serving/base.rb (renamed from lib/puppet/file_serving/file_base.rb)14
-rw-r--r--lib/puppet/file_serving/configuration.rb5
-rw-r--r--lib/puppet/file_serving/content.rb37
-rw-r--r--lib/puppet/file_serving/fileset.rb4
-rw-r--r--lib/puppet/file_serving/indirection_hooks.rb45
-rw-r--r--lib/puppet/file_serving/metadata.rb6
-rw-r--r--lib/puppet/file_serving/terminus_helper.rb14
-rw-r--r--lib/puppet/indirector/direct_file_server.rb10
-rw-r--r--lib/puppet/indirector/file_metadata/file.rb4
-rw-r--r--lib/puppet/indirector/file_metadata/modules.rb2
-rw-r--r--lib/puppet/indirector/file_server.rb3
-rw-r--r--lib/puppet/indirector/indirection.rb10
-rw-r--r--lib/puppet/indirector/module_files.rb9
-rw-r--r--lib/puppet/indirector/request.rb53
-rw-r--r--lib/puppet/indirector/rest.rb42
-rw-r--r--lib/puppet/network/format.rb12
-rw-r--r--lib/puppet/network/format_handler.rb5
-rw-r--r--lib/puppet/network/formats.rb23
-rwxr-xr-xlib/puppet/network/handler/fileserver.rb2
-rw-r--r--lib/puppet/network/http/handler.rb13
-rw-r--r--lib/puppet/network/http/mongrel/rest.rb2
-rw-r--r--lib/puppet/network/http/webrick/rest.rb2
-rw-r--r--lib/puppet/type.rb11
-rw-r--r--lib/puppet/type/file.rb451
-rwxr-xr-xlib/puppet/type/file/source.rb201
-rwxr-xr-xspec/integration/file_serving/configuration.rb4
-rwxr-xr-xspec/integration/file_serving/content.rb2
-rwxr-xr-xspec/integration/file_serving/fileset.rb14
-rwxr-xr-xspec/integration/file_serving/metadata.rb2
-rwxr-xr-xspec/integration/file_serving/terminus_helper.rb22
-rwxr-xr-xspec/integration/indirector/direct_file_server.rb31
-rwxr-xr-xspec/integration/indirector/module_files.rb28
-rwxr-xr-xspec/integration/type/file.rb128
-rw-r--r--spec/shared_behaviours/file_server_terminus.rb20
-rw-r--r--spec/shared_behaviours/file_serving.rb23
-rw-r--r--spec/spec_helper.rb6
-rwxr-xr-xspec/unit/file_serving/base.rb (renamed from spec/unit/file_serving/file_base.rb)65
-rwxr-xr-xspec/unit/file_serving/configuration.rb47
-rwxr-xr-xspec/unit/file_serving/content.rb91
-rwxr-xr-xspec/unit/file_serving/fileset.rb7
-rwxr-xr-xspec/unit/file_serving/indirection_hooks.rb156
-rwxr-xr-xspec/unit/file_serving/metadata.rb59
-rwxr-xr-xspec/unit/file_serving/terminus_helper.rb59
-rwxr-xr-xspec/unit/indirector/direct_file_server.rb11
-rwxr-xr-xspec/unit/indirector/file_metadata/file.rb10
-rwxr-xr-xspec/unit/indirector/file_metadata/modules.rb4
-rwxr-xr-xspec/unit/indirector/file_server.rb57
-rwxr-xr-xspec/unit/indirector/indirection.rb16
-rwxr-xr-xspec/unit/indirector/module_files.rb69
-rwxr-xr-xspec/unit/indirector/request.rb63
-rwxr-xr-xspec/unit/indirector/rest.rb84
-rwxr-xr-xspec/unit/network/format.rb17
-rwxr-xr-xspec/unit/network/format_handler.rb31
-rwxr-xr-xspec/unit/network/formats.rb30
-rwxr-xr-xspec/unit/network/http/handler.rb31
-rwxr-xr-xspec/unit/network/http/mongrel/rest.rb6
-rwxr-xr-xspec/unit/network/http/webrick/rest.rb6
-rwxr-xr-xspec/unit/type/file.rb497
-rwxr-xr-xspec/unit/type/file/source.rb282
60 files changed, 1980 insertions, 979 deletions
diff --git a/bin/puppetd b/bin/puppetd
index c17654892..758494c4f 100755
--- a/bin/puppetd
+++ b/bin/puppetd
@@ -162,7 +162,6 @@ trap(:INT) do
end
require 'puppet'
-require 'puppet/executables/client/certhandler'
require 'puppet/network/client'
require 'getoptlong'
diff --git a/lib/puppet/file_serving/file_base.rb b/lib/puppet/file_serving/base.rb
index e87d683aa..c59a54786 100644
--- a/lib/puppet/file_serving/file_base.rb
+++ b/lib/puppet/file_serving/base.rb
@@ -6,8 +6,10 @@ require 'puppet/file_serving'
# The base class for Content and Metadata; provides common
# functionality like the behaviour around links.
-class Puppet::FileServing::FileBase
- attr_accessor :key
+class Puppet::FileServing::Base
+ # This is for external consumers to store the source that was used
+ # to retrieve the metadata.
+ attr_accessor :source
# Does our file exist?
def exist?
@@ -21,17 +23,15 @@ class Puppet::FileServing::FileBase
# Return the full path to our file. Fails if there's no path set.
def full_path
- raise(ArgumentError, "You must set a path to get a file's path") unless self.path
-
- if relative_path.nil? or relative_path == ""
+ if relative_path.nil? or relative_path == "" or relative_path == "."
path
else
File.join(path, relative_path)
end
end
- def initialize(key, options = {})
- @key = key
+ def initialize(path, options = {})
+ self.path = path
@links = :manage
options.each do |param, value|
diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb
index 9c38aaa19..bceecc30c 100644
--- a/lib/puppet/file_serving/configuration.rb
+++ b/lib/puppet/file_serving/configuration.rb
@@ -98,13 +98,14 @@ class Puppet::FileServing::Configuration
# Reparse the configuration if necessary.
readconfig
- raise(ArgumentError, "Cannot find file: Invalid path '%s'" % uri) unless uri =~ %r{/([-\w]+)/?}
+ 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)
+ mount_name, path = uri.split(File::Separator, 2)
return nil unless mount = @mounts[mount_name]
diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb
index 9398513e7..c1ecff749 100644
--- a/lib/puppet/file_serving/content.rb
+++ b/lib/puppet/file_serving/content.rb
@@ -4,31 +4,46 @@
require 'puppet/indirector'
require 'puppet/file_serving'
-require 'puppet/file_serving/file_base'
+require 'puppet/file_serving/base'
require 'puppet/file_serving/indirection_hooks'
# A class that handles retrieving file contents.
# It only reads the file when its content is specifically
# asked for.
-class Puppet::FileServing::Content < Puppet::FileServing::FileBase
+class Puppet::FileServing::Content < Puppet::FileServing::Base
extend Puppet::Indirector
indirects :file_content, :extend => Puppet::FileServing::IndirectionHooks
- attr_reader :path
+ attr_writer :content
+
+ def self.supported_formats
+ [:raw]
+ end
+
+ def self.from_raw(content)
+ instance = new("/this/is/a/fake/path")
+ instance.content = content
+ instance
+ end
+
+ # Collect our data.
+ def collect
+ return if stat.ftype == "directory"
+ content
+ end
# Read the content of our file in.
def content
- # This stat can raise an exception, too.
- raise(ArgumentError, "Cannot read the contents of links unless following links") if stat().ftype == "symlink"
+ unless defined?(@content) and @content
+ # This stat can raise an exception, too.
+ raise(ArgumentError, "Cannot read the contents of links unless following links") if stat().ftype == "symlink"
- ::File.read(full_path())
+ @content = ::File.read(full_path())
+ end
+ @content
end
- # Just return the file contents as the yaml. This allows us to
- # avoid escaping or any such thing. LAK:NOTE Not really sure how
- # this will behave if the file contains yaml... I think the far
- # side needs to understand that it's a plain string.
- def to_yaml
+ def to_raw
content
end
end
diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb
index fe54350b1..a90734a2b 100644
--- a/lib/puppet/file_serving/fileset.rb
+++ b/lib/puppet/file_serving/fileset.rb
@@ -20,7 +20,7 @@ class Puppet::FileServing::Fileset
# Now strip off the leading path, so each file becomes relative, and remove
# any slashes that might end up at the beginning of the path.
- result = files.collect { |file| file.sub(%r{^#{@path}/*}, '') }
+ result = files.collect { |file| file.sub(@path, '').sub(%r{^/},'') }
# And add the path itself.
result.unshift(".")
@@ -30,6 +30,8 @@ class Puppet::FileServing::Fileset
# Should we ignore this path?
def ignore?(path)
+ return false if @ignore == [nil]
+
# 'detect' normally returns the found result, whereas we just want true/false.
! @ignore.detect { |pattern| File.fnmatch?(pattern, path) }.nil?
end
diff --git a/lib/puppet/file_serving/indirection_hooks.rb b/lib/puppet/file_serving/indirection_hooks.rb
index 66ed169dc..15564cf3d 100644
--- a/lib/puppet/file_serving/indirection_hooks.rb
+++ b/lib/puppet/file_serving/indirection_hooks.rb
@@ -9,36 +9,37 @@ require 'puppet/file_serving'
# in file-serving indirections. This is necessary because
# the terminus varies based on the URI asked for.
module Puppet::FileServing::IndirectionHooks
- PROTOCOL_MAP = {"puppet" => :rest, "file" => :file, "puppetmounts" => :file_server}
+ PROTOCOL_MAP = {"puppet" => :rest, "file" => :file}
# Pick an appropriate terminus based on the protocol.
def select_terminus(request)
- full_uri = request.key
- # Short-circuit to :file if it's a fully-qualified path.
- return PROTOCOL_MAP["file"] if full_uri =~ /^#{::File::SEPARATOR}/
- begin
- uri = URI.parse(URI.escape(full_uri))
- rescue => detail
- raise ArgumentError, "Could not understand URI %s: %s" % [full_uri, detail.to_s]
- end
+ # We rely on the request's parsing of the URI.
- terminus = PROTOCOL_MAP[uri.scheme] || raise(ArgumentError, "URI protocol '%s' is not supported for file serving" % uri.scheme)
+ # Short-circuit to :file if it's a fully-qualified path or specifies a 'file' protocol.
+ return PROTOCOL_MAP["file"] if request.key =~ /^#{::File::SEPARATOR}/
+ return PROTOCOL_MAP["file"] if request.protocol == "file"
- # 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 = :file_server
+ # We're heading over the wire the protocol is 'puppet' and we've got a server name or we're not named 'puppet'
+ if request.protocol == "puppet" and (request.server or Puppet.settings[:name] != "puppet")
+ return PROTOCOL_MAP["puppet"]
+ end
+
+ if request.protocol and PROTOCOL_MAP[request.protocol].nil?
+ raise(ArgumentError, "URI protocol '%s' is not currently supported for file serving" % request.protocol)
end
+ # If we're still here, we're using the file_server or modules.
+
# This is the backward-compatible module terminus.
- if terminus == :file_server and uri.path =~ %r{^/([^/]+)\b}
- modname = $1
- if modname == "modules"
- terminus = :modules
- elsif terminus(:modules).find_module(modname, request.options[:node])
- Puppet.warning "DEPRECATION NOTICE: Found file '%s' in module without using the 'modules' mount; please prefix path with '/modules'" % uri.path
- terminus = :modules
- end
+ modname = request.key.split("/")[0]
+
+ if modname == "modules"
+ terminus = :modules
+ elsif terminus(:modules).find_module(modname, request.options[:node])
+ Puppet.warning "DEPRECATION NOTICE: Found file '%s' in module without using the 'modules' mount; please prefix path with 'modules/'" % request.key
+ terminus = :modules
+ else
+ terminus = :file_server
end
return terminus
diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb
index beecaef48..1cc3fa355 100644
--- a/lib/puppet/file_serving/metadata.rb
+++ b/lib/puppet/file_serving/metadata.rb
@@ -5,12 +5,12 @@
require 'puppet'
require 'puppet/indirector'
require 'puppet/file_serving'
-require 'puppet/file_serving/file_base'
+require 'puppet/file_serving/base'
require 'puppet/util/checksums'
require 'puppet/file_serving/indirection_hooks'
# A class that handles retrieving file metadata.
-class Puppet::FileServing::Metadata < Puppet::FileServing::FileBase
+class Puppet::FileServing::Metadata < Puppet::FileServing::Base
include Puppet::Util::Checksums
@@ -47,7 +47,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::FileBase
# Retrieve the attributes for this file, relative to a base directory.
# Note that File.stat raises Errno::ENOENT if the file is absent and this
# method does not catch that exception.
- def collect_attributes
+ def collect
real_path = full_path()
stat = stat()
@owner = stat.uid
diff --git a/lib/puppet/file_serving/terminus_helper.rb b/lib/puppet/file_serving/terminus_helper.rb
index e5da0e29f..b51e27297 100644
--- a/lib/puppet/file_serving/terminus_helper.rb
+++ b/lib/puppet/file_serving/terminus_helper.rb
@@ -9,10 +9,20 @@ require 'puppet/file_serving/fileset'
module Puppet::FileServing::TerminusHelper
# Create model instances for all files in a fileset.
def path2instances(request, path)
- args = [:links, :ignore, :recurse].inject({}) { |hash, param| hash[param] = request.options[param] if request.options[param]; hash }
+ args = [:links, :ignore, :recurse].inject({}) do |hash, param|
+ if request.options.include?(param) # use 'include?' so the values can be false
+ hash[param] = request.options[param]
+ elsif request.options.include?(param.to_s)
+ hash[param] = request.options[param.to_s]
+ end
+ hash[param] = true if hash[param] == "true"
+ hash[param] = false if hash[param] == "false"
+ hash
+ end
Puppet::FileServing::Fileset.new(path, args).files.collect do |file|
- inst = model.new(File.join(request.key, file), :path => path, :relative_path => file)
+ inst = model.new(path, :relative_path => file)
inst.links = request.options[:links] if request.options[:links]
+ inst.collect
inst
end
end
diff --git a/lib/puppet/indirector/direct_file_server.rb b/lib/puppet/indirector/direct_file_server.rb
index b3b4886f3..bcda92366 100644
--- a/lib/puppet/indirector/direct_file_server.rb
+++ b/lib/puppet/indirector/direct_file_server.rb
@@ -12,16 +12,14 @@ class Puppet::Indirector::DirectFileServer < Puppet::Indirector::Terminus
include Puppet::FileServing::TerminusHelper
def find(request)
- uri = key2uri(request.key)
- return nil unless FileTest.exists?(uri.path)
- instance = model.new(request.key, :path => uri.path)
+ return nil unless FileTest.exists?(request.key)
+ instance = model.new(request.key)
instance.links = request.options[:links] if request.options[:links]
return instance
end
def search(request)
- uri = key2uri(request.key)
- return nil unless FileTest.exists?(uri.path)
- path2instances(request, uri.path)
+ return nil unless FileTest.exists?(request.key)
+ path2instances(request, request.key)
end
end
diff --git a/lib/puppet/indirector/file_metadata/file.rb b/lib/puppet/indirector/file_metadata/file.rb
index c46015c38..bb586489d 100644
--- a/lib/puppet/indirector/file_metadata/file.rb
+++ b/lib/puppet/indirector/file_metadata/file.rb
@@ -11,7 +11,7 @@ class Puppet::Indirector::FileMetadata::File < Puppet::Indirector::DirectFileSer
def find(request)
return unless data = super
- data.collect_attributes
+ data.collect
return data
end
@@ -19,7 +19,7 @@ class Puppet::Indirector::FileMetadata::File < Puppet::Indirector::DirectFileSer
def search(request)
return unless result = super
- result.each { |instance| instance.collect_attributes }
+ result.each { |instance| instance.collect }
return result
end
diff --git a/lib/puppet/indirector/file_metadata/modules.rb b/lib/puppet/indirector/file_metadata/modules.rb
index 5ed7a8a45..4598c2175 100644
--- a/lib/puppet/indirector/file_metadata/modules.rb
+++ b/lib/puppet/indirector/file_metadata/modules.rb
@@ -11,7 +11,7 @@ class Puppet::Indirector::FileMetadata::Modules < Puppet::Indirector::ModuleFile
def find(*args)
return unless instance = super
- instance.collect_attributes
+ instance.collect
instance
end
end
diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb
index b0df7ff5d..46a590f9c 100644
--- a/lib/puppet/indirector/file_server.rb
+++ b/lib/puppet/indirector/file_server.rb
@@ -25,8 +25,9 @@ class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus
# Find our key using the fileserver.
def find(request)
return nil unless path = find_path(request)
- result = model.new(request.key, :path => path)
+ result = model.new(path)
result.links = request.options[:links] if request.options[:links]
+ result.collect
return result
end
diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb
index a9fff75b8..04c3aed23 100644
--- a/lib/puppet/indirector/indirection.rb
+++ b/lib/puppet/indirector/indirection.rb
@@ -262,7 +262,11 @@ class Puppet::Indirector::Indirection
return unless terminus.respond_to?(:authorized?)
unless terminus.authorized?(request)
- raise ArgumentError, "Not authorized to call %s on %s with %s" % [request.method, request.key, request.options.inspect]
+ msg = "Not authorized to call %s on %s" % [request.method, request.key]
+ unless request.options.empty?
+ msg += " with %s" % request.options.inspect
+ end
+ raise ArgumentError, msg
end
end
@@ -270,7 +274,9 @@ class Puppet::Indirector::Indirection
def prepare(request)
# Pick our terminus.
if respond_to?(:select_terminus)
- terminus_name = select_terminus(request)
+ unless terminus_name = select_terminus(request)
+ raise ArgumentError, "Could not determine appropriate terminus for %s" % request
+ end
else
terminus_name = terminus_class
end
diff --git a/lib/puppet/indirector/module_files.rb b/lib/puppet/indirector/module_files.rb
index cf5c29cab..7c5cf278f 100644
--- a/lib/puppet/indirector/module_files.rb
+++ b/lib/puppet/indirector/module_files.rb
@@ -21,7 +21,7 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus
# Make sure our file path starts with /modules, so that we authorize
# against the 'modules' mount.
- path = uri.path =~ /^\/modules/ ? uri.path : "/modules" + uri.path
+ path = uri.path =~ /^modules\// ? uri.path : "modules/" + uri.path
configuration.authorized?(path, :node => request.node, :ipaddress => request.ip)
end
@@ -30,7 +30,7 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus
def find(request)
return nil unless path = find_path(request)
- result = model.new(request.key, :path => path)
+ result = model.new(path)
result.links = request.options[:links] if request.options[:links]
return result
end
@@ -66,9 +66,8 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus
def find_path(request)
uri = key2uri(request.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)
+ # Strip off modules/ if it's there -- that's how requests get routed to this terminus.
+ module_name, relative_path = uri.path.sub(/^modules\//, '').sub(%r{^/}, '').split(File::Separator, 2)
# And use the environment to look up the module.
return nil unless mod = find_module(module_name, request.node)
diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb
index 98fa38885..49cc01aab 100644
--- a/lib/puppet/indirector/request.rb
+++ b/lib/puppet/indirector/request.rb
@@ -1,10 +1,13 @@
require 'puppet/indirector'
-# Provide any attributes or functionality needed for indirected
-# instances.
+# This class encapsulates all of the information you need to make an
+# Indirection call, and as a a result also handles REST calls. It's somewhat
+# analogous to an HTTP Request object, except tuned for our Indirector.
class Puppet::Indirector::Request
attr_accessor :indirection_name, :key, :method, :options, :instance, :node, :ip, :authenticated
+ attr_accessor :server, :port, :uri, :protocol
+
# Is this an authenticated request?
def authenticated?
# Double negative, so we just get true or false
@@ -28,7 +31,15 @@ class Puppet::Indirector::Request
end
if key.is_a?(String) or key.is_a?(Symbol)
- @key = key
+ # If the request key is a URI, then we need to treat it specially,
+ # because it rewrites the key. We could otherwise strip server/port/etc
+ # info out in the REST class, but it seemed bad design for the REST
+ # class to rewrite the key.
+ if key.to_s =~ /^\w+:\/\// # it's a URI
+ set_uri_key(key)
+ else
+ @key = key
+ end
else
@instance = key
@key = @instance.name
@@ -39,4 +50,40 @@ class Puppet::Indirector::Request
def indirection
Puppet::Indirector::Indirection.instance(@indirection_name)
end
+
+ # Are we trying to interact with multiple resources, or just one?
+ def plural?
+ method == :search
+ end
+
+ private
+
+ # Parse the key as a URI, setting attributes appropriately.
+ def set_uri_key(key)
+ @uri = key
+ begin
+ uri = URI.parse(URI.escape(key))
+ rescue => detail
+ raise ArgumentError, "Could not understand URL %s: %s" % [source, detail.to_s]
+ end
+
+ # Just short-circuit these to full paths
+ if uri.scheme == "file"
+ @key = uri.path
+ return
+ end
+
+ @server = uri.host if uri.host
+
+ # If the URI class can look up the scheme, it will provide a port,
+ # otherwise it will default to '0'.
+ if uri.port.to_i == 0 and uri.scheme == "puppet"
+ @port = Puppet.settings[:masterport].to_i
+ else
+ @port = uri.port.to_i
+ end
+
+ @protocol = uri.scheme
+ @key = uri.path.sub(/^\//, '')
+ end
end
diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb
index 4389dfb7e..5ac25f02d 100644
--- a/lib/puppet/indirector/rest.rb
+++ b/lib/puppet/indirector/rest.rb
@@ -1,8 +1,33 @@
require 'net/http'
require 'uri'
+require 'puppet/network/http_pool'
+
# Access objects via REST
class Puppet::Indirector::REST < Puppet::Indirector::Terminus
+
+ class << self
+ attr_reader :server_setting, :port_setting
+ end
+
+ # Specify the setting that we should use to get the server name.
+ def self.use_server_setting(setting)
+ @server_setting = setting
+ end
+
+ def self.server
+ return Puppet.settings[server_setting || :server]
+ end
+
+ # Specify the setting that we should use to get the port.
+ def self.use_port_setting(setting)
+ @port_setting = setting
+ end
+
+ def self.port
+ return Puppet.settings[port_setting || :masterport].to_i
+ end
+
# Figure out the content type, turn that into a format, and use the format
# to extract the body of the response.
def deserialize(response, multiple = false)
@@ -33,20 +58,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus
end
def network(request)
- if request.key =~ /^\w+:\/\// # it looks like a URI
- begin
- uri = URI.parse(URI.escape(request.key))
- rescue => detail
- raise ArgumentError, "Could not understand URL %s: %s" % [source, detail.to_s]
- end
- server = uri.host || Puppet[:server]
- port = uri.port.to_i == 0 ? Puppet[:masterport].to_i : uri.port.to_i
- else
- server = Puppet[:server]
- port = Puppet[:masterport].to_i
- end
-
- Puppet::Network::HttpPool.http_instance(server, port)
+ Puppet::Network::HttpPool.http_instance(request.server || self.class.server, request.port || self.class.port)
end
def find(request)
@@ -77,7 +89,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus
private
- # Create the qurey string, if options are present.
+ # Create the query string, if options are present.
def query_string(request)
return "" unless request.options and ! request.options.empty?
"?" + request.options.collect { |key, value| "%s=%s" % [key, value] }.join("&")
diff --git a/lib/puppet/network/format.rb b/lib/puppet/network/format.rb
index 5f259fa49..21aead7cc 100644
--- a/lib/puppet/network/format.rb
+++ b/lib/puppet/network/format.rb
@@ -5,7 +5,7 @@ require 'puppet/provider/confiner'
class Puppet::Network::Format
include Puppet::Provider::Confiner
- attr_reader :name, :mime
+ attr_reader :name, :mime, :weight
def initialize(name, options = {}, &block)
@name = name.to_s.downcase.intern
@@ -17,6 +17,13 @@ class Puppet::Network::Format
self.mime = "text/%s" % name
end
+ if weight = options[:weight]
+ @weight = weight
+ options.delete(:weight)
+ else
+ @weight = 5
+ end
+
unless options.empty?
raise ArgumentError, "Unsupported option(s) %s" % options.keys
end
@@ -55,7 +62,8 @@ class Puppet::Network::Format
end
def supported?(klass)
- klass.respond_to?(intern_method) and
+ suitable? and
+ klass.respond_to?(intern_method) and
klass.respond_to?(intern_multiple_method) and
klass.respond_to?(render_multiple_method) and
klass.instance_methods.include?(render_method)
diff --git a/lib/puppet/network/format_handler.rb b/lib/puppet/network/format_handler.rb
index 4c9f4e59e..f3c3380e1 100644
--- a/lib/puppet/network/format_handler.rb
+++ b/lib/puppet/network/format_handler.rb
@@ -61,7 +61,10 @@ module Puppet::Network::FormatHandler
end
def supported_formats
- format_handler.formats.collect { |f| format_handler.format(f) }.find_all { |f| f.supported?(self) }.collect { |f| f.name }
+ format_handler.formats.collect { |f| format_handler.format(f) }.find_all { |f| f.supported?(self) }.collect { |f| f.name }.sort do |a, b|
+ # It's an inverse sort -- higher weight formats go first.
+ format_handler.format(b).weight <=> format_handler.format(a).weight
+ end
end
end
diff --git a/lib/puppet/network/formats.rb b/lib/puppet/network/formats.rb
index 8e4c59fb3..85e8ce6f8 100644
--- a/lib/puppet/network/formats.rb
+++ b/lib/puppet/network/formats.rb
@@ -42,7 +42,7 @@ Puppet::Network::FormatHandler.create(:marshal, :mime => "text/marshal") do
Marshal.dump(instance)
end
- # Yaml monkey-patches Array, so this works.
+ # Marshal monkey-patches Array, so this works.
def render_multiple(instances)
Marshal.dump(instances)
end
@@ -54,3 +54,24 @@ Puppet::Network::FormatHandler.create(:marshal, :mime => "text/marshal") do
end
Puppet::Network::FormatHandler.create(:s, :mime => "text/plain")
+
+# A very low-weight format so it'll never get chosen automatically.
+Puppet::Network::FormatHandler.create(:raw, :mime => "application/x-raw", :weight => 1) do
+ def intern_multiple(klass, text)
+ raise NotImplementedError
+ end
+
+ def render_multiple(instances)
+ raise NotImplementedError
+ end
+
+ # LAK:NOTE The format system isn't currently flexible enough to handle
+ # what I need to support raw formats just for individual instances (rather
+ # than both individual and collections), but we don't yet have enough data
+ # to make a "correct" design.
+ # So, we hack it so it works for singular but fail if someone tries it
+ # on plurals.
+ def supported?(klass)
+ true
+ end
+end
diff --git a/lib/puppet/network/handler/fileserver.rb b/lib/puppet/network/handler/fileserver.rb
index 183979429..14319ef96 100755
--- a/lib/puppet/network/handler/fileserver.rb
+++ b/lib/puppet/network/handler/fileserver.rb
@@ -75,7 +75,7 @@ class Puppet::Network::Handler
return "" unless metadata.exist?
begin
- metadata.collect_attributes
+ metadata.collect
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err detail
diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb
index 6f5117b16..7c7abccf5 100644
--- a/lib/puppet/network/http/handler.rb
+++ b/lib/puppet/network/http/handler.rb
@@ -12,7 +12,18 @@ module Puppet::Network::HTTP::Handler
# Which format to use when serializing our response. Just picks
# the first value in the accept header, at this point.
def format_to_use(request)
- accept_header(request).split(/,\s*/)[0]
+ unless header = accept_header(request)
+ raise ArgumentError, "An Accept header must be provided to pick the right format"
+ end
+
+ format = nil
+ header.split(/,\s*/).each do |name|
+ next unless format = Puppet::Network::FormatHandler.format(name)
+ next unless format.suitable?
+ return name
+ end
+
+ raise "No specified acceptable formats (%s) are functional on this machine" % header
end
def initialize_for_puppet(args = {})
diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb
index d265dde86..45d21ea62 100644
--- a/lib/puppet/network/http/mongrel/rest.rb
+++ b/lib/puppet/network/http/mongrel/rest.rb
@@ -35,7 +35,7 @@ class Puppet::Network::HTTP::MongrelREST < Mongrel::HttpHandler
# return the key included in the request path
def request_key(request)
# LAK:NOTE See http://snurl.com/21zf8 [groups_google_com]
- x = request.params[Mongrel::Const::REQUEST_PATH].split('/')[2]
+ x = request.params[Mongrel::Const::REQUEST_PATH].split('/', 3)[2]
end
# return the request body
diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb
index 13f795fb2..f06914365 100644
--- a/lib/puppet/network/http/webrick/rest.rb
+++ b/lib/puppet/network/http/webrick/rest.rb
@@ -36,7 +36,7 @@ class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet
def request_key(request)
# LAK:NOTE See http://snurl.com/21zf8 [groups_google_com]
- x = request.path.split('/')[2]
+ x = request.path.split('/', 3)[2]
end
def body(request)
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 11e8c8248..eb663968e 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -793,7 +793,6 @@ class Type
obj.remove
end
@parameters.clear
- self.class.delete(self)
@parent = nil
@@ -1519,6 +1518,7 @@ class Type
# to an object...
tname, name = value
reference = Puppet::ResourceReference.new(tname, name)
+ reference.catalog = resource.catalog
# Either of the two retrieval attempts could have returned
# nil.
@@ -1932,7 +1932,10 @@ class Type
# Figure out of there are any objects we can automatically add as
# dependencies.
- def autorequire
+ def autorequire(rel_catalog = nil)
+ rel_catalog ||= catalog
+ raise(Puppet::DevError, "You cannot add relationships without a catalog") unless rel_catalog
+
reqs = []
self.class.eachautorequire { |type, block|
# Ignore any types we can't find, although that would be a bit odd.
@@ -1954,11 +1957,11 @@ class Type
next
end
end
-
+
reqs << Puppet::Relationship.new(dep, self)
}
}
-
+
return reqs
end
diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb
index c6ab2570c..370ce1b4f 100644
--- a/lib/puppet/type/file.rb
+++ b/lib/puppet/type/file.rb
@@ -155,8 +155,6 @@ module Puppet
engine, so shell metacharacters are fully supported, e.g. ``[a-z]*``.
Matches that would descend into the directory structure are ignored,
e.g., ``*/*``."
-
- defaultto false
validate do |value|
unless value.is_a?(Array) or value.is_a?(String) or value == false
@@ -277,11 +275,6 @@ module Puppet
@depthfirst = false
-
- def argument?(arg)
- @arghash.include?(arg)
- end
-
# Determine the user to write files as.
def asuser
if self.should(:owner) and ! self.should(:owner).is_a?(Symbol)
@@ -329,7 +322,23 @@ module Puppet
# Create any children via recursion or whatever.
def eval_generate
- recurse()
+ return nil unless self.recurse?
+
+ raise(Puppet::DevError, "Cannot generate resources for recursion without a catalog") unless catalog
+
+ recurse.reject do |resource|
+ catalog.resource(:file, resource[:path])
+ end.each do |child|
+ catalog.add_resource child
+ catalog.relationship_graph.add_edge self, child
+ end
+ end
+
+ def flush
+ # We want to make sure we retrieve metadata anew on each transaction.
+ @parameters.each do |name, param|
+ param.flush if param.respond_to?(:flush)
+ end
end
# Deal with backups.
@@ -455,194 +464,21 @@ module Puppet
@title.sub!(/\/$/, "") unless @title == "/"
- # Clean out as many references to any file paths as possible.
- # This was the source of many, many bugs.
- @arghash = tmphash
- @arghash.delete(self.class.namevar)
-
- [:source, :parent].each do |param|
- if @arghash.include?(param)
- @arghash.delete(param)
- end
- end
-
@stat = nil
end
-
- # Build a recursive map of a link source
- def linkrecurse(recurse)
- target = @parameters[:target].should
-
- method = :lstat
- if self[:links] == :follow
- method = :stat
- end
-
- targetstat = nil
- unless FileTest.exist?(target)
- return
- end
- # Now stat our target
- targetstat = File.send(method, target)
- unless targetstat.ftype == "directory"
- return
- end
-
- # Now that we know our corresponding target is a directory,
- # change our type
- self[:ensure] = :directory
-
- unless FileTest.readable? target
- self.notice "Cannot manage %s: permission denied" % self.name
- return
- end
-
- children = Dir.entries(target).reject { |d| d =~ /^\.+$/ }
-
- # Get rid of ignored children
- if @parameters.include?(:ignore)
- children = handleignore(children)
- end
-
- added = []
- children.each do |file|
- Dir.chdir(target) do
- longname = File.join(target, file)
-
- # Files know to create directories when recursion
- # is enabled and we're making links
- args = {
- :recurse => recurse,
- :ensure => longname
- }
-
- if child = self.newchild(file, true, args)
- added << child
- end
- end
- end
-
- added
- end
-
- # Build up a recursive map of what's around right now
- def localrecurse(recurse)
- unless FileTest.exist?(self[:path]) and self.stat.directory?
- #self.info "%s is not a directory; not recursing" %
- # self[:path]
- return
- end
-
- unless FileTest.readable? self[:path]
- self.notice "Cannot manage %s: permission denied" % self.name
- return
- end
-
- children = Dir.entries(self[:path])
-
- #Get rid of ignored children
- if @parameters.include?(:ignore)
- children = handleignore(children)
- end
-
- added = []
- children.each { |file|
- file = File.basename(file)
- next if file =~ /^\.\.?$/ # skip . and ..
- options = {:recurse => recurse}
-
- if child = self.newchild(file, true, options)
- added << child
- end
- }
-
- added
- end
# Create a new file or directory object as a child to the current
# object.
- def newchild(path, local, hash = {})
- raise(Puppet::DevError, "File recursion cannot happen without a catalog") unless catalog
-
- # make local copy of arguments
- args = symbolize_options(@arghash)
-
- # There's probably a better way to do this, but we don't want
- # to pass this info on.
- if v = args[:ensure]
- v = symbolize(v)
- args.delete(:ensure)
- end
-
- if path =~ %r{^#{File::SEPARATOR}}
- self.devfail(
- "Must pass relative paths to PFile#newchild()"
- )
- else
- path = File.join(self[:path], path)
- end
-
- args[:path] = path
-
- unless hash.include?(:recurse)
- if args.include?(:recurse)
- if args[:recurse].is_a?(Integer)
- args[:recurse] -= 1 # reduce the level of recursion
- end
- end
-
- end
-
- hash.each { |key,value|
- args[key] = value
- }
-
- child = nil
-
- # The child might already exist because 'localrecurse' runs
- # before 'sourcerecurse'. I could push the override stuff into
- # a separate method or something, but the work is the same other
- # than this last bit, so it doesn't really make sense.
- if child = catalog.resource(:file, path)
- unless child.parent.object_id == self.object_id
- self.debug "Not managing more explicit file %s" %
- path
- return nil
- end
+ def newchild(path)
+ full_path = File.join(self[:path], path)
- # This is only necessary for sourcerecurse, because we might have
- # created the object with different 'should' values than are
- # set remotely.
- unless local
- args.each { |var,value|
- next if var == :path
- next if var == :name
-
- # behave idempotently
- unless child.should(var) == value
- child[var] = value
- end
- }
- end
- return nil
- else # create it anew
- #notice "Creating new file with args %s" % args.inspect
- args[:parent] = self
- begin
- # This method is used by subclasses of :file, so use the class name rather than hard-coding
- # :file.
- return nil unless child = catalog.create_implicit_resource(self.class.name, args)
- rescue => detail
- self.notice "Cannot manage: %s" % [detail]
- return nil
- end
+ # the right-side hash wins in the merge.
+ options = to_hash.merge(:path => full_path, :implicit => true).reject { |param, value| value.nil? }
+ [:parent, :recurse, :target].each do |param|
+ options.delete(param) if options.include?(param)
end
- # LAK:FIXME This shouldn't be necessary, but as long as we're
- # modeling the relationship graph specifically, it is.
- catalog.relationship_graph.add_edge self, child
-
- return child
+ return self.class.create(options)
end
# Files handle paths specially, because they just lengthen their
@@ -672,70 +508,121 @@ module Puppet
@parameters.include?(:purge) and (self[:purge] == :true or self[:purge] == "true")
end
- # Recurse into the directory. This basically just calls 'localrecurse'
- # and maybe 'sourcerecurse', returning the collection of generated
- # files.
+ def make_children(metadata)
+ metadata.collect { |meta| newchild(meta.relative_path) }
+ end
+
+ # Recursively generate a list of file resources, which will
+ # be used to copy remote files, manage local files, and/or make links
+ # to map to another directory.
def recurse
- # are we at the end of the recursion?
- return unless self.recurse?
-
- recurse = self[:recurse]
- # we might have a string, rather than a number
- if recurse.is_a?(String)
- if recurse =~ /^[0-9]+$/
- recurse = Integer(recurse)
- else # anything else is infinite recursion
- recurse = true
- end
+ children = recurse_local
+
+ if self[:target]
+ recurse_link(children)
+ elsif self[:source]
+ recurse_remote(children)
end
- if recurse.is_a?(Integer)
- recurse -= 1
+ return children.values.sort { |a, b| a[:path] <=> b[:path] }
+ end
+
+ # A simple method for determining whether we should be recursing.
+ def recurse?
+ return false unless @parameters.include?(:recurse)
+
+ val = @parameters[:recurse].value
+
+ if val and (val == true or val > 0)
+ return true
+ else
+ return false
end
-
- children = []
-
- # We want to do link-recursing before normal recursion so that all
- # of the target stuff gets copied over correctly.
- if @parameters.include? :target and ret = self.linkrecurse(recurse)
- children += ret
+ end
+
+ # Recurse the target of the link.
+ def recurse_link(children)
+ perform_recursion(self[:target]).each do |meta|
+ if meta.relative_path == "."
+ self[:ensure] = :directory
+ next
+ end
+
+ children[meta.relative_path] ||= newchild(meta.relative_path)
+ if meta.ftype == "directory"
+ children[meta.relative_path][:ensure] = :directory
+ else
+ children[meta.relative_path][:ensure] = :link
+ children[meta.relative_path][:target] = meta.full_path
+ end
+ end
+ children
+ end
+
+ # Recurse the file itself, returning a Metadata instance for every found file.
+ def recurse_local
+ result = perform_recursion(self[:path])
+ return {} unless result
+ result.inject({}) do |hash, meta|
+ next hash if meta.relative_path == "."
+
+ hash[meta.relative_path] = newchild(meta.relative_path)
+ hash
end
- if ret = self.localrecurse(recurse)
- children += ret
+ end
+
+ # Recurse against our remote file.
+ def recurse_remote(children)
+ sourceselect = self[:sourceselect]
+
+ total = self[:source].collect do |source|
+ next unless result = perform_recursion(source)
+ result.each { |data| data.source = "%s/%s" % [source, data.relative_path] }
+ break result if result and ! result.empty? and sourceselect == :first
+ result
+ end.flatten
+
+ # This only happens if we have sourceselect == :all
+ unless sourceselect == :first
+ found = []
+ total.reject! do |data|
+ result = found.include?(data.relative_path)
+ found << data.relative_path unless found.include?(data.relative_path)
+ result
+ end
end
- # These will be files pulled in by the file source
- sourced = false
- if @parameters.include?(:source)
- ret, sourced = self.sourcerecurse(recurse)
- if ret
- children += ret
+ total.each do |meta|
+ if meta.relative_path == "."
+ property(:source).metadata = meta
+ next
end
+ children[meta.relative_path] ||= newchild(meta.relative_path)
+ children[meta.relative_path][:source] = meta.source
+ children[meta.relative_path][:checksum] = :md5 if meta.ftype == "file"
+
+ children[meta.relative_path].property(:source).metadata = meta
end
- # The purge check needs to happen after all of the other recursion.
+ # If we're purging resources, then delete any resource that isn't on the
+ # remote system.
if self.purge?
- children.each do |child|
- if (sourced and ! sourced.include?(child[:path])) or ! child.managed?
+ # Make a hash of all of the resources we found remotely -- all we need is the
+ # fast lookup, the values don't matter.
+ remotes = total.inject({}) { |hash, meta| hash[meta.relative_path] = true; hash }
+
+ children.each do |name, child|
+ unless remotes.include?(name)
child[:ensure] = :absent
end
end
end
-
+
children
end
- # A simple method for determining whether we should be recursing.
- def recurse?
- return false unless @parameters.include?(:recurse)
-
- val = @parameters[:recurse].value
-
- if val and (val == true or val > 0)
- return true
- else
- return false
- end
+ def perform_recursion(path)
+ Puppet::FileServing::Metadata.search(path, :links => self[:links], :recurse => self[:recurse], :ignore => self[:ignore])
end
# Remove the old backup.
@@ -796,108 +683,22 @@ module Puppet
# a wrapper method to make sure the file exists before doing anything
def retrieve
unless stat = self.stat(true)
- # If the file doesn't exist but we have a source, then call
- # retrieve on that property
propertyvalues = properties().inject({}) { |hash, property|
hash[property] = :absent
hash
}
+ # If the file doesn't exist but we have a source, then call
+ # retrieve on the source property so it will set the 'should'
+ # values all around.
if @parameters.include?(:source)
- propertyvalues[:source] = @parameters[:source].retrieve
+ @parameters[:source].copy_source_values
end
return propertyvalues
end
- return currentpropvalues()
- end
-
- # This recurses against the remote source and makes sure the local
- # and remote structures match. It's run after 'localrecurse'. This
- # method only does anything when its corresponding remote entry is
- # a directory; in that case, this method creates file objects that
- # correspond to any contained remote files.
- def sourcerecurse(recurse)
- # we'll set this manually as necessary
- if @arghash.include?(:ensure)
- @arghash.delete(:ensure)
- end
-
- r = false
- if recurse
- unless recurse == 0
- r = 1
- end
- end
-
- ignore = self[:ignore]
-
- result = []
- found = []
-
- # Keep track of all the files we found in the source, so we can purge
- # appropriately.
- sourced = []
-
- success = false
-
- @parameters[:source].should.each do |source|
- sourceobj, path = uri2obj(source)
-
- # okay, we've got our source object; now we need to
- # build up a local file structure to match the remote
- # one
-
- server = sourceobj.server
-
- desc = server.list(path, self[:links], r, ignore)
- if desc == ""
- next
- end
-
- success = true
-
- # Now create a new child for every file returned in the list.
- result += desc.split("\n").collect { |line|
- file, type = line.split("\t")
- next if file == "/" # skip the listing object
- name = file.sub(/^\//, '')
-
- # This makes sure that the first source *always* wins
- # for conflicting files.
- next if found.include?(name)
-
- # For directories, keep all of the sources, so that
- # sourceselect still works as planned.
- if type == "directory"
- newsource = @parameters[:source].should.collect do |tmpsource|
- tmpsource + file
- end
- else
- newsource = source + file
- end
- args = {:source => newsource}
- if type == file
- args[:recurse] = nil
- end
-
- found << name
- sourced << File.join(self[:path], name)
-
- self.newchild(name, false, args)
- }.reject {|c| c.nil? }
-
- if self[:sourceselect] == :first
- return [result, sourced]
- end
- end
-
- unless success
- raise Puppet::Error, "None of the provided sources exist"
- end
-
- return [result, sourced]
+ currentpropvalues()
end
# Set the checksum, from another property. There are multiple
diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb
index 2514d3d1e..e43706051 100755
--- a/lib/puppet/type/file/source.rb
+++ b/lib/puppet/type/file/source.rb
@@ -1,3 +1,7 @@
+
+require 'puppet/file_serving/content'
+require 'puppet/file_serving/metadata'
+
module Puppet
# Copy files from a local or remote source. This state *only* does any work
# when the remote file is an actual file; in that case, this state copies
@@ -62,8 +66,14 @@ module Puppet
uncheckable
validate do |source|
- unless @resource.uri2obj(source)
- raise Puppet::Error, "Invalid source %s" % source
+ begin
+ uri = URI.parse(URI.escape(source))
+ rescue => detail
+ self.fail "Could not understand source %s: %s" % [source, detail.to_s]
+ end
+
+ unless uri.scheme.nil? or %w{file puppet}.include?(uri.scheme)
+ self.fail "Cannot use URLs of type '%s' as source for fileserving" % [uri.scheme]
end
end
@@ -77,72 +87,67 @@ module Puppet
end
def change_to_s(currentvalue, newvalue)
- # newvalue = "{md5}" + @stats[:checksum]
+ # newvalue = "{md5}" + @metadata.checksum
if @resource.property(:ensure).retrieve == :absent
- return "creating from source %s with contents %s" % [@source, @stats[:checksum]]
+ return "creating from source %s with contents %s" % [metadata.source, @metadata.checksum]
else
- return "replacing from source %s with contents %s" % [@source, @stats[:checksum]]
+ return "replacing from source %s with contents %s" % [metadata.source, @metadata.checksum]
end
end
def checksum
- if defined?(@stats)
- @stats[:checksum]
+ if defined?(@metadata)
+ @metadata.checksum
else
nil
end
end
- # Ask the file server to describe our file.
- def describe(source)
- sourceobj, path = @resource.uri2obj(source)
- server = sourceobj.server
+ # Look up (if necessary) and return remote content.
+ def content
+ raise Puppet::DevError, "No source for content was stored with the metadata" unless metadata.source
- begin
- desc = server.describe(path, @resource[:links])
- rescue Puppet::Network::XMLRPCClientError => detail
- fail detail, "Could not describe %s: %s" % [path, detail]
+ unless defined?(@content) and @content
+ unless tmp = Puppet::FileServing::Content.find(@metadata.source)
+ fail "Could not find any content at %s" % @metadata.source
+ end
+ @content = tmp.content
end
+ @content
+ end
- return nil if desc == ""
+ # Copy the values from the source to the resource. Yay.
+ def copy_source_values
+ devfail "Somehow got asked to copy source values without any metadata" unless metadata
- # Collect everything except the checksum
- values = desc.split("\t")
- other = values.pop
- args = {}
- pinparams.zip(values).each { |param, value|
- if value =~ /^[0-9]+$/
- value = value.to_i
- end
- unless value.nil?
- args[param] = value
+ # Take each of the stats and set them as states on the local file
+ # if a value has not already been provided.
+ [:owner, :mode, :group, :checksum].each do |param|
+ next if param == :owner and Puppet::Util::SUIDManager.uid != 0
+ unless value = @resource[param] and value != :absent
+ @resource[param] = metadata.send(param)
end
- }
-
- # Now decide whether we're doing checksums or symlinks
- if args[:type] == "link"
- args[:target] = other
- else
- args[:checksum] = other
end
- # we can't manage ownership unless we're root, so don't even try
- unless Puppet::Util::SUIDManager.uid == 0
- args.delete(:owner)
+ @resource[:ensure] = metadata.ftype
+
+ if metadata.ftype == "link"
+ @resource[:target] = metadata.destination
end
-
- return args
end
-
- # Use the info we get from describe() to check if we're in sync.
+
+ # Remove any temporary attributes we manage.
+ def flush
+ @metadata = nil
+ @content = nil
+ end
+
+ # Use the remote metadata to see if we're in sync.
+ # LAK:NOTE This method should still get refactored.
def insync?(currentvalue)
- if currentvalue == :nocopy
- return true
- end
-
# the only thing this actual state can do is copy files around. Therefore,
# only pay attention if the remote is a file.
- unless @stats[:type] == "file"
+ unless @metadata.ftype == "file"
return true
end
@@ -153,15 +158,13 @@ module Puppet
end
# Now, we just check to see if the checksums are the same
parentchecksum = @resource.property(:checksum).retrieve
- result = (!parentchecksum.nil? and (parentchecksum == @stats[:checksum]))
+ result = (!parentchecksum.nil? and (parentchecksum == @metadata.checksum))
# Diff the contents if they ask it. This is quite annoying -- we need to do this in
# 'insync?' because they might be in noop mode, but we don't want to do the file
# retrieval twice, so we cache the value.
- if ! result and Puppet[:show_diff] and File.exists?(@resource[:path]) and ! @stats[:_diffed]
- @stats[:_remote_content] = get_remote_content
- string_file_diff(@resource[:path], @stats[:_remote_content])
- @stats[:_diffed] = true
+ if ! result and Puppet[:show_diff] and File.exists?(@resource[:path])
+ string_file_diff(@resource[:path], content)
end
return result
end
@@ -171,57 +174,39 @@ module Puppet
end
def found?
- ! (@stats.nil? or @stats[:type].nil?)
+ ! (@metadata.nil? or @metadata.ftype.nil?)
end
- # This basically calls describe() on our file, and then sets all
- # of the local states appropriately. If the remote file is a normal
- # file then we set it to copy; if it's a directory, then we just mark
- # that the local directory should be created.
- def retrieve(remote = true)
- sum = nil
- @source = nil
-
- # This is set to false by the File#retrieve function on the second
- # retrieve, so that we do not do two describes.
- if remote
- # Find the first source that exists. @shouldorig contains
- # the sources as specified by the user.
- @should.each { |source|
- if @stats = self.describe(source)
- @source = source
- break
+ # Provide, and retrieve if necessary, the metadata for this file. Fail
+ # if we can't find data about this host, and fail if there are any
+ # problems in our query.
+ attr_writer :metadata
+ def metadata
+ unless defined?(@metadata) and @metadata
+ return @metadata = nil unless should
+ should.each do |source|
+ begin
+ if data = Puppet::FileServing::Metadata.find(source)
+ @metadata = data
+ @metadata.source = source
+ break
+ end
+ rescue => detail
+ fail detail, "Could not retrieve file metadata for %s: %s" % [source, detail]
end
- }
- end
-
- if !found?
- raise Puppet::Error, "No specified source was found from" + @should.inject("") { |s, source| s + " #{source},"}.gsub(/,$/,"")
- end
-
- case @stats[:type]
- when "directory", "file", "link":
- @resource[:ensure] = @stats[:type] unless @resource.deleting?
- else
- self.info @stats.inspect
- self.err "Cannot use files of type %s as sources" % @stats[:type]
- return :nocopy
+ end
+ fail "Could not retrieve information from source(s) %s" % @should.join(", ") unless @metadata
end
+ return @metadata
+ end
- # Take each of the stats and set them as states on the local file
- # if a value has not already been provided.
- @stats.each { |stat, value|
- next if stat == :checksum
- next if stat == :type
-
- # was the stat already specified, or should the value
- # be inherited from the source?
- @resource[stat] = value unless @resource.argument?(stat)
- }
-
- return @stats[:checksum]
+ # Just call out to our copy method. Hopefully we'll refactor 'source' to
+ # be a parameter soon, in which case 'retrieve' is unnecessary.
+ def retrieve
+ copy_source_values
end
+ # Return the whole array, rather than the first item.
def should
@should
end
@@ -238,11 +223,9 @@ module Puppet
end
def sync
- contents = @stats[:_remote_content] || get_remote_content()
-
- exists = File.exists?(@resource[:path])
+ exists = FileTest.exist?(@resource[:path])
- @resource.write(contents, :source, @stats[:checksum])
+ @resource.write(content, :source, @metadata.checksum)
if exists
return :file_changed
@@ -250,27 +233,5 @@ module Puppet
return :file_created
end
end
-
- private
-
- def get_remote_content
- raise Puppet::DevError, "Got told to copy non-file %s" % @resource[:path] unless @stats[:type] == "file"
-
- sourceobj, path = @resource.uri2obj(@source)
-
- begin
- contents = sourceobj.server.retrieve(path, @resource[:links])
- rescue => detail
- self.fail "Could not retrieve %s: %s" % [path, detail]
- end
-
- contents = CGI.unescape(contents) unless sourceobj.server.local
-
- if contents == ""
- self.notice "Could not retrieve contents for %s" % @source
- end
-
- return contents
- end
end
end
diff --git a/spec/integration/file_serving/configuration.rb b/spec/integration/file_serving/configuration.rb
index cb5a23d3b..dfdda402d 100755
--- a/spec/integration/file_serving/configuration.rb
+++ b/spec/integration/file_serving/configuration.rb
@@ -29,12 +29,12 @@ describe Puppet::FileServing::Configuration, " when finding files with Puppet::F
it "should return nil if the file does not exist" do
FileTest.expects(:exists?).with("/my/path/my/file").returns(false)
- @config.file_path("/mymount/my/file").should be_nil
+ @config.file_path("mymount/my/file").should be_nil
end
it "should return the full file path if the file exists" do
FileTest.expects(:exists?).with("/my/path/my/file").returns(true)
- @config.file_path("/mymount/my/file").should == "/my/path/my/file"
+ @config.file_path("mymount/my/file").should == "/my/path/my/file"
end
after do
diff --git a/spec/integration/file_serving/content.rb b/spec/integration/file_serving/content.rb
index aee2a9f2d..af0181393 100755
--- a/spec/integration/file_serving/content.rb
+++ b/spec/integration/file_serving/content.rb
@@ -15,4 +15,6 @@ describe Puppet::FileServing::Content, " when finding files" do
@test_class = Puppet::FileServing::Content
@indirection = Puppet::FileServing::Content.indirection
end
+
+ after { Puppet::Util::Cacher.invalidate }
end
diff --git a/spec/integration/file_serving/fileset.rb b/spec/integration/file_serving/fileset.rb
new file mode 100755
index 000000000..80bf0f376
--- /dev/null
+++ b/spec/integration/file_serving/fileset.rb
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/file_serving/fileset'
+
+describe Puppet::FileServing::Fileset do
+ it "should be able to recurse on a single file" do
+ @path = Tempfile.new("fileset_integration")
+
+ fileset = Puppet::FileServing::Fileset.new(@path.path)
+ lambda { fileset.files }.should_not raise_error
+ end
+end
diff --git a/spec/integration/file_serving/metadata.rb b/spec/integration/file_serving/metadata.rb
index 067cb566a..af3e16324 100755
--- a/spec/integration/file_serving/metadata.rb
+++ b/spec/integration/file_serving/metadata.rb
@@ -15,4 +15,6 @@ describe Puppet::FileServing::Metadata, " when finding files" do
@test_class = Puppet::FileServing::Metadata
@indirection = Puppet::FileServing::Metadata.indirection
end
+
+ after { Puppet::Util::Cacher.invalidate }
end
diff --git a/spec/integration/file_serving/terminus_helper.rb b/spec/integration/file_serving/terminus_helper.rb
new file mode 100755
index 000000000..7d2587af1
--- /dev/null
+++ b/spec/integration/file_serving/terminus_helper.rb
@@ -0,0 +1,22 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/file_serving/terminus_helper'
+
+class TerminusHelperIntegrationTester
+ include Puppet::FileServing::TerminusHelper
+ def model
+ Puppet::FileServing::Metadata
+ end
+end
+
+describe Puppet::FileServing::TerminusHelper do
+ it "should be able to recurse on a single file" do
+ @path = Tempfile.new("fileset_integration")
+ request = Puppet::Indirector::Request.new(:metadata, :find, @path.path, :recurse => true)
+
+ tester = TerminusHelperIntegrationTester.new
+ lambda { tester.path2instances(request, @path.path) }.should_not raise_error
+ end
+end
diff --git a/spec/integration/indirector/direct_file_server.rb b/spec/integration/indirector/direct_file_server.rb
index 40b753a6c..417d661e8 100755
--- a/spec/integration/indirector/direct_file_server.rb
+++ b/spec/integration/indirector/direct_file_server.rb
@@ -37,21 +37,19 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with FileServi
before do
@terminus = Puppet::Indirector::FileContent::File.new
- @filepath = "/my/file"
- FileTest.stubs(:exists?).with(@filepath).returns(true)
+ @path = Tempfile.new("direct_file_server_testing")
+ @path.close!
+ @path = @path.path
- stat = stub 'stat', :directory? => true
- File.stubs(:lstat).with(@filepath).returns(stat)
+ Dir.mkdir(@path)
+ File.open(File.join(@path, "one"), "w") { |f| f.print "one content" }
+ File.open(File.join(@path, "two"), "w") { |f| f.print "two content" }
- @subfiles = %w{one two}
- @subfiles.each do |f|
- path = File.join(@filepath, f)
- FileTest.stubs(:exists?).with(@path).returns(true)
- end
-
- Dir.expects(:entries).with(@filepath).returns @subfiles
+ @request = @terminus.indirection.request(:search, "file:///%s" % @path, :recurse => true)
+ end
- @request = @terminus.indirection.request(:search, "file:///my/file", :recurse => true)
+ after do
+ system("rm -rf %s" % @path)
end
it "should return an instance for every file in the fileset" do
@@ -62,18 +60,13 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with FileServi
end
it "should return instances capable of returning their content" do
- @subfiles.each do |name|
- File.stubs(:lstat).with(File.join(@filepath, name)).returns stub("#{name} stat", :ftype => "file", :directory? => false)
- File.expects(:read).with(File.join(@filepath, name)).returns("#{name} content")
- end
-
@terminus.search(@request).each do |instance|
- case instance.key
+ case instance.full_path
when /one/: instance.content.should == "one content"
when /two/: instance.content.should == "two content"
when /\.$/:
else
- raise "No valid key for %s" % instance.key.inspect
+ raise "No valid key for %s" % instance.path.inspect
end
end
end
diff --git a/spec/integration/indirector/module_files.rb b/spec/integration/indirector/module_files.rb
index ae14aa5a7..a54588ec5 100755
--- a/spec/integration/indirector/module_files.rb
+++ b/spec/integration/indirector/module_files.rb
@@ -20,7 +20,7 @@ describe Puppet::Indirector::ModuleFiles, " when interacting with Puppet::Module
FileTest.expects(:exists?).with(filepath).returns(true)
- @request = Puppet::Indirector::Request.new(:content, :find, "puppetmounts://host/modules/mymod/myfile")
+ @request = Puppet::Indirector::Request.new(:content, :find, "puppet://host/modules/mymod/myfile")
@terminus.find(@request).should be_instance_of(Puppet::FileServing::Content)
end
@@ -30,24 +30,24 @@ describe Puppet::Indirector::ModuleFiles, " when interacting with FileServing::F
it "should return an instance for every file in the fileset" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
@terminus = Puppet::Indirector::FileContent::Modules.new
- @module = Puppet::Module.new("mymod", "/some/path/mymod")
- Puppet::Module.expects(:find).with("mymod", "myenv").returns(@module)
- filepath = "/some/path/mymod/files/myfile"
- FileTest.stubs(:exists?).with(filepath).returns(true)
+ @path = Tempfile.new("module_file_testing")
+ @path.close!
+ @path = @path.path
- stat = stub 'stat', :directory? => true
- File.stubs(:lstat).with(filepath).returns(stat)
+ Dir.mkdir(@path)
+ Dir.mkdir(File.join(@path, "files"))
- subfiles = %w{one two}
- subfiles.each do |f|
- path = File.join(filepath, f)
- FileTest.stubs(:exists?).with(path).returns(true)
- end
+ basedir = File.join(@path, "files", "myfile")
+ Dir.mkdir(basedir)
- Dir.expects(:entries).with(filepath).returns(%w{one two})
+ File.open(File.join(basedir, "one"), "w") { |f| f.print "one content" }
+ File.open(File.join(basedir, "two"), "w") { |f| f.print "two content" }
+
+ @module = Puppet::Module.new("mymod", @path)
+ Puppet::Module.expects(:find).with("mymod", "myenv").returns(@module)
- @request = Puppet::Indirector::Request.new(:content, :search, "puppetmounts://host/modules/mymod/myfile", :recurse => true)
+ @request = Puppet::Indirector::Request.new(:content, :search, "puppet://host/modules/mymod/myfile", :recurse => true)
result = @terminus.search(@request)
result.should be_instance_of(Array)
diff --git a/spec/integration/type/file.rb b/spec/integration/type/file.rb
new file mode 100755
index 000000000..b59fa3294
--- /dev/null
+++ b/spec/integration/type/file.rb
@@ -0,0 +1,128 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Type.type(:file), "when recursing" do
+ def mkdir
+ end
+
+ def build_path(dir)
+ Dir.mkdir(dir)
+ File.chmod(0750, dir)
+
+ @dirs = [dir]
+ @files = []
+
+ %w{one two}.each do |subdir|
+ fdir = File.join(dir, subdir)
+ Dir.mkdir(fdir)
+ File.chmod(0750, fdir)
+ @dirs << fdir
+
+ %w{three}.each do |file|
+ ffile = File.join(fdir, file)
+ @files << ffile
+ File.open(ffile, "w") { |f| f.puts "test %s" % file }
+ File.chmod(0640, ffile)
+ end
+ end
+ end
+
+ it "should be able to recurse over a nonexistent file" do
+ @path = Tempfile.new("file_integration_tests")
+ @path.close!()
+ @path = @path.path
+
+ @file = Puppet::Type::File.create(:name => @path, :mode => 0644, :recurse => true)
+
+ @catalog = Puppet::Node::Catalog.new
+ @catalog.add_resource @file
+
+ lambda { @file.eval_generate }.should_not raise_error
+ end
+
+ it "should be able to recursively set properties on existing files" do
+ @path = Tempfile.new("file_integration_tests")
+ @path.close!()
+ @path = @path.path
+
+ build_path(@path)
+
+ @file = Puppet::Type::File.create(:name => @path, :mode => 0644, :recurse => true)
+
+ @catalog = Puppet::Node::Catalog.new
+ @catalog.add_resource @file
+
+ @catalog.apply
+
+ @dirs.each do |path|
+ (File.stat(path).mode & 007777).should == 0755
+ end
+
+ @files.each do |path|
+ (File.stat(path).mode & 007777).should == 0644
+ end
+ end
+
+ it "should be able to recursively make links to other files" do
+ source = Tempfile.new("file_link_integration_source")
+ source.close!()
+ source = source.path
+
+ build_path(source)
+
+ dest = Tempfile.new("file_link_integration_dest")
+ dest.close!()
+ dest = dest.path
+
+ @file = Puppet::Type::File.create(:name => dest, :target => source, :recurse => true, :ensure => :link)
+
+ @catalog = Puppet::Node::Catalog.new
+ @catalog.add_resource @file
+
+ @catalog.apply
+
+ @dirs.each do |path|
+ link_path = path.sub(source, dest)
+
+ File.lstat(link_path).should be_directory
+ end
+
+ @files.each do |path|
+ link_path = path.sub(source, dest)
+
+ File.lstat(link_path).ftype.should == "link"
+ end
+ end
+
+ it "should be able to recursively copy files" do
+ source = Tempfile.new("file_source_integration_source")
+ source.close!()
+ source = source.path
+
+ build_path(source)
+
+ dest = Tempfile.new("file_source_integration_dest")
+ dest.close!()
+ dest = dest.path
+
+ @file = Puppet::Type::File.create(:name => dest, :source => source, :recurse => true)
+
+ @catalog = Puppet::Node::Catalog.new
+ @catalog.add_resource @file
+
+ @catalog.apply
+
+ @dirs.each do |path|
+ newpath = path.sub(source, dest)
+
+ File.lstat(newpath).should be_directory
+ end
+
+ @files.each do |path|
+ newpath = path.sub(source, dest)
+
+ File.lstat(newpath).ftype.should == "file"
+ end
+ end
+end
diff --git a/spec/shared_behaviours/file_server_terminus.rb b/spec/shared_behaviours/file_server_terminus.rb
index 0230d39e8..fc5673a65 100644
--- a/spec/shared_behaviours/file_server_terminus.rb
+++ b/spec/shared_behaviours/file_server_terminus.rb
@@ -8,14 +8,19 @@ describe "Puppet::Indirector::FileServerTerminus", :shared => true do
# the 'before' block in the including context.
before do
Puppet::Util::Cacher.invalidate
+ FileTest.stubs(:exists?).returns true
FileTest.stubs(:exists?).with(Puppet[:fileserverconfig]).returns(true)
- FileTest.stubs(:exists?).with("/my/mount/path").returns(true)
- FileTest.stubs(:directory?).with("/my/mount/path").returns(true)
- FileTest.stubs(:readable?).with("/my/mount/path").returns(true)
+
+ @path = Tempfile.new("file_server_testing")
+ @path.close!
+ @path = @path.path
+
+ Dir.mkdir(@path)
+ File.open(File.join(@path, "myfile"), "w") { |f| f.print "my content" }
# Use a real mount, so the integration is a bit deeper.
@mount1 = Puppet::FileServing::Configuration::Mount.new("one")
- @mount1.path = "/my/mount/path"
+ @mount1.path = @path
@parser = stub 'parser', :changed? => false
@parser.stubs(:parse).returns("one" => @mount1)
@@ -25,17 +30,14 @@ describe "Puppet::Indirector::FileServerTerminus", :shared => true do
# Stub out the modules terminus
@modules = mock 'modules terminus'
- @request = Puppet::Indirector::Request.new(:indirection, :method, "puppetmounts://myhost/one/my/file")
+ @request = Puppet::Indirector::Request.new(:indirection, :method, "puppet://myhost/one/myfile")
end
it "should use the file server configuration to find files" do
@modules.stubs(:find).returns(nil)
@terminus.indirection.stubs(:terminus).with(:modules).returns(@modules)
- path = "/my/mount/path/my/file"
- FileTest.stubs(:exists?).with(path).returns(true)
- FileTest.stubs(:exists?).with("/my/mount/path").returns(true)
- @mount1.expects(:file).with("my/file", :node => nil).returns(path)
+ path = File.join(@path, "myfile")
@terminus.find(@request).should be_instance_of(@test_class)
end
diff --git a/spec/shared_behaviours/file_serving.rb b/spec/shared_behaviours/file_serving.rb
index ba01f75a1..99994b99a 100644
--- a/spec/shared_behaviours/file_serving.rb
+++ b/spec/shared_behaviours/file_serving.rb
@@ -6,12 +6,18 @@
describe "Puppet::FileServing::Files", :shared => true do
it "should use the rest terminus when the 'puppet' URI scheme is used and a host name is present" do
uri = "puppet://myhost/fakemod/my/file"
- @indirection.terminus(:rest).expects(:find)
+
+ # It appears that the mocking somehow interferes with the caching subsystem.
+ # This mock somehow causes another terminus to get generated.
+ term = @indirection.terminus(:rest)
+ @indirection.stubs(:terminus).with(:rest).returns term
+ term.expects(:find)
@test_class.find(uri)
end
it "should use the rest terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is not 'puppet'" do
uri = "puppet:///fakemod/my/file"
+ Puppet.settings.stubs(:value).returns "foo"
Puppet.settings.stubs(:value).with(:name).returns("puppetd")
Puppet.settings.stubs(:value).with(:modulepath).returns("")
@indirection.terminus(:rest).expects(:find)
@@ -21,19 +27,9 @@ describe "Puppet::FileServing::Files", :shared => true do
it "should use the file_server terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is 'puppet'" do
uri = "puppet:///fakemod/my/file"
Puppet::Node::Environment.stubs(:new).returns(stub("env", :name => "testing"))
+ Puppet.settings.stubs(:value).returns ""
Puppet.settings.stubs(:value).with(:name).returns("puppet")
- Puppet.settings.stubs(:value).with(:modulepath, "testing").returns("")
- Puppet.settings.stubs(:value).with(:modulepath).returns("")
- Puppet.settings.stubs(:value).with(:libdir).returns("")
Puppet.settings.stubs(:value).with(:fileserverconfig).returns("/whatever")
- Puppet.settings.stubs(:value).with(:environment).returns("")
- @indirection.terminus(:file_server).expects(:find)
- @indirection.terminus(:file_server).stubs(:authorized?).returns(true)
- @test_class.find(uri)
- end
-
- it "should use the file_server terminus when the 'puppetmounts' URI scheme is used" do
- uri = "puppetmounts:///fakemod/my/file"
@indirection.terminus(:file_server).expects(:find)
@indirection.terminus(:file_server).stubs(:authorized?).returns(true)
@test_class.find(uri)
@@ -52,7 +48,7 @@ describe "Puppet::FileServing::Files", :shared => true do
end
it "should use the configuration to test whether the request is allowed" do
- uri = "puppetmounts:///fakemod/my/file"
+ uri = "fakemod/my/file"
config = mock 'configuration'
@indirection.terminus(:file_server).stubs(:configuration).returns config
@@ -61,4 +57,3 @@ describe "Puppet::FileServing::Files", :shared => true do
@test_class.find(uri, :node => "foo", :ip => "bar")
end
end
-
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 52aed5b62..41b5e7443 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -39,3 +39,9 @@ end
# have to be correctly mocked.
Puppet[:confdir] = "/dev/null"
Puppet[:vardir] = "/dev/null"
+
+# We need this because the RAL uses 'should' as a method. This
+# allows us the same behaviour but with a different method name.
+class Object
+ alias :must :should
+end
diff --git a/spec/unit/file_serving/file_base.rb b/spec/unit/file_serving/base.rb
index ded6ae4a8..6a76d81e9 100755
--- a/spec/unit/file_serving/file_base.rb
+++ b/spec/unit/file_serving/base.rb
@@ -2,67 +2,66 @@
require File.dirname(__FILE__) + '/../../spec_helper'
-require 'puppet/file_serving/file_base'
+require 'puppet/file_serving/base'
-describe Puppet::FileServing::FileBase do
- it "should accept a key in the form of a URI" do
- Puppet::FileServing::FileBase.new("puppet://host/module/dir/file").key.should == "puppet://host/module/dir/file"
+describe Puppet::FileServing::Base do
+ it "should accept a path" do
+ Puppet::FileServing::Base.new("/module/dir/file").path.should == "/module/dir/file"
+ end
+
+ it "should require that paths be fully qualified" do
+ lambda { Puppet::FileServing::Base.new("module/dir/file") }.should raise_error(ArgumentError)
end
it "should allow specification of whether links should be managed" do
- Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :links => :manage).links.should == :manage
+ Puppet::FileServing::Base.new("/module/dir/file", :links => :manage).links.should == :manage
+ end
+
+ it "should have a :source attribute" do
+ file = Puppet::FileServing::Base.new("/module/dir/file")
+ file.should respond_to(:source)
+ file.should respond_to(:source=)
end
it "should consider :ignore links equivalent to :manage links" do
- Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :links => :ignore).links.should == :manage
+ Puppet::FileServing::Base.new("/module/dir/file", :links => :ignore).links.should == :manage
end
it "should fail if :links is set to anything other than :manage, :follow, or :ignore" do
- proc { Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :links => :else) }.should raise_error(ArgumentError)
+ proc { Puppet::FileServing::Base.new("/module/dir/file", :links => :else) }.should raise_error(ArgumentError)
end
it "should default to :manage for :links" do
- Puppet::FileServing::FileBase.new("puppet://host/module/dir/file").links.should == :manage
+ Puppet::FileServing::Base.new("/module/dir/file").links.should == :manage
end
it "should allow specification of a path" do
FileTest.stubs(:exists?).returns(true)
- Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :path => "/my/file").path.should == "/my/file"
+ Puppet::FileServing::Base.new("/module/dir/file", :path => "/my/file").path.should == "/my/file"
end
it "should allow specification of a relative path" do
FileTest.stubs(:exists?).returns(true)
- Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :relative_path => "my/file").relative_path.should == "my/file"
+ Puppet::FileServing::Base.new("/module/dir/file", :relative_path => "my/file").relative_path.should == "my/file"
end
it "should have a means of determining if the file exists" do
- Puppet::FileServing::FileBase.new("blah").should respond_to(:exist?)
+ Puppet::FileServing::Base.new("/blah").should respond_to(:exist?)
end
it "should correctly indicate if the file is present" do
File.expects(:lstat).with("/my/file").returns(mock("stat"))
- Puppet::FileServing::FileBase.new("blah", :path => "/my/file").exist?.should be_true
+ Puppet::FileServing::Base.new("/my/file").exist?.should be_true
end
- it "should correctly indicate if the file is asbsent" do
+ it "should correctly indicate if the file is absent" do
File.expects(:lstat).with("/my/file").raises RuntimeError
- Puppet::FileServing::FileBase.new("blah", :path => "/my/file").exist?.should be_false
- end
-
- describe "when setting the base path" do
- before do
- @file = Puppet::FileServing::FileBase.new("puppet://host/module/dir/file")
- end
-
- it "should require that the base path be fully qualified" do
- FileTest.stubs(:exists?).returns(true)
- proc { @file.path = "unqualified/file" }.should raise_error(ArgumentError)
- end
+ Puppet::FileServing::Base.new("/my/file").exist?.should be_false
end
describe "when setting the relative path" do
it "should require that the relative path be unqualified" do
- @file = Puppet::FileServing::FileBase.new("puppet://host/module/dir/file")
+ @file = Puppet::FileServing::Base.new("/module/dir/file")
FileTest.stubs(:exists?).returns(true)
proc { @file.relative_path = "/qualified/file" }.should raise_error(ArgumentError)
end
@@ -70,7 +69,7 @@ describe Puppet::FileServing::FileBase do
describe "when determining the full file path" do
before do
- @file = Puppet::FileServing::FileBase.new("mykey", :path => "/this/file")
+ @file = Puppet::FileServing::Base.new("/this/file")
end
it "should return the path if there is no relative path" do
@@ -82,20 +81,20 @@ describe Puppet::FileServing::FileBase do
@file.full_path.should == "/this/file"
end
+ it "should return the path if the relative_path is set to '.'" do
+ @file.relative_path = "."
+ @file.full_path.should == "/this/file"
+ end
+
it "should return the path joined with the relative path if there is a relative path and it is not set to '/' or ''" do
@file.relative_path = "not/qualified"
@file.full_path.should == "/this/file/not/qualified"
end
-
- it "should should fail if there is no path set" do
- @file = Puppet::FileServing::FileBase.new("not/qualified")
- proc { @file.full_path }.should raise_error(ArgumentError)
- end
end
describe "when stat'ing files" do
before do
- @file = Puppet::FileServing::FileBase.new("mykey", :path => "/this/file")
+ @file = Puppet::FileServing::Base.new("/this/file")
end
it "should stat the file's full path" do
diff --git a/spec/unit/file_serving/configuration.rb b/spec/unit/file_serving/configuration.rb
index d51fa70b2..741e3bc1d 100755
--- a/spec/unit/file_serving/configuration.rb
+++ b/spec/unit/file_serving/configuration.rb
@@ -35,7 +35,7 @@ describe Puppet::FileServing::Configuration do
Puppet::Util::Cacher.invalidate
end
- describe Puppet::FileServing::Configuration, " when initializing" do
+ describe Puppet::FileServing::Configuration, "when initializing" do
it "should work without a configuration file" do
FileTest.stubs(:exists?).with(@path).returns(false)
@@ -55,7 +55,7 @@ describe Puppet::FileServing::Configuration do
end
end
- describe Puppet::FileServing::Configuration, " when parsing the configuration file" do
+ describe Puppet::FileServing::Configuration, "when parsing the configuration file" do
before do
FileTest.stubs(:exists?).with(@path).returns(true)
@@ -95,7 +95,7 @@ describe Puppet::FileServing::Configuration do
end
end
- describe Puppet::FileServing::Configuration, " when finding files" do
+ describe Puppet::FileServing::Configuration, "when finding files" do
before do
@parser = mock 'parser'
@@ -111,55 +111,60 @@ describe Puppet::FileServing::Configuration do
@config = Puppet::FileServing::Configuration.create
end
- it "should fail if the uri does not match a leading slash followed by a valid mount name" do
+ it "should fail if the uri has a leading slash" do
@parser.expects(:parse).returns(@mounts)
- proc { @config.file_path("something") }.should raise_error(ArgumentError)
+ proc { @config.file_path("/something") }.should raise_error(ArgumentError)
+ end
+
+ it "should fail if the uri does not start with a valid mount name" do
+ @parser.expects(:parse).returns(@mounts)
+ proc { @config.file_path("some.thing") }.should raise_error(ArgumentError)
end
it "should use the first term after the first slash for the mount name" do
@parser.expects(:parse).returns(@mounts)
FileTest.stubs(:exists?).returns(true)
@mount1.expects(:file)
- @config.file_path("/one")
+ @config.file_path("one")
end
it "should use the remainder of the URI after the mount name as the file name" do
@parser.expects(:parse).returns(@mounts)
@mount1.expects(:file).with("something/else", {})
FileTest.stubs(:exists?).returns(true)
- @config.file_path("/one/something/else")
+ @config.file_path("one/something/else")
end
it "should treat a bare name as a mount and no relative file" do
@parser.expects(:parse).returns(@mounts)
@mount1.expects(:file).with(nil, {})
FileTest.stubs(:exists?).returns(true)
- @config.file_path("/one")
+ @config.file_path("one")
end
it "should treat a name with a trailing slash equivalently to a name with no trailing slash" do
@parser.expects(:parse).returns(@mounts)
@mount1.expects(:file).with(nil, {})
FileTest.stubs(:exists?).returns(true)
- @config.file_path("/one/")
+ @config.file_path("one/")
end
it "should return nil if the mount cannot be found" do
@parser.expects(:changed?).returns(true)
@parser.expects(:parse).returns({})
- @config.file_path("/one/something").should be_nil
+ @config.file_path("one/something").should be_nil
end
it "should return nil if the mount does not contain the file" do
@parser.expects(:parse).returns(@mounts)
@mount1.expects(:file).with("something/else", {}).returns(nil)
- @config.file_path("/one/something/else").should be_nil
+ @config.file_path("one/something/else").should be_nil
end
it "should return the fully qualified path if the mount exists" do
@parser.expects(:parse).returns(@mounts)
@mount1.expects(:file).with("something/else", {}).returns("/full/path")
- @config.file_path("/one/something/else").should == "/full/path"
+ @config.file_path("one/something/else").should == "/full/path"
end
it "should reparse the configuration file when it has changed" do
@@ -167,15 +172,15 @@ describe Puppet::FileServing::Configuration do
@parser.expects(:changed?).returns(true)
@parser.expects(:parse).returns(@mounts)
FileTest.stubs(:exists?).returns(true)
- @config.file_path("/one/something")
+ @config.file_path("one/something")
@parser.expects(:changed?).returns(true)
@parser.expects(:parse).returns({})
- @config.file_path("/one/something").should be_nil
+ @config.file_path("one/something").should be_nil
end
end
- describe Puppet::FileServing::Configuration, " when checking authorization" do
+ describe Puppet::FileServing::Configuration, "when checking authorization" do
before do
@parser = mock 'parser'
@@ -193,32 +198,32 @@ describe Puppet::FileServing::Configuration do
end
it "should return false if the mount cannot be found" do
- @config.authorized?("/nope/my/file").should be_false
+ @config.authorized?("nope/my/file").should be_false
end
it "should use the mount to determine authorization" do
@mount1.expects(:allowed?)
- @config.authorized?("/one/my/file")
+ @config.authorized?("one/my/file")
end
it "should pass the client's name to the mount if provided" do
@mount1.expects(:allowed?).with("myhost", nil)
- @config.authorized?("/one/my/file", :node => "myhost")
+ @config.authorized?("one/my/file", :node => "myhost")
end
it "should pass the client's IP to the mount if provided" do
@mount1.expects(:allowed?).with("myhost", "myip")
- @config.authorized?("/one/my/file", :node => "myhost", :ipaddress => "myip")
+ @config.authorized?("one/my/file", :node => "myhost", :ipaddress => "myip")
end
it "should return true if the mount allows the client" do
@mount1.expects(:allowed?).returns(true)
- @config.authorized?("/one/my/file").should be_true
+ @config.authorized?("one/my/file").should be_true
end
it "should return false if the mount denies the client" do
@mount1.expects(:allowed?).returns(false)
- @config.authorized?("/one/my/file").should be_false
+ @config.authorized?("one/my/file").should be_false
end
end
end
diff --git a/spec/unit/file_serving/content.rb b/spec/unit/file_serving/content.rb
index 89d786295..eacaff89f 100755
--- a/spec/unit/file_serving/content.rb
+++ b/spec/unit/file_serving/content.rb
@@ -5,8 +5,8 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/file_serving/content'
describe Puppet::FileServing::Content do
- it "should should be a subclass of FileBase" do
- Puppet::FileServing::Content.superclass.should equal(Puppet::FileServing::FileBase)
+ it "should should be a subclass of Base" do
+ Puppet::FileServing::Content.superclass.should equal(Puppet::FileServing::Base)
end
it "should indirect file_content" do
@@ -16,12 +16,66 @@ describe Puppet::FileServing::Content do
it "should should include the IndirectionHooks module in its indirection" do
Puppet::FileServing::Content.indirection.metaclass.included_modules.should include(Puppet::FileServing::IndirectionHooks)
end
+
+ it "should only support the raw format" do
+ Puppet::FileServing::Content.supported_formats.should == [:raw]
+ end
+
+ it "should have a method for collecting its attributes" do
+ Puppet::FileServing::Content.new("/path").should respond_to(:collect)
+ end
+
+ it "should retrieve and store its contents when its attributes are collected if the file is a normal file" do
+ content = Puppet::FileServing::Content.new("/path")
+
+ result = "foo"
+ File.stubs(:lstat).returns(stub("stat", :ftype => "file"))
+ File.expects(:read).with("/path").returns result
+ content.collect
+
+ content.instance_variable_get("@content").should_not be_nil
+ end
+
+ it "should not attempt to retrieve its contents if the file is a directory" do
+ content = Puppet::FileServing::Content.new("/path")
+
+ result = "foo"
+ File.stubs(:lstat).returns(stub("stat", :ftype => "directory"))
+ File.expects(:read).with("/path").never
+ content.collect
+
+ content.instance_variable_get("@content").should be_nil
+ end
+
+ it "should have a method for setting its content" do
+ content = Puppet::FileServing::Content.new("/path")
+ content.should respond_to(:content=)
+ end
+
+ it "should make content available when set externally" do
+ content = Puppet::FileServing::Content.new("/path")
+ content.content = "foo/bar"
+ content.content.should == "foo/bar"
+ end
+
+ it "should be able to create a content instance from raw file contents" do
+ Puppet::FileServing::Content.should respond_to(:from_raw)
+ end
+
+ it "should create an instance with a fake file name and correct content when converting from raw" do
+ instance = mock 'instance'
+ Puppet::FileServing::Content.expects(:new).with("/this/is/a/fake/path").returns instance
+
+ instance.expects(:content=).with "foo/bar"
+
+ Puppet::FileServing::Content.from_raw("foo/bar").should equal(instance)
+ end
end
-describe Puppet::FileServing::Content, " when returning the contents" do
+describe Puppet::FileServing::Content, "when returning the contents" do
before do
- @path = "/my/base"
- @content = Puppet::FileServing::Content.new("sub/path", :links => :follow, :path => @path)
+ @path = "/my/path"
+ @content = Puppet::FileServing::Content.new(@path, :links => :follow)
end
it "should fail if the file is a symlink and links are set to :manage" do
@@ -44,28 +98,13 @@ describe Puppet::FileServing::Content, " when returning the contents" do
File.expects(:read).with(@path).returns(:mycontent)
@content.content.should == :mycontent
end
-end
-
-describe Puppet::FileServing::Content, " when converting to yaml" do
- it "should fail if no path has been set" do
- @content = Puppet::FileServing::Content.new("some/key")
- proc { @content.to_yaml }.should raise_error(ArgumentError)
- end
- it "should return the file contents" do
- @content = Puppet::FileServing::Content.new("some/path")
- @content.path = "/base/path"
- @content.expects(:content).returns(:content)
- @content.to_yaml.should == :content
- end
-end
+ it "should cache the returned contents" do
+ File.expects(:stat).with(@path).returns stub("stat", :ftype => "file")
+ File.expects(:read).with(@path).returns(:mycontent)
+ @content.content
-describe Puppet::FileServing::Content, " when converting from yaml" do
- # LAK:FIXME This isn't in the right place, but we need some kind of
- # control somewhere that requires that all REST connections only pull
- # from the file-server, thus guaranteeing they go through our authorization
- # hook.
- it "should set the URI scheme to 'puppetmounts'" do
- pending "We need to figure out where this should be"
+ # The second run would throw a failure if the content weren't being cached.
+ @content.content
end
end
diff --git a/spec/unit/file_serving/fileset.rb b/spec/unit/file_serving/fileset.rb
index 2cd3e83dd..f95271050 100755
--- a/spec/unit/file_serving/fileset.rb
+++ b/spec/unit/file_serving/fileset.rb
@@ -149,6 +149,13 @@ describe Puppet::FileServing::Fileset, " when recursing" do
@fileset.files.sort.should == @files.sort
end
+ it "should function if the :ignore value provided is nil" do
+ mock_dir_structure(@path)
+ @fileset.recurse = true
+ @fileset.ignore = nil
+ lambda { @fileset.files }.should_not raise_error
+ end
+
it "should ignore files that match a single pattern in the ignore list" do
mock_dir_structure(@path)
@fileset.recurse = true
diff --git a/spec/unit/file_serving/indirection_hooks.rb b/spec/unit/file_serving/indirection_hooks.rb
index 160e3ff0a..83ff5848f 100755
--- a/spec/unit/file_serving/indirection_hooks.rb
+++ b/spec/unit/file_serving/indirection_hooks.rb
@@ -12,113 +12,113 @@ describe Puppet::FileServing::IndirectionHooks do
@object = Object.new
@object.extend(Puppet::FileServing::IndirectionHooks)
- @request = stub 'request', :key => "http://myhost/blah", :options => {:node => "whatever"}
+ @request = stub 'request', :key => "mymod/myfile", :options => {:node => "whatever"}, :server => nil, :protocol => nil
end
describe "when being used to select termini" do
- it "should escape the key before parsing" do
- uri = stub 'uri', :scheme => "puppet", :host => "blah", :path => "/something"
- URI.expects(:escape).with("http://myhost/blah").returns("escaped_blah")
- URI.expects(:parse).with("escaped_blah").returns(uri)
- @object.select_terminus(@request)
+ it "should return :file if the request key is fully qualified" do
+ @request.expects(:key).returns "#{File::SEPARATOR}foo"
+ @object.select_terminus(@request).should == :file
end
- it "should use the URI class to parse the key" do
- uri = stub 'uri', :scheme => "puppet", :host => "blah", :path => "/something"
- URI.expects(:parse).with("http://myhost/blah").returns(uri)
- @object.select_terminus @request
+ it "should return :file if the URI protocol is set to 'file'" do
+ @request.expects(:protocol).returns "file"
+ @object.select_terminus(@request).should == :file
end
- it "should choose :rest when the protocol is 'puppet'" do
- @request.stubs(:key).returns "puppet://host/module/file"
- @object.select_terminus(@request).should == :rest
+ it "should fail when a protocol other than :puppet or :file is used" do
+ @request.stubs(:protocol).returns "http"
+ proc { @object.select_terminus(@request) }.should raise_error(ArgumentError)
end
- it "should choose :file_server when the protocol is 'puppetmounts' and the mount name is not 'modules'" do
- modules = mock 'modules'
- @object.stubs(:terminus).with(:modules).returns(modules)
- modules.stubs(:find_module).returns(nil)
+ describe "and the protocol is 'puppet'" do
+ before do
+ @request.stubs(:protocol).returns "puppet"
+ end
- @request.stubs(:key).returns "puppetmounts://host/notmodules/file"
+ it "should choose :rest when a server is specified" do
+ @request.stubs(:protocol).returns "puppet"
+ @request.expects(:server).returns "foo"
+ @object.select_terminus(@request).should == :rest
+ end
- @object.select_terminus(@request).should == :file_server
- end
+ # This is so a given file location works when bootstrapping with no server.
+ it "should choose :rest when the Settings name isn't 'puppet'" do
+ @request.stubs(:protocol).returns "puppet"
+ @request.stubs(:server).returns "foo"
+ Puppet.settings.stubs(:value).with(:name).returns "foo"
+ @object.select_terminus(@request).should == :rest
+ end
- it "should choose :file_server when no server name is provided, the process name is 'puppet', and the mount name is not 'modules'" do
- modules = mock 'modules'
- @object.stubs(:terminus).with(:modules).returns(modules)
- modules.stubs(:find_module).returns(nil)
+ it "should not choose :rest when the settings name is 'puppet' and no server is specified" do
+ modules = mock 'modules'
- Puppet.settings.expects(:value).with(:name).returns("puppet")
- @request.stubs(:key).returns "puppet:///notmodules/file"
- @object.select_terminus(@request).should == :file_server
- end
+ @object.stubs(:terminus).with(:modules).returns(modules)
+ modules.stubs(:find_module).with("mymod", @request.options[:node]).returns nil
- it "should choose :modules if it would normally choose :file_server but the mount name is 'modules'" do
- @request.stubs(:key).returns "puppetmounts://host/modules/mymod/file"
- @object.select_terminus(@request).should == :modules
+ @request.expects(:protocol).returns "puppet"
+ @request.expects(:server).returns nil
+ Puppet.settings.expects(:value).with(:name).returns "puppet"
+ @object.select_terminus(@request).should_not == :rest
+ end
end
- it "should choose :modules if it would normally choose :file_server but a module exists with the mount name" do
- modules = mock 'modules'
+ describe "and the terminus is not :rest or :file" do
+ before do
+ @request.stubs(:protocol).returns nil
+ end
- @object.expects(:terminus).with(:modules).returns(modules)
- modules.expects(:find_module).with("mymod", @request.options[:node]).returns(:thing)
+ it "should choose :modules if the mount name is 'modules'" do
+ @request.stubs(:key).returns "modules/mymod/file"
+ @object.select_terminus(@request).should == :modules
+ end
- @request.stubs(:key).returns "puppetmounts://host/mymod/file"
- @object.select_terminus(@request).should == :modules
- end
+ it "should choose :modules and provide a deprecation notice if a module exists with the mount name" do
+ modules = mock 'modules'
- it "should choose :rest when no server name is provided and the process name is not 'puppet'" do
- Puppet.settings.expects(:value).with(:name).returns("puppetd")
- @request.stubs(:key).returns "puppet:///module/file"
- @object.select_terminus(@request).should == :rest
- end
+ @object.expects(:terminus).with(:modules).returns(modules)
+ modules.expects(:find_module).with("mymod", @request.options[:node]).returns(:thing)
- it "should choose :file when the protocol is 'file'" do
- @request.stubs(:key).returns "file://host/module/file"
- @object.select_terminus(@request).should == :file
- end
+ Puppet.expects(:warning)
- it "should choose :file when the URI is a normal path name" do
- @request.stubs(:key).returns "/module/file"
- @object.select_terminus(@request).should == :file
- end
+ @request.stubs(:key).returns "mymod/file"
+ @object.select_terminus(@request).should == :modules
+ end
- # This is so that we only choose modules over mounts, not file
- it "should choose :file when the protocol is 'file' and the fully qualified path starts with '/modules'" do
- @request.stubs(:key).returns "/module/file"
- @object.select_terminus(@request).should == :file
- end
+ it "should choose :file_server if the mount name is not 'modules' nor matches a module name" do
+ modules = mock 'modules'
+ @object.stubs(:terminus).with(:modules).returns(modules)
+ modules.stubs(:find_module).returns(nil)
- it "should fail when a protocol other than :puppet, :file, or :puppetmounts is used" do
- @request.stubs(:key).returns "http:///module/file"
- proc { @object.select_terminus(@request) }.should raise_error(ArgumentError)
+ @request.stubs(:key).returns "notmodules/file"
+
+ @object.select_terminus(@request).should == :file_server
+ end
end
- end
- describe "when looking for a module whose name matches the mount name" do
- before do
- @modules = mock 'modules'
- @object.stubs(:terminus).with(:modules).returns(@modules)
+ describe "when looking for a module whose name matches the mount name" do
+ before do
+ @modules = mock 'modules'
+ @object.stubs(:terminus).with(:modules).returns(@modules)
- @request.stubs(:key).returns "puppetmounts://host/mymod/file"
- end
+ @request.stubs(:key).returns "mymod/file"
+ end
- it "should use the modules terminus to look up the module" do
- @modules.expects(:find_module).with("mymod", @request.options[:node])
- @object.select_terminus @request
- end
+ it "should use the modules terminus to look up the module" do
+ @modules.expects(:find_module).with("mymod", @request.options[:node])
+ @object.select_terminus @request
+ end
- it "should pass the node name to the modules terminus" do
- @modules.expects(:find_module).with("mymod", @request.options[:node])
- @object.select_terminus @request
- end
+ it "should pass the node name to the modules terminus" do
+ @modules.expects(:find_module).with("mymod", @request.options[:node])
+ @object.select_terminus @request
+ end
- it "should log a deprecation warning if a module is found" do
- @modules.expects(:find_module).with("mymod", @request.options[:node]).returns(:something)
- Puppet.expects(:warning)
- @object.select_terminus @request
+ it "should log a deprecation warning if a module is found" do
+ @modules.expects(:find_module).with("mymod", @request.options[:node]).returns(:something)
+ Puppet.expects(:warning)
+ @object.select_terminus @request
+ end
end
end
end
diff --git a/spec/unit/file_serving/metadata.rb b/spec/unit/file_serving/metadata.rb
index 9743370c1..768a7c56d 100755
--- a/spec/unit/file_serving/metadata.rb
+++ b/spec/unit/file_serving/metadata.rb
@@ -5,8 +5,8 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/file_serving/metadata'
describe Puppet::FileServing::Metadata do
- it "should should be a subclass of FileBase" do
- Puppet::FileServing::Metadata.superclass.should equal(Puppet::FileServing::FileBase)
+ it "should should be a subclass of Base" do
+ Puppet::FileServing::Metadata.superclass.should equal(Puppet::FileServing::Base)
end
it "should indirect file_metadata" do
@@ -16,39 +16,40 @@ describe Puppet::FileServing::Metadata do
it "should should include the IndirectionHooks module in its indirection" do
Puppet::FileServing::Metadata.indirection.metaclass.included_modules.should include(Puppet::FileServing::IndirectionHooks)
end
+
+ it "should have a method that triggers attribute collection" do
+ Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:collect)
+ end
end
describe Puppet::FileServing::Metadata, " when finding the file to use for setting attributes" do
before do
- @metadata = Puppet::FileServing::Metadata.new("my/path")
-
- @full = "/base/path/my/path"
-
- @metadata.path = @full
+ @path = "/my/path"
+ @metadata = Puppet::FileServing::Metadata.new(@path)
# Use a link because it's easier to test -- no checksumming
@stat = stub "stat", :uid => 10, :gid => 20, :mode => 0755, :ftype => "link"
end
it "should accept a base path path to which the file should be relative" do
- File.expects(:lstat).with(@full).returns @stat
- File.expects(:readlink).with(@full).returns "/what/ever"
- @metadata.collect_attributes
+ File.expects(:lstat).with(@path).returns @stat
+ File.expects(:readlink).with(@path).returns "/what/ever"
+ @metadata.collect
end
it "should use the set base path if one is not provided" do
- File.expects(:lstat).with(@full).returns @stat
- File.expects(:readlink).with(@full).returns "/what/ever"
- @metadata.collect_attributes()
+ File.expects(:lstat).with(@path).returns @stat
+ File.expects(:readlink).with(@path).returns "/what/ever"
+ @metadata.collect()
end
it "should fail if a base path is neither set nor provided" do
- proc { @metadata.collect_attributes() }.should raise_error(Errno::ENOENT)
+ proc { @metadata.collect() }.should raise_error(Errno::ENOENT)
end
it "should raise an exception if the file does not exist" do
- File.expects(:lstat).with(@full).raises(Errno::ENOENT)
- proc { @metadata.collect_attributes()}.should raise_error(Errno::ENOENT)
+ File.expects(:lstat).with(@path).raises(Errno::ENOENT)
+ proc { @metadata.collect()}.should raise_error(Errno::ENOENT)
end
end
@@ -59,9 +60,9 @@ describe Puppet::FileServing::Metadata, " when collecting attributes" do
@stat = stub 'stat', :uid => 10, :gid => 20, :mode => 33261, :ftype => "file"
File.stubs(:lstat).returns(@stat)
@checksum = Digest::MD5.hexdigest("some content\n")
- @metadata = Puppet::FileServing::Metadata.new("file", :path => "/my/file")
+ @metadata = Puppet::FileServing::Metadata.new("/my/file")
@metadata.stubs(:md5_file).returns(@checksum)
- @metadata.collect_attributes
+ @metadata.collect
end
it "should be able to produce xmlrpc-style attribute information" do
@@ -109,7 +110,7 @@ describe Puppet::FileServing::Metadata, " when collecting attributes" do
@stat.stubs(:ftype).returns("directory")
@time = Time.now
@metadata.expects(:ctime_file).returns(@time)
- @metadata.collect_attributes
+ @metadata.collect
end
it "should only use checksums of type 'ctime' for directories" do
@@ -125,7 +126,7 @@ describe Puppet::FileServing::Metadata, " when collecting attributes" do
before do
@stat.stubs(:ftype).returns("link")
File.expects(:readlink).with("/my/file").returns("/path/to/link")
- @metadata.collect_attributes
+ @metadata.collect
end
it "should read links instead of returning their checksums" do
@@ -140,32 +141,22 @@ end
describe Puppet::FileServing::Metadata, " when pointing to a link" do
it "should store the destination of the link in :destination if links are :manage" do
- file = Puppet::FileServing::Metadata.new("mykey", :links => :manage, :path => "/base/path/my/file")
+ file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :manage)
File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755)
File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path"
- file.collect_attributes
+ file.collect
file.destination.should == "/some/other/path"
end
it "should not collect the checksum" do
- file = Puppet::FileServing::Metadata.new("my/file", :links => :manage, :path => "/base/path/my/file")
+ file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :manage)
File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755)
File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path"
- file.collect_attributes
+ file.collect
file.checksum.should be_nil
end
end
-
-describe Puppet::FileServing::Metadata, " when converting from yaml" do
- # LAK:FIXME This isn't in the right place, but we need some kind of
- # control somewhere that requires that all REST connections only pull
- # from the file-server, thus guaranteeing they go through our authorization
- # hook.
- it "should set the URI scheme to 'puppetmounts'" do
- pending "We need to figure out where this should be"
- end
-end
diff --git a/spec/unit/file_serving/terminus_helper.rb b/spec/unit/file_serving/terminus_helper.rb
index 763ce9eb0..7f08aeb7e 100755
--- a/spec/unit/file_serving/terminus_helper.rb
+++ b/spec/unit/file_serving/terminus_helper.rb
@@ -31,46 +31,73 @@ describe Puppet::FileServing::TerminusHelper do
@helper.path2instances(@request, "/my/file")
end
+ it "should pass :recurse, :ignore, and :links settings on to the fileset if present with the keys stored as strings" do
+ fileset = mock 'fileset', :files => []
+ Puppet::FileServing::Fileset.expects(:new).with("/my/file", :links => :a, :ignore => :b, :recurse => :c).returns(fileset)
+ @request.stubs(:options).returns("links" => :a, "ignore" => :b, "recurse" => :c)
+ @helper.path2instances(@request, "/my/file")
+ end
+
+ it "should convert the string 'true' to the boolean true when setting options" do
+ fileset = mock 'fileset', :files => []
+ Puppet::FileServing::Fileset.expects(:new).with("/my/file", :recurse => true).returns(fileset)
+ @request.stubs(:options).returns(:recurse => "true")
+ @helper.path2instances(@request, "/my/file")
+ end
+
+ it "should convert the string 'false' to the boolean false when setting options" do
+ fileset = mock 'fileset', :files => []
+ Puppet::FileServing::Fileset.expects(:new).with("/my/file", :recurse => false).returns(fileset)
+ @request.stubs(:options).returns(:recurse => "false")
+ @helper.path2instances(@request, "/my/file")
+ end
+
describe "when creating instances" do
before do
@request.stubs(:key).returns "puppet://host/mount/dir"
+ @one = stub 'one', :links= => nil, :collect => nil
+ @two = stub 'two', :links= => nil, :collect => nil
+
@fileset = mock 'fileset', :files => %w{one two}
Puppet::FileServing::Fileset.expects(:new).returns(@fileset)
end
it "should create an instance of the model for each path returned by the fileset" do
- @model.expects(:new).returns(:one)
- @model.expects(:new).returns(:two)
+ @model.expects(:new).returns(@one)
+ @model.expects(:new).returns(@two)
@helper.path2instances(@request, "/my/file").length.should == 2
end
- it "should set each instance's key to be the original key plus the file-specific path" do
- @model.expects(:new).with { |key, options| key == @request.key + "/one" }.returns(:one)
- @model.expects(:new).with { |key, options| key == @request.key + "/two" }.returns(:two)
- @helper.path2instances(@request, "/my/file")
- end
-
it "should set each returned instance's path to the original path" do
- @model.expects(:new).with { |key, options| options[:path] == "/my/file" }.returns(:one)
- @model.expects(:new).with { |key, options| options[:path] == "/my/file" }.returns(:two)
+ @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@one)
+ @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@two)
@helper.path2instances(@request, "/my/file")
end
it "should set each returned instance's relative path to the file-specific path" do
- @model.expects(:new).with { |key, options| options[:relative_path] == "one" }.returns(:one)
- @model.expects(:new).with { |key, options| options[:relative_path] == "two" }.returns(:two)
+ @model.expects(:new).with { |key, options| options[:relative_path] == "one" }.returns(@one)
+ @model.expects(:new).with { |key, options| options[:relative_path] == "two" }.returns(@two)
@helper.path2instances(@request, "/my/file")
end
it "should set the links value on each instance if one is provided" do
- one = mock 'one', :links= => :manage
- two = mock 'two', :links= => :manage
- @model.expects(:new).returns(one)
- @model.expects(:new).returns(two)
+ @one.expects(:links=).with :manage
+ @two.expects(:links=).with :manage
+ @model.expects(:new).returns(@one)
+ @model.expects(:new).returns(@two)
@request.options[:links] = :manage
@helper.path2instances(@request, "/my/file")
end
+
+ it "should collect the instance's attributes" do
+ @one.expects(:collect)
+ @two.expects(:collect)
+ @model.expects(:new).returns(@one)
+ @model.expects(:new).returns(@two)
+
+ @helper.path2instances(@request, "/my/file")
+ end
end
end
diff --git a/spec/unit/indirector/direct_file_server.rb b/spec/unit/indirector/direct_file_server.rb
index 0753f1bbe..e32fe6678 100755
--- a/spec/unit/indirector/direct_file_server.rb
+++ b/spec/unit/indirector/direct_file_server.rb
@@ -24,7 +24,7 @@ describe Puppet::Indirector::DirectFileServer do
@uri = "file:///my/local"
- @request = stub 'request', :key => @uri, :options => {}
+ @request = Puppet::Indirector::Request.new(:mytype, :find, @uri)
end
describe Puppet::Indirector::DirectFileServer, "when finding a single file" do
@@ -45,17 +45,12 @@ describe Puppet::Indirector::DirectFileServer do
before do
@data = mock 'content'
- @data.stubs(:collect_attributes)
+ @data.stubs(:collect)
FileTest.expects(:exists?).with("/my/local").returns true
end
- it "should create the Content instance with the original key as the key" do
- @model.expects(:new).with { |key, options| key == @uri }.returns(@data)
- @server.find(@request)
- end
-
it "should pass the full path to the instance" do
- @model.expects(:new).with { |key, options| options[:path] == "/my/local" }.returns(@data)
+ @model.expects(:new).with { |key, options| key == "/my/local" }.returns(@data)
@server.find(@request)
end
diff --git a/spec/unit/indirector/file_metadata/file.rb b/spec/unit/indirector/file_metadata/file.rb
index 9474620c7..a096d469d 100755
--- a/spec/unit/indirector/file_metadata/file.rb
+++ b/spec/unit/indirector/file_metadata/file.rb
@@ -21,14 +21,14 @@ describe Puppet::Indirector::FileMetadata::File do
@metadata = Puppet::Indirector::FileMetadata::File.new
@uri = "file:///my/local"
@data = mock 'metadata'
- @data.stubs(:collect_attributes)
+ @data.stubs(:collect)
FileTest.expects(:exists?).with("/my/local").returns true
- @request = stub 'request', :key => @uri, :options => {}
+ @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri)
end
it "should collect its attributes when a file is found" do
- @data.expects(:collect_attributes)
+ @data.expects(:collect)
Puppet::FileServing::Metadata.expects(:new).returns(@data)
@metadata.find(@request).should == @data
@@ -40,12 +40,12 @@ describe Puppet::Indirector::FileMetadata::File do
@metadata = Puppet::Indirector::FileMetadata::File.new
@uri = "file:///my/local"
- @request = stub 'request', :key => @uri, :options => {}
+ @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri)
end
it "should collect the attributes of the instances returned" do
FileTest.expects(:exists?).with("/my/local").returns true
- @metadata.expects(:path2instances).returns( [mock("one", :collect_attributes => nil), mock("two", :collect_attributes => nil)] )
+ @metadata.expects(:path2instances).returns( [mock("one", :collect => nil), mock("two", :collect => nil)] )
@metadata.search(@request)
end
end
diff --git a/spec/unit/indirector/file_metadata/modules.rb b/spec/unit/indirector/file_metadata/modules.rb
index 3905a49ad..7e5113df5 100755
--- a/spec/unit/indirector/file_metadata/modules.rb
+++ b/spec/unit/indirector/file_metadata/modules.rb
@@ -24,7 +24,7 @@ describe Puppet::Indirector::FileMetadata::Modules, " when finding metadata" do
@module = Puppet::Module.new("mymod", "/path/to")
@finder.stubs(:find_module).returns(@module)
- @request = Puppet::Indirector::Request.new(:metadata, :find, "puppetmounts://hostname/modules/mymod/my/file")
+ @request = Puppet::Indirector::Request.new(:metadata, :find, "puppet://hostname/modules/mymod/my/file")
end
it "should return nil if the file is not found" do
@@ -36,7 +36,7 @@ describe Puppet::Indirector::FileMetadata::Modules, " when finding metadata" do
FileTest.expects(:exists?).with("/path/to/files/my/file").returns true
instance = mock 'metadta'
Puppet::FileServing::Metadata.expects(:new).returns instance
- instance.expects :collect_attributes
+ instance.expects :collect
@finder.find(@request)
end
end
diff --git a/spec/unit/indirector/file_server.rb b/spec/unit/indirector/file_server.rb
index ba951737a..e17deecf9 100755
--- a/spec/unit/indirector/file_server.rb
+++ b/spec/unit/indirector/file_server.rb
@@ -24,7 +24,7 @@ describe Puppet::Indirector::FileServer do
@file_server = @file_server_class.new
- @uri = "puppetmounts://host/my/local/file"
+ @uri = "puppet://host/my/local/file"
@configuration = mock 'configuration'
Puppet::FileServing::Configuration.stubs(:create).returns(@configuration)
@@ -34,46 +34,42 @@ describe Puppet::Indirector::FileServer do
describe Puppet::Indirector::FileServer, " when finding files" do
it "should use the path portion of the URI as the file name" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil)
+ @configuration.expects(:file_path).with("my/local/file", :node => nil)
@file_server.find(@request)
end
it "should use the FileServing configuration to convert the file name to a fully qualified path" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil)
+ @configuration.expects(:file_path).with("my/local/file", :node => nil)
@file_server.find(@request)
end
it "should pass the node name to the FileServing configuration if one is provided" do
- @configuration.expects(:file_path).with("/my/local/file", :node => "testing")
+ @configuration.expects(:file_path).with("my/local/file", :node => "testing")
@request.node = "testing"
@file_server.find(@request)
end
it "should return nil if no fully qualified path is found" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns(nil)
+ @configuration.expects(:file_path).with("my/local/file", :node => nil).returns(nil)
@file_server.find(@request).should be_nil
end
it "should return an instance of the model created with the full path if a file is found" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/some/file")
- @model.expects(:new).returns(:myinstance)
- @file_server.find(@request).should == :myinstance
+ @configuration.expects(:file_path).with("my/local/file", :node => nil).returns("/some/file")
+ instance = stub("instance", :collect => nil)
+ @model.expects(:new).returns instance
+ @file_server.find(@request).should equal(instance)
end
end
describe Puppet::Indirector::FileServer, " when returning instances" do
before :each do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/some/file")
- @instance = mock 'instance'
- end
-
- it "should create the instance with the key used to find the instance" do
- @model.expects(:new).with { |key, *options| key == @uri }
- @file_server.find(@request)
+ @configuration.expects(:file_path).with("my/local/file", :node => nil).returns("/some/file")
+ @instance = stub 'instance', :collect => nil
end
it "should create the instance with the path at which the instance was found" do
- @model.expects(:new).with { |key, options| options[:path] == "/some/file" }
+ @model.expects(:new).with { |key, options| key == "/some/file" }.returns @instance
@file_server.find(@request)
end
@@ -88,6 +84,12 @@ describe Puppet::Indirector::FileServer do
@model.expects(:new).returns(@instance)
@file_server.find(@request)
end
+
+ it "should collect each instance's attributes before returning" do
+ @instance.expects(:collect)
+ @model.expects(:new).returns @instance
+ @file_server.find(@request)
+ end
end
describe Puppet::Indirector::FileServer, " when checking authorization" do
@@ -108,7 +110,6 @@ describe Puppet::Indirector::FileServer do
describe "and finding file information" do
before do
- @request.key = "puppetmounts://host/my/file"
@request.method = :find
end
@@ -118,7 +119,7 @@ describe Puppet::Indirector::FileServer do
end
it "should pass the file path from the URI to the file server configuration" do
- @configuration.expects(:authorized?).with { |uri, *args| uri == "/my/file" }
+ @configuration.expects(:authorized?).with { |uri, *args| uri == "my/local/file" }
@file_server.authorized?(@request)
end
@@ -149,36 +150,42 @@ describe Puppet::Indirector::FileServer do
describe Puppet::Indirector::FileServer, " when searching for files" do
it "should use the path portion of the URI as the file name" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil)
+ @configuration.expects(:file_path).with("my/local/file", :node => nil)
@file_server.search(@request)
end
it "should use the FileServing configuration to convert the file name to a fully qualified path" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil)
+ @configuration.expects(:file_path).with("my/local/file", :node => nil)
@file_server.search(@request)
end
it "should pass the node name to the FileServing configuration if one is provided" do
- @configuration.expects(:file_path).with("/my/local/file", :node => "testing")
+ @configuration.expects(:file_path).with("my/local/file", :node => "testing")
@request.node = "testing"
@file_server.search(@request)
end
it "should return nil if no fully qualified path is found" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns(nil)
+ @configuration.expects(:file_path).with("my/local/file", :node => nil).returns(nil)
@file_server.search(@request).should be_nil
end
it "should use :path2instances from the terminus_helper to return instances if a module is found and the file exists" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/my/file")
+ @configuration.expects(:file_path).with("my/local/file", :node => nil).returns("my/file")
@file_server.expects(:path2instances)
@file_server.search(@request)
end
it "should pass the request on to :path2instances" do
- @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/my/file")
- @file_server.expects(:path2instances).with(@request, "/my/file")
+ @configuration.expects(:file_path).with("my/local/file", :node => nil).returns("my/file")
+ @file_server.expects(:path2instances).with(@request, "my/file").returns []
@file_server.search(@request)
end
+
+ it "should return the result of :path2instances" do
+ @configuration.expects(:file_path).with("my/local/file", :node => nil).returns("my/file")
+ @file_server.expects(:path2instances).with(@request, "my/file").returns :footest
+ @file_server.search(@request).should == :footest
+ end
end
end
diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb
index e52be31e1..166065ecf 100755
--- a/spec/unit/indirector/indirection.rb
+++ b/spec/unit/indirector/indirection.rb
@@ -34,6 +34,22 @@ describe "Indirection Delegator", :shared => true do
@indirection.send(@method, "me")
end
+ it "should fail if the :select_terminus hook does not return a terminus name" do
+ # Define the method, so our respond_to? hook matches.
+ class << @indirection
+ def select_terminus(request)
+ end
+ end
+
+ request = stub 'request', :key => "me", :options => {}
+
+ @indirection.stubs(:request).returns request
+
+ @indirection.expects(:select_terminus).with(request).returns nil
+
+ lambda { @indirection.send(@method, "me") }.should raise_error(ArgumentError)
+ end
+
it "should choose the terminus returned by the :terminus_class method if no :select_terminus method is available" do
@indirection.expects(:terminus_class).returns :test_terminus
diff --git a/spec/unit/indirector/module_files.rb b/spec/unit/indirector/module_files.rb
index 32dedd4f1..f5f9527df 100755
--- a/spec/unit/indirector/module_files.rb
+++ b/spec/unit/indirector/module_files.rb
@@ -25,49 +25,47 @@ describe Puppet::Indirector::ModuleFiles do
@module_files = @module_files_class.new
- @uri = "puppetmounts://host/modules/my/local/file"
@module = Puppet::Module.new("mymod", "/module/path")
- @request = stub 'request', :key => @uri, :options => {}, :node => nil, :ip => nil, :method => :find
+ @request = Puppet::Indirector::Request.new(:mytype, :find, "puppet://host/modules/mymod/local/file")
end
describe Puppet::Indirector::ModuleFiles, " when finding files" do
+ before do
+ Puppet::Module.stubs(:find).returns @module
+ end
- it "should strip off the leading '/modules' mount name" do
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
+ it "should strip off the leading 'modules/' mount name" do
+ Puppet::Module.expects(:find).with { |key, env| key == 'mymod' }.returns @module
@module_files.find(@request)
end
- it "should not strip off leading terms that start with '/modules' but are longer words" do
- @request.stubs(:key).returns "puppetmounts://host/modulestart/my/local/file"
- Puppet::Module.expects(:find).with('modulestart', "myenv").returns nil
+ it "should not strip off leading terms that start with 'modules' but are longer words" do
+ @request.stubs(:key).returns "modulestart/mymod/local/file"
+ Puppet::Module.expects(:find).with { |key, env| key == 'modulestart'}.returns nil
@module_files.find(@request)
end
it "should search for a module whose name is the first term in the remaining file path" do
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
@module_files.find(@request)
end
it "should search for a file relative to the module's files directory" do
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
FileTest.expects(:exists?).with("/module/path/files/local/file")
@module_files.find(@request)
end
it "should return nil if the module does not exist" do
- Puppet::Module.expects(:find).with('my', "myenv").returns nil
+ Puppet::Module.expects(:find).returns nil
@module_files.find(@request).should be_nil
end
it "should return nil if the module exists but the file does not" do
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
FileTest.expects(:exists?).with("/module/path/files/local/file").returns(false)
@module_files.find(@request).should be_nil
end
it "should return an instance of the model if a module is found and the file exists" do
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true)
@model.expects(:new).returns(:myinstance)
@module_files.find(@request).should == :myinstance
@@ -76,7 +74,7 @@ describe Puppet::Indirector::ModuleFiles do
it "should use the node's environment to look up the module if the node name is provided" do
node = stub "node", :environment => "testing"
Puppet::Node.expects(:find).with("mynode").returns(node)
- Puppet::Module.expects(:find).with('my', "testing")
+ Puppet::Module.expects(:find).with('mymod', "testing")
@request.stubs(:node).returns "mynode"
@module_files.find(@request)
@@ -85,7 +83,7 @@ describe Puppet::Indirector::ModuleFiles do
it "should use the default environment setting to look up the module if the node name is not provided" do
env = stub "environment", :name => "testing"
Puppet::Node::Environment.stubs(:new).returns(env)
- Puppet::Module.expects(:find).with('my', "testing")
+ Puppet::Module.expects(:find).with('mymod', "testing")
@module_files.find(@request)
end
end
@@ -93,18 +91,13 @@ describe Puppet::Indirector::ModuleFiles do
describe Puppet::Indirector::ModuleFiles, " when returning instances" do
before do
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
+ Puppet::Module.expects(:find).with('mymod', "myenv").returns @module
FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true)
@instance = mock 'instance'
end
- it "should create the instance with the key used to find the instance" do
- @model.expects(:new).with { |key, *options| key == @uri }
- @module_files.find(@request)
- end
-
it "should create the instance with the path at which the instance was found" do
- @model.expects(:new).with { |key, options| options[:path] == "/module/path/files/local/file" }
+ @model.expects(:new).with { |key, options| key == "/module/path/files/local/file" }
@module_files.find(@request)
end
@@ -149,19 +142,19 @@ describe Puppet::Indirector::ModuleFiles do
end
it "should use the path directly from the URI if it already includes /modules" do
- @request.expects(:key).returns "puppetmounts://host/modules/my/file"
- @configuration.expects(:authorized?).with { |uri, *args| uri == "/modules/my/file" }
+ @request.expects(:key).returns "modules/my/file"
+ @configuration.expects(:authorized?).with { |uri, *args| uri == "modules/my/file" }
@module_files.authorized?(@request)
end
- it "should add /modules to the file path if it's not included in the URI" do
- @request.expects(:key).returns "puppetmounts://host/my/file"
- @configuration.expects(:authorized?).with { |uri, *args| uri == "/modules/my/file" }
+ it "should add modules/ to the file path if it's not included in the URI" do
+ @request.expects(:key).returns "my/file"
+ @configuration.expects(:authorized?).with { |uri, *args| uri == "modules/my/file" }
@module_files.authorized?(@request)
end
it "should pass the node name to the file server configuration" do
- @request.expects(:key).returns "puppetmounts://host/my/file"
+ @request.expects(:key).returns "my/file"
@configuration.expects(:authorized?).with { |key, options| options[:node] == "mynode" }
@request.stubs(:node).returns "mynode"
@module_files.authorized?(@request)
@@ -186,41 +179,41 @@ describe Puppet::Indirector::ModuleFiles do
describe Puppet::Indirector::ModuleFiles, " when searching for files" do
- it "should strip off the leading '/modules' mount name" do
+ it "should strip off the leading 'modules/' mount name" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
+ Puppet::Module.expects(:find).with { |key, env| key == 'mymod'}.returns @module
@module_files.search(@request)
end
it "should not strip off leading terms that start with '/modules' but are longer words" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
Puppet::Module.expects(:find).with('modulestart', "myenv").returns nil
- @request.stubs(:key).returns "puppetmounts://host/modulestart/my/local/file"
+ @request.stubs(:key).returns "modulestart/my/local/file"
@module_files.search @request
end
it "should search for a module whose name is the first term in the remaining file path" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
+ Puppet::Module.expects(:find).with('mymod', "myenv").returns @module
@module_files.search(@request)
end
it "should search for a file relative to the module's files directory" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
+ Puppet::Module.expects(:find).with('mymod', "myenv").returns @module
FileTest.expects(:exists?).with("/module/path/files/local/file")
@module_files.search(@request)
end
it "should return nil if the module does not exist" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
- Puppet::Module.expects(:find).with('my', "myenv").returns nil
+ Puppet::Module.expects(:find).with('mymod', "myenv").returns nil
@module_files.search(@request).should be_nil
end
it "should return nil if the module exists but the file does not" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
+ Puppet::Module.expects(:find).with('mymod', "myenv").returns @module
FileTest.expects(:exists?).with("/module/path/files/local/file").returns(false)
@module_files.search(@request).should be_nil
end
@@ -228,7 +221,7 @@ describe Puppet::Indirector::ModuleFiles do
it "should use the node's environment to look up the module if the node name is provided" do
node = stub "node", :environment => "testing"
Puppet::Node.expects(:find).with("mynode").returns(node)
- Puppet::Module.expects(:find).with('my', "testing")
+ Puppet::Module.expects(:find).with('mymod', "testing")
@request.stubs(:node).returns "mynode"
@module_files.search(@request)
end
@@ -236,13 +229,13 @@ describe Puppet::Indirector::ModuleFiles do
it "should use the default environment setting to look up the module if the node name is not provided and the environment is not set to ''" do
env = stub 'env', :name => "testing"
Puppet::Node::Environment.stubs(:new).returns(env)
- Puppet::Module.expects(:find).with('my', "testing")
+ Puppet::Module.expects(:find).with('mymod', "testing")
@module_files.search(@request)
end
it "should use :path2instances from the terminus_helper to return instances if a module is found and the file exists" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
+ Puppet::Module.expects(:find).with('mymod', "myenv").returns @module
FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true)
@module_files.expects(:path2instances).with(@request, "/module/path/files/local/file")
@module_files.search(@request)
@@ -250,7 +243,7 @@ describe Puppet::Indirector::ModuleFiles do
it "should pass the request directly to :path2instances" do
Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv"))
- Puppet::Module.expects(:find).with('my', "myenv").returns @module
+ Puppet::Module.expects(:find).with('mymod', "myenv").returns @module
FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true)
@module_files.expects(:path2instances).with(@request, "/module/path/files/local/file")
@module_files.search(@request)
diff --git a/spec/unit/indirector/request.rb b/spec/unit/indirector/request.rb
index f7702f821..18f625946 100755
--- a/spec/unit/indirector/request.rb
+++ b/spec/unit/indirector/request.rb
@@ -75,6 +75,57 @@ describe Puppet::Indirector::Request do
it "should keep its options as a hash even if another option is specified" do
Puppet::Indirector::Request.new(:ind, :method, :key, :foo => "bar").options.should be_instance_of(Hash)
end
+
+ describe "and the request key is a URI" do
+ describe "and the URI is a 'file' URI" do
+ before do
+ @request = Puppet::Indirector::Request.new(:ind, :method, "file:///my/file")
+ end
+
+ it "should set the request key to the full file path" do @request.key.should == "/my/file" end
+
+ it "should not set the protocol" do
+ @request.protocol.should be_nil
+ end
+
+ it "should not set the port" do
+ @request.port.should be_nil
+ end
+
+ it "should not set the server" do
+ @request.server.should be_nil
+ end
+ end
+
+ it "should set the protocol to the URI scheme" do
+ Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").protocol.should == "http"
+ end
+
+ it "should set the server if a server is provided" do
+ Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").server.should == "host"
+ end
+
+ it "should set the server and port if both are provided" do
+ Puppet::Indirector::Request.new(:ind, :method, "http://host:543/stuff").port.should == 543
+ end
+
+ it "should default to the masterport if the URI scheme is 'puppet'" do
+ Puppet.settings.expects(:value).with(:masterport).returns "321"
+ Puppet::Indirector::Request.new(:ind, :method, "puppet://host/stuff").port.should == 321
+ end
+
+ it "should use the provided port if the URI scheme is not 'puppet'" do
+ Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").port.should == 80
+ end
+
+ it "should set the request key to the unqualified path from the URI" do
+ Puppet::Indirector::Request.new(:ind, :method, "http:///stuff").key.should == "stuff"
+ end
+
+ it "should set the :uri attribute to the full URI" do
+ Puppet::Indirector::Request.new(:ind, :method, "http:///stuff").uri.should == "http:///stuff"
+ end
+ end
end
it "should look use the Indirection class to return the appropriate indirection" do
@@ -84,4 +135,16 @@ describe Puppet::Indirector::Request do
request.indirection.should equal(ind)
end
+
+ it "should have a method for determining if the request is plural or singular" do
+ Puppet::Indirector::Request.new(:myind, :method, :key).should respond_to(:plural?)
+ end
+
+ it "should be considered plural if the method is 'search'" do
+ Puppet::Indirector::Request.new(:myind, :search, :key).should be_plural
+ end
+
+ it "should not be considered plural if the method is not 'search'" do
+ Puppet::Indirector::Request.new(:myind, :find, :key).should_not be_plural
+ end
end
diff --git a/spec/unit/indirector/rest.rb b/spec/unit/indirector/rest.rb
index 3bd63359d..e7afbb5b2 100755
--- a/spec/unit/indirector/rest.rb
+++ b/spec/unit/indirector/rest.rb
@@ -45,6 +45,38 @@ describe Puppet::Indirector::REST do
@searcher = @rest_class.new
end
+ it "should have a method for specifying what setting a subclass should use to retrieve its server" do
+ @rest_class.should respond_to(:use_server_setting)
+ end
+
+ it "should use any specified setting to pick the server" do
+ @rest_class.expects(:server_setting).returns :servset
+ Puppet.settings.expects(:value).with(:servset).returns "myserver"
+ @rest_class.server.should == "myserver"
+ end
+
+ it "should default to :server for the server setting" do
+ @rest_class.expects(:server_setting).returns nil
+ Puppet.settings.expects(:value).with(:server).returns "myserver"
+ @rest_class.server.should == "myserver"
+ end
+
+ it "should have a method for specifying what setting a subclass should use to retrieve its port" do
+ @rest_class.should respond_to(:use_port_setting)
+ end
+
+ it "should use any specified setting to pick the port" do
+ @rest_class.expects(:port_setting).returns :servset
+ Puppet.settings.expects(:value).with(:servset).returns "321"
+ @rest_class.port.should == 321
+ end
+
+ it "should default to :port for the port setting" do
+ @rest_class.expects(:port_setting).returns nil
+ Puppet.settings.expects(:value).with(:masterport).returns "543"
+ @rest_class.port.should == 543
+ end
+
describe "when deserializing responses" do
it "should return nil if the response code is 404" do
response = mock 'response'
@@ -91,40 +123,26 @@ describe Puppet::Indirector::REST do
Puppet.settings.stubs(:value).returns("rest_testing")
end
- describe "and the request key is not a URL" do
- it "should use the :server setting as the host and the :masterport setting (as an Integer) as the port" do
- @request = stub 'request', :key => "foo"
- Puppet.settings.expects(:value).with(:server).returns "myserver"
- Puppet.settings.expects(:value).with(:masterport).returns "1234"
- Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 1234).returns "myconn"
- @searcher.network(@request).should == "myconn"
- end
+ it "should use the class's server and port if the indirection request provides neither" do
+ @request = stub 'request', :key => "foo", :server => nil, :port => nil
+ @searcher.class.expects(:port).returns 321
+ @searcher.class.expects(:server).returns "myserver"
+ Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn"
+ @searcher.network(@request).should == "myconn"
end
- describe "and the request key is a URL" do
- it "should use the :server setting and masterport setting, as an Integer, as the host if no values are provided" do
- @request = stub 'request', :key => "puppet:///key"
-
- Puppet.settings.expects(:value).with(:server).returns "myserver"
- Puppet.settings.expects(:value).with(:masterport).returns "1234"
- Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 1234).returns "myconn"
- @searcher.network(@request).should == "myconn"
- end
-
- it "should use the server if one is provided" do
- @request.stubs(:key).returns "puppet://host/key"
-
- Puppet.settings.expects(:value).with(:masterport).returns "1234"
- Puppet::Network::HttpPool.expects(:http_instance).with("host", 1234).returns "myconn"
- @searcher.network(@request).should == "myconn"
- end
-
- it "should use the server and port if they are provided" do
- @request.stubs(:key).returns "puppet://host:123/key"
+ it "should use the server from the indirection request if one is present" do
+ @request = stub 'request', :key => "foo", :server => "myserver", :port => nil
+ @searcher.class.stubs(:port).returns 321
+ Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn"
+ @searcher.network(@request).should == "myconn"
+ end
- Puppet::Network::HttpPool.expects(:http_instance).with("host", 123).returns "myconn"
- @searcher.network(@request).should == "myconn"
- end
+ it "should use the port from the indirection request if one is present" do
+ @request = stub 'request', :key => "foo", :server => nil, :port => 321
+ @searcher.class.stubs(:server).returns "myserver"
+ Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn"
+ @searcher.network(@request).should == "myconn"
end
end
@@ -158,7 +176,7 @@ describe Puppet::Indirector::REST do
it "should include all options in the query string" do
@request.stubs(:options).returns(:one => "two", :three => "four")
should_path = "/%s/%s" % [@indirection.name.to_s, "foo"]
- @connection.expects(:get).with { |path, args| path =~ /\?one=two&three=four$/ }.returns(@response)
+ @connection.expects(:get).with { |path, args| path =~ /\?one=two&three=four$/ or path =~ /\?three=four&one=two$/ }.returns(@response)
@searcher.find(@request)
end
@@ -220,7 +238,7 @@ describe Puppet::Indirector::REST do
@request.stubs(:options).returns(:one => "two", :three => "four")
should_path = "/%s/%s" % [@indirection.name.to_s, "foo"]
- @connection.expects(:get).with { |path, args| path =~ /\?one=two&three=four$/ }.returns(@response)
+ @connection.expects(:get).with { |path, args| path =~ /\?one=two&three=four$/ or path =~ /\?three=four&one=two$/ }.returns(@response)
@searcher.search(@request)
end
diff --git a/spec/unit/network/format.rb b/spec/unit/network/format.rb
index b960a3f73..244bff306 100755
--- a/spec/unit/network/format.rb
+++ b/spec/unit/network/format.rb
@@ -74,10 +74,27 @@ describe Puppet::Network::Format do
Puppet::Network::Format.new(:yaml).should_not be_supported(String)
end
+ it "should not consider a class supported unless the format is suitable" do
+ @format.expects(:suitable?).returns false
+ @format.should_not be_supported(FormatRenderer)
+ end
+
it "should always downcase mimetypes" do
@format.mime = "Foo/Bar"
@format.mime.should == "foo/bar"
end
+
+ it "should support having a weight" do
+ @format.should respond_to(:weight)
+ end
+
+ it "should default to a weight of of 5" do
+ @format.weight.should == 5
+ end
+
+ it "should be able to override its weight at initialization" do
+ Puppet::Network::Format.new(:foo, :weight => 1).weight.should == 1
+ end
end
describe "when converting between instances and formatted text" do
diff --git a/spec/unit/network/format_handler.rb b/spec/unit/network/format_handler.rb
index 35e996516..3dcff6199 100755
--- a/spec/unit/network/format_handler.rb
+++ b/spec/unit/network/format_handler.rb
@@ -21,18 +21,31 @@ describe Puppet::Network::FormatHandler do
end
it "should include all supported formats" do
- one = stub 'supported', :supported? => true, :name => "one"
- two = stub 'supported', :supported? => false, :name => "two"
- three = stub 'supported', :supported? => true, :name => "three"
- four = stub 'supported', :supported? => false, :name => "four"
- Puppet::Network::FormatHandler.expects(:formats).returns %w{one two three four}
- Puppet::Network::FormatHandler.expects(:format).with("one").returns one
- Puppet::Network::FormatHandler.expects(:format).with("two").returns two
- Puppet::Network::FormatHandler.expects(:format).with("three").returns three
- Puppet::Network::FormatHandler.expects(:format).with("four").returns four
+ one = stub 'supported', :supported? => true, :name => "one", :weight => 1
+ two = stub 'supported', :supported? => false, :name => "two", :weight => 1
+ three = stub 'supported', :supported? => true, :name => "three", :weight => 1
+ four = stub 'supported', :supported? => false, :name => "four", :weight => 1
+ Puppet::Network::FormatHandler.stubs(:formats).returns %w{one two three four}
+ Puppet::Network::FormatHandler.stubs(:format).with("one").returns one
+ Puppet::Network::FormatHandler.stubs(:format).with("two").returns two
+ Puppet::Network::FormatHandler.stubs(:format).with("three").returns three
+ Puppet::Network::FormatHandler.stubs(:format).with("four").returns four
FormatTester.supported_formats.sort.should == %w{one three}.sort
end
+ it "should return the supported formats in decreasing order of weight" do
+ one = stub 'supported', :supported? => true, :name => "one", :weight => 1
+ two = stub 'supported', :supported? => true, :name => "two", :weight => 6
+ three = stub 'supported', :supported? => true, :name => "three", :weight => 2
+ four = stub 'supported', :supported? => true, :name => "four", :weight => 8
+ Puppet::Network::FormatHandler.stubs(:formats).returns %w{one two three four}
+ Puppet::Network::FormatHandler.stubs(:format).with("one").returns one
+ Puppet::Network::FormatHandler.stubs(:format).with("two").returns two
+ Puppet::Network::FormatHandler.stubs(:format).with("three").returns three
+ Puppet::Network::FormatHandler.stubs(:format).with("four").returns four
+ FormatTester.supported_formats.should == %w{four two three one}
+ end
+
it "should return the first format as the default format" do
FormatTester.expects(:supported_formats).returns %w{one two}
FormatTester.default_format.should == "one"
diff --git a/spec/unit/network/formats.rb b/spec/unit/network/formats.rb
index f7493360d..0e21fefa7 100755
--- a/spec/unit/network/formats.rb
+++ b/spec/unit/network/formats.rb
@@ -99,4 +99,34 @@ describe "Puppet Network Format" do
@text.mime.should == "text/plain"
end
end
+
+ describe Puppet::Network::FormatHandler.format(:raw) do
+ before do
+ @format = Puppet::Network::FormatHandler.format(:raw)
+ end
+
+ it "should exist" do
+ @format.should_not be_nil
+ end
+
+ it "should have its mimetype set to application/x-raw" do
+ @format.mime.should == "application/x-raw"
+ end
+
+ it "should always be supported" do
+ @format.should be_supported(String)
+ end
+
+ it "should fail if its multiple_render method is used" do
+ lambda { @format.render_multiple("foo") }.should raise_error(NotImplementedError)
+ end
+
+ it "should fail if its multiple_intern method is used" do
+ lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError)
+ end
+
+ it "should have a weight of 1" do
+ @format.weight.should == 1
+ end
+ end
end
diff --git a/spec/unit/network/http/handler.rb b/spec/unit/network/http/handler.rb
index 6fc932091..ed0f25121 100755
--- a/spec/unit/network/http/handler.rb
+++ b/spec/unit/network/http/handler.rb
@@ -224,6 +224,9 @@ describe Puppet::Network::HTTP::Handler do
@handler.stubs(:singular?).returns(true)
@handler.stubs(:request_key).returns('key')
@model_class.stubs(:find).returns @result
+
+ @format = stub 'format', :suitable? => true
+ Puppet::Network::FormatHandler.stubs(:format).returns @format
end
it "should fail to find model if key is not specified" do
@@ -246,6 +249,28 @@ describe Puppet::Network::HTTP::Handler do
@handler.do_find(@request, @response)
end
+ it "should fail if no accept header is provided" do
+ @handler.expects(:accept_header).with(@request).returns nil
+ lambda { @handler.do_find(@request, @response) }.should raise_error(ArgumentError)
+ end
+
+ it "should fail if the accept header does not contain a valid format" do
+ @handler.expects(:accept_header).with(@request).returns ""
+ lambda { @handler.do_find(@request, @response) }.should raise_error(RuntimeError)
+ end
+
+ it "should not use an unsuitable format" do
+ @handler.expects(:accept_header).with(@request).returns "foo,bar"
+ foo = mock 'foo', :suitable? => false
+ bar = mock 'bar', :suitable? => true
+ Puppet::Network::FormatHandler.expects(:format).with("foo").returns foo
+ Puppet::Network::FormatHandler.expects(:format).with("bar").returns bar
+
+ @handler.expects(:set_content_type).with(@response, "bar") # the suitable one
+
+ @handler.do_find(@request, @response)
+ end
+
it "should render the result using the first format specified in the accept header" do
@handler.expects(:accept_header).with(@request).returns "one,two"
@result.expects(:render).with("one")
@@ -298,6 +323,9 @@ describe Puppet::Network::HTTP::Handler do
@result = [@result1, @result2]
@model_class.stubs(:render_multiple).returns "my rendered instances"
@model_class.stubs(:search).returns(@result)
+
+ @format = stub 'format', :suitable? => true
+ Puppet::Network::FormatHandler.stubs(:format).returns @format
end
it "should use a common method for determining the request parameters" do
@@ -409,6 +437,9 @@ describe Puppet::Network::HTTP::Handler do
@model_instance = stub('indirected model instance', :save => true)
@model_class.stubs(:convert_from).returns(@model_instance)
+
+ @format = stub 'format', :suitable? => true
+ Puppet::Network::FormatHandler.stubs(:format).returns @format
end
it "should use the 'body' hook to retrieve the body of the request" do
diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb
index 3b248dcfe..9dbc1c9ab 100755
--- a/spec/unit/network/http/mongrel/rest.rb
+++ b/spec/unit/network/http/mongrel/rest.rb
@@ -53,9 +53,9 @@ describe "Puppet::Network::HTTP::MongrelREST" do
@handler.path(@request).should == "/foo"
end
- it "should use the second part of the request path as the request key" do
- @params.expects(:[]).with(Mongrel::Const::REQUEST_PATH).returns "/foo/bar"
- @handler.request_key(@request).should == "bar"
+ it "should use the remainder of the request path as the request key" do
+ @params.expects(:[]).with(Mongrel::Const::REQUEST_PATH).returns "/foo/bar/baz"
+ @handler.request_key(@request).should == "bar/baz"
end
it "should return nil as the request key if no second field is present" do
diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb
index 620201472..1f4ccbe29 100755
--- a/spec/unit/network/http/webrick/rest.rb
+++ b/spec/unit/network/http/webrick/rest.rb
@@ -47,9 +47,9 @@ describe Puppet::Network::HTTP::WEBrickREST do
@handler.path(@request).should == "/foo"
end
- it "should return the second field in the path as the request key" do
- @request.expects(:path).returns "/foo/bar"
- @handler.request_key(@request).should == "bar"
+ it "should return the remainder of the path as the request key" do
+ @request.expects(:path).returns "/foo/bar/baz"
+ @handler.request_key(@request).should == "bar/baz"
end
it "should return nil as the request key if there is no second field" do
diff --git a/spec/unit/type/file.rb b/spec/unit/type/file.rb
index 663c5dc15..f0ebb49e2 100755
--- a/spec/unit/type/file.rb
+++ b/spec/unit/type/file.rb
@@ -8,6 +8,10 @@ describe Puppet::Type.type(:file) do
@path.close!()
@path = @path.path
@file = Puppet::Type::File.create(:name => @path)
+
+ @catalog = mock 'catalog'
+ @catalog.stub_everything
+ @file.catalog = @catalog
end
describe "when used with content and replace=>false" do
@@ -31,52 +35,6 @@ describe Puppet::Type.type(:file) do
end
end
- describe "when specifying a source" do
- before do
- @file[:source] = "/bar"
- end
-
- it "should raise if source doesn't exist" do
- @file.property(:source).expects(:found?).returns(false)
- lambda { @file.retrieve }.should raise_error(Puppet::Error)
- end
-
- end
-
- describe "when retrieving remote files" do
- before do
- @filesource = Puppet::Type::File::FileSource.new
- @filesource.server = mock 'fileserver'
-
- @file.stubs(:uri2obj).returns(@filesource)
-
- @file[:source] = "puppet:///test"
- end
-
- it "should fail without writing if it cannot retrieve remote contents" do
- # create the file, because we only get the problem when it starts
- # out absent.
- File.open(@file[:path], "w") { |f| f.puts "a" }
- @file.expects(:write).never
-
- @filesource.server.stubs(:describe).returns("493\tfile\t100\t0\t{md5}3f5fef3bddbc4398c46a7bd7ba7b3af7")
- @filesource.server.stubs(:retrieve).raises(RuntimeError)
- @file.property(:source).retrieve
- lambda { @file.property(:source).sync }.should raise_error(Puppet::Error)
- end
-
- it "should fail if it cannot describe remote contents" do
- @filesource.server.stubs(:describe).raises(Puppet::Network::XMLRPCClientError.new("Testing"))
- lambda { @file.retrieve }.should raise_error(Puppet::Error)
- end
-
- it "should fail during eval_generate if no remote sources exist" do
- file = Puppet::Type.type(:file).create :path => "/foobar", :source => "/this/file/does/not/exist", :recurse => true
-
- lambda { file.eval_generate }.should raise_error(Puppet::Error)
- end
- end
-
describe "when managing links" do
require 'puppettest/support/assertions'
include PuppetTest
@@ -116,4 +74,451 @@ describe Puppet::Type.type(:file) do
("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0755
end
end
+
+ describe "when flushing" do
+ it "should flush all properties that respond to :flush" do
+ @resource = Puppet.type(:file).create(:path => "/foo/bar", :source => "/bar/foo")
+ @resource.property(:source).expects(:flush)
+ @resource.flush
+ end
+ end
+
+ it "should have a method for performing recursion" do
+ @file.must respond_to(:perform_recursion)
+ end
+
+ describe "when executing a recursive search" do
+ it "should use Metadata to do its recursion" do
+ Puppet::FileServing::Metadata.expects(:search)
+ @file.perform_recursion(@file[:path])
+ end
+
+ it "should use the provided path as the key to the search" do
+ Puppet::FileServing::Metadata.expects(:search).with { |key, options| key == "/foo" }
+ @file.perform_recursion("/foo")
+ end
+
+ it "should return the results of the metadata search" do
+ Puppet::FileServing::Metadata.expects(:search).returns "foobar"
+ @file.perform_recursion(@file[:path]).should == "foobar"
+ end
+
+ it "should pass its recursion value to the search" do
+ @file[:recurse] = 10
+ Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:recurse] == 10 }
+ @file.perform_recursion(@file[:path])
+ end
+
+ it "should configure the search to ignore or manage links" do
+ @file[:links] = :manage
+ Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:links] == :manage }
+ @file.perform_recursion(@file[:path])
+ end
+
+ it "should pass its 'ignore' setting to the search if it has one" do
+ @file[:ignore] = %w{.svn CVS}
+ Puppet::FileServing::Metadata.expects(:search).with { |key, options| options[:ignore] == %w{.svn CVS} }
+ @file.perform_recursion(@file[:path])
+ end
+ end
+
+ it "should have a method for performing local recursion" do
+ @file.must respond_to(:recurse_local)
+ end
+
+ describe "when doing local recursion" do
+ before do
+ @metadata = stub 'metadata', :relative_path => "my/file"
+ end
+
+ it "should pass its to the :perform_recursion method" do
+ @file.expects(:perform_recursion).with(@file[:path]).returns [@metadata]
+ @file.stubs(:newchild)
+ @file.recurse_local
+ end
+
+ it "should return an empty hash if the recursion returns nothing" do
+ @file.expects(:perform_recursion).returns nil
+ @file.recurse_local.should == {}
+ end
+
+ it "should create a new child resource with each generated metadata instance's relative path" do
+ @file.expects(:perform_recursion).returns [@metadata]
+ @file.expects(:newchild).with(@metadata.relative_path).returns "fiebar"
+ @file.recurse_local
+ end
+
+ it "should not create a new child resource for the '.' directory" do
+ @metadata.stubs(:relative_path).returns "."
+
+ @file.expects(:perform_recursion).returns [@metadata]
+ @file.expects(:newchild).never
+ @file.recurse_local
+ end
+
+ it "should return a hash of the created resources with the relative paths as the hash keys" do
+ @file.expects(:perform_recursion).returns [@metadata]
+ @file.expects(:newchild).with("my/file").returns "fiebar"
+ @file.recurse_local.should == {"my/file" => "fiebar"}
+ end
+ end
+
+ it "should have a method for performing link recursion" do
+ @file.must respond_to(:recurse_link)
+ end
+
+ describe "when doing link recursion" do
+ before do
+ @first = stub 'first', :relative_path => "first", :full_path => "/my/first", :ftype => "directory"
+ @second = stub 'second', :relative_path => "second", :full_path => "/my/second", :ftype => "file"
+
+ @resource = stub 'file', :[]= => nil
+ end
+
+ it "should pass its target to the :perform_recursion method" do
+ @file[:target] = "mylinks"
+ @file.expects(:perform_recursion).with("mylinks").returns [@first]
+ @file.stubs(:newchild).returns @resource
+ @file.recurse_link({})
+ end
+
+ it "should ignore the recursively-found '.' file and configure the top-level file to create a directory" do
+ @first.stubs(:relative_path).returns "."
+ @file[:target] = "mylinks"
+ @file.expects(:perform_recursion).with("mylinks").returns [@first]
+ @file.stubs(:newchild).never
+ @file.expects(:[]=).with(:ensure, :directory)
+ @file.recurse_link({})
+ end
+
+ it "should create a new child resource for each generated metadata instance's relative path that doesn't already exist in the children hash" do
+ @file.expects(:perform_recursion).returns [@first, @second]
+ @file.expects(:newchild).with(@first.relative_path).returns @resource
+ @file.recurse_link("second" => @resource)
+ end
+
+ it "should not create a new child resource for paths that already exist in the children hash" do
+ @file.expects(:perform_recursion).returns [@first]
+ @file.expects(:newchild).never
+ @file.recurse_link("first" => @resource)
+ end
+
+ it "should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory" do
+ file = stub 'file'
+ file.expects(:[]=).with(:target, "/my/second")
+ file.expects(:[]=).with(:ensure, :link)
+
+ @file.stubs(:perform_recursion).returns [@first, @second]
+ @file.recurse_link("first" => @resource, "second" => file)
+ end
+
+ it "should :ensure to :directory if the file is a directory" do
+ file = stub 'file'
+ file.expects(:[]=).with(:ensure, :directory)
+
+ @file.stubs(:perform_recursion).returns [@first, @second]
+ @file.recurse_link("first" => file, "second" => @resource)
+ end
+
+ it "should return a hash with both created and existing resources with the relative paths as the hash keys" do
+ file = stub 'file', :[]= => nil
+
+ @file.expects(:perform_recursion).returns [@first, @second]
+ @file.stubs(:newchild).returns file
+ @file.recurse_link("second" => @resource).should == {"second" => @resource, "first" => file}
+ end
+ end
+
+ it "should have a method for performing remote recursion" do
+ @file.must respond_to(:recurse_remote)
+ end
+
+ describe "when doing remote recursion" do
+ before do
+ @file[:source] = "puppet://foo/bar"
+
+ @first = Puppet::FileServing::Metadata.new("/my", :relative_path => "first")
+ @second = Puppet::FileServing::Metadata.new("/my", :relative_path => "second")
+
+ @property = stub 'property', :metadata= => nil
+ @resource = stub 'file', :[]= => nil, :property => @property
+ end
+
+ it "should pass its source to the :perform_recursion method" do
+ data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar")
+ @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data]
+ @file.stubs(:newchild).returns @resource
+ @file.recurse_remote({})
+ end
+
+ it "should set the source of each returned file to the searched-for URI plus the found relative path" do
+ @first.expects(:source=).with File.join("puppet://foo/bar", @first.relative_path)
+ @file.expects(:perform_recursion).returns [@first]
+ @file.stubs(:newchild).returns @resource
+ @file.recurse_remote({})
+ end
+
+ it "should create a new resource for any relative file paths that do not already have a resource" do
+ @file.stubs(:perform_recursion).returns [@first]
+ @file.expects(:newchild).with("first").returns @resource
+ @file.recurse_remote({}).should == {"first" => @resource}
+ end
+
+ it "should not create a new resource for any relative file paths that do already have a resource" do
+ @file.stubs(:perform_recursion).returns [@first]
+ @file.expects(:newchild).never
+ @file.recurse_remote("first" => @resource)
+ end
+
+ it "should set the source of each resource to the source of the metadata" do
+ @file.stubs(:perform_recursion).returns [@first]
+ @resource.stubs(:[]=)
+ @resource.expects(:[]=).with(:source, File.join("puppet://foo/bar", @first.relative_path))
+ @file.recurse_remote("first" => @resource)
+ end
+
+ # LAK:FIXME This is a bug, but I can't think of a fix for it. Fortunately it's already
+ # filed, and when it's fixed, we'll just fix the whole flow.
+ it "should set the checksum type to :md5 if the remote file is a file" do
+ @first.stubs(:ftype).returns "file"
+ @file.stubs(:perform_recursion).returns [@first]
+ @resource.stubs(:[]=)
+ @resource.expects(:[]=).with(:checksum, :md5)
+ @file.recurse_remote("first" => @resource)
+ end
+
+ it "should store the metadata in the source property for each resource so the source does not have to requery the metadata" do
+ @file.stubs(:perform_recursion).returns [@first]
+ @resource.expects(:property).with(:source).returns @property
+
+ @property.expects(:metadata=).with(@first)
+
+ @file.recurse_remote("first" => @resource)
+ end
+
+ it "should not create a new resource for the '.' file" do
+ @first.stubs(:relative_path).returns "."
+ @file.stubs(:perform_recursion).returns [@first]
+
+ @file.expects(:newchild).never
+
+ @file.recurse_remote({})
+ end
+
+ it "should store the metadata in the main file's source property if the relative path is '.'" do
+ @first.stubs(:relative_path).returns "."
+ @file.stubs(:perform_recursion).returns [@first]
+
+ @file.property(:source).expects(:metadata=).with @first
+
+ @file.recurse_remote("first" => @resource)
+ end
+
+ describe "and purging is enabled" do
+ before do
+ @file[:purge] = true
+ end
+
+ it "should configure each file not on the remote system to be removed" do
+ @file.stubs(:perform_recursion).returns [@second]
+
+ @resource.expects(:[]=).with(:ensure, :absent)
+
+ @file.expects(:newchild).returns stub('secondfile', :[]= => nil, :property => @property)
+
+ @file.recurse_remote("first" => @resource)
+ end
+ end
+
+ describe "and multiple sources are provided" do
+ describe "and :sourceselect is set to :first" do
+ it "should create file instances for the results for the first source to return any values" do
+ data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar")
+ @file[:source] = %w{/one /two /three /four}
+ @file.expects(:perform_recursion).with("/one").returns nil
+ @file.expects(:perform_recursion).with("/two").returns []
+ @file.expects(:perform_recursion).with("/three").returns [data]
+ @file.expects(:perform_recursion).with("/four").never
+ @file.expects(:newchild).with("foobar").returns @resource
+ @file.recurse_remote({})
+ end
+ end
+
+ describe "and :sourceselect is set to :all" do
+ before do
+ @file[:sourceselect] = :all
+ end
+
+ it "should return every found file that is not in a previous source" do
+ klass = Puppet::FileServing::Metadata
+ @file[:source] = %w{/one /two /three /four}
+ @file.stubs(:newchild).returns @resource
+
+ one = [klass.new("/one", :relative_path => "a")]
+ @file.expects(:perform_recursion).with("/one").returns one
+ @file.expects(:newchild).with("a").returns @resource
+
+ two = [klass.new("/two", :relative_path => "a"), klass.new("/two", :relative_path => "b")]
+ @file.expects(:perform_recursion).with("/two").returns two
+ @file.expects(:newchild).with("b").returns @resource
+
+ three = [klass.new("/three", :relative_path => "a"), klass.new("/three", :relative_path => "c")]
+ @file.expects(:perform_recursion).with("/three").returns three
+ @file.expects(:newchild).with("c").returns @resource
+
+ @file.expects(:perform_recursion).with("/four").returns []
+
+ @file.recurse_remote({})
+ end
+ end
+ end
+ end
+
+ describe "when returning resources with :eval_generate" do
+ before do
+ @catalog = mock 'catalog'
+ @catalog.stub_everything
+
+ @graph = stub 'graph', :add_edge => nil
+ @catalog.stubs(:relationship_graph).returns @graph
+
+ @file.catalog = @catalog
+ @file[:recurse] = true
+ end
+
+ it "should recurse if recursion is enabled" do
+ resource = stub('resource', :[] => "resource")
+ @file.expects(:recurse?).returns true
+ @file.expects(:recurse).returns [resource]
+ @file.eval_generate.should == [resource]
+ end
+
+ it "should not recurse if recursion is disabled" do
+ @file.expects(:recurse?).returns false
+ @file.expects(:recurse).never
+ @file.eval_generate.should be_nil
+ end
+
+ it "should fail if no catalog is set" do
+ @file.catalog = nil
+ lambda { @file.eval_generate }.should raise_error(Puppet::DevError)
+ end
+
+ it "should skip resources that are already in the catalog" do
+ foo = stub 'foo', :[] => "/foo"
+ bar = stub 'bar', :[] => "/bar"
+ bar2 = stub 'bar2', :[] => "/bar"
+
+ @catalog.expects(:resource).with(:file, "/foo").returns nil
+ @catalog.expects(:resource).with(:file, "/bar").returns bar2
+
+ @file.expects(:recurse).returns [foo, bar]
+
+ @file.eval_generate.should == [foo]
+ end
+
+ it "should add each resource to the catalog" do
+ foo = stub 'foo', :[] => "/foo"
+ bar = stub 'bar', :[] => "/bar"
+ bar2 = stub 'bar2', :[] => "/bar"
+
+ @catalog.expects(:add_resource).with(foo)
+ @catalog.expects(:add_resource).with(bar)
+
+ @file.expects(:recurse).returns [foo, bar]
+
+ @file.eval_generate
+ end
+
+ it "should add a relationshp edge for each returned resource" do
+ foo = stub 'foo', :[] => "/foo"
+
+ @file.expects(:recurse).returns [foo]
+
+ graph = mock 'graph'
+ @catalog.stubs(:relationship_graph).returns graph
+
+ graph.expects(:add_edge).with(@file, foo)
+
+ @file.eval_generate
+ end
+ end
+
+ describe "when recursing" do
+ before do
+ @file[:recurse] = true
+ @metadata = Puppet::FileServing::Metadata
+ end
+
+ describe "and a source is set" do
+ before { @file[:source] = "/my/source" }
+
+ it "should pass the already-discovered resources to recurse_remote" do
+ @file.stubs(:recurse_local).returns(:foo => "bar")
+ @file.expects(:recurse_remote).with(:foo => "bar").returns []
+ @file.recurse
+ end
+ end
+
+ describe "and a target is set" do
+ before { @file[:target] = "/link/target" }
+
+ it "should use recurse_link" do
+ @file.stubs(:recurse_local).returns(:foo => "bar")
+ @file.expects(:recurse_link).with(:foo => "bar").returns []
+ @file.recurse
+ end
+ end
+
+ it "should use recurse_local" do
+ @file.expects(:recurse_local).returns({})
+ @file.recurse
+ end
+
+ it "should return the generated resources as an array sorted by file path" do
+ one = stub 'one', :[] => "/one"
+ two = stub 'two', :[] => "/one/two"
+ three = stub 'three', :[] => "/three"
+ @file.expects(:recurse_local).returns(:one => one, :two => two, :three => three)
+ @file.recurse.should == [one, two, three]
+ end
+
+ describe "and making a new child resource" do
+ it "should create an implicit resource using the provided relative path joined with the file's path" do
+ path = File.join(@file[:path], "my/path")
+ Puppet::Type.type(:file).expects(:create).with { |options| options[:implicit] == true and options[:path] == path }
+ @file.newchild("my/path")
+ end
+
+ it "should copy most of the parent resource's 'should' values to the new resource" do
+ @file.expects(:to_hash).returns :foo => "bar", :fee => "fum"
+ Puppet::Type.type(:file).expects(:create).with { |options| options[:foo] == "bar" and options[:fee] == "fum" }
+ @file.newchild("my/path")
+ end
+
+ it "should not copy the parent resource's parent" do
+ @file.expects(:to_hash).returns :parent => "foo"
+ Puppet::Type.type(:file).expects(:create).with { |options| ! options.include?(:parent) }
+ @file.newchild("my/path")
+ end
+
+ it "should not copy the parent resource's recurse value" do
+ @file.expects(:to_hash).returns :recurse => true
+ Puppet::Type.type(:file).expects(:create).with { |options| ! options.include?(:recurse) }
+ @file.newchild("my/path")
+ end
+
+ it "should not copy the parent resource's target value" do
+ @file.expects(:to_hash).returns :target => "foo"
+ Puppet::Type.type(:file).expects(:create).with { |options| ! options.include?(:target) }
+ @file.newchild("my/path")
+ end
+
+ it "should not copy any nil values from the parent" do
+ @file.expects(:to_hash).returns :ensure => nil
+ Puppet::Type.type(:file).expects(:create).with { |options| ! options.include?(:ensure) }
+ @file.newchild("my/path")
+ end
+ end
+ end
end
diff --git a/spec/unit/type/file/source.rb b/spec/unit/type/file/source.rb
new file mode 100755
index 000000000..2883e9c6b
--- /dev/null
+++ b/spec/unit/type/file/source.rb
@@ -0,0 +1,282 @@
+#!/usr/bin/env ruby
+
+Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+
+source = Puppet::Type.type(:file).attrclass(:source)
+describe Puppet::Type.type(:file).attrclass(:source) do
+ before do
+ # Wow that's a messy interface to the resource.
+ @resource = stub 'resource', :uri2obj => true, :[]= => nil, :property => nil
+ end
+
+ it "should be a subclass of Property" do
+ source.superclass.must == Puppet::Property
+ end
+
+ describe "when initializing" do
+ it "should fail if the 'should' values are not URLs" do
+ s = source.new(:resource => @resource)
+ URI.expects(:parse).with('foo').raises RuntimeError
+
+ lambda { s.should = %w{foo} }.must raise_error(Puppet::Error)
+ end
+
+ it "should fail if the URI is not a local file, file URI, or puppet URI" do
+ s = source.new(:resource => @resource)
+
+ lambda { s.should = %w{http://foo/bar} }.must raise_error(Puppet::Error)
+ end
+ end
+
+ it "should have a method for retrieving its metadata" do
+ source.new(:resource => @resource).must respond_to(:metadata)
+ end
+
+ it "should have a method for setting its metadata" do
+ source.new(:resource => @resource).must respond_to(:metadata=)
+ end
+
+ describe "when returning the metadata" do
+ before do
+ @metadata = stub 'metadata', :source= => nil
+ end
+
+ it "should return already-available metadata" do
+ @source = source.new(:resource => @resource)
+ @source.metadata = "foo"
+ @source.metadata.should == "foo"
+ end
+
+ it "should return nil if no @should value is set and no metadata is available" do
+ @source = source.new(:resource => @resource)
+ @source.metadata.should be_nil
+ end
+
+ it "should collect its metadata using the Metadata class if it is not already set" do
+ @source = source.new(:resource => @resource, :should => "/foo/bar")
+ Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns @metadata
+ @source.metadata
+ end
+
+ it "should use the metadata from the first found source" do
+ metadata = stub 'metadata', :source= => nil
+ @source = source.new(:resource => @resource, :should => ["/foo/bar", "/fee/booz"])
+ Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns nil
+ Puppet::FileServing::Metadata.expects(:find).with("/fee/booz").returns metadata
+ @source.metadata.should equal(metadata)
+ end
+
+ it "should store the found source as the metadata's source" do
+ metadata = mock 'metadata'
+ @source = source.new(:resource => @resource, :should => "/foo/bar")
+ Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns metadata
+
+ metadata.expects(:source=).with("/foo/bar")
+ @source.metadata
+ end
+
+ it "should fail intelligently if an exception is encountered while querying for metadata" do
+ @source = source.new(:resource => @resource, :should => "/foo/bar")
+ Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").raises RuntimeError
+
+ @source.expects(:fail).raises ArgumentError
+ lambda { @source.metadata }.should raise_error(ArgumentError)
+ end
+
+ it "should fail if no specified sources can be found" do
+ @source = source.new(:resource => @resource, :should => "/foo/bar")
+ Puppet::FileServing::Metadata.expects(:find).with("/foo/bar").returns nil
+
+ @source.expects(:fail).raises RuntimeError
+
+ lambda { @source.metadata }.should raise_error(RuntimeError)
+ end
+ end
+
+ it "should have a method for setting the desired values on the resource" do
+ source.new(:resource => @resource).must respond_to(:copy_source_values)
+ end
+
+ describe "when copying the source values" do
+ before do
+ @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => 123, :checksum => "{md5}asdfasdf"
+
+ @source = source.new(:resource => @resource)
+ @source.metadata = @metadata
+
+ @resource.stubs(:deleting?).returns false
+ end
+
+ it "should fail if there is no metadata" do
+ @source.metadata = nil
+ @source.expects(:devfail).raises ArgumentError
+ lambda { @source.copy_source_values }.should raise_error(ArgumentError)
+ end
+
+ it "should set :ensure to the file type" do
+ @resource.stubs(:[])
+ @resource.stubs(:[]=)
+ @metadata.stubs(:ftype).returns "foobar"
+
+ @resource.expects(:[]=).with(:ensure, "foobar")
+ @source.copy_source_values
+ end
+
+ describe "and the source is a file" do
+ before do
+ @metadata.stubs(:ftype).returns "file"
+ end
+
+ it "should copy the metadata's owner, group, and mode to the resource if they are not set on the resource" do
+ @resource.stubs(:[]).returns nil
+
+ Puppet::Util::SUIDManager.expects(:uid).returns 0
+
+ @resource.expects(:[]=).with(:owner, 100)
+ @resource.expects(:[]=).with(:group, 200)
+ @resource.expects(:[]=).with(:mode, 123)
+ @resource.expects(:[]=).with(:checksum, "{md5}asdfasdf")
+
+ @source.copy_source_values
+ end
+
+ it "should copy the metadata's owner, group, and mode to the resource if they are set to :absent on the resource" do
+ @resource.stubs(:[]).returns :absent
+
+ Puppet::Util::SUIDManager.expects(:uid).returns 0
+
+ @resource.expects(:[]=).with(:owner, 100)
+ @resource.expects(:[]=).with(:group, 200)
+ @resource.expects(:[]=).with(:mode, 123)
+ @resource.expects(:[]=).with(:checksum, "{md5}asdfasdf")
+
+ @source.copy_source_values
+ end
+
+ it "should not copy the metadata's owner to the resource if it is already set" do
+ @resource.stubs(:[]).returns "value"
+ @resource.expects(:[]).returns "value"
+
+ @resource.expects(:[]=).never
+
+ @source.copy_source_values
+ end
+
+ describe "and puppet is not running as root" do
+ it "should not try to set the owner" do
+ @resource.stubs(:[]).returns nil
+ @resource.stubs(:[]=)
+
+ @resource.expects(:[]=).with(:owner, 100).never
+
+ Puppet::Util::SUIDManager.expects(:uid).returns 100
+
+ @source.copy_source_values
+ end
+ end
+ end
+
+ describe "and the source is a link" do
+ it "should set the target to the link destination" do
+ @metadata.stubs(:ftype).returns "link"
+ @resource.stubs(:[])
+ @resource.stubs(:[]=)
+
+ @metadata.expects(:destination).returns "/path/to/symlink"
+
+ @resource.expects(:[]=).with(:target, "/path/to/symlink")
+ @source.copy_source_values
+ end
+ end
+ end
+
+ describe "when retrieving the property state" do
+ it "should copy all metadata to the resource" do
+ @source = source.new(:resource => @resource)
+ @source.expects(:copy_source_values)
+
+ @source.retrieve
+ end
+ end
+
+ describe "when flushing" do
+ it "should set its metadata to nil" do
+ @source = source.new(:resource => @resource)
+ @source.metadata = "foo"
+ @source.flush
+ @source.instance_variable_get("@metadata").should be_nil
+ end
+
+ it "should reset its content" do
+ @source = source.new(:resource => @resource)
+ @source.instance_variable_set("@content", "foo")
+ @source.flush
+ @source.instance_variable_get("@content").should be_nil
+ end
+ end
+
+ it "should have a method for returning the content" do
+ source.new(:resource => @resource).must respond_to(:content)
+ end
+
+ describe "when looking up the content" do
+ before do
+ @source = source.new(:resource => @resource)
+ @metadata = stub 'metadata', :source => "/my/source"
+ @source.metadata = @metadata
+
+ @content = stub 'content', :content => "foobar"
+ end
+
+ it "should fail if the metadata does not have a source set" do
+ @metadata.stubs(:source).returns nil
+ lambda { @source.content }.should raise_error(Puppet::DevError)
+ end
+
+ it "should look the content up from the Content class using the metadata source if no content is set" do
+ Puppet::FileServing::Content.expects(:find).with("/my/source").returns @content
+ @source.content.should == "foobar"
+ end
+
+ it "should return previously found content" do
+ Puppet::FileServing::Content.expects(:find).with("/my/source").returns @content
+ @source.content.should == "foobar"
+ @source.content.should == "foobar"
+ end
+
+ it "should fail if no content can be retrieved" do
+ Puppet::FileServing::Content.expects(:find).with("/my/source").returns nil
+ @source.expects(:fail).raises RuntimeError
+ lambda { @source.content }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "when changing the content" do
+ before do
+ @source = source.new(:resource => @resource)
+ @source.stubs(:content).returns "foobar"
+
+ @metadata = stub 'metadata', :checksum => 123
+ @source.metadata = @metadata
+ @resource.stubs(:[]).with(:path).returns "/boo"
+ end
+
+ it "should use the file's :write method to write the content" do
+ @resource.expects(:write).with("foobar", :source, 123)
+
+ @source.sync
+ end
+
+ it "should return :file_changed if the file already existed" do
+ @resource.stubs(:write)
+ FileTest.expects(:exist?).with("/boo").returns true
+ @source.sync.should == :file_changed
+ end
+
+ it "should return :file_created if the file already existed" do
+ @resource.stubs(:write)
+ FileTest.expects(:exist?).with("/boo").returns false
+ @source.sync.should == :file_created
+ end
+ end
+end