puppet 0.9.2 → 0.13.0

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 (213) hide show
  1. data/CHANGELOG +58 -0
  2. data/README +21 -18
  3. data/Rakefile +176 -36
  4. data/bin/puppet +34 -48
  5. data/bin/puppetca +41 -28
  6. data/bin/puppetd +87 -65
  7. data/bin/puppetdoc +99 -23
  8. data/bin/puppetmasterd +72 -91
  9. data/conf/redhat/client.init +80 -0
  10. data/conf/redhat/client.sysconfig +11 -0
  11. data/conf/redhat/fileserver.conf +12 -0
  12. data/conf/redhat/puppet.spec +130 -0
  13. data/conf/redhat/server.init +89 -0
  14. data/conf/redhat/server.sysconfig +9 -0
  15. data/examples/code/allatonce +2 -2
  16. data/examples/code/assignments +1 -1
  17. data/examples/code/classing +2 -2
  18. data/examples/code/components +2 -2
  19. data/examples/code/file.bl +5 -5
  20. data/examples/code/filedefaults +2 -2
  21. data/examples/code/fileparsing +1 -1
  22. data/examples/code/filerecursion +1 -1
  23. data/examples/code/functions +1 -1
  24. data/examples/code/groups +1 -1
  25. data/examples/code/importing +1 -1
  26. data/examples/code/nodes +1 -1
  27. data/examples/code/one +1 -1
  28. data/examples/code/relationships +2 -2
  29. data/examples/code/simpletests +5 -5
  30. data/examples/code/snippets/argumentdefaults +2 -2
  31. data/examples/code/snippets/casestatement +16 -8
  32. data/examples/code/snippets/classheirarchy.pp +4 -4
  33. data/examples/code/snippets/classincludes.pp +4 -4
  34. data/examples/code/snippets/classpathtest +2 -2
  35. data/examples/code/snippets/componentmetaparams.pp +11 -0
  36. data/examples/code/snippets/dirchmod +5 -5
  37. data/examples/code/snippets/emptyclass.pp +9 -0
  38. data/examples/code/snippets/failmissingexecpath.pp +1 -1
  39. data/examples/code/snippets/falsevalues.pp +1 -1
  40. data/examples/code/snippets/filecreate +5 -5
  41. data/examples/code/snippets/implicititeration +5 -5
  42. data/examples/code/snippets/multipleinstances +4 -4
  43. data/examples/code/snippets/namevartest +3 -3
  44. data/examples/code/snippets/scopetest +1 -1
  45. data/examples/code/snippets/selectorvalues.pp +3 -3
  46. data/examples/code/snippets/simpledefaults +2 -2
  47. data/examples/code/snippets/simpleselector +5 -5
  48. data/examples/code/snippets/singleary.pp +19 -0
  49. data/examples/root/etc/init.d/sleeper +3 -2
  50. data/ext/emacs/puppet-mode-init.el +6 -0
  51. data/ext/emacs/puppet-mode.el +189 -0
  52. data/ext/ldap/puppet.schema +17 -0
  53. data/ext/{module:puppet → module_puppet} +30 -31
  54. data/ext/vim/filetype.vim +9 -0
  55. data/ext/vim/puppet.vim +87 -0
  56. data/install.rb +63 -30
  57. data/lib/puppet.rb +216 -122
  58. data/lib/puppet/client.rb +51 -416
  59. data/lib/puppet/client/ca.rb +17 -0
  60. data/lib/puppet/client/dipper.rb +78 -0
  61. data/lib/puppet/client/file.rb +20 -0
  62. data/lib/puppet/client/log.rb +17 -0
  63. data/lib/puppet/client/master.rb +246 -0
  64. data/lib/puppet/client/proxy.rb +27 -0
  65. data/lib/puppet/client/status.rb +7 -0
  66. data/lib/puppet/config.rb +563 -13
  67. data/lib/puppet/daemon.rb +50 -22
  68. data/lib/puppet/element.rb +4 -4
  69. data/lib/puppet/event-loop.rb +1 -0
  70. data/lib/puppet/event-loop/better-definers.rb +367 -0
  71. data/lib/puppet/event-loop/event-loop.rb +355 -0
  72. data/lib/puppet/event-loop/signal-system.rb +220 -0
  73. data/lib/puppet/event.rb +9 -11
  74. data/lib/puppet/filetype.rb +195 -0
  75. data/lib/puppet/log.rb +35 -12
  76. data/lib/puppet/metric.rb +2 -2
  77. data/lib/puppet/networkclient.rb +145 -0
  78. data/lib/puppet/parameter.rb +335 -0
  79. data/lib/puppet/parser/ast.rb +42 -1453
  80. data/lib/puppet/parser/ast/astarray.rb +88 -0
  81. data/lib/puppet/parser/ast/branch.rb +47 -0
  82. data/lib/puppet/parser/ast/caseopt.rb +66 -0
  83. data/lib/puppet/parser/ast/casestatement.rb +78 -0
  84. data/lib/puppet/parser/ast/classdef.rb +78 -0
  85. data/lib/puppet/parser/ast/compdef.rb +111 -0
  86. data/lib/puppet/parser/ast/component.rb +105 -0
  87. data/lib/puppet/parser/ast/hostclass.rb +82 -0
  88. data/lib/puppet/parser/ast/leaf.rb +86 -0
  89. data/lib/puppet/parser/ast/node.rb +103 -0
  90. data/lib/puppet/parser/ast/nodedef.rb +68 -0
  91. data/lib/puppet/parser/ast/objectdef.rb +336 -0
  92. data/lib/puppet/parser/ast/objectparam.rb +30 -0
  93. data/lib/puppet/parser/ast/objectref.rb +76 -0
  94. data/lib/puppet/parser/ast/selector.rb +60 -0
  95. data/lib/puppet/parser/ast/typedefaults.rb +45 -0
  96. data/lib/puppet/parser/ast/vardef.rb +44 -0
  97. data/lib/puppet/parser/interpreter.rb +31 -14
  98. data/lib/puppet/parser/lexer.rb +2 -4
  99. data/lib/puppet/parser/parser.rb +332 -242
  100. data/lib/puppet/parser/scope.rb +55 -38
  101. data/lib/puppet/server.rb +43 -44
  102. data/lib/puppet/server/authstore.rb +3 -6
  103. data/lib/puppet/server/ca.rb +5 -2
  104. data/lib/puppet/server/filebucket.rb +2 -4
  105. data/lib/puppet/server/fileserver.rb +28 -12
  106. data/lib/puppet/server/logger.rb +15 -4
  107. data/lib/puppet/server/master.rb +62 -7
  108. data/lib/puppet/sslcertificates.rb +41 -607
  109. data/lib/puppet/sslcertificates/ca.rb +291 -0
  110. data/lib/puppet/sslcertificates/certificate.rb +283 -0
  111. data/lib/puppet/statechange.rb +6 -1
  112. data/lib/puppet/storage.rb +67 -56
  113. data/lib/puppet/transaction.rb +25 -9
  114. data/lib/puppet/transportable.rb +102 -22
  115. data/lib/puppet/type.rb +1096 -315
  116. data/lib/puppet/type/component.rb +30 -21
  117. data/lib/puppet/type/cron.rb +409 -448
  118. data/lib/puppet/type/exec.rb +234 -174
  119. data/lib/puppet/type/group.rb +65 -82
  120. data/lib/puppet/type/nameservice.rb +247 -3
  121. data/lib/puppet/type/nameservice/netinfo.rb +29 -40
  122. data/lib/puppet/type/nameservice/objectadd.rb +52 -66
  123. data/lib/puppet/type/nameservice/posix.rb +6 -194
  124. data/lib/puppet/type/package.rb +447 -295
  125. data/lib/puppet/type/package/apt.rb +51 -50
  126. data/lib/puppet/type/package/bsd.rb +82 -0
  127. data/lib/puppet/type/package/dpkg.rb +85 -88
  128. data/lib/puppet/type/package/rpm.rb +67 -63
  129. data/lib/puppet/type/package/sun.rb +119 -98
  130. data/lib/puppet/type/package/yum.rb +41 -37
  131. data/lib/puppet/type/parsedtype.rb +295 -0
  132. data/lib/puppet/type/parsedtype/host.rb +143 -0
  133. data/lib/puppet/type/parsedtype/port.rb +232 -0
  134. data/lib/puppet/type/parsedtype/sshkey.rb +129 -0
  135. data/lib/puppet/type/pfile.rb +484 -460
  136. data/lib/puppet/type/pfile/checksum.rb +237 -181
  137. data/lib/puppet/type/pfile/content.rb +67 -0
  138. data/lib/puppet/type/pfile/ensure.rb +212 -0
  139. data/lib/puppet/type/pfile/group.rb +106 -105
  140. data/lib/puppet/type/pfile/mode.rb +98 -101
  141. data/lib/puppet/type/pfile/source.rb +228 -209
  142. data/lib/puppet/type/pfile/type.rb +18 -21
  143. data/lib/puppet/type/pfile/uid.rb +127 -130
  144. data/lib/puppet/type/pfilebucket.rb +68 -63
  145. data/lib/puppet/type/schedule.rb +341 -0
  146. data/lib/puppet/type/service.rb +351 -255
  147. data/lib/puppet/type/service/base.rb +9 -14
  148. data/lib/puppet/type/service/debian.rb +32 -38
  149. data/lib/puppet/type/service/init.rb +130 -130
  150. data/lib/puppet/type/service/smf.rb +48 -20
  151. data/lib/puppet/type/state.rb +229 -16
  152. data/lib/puppet/type/symlink.rb +51 -63
  153. data/lib/puppet/type/tidy.rb +105 -102
  154. data/lib/puppet/type/user.rb +118 -180
  155. data/lib/puppet/util.rb +100 -6
  156. data/test/certmgr/certmgr.rb +0 -1
  157. data/test/client/client.rb +4 -4
  158. data/test/executables/puppetbin.rb +7 -14
  159. data/test/executables/puppetca.rb +18 -24
  160. data/test/executables/puppetd.rb +7 -16
  161. data/test/executables/puppetmasterd.rb +7 -9
  162. data/test/executables/puppetmodule.rb +11 -16
  163. data/test/language/ast.rb +11 -7
  164. data/test/language/interpreter.rb +1 -1
  165. data/test/language/scope.rb +2 -0
  166. data/test/language/snippets.rb +30 -5
  167. data/test/language/transportable.rb +77 -0
  168. data/test/other/config.rb +316 -0
  169. data/test/other/events.rb +22 -21
  170. data/test/other/log.rb +14 -14
  171. data/test/other/metrics.rb +4 -8
  172. data/test/other/overrides.rb +5 -5
  173. data/test/other/relationships.rb +4 -2
  174. data/test/other/storage.rb +64 -3
  175. data/test/other/transactions.rb +20 -20
  176. data/test/parser/parser.rb +7 -4
  177. data/test/puppet/conffiles.rb +12 -12
  178. data/test/puppet/defaults.rb +13 -11
  179. data/test/puppet/utiltest.rb +14 -11
  180. data/test/puppettest.rb +156 -48
  181. data/test/server/bucket.rb +2 -2
  182. data/test/server/fileserver.rb +6 -6
  183. data/test/server/logger.rb +19 -11
  184. data/test/server/master.rb +33 -4
  185. data/test/server/server.rb +2 -7
  186. data/test/types/basic.rb +5 -7
  187. data/test/types/component.rb +22 -18
  188. data/test/types/cron.rb +111 -44
  189. data/test/types/exec.rb +116 -59
  190. data/test/types/file.rb +262 -137
  191. data/test/types/filebucket.rb +13 -15
  192. data/test/types/fileignoresource.rb +12 -16
  193. data/test/types/filesources.rb +73 -48
  194. data/test/types/filetype.rb +13 -15
  195. data/test/types/group.rb +15 -13
  196. data/test/types/host.rb +146 -0
  197. data/test/types/package.rb +74 -63
  198. data/test/types/port.rb +139 -0
  199. data/test/types/query.rb +8 -8
  200. data/test/types/schedule.rb +335 -0
  201. data/test/types/service.rb +137 -21
  202. data/test/types/sshkey.rb +140 -0
  203. data/test/types/symlink.rb +3 -5
  204. data/test/types/tidy.rb +5 -14
  205. data/test/types/type.rb +67 -11
  206. data/test/types/user.rb +25 -23
  207. metadata +186 -122
  208. data/lib/puppet/type/pfile/create.rb +0 -108
  209. data/lib/puppet/type/pprocess.rb +0 -97
  210. data/lib/puppet/type/typegen.rb +0 -149
  211. data/lib/puppet/type/typegen/filerecord.rb +0 -243
  212. data/lib/puppet/type/typegen/filetype.rb +0 -316
  213. data/test/other/state.rb +0 -106
