puppet 0.9.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (182) hide show
  1. data/CHANGELOG +0 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +17 -0
  4. data/README +24 -0
  5. data/Rakefile +294 -0
  6. data/TODO +4 -0
  7. data/bin/cf2puppet +186 -0
  8. data/bin/puppet +176 -0
  9. data/bin/puppetca +213 -0
  10. data/bin/puppetd +246 -0
  11. data/bin/puppetdoc +184 -0
  12. data/bin/puppetmasterd +258 -0
  13. data/examples/code/allatonce +13 -0
  14. data/examples/code/assignments +11 -0
  15. data/examples/code/classing +35 -0
  16. data/examples/code/components +73 -0
  17. data/examples/code/execs +16 -0
  18. data/examples/code/failers/badclassnoparam +10 -0
  19. data/examples/code/failers/badclassparam +10 -0
  20. data/examples/code/failers/badcompnoparam +9 -0
  21. data/examples/code/failers/badcompparam +9 -0
  22. data/examples/code/failers/badtypeparam +3 -0
  23. data/examples/code/file.bl +11 -0
  24. data/examples/code/filedefaults +10 -0
  25. data/examples/code/fileparsing +116 -0
  26. data/examples/code/filerecursion +15 -0
  27. data/examples/code/functions +3 -0
  28. data/examples/code/groups +7 -0
  29. data/examples/code/head +30 -0
  30. data/examples/code/importing +8 -0
  31. data/examples/code/nodes +20 -0
  32. data/examples/code/one +8 -0
  33. data/examples/code/relationships +34 -0
  34. data/examples/code/selectors +28 -0
  35. data/examples/code/simpletests +11 -0
  36. data/examples/code/snippets/argumentdefaults +14 -0
  37. data/examples/code/snippets/casestatement +39 -0
  38. data/examples/code/snippets/classheirarchy.pp +15 -0
  39. data/examples/code/snippets/classincludes.pp +17 -0
  40. data/examples/code/snippets/classpathtest +11 -0
  41. data/examples/code/snippets/dirchmod +19 -0
  42. data/examples/code/snippets/failmissingexecpath.pp +13 -0
  43. data/examples/code/snippets/falsevalues.pp +3 -0
  44. data/examples/code/snippets/filecreate +11 -0
  45. data/examples/code/snippets/implicititeration +15 -0
  46. data/examples/code/snippets/multipleinstances +7 -0
  47. data/examples/code/snippets/namevartest +9 -0
  48. data/examples/code/snippets/scopetest +13 -0
  49. data/examples/code/snippets/selectorvalues.pp +22 -0
  50. data/examples/code/snippets/simpledefaults +5 -0
  51. data/examples/code/snippets/simpleselector +38 -0
  52. data/examples/code/svncommit +13 -0
  53. data/examples/root/bin/sleeper +69 -0
  54. data/examples/root/etc/configfile +0 -0
  55. data/examples/root/etc/debian-passwd +29 -0
  56. data/examples/root/etc/debian-syslog.conf +71 -0
  57. data/examples/root/etc/init.d/sleeper +65 -0
  58. data/examples/root/etc/otherfile +0 -0
  59. data/examples/root/etc/puppet/fileserver.conf +3 -0
  60. data/examples/root/etc/puppet/puppetmasterd.conf +10 -0
  61. data/ext/module:puppet +195 -0
  62. data/install.rb +270 -0
  63. data/lib/puppet.rb +249 -0
  64. data/lib/puppet/base64.rb +19 -0
  65. data/lib/puppet/client.rb +519 -0
  66. data/lib/puppet/config.rb +49 -0
  67. data/lib/puppet/daemon.rb +208 -0
  68. data/lib/puppet/element.rb +71 -0
  69. data/lib/puppet/event.rb +259 -0
  70. data/lib/puppet/log.rb +321 -0
  71. data/lib/puppet/metric.rb +250 -0
  72. data/lib/puppet/parsedfile.rb +38 -0
  73. data/lib/puppet/parser/ast.rb +1560 -0
  74. data/lib/puppet/parser/interpreter.rb +150 -0
  75. data/lib/puppet/parser/lexer.rb +226 -0
  76. data/lib/puppet/parser/parser.rb +1354 -0
  77. data/lib/puppet/parser/scope.rb +755 -0
  78. data/lib/puppet/server.rb +170 -0
  79. data/lib/puppet/server/authstore.rb +227 -0
  80. data/lib/puppet/server/ca.rb +140 -0
  81. data/lib/puppet/server/filebucket.rb +147 -0
  82. data/lib/puppet/server/fileserver.rb +477 -0
  83. data/lib/puppet/server/logger.rb +43 -0
  84. data/lib/puppet/server/master.rb +103 -0
  85. data/lib/puppet/server/servlet.rb +247 -0
  86. data/lib/puppet/sslcertificates.rb +737 -0
  87. data/lib/puppet/statechange.rb +150 -0
  88. data/lib/puppet/storage.rb +95 -0
  89. data/lib/puppet/transaction.rb +179 -0
  90. data/lib/puppet/transportable.rb +151 -0
  91. data/lib/puppet/type.rb +1354 -0
  92. data/lib/puppet/type/component.rb +141 -0
  93. data/lib/puppet/type/cron.rb +543 -0
  94. data/lib/puppet/type/exec.rb +316 -0
  95. data/lib/puppet/type/group.rb +152 -0
  96. data/lib/puppet/type/nameservice.rb +3 -0
  97. data/lib/puppet/type/nameservice/netinfo.rb +173 -0
  98. data/lib/puppet/type/nameservice/objectadd.rb +146 -0
  99. data/lib/puppet/type/nameservice/posix.rb +200 -0
  100. data/lib/puppet/type/package.rb +420 -0
  101. data/lib/puppet/type/package/apt.rb +70 -0
  102. data/lib/puppet/type/package/dpkg.rb +108 -0
  103. data/lib/puppet/type/package/rpm.rb +81 -0
  104. data/lib/puppet/type/package/sun.rb +117 -0
  105. data/lib/puppet/type/package/yum.rb +58 -0
  106. data/lib/puppet/type/pfile.rb +569 -0
  107. data/lib/puppet/type/pfile/checksum.rb +219 -0
  108. data/lib/puppet/type/pfile/create.rb +108 -0
  109. data/lib/puppet/type/pfile/group.rb +129 -0
  110. data/lib/puppet/type/pfile/mode.rb +131 -0
  111. data/lib/puppet/type/pfile/source.rb +264 -0
  112. data/lib/puppet/type/pfile/type.rb +31 -0
  113. data/lib/puppet/type/pfile/uid.rb +166 -0
  114. data/lib/puppet/type/pfilebucket.rb +80 -0
  115. data/lib/puppet/type/pprocess.rb +97 -0
  116. data/lib/puppet/type/service.rb +347 -0
  117. data/lib/puppet/type/service/base.rb +17 -0
  118. data/lib/puppet/type/service/debian.rb +50 -0
  119. data/lib/puppet/type/service/init.rb +145 -0
  120. data/lib/puppet/type/service/smf.rb +29 -0
  121. data/lib/puppet/type/state.rb +182 -0
  122. data/lib/puppet/type/symlink.rb +183 -0
  123. data/lib/puppet/type/tidy.rb +183 -0
  124. data/lib/puppet/type/typegen.rb +149 -0
  125. data/lib/puppet/type/typegen/filerecord.rb +243 -0
  126. data/lib/puppet/type/typegen/filetype.rb +316 -0
  127. data/lib/puppet/type/user.rb +290 -0
  128. data/lib/puppet/util.rb +138 -0
  129. data/test/certmgr/certmgr.rb +265 -0
  130. data/test/client/client.rb +203 -0
  131. data/test/executables/puppetbin.rb +53 -0
  132. data/test/executables/puppetca.rb +79 -0
  133. data/test/executables/puppetd.rb +71 -0
  134. data/test/executables/puppetmasterd.rb +153 -0
  135. data/test/executables/puppetmodule.rb +60 -0
  136. data/test/language/ast.rb +412 -0
  137. data/test/language/interpreter.rb +71 -0
  138. data/test/language/scope.rb +412 -0
  139. data/test/language/snippets.rb +445 -0
  140. data/test/other/events.rb +111 -0
  141. data/test/other/log.rb +195 -0
  142. data/test/other/metrics.rb +92 -0
  143. data/test/other/overrides.rb +115 -0
  144. data/test/other/parsedfile.rb +31 -0
  145. data/test/other/relationships.rb +113 -0
  146. data/test/other/state.rb +106 -0
  147. data/test/other/storage.rb +39 -0
  148. data/test/other/transactions.rb +235 -0
  149. data/test/parser/lexer.rb +120 -0
  150. data/test/parser/parser.rb +180 -0
  151. data/test/puppet/conffiles.rb +104 -0
  152. data/test/puppet/defaults.rb +100 -0
  153. data/test/puppet/error.rb +23 -0
  154. data/test/puppet/utiltest.rb +120 -0
  155. data/test/puppettest.rb +774 -0
  156. data/test/server/authstore.rb +209 -0
  157. data/test/server/bucket.rb +227 -0
  158. data/test/server/ca.rb +201 -0
  159. data/test/server/fileserver.rb +710 -0
  160. data/test/server/logger.rb +175 -0
  161. data/test/server/master.rb +150 -0
  162. data/test/server/server.rb +130 -0
  163. data/test/tagging/tagging.rb +80 -0
  164. data/test/test +51 -0
  165. data/test/types/basic.rb +119 -0
  166. data/test/types/component.rb +272 -0
  167. data/test/types/cron.rb +261 -0
  168. data/test/types/exec.rb +273 -0
  169. data/test/types/file.rb +616 -0
  170. data/test/types/filebucket.rb +167 -0
  171. data/test/types/fileignoresource.rb +287 -0
  172. data/test/types/filesources.rb +587 -0
  173. data/test/types/filetype.rb +162 -0
  174. data/test/types/group.rb +271 -0
  175. data/test/types/package.rb +205 -0
  176. data/test/types/query.rb +101 -0
  177. data/test/types/service.rb +100 -0
  178. data/test/types/symlink.rb +93 -0
  179. data/test/types/tidy.rb +124 -0
  180. data/test/types/type.rb +135 -0
  181. data/test/types/user.rb +371 -0
  182. metadata +243 -0
