Update unit test to support Sensitive data type
[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 lots of non-defaults' do
204     let :params do
205       {
206         update: { 'frequency' => 'always', 'timeout' => 1, 'tries' => 3 },
207         purge: { 'sources.list' => true, 'sources.list.d' => true,
208                  'preferences' => true, 'preferences.d' => true,
209                  'apt.conf.d' => true },
210       }
211     end
212
213     it {
214       is_expected.to contain_file('sources.list').with(content: "# Repos managed by puppet.\n")
215     }
216
217     it {
218       is_expected.to contain_file('sources.list.d').with(purge: true,
219                                                          recurse: true)
220     }
221
222     it {
223       is_expected.to contain_file('preferences').with(ensure: 'absent')
224     }
225
226     it {
227       is_expected.to contain_file('preferences.d').with(purge: true,
228                                                         recurse: true)
229     }
230
231     it {
232       is_expected.to contain_file('apt.conf.d').with(purge: true,
233                                                      recurse: true)
234     }
235
236     it {
237       is_expected.to contain_exec('apt_update').with(refreshonly: false,
238                                                      timeout: 1,
239                                                      tries: 3)
240     }
241   end
242
243   context 'with defaults for sources_list_force' do
244     let :params do
245       {
246         update: { 'frequency' => 'always', 'timeout' => 1, 'tries' => 3 },
247         purge: { 'sources.list' => true },
248         sources_list_force: false,
249       }
250     end
251
252     it {
253       is_expected.to contain_file('sources.list').with(content: "# Repos managed by puppet.\n")
254     }
255   end
256
257   context 'with non defaults for sources_list_force' do
258     let :params do
259       {
260         update: { 'frequency' => 'always', 'timeout' => 1, 'tries' => 3 },
261         purge: { 'sources.list' => true },
262         sources_list_force: true,
263       }
264     end
265
266     it {
267       is_expected.to contain_file('sources.list').with(ensure: 'absent')
268     }
269   end
270
271   context 'with entries for /etc/apt/auth.conf' do
272     facts_hash = {
273       'Ubuntu 14.04' => {
274         os: { family: 'Debian', name: 'Ubuntu', release: { major: '14', full: '14.04' } },
275         osfamily: 'Debian',
276         lsbdistcodename: 'trusty',
277         lsbdistid: 'Ubuntu',
278         lsbdistrelease: '14.04',
279       },
280       'Ubuntu 16.04' => {
281         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
282         osfamily: 'Debian',
283         lsbdistcodename: 'xenial',
284         lsbdistid: 'Ubuntu',
285         lsbdistrelease: '16.04',
286       },
287       'Ubuntu 18.04' => {
288         os: { family: 'Debian', name: 'Ubuntu', release: { major: '18', full: '18.04' } },
289         osfamily: 'Debian',
290         lsbdistcodename: 'bionic',
291         lsbdistid: 'Ubuntu',
292         lsbdistrelease: '18.04',
293       },
294       'Debian 7.0' => {
295         os: { family: 'Debian', name: 'Debian', release: { major: '7', full: '7.0' } },
296         lsbdistid: 'Debian',
297         osfamily: 'Debian',
298         lsbdistcodename: 'wheezy',
299       },
300       'Debian 8.0' => {
301         os: { family: 'Debian', name: 'Debian', release: { major: '8', full: '8.0' } },
302         lsbdistid: 'Debian',
303         osfamily: 'Debian',
304         lsbdistcodename: 'jessie',
305       },
306       'Debian 9.0' => {
307         os: { family: 'Debian', name: 'Debian', release: { major: '9', full: '9.0' } },
308         lsbdistid: 'Debian',
309         osfamily: 'Debian',
310         lsbdistcodename: 'stretch',
311       },
312       'Debian 10.0' => {
313         os: { family: 'Debian', name: 'Debian', release: { major: '10', full: '10.0' } },
314         lsbdistid: 'Debian',
315         osfamily: 'Debian',
316         lsbdistcodename: 'buster',
317       },
318     }
319
320     facts_hash.each do |os, facts|
321       context "on #{os}" do
322         let(:facts) do
323           facts
324         end
325         let(:params) do
326           {
327             auth_conf_entries: [
328               {
329                 machine: 'deb.example.net',
330                 login: 'foologin',
331                 password: 'secret',
332               },
333               {
334                 machine: 'apt.example.com',
335                 login: 'aptlogin',
336                 password: 'supersecret',
337               },
338             ],
339           }
340         end
341
342         context 'with manage_auth_conf => true' do
343           let(:params) do
344             super().merge(manage_auth_conf: true)
345           end
346
347           # Going forward starting with Ubuntu 16.04 and Debian 9.0
348           # /etc/apt/auth.conf is owned by _apt. In previous versions it is
349           # root.
350           auth_conf_owner = case os
351                             when 'Ubuntu 14.04', 'Debian 7.0', 'Debian 8.0'
352                               'root'
353                             else
354                               '_apt'
355                             end
356
357           auth_conf_content = "// This file is managed by Puppet. DO NOT EDIT.
358 machine deb.example.net login foologin password secret
359 machine apt.example.com login aptlogin password supersecret
360 "
361
362           it {
363             is_expected.to contain_file('/etc/apt/auth.conf').with(ensure: 'present',
364                                                                    owner: auth_conf_owner,
365                                                                    group: 'root',
366                                                                    mode: '0600',
367                                                                    notify: 'Class[Apt::Update]',
368                                                                    content: sensitive(auth_conf_content))
369           }
370         end
371
372         context 'with manage_auth_conf => false' do
373           let(:params) do
374             super().merge(manage_auth_conf: false)
375           end
376
377           it {
378             is_expected.not_to contain_file('/etc/apt/auth.conf')
379           }
380         end
381       end
382
383       context 'with improperly specified entries for /etc/apt/auth.conf' do
384         let(:params) do
385           {
386             auth_conf_entries: [
387               {
388                 machinn: 'deb.example.net',
389                 username: 'foologin',
390                 password: 'secret',
391               },
392               {
393                 machine: 'apt.example.com',
394                 login: 'aptlogin',
395                 password: 'supersecret',
396               },
397             ],
398           }
399         end
400
401         it { is_expected.to raise_error(Puppet::Error) }
402       end
403     end
404   end
405
406   context 'with sources defined on valid osfamily' do
407     let :facts do
408       {
409         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
410         osfamily: 'Debian',
411         lsbdistcodename: 'xenial',
412         lsbdistid: 'Ubuntu',
413         lsbdistrelease: '16.04',
414       }
415     end
416     let(:params) do
417       { sources: {
418         'debian_unstable' => {
419           'location'          => 'http://debian.mirror.iweb.ca/debian/',
420           'release'           => 'unstable',
421           'repos'             => 'main contrib non-free',
422           'key'               => { 'id' => '150C8614919D8446E01E83AF9AA38DCD55BE302B', 'server' => 'subkeys.pgp.net' },
423           'pin'               => '-10',
424           'include'           => { 'src' => true },
425         },
426         'puppetlabs' => {
427           'location' => 'http://apt.puppetlabs.com',
428           'repos'      => 'main',
429           'key'        => { 'id' => '6F6B15509CF8E59E6E469F327F438280EF8D349F', 'server' => 'pgp.mit.edu' },
430         },
431       } }
432     end
433
434     it {
435       is_expected.to contain_apt__setting('list-debian_unstable').with(ensure: 'present')
436     }
437
438     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$}) }
439     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$}) }
440
441     it {
442       is_expected.to contain_apt__setting('list-puppetlabs').with(ensure: 'present')
443     }
444
445     it { is_expected.to contain_file('/etc/apt/sources.list.d/puppetlabs.list').with_content(%r{^deb http://apt.puppetlabs.com xenial main$}) }
446   end
447
448   context 'with confs defined on valid osfamily' do
449     let :facts do
450       {
451         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
452         osfamily: 'Debian',
453         lsbdistcodename: 'xenial',
454         lsbdistid: 'Ubuntu',
455       }
456     end
457     let(:params) do
458       { confs: {
459         'foo' => {
460           'content' => 'foo',
461         },
462         'bar' => {
463           'content' => 'bar',
464         },
465       } }
466     end
467
468     it {
469       is_expected.to contain_apt__conf('foo').with(content: 'foo')
470     }
471
472     it {
473       is_expected.to contain_apt__conf('bar').with(content: 'bar')
474     }
475   end
476
477   context 'with keys defined on valid osfamily' do
478     let :facts do
479       {
480         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
481         osfamily: 'Debian',
482         lsbdistcodename: 'xenial',
483         lsbdistid: 'Ubuntu',
484       }
485     end
486     let(:params) do
487       { keys: {
488         '55BE302B' => {
489           'server' => 'subkeys.pgp.net',
490         },
491         'EF8D349F' => {
492           'server' => 'pgp.mit.edu',
493         },
494       } }
495     end
496
497     it {
498       is_expected.to contain_apt__key('55BE302B').with(server: 'subkeys.pgp.net')
499     }
500
501     it {
502       is_expected.to contain_apt__key('EF8D349F').with(server: 'pgp.mit.edu')
503     }
504   end
505
506   context 'with ppas defined on valid osfamily' do
507     let :facts do
508       {
509         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
510         osfamily: 'Debian',
511         lsbdistcodename: 'xenial',
512         lsbdistid: 'Ubuntu',
513         lsbdistrelease: '16.04',
514       }
515     end
516     let(:params) do
517       { ppas: {
518         'ppa:drizzle-developers/ppa' => {},
519         'ppa:nginx/stable' => {},
520       } }
521     end
522
523     it { is_expected.to contain_apt__ppa('ppa:drizzle-developers/ppa') }
524     it { is_expected.to contain_apt__ppa('ppa:nginx/stable') }
525   end
526
527   context 'with settings defined on valid osfamily' do
528     let :facts do
529       {
530         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
531         osfamily: 'Debian',
532         lsbdistcodename: 'xenial',
533         lsbdistid: 'Ubuntu',
534       }
535     end
536     let(:params) do
537       { settings: {
538         'conf-banana' => { 'content' => 'banana' },
539         'pref-banana' => { 'content' => 'banana' },
540       } }
541     end
542
543     it { is_expected.to contain_apt__setting('conf-banana') }
544     it { is_expected.to contain_apt__setting('pref-banana') }
545   end
546
547   context 'with pins defined on valid osfamily' do
548     let :facts do
549       {
550         os: { family: 'Debian', name: 'Ubuntu', release: { major: '16', full: '16.04' } },
551         osfamily: 'Debian',
552         lsbdistcodename: 'xenial',
553         lsbdistid: 'Ubuntu',
554       }
555     end
556     let(:params) do
557       { pins: {
558         'stable' => { 'priority' => 600, 'order' => 50 },
559         'testing' =>  { 'priority' => 700, 'order' => 100 },
560       } }
561     end
562
563     it { is_expected.to contain_apt__pin('stable') }
564     it { is_expected.to contain_apt__pin('testing') }
565   end
566
567   describe 'failing tests' do
568     context "with purge['sources.list']=>'banana'" do
569       let(:params) { { purge: { 'sources.list' => 'banana' } } }
570
571       it do
572         is_expected.to raise_error(Puppet::Error)
573       end
574     end
575
576     context "with purge['sources.list.d']=>'banana'" do
577       let(:params) { { purge: { 'sources.list.d' => 'banana' } } }
578
579       it do
580         is_expected.to raise_error(Puppet::Error)
581       end
582     end
583
584     context "with purge['preferences']=>'banana'" do
585       let(:params) { { purge: { 'preferences' => 'banana' } } }
586
587       it do
588         is_expected.to raise_error(Puppet::Error)
589       end
590     end
591
592     context "with purge['preferences.d']=>'banana'" do
593       let(:params) { { purge: { 'preferences.d' => 'banana' } } }
594
595       it do
596         is_expected.to raise_error(Puppet::Error)
597       end
598     end
599
600     context "with purge['apt.conf.d']=>'banana'" do
601       let(:params) { { purge: { 'apt.conf.d' => 'banana' } } }
602
603       it do
604         is_expected.to raise_error(Puppet::Error)
605       end
606     end
607   end
608 end