@@ -1,6 +1,3 @@
1
- #!/usr/local/bin/ruby -w
2
-
3
- # $Id: component.rb 740 2005-11-01 20:22:19Z luke $
4
1
 
5
2
  # the object allowing us to build complex structures
6
3
  # this thing contains everything else, including itself
@@ -10,15 +7,20 @@ require 'puppet/type'
10
7
  require 'puppet/transaction'
11
8
 
12
9
  module Puppet
13
- class Type
14
- class Component < Puppet::Type
10
+ newtype(:component) do
15
11
  include Enumerable
16
12
 
17
- @name = :component
18
- @namevar = :name
13
+ newparam(:name) do
14
+ desc "The name of the component. Generally optional."
15
+ isnamevar
16
+ end
17
+
18
+ newparam(:type) do
19
+ desc "The type that this component maps to. Generally some kind of
20
+ class from the language."
19
21
 
20
- @states = []
21
- @parameters = [:name,:type]
22
+ defaultto "component"
23
+ end
22
24
 
23
25
  # topo sort functions
24
26
  def self.sort(objects)
@@ -39,11 +41,15 @@ module Puppet
39
41
  return
40
42
  end
41
43
  inlist[obj.object_id] = true
42
- obj.eachdependency { |req|
43
- self.recurse(req, inlist, list)
44
- }
44
+ begin
45
+ obj.eachdependency { |req|
46
+ self.recurse(req, inlist, list)
47
+ }
48
+ rescue Puppet::Error => detail
49
+ raise Puppet::Error, "%s: %s" % [obj.path, detail]
50
+ end
45
51
 
