puppet 0.13.6 → 0.16.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 (149) hide show
  1. data/CHANGELOG +57 -0
  2. data/Rakefile +38 -410
  3. data/bin/puppet +14 -12
  4. data/bin/puppetca +1 -3
  5. data/bin/puppetd +25 -7
  6. data/bin/puppetdoc +161 -104
  7. data/bin/puppetmasterd +4 -4
  8. data/conf/epm.list +8 -0
  9. data/conf/redhat/client.init +6 -1
  10. data/conf/redhat/no-chuser-0.15.1.patch +38 -0
  11. data/conf/redhat/puppet.spec +20 -5
  12. data/conf/redhat/puppetd.conf +1 -1
  13. data/conf/redhat/puppetmasterd.conf +1 -1
  14. data/conf/redhat/server.init +2 -4
  15. data/examples/code/snippets/{casestatement → casestatement.pp} +12 -1
  16. data/examples/code/snippets/selectorvalues.pp +15 -0
  17. data/examples/code/snippets/singleselector.pp +22 -0
  18. data/examples/code/snippets/tag.pp +9 -0
  19. data/ext/module_puppet +1 -1
  20. data/install.rb +303 -303
  21. data/lib/puppet.rb +7 -9
  22. data/lib/puppet/client.rb +18 -5
  23. data/lib/puppet/client/dipper.rb +12 -10
  24. data/lib/puppet/client/master.rb +113 -41
  25. data/lib/puppet/client/pelement.rb +20 -0
  26. data/lib/puppet/config.rb +113 -6
  27. data/lib/puppet/element.rb +1 -3
  28. data/lib/puppet/event.rb +12 -23
  29. data/lib/puppet/filetype.rb +93 -5
  30. data/lib/puppet/inifile.rb +201 -0
  31. data/lib/puppet/log.rb +18 -6
  32. data/lib/puppet/parameter.rb +80 -29
  33. data/lib/puppet/parser/ast.rb +6 -4
  34. data/lib/puppet/parser/ast/caseopt.rb +13 -4
  35. data/lib/puppet/parser/ast/casestatement.rb +2 -2
  36. data/lib/puppet/parser/ast/component.rb +4 -14
  37. data/lib/puppet/parser/ast/hostclass.rb +1 -1
  38. data/lib/puppet/parser/ast/leaf.rb +12 -0
  39. data/lib/puppet/parser/ast/node.rb +4 -4
  40. data/lib/puppet/parser/ast/objectdef.rb +5 -51
  41. data/lib/puppet/parser/ast/selector.rb +2 -0
  42. data/lib/puppet/parser/ast/tag.rb +26 -0
  43. data/lib/puppet/parser/interpreter.rb +89 -74
  44. data/lib/puppet/parser/lexer.rb +4 -3
  45. data/lib/puppet/parser/parser.rb +440 -378
  46. data/lib/puppet/parser/scope.rb +844 -887
  47. data/lib/puppet/server.rb +12 -1
  48. data/lib/puppet/server/authconfig.rb +166 -0
  49. data/lib/puppet/server/authstore.rb +8 -6
  50. data/lib/puppet/server/ca.rb +23 -26
  51. data/lib/puppet/server/filebucket.rb +24 -23
  52. data/lib/puppet/server/fileserver.rb +116 -47
  53. data/lib/puppet/server/master.rb +58 -19
  54. data/lib/puppet/server/pelement.rb +176 -0
  55. data/lib/puppet/server/rights.rb +78 -0
  56. data/lib/puppet/server/servlet.rb +19 -6
  57. data/lib/puppet/sslcertificates.rb +4 -2
  58. data/lib/puppet/sslcertificates/ca.rb +66 -34
  59. data/lib/puppet/storage.rb +20 -26
  60. data/lib/puppet/transaction.rb +49 -92
  61. data/lib/puppet/type.rb +142 -35
  62. data/lib/puppet/type/cron.rb +29 -14
  63. data/lib/puppet/type/exec.rb +92 -35
  64. data/lib/puppet/type/group.rb +29 -11
  65. data/lib/puppet/type/nameservice.rb +50 -1
  66. data/lib/puppet/type/nameservice/netinfo.rb +68 -1
  67. data/lib/puppet/type/nameservice/objectadd.rb +1 -0
  68. data/lib/puppet/type/package.rb +150 -109
  69. data/lib/puppet/type/package/apple.rb +27 -0
  70. data/lib/puppet/type/package/apt.rb +1 -0
  71. data/lib/puppet/type/package/darwinport.rb +97 -0
  72. data/lib/puppet/type/package/dpkg.rb +10 -2
  73. data/lib/puppet/type/package/freebsd.rb +19 -0
  74. data/lib/puppet/type/package/{bsd.rb → openbsd.rb} +36 -7
  75. data/lib/puppet/type/package/ports.rb +98 -0
  76. data/lib/puppet/type/package/rpm.rb +43 -7
  77. data/lib/puppet/type/package/sun.rb +53 -36
  78. data/lib/puppet/type/package/yum.rb +5 -16
  79. data/lib/puppet/type/parsedtype.rb +41 -29
  80. data/lib/puppet/type/parsedtype/host.rb +13 -5
  81. data/lib/puppet/type/parsedtype/mount.rb +250 -0
  82. data/lib/puppet/type/parsedtype/port.rb +8 -6
  83. data/lib/puppet/type/pfile.rb +284 -30
  84. data/lib/puppet/type/pfile/checksum.rb +96 -68
  85. data/lib/puppet/type/pfile/content.rb +16 -13
  86. data/lib/puppet/type/pfile/ensure.rb +64 -126
  87. data/lib/puppet/type/pfile/group.rb +12 -5
  88. data/lib/puppet/type/pfile/mode.rb +16 -4
  89. data/lib/puppet/type/pfile/source.rb +47 -73
  90. data/lib/puppet/type/pfile/target.rb +81 -0
  91. data/lib/puppet/type/pfile/uid.rb +10 -3
  92. data/lib/puppet/type/pfilebucket.rb +12 -3
  93. data/lib/puppet/type/schedule.rb +5 -1
  94. data/lib/puppet/type/service.rb +138 -66
  95. data/lib/puppet/type/service/debian.rb +9 -3
  96. data/lib/puppet/type/service/init.rb +51 -56
  97. data/lib/puppet/type/service/smf.rb +16 -6
  98. data/lib/puppet/type/state.rb +71 -32
  99. data/lib/puppet/type/symlink.rb +12 -7
  100. data/lib/puppet/type/tidy.rb +5 -1
  101. data/lib/puppet/type/user.rb +116 -20
  102. data/lib/puppet/type/yumrepo.rb +314 -0
  103. data/lib/puppet/util.rb +84 -14
  104. data/test/client/client.rb +41 -18
  105. data/test/client/master.rb +50 -4
  106. data/test/executables/puppetbin.rb +31 -4
  107. data/test/executables/puppetca.rb +18 -2
  108. data/test/language/ast.rb +201 -31
  109. data/test/language/interpreter.rb +8 -2
  110. data/test/{parser → language}/lexer.rb +1 -1
  111. data/test/language/node.rb +84 -0
  112. data/test/{parser → language}/parser.rb +1 -1
  113. data/test/language/scope.rb +101 -2
  114. data/test/language/snippets.rb +23 -2
  115. data/test/other/config.rb +99 -1
  116. data/test/other/filetype.rb +95 -0
  117. data/test/other/inifile.rb +114 -0
  118. data/test/other/log.rb +3 -2
  119. data/test/other/transactions.rb +55 -10
  120. data/test/puppet/utiltest.rb +25 -1
  121. data/test/puppettest.rb +140 -46
  122. data/test/server/authconfig.rb +56 -0
  123. data/test/server/bucket.rb +32 -18
  124. data/test/server/fileserver.rb +75 -30
  125. data/test/server/master.rb +27 -4
  126. data/test/server/pelement.rb +298 -0
  127. data/test/server/rights.rb +41 -0
  128. data/test/server/server.rb +2 -2
  129. data/test/tagging/tagging.rb +100 -1
  130. data/test/types/basic.rb +3 -3
  131. data/test/types/cron.rb +24 -1
  132. data/test/types/exec.rb +99 -1
  133. data/test/types/file.rb +298 -2
  134. data/test/types/filebucket.rb +4 -15
  135. data/test/types/filesources.rb +43 -14
  136. data/test/types/group.rb +1 -13
  137. data/test/types/mount.rb +277 -0
  138. data/test/types/package.rb +164 -33
  139. data/test/types/parameter.rb +107 -0
  140. data/test/types/port.rb +2 -1
  141. data/test/types/service.rb +37 -2
  142. data/test/types/state.rb +92 -0
  143. data/test/types/symlink.rb +30 -2
  144. data/test/types/tidy.rb +2 -14
  145. data/test/types/type.rb +35 -1
  146. data/test/types/user.rb +110 -1
  147. data/test/types/yumrepo.rb +95 -0
  148. metadata +316 -290
  149. data/test/types/filetype.rb +0 -160