@@ -0,0 +1,108 @@
1
+ module Puppet
2
+ module PackagingType
3
+ # The packaging system for Debian systems.
4
+ module DPKG
5
+ def query
6
+ packages = []
7
+
8
+ # dpkg only prints as many columns as you have available
9
+ # which means we don't get all of the info
10
+ # stupid stupid
11
+ oldcol = ENV["COLUMNS"]
12
+ ENV["COLUMNS"] = "500"
13
+ fields = [:desired, :status, :error, :name, :version, :description]
14
+
15
+ hash = {}
16
+ # list out our specific package
17
+ open("| dpkg -l %s 2>/dev/null" % self.name) { |process|
18
+ # our regex for matching dpkg output
19
+ regex = %r{^(.)(.)(.)\s(\S+)\s+(\S+)\s+(.+)$}
20
+
21
+ # we only want the last line
22
+ lines = process.readlines
23
+ # we've got four header lines, so we should expect all of those
24
+ # plus our output
25
+ if lines.length < 5
26
+ return nil
27
+ end
28
+
29
+ line = lines[-1]
30
+
31
+ if match = regex.match(line)
32
+ fields.zip(match.captures) { |field,value|
33
+ hash[field] = value
34
+ }
35
+ #packages.push Puppet::Type::Package.installedpkg(hash)
36
+ else
37
+ raise Puppet::DevError,
38
+ "failed to match dpkg line %s" % line
39
+ end
40
+ }
41
+ ENV["COLUMNS"] = oldcol
42
+
43
+ if hash[:error] != " "
44
+ raise Puppet::PackageError.new(
45
+ "Package %s, version %s is in error state: %s" %
46
+ [hash[:name], hash[:install], hash[:error]]
47
+ )
48
+ end
49
+
50
+ if hash[:status] == "i"
51
+ hash[:install] = hash[:version]
52
+ else
53
+ hash[:install] = :notinstalled
54
+ end
55
+
56
+ return hash
57
+ end
58
+
59
+ def list
60
+ packages = []
61
+
62
+ # dpkg only prints as many columns as you have available
63
+ # which means we don't get all of the info
64
+ # stupid stupid
65
+ oldcol = ENV["COLUMNS"]
66
+ ENV["COLUMNS"] = "500"
67
+
68
+ # list out all of the packages
69
+ open("| dpkg -l") { |process|
70
+ # our regex for matching dpkg output
71
+ regex = %r{^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$}
72
+ fields = [:status, :name, :install, :description]
73
+ hash = {}
74
+
75
+ 5.times { process.gets } # throw away the header
76
+
77
+ # now turn each returned line into a package object
78
+ process.each { |line|
79
+ if match = regex.match(line)
80
+ hash.clear
81
+
82
+ fields.zip(match.captures) { |field,value|
83
+ hash[field] = value
84
+ }
85
+ packages.push Puppet::Type::Package.installedpkg(hash)
86
+ else
87
+ raise Puppet::DevError,
88
+ "Failed to match dpkg line %s" % line
89
+ end
90
+ }
91
+ }
92
+ ENV["COLUMNS"] = oldcol
93
+
94
+ return packages
95
+ end
96
+
97
+ def remove
98
+ cmd = "dpkg -r %s" % self.name
99
+ output = %x{#{cmd} 2>&1}
100
+ if $? != 0
101
+ raise Puppet::PackageError.new(output)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ # $Id: dpkg.rb 707 2005-09-27 19:59:36Z luke $
@@ -0,0 +1,81 @@
1
+ module Puppet
2
+ module PackagingType
3
+ module RPM
4
+ def query
5
+ fields = {
6
+ :name => "NAME",
7
+ :install => "VERSION",
8
+ :description => "DESCRIPTION"
9
+ }
10
+
11
+ cmd = "rpm -q #{self.name} --qf '%s\n'" %
12
+ "%{NAME} %{VERSION}-%{RELEASE}"
13
+
14
+ # list out all of the packages
15
+ output = %x{#{cmd} 2>/dev/null}.chomp
16
+
17
+ if $? != 0
18
+ return nil
19
+ end
20
+
21
+ regex = %r{^(\S+)\s+(\S+)}
22
+ #fields = [:name, :install, :description]
23
+ fields = [:name, :install]
24
+ hash = {}
25
+ if match = regex.match(output)
26
+ fields.zip(match.captures) { |field,value|
27
+ hash[field] = value
28
+ }
29
+ else
30
+ raise Puppet::DevError,
31
+ "Failed to match rpm output '%s'" %
32
+ output
33
+ end
34
+
35
+ return hash
36
+ end
37
+
38
+ def list
39
+ packages = []
40
+
41
+ # list out all of the packages
42
+ open("| rpm -q -a --qf '%{NAME} %{VERSION}\n'") { |process|
43
+ # our regex for matching dpkg output
44
+ regex = %r{^(\S+)\s+(\S+)}
45
+ fields = [:name, :install]
46
+ hash = {}
47
+
48
+ # now turn each returned line into a package object
49
+ process.each { |line|
50
+ if match = regex.match(line)
51
+ hash.clear
52
+
53
+ fields.zip(match.captures) { |field,value|
54
+ hash[field] = value
55
+ }
56
+ packages.push Puppet::Type::Package.installedpkg(hash)
57
+ else
58
+ raise "failed to match rpm line %s" % line
59
+ end
60
+ }
61
+ }
62
+
63
+ return packages
64
+ end
65
+
66
+ # we need package retrieval mechanisms before we can have package
67
+ # installation mechanisms...
68
+ #type.install = proc { |pkg|
69
+ # raise "installation not implemented yet"
70
+ #}
71
+
72
+ def remove
73
+ cmd = "rpm -e %s" % self.name
74
+ output = %x{#{cmd}}
75
+ if $? != 0
76
+ raise output
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,117 @@
1
+ module Puppet
2
+ module PackagingType
3
+ module Sun
4
+ def query
5
+ names = {
6
+ "PKGINST" => :name,
7
+ "NAME" => nil,
8
+ "CATEGORY" => :category,
9
+ "ARCH" => :platform,
10
+ "VERSION" => :install,
11
+ "BASEDIR" => :root,
12
+ "HOTLINE" => nil,
13
+ "EMAIL" => nil,
14
+ "VENDOR" => :vendor,
15
+ "DESC" => :description,
16
+ "PSTAMP" => nil,
17
+ "INSTDATE" => nil,
18
+ "STATUS" => nil,
19
+ "FILES" => nil
20
+ }
21
+
22
+ hash = {}
23
+
24
+ # list out all of the packages
25
+ open("| pkginfo -l %s 2>/dev/null" % self.name) { |process|
26
+ # we're using the long listing, so each line is a separate
27
+ # piece of information
28
+ process.each { |line|
29
+ case line
30
+ when /^$/: # ignore
31
+ when /\s*([A-Z]+):\s+(.+)/:
32
+ name = $1
33
+ value = $2
34
+ if names.include?(name)
35
+ unless names[name].nil?
36
+ hash[names[name]] = value
37
+ end
38
+ else
39
+ self.err "'pkginfo' returned invalid name %s" %
40
+ name
41
+ end
42
+ when /\s+\d+.+/:
43
+ # nothing; we're ignoring the FILES info
44
+ end
45
+ }
46
+ }
47
+
48
+ if hash.empty?
49
+ return nil
50
+ else
51
+ return hash
52
+ end
53
+ end
54
+
55
+ def list
56
+ packages = []
57
+ hash = {}
58
+ names = {
59
+ "PKGINST" => :name,
60
+ "NAME" => nil,
61
+ "CATEGORY" => :category,
62
+ "ARCH" => :platform,
63
+ "VERSION" => :install,
64
+ "BASEDIR" => :root,
65
+ "HOTLINE" => nil,
66
+ "EMAIL" => nil,
67
+ "VENDOR" => :vendor,
68
+ "DESC" => :description,
69
+ "PSTAMP" => nil,
70
+ "INSTDATE" => nil,
71
+ "STATUS" => nil,
72
+ "FILES" => nil
73
+ }
74
+
75
+ # list out all of the packages
76
+ open("| pkginfo -l") { |process|
77
+ # we're using the long listing, so each line is a separate
78
+ # piece of information
79
+ process.each { |line|
80
+ case line
81
+ when /^$/:
82
+ packages.push Puppet::Type::Package.installedpkg(hash)
83
+ hash.clear
84
+ when /\s*(\w+):\s+(.+)/:
85
+ name = $1
86
+ value = $2
87
+ if names.include?(name)
88
+ unless names[name].nil?
89
+ hash[names[name]] = value
90
+ end
91
+ else
92
+ raise "Could not find %s" % name
93
+ end
94
+ when /\s+\d+.+/:
95
+ # nothing; we're ignoring the FILES info
96
+ end
97
+ }
98
+ }
99
+ return packages
100
+ end
101
+
102
+ # we need package retrieval mechanisms before we can have package
103
+ # installation mechanisms...
104
+ #type.install = proc { |pkg|
105
+ # raise "installation not implemented yet"
106
+ #}
107
+
108
+ def remove
109
+ cmd = "pkgrm -n %s" % self.name
110
+ output = %x{#{cmd}}
111
+ if $? != 0
112
+ raise output
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,58 @@
1
+ module Puppet
2
+ module PackagingType
3
+ # A derivative of DPKG; this is how most people actually manage
4
+ # Debian boxes, and the only thing that differs is that it can
5
+ # install packages from remote sites.
6
+ module Yum
7
+ include RPM
8
+
9
+ # Install a package using 'apt-get'.
10
+ def install
11
+ cmd = "yum -y install %s" % self.name
12
+
13
+ self.info "Executing %s" % cmd.inspect
14
+ output = %x{#{cmd} 2>&1}
15
+
16
+ unless $? == 0
17
+ raise Puppet::PackageError.new(output)
18
+ end
19
+ end
20
+
21
+ # What's the latest package version available?
22
+ def latest
23
+ cmd = "yum list %s" % self.name
24
+ output = %x{#{cmd} 2>&1}
25
+
26
+ unless $? == 0
27
+ raise Puppet::PackageError.new(output)
28
+ end
29
+
30
+ if output =~ /#{self.name}\S+\s+(\S+)\s/
31
+ return $1
32
+ else
33
+ self.debug "No version"
34
+ if Puppet[:debug]
35
+ print output
36
+ end
37
+
38
+ return nil
39
+ end
40
+ end
41
+
42
+ def update
43
+ cmd = "yum -y update %s" % self.name
44
+
45
+ self.info "Executing %s" % cmd.inspect
46
+ output = %x{#{cmd} 2>&1}
47
+
48
+ unless $? == 0
49
+ raise Puppet::PackageError.new(output)
50
+ end
51
+ end
52
+
53
+ def versionable?
54
+ false
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,569 @@
1
+ require 'digest/md5'
2
+ require 'cgi'
3
+ require 'etc'
4
+ require 'uri'
5
+ require 'fileutils'
6
+ require 'puppet/type/state'
7
+ require 'puppet/server/fileserver'
8
+
9
+ # We put all of the states in separate files, because there are so many
10
+ # of them.
11
+ require 'puppet/type/pfile/type'
12
+ require 'puppet/type/pfile/create'
13
+ require 'puppet/type/pfile/checksum'
14
+ require 'puppet/type/pfile/uid'
15
+ require 'puppet/type/pfile/mode'
16
+ require 'puppet/type/pfile/group'
17
+ require 'puppet/type/pfile/source'
18
+
19
+ module Puppet
20
+ class Type
21
+ class PFile < Type
22
+ @doc = "Manages local files, including setting ownership and
23
+ permissions, and allowing creation of both files and directories."
24
+
25
+ @states = [
26
+ Puppet::State::PFileCreate,
27
+ Puppet::State::PFileChecksum,
28
+ Puppet::State::PFileSource,
29
+ Puppet::State::PFileUID,
30
+ Puppet::State::PFileGroup,
31
+ Puppet::State::PFileMode,
32
+ Puppet::State::PFileType
33
+ ]
34
+
35
+ @parameters = [
36
+ :path,
37
+ :backup,
38
+ :linkmaker,
39
+ :recurse,
40
+ :ignore
41
+ ]
42
+
43
+ @paramdoc[:path] = "The path to the file to manage. Must be fully
44
+ qualified."
45
+
46
+ @paramdoc[:backup] = "Whether files should be backed up before
47
+ being replaced. If a ``filebucket`` is specified, files will be
48
+ backed up there; else, they will be backed up in the same directory
49
+ with a ``.puppet-bak`` extension."
50
+
51
+ @paramdoc[:linkmaker] = "An internal parameter used by the *symlink*
52
+ type to do recursive link creation."
53
+
54
+ @paramdoc[:recurse] = "Whether and how deeply to do recursive
55
+ management. **false**/*true*/*inf*/*number*"
56
+
57
+ @paramdoc[:ignore] = "A parameter which omits action on files matching
58
+ specified patterns during recursion. Uses Ruby's builtin globbing
59
+ engine, so shell metacharacters are fully supported, e.g. ``[a-z]*``.
60
+ Matches that would descend into the directory structure are ignored,
61
+ e.g., ``*/*``."
62
+
63
+ #no longer a parameter
64
+ # @paramdoc[:source] = "Where to retrieve the contents of the files.
65
+ # Currently only supports local copying, but will eventually
66
+ # support multiple protocols for copying. Arguments are specified
67
+ # using either a full local path or using a URI (currently only
68
+ # *file* is supported as a protocol)."
69
+
70
+
71
+
72
+ @name = :file
73
+ @namevar = :path
74
+
75
+ @depthfirst = false
76
+
77
+ PINPARAMS = [:mode, :type, :owner, :group, :checksum]
78
+
79
+
80
+ def argument?(arg)
81
+ @arghash.include?(arg)
82
+ end
83
+
84
+ def handlebackup(file = nil)
85
+ # let the path be specified
86
+ file ||= self[:path]
87
+ # if they specifically don't want a backup, then just say
88
+ # we're good
89
+ unless FileTest.exists?(file)
90
+ return true
91
+ end
92
+
93
+ unless self[:backup]
94
+ return true
95
+ end
96
+
97
+ case File.stat(file).ftype
98
+ when "directory":
99
+ # we don't need to backup directories
100
+ return true
101
+ when "file":
102
+ backup = self[:backup]
103
+ case backup
104
+ when Puppet::Client::Dipper:
105
+ sum = backup.backup(file)
106
+ self.info "Filebucketed %s with sum %s" %
107
+ [file, sum]
108
+ return true
109
+ when String:
110
+ newfile = file + backup
111
+ if FileTest.exists?(newfile)
112
+ begin
113
+ File.unlink(newfile)
114
+ rescue => detail
115
+ self.err "Could not remove old backup: %s" %
116
+ detail
117
+ return false
118
+ end
119
+ end
120
+ begin
121
+ FileUtils.cp(file,
122
+ file + backup)
123
+ return true
124
+ rescue => detail
125
+ # since they said they want a backup, let's error out
126
+ # if we couldn't make one
127
+ raise Puppet::Error.new("Could not back %s up: %s" %
128
+ [file, detail.message])
129
+ end
130
+ else
131
+ self.err "Invalid backup type %s" % backup
132
+ return false
133
+ end
134
+ else
135
+ self.notice "Cannot backup files of type %s" %
136
+ File.stat(file).ftype
137
+ return false
138
+ end
139
+ end
140
+
141
+ def handleignore(children)
142
+ @parameters[:ignore].each { |ignore|
143
+ ignored = []
144
+ Dir.glob(File.join(self.name,ignore), File::FNM_DOTMATCH) { |match|
145
+ ignored.push(File.basename(match))
146
+ }
147
+ children = children - ignored
148
+ }
149
+ return children
150
+ end
151
+
152
+ def initialize(hash)
153
+ # clean out as many references to any file paths as possible
154
+ # this was the source of many, many bugs
155
+
156
+ @arghash = self.argclean(hash)
157
+ @arghash.delete(self.class.namevar)
158
+
159
+ if @arghash.include?(:source)
160
+ @arghash.delete(:source)
161
+ end
162
+
163
+ @stat = nil
164
+ @parameters = Hash.new(false)
165
+
166
+ # default to true
167
+ self[:backup] = true
168
+
169
+ # Used for caching clients
170
+ @clients = {}
171
+
172
+ super
173
+ end
174
+
175
+ def parambackup=(value)
176
+ case value
177
+ when false, "false":
178
+ @parameters[:backup] = false
179
+ when true, "true":
180
+ @parameters[:backup] = ".puppet-bak"
181
+ when Array:
182
+ case value[0]
183
+ when "filebucket":
184
+ if bucket = Puppet::Type::PFileBucket.bucket(value[1])
185
+ @parameters[:backup] = bucket
186
+ else
187
+ @parameters[:backup] = ".puppet-bak"
188
+ raise Puppet::Error,
189
+ "Could not retrieve filebucket %s" %
190
+ value[1]
191
+ end
192
+ else
193
+ raise Puppet::Error, "Invalid backup object type %s" %
194
+ value[0].inspect
195
+ end
196
+ else
197
+ raise Puppet::Error, "Invalid backup type %s" %
198
+ value.inspect
199
+ end
200
+ end
201
+
202
+ def paramignore=(value)
203
+
204
+ #Make sure the value of ignore is in correct type
205
+ unless value.is_a?(Array) or value.is_a?(String)
206
+ raise Puppet::DevError.new("Ignore must be a string or an Array")
207
+ end
208
+
209
+ @parameters[:ignore] = value
210
+ end
211
+
212
+ def newchild(path, hash = {})
213
+ # make local copy of arguments
214
+ args = @arghash.dup
215
+
216
+ if path =~ %r{^#{File::SEPARATOR}}
217
+ raise Puppet::DevError.new(
218
+ "Must pass relative paths to PFile#newchild()"
219
+ )
220
+ else
221
+ path = File.join(self.name, path)
222
+ end
223
+
224
+ args[:path] = path
225
+
226
+ unless hash.include?(:recurse)
227
+ if args.include?(:recurse)
228
+ if args[:recurse].is_a?(Integer)
229
+ self.notice "Decrementing recurse on %s" % path
230
+ args[:recurse] -= 1 # reduce the level of recursion
231
+ end
232
+ end
233
+
234
+ end
235
+
236
+ hash.each { |key,value|
237
+ args[key] = value
238
+ }
239
+
240
+ child = nil
241
+ klass = nil
242
+ if @parameters[:linkmaker] and args.include?(:source) and
243
+ ! FileTest.directory?(args[:source])
244
+ klass = Puppet::Type::Symlink
245
+
246
+ self.debug "%s is a link" % path
247
+ # clean up the args a lot for links
248
+ old = args.dup
249
+ args = {
250
+ :target => old[:source],
251
+ :path => path
252
+ }
253
+ else
254
+ klass = self.class
255
+ end
256
+
257
+ # The child might already exist because 'localrecurse' runs
258
+ # before 'sourcerecurse'. I could push the override stuff into
259
+ # a separate method or something, but the work is the same other
260
+ # than this last bit, so it doesn't really make sense.
261
+ if child = klass[path]
262
+ unless @children.include?(child)
263
+ self.notice "Not managing more explicit file %s" %
264
+ path
265
+ return nil
266
+ end
267
+ args.each { |var,value|
268
+ next if var == :path
269
+ next if var == :name
270
+ # behave idempotently
271
+ unless child.should(var) == value
272
+ child[var] = value
273
+ end
274
+ }
275
+ else # create it anew
276
+ #notice "Creating new file with args %s" % args.inspect
277
+ args[:parent] = self
278
+ begin
279
+ child = klass.implicitcreate(args)
280
+
281
+ # implicit creation can return nil
282
+ if child.nil?
283
+ return nil
284
+ end
285
+ @children << child
286
+ rescue Puppet::Error => detail
287
+ self.notice(
288
+ "Cannot manage: %s" %
289
+ [detail.message]
290
+ )
291
+ self.debug args.inspect
292
+ child = nil
293
+ rescue => detail
294
+ self.notice(
295
+ "Cannot manage: %s" %
296
+ [detail]
297
+ )
298
+ self.debug args.inspect
299
+ child = nil
300
+ end
301
+ end
302
+ return child
303
+ end
304
+
305
+ # Paths are special for files, because we don't actually want to show
306
+ # the parent's full path.
307
+ def path
308
+ unless defined? @path
309
+ if defined? @parent
310
+ # We only need to behave specially when our parent is also
311
+ # a file
312
+ if @parent.is_a?(self.class)
313
+ # Remove the parent file name
314
+ ppath = @parent.path.sub(/\/?file=.+/, '')
315
+ @path = []
316
+ if ppath != "/" and ppath != ""
317
+ @path << ppath
318
+ end
319
+ @path << self.class.name.to_s + "=" + self.name
320
+ else
321
+ super
322
+ end
323
+ else
324
+ # The top-level name is always puppet[top], so we don't
325
+ # bother with that. And we don't add the hostname
326
+ # here, it gets added in the log server thingy.
327
+ if self.name == "puppet[top]"
328
+ @path = ["/"]
329
+ else
330
+ # We assume that if we don't have a parent that we
331
+ # should not cache the path
332
+ @path = [self.class.name.to_s + "=" + self.name]
333
+ end
334
+ end
335
+ end
336
+
337
+ return @path.join("/")
338
+ end
339
+
340
+ # Recurse into the directory. This basically just calls 'localrecurse'
341
+ # and maybe 'sourcerecurse'.
342
+ def recurse
343
+ recurse = @parameters[:recurse]
344
+ # we might have a string, rather than a number
345
+ if recurse.is_a?(String)
346
+ if recurse =~ /^[0-9]+$/
347
+ recurse = Integer(recurse)
348
+ #elsif recurse =~ /^inf/ # infinite recursion
349
+ else # anything else is infinite recursion
350
+ recurse = true
351
+ end
352
+ end
353
+
354
+ # are we at the end of the recursion?
355
+ if recurse == 0
356
+ self.info "finished recursing"
357
+ return
358
+ end
359
+
360
+ if recurse.is_a?(Integer)
361
+ recurse -= 1
362
+ end
363
+
364
+ self.localrecurse(recurse)
365
+ if @states.include?(:source)
366
+ self.sourcerecurse(recurse)
367
+ end
368
+ end
369
+
370
+ def localrecurse(recurse)
371
+ unless FileTest.exist?(self.name) and self.stat.directory?
372
+ #self.info "%s is not a directory; not recursing" %
373
+ # self.name
374
+ return
375
+ end
376
+
377
+ unless FileTest.directory? self.name
378
+ raise Puppet::Error.new(
379
+ "Uh, somehow trying to manage non-dir %s" % self.name
380
+ )
381
+ end
382
+ unless FileTest.readable? self.name
383
+ self.notice "Cannot manage %s: permission denied" % self.name
384
+ return
385
+ end
386
+
387
+ children = Dir.entries(self.name)
388
+
389
+ #Get rid of ignored children
390
+ if @parameters.include?(:ignore)
391
+ children = handleignore(children)
392
+ end
393
+
394
+ added = []
395
+ children.each { |file|
396
+ file = File.basename(file)
397
+ next if file =~ /^\.\.?$/ # skip . and ..
398
+ if child = self.newchild(file, :recurse => recurse)
399
+ unless @children.include?(child)
400
+ self.push child
401
+ added.push file
402
+
403
+ end
404
+ child.retrieve
405
+ end
406
+ }
407
+ end
408
+
409
+ # This recurses against the remote source and makes sure the local
410
+ # and remote structures match. It's run after 'localrecurse'.
411
+ def sourcerecurse(recurse)
412
+ # FIXME sourcerecurse should support purging non-remote files
413
+ source = @states[:source].source
414
+
415
+ sourceobj, path = uri2obj(source)
416
+
417
+ # we'll set this manually as necessary
418
+ if @arghash.include?(:create)
419
+ @arghash.delete(:create)
420
+ end
421
+
422
+ # okay, we've got our source object; now we need to
423
+ # build up a local file structure to match the remote
424
+ # one
425
+
426
+ server = sourceobj.server
427
+ sum = "md5"
428
+ if state = self.state(:checksum)
429
+ sum = state.checktype
430
+ end
431
+ r = false
432
+ if recurse
433
+ unless recurse == 0
434
+ r = 1
435
+ end
436
+ end
437
+
438
+ ignore = @parameters[:ignore]
439
+
440
+ #self.warning "Listing path %s" % path.inspect
441
+ desc = server.list(path, r, ignore)
442
+
443
+ desc.split("\n").each { |line|
444
+ file, type = line.split("\t")
445
+ next if file == "/"
446
+ name = file.sub(/^\//, '')
447
+ #self.warning "child name is %s" % name
448
+ args = {:source => source + file}
449
+ if type == file
450
+ args[:recurse] = nil
451
+ end
452
+ self.newchild(name, args)
453
+ #self.newchild(hash, source, recurse)
454
+ #hash2child(hash, source, recurse)
455
+ }
456
+ end
457
+
458
+ # a wrapper method to make sure the file exists before doing anything
459
+ def retrieve
460
+ if @states.include?(:source)
461
+ # This probably isn't the best place for it, but we need
462
+ # to make sure that we have a corresponding checksum state.
463
+ unless @states.include?(:checksum)
464
+ self[:checksum] = "md5"
465
+ end
466
+ @states[:source].retrieve
467
+ end
468
+
469
+ if @parameters.include?(:recurse)
470
+ self.recurse
471
+ end
472
+
473
+ unless stat = self.stat(true)
474
+ self.debug "File does not exist"
475
+ @states.each { |name,state|
476
+ # We've already retreived the source, and we don't
477
+ # want to overwrite whatever it did. This is a bit
478
+ # of a hack, but oh well, source is definitely special.
479
+ next if name == :source
480
+ state.is = :notfound
481
+ }
482
+ return
483
+ end
484
+
485
+ super
486
+ end
487
+
488
+ def stat(refresh = false)
489
+ if @stat.nil? or refresh == true
490
+ begin
491
+ @stat = File.lstat(self.name)
492
+ rescue Errno::ENOENT => error
493
+ @stat = nil
494
+ rescue => error
495
+ self.debug "Failed to stat %s: %s" %
496
+ [self.name,error]
497
+ @stat = nil
498
+ end
499
+ end
500
+
501
+ return @stat
502
+ end
503
+
504
+ def uri2obj(source)
505
+ sourceobj = FileSource.new
506
+ path = nil
507
+ if source =~ /^\//
508
+ source = "file://localhost/%s" % source
509
+ sourceobj.mount = "localhost"
510
+ sourceobj.local = true
511
+ end
512
+ begin
513
+ uri = URI.parse(source)
514
+ rescue => detail
515
+ raise Puppet::Error, "Could not understand source %s: %s" %
516
+ [source, detail.to_s]
517
+ end
518
+
519
+ case uri.scheme
520
+ when "file":
521
+ unless defined? @@localfileserver
522
+ @@localfileserver = Puppet::Server::FileServer.new(
523
+ :Local => true,
524
+ :Mount => { "/" => "localhost" },
525
+ :Config => false
526
+ )
527
+ #@@localfileserver.mount("/", "localhost")
528
+ end
529
+ sourceobj.server = @@localfileserver
530
+ path = "/localhost" + uri.path
531
+ when "puppet":
532
+ args = { :Server => uri.host }
533
+ if uri.port
534
+ args[:Port] = uri.port
535
+ end
536
+ # FIXME We should cache a copy of this server
537
+ #sourceobj.server = Puppet::NetworkClient.new(args)
538
+ unless @clients.include?(source)
539
+ @clients[source] = Puppet::Client::FileClient.new(args)
540
+ end
541
+ sourceobj.server = @clients[source]
542
+
543
+ tmp = uri.path
544
+ if tmp =~ %r{^/(\w+)}
545
+ sourceobj.mount = $1
546
+ path = tmp
547
+ #path = tmp.sub(%r{^/\w+},'') || "/"
548
+ else
549
+ raise Puppet::Error, "Invalid source path %s" % tmp
550
+ end
551
+ else
552
+ raise Puppet::Error,
553
+ "Got other recursive file proto %s from %s" %
554
+ [uri.scheme, source]
555
+ end
556
+
557
+ return [sourceobj, path.sub(/\/\//, '/')]
558
+ end
559
+ end # Puppet::Type::PFile
560
+ end # Puppet::Type
561
+
562
+ # the filesource class can't include the path, because the path
563
+ # changes for every file instance
564
+ class FileSource
565
+ attr_accessor :mount, :root, :server, :local
566
+ end
567
+ end
568
+
569
+ # $Id$