46
- if obj.is_a?(Puppet::Type::Component)
52
+ if obj.is_a? self
47
53
  obj.each { |child|
48
54
  self.recurse(child, inlist, list)
49
55
  }
@@ -76,11 +82,6 @@ module Puppet
76
82
  # Initialize a new component
77
83
  def initialize(args)
78
84
  @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
85
  super(args)
85
86
  end
86
87
 
@@ -88,6 +89,12 @@ module Puppet
88
89
  # this is only called on one component over the whole system
89
90
  # this also won't work with scheduling, but eh
90
91
  def evaluate
92
+ # The normal client process will automatically finalize things, but
93
+ # this simplifies a lot of test code -- as long as we use a
94
+ # compontent, we get finalized.
95
+ unless Puppet::Type.finalized?
96
+ Puppet::Type.finalize
97
+ end
91
98
  transaction = Puppet::Transaction.new(self.flatten)
92
99
  transaction.component = self
93
100
  return transaction
@@ -96,7 +103,7 @@ module Puppet
96
103
  def name
97
104
  #return self[:name]
98
105
  unless defined? @name
99
- if self[:type] == self[:name]
106
+ if self[:type] == self[:name] or self[:name] =~ /--\d+$/
100
107
  @name = self[:type]
101
108
  else
102
109
  @name = "%s[%s]" % [self[:type],self[:name]]
