diff --git a/manifests/init.pp b/manifests/init.pp index 6c37745..79fe4d0 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,81 +1,82 @@ class unattended_upgrades ( Unattended_upgrades::Age $age = {}, Unattended_upgrades::Auto $auto = {}, Unattended_upgrades::Backup $backup = {}, Array[String[1]] $blacklist = [], + Array[String[1]] $whitelist = [], Optional[Integer[0]] $dl_limit = undef, Integer[0, 1] $enable = 1, Boolean $install_on_shutdown = false, Boolean $legacy_origin = $unattended_upgrades::params::legacy_origin, Unattended_upgrades::Mail $mail = {}, Boolean $minimal_steps = true, Array[String[1]] $origins = $unattended_upgrades::params::origins, String[1] $package_ensure = installed, Array[String[1]] $extra_origins = [], Optional[Integer[0]] $random_sleep = undef, Optional[String] $sender = undef, Integer[0] $size = 0, Integer[0] $update = 1, Integer[0] $upgrade = 1, Unattended_upgrades::Upgradeable_packages $upgradeable_packages = {}, Integer[0] $verbose = 0, Boolean $notify_update = false, Unattended_upgrades::Options $options = {}, Array[String[1]] $days = [], Optional[Boolean] $remove_unused_kernel = undef, Optional[Boolean] $remove_new_unused_deps = undef, Optional[Boolean] $syslog_enable = undef, Optional[String] $syslog_facility = undef, ) inherits unattended_upgrades::params { # apt::conf settings require the apt class to work include apt $_age = merge($unattended_upgrades::default_age, $age) assert_type(Unattended_upgrades::Age, $_age) $_auto = merge($unattended_upgrades::default_auto, $auto) assert_type(Unattended_upgrades::Auto, $_auto) $_backup = merge($unattended_upgrades::default_backup, $backup) assert_type(Unattended_upgrades::Backup, $_backup) $_mail = merge($unattended_upgrades::default_mail, $mail) assert_type(Unattended_upgrades::Mail, $_mail) $_upgradeable_packages = merge($unattended_upgrades::default_upgradeable_packages, $upgradeable_packages) assert_type(Unattended_upgrades::Upgradeable_packages, $_upgradeable_packages) $_options = merge($unattended_upgrades::default_options, $options) assert_type(Unattended_upgrades::Options, $_options) package { 'unattended-upgrades': ensure => $package_ensure, } apt::conf { 'unattended-upgrades': priority => 50, content => template("${module_name}/unattended-upgrades.erb"), require => Package['unattended-upgrades'], notify_update => $notify_update, } apt::conf { 'periodic': priority => 10, content => template("${module_name}/periodic.erb"), require => Package['unattended-upgrades'], notify_update => $notify_update, } apt::conf { 'auto-upgrades': ensure => absent, priority => 20, require => Package['unattended-upgrades'], notify_update => $notify_update, } apt::conf { 'options': priority => 10, content => template("${module_name}/options.erb"), require => Package['unattended-upgrades'], notify_update => $notify_update, } } diff --git a/spec/classes/unattended_upgrades_spec.rb b/spec/classes/unattended_upgrades_spec.rb index 501716f..1909330 100644 --- a/spec/classes/unattended_upgrades_spec.rb +++ b/spec/classes/unattended_upgrades_spec.rb @@ -1,391 +1,394 @@ require 'spec_helper' # rubocop:disable Style/RegexpLiteral describe 'unattended_upgrades' do let(:file_unattended) { '/etc/apt/apt.conf.d/50unattended-upgrades' } let(:file_periodic) { '/etc/apt/apt.conf.d/10periodic' } let(:file_options) { '/etc/apt/apt.conf.d/10options' } shared_examples 'basic specs' do let(:params) { {} } context 'baseline specs' do it { is_expected.to compile.with_all_deps } it do is_expected.to contain_package('unattended-upgrades') is_expected.to compile.with_all_deps is_expected.to contain_class('unattended_upgrades::params') is_expected.to contain_class('unattended_upgrades') is_expected.to contain_class('apt') end it do is_expected.to contain_apt__conf('unattended-upgrades').with( require: 'Package[unattended-upgrades]', notify_update: false ) end it do is_expected.to contain_apt__conf('periodic').with( require: 'Package[unattended-upgrades]', notify_update: false ) end it do is_expected.to contain_apt__conf('options').with( require: 'Package[unattended-upgrades]', notify_update: false ) end it { is_expected.to create_file(file_unattended).without_content(/Unattended-Upgrade::Sender/) } end context 'set all the things' do let :params do { age: { 'min' => 1, 'max' => 20 }, size: 1000, update: 5, upgradeable_packages: { 'download_only' => 5, 'debdelta' => 5 }, upgrade: 5, days: %w[tuesday Thursday 5], auto: { 'clean' => 5, 'fix_interrupted_dpkg' => false, 'remove' => false, 'reboot' => true, 'reboot_time' => '03:00' }, verbose: 1, legacy_origin: true, origins: %w[bananas], blacklist: %w[foo bar], + whitelist: %w[foo bar], minimal_steps: false, install_on_shutdown: true, mail: { 'to' => 'root@localhost', 'only_on_error' => true, 'report' => 'on-change' }, sender: 'root@server.example.com', dl_limit: 70, random_sleep: 300, notify_update: true, options: { 'force_confdef' => false, 'force_confold' => false, 'force_confnew' => true, 'force_confmiss' => true }, remove_new_unused_deps: false, syslog_enable: true, syslog_facility: 'daemon' } end it { is_expected.to contain_package('unattended-upgrades') } it do is_expected.to contain_apt__conf('unattended-upgrades').with( require: 'Package[unattended-upgrades]', notify_update: true ) end it do is_expected.to contain_apt__conf('periodic').with( require: 'Package[unattended-upgrades]', notify_update: true ) end it do is_expected.to contain_apt__conf('options').with( require: 'Package[unattended-upgrades]', notify_update: true ) end it do is_expected.to create_file(file_unattended).with( owner: 'root', group: 'root' ).with_content( /Unattended-Upgrade::Allowed-Origins {\n\t"bananas";\n};/ ).with_content( /Unattended-Upgrade::Package-Blacklist {\n\t"foo";\n\t"bar";\n};/ + ).with_content( + /Unattended-Upgrade::Package-Whitelist {\n\t"foo";\n\t"bar";\n};\n/ ).with_content( /Unattended-Upgrade::Update-Days {\n\t"Tuesday";\n\t"Thursday";\n\t"5";\n};/ ).with_content( /Unattended-Upgrade::AutoFixInterruptedDpkg "false";/ ).with_content( /Unattended-Upgrade::MinimalSteps "false";/ ).with_content( /Unattended-Upgrade::InstallOnShutdown "true";/ ).with_content( /Unattended-Upgrade::Remove-Unused-Dependencies "false";/ ).with_content( /Unattended-Upgrade::Automatic-Reboot "true";/ ).with_content( /Unattended-Upgrade::Automatic-Reboot-Time "03:00";/ ).with_content( /Unattended-Upgrade::Mail "root@localhost";/ ).with_content( /Unattended-Upgrade::Sender "root@server.example.com";/ ).with_content( /Unattended-Upgrade::MailOnlyOnError "true";/ ).with_content( /Unattended-Upgrade::MailReport "on-change";/ ).with_content( /Acquire::http::Dl-Limit "70";/ ).with_content( /Unattended-Upgrade::Remove-New-Unused-Dependencies "false";/ ).without_content( /Unattended-Upgrade::Remove-Unused-Kernel-Packages/ ).with_content( /Unattended-Upgrade::SyslogEnable "true";/ ).with_content( /Unattended-Upgrade::SyslogFacility "daemon";/ ) end it do is_expected.to create_file(file_periodic).with( owner: 'root', group: 'root' ).with_content( /APT::Periodic::Enable "1";/ ).with_content( /APT::Periodic::BackupArchiveInterval "0";/ ).with_content( /APT::Periodic::BackupLevel "3";/ ).with_content( /APT::Periodic::MaxAge "20";/ ).with_content( /APT::Periodic::MinAge "1";/ ).with_content( /APT::Periodic::MaxSize "1000";/ ).with_content( /APT::Periodic::Update-Package-Lists "5";/ ).with_content( /APT::Periodic::Download-Upgradeable-Packages "5";/ ).with_content( /APT::Periodic::Download-Upgradeable-Packages-Debdelta "5";/ ).with_content( /APT::Periodic::Unattended-Upgrade "5";/ ).with_content( /APT::Periodic::AutocleanInterval "5";/ ).with_content( /APT::Periodic::Verbose "1";/ ).with_content( /APT::Periodic::RandomSleep "300";/ ) end it do is_expected.to create_file(file_options).with( owner: 'root', group: 'root' ).with_content( /^Dpkg::Options\s{/ ).without_content( /"--force-confdef";/ ).without_content( /"--force-confold";/ ).with_content( /^\s+"--force-confnew";/ ).with_content( /^\s+"--force-confmiss";/ ) end it do is_expected.to contain_apt__conf('auto-upgrades').with( ensure: 'absent' ) end end describe 'validation tests' do context 'bad install_on_shutdown' do let :params do { install_on_shutdown: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad days' do let :params do { days: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad legacy_origin' do let :params do { legacy_origin: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad minimal_steps' do let :params do { minimal_steps: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad blacklist' do let :params do { blacklist: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad origins' do let :params do { origins: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad auto' do let :params do { auto: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad mail' do let :params do { mail: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad backup' do let :params do { backup: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad age' do let :params do { age: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad size' do let :params do { size: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad upgradeable_packages' do let :params do { upgradeable_packages: 'foo' } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad mail[\'only_on_error\']' do let :params do { mail: { 'only_on_error' => 'foo' } } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad options[\'force_confdef\']' do let :params do { options: { 'force_confdef' => 'foo' } } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad options[\'force_confold\']' do let :params do { options: { 'force_confold' => 'foo' } } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad options[\'force_confnew\']' do let :params do { options: { 'force_confnew' => 'foo' } } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad options[\'force_confmiss\']' do let :params do { options: { 'force_confmiss' => 'foo' } } end it { is_expected.to compile.and_raise_error(/got String/) } end context 'bad options[\'invalid_key\']' do let :params do { options: { 'invalid_key' => true } } end it { is_expected.to compile.and_raise_error(/unrecognized key 'invalid_key'/) } end end end on_supported_os.each do |os, facts| context "on #{os}" do let(:facts) do facts.merge(fqdn: 'unattended-upgrades.example.com', path: '/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/puppetlabs/bin:/root/bin') end it_behaves_like 'basic specs' end end end diff --git a/templates/unattended-upgrades.erb b/templates/unattended-upgrades.erb index 621fef5..a325799 100644 --- a/templates/unattended-upgrades.erb +++ b/templates/unattended-upgrades.erb @@ -1,119 +1,128 @@ // Automatically upgrade packages from these (origin:archive) pairs // // Note that in Ubuntu security updates may pull in new dependencies // from non-security sources (e.g. chromium). By allowing the release // pocket these get automatically pulled in. <%- if @legacy_origin -%> Unattended-Upgrade::Allowed-Origins { <%- else -%> Unattended-Upgrade::Origins-Pattern { <%- end -%> <% @origins.each do |origin| -%> "<%= origin %>"; <% end -%> <% @extra_origins.each do |origin| -%> "<%= origin %>"; <% end -%> }; // List of packages to not update (regexp are supported) Unattended-Upgrade::Package-Blacklist { <% @blacklist.each do |package| -%> "<%= package %>"; <% end -%> }; +<% unless @whitelist.empty? -%> +// List of pacakages to only update +Unattended-Upgrade::Package-Whitelist { +<% @whitelist.each do |package| -%> + "<%= package %>"; +<% end -%> +}; + +<% end -%> // List of days in the week that updates should be applied. // The days can be specified as localized abbreviated or full names. // Or as integers where "0" is Sunday, "1" is Monday etc. // Require Unattended-upgrades version >=0.91 to work, else it is ignored Unattended-Upgrade::Update-Days { <% @days.each do |day| -%> "<%= day.capitalize %>"; <% end -%> }; // This option allows you to control if on a unclean dpkg exit // unattended-upgrades will automatically run // dpkg --force-confold --configure -a // The default is true, to ensure updates keep getting installed Unattended-Upgrade::AutoFixInterruptedDpkg "<%= @_auto['fix_interrupted_dpkg'].to_s %>"; // Split the upgrade into the smallest possible chunks so that // they can be interrupted with SIGTERM. This makes the upgrade // a bit slower but it has the benefit that shutdown while a upgrade // is running is possible (with a small delay) Unattended-Upgrade::MinimalSteps "<%= @minimal_steps.to_s %>"; // Install all unattended-upgrades when the machine is shuting down // instead of doing it in the background while the machine is running // This will (obviously) make shutdown slower Unattended-Upgrade::InstallOnShutdown "<%= @install_on_shutdown.to_s %>"; <%- unless @_mail['to'].nil? -%> // Send email to this address for problems or packages upgrades // If empty or unset then no email is sent, make sure that you // have a working mail setup on your system. A package that provides // 'mailx' must be installed. E.g. "user@example.com" Unattended-Upgrade::Mail "<%= @_mail['to'] %>"; <%- if @_mail['only_on_error'] -%> // Set this value to "true" to get emails only on errors. Default // is to always send a mail if Unattended-Upgrade::Mail is set Unattended-Upgrade::MailOnlyOnError "<%= @_mail['only_on_error'].to_s %>"; <%- end -%> <%- if @_mail['report'] -%> // Set this value to one of: // "always", "only-on-error" or "on-change" // If this is not set, then any legacy MailOnlyOnError (boolean) value // is used to chose between "only-on-error" and "on-change" Unattended-Upgrade::MailReport "<%= @_mail['report'] %>"; <%- end -%> <%- end -%> <%- if @sender -%> // Use the specified value in the "From" field of outgoing mails. // Defaults to "root" Unattended-Upgrade::Sender "<%= @sender %>"; <%- end -%> <%- unless @remove_unused_kernel.nil? -%> // Remove unused automatically installed kernel-related packages // (kernel images, kernel headers and kernel version locked tools). Unattended-Upgrade::Remove-Unused-Kernel-Packages "<%= @remove_unused_kernel %>"; <%- end -%> <%- unless @remove_new_unused_deps.nil? -%> // Do automatic removal of newly unused dependencies after the upgrade Unattended-Upgrade::Remove-New-Unused-Dependencies "<%= @remove_new_unused_deps %>"; <%- end -%> // Do automatic removal of new unused dependencies after the upgrade // (equivalent to apt-get autoremove) Unattended-Upgrade::Remove-Unused-Dependencies "<%= @_auto['remove'].to_s %>"; // Automatically reboot *WITHOUT CONFIRMATION* // if the file /var/run/reboot-required is found after the upgrade Unattended-Upgrade::Automatic-Reboot "<%= @_auto['reboot'].to_s %>"; // If automatic reboot is enabled and needed, reboot at the specific // time instead of immediately // Default: "now" Unattended-Upgrade::Automatic-Reboot-Time "<%= @_auto['reboot_time'].to_s %>"; <%- unless @dl_limit.nil? -%> // Use apt bandwidth limit feature, this example limits the download // speed to 70kb/sec Acquire::http::Dl-Limit "<%= @dl_limit %>"; <%- end -%> <%- unless @syslog_enable.nil? -%> // Enable logging to syslog. Default is False Unattended-Upgrade::SyslogEnable "<%= @syslog_enable %>"; <%- end -%> <%- unless @syslog_facility.nil? -%> // Specify syslog facility. Default is daemon Unattended-Upgrade::SyslogFacility "<%= @syslog_facility %>"; <%- end -%>