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