@@ -109,7 +116,7 @@ module Puppet
109
116
  ary.each { |child|
110
117
  unless child.is_a?(Puppet::Element)
111
118
  self.debug "Got object of type %s" % child.class
112
- raise Puppet::DevError.new(
119
+ self.devfail(
113
120
  "Containers can only contain Puppet::Elements, not %s" %
114
121
  child.class
115
122
  )
@@ -123,6 +130,7 @@ module Puppet
123
130
  @children.collect { |child|
124
131
  if child.respond_to?(:refresh)
125
132
  child.refresh
133
+ child.log "triggering %s" % :refresh
126
134
  end
127
135
  }
128
136
  end
@@ -137,5 +145,6 @@ module Puppet
137
145
  return "component(%s)" % self.name
138
146
  end
139
147
  end
140
- end
141
148
  end
149
+
150
+ # $Id: component.rb 841 2006-01-18 17:24:15Z luke $
@@ -1,543 +1,504 @@
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
1
  require 'etc'
5
2
  require 'facter'
6
3
  require 'puppet/type/state'
4
+ require 'puppet/filetype'
5
+ require 'puppet/type/parsedtype'
7
6
 
8
7
  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
8
+ # Model the actual cron jobs. Supports all of the normal cron job fields
9
+ # as parameters, with the 'command' as the single state. Also requires a
10
+ # completely symbolic 'name' paremeter, which gets written to the file
11
+ # and is used to manage the job.
12
+ newtype(:cron) do
13
+ ensurable
14
+
15
+ # A base class for all of the Cron parameters, since they all have
16
+ # similar argument checking going on. We're stealing the base class
17
+ # from parsedtype, and we should probably subclass Cron from there,
18
+ # but it was just too annoying to do.
19
+ class CronParam < Puppet::State::ParsedParam
20
+ class << self
21
+ attr_accessor :boundaries
25
22
  end
26
- end
27
23
 
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"
24
+ # A method used to do parameter input handling. Converts integers
25
+ # in string form to actual integers, and returns the value if it's
26
+ # an integer or false if it's just a normal string.
27
+ def numfix(num)
28
+ if num =~ /^\d+$/
29
+ return num.to_i
30
+ elsif num.is_a?(Integer)
31
+ return num
37
32
  else
38
- return "crontab -u #{user}"
33
+ return false
39
34
  end
40
35
  end
41
36
 
42
- # Read a specific user's cron tab.
43
- def self.read(user)
44
- tab = %x{#{self.cmdbase(user)} -l 2>/dev/null}
37
+ # Verify that a number is within the specified limits. Return the
38
+ # number if it is, or false if it is not.
39
+ def limitcheck(num, lower, upper)
40
+ if num >= lower and num <= upper
41
+ return num
42
+ else
43
+ return false
44
+ end
45
45
  end
46
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
47
+ # Verify that a value falls within the specified array. Does case
48
+ # insensitive matching, and supports matching either the entire word
49
+ # or the first three letters of the word.
50
+ def alphacheck(value, ary)
51
+ tmp = value.downcase
52
+
53
+ # If they specified a shortened version of the name, then see
54
+ # if we can lengthen it (e.g., mon => monday).
55
+ if tmp.length == 3
56
+ ary.each_with_index { |name, index|
57
+ if name =~ /#{tmp}/i
58
+ return index
59
+ end
60
+ }
61
+ else
62
+ if ary.include?(tmp)
63
+ return ary.index(tmp)
64
+ end
65
+ end
51
66
 
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
- }
67
+ return false
58
68
  end
59
- end
60
69
 
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
- }
70
+ def should_to_s
71
+ if @should.empty?
72
+ return "*"
73
+ else
74
+ return @should.join(",")
75
+ end
69
76
  end
70
77
 
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
78
+ # The method that does all of the actual parameter value
79
+ # checking; called by all of the +param<name>=+ methods.
80
+ # Requires the value, type, and bounds, and optionally supports
81
+ # a boolean of whether to do alpha checking, and if so requires
82
+ # the ary against which to do the checking.
83
+ munge do |value|
84
+ return value unless self.class.boundaries
85
+ lower, upper = self.class.boundaries
86
+ retval = nil
87
+ if num = numfix(value)
88
+ retval = limitcheck(num, lower, upper)
89
+ elsif respond_to?(:alpha)
90
+ # If it has an alpha method defined, then we check
91
+ # to see if our value is in that list and if so we turn
92
+ # it into a number
93
+ retval = alphacheck(value, alpha())
94
+ end
77
95
 
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
- }
96
+ if retval
97
+ return retval.to_s
98
+ else
99
+ self.fail "%s is not a valid %s" %
100
+ [value, self.class.name]
101
+ end
86
102
  end
