(MODULES-8418) Add parameter $auth_conf_owner
[puppet-modules/puppetlabs-apt.git] / spec / classes / apt_spec.rb
1 require 'spec_helper'
2
3 sources_list = {  ensure: 'file',
4                   path: '/etc/apt/sources.list',
5                   owner: 'root',
6                   group: 'root',
7                   mode: '0644',
8                   notify: 'Class[Apt::Update]' }
9
10 sources_list_d = { ensure: 'directory',
11                    path: '/etc/apt/sources.list.d',
12                    owner: 'root',
13                    group: 'root',
14                    mode: '0644',
15                    purge: false,
16                    recurse: false,
17                    notify: 'Class[Apt::Update]' }
18
19 preferences = { ensure: 'file',
20                 path: '/etc/apt/preferences',
21                 owner: 'root',
22                 group: 'root',
23                 mode: '0644',
24                 notify: 'Class[Apt::Update]' }
25
26 preferences_d = { ensure: 'directory',
27                   path: '/etc/apt/preferences.d',
28                   owner: 'root',
29                   group: 'root',
30                   mode: '0644',
31                   purge: false,
32                   recurse: false,
33                   notify: 'Class[Apt::Update]' }
34
35 describe 'apt' do
36   let(:facts) do
37     {
38       os: { family: 'Debian', name: 'Debian', release: { major: '8', full: '8.0' } },
39       lsbdistid: 'Debian',
40       osfamily: 'Debian',
41       lsbdistcodename: 'jessie',
42     }
43   end
44
45   context 'with defaults' do
46     it {
47       is_expected.to contain_file('sources.list').that_notifies('Class[Apt::Update]').only_with(sources_list)
48     }
49
50     it {
51       is_expected.to contain_file('sources.list.d').that_notifies('Class[Apt::Update]').only_with(sources_list_d)
52     }
53
54     it {
55       is_expected.to contain_file('preferences').that_notifies('Class[Apt::Update]').only_with(preferences)
56     }
57
58     it {
59       is_expected.to contain_file('preferences.d').that_notifies('Class[Apt::Update]').only_with(preferences_d)
60     }
61
62     it { is_expected.to contain_file('/etc/apt/auth.conf').with_ensure('absent') }
63
64     it 'lays down /etc/apt/apt.conf.d/15update-stamp' do
65       is_expected.to contain_file('/etc/apt/apt.conf.d/15update-stamp').with(group: 'root',
66                                                                              mode: '0644',
67                                                                              owner: 'root').with_content(
68                                                                                %r{APT::Update::Post-Invoke-Success {"touch /var/lib/apt/periodic/update-success-stamp 2>/dev/null || true";};},
69                                                                              )
70     end
71
72     it {
73       is_expected.to contain_exec('apt_update').with(refreshonly: 'true')
74     }
75
76     it { is_expected.not_to contain_apt__setting('conf-proxy') }
77   end
78
79   describe 'proxy=' do
80     context 'when host=localhost' do
81       let(:params) { { proxy: { 'host' => 'localhost' } } }
82
83       it {
84         is_expected.to contain_apt__setting('conf-proxy').with(priority: '01').with_content(
85           %r{Acquire::http::proxy "http://localhost:8080/";},
86         ).without_content(
87           %r{Acquire::https::proxy},
88         )
89       }
90     end
91
92     context 'when host=localhost and port=8180' do
93       let(:params) { { proxy: { 'host' => 'localhost', 'port' => 8180 } } }
94
95       it {
96         is_expected.to contain_apt__setting('conf-proxy').with(priority: '01').with_content(
97           %r{Acquire::http::proxy "http://localhost:8180/";},
98         ).without_content(
99           %r{Acquire::https::proxy},
100         )
101       }
102     end
103
104     context 'when host=localhost and https=true' do
105       let(:params) { { proxy: { 'host' => 'localhost', 'https' => true } } }
106
107       it {
108         is_expected.to contain_apt__setting('conf-proxy').with(priority: '01').with_content(
109           %r{Acquire::http::proxy "http://localhost:8080/";},
110         ).with_content(
111           %r{Acquire::https::proxy "https://localhost:8080/";},
112         )
113       }
114     end
115
116     context 'when host=localhost and direct=true' do
117       let(:params) { { proxy: { 'host' => 'localhost', 'direct' => true } } }
118
119       it {
120         is_expected.to contain_apt__setting('conf-proxy').with(priority: '01').with_content(
121           %r{Acquire::http::proxy "http://localhost:8080/";},
122         ).with_content(
123           %r{Acquire::https::proxy "DIRECT";},
124         )
125       }
126     end
127
128     context 'when host=localhost and https=true and direct=true' do
129       let(:params) { { proxy: { 'host' => 'localhost', 'https' => true, 'direct' => true } } }
130
131       it {
132         is_expected.to contain_apt__setting('conf-proxy').with(priority: '01').with_content(
133           %r{Acquire::http::proxy "http://localhost:8080/";},
134         ).with_content(
135           %r{Acquire::https::proxy "https://localhost:8080/";},
136         )
137       }
138       it {
139         is_expected.to contain_apt__setting('conf-proxy').with(priority: '01').with_content(
140           %r{Acquire::http::proxy "http://localhost:8080/";},
141         ).without_content(
142           %r{Acquire::https::proxy "DIRECT";},
143         )
144       }
145     end
146
147     context 'when ensure=absent' do
148       let(:params) { { proxy: { 'ensure' => 'absent' } } }
149
150       it {
151         is_expected.to contain_apt__setting('conf-proxy').with(ensure: 'absent',
152                                                                priority: '01')
153       }
154     end
155   end
156   context 'with lots of non-defaults' do
157     let :params do
158       {
159         update: { 'frequency' => 'always', 'timeout' => 1, 'tries' => 3 },
160         purge: { 'sources.list' => false, 'sources.list.d' => false,
161                  'preferences' => false, 'preferences.d' => false },
162       }
163     end
164
165     it {
166       is_expected.to contain_file('sources.list').with(content: nil)
167     }
168
169     it {
170       is_expected.to contain_file('sources.list.d').with(purge: false,
171                                                          recurse: false)
172     }
173
174     it {
175       is_expected.to contain_file('preferences').with(ensure: 'file')
176     }
177
178     it {
179       is_expected.to contain_file('preferences.d').with(purge: false,
180                                                         recurse: false)
181     }
182
183     it {
184       is_expected.to contain_exec('apt_update').with(refreshonly: false,
185                                                      timeout: 1,
186                                                      tries: 3)
187     }
188   end
189
190   context 'with entries for /etc/apt/auth.conf' do
191     facts_hash = {
192       'Ubuntu 14.04' => {
193         os: { family: 'Debian', name: 'Ubuntu', release: { major: '14', full: '14.04' } },
194         osfamily: 'Debian',
195         lsbdistcodename: 'trusty',
196         lsbdistid: 'Ubuntu',
197         lsbdistrelease: '14.04',
198       },
199       'Ubuntu 16.04' => {
200         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
201         osfamily: 'Debian',
202         lsbdistcodename: 'xenial',
203         lsbdistid: 'Ubuntu',
204         lsbdistrelease: '16.04',
205       },
206       'Ubuntu 18.04' => {
207         os: { family: 'Debian', name: 'Ubuntu', release: { major: '18', full: '18.04' } },
208         osfamily: 'Debian',
209         lsbdistcodename: 'bionic',
210         lsbdistid: 'Ubuntu',
211         lsbdistrelease: '18.04',
212       },
213       'Debian 7.0' => {
214         os: { family: 'Debian', name: 'Debian', release: { major: '7', full: '7.0' } },
215         lsbdistid: 'Debian',
216         osfamily: 'Debian',
217         lsbdistcodename: 'wheezy',
218       },
219       'Debian 8.0' => {
220         os: { family: 'Debian', name: 'Debian', release: { major: '8', full: '8.0' } },
221         lsbdistid: 'Debian',
222         osfamily: 'Debian',
223         lsbdistcodename: 'jessie',
224       },
225       'Debian 9.0' => {
226         os: { family: 'Debian', name: 'Debian', release: { major: '9', full: '9.0' } },
227         lsbdistid: 'Debian',
228         osfamily: 'Debian',
229         lsbdistcodename: 'stretch',
230       },
231     }
232
233     facts_hash.each do |os, facts|
234       context "on #{os}" do
235         let(:facts) do
236           facts
237         end
238         let(:params) do
239           {
240             auth_conf_entries: [
241               {
242                 machine: 'deb.example.net',
243                 login: 'foologin',
244                 password: 'secret',
245               },
246               {
247                 machine: 'apt.example.com',
248                 login: 'aptlogin',
249                 password: 'supersecret',
250               },
251             ],
252           }
253         end
254
255         context 'with manage_auth_conf => true' do
256           let(:params) do
257             super().merge(manage_auth_conf: true)
258           end
259
260           # Going forward starting with Ubuntu 16.04 and Debian 9.0
261           # /etc/apt/auth.conf is owned by _apt. In previous versions it is
262           # root.
263           auth_conf_owner = case os
264                             when 'Ubuntu 14.04', 'Debian 7.0', 'Debian 8.0'
265                               'root'
266                             else
267                               '_apt'
268                             end
269
270           auth_conf_content = "// This file is managed by Puppet. DO NOT EDIT.
271 machine deb.example.net login foologin password secret
272 machine apt.example.com login aptlogin password supersecret
273 "
274
275           it {
276             is_expected.to contain_file('/etc/apt/auth.conf').with(ensure: 'present',
277                                                                    owner: auth_conf_owner,
278                                                                    group: 'root',
279                                                                    mode: '0600',
280                                                                    notify: 'Class[Apt::Update]',
281                                                                    content: auth_conf_content)
282           }
283         end
284
285         context 'with manage_auth_conf => false' do
286           let(:params) do
287             super().merge(manage_auth_conf: false)
288           end
289
290           it {
291             is_expected.not_to contain_file('/etc/apt/auth.conf')
292           }
293         end
294       end
295
296       context 'with improperly specified entries for /etc/apt/auth.conf' do
297         let(:params) do
298           {
299             auth_conf_entries: [
300               {
301                 machinn: 'deb.example.net',
302                 username: 'foologin',
303                 password: 'secret',
304               },
305               {
306                 machine: 'apt.example.com',
307                 login: 'aptlogin',
308                 password: 'supersecret',
309               },
310             ],
311           }
312         end
313
314         it { is_expected.to raise_error(Puppet::Error) }
315       end
316     end
317   end
318
319   context 'with sources defined on valid osfamily' do
320     let :facts do
321       {
322         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
323         osfamily: 'Debian',
324         lsbdistcodename: 'xenial',
325         lsbdistid: 'Ubuntu',
326         lsbdistrelease: '16.04',
327       }
328     end
329     let(:params) do
330       { sources: {
331         'debian_unstable' => {
332           'location'          => 'http://debian.mirror.iweb.ca/debian/',
333           'release'           => 'unstable',
334           'repos'             => 'main contrib non-free',
335           'key'               => { 'id' => '150C8614919D8446E01E83AF9AA38DCD55BE302B', 'server' => 'subkeys.pgp.net' },
336           'pin'               => '-10',
337           'include'           => { 'src' => true },
338         },
339         'puppetlabs' => {
340           'location' => 'http://apt.puppetlabs.com',
341           'repos'      => 'main',
342           'key'        => { 'id' => '6F6B15509CF8E59E6E469F327F438280EF8D349F', 'server' => 'pgp.mit.edu' },
343         },
344       } }
345     end
346
347     it {
348       is_expected.to contain_apt__setting('list-debian_unstable').with(ensure: 'present')
349     }
350
351     it { is_expected.to contain_file('/etc/apt/sources.list.d/debian_unstable.list').with_content(%r{^deb http://debian.mirror.iweb.ca/debian/ unstable main contrib non-free$}) }
352     it { is_expected.to contain_file('/etc/apt/sources.list.d/debian_unstable.list').with_content(%r{^deb-src http://debian.mirror.iweb.ca/debian/ unstable main contrib non-free$}) }
353
354     it {
355       is_expected.to contain_apt__setting('list-puppetlabs').with(ensure: 'present')
356     }
357
358     it { is_expected.to contain_file('/etc/apt/sources.list.d/puppetlabs.list').with_content(%r{^deb http://apt.puppetlabs.com xenial main$}) }
359   end
360
361   context 'with confs defined on valid osfamily' do
362     let :facts do
363       {
364         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
365         osfamily: 'Debian',
366         lsbdistcodename: 'xenial',
367         lsbdistid: 'Ubuntu',
368       }
369     end
370     let(:params) do
371       { confs: {
372         'foo' => {
373           'content' => 'foo',
374         },
375         'bar' => {
376           'content' => 'bar',
377         },
378       } }
379     end
380
381     it {
382       is_expected.to contain_apt__conf('foo').with(content: 'foo')
383     }
384
385     it {
386       is_expected.to contain_apt__conf('bar').with(content: 'bar')
387     }
388   end
389
390   context 'with keys defined on valid osfamily' do
391     let :facts do
392       {
393         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
394         osfamily: 'Debian',
395         lsbdistcodename: 'xenial',
396         lsbdistid: 'Ubuntu',
397       }
398     end
399     let(:params) do
400       { keys: {
401         '55BE302B' => {
402           'server' => 'subkeys.pgp.net',
403         },
404         'EF8D349F' => {
405           'server' => 'pgp.mit.edu',
406         },
407       } }
408     end
409
410     it {
411       is_expected.to contain_apt__key('55BE302B').with(server: 'subkeys.pgp.net')
412     }
413
414     it {
415       is_expected.to contain_apt__key('EF8D349F').with(server: 'pgp.mit.edu')
416     }
417   end
418
419   context 'with ppas defined on valid osfamily' do
420     let :facts do
421       {
422         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
423         osfamily: 'Debian',
424         lsbdistcodename: 'xenial',
425         lsbdistid: 'Ubuntu',
426         lsbdistrelease: '16.04',
427       }
428     end
429     let(:params) do
430       { ppas: {
431         'ppa:drizzle-developers/ppa' => {},
432         'ppa:nginx/stable' => {},
433       } }
434     end
435
436     it { is_expected.to contain_apt__ppa('ppa:drizzle-developers/ppa') }
437     it { is_expected.to contain_apt__ppa('ppa:nginx/stable') }
438   end
439
440   context 'with settings defined on valid osfamily' do
441     let :facts do
442       {
443         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
444         osfamily: 'Debian',
445         lsbdistcodename: 'xenial',
446         lsbdistid: 'Ubuntu',
447       }
448     end
449     let(:params) do
450       { settings: {
451         'conf-banana' => { 'content' => 'banana' },
452         'pref-banana' => { 'content' => 'banana' },
453       } }
454     end
455
456     it { is_expected.to contain_apt__setting('conf-banana') }
457     it { is_expected.to contain_apt__setting('pref-banana') }
458   end
459
460   context 'with pins defined on valid osfamily' do
461     let :facts do
462       {
463         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
464         osfamily: 'Debian',
465         lsbdistcodename: 'xenial',
466         lsbdistid: 'Ubuntu',
467       }
468     end
469     let(:params) do
470       { pins: {
471         'stable' => { 'priority' => 600, 'order' => 50 },
472         'testing' =>  { 'priority' => 700, 'order' => 100 },
473       } }
474     end
475
476     it { is_expected.to contain_apt__pin('stable') }
477     it { is_expected.to contain_apt__pin('testing') }
478   end
479
480   describe 'failing tests' do
481     context "with purge['sources.list']=>'banana'" do
482       let(:params) { { purge: { 'sources.list' => 'banana' } } }
483
484       it do
485         is_expected.to raise_error(Puppet::Error)
486       end
487     end
488
489     context "with purge['sources.list.d']=>'banana'" do
490       let(:params) { { purge: { 'sources.list.d' => 'banana' } } }
491
492       it do
493         is_expected.to raise_error(Puppet::Error)
494       end
495     end
496
497     context "with purge['preferences']=>'banana'" do
498       let(:params) { { purge: { 'preferences' => 'banana' } } }
499
500       it do
501         is_expected.to raise_error(Puppet::Error)
502       end
503     end
504
505     context "with purge['preferences.d']=>'banana'" do
506       let(:params) { { purge: { 'preferences.d' => 'banana' } } }
507
508       it do
509         is_expected.to raise_error(Puppet::Error)
510       end
511     end
512   end
513 end