diff --git a/Gemfile b/Gemfile index fa2713dbf..c71f0b28d 100644 --- a/Gemfile +++ b/Gemfile @@ -40,7 +40,7 @@ group :development do gem "rake", '~> 12', require: false gem "parallel_tests", '>= 2.14.1', '< 2.14.3', require: false gem "metadata-json-lint", '>= 2.0.2', '< 3.0.0', require: false - gem "rspec-puppet-facts", '~> 1.10.0', require: false + gem "rspec-puppet-facts", '~> 2.0.1', require: false gem "rspec_junit_formatter", '~> 0.2', require: false gem "rubocop", '~> 0.49.0', require: false gem "rubocop-rspec", '~> 1.16.0', require: false @@ -48,7 +48,7 @@ group :development do gem "puppetlabs_spec_helper", '>= 2.9.0', '< 3.0.0', require: false gem "puppet-module-posix-default-r#{minor_version}", require: false, platforms: "ruby" gem "puppet-module-win-default-r#{minor_version}", require: false, platforms: ["mswin", "mingw", "x64_mingw"] - gem "rspec-puppet", require: true, git: "https://github.com/rodjek/rspec-puppet", tag: "v2.7.9" + gem "rspec-puppet", require: true gem 'rspec-expectations', '~> 3.9.0', require: false gem "json_pure", '<= 2.0.1', require: false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0') gem "fast_gettext", '1.1.0', require: false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0') diff --git a/README.markdown b/README.markdown index 0a32c2950..a9b54b04e 100644 --- a/README.markdown +++ b/README.markdown @@ -72,7 +72,7 @@ Previous releases of this module, now unsupported, upgraded agents from later ve The puppet_agent module installs the appropriate official Puppet package repository (on systems that support repositories); migrates configuration required by Puppet to new locations used by puppet-agent; and installs the puppet-agent package, removing the previous Puppet installation. -If a package_version parameter is provided, it will ensure that puppet-agent version is installed. The package_version parameter is required to perform upgrades starting from a puppet-agent package, also this parameter can be set to "auto", ensuring that agent version matches the version on the master without having to manually update package_version after upgrading the master(s). +If a package_version parameter is provided, it will ensure that puppet-agent version is installed. The package_version parameter is required to perform upgrades starting from a puppet-agent package, also this parameter can be set to "auto", ensuring that agent version matches the version on the master without having to manually update package_version after upgrading the master(s). On platforms that install packages through repos (EL, Fedora, Debian, Ubuntu, SLES), the parameter can be set to "latest" in order to install the latest available package. To only ensure the presence of the package, the parameter can be set to "present". If a config parameter is provided, it will manage the defined agent configuration settings. @@ -191,6 +191,14 @@ or ``` puppet package_version => 'auto' ``` +or +``` puppet + package_version => 'latest' +``` +or +``` puppet + package_version => 'present' +``` ##### `service_names` diff --git a/acceptance/tests/test_upgrade_puppet5_to_puppet6.rb b/acceptance/tests/test_upgrade_puppet5_to_puppet6.rb index 36f84086a..83f4c4ca2 100644 --- a/acceptance/tests/test_upgrade_puppet5_to_puppet6.rb +++ b/acceptance/tests/test_upgrade_puppet5_to_puppet6.rb @@ -13,8 +13,14 @@ step "Create new site.pp with upgrade manifest" do manifest = <<-PP node default { + if $::osfamily =~ /^(?i:windows|solaris|aix|darwin)$/ { + $_package_version = '#{latest_version}' + } else { + $_package_version = 'latest' + } + class { puppet_agent: - package_version => '#{latest_version}', + package_version => $_package_version, apt_source => 'http://nightlies.puppet.com/apt', yum_source => 'http://nightlies.puppet.com/yum', windows_source => 'http://nightlies.puppet.com/downloads', diff --git a/acceptance/tests/test_upgrade_puppet6_to_puppet7.rb b/acceptance/tests/test_upgrade_puppet6_to_puppet7.rb index d0c04eb9f..20501c72f 100644 --- a/acceptance/tests/test_upgrade_puppet6_to_puppet7.rb +++ b/acceptance/tests/test_upgrade_puppet6_to_puppet7.rb @@ -13,8 +13,14 @@ step "Create new site.pp with upgrade manifest" do manifest = <<-PP node default { + if $::osfamily =~ /^(?i:windows|solaris|aix|darwin)$/ { + $_package_version = '#{latest_version}' + } else { + $_package_version = 'latest' + } + class { puppet_agent: - package_version => '#{latest_version}', + package_version => $_package_version, apt_source => 'http://nightlies.puppet.com/apt', yum_source => 'http://nightlies.puppet.com/yum', windows_source => 'http://nightlies.puppet.com/downloads', diff --git a/lib/puppet/provider/puppet_agent_end_run/puppet_agent_end_run.rb b/lib/puppet/provider/puppet_agent_end_run/puppet_agent_end_run.rb index dec6a8932..68c0f6092 100644 --- a/lib/puppet/provider/puppet_agent_end_run/puppet_agent_end_run.rb +++ b/lib/puppet/provider/puppet_agent_end_run/puppet_agent_end_run.rb @@ -27,6 +27,13 @@ def needs_upgrade? current_version = Facter.value('aio_agent_version') desired_version = @resource.name + return false if desired_version == 'present' + + if desired_version == 'latest' + latest_version = @resource.catalog.resource('package', 'puppet-agent').parameters[:ensure].latest + desired_version = latest_version.match(%r{^(?:[0-9]:)?(\d+\.\d+(\.\d+)?(?:\.\d+))?}).captures.first + end + Puppet::Util::Package.versioncmp(desired_version, current_version) != 0 end end diff --git a/manifests/init.pp b/manifests/init.pp index e29e927bf..3c49ec506 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -139,6 +139,10 @@ fail('The puppet_agent module does not support pre-Puppet 4 upgrades.') } + if $package_version == 'latest' and $::osfamily =~ /^(?i:windows|solaris|aix|darwin)$/ { + fail("Setting package_version to 'latest' is not supported on ${::osfamily.capitalize()}") + } + if $source != undef and $absolute_source != undef { fail('Only one of $source and $absolute_source can be set') } @@ -166,12 +170,11 @@ } if $::osfamily == 'redhat' { - if $master_or_package_version !~ /^\d+\.\d+\.\d+.*$/ { + if $master_or_package_version !~ /^\d+\.\d+\.\d+.*$|^latest$|^present$/ { fail("invalid version ${master_or_package_version} requested") } - } - else { - if $master_or_package_version !~ /^\d+\.\d+\.\d+([.-]?\d*|\.\d+\.g[0-9a-f]+)$/ { + } else { + if $master_or_package_version !~ /^\d+\.\d+\.\d+([.-]?\d*|\.\d+\.g[0-9a-f]+)$|^latest$|^present$/ { fail("invalid version ${master_or_package_version} requested") } } @@ -180,13 +183,21 @@ if $master_or_package_version =~ /.g/ { $_expected_package_version = split($master_or_package_version, /[.-]g.*/)[0] } elsif $::osfamily == 'redhat' { - $_expected_package_version = $master_or_package_version.match(/^\d+\.\d+\.\d+/)[0] + $_expected_package_version = $master_or_package_version.match(/^\d+\.\d+\.\d+|^latest$|^present$/)[0] } else { $_expected_package_version = $master_or_package_version } - $aio_upgrade_required = versioncmp($::aio_agent_version, $_expected_package_version) < 0 - $aio_downgrade_required = versioncmp($::aio_agent_version, $_expected_package_version) > 0 + if $_expected_package_version == 'latest' { + $aio_upgrade_required = true + $aio_downgrade_required = false + } elsif $_expected_package_version == 'present' { + $aio_upgrade_required = false + $aio_downgrade_required = false + } else { + $aio_upgrade_required = versioncmp($::aio_agent_version, $_expected_package_version) < 0 + $aio_downgrade_required = versioncmp($::aio_agent_version, $_expected_package_version) > 0 + } if $aio_upgrade_required { if any_resources_of_type('filebucket', { path => false }) { diff --git a/manifests/install.pp b/manifests/install.pp index 252fa9726..b0fcfa93b 100644 --- a/manifests/install.pp +++ b/manifests/install.pp @@ -43,7 +43,7 @@ } } elsif $::osfamily == 'suse' { # Prevent re-running the batch install - if ($puppet_agent::aio_upgrade_required) or ($puppet_agent::aio_downgrade_required){ + if ($package_version =~ /^latest$|^present$/) or ($puppet_agent::aio_upgrade_required) or ($puppet_agent::aio_downgrade_required){ class { 'puppet_agent::install::suse': package_version => $package_version, install_options => $install_options, @@ -67,7 +67,7 @@ $_source = "${::puppet_agent::params::local_packages_dir}/${::puppet_agent::prepare::package::package_file_name}" } else { # any other type of source means we use apt with no 'source' defined in the package resource below - if $package_version == 'present' { + if $package_version =~ /^latest$|^present$/ { $_package_version = $package_version } else { $_package_version = "${package_version}-1${::lsbdistcodename}" @@ -91,7 +91,7 @@ $_source = undef } } - $_aio_package_version = $package_version.match(/^\d+\.\d+\.\d+(\.\d+)?/)[0] + $_aio_package_version = $package_version.match(/^\d+\.\d+\.\d+(\.\d+)?|^latest$|^present$/)[0] package { $::puppet_agent::package_name: ensure => $_package_version, install_options => $_install_options, diff --git a/manifests/install/suse.pp b/manifests/install/suse.pp index eec7cc1da..3c4bcbd62 100644 --- a/manifests/install/suse.pp +++ b/manifests/install/suse.pp @@ -26,7 +26,7 @@ $_source = undef } - $_aio_package_version = $package_version.match(/^\d+\.\d+\.\d+(\.\d+)?/)[0] + $_aio_package_version = $package_version.match(/^\d+\.\d+\.\d+(\.\d+)?|^latest$|^present$/)[0] package { $::puppet_agent::package_name: ensure => $package_version, install_options => $install_options, diff --git a/manifests/osfamily/suse.pp b/manifests/osfamily/suse.pp index 903415306..5f009b55e 100644 --- a/manifests/osfamily/suse.pp +++ b/manifests/osfamily/suse.pp @@ -143,21 +143,36 @@ } $repo_settings.each |String $setting, String $value| { - ini_setting { "zypper ${repo_name} ${setting}": - ensure => present, - path => $repo_file, - section => $repo_name, - setting => $setting, - value => $value, - before => Exec["refresh-${repo_name}"], + if $_package_version =~ /^present$|^latest$/ { + ini_setting { "zypper ${repo_name} ${setting}": + ensure => present, + path => $repo_file, + section => $repo_name, + setting => $setting, + value => $value, + } + } else { + ini_setting { "zypper ${repo_name} ${setting}": + ensure => present, + path => $repo_file, + section => $repo_name, + setting => $setting, + value => $value, + before => Exec["refresh-${repo_name}"], + } } } - exec { "refresh-${repo_name}": - path => '/bin:/usr/bin:/sbin:/usr/sbin', - unless => "zypper search -r ${repo_name} -s | grep puppet-agent | awk '{print \$7}' | grep \"^${_package_version}\"", - command => "zypper refresh ${repo_name}", - logoutput => 'on_failure', + # If the requested package is "latest" we don't know the exact version yet, + # so we can't reliably refresh the repo. If "present", the repo contents don't + # matter at all if a version is already installed. + unless $_package_version =~ /^present$|^latest$/ { + exec { "refresh-${repo_name}": + path => '/bin:/usr/bin:/sbin:/usr/sbin', + unless => "zypper search -r ${repo_name} -s | grep puppet-agent | awk '{print \$7}' | grep \"^${_package_version}\"", + command => "zypper refresh ${repo_name}", + logoutput => 'on_failure', + } } } } diff --git a/spec/classes/puppet_agent_osfamily_solaris_spec.rb b/spec/classes/puppet_agent_osfamily_solaris_spec.rb index c0e1bda4d..447c9cef7 100644 --- a/spec/classes/puppet_agent_osfamily_solaris_spec.rb +++ b/spec/classes/puppet_agent_osfamily_solaris_spec.rb @@ -111,7 +111,7 @@ def install_script(ver, arch) # Ensure we get a versionable package provider pkg = Puppet::Type.type(:package) - pkg.stubs(:defaultprovider).returns(pkg.provider(:pkg)) + allow(pkg).to receive(:defaultprovider).and_return(pkg.provider(:pkg)) end context "when Solaris 11 i386 and a custom source" do diff --git a/spec/classes/puppet_agent_spec.rb b/spec/classes/puppet_agent_spec.rb index 89062ca86..b84bfa5a6 100644 --- a/spec/classes/puppet_agent_spec.rb +++ b/spec/classes/puppet_agent_spec.rb @@ -62,7 +62,7 @@ def global_facts(facts, os) context 'package version' do context 'valid' do - ['5.5.15-1.el7', '5.5.15.el7', '6.0.9.3.g886c5ab'].each do |version| + ['5.5.15-1.el7', '5.5.15.el7', '6.0.9.3.g886c5ab', 'present', 'latest'].each do |version| redhat_familly_supported_os.each do |os, facts| let(:facts) { global_facts(facts, os) } @@ -92,6 +92,32 @@ def global_facts(facts, os) end end end + + context 'latest' do + context 'on unsupported platform' do + on_supported_os.select { |platform, _| platform =~ /solaris|aix|windows|osx/ }.each do |os, facts| + let(:facts) { global_facts(facts, os) } + let(:params) { { package_version: 'latest' } } + + context os do + it { is_expected.not_to compile } + it { expect { catalogue }.to raise_error(Puppet::Error, /Setting package_version to 'latest' is not supported/) } + end + end + end + + context 'on supported platform' do + on_supported_os.reject { |platform, _| platform =~ /solaris|aix|windows|osx/ }.each do |os, facts| + let(:facts) { global_facts(facts, os) } + let(:params) { { package_version: 'latest' } } + + context os do + it { is_expected.to compile.with_all_deps } + it { expect { catalogue }.not_to raise_error } + end + end + end + end end context 'supported_operating systems' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index fc4d8257a..b61ee604f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -10,6 +10,7 @@ end RSpec.configure do |c| + c.mock_with :rspec c.before :each do Puppet::Parser::Functions.newfunction(:pe_build_version, type: :rvalue, doc: '') do |args| '2018.1.0' diff --git a/spec/unit/puppet/provider/puppet_agent_end_run_spec.rb b/spec/unit/puppet/provider/puppet_agent_end_run_spec.rb new file mode 100644 index 000000000..3e2be5e99 --- /dev/null +++ b/spec/unit/puppet/provider/puppet_agent_end_run_spec.rb @@ -0,0 +1,112 @@ +require 'spec_helper' + +describe Puppet::Type.type(:puppet_agent_end_run).provider(:puppet_agent_end_run) do + let(:catalog) { Puppet::Resource::Catalog.new } + + before do + allow(Facter).to receive(:value) + end + + context 'when package_version is latest' do + let(:resource) do + Puppet::Type.type(:puppet_agent_end_run).new(:name => 'latest', :provider => :puppet_agent_end_run) + end + + let(:agent_latest_package) do + Puppet::Type.type(:package).new(:name => 'puppet-agent', :ensure => 'latest', :provider => :yum) + end + + before do + catalog.add_resource(agent_latest_package) + resource.catalog = catalog + end + + context 'with dev versions' do + it 'does not stop the run if package is already latest' do + catalog.resource('package', 'puppet-agent').parameters[:ensure].latest = '0:7.8.0.64.g6670bf40b-1.el8' + allow(Facter).to receive(:value).with('aio_agent_version').and_return('7.8.0.64') + expect(Puppet::Application).not_to receive(:stop!) + + resource.provider.stop + end + + it 'stops the run if the current and desired versions differ' do + catalog.resource('package', 'puppet-agent').parameters[:ensure].latest = '0:7.8.0.64.g6670bf40b-1.el8' + allow(Facter).to receive(:value).with('aio_agent_version').and_return('7.8.0.32') + expect(Puppet::Application).to receive(:stop!) + + resource.provider.stop + end + end + + context 'with released versions' do + it 'does not stop the run if package is already latest' do + catalog.resource('package', 'puppet-agent').parameters[:ensure].latest = '7.8.0-1.el8' + allow(Facter).to receive(:value).with('aio_agent_version').and_return('7.8.0') + expect(Puppet::Application).not_to receive(:stop!) + + resource.provider.stop + end + + + it 'stops the run if the current and desired versions differ' do + catalog.resource('package', 'puppet-agent').parameters[:ensure].latest = '7.9.0-1.el8' + allow(Facter).to receive(:value).with('aio_agent_version').and_return('7.8.0') + expect(Puppet::Application).to receive(:stop!) + + resource.provider.stop + end + end + end + + context 'when package_version is present' do + let(:resource) do + Puppet::Type.type(:puppet_agent_end_run).new(:name => 'present', :provider => :puppet_agent_end_run) + end + + it 'never stops the run' do + expect(Puppet::Application).not_to receive(:stop!) + resource.provider.stop + end + end + + context 'when package_version is a released version' do + let(:resource) do + Puppet::Type.type(:puppet_agent_end_run).new(:name => '7.8.0', :provider => :puppet_agent_end_run) + end + + it 'does not stop the run if current and desired versions match' do + allow(Facter).to receive(:value).with('aio_agent_version').and_return('7.8.0') + expect(Puppet::Application).not_to receive(:stop!) + + resource.provider.stop + end + + it 'stops the run if current and desired versions do not match' do + allow(Facter).to receive(:value).with('aio_agent_version').and_return('7.7.0') + expect(Puppet::Application).to receive(:stop!) + + resource.provider.stop + end + end + + context 'when package_version is a nightly version' do + let(:resource) do + Puppet::Type.type(:puppet_agent_end_run).new(:name => '7.8.0.32', :provider => :puppet_agent_end_run) + end + + it 'does not stop the run if current and desired versions match' do + allow(Facter).to receive(:value).with('aio_agent_version').and_return('7.8.0.32') + expect(Puppet::Application).not_to receive(:stop!) + + resource.provider.stop + end + + it 'stops the run if current and desired versions do not match' do + allow(Facter).to receive(:value).with('aio_agent_version').and_return('7.8.0') + expect(Puppet::Application).to receive(:stop!) + + resource.provider.stop + end + end +end