87
103
  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.
104
+ # Somewhat uniquely, this state does not actually change anything -- it
105
+ # just calls +@parent.sync+, which writes out the whole cron tab for
106
+ # the user in question. There is no real way to change individual cron
107
+ # jobs without rewriting the entire cron file.
95
108
  #
96
109
  # Note that this means that managing many cron jobs for a given user
97
110
  # 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
111
+ newstate(:command, CronParam) do
112
+ desc "The command to execute in the cron job. The environment
101
113
  provided to the command varies by local system rules, and it is
102
114
  best to always provide a fully qualified command. The user's
103
115
  profile is not sourced when the command is run, so if the
104
116
  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
117
  end
142
- end
143
118
 
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.
119
+ newstate(:minute, CronParam) do
120
+ self.boundaries = [0, 59]
121
+ desc "The minute at which to run the cron job.
170
122
  Optional; if specified, must be between 0 and 59, inclusive."
171
- @paramdoc[:hour] = "The hour at which to run the cron job. Optional;
123
+ end
124
+
125
+ newstate(:hour, CronParam) do
126
+ self.boundaries = [0, 23]
127
+ desc "The hour at which to run the cron job. Optional;
172
128
  if specified, must be between 0 and 23, inclusive."
173
- @paramdoc[:weekday] = "The weekday on which to run the command.
129
+ end
130
+
131
+ newstate(:weekday, CronParam) do
132
+ def alpha
133
+ %w{sunday monday tuesday wednesday thursday friday saturday}
134
+ end
135
+ self.boundaries = [0, 6]
136
+ desc "The weekday on which to run the command.
174
137
  Optional; if specified, must be between 0 and 6, inclusive, with
175
138
  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
139
+ end
140
+
141
+ newstate(:month, CronParam) do
142
+ def alpha
143
+ %w{january february march april may june july
144
+ august september october november december}
145
+ end
146
+ self.boundaries = [1, 12]
147
+ desc "The month of the year. Optional; if specified
177
148
  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."
149
+ end
180
150
 
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
151
+ newstate(:monthday, CronParam) do
152
+ self.boundaries = [1, 31]
153
+ desc "The day of the month on which to run the
154
+ command. Optional; if specified, must be between 1 and 31."
155
+ end
192
156
 
