1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
|
# Define the different packaging systems. Each package system is implemented
# in a module, which then gets used to individually extend each package object.
# This allows packages to exist on the same machine using different packaging
# systems.
module Puppet
newtype(:package) do
@doc = "Manage packages. There is a basic dichotomy in package
support right now: Some package types (e.g., yum and apt) can
retrieve their own package files, while others (e.g., rpm and sun) cannot. For those package formats that cannot retrieve
their own files, you can use the `source` parameter to point to
the correct file.
Puppet will automatically guess the packaging format that you are
using based on the platform you are on, but you can override it
using the `provider` parameter; each provider defines what it
requires in order to function, and you must meet those requirements
to use a given provider."
feature :installable, "The provider can install packages.",
:methods => [:install]
feature :uninstallable, "The provider can uninstall packages.",
:methods => [:uninstall]
feature :upgradeable, "The provider can upgrade to the latest version of a
package. This feature is used by specifying `latest` as the
desired value for the package.",
:methods => [:update, :latest]
feature :purgeable, "The provider can purge packages. This generally means
that all traces of the package are removed, including
existing configuration files. This feature is thus destructive
and should be used with the utmost care.",
:methods => [:purge]
feature :versionable, "The provider is capable of interrogating the
package database for installed version(s), and can select
which out of a set of available versions of a package to
install if asked."
feature :holdable, "The provider is capable of placing packages on hold
such that they are not automatically upgraded as a result of
other package dependencies unless explicit action is taken by
a user or another package. Held is considered a superset of
installed.",
:methods => [:hold]
ensurable do
desc "What state the package should be in.
*latest* only makes sense for those packaging formats that can
retrieve new packages on their own and will throw an error on
those that cannot. For those packaging systems that allow you
to specify package versions, specify them here. Similarly,
*purged* is only useful for packaging systems that support
the notion of managing configuration files separately from
'normal' system files."
attr_accessor :latest
newvalue(:present, :event => :package_installed) do
provider.install
end
newvalue(:absent, :event => :package_removed) do
provider.uninstall
end
newvalue(:purged, :event => :package_purged, :required_features => :purgeable) do
provider.purge
end
newvalue(:held, :event => :package_held, :required_features => :holdable) do
provider.hold
end
# Alias the 'present' value.
aliasvalue(:installed, :present)
newvalue(:latest, :required_features => :upgradeable) do
# Because yum always exits with a 0 exit code, there's a retrieve
# in the "install" method. So, check the current state now,
# to compare against later.
current = self.retrieve
begin
provider.update
rescue => detail
self.fail "Could not update: #{detail}"
end
if current == :absent
:package_installed
else
:package_changed
end
end
newvalue(/./, :required_features => :versionable) do
begin
provider.install
rescue => detail
self.fail "Could not update: #{detail}"
end
if self.retrieve == :absent
:package_installed
else
:package_changed
end
end
defaultto :installed
# Override the parent method, because we've got all kinds of
# funky definitions of 'in sync'.
def insync?(is)
@latest ||= nil
@lateststamp ||= (Time.now.to_i - 1000)
# Iterate across all of the should values, and see how they
# turn out.
@should.each { |should|
case should
when :present
return true unless [:absent, :purged, :held].include?(is)
when :latest
# Short-circuit packages that are not present
return false if is == :absent or is == :purged
# Don't run 'latest' more than about every 5 minutes
if @latest and ((Time.now.to_i - @lateststamp) / 60) < 5
#self.debug "Skipping latest check"
else
begin
@latest = provider.latest
@lateststamp = Time.now.to_i
rescue => detail
error = Puppet::Error.new("Could not get latest version: #{detail}")
error.set_backtrace(detail.backtrace)
raise error
end
end
case is
when @latest
return true
when :present
# This will only happen on retarded packaging systems
# that can't query versions.
return true
else
self.debug "#{@resource.name} #{is.inspect} is installed, latest is #{@latest.inspect}"
end
when :absent
return true if is == :absent or is == :purged
when :purged
return true if is == :purged
when is
return true
end
}
false
end
# This retrieves the current state. LAK: I think this method is unused.
def retrieve
provider.properties[:ensure]
end
# Provide a bit more information when logging upgrades.
def should_to_s(newvalue = @should)
if @latest
@latest.to_s
else
super(newvalue)
end
end
end
newparam(:name) do
desc "The package name. This is the name that the packaging
system uses internally, which is sometimes (especially on Solaris)
a name that is basically useless to humans. If you want to
abstract package installation, then you can use aliases to provide
a common name to packages:
# In the 'openssl' class
$ssl = $operatingsystem ? {
solaris => SMCossl,
default => openssl
}
# It is not an error to set an alias to the same value as the
# object name.
package { $ssl:
ensure => installed,
alias => openssl
}
. etc. .
$ssh = $operatingsystem ? {
solaris => SMCossh,
default => openssh
}
# Use the alias to specify a dependency, rather than
# having another selector to figure it out again.
package { $ssh:
ensure => installed,
alias => openssh,
require => Package[openssl]
}
"
isnamevar
end
newparam(:source) do
desc "Where to find the actual package. This must be a local file
(or on a network file system) or a URL that your specific
packaging type understands; Puppet will not retrieve files for you."
end
newparam(:instance) do
desc "A read-only parameter set by the package."
end
newparam(:status) do
desc "A read-only parameter set by the package."
end
newparam(:type) do
desc "Deprecated form of `provider`."
munge do |value|
warning "'type' is deprecated; use 'provider' instead"
@resource[:provider] = value
@resource[:provider]
end
end
newparam(:adminfile) do
desc "A file containing package defaults for installing packages.
This is currently only used on Solaris. The value will be
validated according to system rules, which in the case of
Solaris means that it should either be a fully qualified path
or it should be in `/var/sadm/install/admin`."
end
newparam(:responsefile) do
desc "A file containing any necessary answers to questions asked by
the package. This is currently used on Solaris and Debian. The
value will be validated according to system rules, but it should
generally be a fully qualified path."
end
newparam(:configfiles) do
desc "Whether configfiles should be kept or replaced. Most packages
types do not support this parameter."
defaultto :keep
newvalues(:keep, :replace)
end
newparam(:category) do
desc "A read-only parameter set by the package."
end
newparam(:platform) do
desc "A read-only parameter set by the package."
end
newparam(:root) do
desc "A read-only parameter set by the package."
end
newparam(:vendor) do
desc "A read-only parameter set by the package."
end
newparam(:description) do
desc "A read-only parameter set by the package."
end
newparam(:allowcdrom) do
desc "Tells apt to allow cdrom sources in the sources.list file.
Normally apt will bail if you try this."
newvalues(:true, :false)
end
newparam(:flavor) do
desc "Newer versions of OpenBSD support 'flavors', which are
further specifications for which type of package you want."
end
autorequire(:file) do
autos = []
[:responsefile, :adminfile].each { |param|
if val = self[param]
autos << val
end
}
if source = self[:source]
if source =~ /^#{File::SEPARATOR}/
autos << source
end
end
autos
end
# This only exists for testing.
def clear
if obj = @parameters[:ensure]
obj.latest = nil
end
end
# The 'query' method returns a hash of info if the package
# exists and returns nil if it does not.
def exists?
@provider.get(:ensure) != :absent
end
end
end
|