summaryrefslogtreecommitdiffstats
path: root/lib/puppet/type/yumrepo.rb
blob: b85bc5994db15ce2ea9cc1658971b4878225d4e1 (plain)
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
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# Description of yum repositories

require 'puppet/util/inifile'

module Puppet
    # A property for one entry in a .ini-style file
    class IniProperty < Puppet::Property
        def insync?(is)
            # A should property of :absent is the same as nil
            if is.nil? && (should.nil? || should == :absent)
                return true
            end
            super(is)
        end

        def sync
            if insync?(retrieve)
                result = nil
            else
                result = set(self.should)
                if should == :absent
                    resource.section[inikey] = nil
                else
                    resource.section[inikey] = should
                end
            end
            result
        end

        def retrieve
            resource.section[inikey]
        end

        def inikey
            name.to_s
        end

        # Set the key associated with this property to KEY, instead
        # of using the property's NAME
        def self.inikey(key)
            # Override the inikey instance method
            # Is there a way to do this without resorting to strings ?
            # Using a block fails because the block can't access
            # the variable 'key' in the outer scope
            self.class_eval("def inikey ; \"#{key.to_s}\" ; end")
        end

    end

    # Doc string for properties that can be made 'absent'
    ABSENT_DOC="Set this to 'absent' to remove it from the file completely"

    newtype(:yumrepo) do
        @doc = "The client-side description of a yum repository. Repository
            configurations are found by parsing /etc/yum.conf and
            the files indicated by reposdir in that file (see yum.conf(5) for details)

            Most parameters are identical to the ones documented
            in yum.conf(5)

            Continuation lines that yum supports for example for the
            baseurl are not supported. No attempt is made to access
            files included with the **include** directive"

        class << self
            attr_accessor :filetype
            # The writer is only used for testing, there should be no need
            # to change yumconf or inifile in any other context
            attr_accessor :yumconf
            attr_writer :inifile
        end

        self.filetype = Puppet::Util::FileType.filetype(:flat)

        @inifile = nil

        @yumconf = "/etc/yum.conf"

        # Where to put files for brand new sections
        @defaultrepodir = nil

        def self.instances
            l = []
            check = validproperties
            clear
            inifile.each_section do |s|
                next if s.name == "main"
                obj = create(:name => s.name, :audit => check)
                current_values = obj.retrieve
                obj.eachproperty do |property|
                    if current_values[property].nil?
                        obj.delete(property.name)
                    else
                        property.should = current_values[property]
                    end
                end
                obj.delete(:audit)
                l << obj
            end
            l
        end

        # Return the Puppet::Util::IniConfig::File for the whole yum config
        def self.inifile
            if @inifile.nil?
                @inifile = read()
                main = @inifile['main']
                raise Puppet::Error, "File #{yumconf} does not contain a main section" if main.nil?
                reposdir = main['reposdir']
                reposdir ||= "/etc/yum.repos.d, /etc/yum/repos.d"
                reposdir.gsub!(/[\n,]/, " ")
                reposdir.split.each do |dir|
                    Dir::glob("#{dir}/*.repo").each do |file|
                        @inifile.read(file) if File.file?(file)
                    end
                end
                reposdir.split.each do |dir|
                    if File::directory?(dir) && File::writable?(dir)
                        @defaultrepodir = dir
                        break
                    end
                end
            end
            @inifile
        end

        # Parse the yum config files. Only exposed for the tests
        # Non-test code should use self.inifile to get at the
        # underlying file
        def self.read
            result = Puppet::Util::IniConfig::File.new()
            result.read(yumconf)
            main = result['main']
            raise Puppet::Error, "File #{yumconf} does not contain a main section" if main.nil?
            reposdir = main['reposdir']
            reposdir ||= "/etc/yum.repos.d, /etc/yum/repos.d"
            reposdir.gsub!(/[\n,]/, " ")
            reposdir.split.each do |dir|
                Dir::glob("#{dir}/*.repo").each do |file|
                    result.read(file) if File.file?(file)
                end
            end
            if @defaultrepodir.nil?
                reposdir.split.each do |dir|
                    if File::directory?(dir) && File::writable?(dir)
                        @defaultrepodir = dir
                        break
                    end
                end
            end
            result
        end

        # Return the Puppet::Util::IniConfig::Section with name NAME
        # from the yum config
        def self.section(name)
            result = inifile[name]
            if result.nil?
                # Brand new section
                path = yumconf
                path = File::join(@defaultrepodir, "#{name}.repo") unless @defaultrepodir.nil?
                Puppet::info "create new repo #{name} in file #{path}"
                result = inifile.add_section(name, path)
            end
            result
        end

        # Store all modifications back to disk
        def self.store
            inifile.store
            unless Puppet[:noop]
                target_mode = 0644 # FIXME: should be configurable
                inifile.each_file do |file|
                    current_mode = File.stat(file).mode & 0777
                    unless current_mode == target_mode
                        Puppet::info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode]
                        File.chmod(target_mode, file)
                    end
                end
            end
        end

        # This is only used during testing.
        def self.clear
            @inifile = nil
            @yumconf = "/etc/yum.conf"
            @defaultrepodir = nil
        end

        # Return the Puppet::Util::IniConfig::Section for this yumrepo resource
        def section
            self.class.section(self[:name])
        end

        # Store modifications to this yumrepo resource back to disk
        def flush
            self.class.store
        end

        newparam(:name) do
            desc "The name of the repository.  This corresponds to the
                repositoryid parameter in yum.conf(5)."
            isnamevar
        end

        newproperty(:descr, :parent => Puppet::IniProperty) do
            desc "A human readable description of the repository.
                This corresponds to the name parameter in yum.conf(5).
                #{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(/.*/) { }
            inikey "name"
        end

        newproperty(:mirrorlist, :parent => Puppet::IniProperty) do
            desc "The URL that holds the list of mirrors for this repository.
                #{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            # Should really check that it's a valid URL
            newvalue(/.*/) { }
        end

        newproperty(:baseurl, :parent => Puppet::IniProperty) do
            desc "The URL for this repository.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            # Should really check that it's a valid URL
            newvalue(/.*/) { }
        end

        newproperty(:enabled, :parent => Puppet::IniProperty) do
            desc "Whether this repository is enabled or disabled. Possible
                values are '0', and '1'.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{(0|1)}) { }
        end

        newproperty(:gpgcheck, :parent => Puppet::IniProperty) do
            desc "Whether to check the GPG signature on packages installed
                from this repository. Possible values are '0', and '1'.
                \n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{(0|1)}) { }
        end

        newproperty(:gpgkey, :parent => Puppet::IniProperty) do
            desc "The URL for the GPG key with which packages from this
                repository are signed.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            # Should really check that it's a valid URL
            newvalue(/.*/) { }
        end

        newproperty(:include, :parent => Puppet::IniProperty) do
            desc "A URL from which to include the config.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            # Should really check that it's a valid URL
            newvalue(/.*/) { }
        end

        newproperty(:exclude, :parent => Puppet::IniProperty) do
            desc "List of shell globs. Matching packages will never be
                considered in updates or installs for this repo.
                #{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(/.*/) { }
        end

        newproperty(:includepkgs, :parent => Puppet::IniProperty) do
            desc "List of shell globs. If this is set, only packages
                matching one of the globs will be considered for
                update or install.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(/.*/) { }
        end

        newproperty(:enablegroups, :parent => Puppet::IniProperty) do
            desc "Determines whether yum will allow the use of
                package groups for this  repository. Possible
                values are '0', and '1'.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{(0|1)}) { }
        end

        newproperty(:failovermethod, :parent => Puppet::IniProperty) do
            desc "Either 'roundrobin' or 'priority'.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{roundrobin|priority}) { }
        end

        newproperty(:keepalive, :parent => Puppet::IniProperty) do
            desc "Either '1' or '0'. This tells yum whether or not HTTP/1.1
                keepalive  should  be  used with this repository.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{(0|1)}) { }
        end

        newproperty(:timeout, :parent => Puppet::IniProperty) do
            desc "Number of seconds to wait for a connection before timing
                out.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{[0-9]+}) { }
        end

        newproperty(:metadata_expire, :parent => Puppet::IniProperty) do
            desc "Number of seconds after which the metadata will expire.
                #{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{[0-9]+}) { }
        end

        newproperty(:protect, :parent => Puppet::IniProperty) do
            desc "Enable or disable protection for this repository. Requires
                that the protectbase plugin is installed and enabled.
                #{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{(0|1)}) { }
        end

        newproperty(:priority, :parent => Puppet::IniProperty) do
            desc "Priority of this repository from 1-99. Requires that
                the priorities plugin is installed and enabled.
                #{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(%r{[1-9][0-9]?}) { }
        end

        newproperty(:proxy, :parent => Puppet::IniProperty) do
            desc "URL to the proxy server for this repository.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            # Should really check that it's a valid URL
            newvalue(/.*/) { }
        end

        newproperty(:proxy_username, :parent => Puppet::IniProperty) do
            desc "Username for this proxy.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(/.*/) { }
        end

        newproperty(:proxy_password, :parent => Puppet::IniProperty) do
            desc "Password for this proxy.\n#{ABSENT_DOC}"
            newvalue(:absent) { self.should = :absent }
            newvalue(/.*/) { }
        end
    end
end