193
- @loaded = {}
157
+ newparam(:name) do
158
+ desc "The symbolic name of the cron job. This name
159
+ is used for human reference only and is generated
160
+ automatically for cron jobs found on the system. This generally
161
+ won't matter, as Puppet will do its best to match existing
162
+ cron jobs against specified jobs (and Puppet adds a tag to
163
+ cron jobs it adds), but it is at least possible that converting
164
+ from unmanaged jobs to managed jobs might require manual intervention."
194
165
 
195
- @synced = {}
166
+ isnamevar
167
+ end
196
168
 
197
- @instances = {}
169
+ newparam(:user) do
170
+ desc "The user to run the command as. This user must
171
+ be allowed to run cron jobs, which is not currently checked by
172
+ Puppet."
198
173
 
199
- @@weekdays = %w{sunday monday tuesday wednesday thursday friday saturday}
174
+ # This validation isn't really a good idea, since the user might
175
+ # be created by Puppet, in which case the validation will fail.
176
+ validate do |user|
177
+ require 'etc'
200
178
 
201
- @@months = %w{january february march april may june july
202
- august september october november december}
179
+ begin
180
+ obj = Etc.getpwnam(user)
181
+ parent.uid = obj.uid
182
+ rescue ArgumentError
183
+ self.fail "User %s not found" % user
184
+ end
203
185
 
204
- case Facter["operatingsystem"].value
205
- when "SunOS":
206
- @crontype = Puppet::CronType::SunOS
207
- else
208
- @crontype = Puppet::CronType::Default
186
+ user
209
187
  end
188
+ end
210
189
 
211
- class << self
212
- attr_accessor :crontype
213
- end
190
+ @doc = "Installs and manages cron jobs. All fields except the command
191
+ and the user are optional, although specifying no periodic
192
+ fields would result in the command being executed every
193
+ minute. While the name of the cron job is not part of the actual
194
+ job, it is used by Puppet to store and retrieve it.
195
+
196
+ If you specify a cron job that matches an existing job in every way
197
+ except name, then the jobs will be considered equivalent and the
198
+ new name will be permanently associated with that job. Once this
199
+ association is made and synced to disk, you can then manage the job
200
+ normally (e.g., change the schedule of the job).
201
+
202
+ Example::
203
+
204
+ cron { logrotate:
205
+ command => \"/usr/sbin/logrotate\",
206
+ user => root,
207
+ hour => 2,
208
+ minute => 0
209
+ }
210
+ "
214
211
 
215
- attr_accessor :uid
212
+ @instances = {}
213
+ @tabs = {}
216
214
 
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
215
+ case Facter["operatingsystem"].value
216
+ when "Solaris":
217
+ @filetype = Puppet::FileType.filetype(:suntab)
218
+ else
219
+ @filetype = Puppet::FileType.filetype(:crontab)
220
+ end
223
221
 
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
222
+ class << self
223
+ attr_accessor :filetype
224
+ end
232
225
 
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
226
+ attr_accessor :uid
227
+
228
+ # Override the Puppet::Type#[]= method so that we can store the instances
229
+ # in per-user arrays. Then just call +super+.
230
+ def self.[]=(name, object)
231
+ self.instance(object)
232
+ super
233
+ end
234
+
235
+ # In addition to removing the instances in @objects, Cron has to remove
236
+ # per-user cron tab information.
237
+ def self.clear
238
+ @instances = {}
239
+ @tabs = {}
240
+ super
241
+ end
242
+
243
+ # Override the default Puppet::Type method, because instances
244
+ # also need to be deleted from the @instances hash
245
+ def self.delete(child)
246
+ if @instances.include?(child[:user])
247
+ if @instances[child[:user]].include?(child)
248
+ @instances[child[:user]].delete(child)
240
249
  end
241
- super
242
250
  end
251
+ super
252
+ end
243
253
 
244
- # Return the fields found in the cron tab.
245
- def self.fields
246
- return [:minute, :hour, :monthday, :month, :weekday, :command]
247
- end
254
+ # Return the fields found in the cron tab.
255
+ def self.fields
256
+ return [:minute, :hour, :monthday, :month, :weekday, :command]
257
+ end
248
258
 
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
259
+ # Return the header placed at the top of each generated file, warning
260
+ # users that modifying this file manually is probably a bad idea.
261
+ def self.header
262
+ %{# HEADER This file was autogenerated at #{Time.now} by puppet. While it
263
+ # HEADER can still be managed manually, it is definitely not recommended.
264
+ # HEADER Note particularly that the comments starting with 'Puppet Name' should
265
+ # HEADER not be deleted, as doing so could cause duplicate cron jobs.\n}
266
+ end
257
267
 
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]
268
+ # Store a new instance of a cron job. Called from Cron#initialize.
269
+ def self.instance(obj)
270
+ user = obj[:user]
271
+ if @instances.include?(user)
272
+ unless @instances[obj[:user]].include?(obj)
273
+ @instances[obj[:user]] << obj
267
274
  end
