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