summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/type/pfile.rb64
-rwxr-xr-xlib/puppet/type/pfile/ensure.rb89
-rw-r--r--lib/puppet/type/state.rb4
-rwxr-xr-xlib/puppet/type/symlink.rb4
-rwxr-xr-xlib/puppet/type/user.rb42
-rw-r--r--test/types/file.rb143
-rw-r--r--test/types/parameter.rb2
-rw-r--r--test/types/state.rb6
-rwxr-xr-xtest/types/symlink.rb3
9 files changed, 304 insertions, 53 deletions
diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb
index d23be0fca..d77da1942 100644
--- a/lib/puppet/type/pfile.rb
+++ b/lib/puppet/type/pfile.rb
@@ -218,8 +218,8 @@ module Puppet
return children unless self[:ignore]
self[:ignore].each { |ignore|
ignored = []
- Dir.glob(File.join(self[:path],ignore), File::FNM_DOTMATCH) { |match|
- ignored.push(File.basename(match))
+ Dir.glob(File.join(self[:path],ignore), File::FNM_DOTMATCH) {
+ |match| ignored.push(File.basename(match))
}
children = children - ignored
}
@@ -229,7 +229,6 @@ module Puppet
def initialize(hash)
# clean out as many references to any file paths as possible
# this was the source of many, many bugs
-
@arghash = self.argclean(hash)
@arghash.delete(self.class.namevar)
@@ -409,11 +408,70 @@ module Puppet
end
self.localrecurse(recurse)
+ if @states.include?(:ensure) and @states[:ensure].should =~ /^#{File::SEPARATOR}/
+ self.linkrecurse(recurse)
+ end
if @states.include?(:source)
self.sourcerecurse(recurse)
end
end
+ # Build a recursive map of a link source
+ def linkrecurse(recurse)
+ target = @states[:ensure].should
+
+ method = :lstat
+ if self[:links] == :follow
+ method = :stat
+ end
+
+ targetstat = nil
+ unless FileTest.exist?(target)
+ #self.info "%s does not exist; not recursing" %
+ # target
+ return
+ end
+ # Now stat our target
+ targetstat = File.send(method, target)
+ unless targetstat.ftype == "directory"
+ #self.info "%s is not a directory; not recursing" %
+ # target
+ return
+ end
+
+ 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|
+ longname = File.join(@states[:ensure].should, 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)
+ unless @children.include?(child)
+ self.push child
+ added.push file
+ end
+ end
+ end
+ 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" %
diff --git a/lib/puppet/type/pfile/ensure.rb b/lib/puppet/type/pfile/ensure.rb
index 88dd20024..9f04202cb 100755
--- a/lib/puppet/type/pfile/ensure.rb
+++ b/lib/puppet/type/pfile/ensure.rb
@@ -3,11 +3,32 @@ module Puppet
require 'etc'
desc "Whether to create files that don't currently exist.
Possible values are *absent*, *present* (equivalent to *file*),
- **file**/*directory*. Specifying 'absent' will delete the file,
+ *file*, and *directory*. Specifying 'absent' will delete the file,
although currently this will not recursively delete directories.
+
+ Anything other than those values will be considered to be a symlink.
+ For instance, the following text creates a link::
+
+ # Useful on solaris
+ file { \"/etc/inetd.conf\":
+ ensure => \"/etc/inet/inetd.conf\"
+ }
- This is the only element with an *ensure* state that does not have
- a default value."
+ You can make relative links:
+
+ # Useful on solaris
+ file { \"/etc/inetd.conf\":
+ ensure => \"inet/inetd.conf\"
+ }
+
+ If you need to make a relative link to a file named the same
+ as one of the valid values, you must prefix it with ``./`` or
+ something similar.
+
+ You can also make recursive symlinks, which will create a
+ directory structure that maps to the target directory,
+ with directories corresponding to each directory
+ and links corresponding to each file."
# Most 'ensure' states have a default, but with files we, um, don't.
nodefault
@@ -52,6 +73,41 @@ module Puppet
return :directory_created
end
+ # Symlinks. We pretty much have to match just about anything,
+ # in order to match relative links. Somewhat ugly, but eh, it
+ # works.
+ newvalue(/./) do
+ Dir.chdir(File.dirname(@parent[:path])) do
+ target = self.should
+ unless FileTest.exists?(target)
+ self.debug "Not linking to non-existent '%s'" % target
+ nil # Grrr, can't return
+ else
+ if FileTest.directory?(target) and @parent[:recurse]
+ # If we're pointing to a directory and recursion is
+ # enabled, then make a directory instead of a link.
+ self.set_directory
+ else
+ Puppet::Util.asuser(@parent.asuser()) do
+ mode = @parent.should(:mode)
+ if mode
+ Puppet::Util.withumask(000) do
+ File.symlink(self.should, @parent[:path])
+ end
+ else
+ File.symlink(self.should, @parent[:path])
+ end
+ end
+
+ # We can't use "return" here because we're in an anonymous
+ # block.
+ :link_created
+ end
+ end
+ end
+ end
+
+ # Check that we can actually create anything
def check
basedir = File.dirname(@parent[:path])
@@ -68,7 +124,13 @@ module Puppet
def retrieve
if stat = @parent.stat(false)
- @is = stat.ftype.intern
+ # If we're a link, set the 'is' value to the destination
+ # of the link
+ if stat.ftype == "link"
+ @is = File.readlink(@parent[:path])
+ else
+ @is = stat.ftype.intern
+ end
else
if self.should == :false
@is = :false
@@ -76,26 +138,7 @@ module Puppet
@is = :absent
end
end
-
- #self.debug "'exists' state is %s" % self.is
end
-
-
- # We can mostly rely on the superclass method, but we want other states
- # to take precedence over 'ensure' if they are present.
-# def sync
-# # XXX This is a bad idea, because it almost guarantees bugs if we
-# # introduce more states to manage content, but anything else is just
-# # about as bad.
-# event = nil
-# #if state = @parent.state(:source) or state = @parent.state(:content)
-# # event = state.sync
-# #else
-# event = super
-# @parent.setchecksum
-# #end
-# return event
-# end
end
end
diff --git a/lib/puppet/type/state.rb b/lib/puppet/type/state.rb
index 17a004056..05f876583 100644
--- a/lib/puppet/type/state.rb
+++ b/lib/puppet/type/state.rb
@@ -73,7 +73,9 @@ class State < Puppet::Parameter
[value, self.class.name, detail]
end
elsif ary = self.class.match?(value)
- event = ary[1].call(value)
+ # FIXME It'd be better here to define a method, so that
+ # the blocks could return values.
+ event = self.instance_eval(&ary[1])
else
self.fail "%s is not a valid value for %s" %
[value, self.class.name]
diff --git a/lib/puppet/type/symlink.rb b/lib/puppet/type/symlink.rb
index cb05c3a88..b00962b0e 100755
--- a/lib/puppet/type/symlink.rb
+++ b/lib/puppet/type/symlink.rb
@@ -122,10 +122,10 @@ module Puppet
@stat = nil
@target = @parent.state(:ensure).should
- self.setparent()
+ self.setparent(@target)
end
- def setparent
+ def setparent(value)
# we want to remove our state, because we're creating children
# to do the links
if FileTest.exist?(@target)
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
index ea80f7f0b..d6a75a4e5 100755
--- a/lib/puppet/type/user.rb
+++ b/lib/puppet/type/user.rb
@@ -19,6 +19,27 @@ module Puppet
newstate(:ensure, @parentstate) do
newvalue(:present) do
+ # Verify that they have provided everything necessary, if we
+ # are trying to manage the user
+ if @parent.managed?
+ @parent.class.states.each { |state|
+ next if stateobj = @parent.state(state.name)
+ next if state.name == :ensure
+
+ unless state.autogen? or state.isoptional?
+ if state.method_defined?(:autogen)
+ @parent[state.name] = :auto
+ else
+ @parent.fail "Users require a value for %s" %
+ state.name
+ end
+ end
+ }
+
+ #if @states.empty?
+ # @parent[:comment] = @parent[:name]
+ #end
+ end
self.syncname(:present)
end
@@ -307,25 +328,8 @@ module Puppet
@userinfo = nil
super
- # Verify that they have provided everything necessary, if we
- # are trying to manage the user
- if self.managed?
- self.class.states.each { |state|
- next if @states.include?(state.name)
- next if state.name == :ensure
-
- unless state.autogen? or state.isoptional?
- if state.method_defined?(:autogen)
- self[state.name] = :auto
- else
- self.fail "Users require a value for %s" % state.name
- end
- end
- }
-
- if @states.empty?
- self[:comment] = self[:name]
- end
+ unless defined? @states
+ raise "wtf?"
end
end
diff --git a/test/types/file.rb b/test/types/file.rb
index a92b62a5c..7951a623c 100644
--- a/test/types/file.rb
+++ b/test/types/file.rb
@@ -929,6 +929,149 @@ class TestFile < Test::Unit::TestCase
assert_apply(file)
assert_equal("%o" % 0755, "%o" % (File.stat(path).mode & 007777))
end
+
+ # Make sure we can create symlinks
+ def test_symlinks
+ path = tempfile()
+ link = tempfile()
+
+ File.open(path, "w") { |f| f.puts "yay" }
+
+ file = nil
+ assert_nothing_raised {
+ file = Puppet.type(:file).create(
+ :ensure => path,
+ :path => link
+ )
+ }
+
+ assert_events([:link_created], file)
+
+ assert(FileTest.symlink?(link), "Link was not created")
+
+ assert_equal(path, File.readlink(link), "Link was created incorrectly")
+ end
+
+ def test_simplerecursivelinking
+ source = tempfile()
+ dest = tempfile()
+ subdir = File.join(source, "subdir")
+ file = File.join(subdir, "file")
+
+ system("mkdir -p %s" % subdir)
+ system("touch %s" % file)
+
+ link = nil
+ assert_nothing_raised {
+ link = Puppet.type(:file).create(
+ :ensure => source,
+ :path => dest,
+ :recurse => true
+ )
+ }
+
+ assert_apply(link)
+
+ subdest = File.join(dest, "subdir")
+ linkpath = File.join(subdest, "file")
+ assert(File.directory?(dest), "dest is not a dir")
+ assert(File.directory?(subdest), "subdest is not a dir")
+ assert(File.symlink?(linkpath), "path is not a link")
+ assert_equal(file, File.readlink(linkpath))
+ end
+
+ def test_recursivelinking
+ source = tempfile()
+ dest = tempfile()
+
+ files = []
+ dirs = []
+
+ # Make a bunch of files and dirs
+ Dir.mkdir(source)
+ Dir.chdir(source) do
+ system("mkdir -p %s" % "some/path/of/dirs")
+ system("mkdir -p %s" % "other/path/of/dirs")
+ system("touch %s" % "file")
+ system("touch %s" % "other/file")
+ system("touch %s" % "some/path/of/file")
+ system("touch %s" % "some/path/of/dirs/file")
+ system("touch %s" % "other/path/of/file")
+
+ files = %x{find . -type f}.chomp.split(/\n/)
+ dirs = %x{find . -type d}.chomp.split(/\n/).reject{|d| d =~ /^\.+$/ }
+ end
+
+ link = nil
+ assert_nothing_raised {
+ link = Puppet.type(:file).create(
+ :ensure => source,
+ :path => dest,
+ :recurse => true
+ )
+ }
+
+ assert_apply(link)
+
+ files.each do |f|
+ f.sub!(/^\.#{File::SEPARATOR}/, '')
+ path = File.join(dest, f)
+ assert(FileTest.exists?(path), "Link %s was not created" % path)
+ assert(FileTest.symlink?(path), "%s is not a link" % f)
+ target = File.readlink(path)
+ assert_equal(File.join(source, f), target)
+ end
+
+ dirs.each do |d|
+ d.sub!(/^\.#{File::SEPARATOR}/, '')
+ path = File.join(dest, d)
+ assert(FileTest.exists?(path), "Dir %s was not created" % path)
+ assert(FileTest.directory?(path), "%s is not a directory" % d)
+ end
+ end
+
+ def test_localrelativelinks
+ dir = tempfile()
+ Dir.mkdir(dir)
+ source = File.join(dir, "source")
+ File.open(source, "w") { |f| f.puts "yay" }
+ dest = File.join(dir, "link")
+
+ link = nil
+ assert_nothing_raised {
+ link = Puppet.type(:file).create(
+ :path => dest,
+ :ensure => "source"
+ )
+ }
+
+ assert_events([:link_created], link)
+ assert(FileTest.symlink?(dest), "Did not create link")
+ assert_equal("source", File.readlink(dest))
+ assert_equal("yay\n", File.read(dest))
+ end
+
+ def test_recursivelinkingmissingtarget
+ source = tempfile()
+ dest = tempfile()
+
+ objects = []
+ objects << Puppet.type(:exec).create(
+ :command => "mkdir %s; touch %s/file" % [source, source],
+ :path => ENV["PATH"]
+ )
+ objects << Puppet.type(:file).create(
+ :ensure => source,
+ :path => dest,
+ :recurse => true
+ )
+
+ assert_apply(*objects)
+
+ link = File.join(dest, "file")
+ assert(FileTest.symlink?(link), "Did not make link")
+ assert_equal(File.join(source, "file"), File.readlink(link))
+ end
end
# $Id$
diff --git a/test/types/parameter.rb b/test/types/parameter.rb
index d95ef461d..902f1398e 100644
--- a/test/types/parameter.rb
+++ b/test/types/parameter.rb
@@ -8,7 +8,7 @@ require 'puppet/type'
require 'puppettest'
require 'test/unit'
-class TestState < Test::Unit::TestCase
+class TestParameter < Test::Unit::TestCase
include TestPuppet
def newparam(name = :fakeparam)
diff --git a/test/types/state.rb b/test/types/state.rb
index 88df6575d..406b9af6b 100644
--- a/test/types/state.rb
+++ b/test/types/state.rb
@@ -63,12 +63,12 @@ class TestState < Test::Unit::TestCase
assert_equal(2, inst.is)
end
- def test_newvaluewithregexes
+ def test_newstatevaluewithregexes
state = newstate()
assert_nothing_raised {
- state.newvalue(/^\w+$/) do |value|
- @is = value.upcase
+ state.newvalue(/^\w+$/) do
+ @is = self.should.upcase
return :regex_matched
end
}
diff --git a/test/types/symlink.rb b/test/types/symlink.rb
index 21b1e865b..eb6b9a04d 100755
--- a/test/types/symlink.rb
+++ b/test/types/symlink.rb
@@ -77,6 +77,7 @@ class TestSymlink < Test::Unit::TestCase
cycle(comp)
path = link.name
+ assert(FileTest.directory?(path), "Did not make %s" % path)
list = file_list(path)
FileUtils.cd(path) {
list.each { |file|
@@ -89,7 +90,7 @@ class TestSymlink < Test::Unit::TestCase
}
end
- def test_createdrecursion
+ def disabled_test_createdrecursion
source = tempfile()
file = File.join(source, "file")
dest = tempfile()