275
+ else
276
+ @instances[obj[:user]] = [obj]
268
277
  end
278
+ end
269
279
 
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
280
+ # Parse a user's cron job into individual cron objects.
281
+ #
282
+ # Autogenerates names for any jobs that don't already have one; these
283
+ # names will get written back to the file.
284
+ #
285
+ # This method also stores existing comments, and it stores all cron
286
+ # jobs in order, mostly so that comments are retained in the order
287
+ # they were written and in proximity to the same jobs.
288
+ def self.parse(user, text)
289
+ count = 0
290
+ hash = {}
291
+ name = nil
292
+ unless @instances.include?(user)
293
+ @instances[user] = []
294
+ end
295
+ text.chomp.split("\n").each { |line|
296
+ case line
297
+ when /^# Puppet Name: (\w+)$/: name = $1
298
+ when /^#/:
299
+ # add other comments to the list as they are
300
+ @instances[user] << line
301
+ else
302
+ if match = /^(\S+) (\S+) (\S+) (\S+) (\S+) (.+)$/.match(line)
303
+ fields().zip(match.captures).each { |param, value|
295
304
  unless value == "*"
305
+ unless param == :command
306
+ if value =~ /,/
307
+ value = value.split(",")
308
+ end
309
+ end
296
310
  hash[param] = value
297
311
  end
298
312
  }
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
313
+ else
314
+ raise Puppet::Error, "Could not match '%s'" % line
343
315
  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
316
 
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
317
+ cron = nil
318
+ unless name
319
+ Puppet.info "Autogenerating name for %s" % hash[:command]
320
+ name = "cron-%s" % hash.object_id
321
+ end
374
322
 
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
323
+ unless hash.include?(:command)
324
+ raise Puppet::DevError, "No command for %s" % name
325
+ end
326
+ # if the cron already exists with that name...
327
+ if cron = Puppet.type(:cron)[name]
328
+ # Mark the cron job as present
329
+ cron.is = [:ensure, :present]
330
+ elsif tmp = @instances[user].reject { |obj|
331
+ ! obj.is_a?(self)
332
+ }.find { |obj|
333
+ obj.should(:command) == hash[:command]
334
+ }
335
+ # if we can find a cron whose spec exactly matches
336
+
337
+ # we now have a cron job whose command exactly matches
338
+ # let's see if the other fields match
339
+ txt = tmp.to_record.sub(/#.+\n/,'')
340
+
341
+ if txt == line
342
+ cron = tmp
384
343
  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
344
+ else
345
+ # create a new cron job, since no existing one
346
+ # seems to match
347
+ cron = self.create(
348
+ :name => name
349
+ )
350
+ end
400
351
 
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
352
+ hash.each { |param, value|
353
+ cron.is = [param, value]
354
+ }
355
+ hash.clear
356
+ name = nil
357
+ count += 1
411
358
  end
412
- end
359
+ }
360
+ end
413
361
 
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
362
+ # Retrieve a given user's cron job, using the @filetype's +retrieve+
363
+ # method. Returns nil if there was no cron job; else, returns the
364
+ # number of cron instances found.
365
+ def self.retrieve(user)
366
+ @tabs[user] ||= @filetype.new(user)
367
+ text = @tabs[user].read
368
+ if $? != 0
369
+ # there is no cron file
370
+ return nil
371
+ else
372
+ # Preemptively mark everything absent, so that retrieving it
373
+ # can mark it present again.
374
+ self.find_all { |obj|
375
+ obj[:user] == user
376
+ }.each { |obj|
377
+ obj.each { |state|
378
+ state.is = :absent
379
+ }
380
+ }
381
+ self.parse(user, text)
422
382
  end
383
+ end
423
384
 
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
385
+ # Remove a user's cron tab.
386
+ def self.remove(user)
387
+ @tabs[user] ||= @filetype.new(user)
388
+ @tabs[user].remove
389
+ end
440
390
 
441
- return false
391
+ # Store the user's cron tab. Collects the text of the new tab and
392
+ # sends it to the +@filetype+ module's +write+ function. Also adds
393
+ # header warning users not to modify the file directly.
394
+ def self.store(user)
395
+ @tabs[user] ||= @filetype.new(user)
396
+ if @instances.include?(user)
397
+ @tabs[user].write(self.tab(user))
398
+ else
399
+ Puppet.notice "No cron instances for %s" % user
442
400
  end
401
+ end
443
402
 
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(/,/)
403
+ # Collect all Cron instances for a given user and convert them
404
+ # into literal text.
405
+ def self.tab(user)
406
+ Puppet.info "writing cron tab for %s" % user
407
+ if @instances.include?(user)
408
+ return self.header() + @instances[user].reject { |obj|
409
+ if obj.is_a?(self) and obj.should(:ensure) == :absent
410
+ #obj.log "now absent"
411
+ #obj.is = [:ensure, :absent]
412
+ true
452
413
  else
453
- values = [values]
414
+ false
454
415
  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
416
+ }.collect { |obj|
417
+ if obj.is_a? self
418
+ obj.to_record
467
419
  else
468
- raise Puppet::Error, "%s is not a valid %s" %
469
- [value, type]
420
+ obj.to_s
470
421
  end
471
- }
472
- end
422
+ }.join("\n") + "\n"
473
423
 
