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,141 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # $Id: component.rb 740 2005-11-01 20:22:19Z luke $
4
+
5
+ # the object allowing us to build complex structures
6
+ # this thing contains everything else, including itself
7
+
8
+ require 'puppet'
9
+ require 'puppet/type'
10
+ require 'puppet/transaction'
11
+
12
+ module Puppet
13
+ class Type
14
+ class Component < Puppet::Type
15
+ include Enumerable
16
+
17
+ @name = :component
18
+ @namevar = :name
19
+
20
+ @states = []
21
+ @parameters = [:name,:type]
22
+
23
+ # topo sort functions
24
+ def self.sort(objects)
25
+ list = []
26
+ tmplist = {}
27
+
28
+ objects.each { |obj|
29
+ self.recurse(obj, tmplist, list)
30
+ }
31
+
32
+ return list.flatten
33
+ end
34
+
35
+ # FIXME this method assumes that dependencies themselves
36
+ # are never components
37
+ def self.recurse(obj, inlist, list)
38
+ if inlist.include?(obj.object_id)
39
+ return
40
+ end
41
+ inlist[obj.object_id] = true
42
+ obj.eachdependency { |req|
43
+ self.recurse(req, inlist, list)
44
+ }
45
+
46
+ if obj.is_a?(Puppet::Type::Component)
47
+ obj.each { |child|
48
+ self.recurse(child, inlist, list)
49
+ }
50
+ else
51
+ list << obj
52
+ end
53
+ end
54
+
55
+ # Remove a child from the component.
56
+ def delete(child)
57
+ if @children.include?(child)
58
+ @children.delete(child)
59
+ return true
60
+ else
61
+ return false
62
+ end
63
+ end
64
+
65
+ # Return each child in turn.
66
+ def each
67
+ @children.each { |child| yield child }
68
+ end
69
+
70
+ # Return a flattened array containing all of the children
71
+ # and all child components' children, sorted in order of dependencies.
72
+ def flatten
73
+ self.class.sort(@children).flatten
74
+ end
75
+
76
+ # Initialize a new component
77
+ def initialize(args)
78
+ @children = []
79
+
80
+ # it makes sense to have a more reasonable default here than 'false'
81
+ unless args.include?(:type) or args.include?("type")
82
+ args[:type] = "component"
83
+ end
84
+ super(args)
85
+ end
86
+
87
+ # flatten all children, sort them, and evaluate them in order
88
+ # this is only called on one component over the whole system
89
+ # this also won't work with scheduling, but eh
90
+ def evaluate
91
+ transaction = Puppet::Transaction.new(self.flatten)
92
+ transaction.component = self
93
+ return transaction
94
+ end
95
+
96
+ def name
97
+ #return self[:name]
98
+ unless defined? @name
99
+ if self[:type] == self[:name]
100
+ @name = self[:type]
101
+ else
102
+ @name = "%s[%s]" % [self[:type],self[:name]]
103
+ end
104
+ end
105
+ return @name
106
+ end
107
+
108
+ def push(*ary)
109
+ ary.each { |child|
110
+ unless child.is_a?(Puppet::Element)
111
+ self.debug "Got object of type %s" % child.class
112
+ raise Puppet::DevError.new(
113
+ "Containers can only contain Puppet::Elements, not %s" %
114
+ child.class
115
+ )
116
+ end
117
+ @children.push child
118
+ child.parent = self
119
+ }
120
+ end
121
+
122
+ def refresh
123
+ @children.collect { |child|
124
+ if child.respond_to?(:refresh)
125
+ child.refresh
126
+ end
127
+ }
128
+ end
129
+
130
+ #def retrieve
131
+ # self.collect { |child|
132
+ # child.retrieve
133
+ # }
134
+ #end
135
+
136
+ def to_s
137
+ return "component(%s)" % self.name
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,543 @@
1
+ # A Puppet::Type class to manage cron jobs. Some abstraction is done,
2
+ # so that different platforms' versions of +crontab+ all work equivalently.
3
+
4
+ require 'etc'
5
+ require 'facter'
6
+ require 'puppet/type/state'
7
+
8
+ module Puppet
9
+ # The Puppet::CronType modules are responsible for the actual abstraction.
10
+ # They must implement three module functions: +read+, +write+, and +remove+,
11
+ # analogous to the three flags accepted by most implementations of +crontab+.
12
+ # All of these methods require the user name to be passed in.
13
+ #
14
+ # These modules operate on the strings that are ore become the cron tabs --
15
+ # they do not have any semantic understanding of what they are reading or
16
+ # writing.
17
+ module CronType
18
+ # Retrieve the uid of a user. This is duplication of code, but the unless
19
+ # I start using Puppet::Type::User objects here, it's a much better design.
20
+ def self.uid(user)
21
+ begin
22
+ return Etc.getpwnam(user).uid
23
+ rescue ArgumentError
24
+ raise Puppet::Error, "User %s not found" % user
25
+ end
26
+ end
27
+
28
+ # This module covers nearly everyone; SunOS is only known exception so far.
29
+ module Default
30
+ # Only add the -u flag when the user is different. Fedora apparently
31
+ # does not think I should be allowed to set the user to myself.
32
+ def self.cmdbase(user)
33
+ uid = CronType.uid(user)
34
+ cmd = nil
35
+ if uid == Process.uid
36
+ return "crontab"
37
+ else
38
+ return "crontab -u #{user}"
39
+ end
40
+ end
41
+
42
+ # Read a specific user's cron tab.
43
+ def self.read(user)
44
+ tab = %x{#{self.cmdbase(user)} -l 2>/dev/null}
45
+ end
46
+
47
+ # Remove a specific user's cron tab.
48
+ def self.remove(user)
49
+ %x{#{self.cmdbase(user)} -r 2>/dev/null}
50
+ end
51
+
52
+ # Overwrite a specific user's cron tab; must be passed the user name
53
+ # and the text with which to create the cron tab.
54
+ def self.write(user, text)
55
+ IO.popen("#{self.cmdbase(user)} -", "w") { |p|
56
+ p.print text
57
+ }
58
+ end
59
+ end
60
+
61
+ # SunOS has completely different cron commands; this module implements
62
+ # its versions.
63
+ module SunOS
64
+ # Read a specific user's cron tab.
65
+ def self.read(user)
66
+ Puppet::Util.asuser(user) {
67
+ %x{crontab -l 2>/dev/null}
68
+ }
69
+ end
70
+
71
+ # Remove a specific user's cron tab.
72
+ def self.remove(user)
73
+ Puppet.asuser(user) {
74
+ %x{crontab -r 2>/dev/null}
75
+ }
76
+ end
77
+
78
+ # Overwrite a specific user's cron tab; must be passed the user name
79
+ # and the text with which to create the cron tab.
80
+ def self.write(user, text)
81
+ Puppet.asuser(user) {
82
+ IO.popen("crontab", "w") { |p|
83
+ p.print text
84
+ }
85
+ }
86
+ end
87
+ end
88
+ end
89
+
90
+ class State
91
+ # This is Cron's single state. Somewhat uniquely, this state does not
92
+ # actually change anything -- it just calls +@parent.sync+, which writes
93
+ # out the whole cron tab for the user in question. There is no real way
94
+ # to change individual cron jobs without rewriting the entire cron file.
95
+ #
96
+ # Note that this means that managing many cron jobs for a given user
97
+ # could currently result in multiple write sessions for that user.
98
+ class CronCommand < Puppet::State
99
+ @name = :command
100
+ @doc = "The command to execute in the cron job. The environment
101
+ provided to the command varies by local system rules, and it is
102
+ best to always provide a fully qualified command. The user's
103
+ profile is not sourced when the command is run, so if the
104
+ user's environment is desired it should be sourced manually."
105
+
106
+ # Normally this would retrieve the current value, but our state is not
107
+ # actually capable of doing so. The Cron class does the actual tab
108
+ # retrieval, so all this method does is default to :notfound for @is.
109
+ def retrieve
110
+ unless defined? @is and ! @is.nil?
111
+ @is = :notfound
112
+ end
113
+ end
114
+
115
+ # Determine whether the cron job should be destroyed, and figure
116
+ # out which event to return. Finally, call @parent.sync to write the
117
+ # cron tab.
118
+ def sync
119
+ @parent.store
120
+
121
+ event = nil
122
+ if @is == :notfound
123
+ #@is = @should
124
+ event = :cron_created
125
+ elsif self.should == :notfound
126
+ @parent.remove(true)
127
+ event = :cron_deleted
128
+ elsif self.should == @is
129
+ self.err "Uh, they're both %s" % self.should
130
+ return nil
131
+ else
132
+ #@is = @should
133
+ self.err "@is is %s" % @is
134
+ event = :cron_changed
135
+ end
136
+
137
+ @parent.store
138
+
139
+ return event
140
+ end
141
+ end
142
+ end
143
+
144
+ class Type
145
+ # Model the actual cron jobs. Supports all of the normal cron job fields
146
+ # as parameters, with the 'command' as the single state. Also requires a
147
+ # completely symbolic 'name' paremeter, which gets written to the file
148
+ # and is used to manage the job.
149
+ class Cron < Type
150
+ @states = [
151
+ Puppet::State::CronCommand
152
+ ]
153
+
154
+ @parameters = [
155
+ :name,
156
+ :user,
157
+ :minute,
158
+ :hour,
159
+ :weekday,
160
+ :month,
161
+ :monthday
162
+ ]
163
+
164
+ @paramdoc[:name] = "The symbolic name of the cron job. This name
165
+ is used for human reference only."
166
+ @paramdoc[:user] = "The user to run the command as. This user must
167
+ be allowed to run cron jobs, which is not currently checked by
168
+ Puppet."
169
+ @paramdoc[:minute] = "The minute at which to run the cron job.
170
+ Optional; if specified, must be between 0 and 59, inclusive."
171
+ @paramdoc[:hour] = "The hour at which to run the cron job. Optional;
172
+ if specified, must be between 0 and 23, inclusive."
173
+ @paramdoc[:weekday] = "The weekday on which to run the command.
174
+ Optional; if specified, must be between 0 and 6, inclusive, with
175
+ 0 being Sunday, or must be the name of the day (e.g., Tuesday)."
176
+ @paramdoc[:month] = "The month of the year. Optional; if specified
177
+ must be between 1 and 12 or the month name (e.g., December)."
178
+ @paramdoc[:monthday] = "The day of the month on which to run the
179
+ command. Optional; if specified, must be between 1 and 31."
180
+
181
+ @doc = "Installs and manages cron jobs. All fields except the command
182
+ and the user are optional, although specifying no periodic
183
+ fields would result in the command being executed every
184
+ minute. While the name of the cron job is not part of the actual
185
+ job, it is used by Puppet to store and retrieve it. If you specify
186
+ a cron job that matches an existing job in every way except name,
187
+ then the jobs will be considered equivalent and the new name will
188
+ be permanently associated with that job. Once this association is
189
+ made and synced to disk, you can then manage the job normally."
190
+ @name = :cron
191
+ @namevar = :name
192
+
193
+ @loaded = {}
194
+
195
+ @synced = {}
196
+
197
+ @instances = {}
198
+
199
+ @@weekdays = %w{sunday monday tuesday wednesday thursday friday saturday}
200
+
201
+ @@months = %w{january february march april may june july
202
+ august september october november december}
203
+
204
+ case Facter["operatingsystem"].value
205
+ when "SunOS":
206
+ @crontype = Puppet::CronType::SunOS
207
+ else
208
+ @crontype = Puppet::CronType::Default
209
+ end
210
+
211
+ class << self
212
+ attr_accessor :crontype
213
+ end
214
+
215
+ attr_accessor :uid
216
+
217
+ # Override the Puppet::Type#[]= method so that we can store the instances
218
+ # in per-user arrays. Then just call +super+.
219
+ def self.[]=(name, object)
220
+ self.instance(object)
221
+ super
222
+ end
223
+
224
+ # In addition to removing the instances in @objects, Cron has to remove
225
+ # per-user cron tab information.
226
+ def self.clear
227
+ @instances = {}
228
+ @loaded = {}
229
+ @synced = {}
230
+ super
231
+ end
232
+
233
+ # Override the default Puppet::Type method, because instances
234
+ # also need to be deleted from the @instances hash
235
+ def self.delete(child)
236
+ if @instances.include?(child[:user])
237
+ if @instances[child[:user]].include?(child)
238
+ @instances[child[:user]].delete(child)
239
+ end
240
+ end
241
+ super
242
+ end
243
+
244
+ # Return the fields found in the cron tab.
245
+ def self.fields
246
+ return [:minute, :hour, :monthday, :month, :weekday, :command]
247
+ end
248
+
249
+ # Return the header placed at the top of each generated file, warning
250
+ # users that modifying this file manually is probably a bad idea.
251
+ def self.header
252
+ %{#This file was autogenerated at #{Time.now} by puppet. While it
253
+ # can still be managed manually, it is definitely not recommended.
254
+ # Note particularly that the comments starting with 'Puppet Name' should
255
+ # not be deleted, as doing so could cause duplicate cron jobs.\n}
256
+ end
257
+
258
+ # Store a new instance of a cron job. Called from Cron#initialize.
259
+ def self.instance(obj)
260
+ user = obj[:user]
261
+ if @instances.include?(user)
262
+ unless @instances[obj[:user]].include?(obj)
263
+ @instances[obj[:user]] << obj
264
+ end
265
+ else
266
+ @instances[obj[:user]] = [obj]
267
+ end
268
+ end
269
+
270
+ # Parse a user's cron job into individual cron objects.
271
+ #
272
+ # Autogenerates names for any jobs that don't already have one; these
273
+ # names will get written back to the file.
274
+ #
275
+ # This method also stores existing comments, and it stores all cron
276
+ # jobs in order, mostly so that comments are retained in the order
277
+ # they were written and in proximity to the same jobs.
278
+ def self.parse(user, text)
279
+ count = 0
280
+ hash = {}
281
+ name = nil
282
+ unless @instances.include?(user)
283
+ @instances[user] = []
284
+ end
285
+ text.chomp.split("\n").each { |line|
286
+ case line
287
+ when /^# Puppet Name: (\w+)$/: name = $1
288
+ when /^#/:
289
+ # add other comments to the list as they are
290
+ @instances[user] << line
291
+ else
292
+ ary = line.split(" ")
293
+ fields().each { |param|
294
+ value = ary.shift
295
+ unless value == "*"
296
+ hash[param] = value
297
+ end
298
+ }
299
+
300
+ if ary.length > 0
301
+ hash[:command] += " " + ary.join(" ")
302
+ end
303
+ cron = nil
304
+ unless name
305
+ Puppet.info "Autogenerating name for %s" % hash[:command]
306
+ name = "cron-%s" % hash.object_id
307
+ end
308
+
309
+ unless hash.include?(:command)
310
+ raise Puppet::DevError, "No command for %s" % name
311
+ end
312
+ # if the cron already exists with that name...
313
+ if cron = Puppet::Type::Cron[name]
314
+ # do nothing...
315
+ elsif tmp = @instances[user].reject { |obj|
316
+ ! obj.is_a?(Cron)
317
+ }.find { |obj|
318
+ obj.should(:command) == hash[:command]
319
+ }
320
+ # if we can find a cron whose spec exactly matches
321
+
322
+ # we now have a cron job whose command exactly matches
323
+ # let's see if the other fields match
324
+ txt = tmp.to_cron.sub(/#.+\n/,'')
325
+
326
+ if txt == line
327
+ cron = tmp
328
+ end
329
+ else
330
+ # create a new cron job, since no existing one
331
+ # seems to match
332
+ cron = Puppet::Type::Cron.create(
333
+ :name => name
334
+ )
335
+ end
336
+
337
+ hash.each { |param, value|
338
+ cron.is = [param, value]
339
+ }
340
+ hash.clear
341
+ name = nil
342
+ count += 1
343
+ end
344
+ }
345
+ end
346
+
347
+ # Retrieve a given user's cron job, using the @crontype's +retrieve+
348
+ # method. Returns nil if there was no cron job; else, returns the
349
+ # number of cron instances found.
350
+ def self.retrieve(user)
351
+ #%x{crontab -u #{user} -l 2>/dev/null}.split("\n").each { |line|
352
+ text = @crontype.read(user)
353
+ if $? != 0
354
+ # there is no cron file
355
+ return nil
356
+ else
357
+ self.parse(user, text)
358
+ end
359
+
360
+ @loaded[user] = Time.now
361
+ end
362
+
363
+ # Store the user's cron tab. Collects the text of the new tab and
364
+ # sends it to the +@crontype+ module's +write+ function. Also adds
365
+ # header warning users not to modify the file directly.
366
+ def self.store(user)
367
+ if @instances.include?(user)
368
+ @crontype.write(user, self.header + self.tab(user))
369
+ @synced[user] = Time.now
370
+ else
371
+ Puppet.notice "No cron instances for %s" % user
372
+ end
373
+ end
374
+
375
+ # Collect all Cron instances for a given user and convert them
376
+ # into literal text.
377
+ def self.tab(user)
378
+ if @instances.include?(user)
379
+ return @instances[user].collect { |obj|
380
+ if obj.is_a?(Cron)
381
+ obj.to_cron
382
+ else
383
+ obj.to_s
384
+ end
385
+ }.join("\n") + "\n"
386
+ else
387
+ Puppet.notice "No cron instances for %s" % user
388
+ end
389
+ end
390
+
391
+ # Return the last time a given user's cron tab was loaded. Could
392
+ # be used for reducing writes, but currently is not.
393
+ def self.loaded?(user)
394
+ if @loaded.include?(user)
395
+ return @loaded[user]
396
+ else
397
+ return nil
398
+ end
399
+ end
400
+
401
+ # A method used to do parameter input handling. Converts integers
402
+ # in string form to actual integers, and returns the value if it's
403
+ # an integer or false if it's just a normal string.
404
+ def numfix(num)
405
+ if num =~ /^\d+$/
406
+ return num.to_i
407
+ elsif num.is_a?(Integer)
408
+ return num
409
+ else
410
+ return false
411
+ end
412
+ end
413
+
414
+ # Verify that a number is within the specified limits. Return the
415
+ # number if it is, or false if it is not.
416
+ def limitcheck(num, lower, upper)
417
+ if num >= lower and num <= upper
418
+ return num
419
+ else
420
+ return false
421
+ end
422
+ end
423
+
424
+ # Verify that a value falls within the specified array. Does case
425
+ # insensitive matching, and supports matching either the entire word
426
+ # or the first three letters of the word.
427
+ def alphacheck(value, ary)
428
+ tmp = value.downcase
429
+ if tmp.length == 3
430
+ ary.each_with_index { |name, index|
431
+ if name =~ /#{tmp}/i
432
+ return index
433
+ end
434
+ }
435
+ else
436
+ if ary.include?(tmp)
437
+ return ary.index(tmp)
438
+ end
439
+ end
440
+
441
+ return false
442
+ end
443
+
444
+ # The method that does all of the actual parameter value checking; called
445
+ # by all of the +param<name>=+ methods. Requires the value, type, and
446
+ # bounds, and optionally supports a boolean of whether to do alpha
447
+ # checking, and if so requires the ary against which to do the checking.
448
+ def parameter(values, type, lower, upper, alpha = nil, ary = nil)
449
+ unless values.is_a?(Array)
450
+ if values =~ /,/
451
+ values = values.split(/,/)
452
+ else
453
+ values = [values]
454
+ end
455
+ end
456
+
457
+ @parameters[type] = values.collect { |value|
458
+ retval = nil
459
+ if num = numfix(value)
460
+ retval = limitcheck(num, lower, upper)
461
+ elsif alpha
462
+ retval = alphacheck(value, ary)
463
+ end
464
+
465
+ if retval
466
+ @parameters[type] = retval
467
+ else
468
+ raise Puppet::Error, "%s is not a valid %s" %
469
+ [value, type]
470
+ end
471
+ }
472
+ end
473
+
474
+ def paramminute=(value)
475
+ parameter(value, :minute, 0, 59)
476
+ end
477
+
478
+ def paramhour=(value)
479
+ parameter(value, :hour, 0, 23)
480
+ end
481
+
482
+ def paramweekday=(value)
483
+ parameter(value, :weekday, 0, 6, true, @@weekdays)
484
+ end
485
+
486
+ def parammonth=(value)
487
+ parameter(value, :month, 1, 12, true, @@months)
488
+ end
489
+
490
+ def parammonthday=(value)
491
+ parameter(value, :monthday, 1, 31)
492
+ end
493
+
494
+ def paramuser=(user)
495
+ require 'etc'
496
+
497
+ begin
498
+ obj = Etc.getpwnam(user)
499
+ @uid = obj.uid
500
+ rescue ArgumentError
501
+ raise Puppet::Error, "User %s not found" % user
502
+ end
503
+ @parameters[:user] = user
504
+ end
505
+
506
+ # Override the default Puppet::Type method because we need to call
507
+ # the +@crontype+ retrieve method.
508
+ def retrieve
509
+ unless @parameters.include?(:user)
510
+ raise Puppet::Error, "You must specify the cron user"
511
+ end
512
+
513
+ self.class.retrieve(@parameters[:user])
514
+ @states[:command].retrieve
515
+ end
516
+
517
+ # Write the entire user's cron tab out.
518
+ def store
519
+ self.class.store(@parameters[:user])
520
+ end
521
+
522
+ # Convert the current object a cron-style string. Adds the cron name
523
+ # as a comment above the cron job, in the form '# Puppet Name: <name>'.
524
+ def to_cron
525
+ hash = {:command => @states[:command].should || @states[:command].is }
526
+ self.class.fields().reject { |f| f == :command }.each { |param|
527
+ hash[param] = @parameters[param] || "*"
528
+ }
529
+
530
+ return "# Puppet Name: %s\n" % self.name +
531
+ self.class.fields.collect { |f|
532
+ if hash[f].is_a?(Array)
533
+ hash[f].join(",")
534
+ else
535
+ hash[f]
536
+ end
537
+ }.join(" ")
538
+ end
539
+ end
540
+ end
541
+ end
542
+
543
+ # $Id: cron.rb 737 2005-10-30 04:07:52Z luke $