@@ -96,7 +96,7 @@ module Puppet
96
96
  end
97
97
  self.debug "Executing %s" % cmd.join(" ").inspect
98
98
 
99
- output = %x{#{cmd.join(" ")} 2>&1}.split("\n").each { |line|
99
+ %x{#{cmd.join(" ")} 2>&1}.split("\n").each { |line|
100
100
  if line =~ /^(\w+)\s+(.+)$/
101
101
  name = $1
102
102
  value = $2.sub(/\s+$/, '')
@@ -118,6 +118,73 @@ module Puppet
118
118
  end
119
119
  end
120
120
 
121
+ # The list of all groups the user is a member of. Different
122
+ # user mgmt systems will need to override this method.
123
+ def grouplist
124
+ groups = []
125
+
126
+ user = @parent[:name]
127
+ # Retrieve them all from netinfo
128
+ open("| nireport / /groups name users") do |file|
129
+ file.each do |line|
130
+ name, members = line.split(/\s+/)
131
+ next unless members
132
+ next if members =~ /NoValue/
133
+ members = members.split(",")
134
+
135
+ if members.include? user
136
+ groups << name
137
+ end
138
+ end
139
+ end
140
+
141
+ groups
142
+ end
143
+
144
+ # This is really lame. We have to iterate over each
145
+ # of the groups and add us to them.
146
+ def setgrouplist(groups)
147
+ # Get just the groups we need to modify
148
+ diff = groups - @is
149
+
150
+ data = {}
151
+ open("| nireport / /groups name users") do |file|
152
+ file.each do |line|
153
+ name, members = line.split(/\s+/)
154
+
155
+ if members.nil? or members =~ /NoValue/
156
+ data[name] = []
157
+ else
158
+ # Add each diff group's current members
159
+ data[name] = members.split(/,/)
160
+ end
161
+ end
162
+ end
163
+
164
+ user = @parent[:name]
165
+ data.each do |name, members|
166
+ if members.include? user and groups.include? name
167
+ # I'm in the group and should be
168
+ next
169
+ elsif members.include? user
170
+ # I'm in the group and shouldn't be
171
+ setuserlist(name, members - [user])
172
+ elsif groups.include? name
173
+ # I'm not in the group and should be
174
+ setuserlist(name, members + [user])
175
+ else
176
+ # I'm not in the group and shouldn't be
177
+ next
178
+ end
179
+ end
180
+ end
181
+
182
+ def setuserlist(group, list)
183
+ cmd = "niutil -createprop / /groups/%s users %s" %
184
+ [group, list.join(",")]
185
+ output = %x{#{cmd}}
186
+ end
187
+
121
188
  # How to add an object.
122
189
  def addcmd
123
190
  creatorcmd("-create")
@@ -92,6 +92,7 @@ module Puppet
92
92
  @allatonce = true
93
93
  case self.name
94
94
  when :home: setflag "-d"
95
+ when :groups: setflag "-G"
95
96
  end
96
97
  end
97
98
  end
@@ -11,7 +11,7 @@ module Puppet
11
11
  @doc = "Manage packages. There is a basic dichotomy in package
12
12
  support right now: Some package types (e.g., yum and apt) can
13
13
  retrieve their own package files, while others (e.g., rpm and
14
- sunpkg) cannot. For those package formats that cannot retrieve
14
+ sun) cannot. For those package formats that cannot retrieve
15
15
  their own files, you can use the ``source`` parameter to point to
16
16
  the correct file.
17
17
 
@@ -33,12 +33,12 @@ module Puppet
33
33
 
34
34
  # Add our parent, if it exists
35
35
  if parent
36
- unless @pkgtypes.include?(parent)
36
+ unless parenttype = pkgtype(parent)
37
37
  raise Puppet::DevError,
38
38
  "No parent type %s for package type %s" %
39
39
  [parent, name]
40
40
  end
41
- mod.send(:include, @pkgtypes[parent])
41
+ mod.send(:include, parenttype)
42
42
  end
43
43
 
44
44
  # And now define the support methods
@@ -60,10 +60,46 @@ module Puppet
60
60
 
61
61
  mod.module_eval(&block)
62
62
 
63
+ # It's at least conceivable that a module would not define this method
64
+ # "module_function" makes the :list method private, so if the parent
65
+ # method also called module_function, then it's already private
66
+ if mod.public_method_defined? :list or mod.private_method_defined? :list
67
+ mod.send(:module_function, :list)
68
+ end
69
+
70
+ # Add it to our list
63
71
  @pkgtypes[name] = mod
72
+
73
+ # And mark it as a valid type
74
+ unless defined? @typeparam
75
+ @typeparam = @parameters.find { |p| p.name == :type }
76
+
77
+ unless @typeparam
78
+ Puppet.warning @parameters.inspect
79
+ raise Puppet::DevError, "Could not package type parameter"
80
+ end
81
+ end
82
+ @typeparam.newvalues(name)
64
83
  end
65
84
 
85
+ # Autoload the package types, if they're not already defined.
66
86
  def self.pkgtype(name)
87
+ #name = name[0] if name.is_a? Array
88
+ name = name.intern if name.is_a? String
89
+ @pkgtypes ||= {}
90
+ unless @pkgtypes.include? name
91
+ begin
92
+ require "puppet/type/package/#{name}"
93
+
94
+ unless @pkgtypes.include? name
95
+ Puppet.warning @pkgtypes.keys
96
+ raise Puppet::DevError,
97
+ "Loaded %s but pkgtype was not created" % name.inspect
98
+ end
99
+ rescue LoadError
100
+ raise Puppet::Error, "Could not load package type %s" % name
101
+ end
102
+ end
67
103
  @pkgtypes[name]
68
104
  end
69
105
 
@@ -78,28 +114,7 @@ module Puppet
78
114
  those packaging formats that can retrieve new packages on
79
115
  their own."
80
116
 
81
- #munge do |value|
82
- # # possible values are: true, false, and a version number
83
- # case value
84
- # when "latest":
85
- # unless @parent.respond_to?(:latest)
86
- # self.err @parent.inspect
87
- # raise Puppet::Error,
88
- # "Package type %s cannot install later versions" %
89
- # @parent[:type].name
90
- # end
91
- # return :latest
92
- # when true, :present:
93
- # return :present
94
- # when false, :absent:
95
- # return :absent
96
- # else
97
- # # We allow them to set a should value however they want,
98
- # # but only specific package types will be able to use this
99
- # # value
100
- # return value
101
- # end
102
- #end
117
+ attr_accessor :latest
103
118
 
104
119
  newvalue(:present) do
105
120
  @parent.install
@@ -111,9 +126,6 @@ module Puppet
111
126
 
112
127
  # Alias the 'present' value.
113
128
  aliasvalue(:installed, :present)
114
- #newvalue(:installed) do
115
- # self.set(:present)
116
- #end
117
129
 
118
130
  newvalue(:latest) do
119
131
  unless @parent.respond_to?(:latest)
@@ -139,6 +151,9 @@ module Puppet
139
151
  # funky definitions of 'in sync'.
140
152
  def insync?
141
153
  @should ||= []
154
+
155
+ @latest = nil unless defined? @latest
156
+ @lateststamp ||= (Time.now.to_i - 1000)
142
157
  # Iterate across all of the should values, and see how they
143
158
  # turn out.
144
159
  @should.each { |should|
@@ -148,6 +163,11 @@ module Puppet
148
163
  return true
149
164
  end
150
165
  when :latest
166
+ # Short-circuit packages that are not present
167
+ if @is == :absent
168
+ return false
169
+ end
170
+
151
171
  unless @parent.respond_to?(:latest)
152
172
  self.fail(
153
173
  "Package type %s does not support specifying 'latest'" %
@@ -155,21 +175,30 @@ module Puppet
155
175
  )
156
176
  end
157
177
 
158
- begin
159
- latest = @parent.latest
160
- rescue => detail
161
- self.fail "Could not get latest version: %s" % detail
178
+ # Don't run 'latest' more than about every 5 minutes
179
+ if @latest and ((Time.now.to_i - @lateststamp) / 60) < 5
180
+ #self.debug "Skipping latest check"
181
+ else
182
+ begin
183
+ @latest = @parent.latest
184
+ @lateststamp = Time.now.to_i
185
+ rescue => detail
186
+ self.fail "Could not get latest version: %s" % detail
187
+ end
162
188
  end
163
189
  case @is
164
- when latest:
190
+ when @latest:
165
191
  return true
166
192
  when :present:
167
- if @parent[:version] == latest
193
+ if @parent[:version] == @latest
168
194
  return true
195
+ else
196
+ self.debug "our version is %s and latest is %s" %
197
+ [@parent[:version], @latest]
169
198
  end
170
199
  else
171
- #self.debug "@is is %s, latest %s is %s" %
172
- # [@is, @parent.name, latest]
200
+ self.debug "@is is %s, latest %s is %s" %
201
+ [@is, @parent.name, @latest]
173
202
  end
174
203
  when :absent
175
204
  if @is == :absent
@@ -255,14 +284,24 @@ module Puppet
255
284
 
256
285
  defaultto { @parent.class.default }
257
286
 
258
- # We cannot log in this routine, because this gets called before
259
- # there's a name for the package.
287
+
288
+ validate do |value|
289
+ value = value[0] if value.is_a? Array
290
+ unless @parent.class.pkgtype(value)
291
+ raise ArgumentError, "Invalid package type '%s'" % value
292
+ end
293
+ end
294
+
295
+
260
296
  munge do |type|
297
+ type = type[0] if type.is_a? Array
261
298
  if type.is_a? String
262
299
  type = type.intern
263
300
  end
264
301
  @parent.type2module(type)
302
+ type
265
303
  end
304
+
266
305
  end
267
306
 
268
307
  newparam(:source) do
@@ -383,7 +422,7 @@ module Puppet
383
422
  )
384
423
  end
385
424
  case @platform
386
- when "solaris": @default = :sunpkg
425
+ when "solaris": @default = :sun
387
426
  when "gentoo":
388
427
  Puppet.notice "No support for gentoo yet"
389
428
  @default = nil
@@ -391,7 +430,9 @@ module Puppet
391
430
  when "centos": @default = :rpm
392
431
  when "fedora": @default = :yum
393
432
  when "redhat": @default = :rpm
394
- when "openbsd": @default = :bsd
433
+ when "freebsd": @default = :ports
434
+ when "openbsd": @default = :openbsd
435
+ when "darwin": @default = :apple
395
436
  else
396
437
  if Facter["kernel"] == "Linux"
397
438
  Puppet.warning "Defaulting to RPM for %s" %
@@ -405,74 +446,68 @@ module Puppet
405
446
  end
406
447
  end
407
448
 
408
- def self.getpkglist
409
- if @types.nil?
410
- if @default.nil?
411
- self.init
412
- end
413
- @types = [@default]
414
- end
415
-
416
- list = @types.collect { |type|
417
- if typeobj = Puppet::PackagingType[type]
418
- # pull all of the objects
419
- typeobj.list
420
- else
421
- raise Puppet::Error, "Could not find package type '%s'" % type
422
- end
423
- }.flatten
424
- @listed = true
425
- return list
426
- end
449
+ # Return a list of valid package types
450
+ # def self.getpkglist
451
+ # if @types.nil?
452
+ # if @default.nil?
453
+ # self.init
454
+ # end
455
+ # @types = [@default]
456
+ # end
457
+ #
458
+ # list = @types.collect { |type|
459
+ # if typeobj = Puppet::PackagingType[type]
460
+ # # pull all of the objects
461
+ # typeobj.list
462
+ # else
463
+ # raise Puppet::Error, "Could not find package type '%s'" % type
464
+ # end
465
+ # }.flatten
466
+ # @listed = true
467
+ # return list
468
+ # end
427
469
 
470
+ # Create a new package object from listed information
428
471
  def self.installedpkg(hash)
472
+ unless hash.include? :type
473
+ raise Puppet::DevError, "Got installed package with no type"
474
+ end
429
475
  # this is from code, so we don't have to do as much checking
430
476
  name = hash[:name]
431
477
  hash.delete(:name)
432
478
 
433
- # if it already exists, modify the existing one
434
- if object = Package[name]
435
- states = {}
436
- object.eachstate { |state|
437
- Puppet.debug "Adding %s" % state.name.inspect
438
- states[state.name] = state
439
- }
440
- hash.each { |var,value|
441
- if states.include?(var)
442
- Puppet.debug "%s is a set state" % var.inspect
443
- states[var].is = value
444
- else
445
- Puppet.debug "%s is not a set state" % var.inspect
446
- if object[var] and object[var] != value
447
- Puppet.warning "Overriding %s => %s on %s with %s" %
448
- [var,object[var],name,value]
449
- end
479
+ object = self[name] || self.create(:name => name)
480
+ object.setparams(hash)
481
+
482
+ return object
483
+ end
450
484
 
451
- #object.state(var).is = value
485
+ # List all package instances
486
+ def self.list
487
+ pkgtype(default).list()
452
488
 
453
- # swap the values if we're a state
454
- if states.include?(var)
455
- Puppet.debug "Swapping %s because it's a state" % var
456
- states[var].is = value
457
- states[var].should = nil
458
- else
459
- Puppet.debug "%s is not a state" % var.inspect
460
- Puppet.debug "States are %s" % states.keys.collect { |st|
461
- st.inspect
462
- }.join(" ")
463
- end
464
- end
465
- }
466
- return object
467
- else # just create it
468
- obj = self.create(:name => name)
469
- hash.each { |var,value|
470
- obj.addis(var,value)
471
- }
472
- return obj
489
+ self.collect do |pkg|
490
+ pkg
473
491
  end
474
492
  end
475
493
 
494
+ # Iterate across all packages of a given type and mark them absent
495
+ # if they are not in the list
496
+ def self.markabsent(pkgtype, packages)
497
+ # Mark any packages we didn't find as absent
498
+ self.each do |pkg|
499
+ next unless packages[:type] == pkgtype
500
+ unless packages.include? pkg
501
+ pkg.is = [:ensure, :absent]
502
+ end
503
+ end
504
+ end
505
+
506
+ # This only exists for testing.
507
+ def clear
508
+ @states[:ensure].latest = nil
509
+ end
510
+
476
511
  # The 'query' method returns a hash of info if the package
477
512
  # exists and returns nil if it does not.
478
513
  def exists?
@@ -515,15 +550,16 @@ module Puppet
515
550
  # about it and set it appropriately.
516
551
  #@states[:ensure].retrieve
517
552
  if hash = self.query
553
+ if hash == :listed # Mmmm, hackalicious
554
+ return
555
+ end
518
556
  hash.each { |param, value|
519
557
  unless self.class.validattr?(param)
520
558
  hash.delete(param)
521
559
  end
522
560
  }
523
561
 
524
- hash.each { |param, value|
525
- self.is = [param, value]
526
- }
562
+ setparams(hash)
527
563
  else
528
564
  # Else just mark all of the states absent.
529
565
  self.class.validstates.each { |name|
@@ -532,6 +568,19 @@ module Puppet
532
568
  end
533
569
  end
534
570
 
571
+ # Set all of the params' "is" value. Most are parameters, but some
572
+ # are states.
573
+ def setparams(hash)
574
+ # Everything on packages is a parameter except :ensure
575
+ hash.each { |param, value|
576
+ if self.class.attrtype(param) == :state
577
+ self.is = [param, value]
578
+ else
579
+ self[param] = value
580
+ end
581
+ }
582
+ end
583
+
535
584
  # Extend the package with the appropriate package type.
536
585
  def type2module(typename)
537
586
  if type = self.class.pkgtype(typename)
@@ -589,12 +638,4 @@ module Puppet
589
638
  }
590
639
  end
591
640
 
592
- # The order these are loaded is important.
593
- require 'puppet/type/package/dpkg.rb'
594
- require 'puppet/type/package/apt.rb'
595
- require 'puppet/type/package/rpm.rb'
596
- require 'puppet/type/package/yum.rb'
597
- require 'puppet/type/package/sun.rb'
598
- require 'puppet/type/package/bsd.rb'
599
-
600
- # $Id: package.rb 937 2006-02-24 20:00:30Z luke $
641
+ # $Id: package.rb 1129 2006-04-21 19:14:59Z luke $