474
- def paramminute=(value)
475
- parameter(value, :minute, 0, 59)
424
+ else
425
+ Puppet.notice "No cron instances for %s" % user
476
426
  end
427
+ end
477
428
 
478
- def paramhour=(value)
479
- parameter(value, :hour, 0, 23)
480
- end
429
+ # Return the tab object itself. Pretty much just used for testing.
430
+ def self.tabobj(user)
431
+ @tabs[user]
432
+ end
481
433
 
482
- def paramweekday=(value)
483
- parameter(value, :weekday, 0, 6, true, @@weekdays)
434
+ # Return the last time a given user's cron tab was loaded. Could
435
+ # be used for reducing writes, but currently is not.
436
+ def self.loaded?(user)
437
+ if @tabs.include?(user)
438
+ return @loaded[user].loaded
439
+ else
440
+ return nil
484
441
  end
442
+ end
485
443
 
486
- def parammonth=(value)
487
- parameter(value, :month, 1, 12, true, @@months)
488
- end
444
+ def create
445
+ # nothing
446
+ self.info "creating"
447
+ self.store
448
+ end
489
449
 
490
- def parammonthday=(value)
491
- parameter(value, :monthday, 1, 31)
492
- end
450
+ def destroy
451
+ # nothing, since the 'Cron.tab' method just doesn't write out
452
+ # crons whose 'ensure' states are set to 'absent'.
453
+ #@states.each { |n, state|
454
+ # next if n == :ensure
455
+ # state.should == :absent
456
+ #}
457
+ self.store
458
+ end
493
459
 
494
- def paramuser=(user)
495
- require 'etc'
460
+ def exists?
461
+ val = false
462
+ if @states.include?(:command) and @states[:command].is != :absent
463
+ val = true
464
+ end
465
+ return val
466
+ end
496
467
 
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
468
+ # Override the default Puppet::Type method because we need to call
469
+ # the +@filetype+ retrieve method.
470
+ def retrieve
471
+ unless @parameters.include?(:user)
472
+ self.fail "You must specify the cron user"
504
473
  end
505
474
 
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
475
+ self.class.retrieve(self[:user])
476
+ self.eachstate { |st| st.retrieve }
477
+ end
512
478
 
513
- self.class.retrieve(@parameters[:user])
514
- @states[:command].retrieve
515
- end
479
+ # Write the entire user's cron tab out.
480
+ def store
481
+ self.class.store(self[:user])
482
+ end
516
483
 
517
- # Write the entire user's cron tab out.
518
- def store
519
- self.class.store(@parameters[:user])
520
- end
484
+ # Convert the current object a cron-style string. Adds the cron name
485
+ # as a comment above the cron job, in the form '# Puppet Name: <name>'.
486
+ def to_record
487
+ hash = {:command => @states[:command].should || @states[:command].is }
521
488
 
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
- }
489
+ # Collect all of the values that we have
490
+ self.class.fields().reject { |f| f == :command }.each { |param|
491
+ if @states.include?(param)
492
+ hash[param] = @states[param].should_to_s
493
+ end
494
+ }
529
495
 
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
496
+ return "# Puppet Name: %s\n" % self.name +
497
+ self.class.fields.collect { |f|
498
+ hash[f] || "*"
499
+ }.join(" ")
539
500
  end
540
501
  end
541
502
  end
542
503
 
543
- # $Id: cron.rb 737 2005-10-30 04:07:52Z luke $
504
+ # $Id: cron.rb 841 2006-01-18 17:24:15Z luke $