diff --git a/manifests/agent/config.pp b/manifests/agent/config.pp index f9b12d2..4ffc2d9 100644 --- a/manifests/agent/config.pp +++ b/manifests/agent/config.pp @@ -1,63 +1,58 @@ # Puppet agent configuration # @api private class puppet::agent::config inherits puppet::config { puppet::config::agent{ 'classfile': value => $::puppet::classfile; 'localconfig': value => '$vardir/localconfig'; 'default_schedules': value => false; 'report': value => $::puppet::report; 'pluginsync': value => $::puppet::pluginsync; 'masterport': value => $::puppet::port; 'environment': value => $::puppet::environment; 'listen': value => $::puppet::listen; 'splay': value => $::puppet::splay; 'splaylimit': value => $::puppet::splaylimit; 'runinterval': value => $::puppet::runinterval; 'noop': value => $::puppet::agent_noop; 'usecacheonfailure': value => $::puppet::usecacheonfailure; } if $::puppet::configtimeout != undef { puppet::config::agent { 'configtimeout': value => $::puppet::configtimeout; } } if $::puppet::prerun_command { puppet::config::agent { 'prerun_command': value => $::puppet::prerun_command; } } if $::puppet::postrun_command { puppet::config::agent { 'postrun_command': value => $::puppet::postrun_command; } } - if $::puppet::client_certname { - puppet::config::agent { - 'certname': value => $::puppet::client_certname; - } - } $::puppet::agent_additional_settings.each |$key,$value| { puppet::config::agent { $key: value => $value } } if $::puppet::runmode == 'service' { $should_start = 'yes' } else { $should_start = 'no' } if $::osfamily == 'Debian' { augeas {'puppet::set_start': context => '/files/etc/default/puppet', changes => "set START ${should_start}", incl => '/etc/default/puppet', lens => 'Shellvars.lns', } if $::puppet::remove_lock { file {'/var/lib/puppet/state/agent_disabled.lock': ensure => absent, } } } } diff --git a/manifests/config.pp b/manifests/config.pp index 7bd5359..8beb9f9 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -1,94 +1,100 @@ # Set up the puppet config # @api private class puppet::config( $allow_any_crl_auth = $::puppet::allow_any_crl_auth, $auth_allowed = $::puppet::auth_allowed, $auth_template = $::puppet::auth_template, $ca_server = $::puppet::ca_server, $ca_port = $::puppet::ca_port, $dns_alt_names = $::puppet::dns_alt_names, $listen_to = $::puppet::listen_to, $module_repository = $::puppet::module_repository, $pluginsource = $::puppet::pluginsource, $pluginfactsource = $::puppet::pluginfactsource, $puppet_dir = $::puppet::dir, $puppetmaster = $::puppet::puppetmaster, $syslogfacility = $::puppet::syslogfacility, $srv_domain = $::puppet::srv_domain, $use_srv_records = $::puppet::use_srv_records, $additional_settings = $::puppet::additional_settings, + $client_certname = $::puppet::client_certname, ) { puppet::config::main{ 'vardir': value => $::puppet::vardir; 'logdir': value => $::puppet::logdir; 'rundir': value => $::puppet::rundir; 'ssldir': value => $::puppet::ssldir; 'privatekeydir': value => '$ssldir/private_keys { group = service }'; 'hostprivkey': value => '$privatekeydir/$certname.pem { mode = 640 }'; 'show_diff': value => $::puppet::show_diff; 'codedir': value => $::puppet::codedir; } if $module_repository and !empty($module_repository) { puppet::config::main{'module_repository': value => $module_repository; } } if $ca_server and !empty($ca_server) { puppet::config::main{'ca_server': value => $ca_server; } } if $ca_port { puppet::config::main{'ca_port': value => $ca_port; } } if $dns_alt_names and !empty($dns_alt_names) { puppet::config::main{'dns_alt_names': value => $dns_alt_names; } } if $use_srv_records { unless $srv_domain { fail('$::domain fact found to be undefined and $srv_domain is undefined') } puppet::config::main{ 'use_srv_records': value => true; 'srv_domain': value => $srv_domain; } } else { puppet::config::main { 'server': value => pick($puppetmaster, $::fqdn); } } if $pluginsource { puppet::config::main{'pluginsource': value => $pluginsource; } } if $pluginfactsource { puppet::config::main{'pluginfactsource': value => $pluginfactsource; } } if $syslogfacility and !empty($syslogfacility) { puppet::config::main{'syslogfacility': value => $syslogfacility; } } + if $client_certname { + puppet::config::main { + 'certname': value => $client_certname; + } + } $additional_settings.each |$key,$value| { puppet::config::main { $key: value => $value } } file { $puppet_dir: ensure => directory, owner => $::puppet::dir_owner, group => $::puppet::dir_group, } -> case $::osfamily { 'Windows': { concat { "${puppet_dir}/puppet.conf": mode => '0674', } } default: { concat { "${puppet_dir}/puppet.conf": owner => 'root', group => $::puppet::params::root_group, mode => '0644', } } } ~> file { "${puppet_dir}/auth.conf": content => template($auth_template), } } diff --git a/spec/classes/puppet_agent_spec.rb b/spec/classes/puppet_agent_spec.rb index 45c23de..a3fbc0a 100644 --- a/spec/classes/puppet_agent_spec.rb +++ b/spec/classes/puppet_agent_spec.rb @@ -1,371 +1,360 @@ require 'spec_helper' require 'deep_merge' describe 'puppet' do on_os_under_test.each do |os, facts| context "on #{os}" do case facts[:osfamily] when 'FreeBSD' puppet_major = facts[:puppetversion].to_i bindir = '/usr/local/bin' client_package = "puppet#{puppet_major}" confdir = '/usr/local/etc/puppet' package_provider = nil when 'windows' bindir = 'C:/ProgramData/PuppetLabs/puppet/bin' client_package = 'puppet-agent' confdir = 'C:/ProgramData/PuppetLabs/puppet/etc' package_provider = 'chocolatey' when 'Archlinux' bindir = '/usr/bin' client_package = 'puppet' confdir = '/etc/puppetlabs/puppet' package_provider = nil else bindir = '/opt/puppetlabs/bin' client_package = 'puppet-agent' confdir = '/etc/puppetlabs/puppet' package_provider = nil end let :facts do facts.deep_merge( - # rspec-puppet(-facts) doesn't mock this - clientcert: 'client.example.com', # Cron/systemd timers are based on the IP - make it consistent networking: { ip: '192.0.2.100' } ) end let :params do { agent: true } end describe 'with no custom parameters' do # For windows we specify a package provider which doesn't compile if facts[:osfamily] != 'windows' it { is_expected.to compile.with_all_deps } end # install it do is_expected.to contain_class('puppet::agent::install') .with_manage_packages(true) .with_package_name([client_package]) .with_package_version('present') .with_package_provider(package_provider) .with_package_source(nil) .that_notifies(['Class[puppet::agent::config]', 'Class[puppet::agent::service]']) end it do is_expected.to contain_package(client_package) .with_ensure('present') .with_provider(package_provider) .with_source(nil) end # config it { is_expected.to contain_class('puppet::agent::config').that_notifies('Class[puppet::agent::service]') } it { is_expected.to contain_file(confdir).with_ensure('directory') } it { is_expected.to contain_concat("#{confdir}/puppet.conf") } it { is_expected.to contain_concat__fragment('puppet.conf_agent').with_content(/^\[agent\]/) } - it { is_expected.to contain_puppet__config__agent('certname').with_value(facts[:clientcert]) } it { is_expected.to contain_puppet__config__agent('report').with_value('true') } it { is_expected.not_to contain_puppet__config__agent('prerun_command') } it { is_expected.not_to contain_puppet__config__agent('postrun_command') } if facts[:osfamily] == 'Debian' it do is_expected.to contain_augeas('puppet::set_start') .with_context('/files/etc/default/puppet') .with_changes('set START yes') .with_incl('/etc/default/puppet') .with_lens('Shellvars.lns') end it { is_expected.to contain_file('/var/lib/puppet/state/agent_disabled.lock').with_ensure(:absent) } end # service it { is_expected.to contain_class('puppet::agent::service') } it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(true) } it do is_expected.to contain_service('puppet') .with_ensure('running') .with_name('puppet') .with_hasstatus('true') .with_enable('true') end it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } if os =~ /\A(windows|archlinux)/ it { is_expected.not_to contain_cron('puppet') } else it { is_expected.to contain_cron('puppet').with_ensure('absent') } end it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } case os when /\Adebian-/, /\A(redhat|centos|scientific)-7/, /\Afedora-/, /\Aubuntu-(16|18)/, /\Aarchlinux-/ it do is_expected.to contain_exec('systemctl-daemon-reload-puppet') .with_refreshonly(true) .with_command('systemctl daemon-reload') end it do is_expected.to contain_service('puppet-run.timer') .with_ensure(:stopped) .with_provider('systemd') .with_name('puppet-run.timer') .with_enable(false) end it { is_expected.to contain_file('/etc/systemd/system/puppet-run.timer').with_ensure(:absent) } it { is_expected.to contain_file('/etc/systemd/system/puppet-run.service').with_ensure(:absent) } else it { is_expected.not_to contain_service('puppet-run.timer') } it { is_expected.not_to contain_file('/etc/systemd/system/puppet-run.timer') } it { is_expected.not_to contain_file('/etc/systemd/system/puppet-run.service') } it { is_expected.not_to contain_exec('systemctl-daemon-reload-puppet') } end end describe 'set prerun_command will be included in config' do let :params do super().merge(prerun_command: '/my/prerun') end it { is_expected.to contain_puppet__config__agent('prerun_command').with_value('/my/prerun') } end describe 'set postrun_command will be included in config' do let :params do super().merge(postrun_command: '/my/postrun') end it { is_expected.to contain_puppet__config__agent('postrun_command').with_value('/my/postrun') } end describe 'with additional settings' do let :params do super().merge(agent_additional_settings: { 'ignoreschedules' => true }) end it { is_expected.to contain_puppet__config__agent('ignoreschedules').with_value('true') } end context 'manage_packages' do describe 'when manage_packages => false' do let :params do super().merge(manage_packages: false) end it { is_expected.not_to contain_package(client_package) } end describe "when manage_packages => 'agent'" do let :params do super().merge(manage_packages: 'agent') end it { is_expected.to contain_package(client_package) } end describe "when manage_packages => 'server'" do let :params do super().merge(manage_packages: 'server') end it { is_expected.not_to contain_package(client_package) } end end context 'runmode' do describe 'when runmode => cron' do let :params do super().merge(runmode: 'cron') end case os when /\A(windows|archlinux)/ it { is_expected.to raise_error(Puppet::Error, /Runmode of cron not supported on #{facts[:kernel]} operating systems!/) } when /\Adebian-/, /\A(redhat|centos|scientific)-7/, /\Afedora-/, /\Aubuntu-(16|18)/ it { is_expected.to compile.with_all_deps } it { is_expected.to contain_concat__fragment('puppet.conf_agent') } if facts[:osfamily] == 'Debian' it do is_expected.to contain_augeas('puppet::set_start') .with_context('/files/etc/default/puppet') .with_changes('set START no') .with_incl('/etc/default/puppet') .with_lens('Shellvars.lns') end it { is_expected.to contain_file('/var/lib/puppet/state/agent_disabled.lock').with_ensure(:absent) } end it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(true) } it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } it do is_expected.to contain_service('puppet') .with_ensure('stopped') .with_name('puppet') .with_hasstatus('true') .with_enable('false') end it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } it { is_expected.to contain_service('puppet-run.timer').with_ensure(:stopped) } it do is_expected.to contain_cron('puppet') .with_command("#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize") .with_user('root') .with_minute(%w[10 40]) .with_hour('*') end else it { is_expected.to compile.with_all_deps } it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(true) } it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } it { is_expected.not_to contain_service('puppet-run.timer') } it do is_expected.to contain_cron('puppet') .with_command("#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize") .with_user('root') .with_minute(%w[10 40]) .with_hour('*') end end end describe 'when runmode => systemd.timer' do let :params do super().merge(runmode: 'systemd.timer') end case os when /\Adebian-/, /\A(redhat|centos|scientific)-7/, /\Afedora-/, /\Aubuntu-(16|18)/, /\Aarchlinux-/ it { is_expected.to compile.with_all_deps } it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(true) } it { is_expected.to contain_service('puppet-run.timer').with_ensure(:running) } it do is_expected.to contain_file('/etc/systemd/system/puppet-run.timer') .with_content(/.*OnCalendar\=\*-\*-\* \*\:10,40:00.*/) end it do is_expected.to contain_file('/etc/systemd/system/puppet-run.timer') .with_content(/^RandomizedDelaySec\=0$/) end it do is_expected.to contain_file('/etc/systemd/system/puppet-run.service') .with_content(%r{^ExecStart=#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize --detailed-exitcode --no-usecacheonfailure$}) end it do is_expected.to contain_exec('systemctl-daemon-reload-puppet') .with_refreshonly(true) .with_command('systemctl daemon-reload') end it do is_expected.to contain_service('puppet-run.timer') .with_provider('systemd') .with_ensure('running') .with_name('puppet-run.timer') .with_enable('true') end else it { is_expected.to raise_error(Puppet::Error, /Runmode of systemd.timer not supported on #{facts[:kernel]} operating systems!/) } end end describe 'when runmode => none' do let :params do super().merge(runmode: 'none') end # For windows we specify a package provider which doesn't compile if facts[:osfamily] != 'windows' it { is_expected.to compile.with_all_deps } end it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } case os when /\Adebian-/, /\A(redhat|centos|scientific)-7/, /\Afedora-/, /\Aubuntu-(16|18)/, /\Aarchlinux-/ it { is_expected.to contain_service('puppet-run.timer').with_ensure(:stopped) } else it { is_expected.not_to contain_service('puppet-run.timer') } end end describe 'when runmode => unmanaged' do let :params do super().merge(runmode: 'unmanaged') end # For windows we specify a package provider which doesn't compile if facts[:osfamily] != 'windows' it { is_expected.to compile.with_all_deps } end it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } it { is_expected.not_to contain_cron('puppet') } it { is_expected.not_to contain_service('puppet') } it { is_expected.not_to contain_service('puppet-run.timer') } end end describe 'when unavailable_runmodes => ["cron"]' do let :params do super().merge(unavailable_runmodes: ['cron']) end it { is_expected.not_to contain_cron('puppet') } end describe 'with custom service_name' do let :params do super().merge(service_name: 'pe-puppet') end it { is_expected.to contain_service('puppet').with_name('pe-puppet') } end context 'with remove_lock => false' do let :params do super().merge(remove_lock: false) end it { should_not contain_file('/var/lib/puppet/state/agent_disabled.lock') } end - context 'with client_certname => false' do - let :params do - super().merge(client_certname: false) - end - - it { is_expected.not_to contain_puppet__config__agent('certname') } - end - context 'with report => false' do let :params do super().merge(report: false) end it { is_expected.to contain_puppet__config__agent('report').with_value('false') } end end end end diff --git a/spec/classes/puppet_config_spec.rb b/spec/classes/puppet_config_spec.rb index 4fb38ff..4df6713 100644 --- a/spec/classes/puppet_config_spec.rb +++ b/spec/classes/puppet_config_spec.rb @@ -1,200 +1,231 @@ require 'spec_helper' +require 'deep_merge' describe 'puppet' do on_os_under_test.each do |os, facts| context "on #{os}" do case facts[:osfamily] when 'FreeBSD' dir_owner = 'puppet' dir_group = 'puppet' confdir = '/usr/local/etc/puppet' logdir = '/var/log/puppet' rundir = '/var/run/puppet' ssldir = '/var/puppet/ssl' vardir = '/var/puppet' when 'windows' dir_owner = nil dir_group = nil confdir = 'C:/ProgramData/PuppetLabs/puppet/etc' logdir = 'C:/ProgramData/PuppetLabs/puppet/var/log' rundir = 'C:/ProgramData/PuppetLabs/puppet/var/run' ssldir = 'C:/ProgramData/PuppetLabs/puppet/etc/ssl' vardir = 'C:/ProgramData/PuppetLabs/puppet/var' when 'Archlinux' dir_owner = 'puppet' dir_group = 'puppet' confdir = '/etc/puppetlabs/puppet' logdir = '/var/log/puppetlabs/puppet' rundir = '/var/run/puppetlabs' ssldir = '/etc/puppetlabs/puppet/ssl' vardir = '/opt/puppetlabs/puppet/cache' else dir_owner = 'root' dir_group = nil confdir = '/etc/puppetlabs/puppet' logdir = '/var/log/puppetlabs/puppet' rundir = '/var/run/puppetlabs' ssldir = '/etc/puppetlabs/puppet/ssl' vardir = '/opt/puppetlabs/puppet/cache' end let :facts do facts.merge(domain: 'example.org') end let :params do {} end describe 'with default parameters' do it { is_expected.to contain_file(confdir).with_owner(dir_owner).with_group(dir_group) } it { is_expected.to contain_file("#{confdir}/auth.conf").with_content(%r{/puppet/v3/}) } it { is_expected.not_to contain_file("#{confdir}/auth.conf").with_content(%r{^path /certificate_revocation_list/ca\nmethod find$}) } it { is_expected.not_to contain_puppet__config__main('default_manifest') } it { is_expected.not_to contain_file('/etc/puppet/manifests/default_manifest.pp') } it { is_expected.not_to contain_puppet__config__main('reports') } it { is_expected.to contain_puppet__config__main('vardir').with_value(vardir) } it { is_expected.to contain_puppet__config__main('logdir').with_value(logdir) } it { is_expected.to contain_puppet__config__main('rundir').with_value(rundir) } it { is_expected.to contain_puppet__config__main('ssldir').with_value(ssldir) } it { is_expected.to contain_puppet__config__main('privatekeydir').with_value('$ssldir/private_keys { group = service }') } it { is_expected.to contain_puppet__config__main('hostprivkey').with_value('$privatekeydir/$certname.pem { mode = 640 }') } it { is_expected.to contain_puppet__config__main('show_diff').with_value('false') } it { is_expected.to contain_puppet__config__main('server').with_value(facts[:fqdn]) } end describe 'with allow_any_crl_auth' do let :params do super().merge(allow_any_crl_auth: true) end it { is_expected.to contain_file("#{confdir}/auth.conf").with_content(%r{^path /puppet-ca/v1/certificate_revocation_list/ca\nauth any$}) } end describe 'with auth_allowed' do let :params do super().merge(auth_allowed: ['$1', 'puppetproxy']) end it { is_expected.to contain_file("#{confdir}/auth.conf").with_content(/^allow \$1, puppetproxy$/) } end describe "when dns_alt_names => ['foo','bar']" do let :params do super().merge(dns_alt_names: %w[foo bar]) end it { is_expected.to contain_puppet__config__main('dns_alt_names').with_value(%w[foo bar]) } end describe "when syslogfacility => 'local6'" do let :params do super().merge(syslogfacility: 'local6') end it { is_expected.to contain_puppet__config__main('syslogfacility').with_value('local6') } end describe "when module_repository => 'https://myforgeapi.example.com'" do let :params do super().merge(module_repository: 'https://myforgeapi.example.com') end it { is_expected.to contain_puppet__config__main('module_repository').with_value('https://myforgeapi.example.com') } end describe 'when use_srv_records => true' do let :params do super().merge(use_srv_records: true) end context 'domain fact is defined' do it { is_expected.to contain_puppet__config__main('use_srv_records').with_value('true') } it { is_expected.to contain_puppet__config__main('srv_domain').with_value('example.org') } it { is_expected.to contain_puppet__config__main('pluginsource').with_value('puppet:///plugins') } it { is_expected.to contain_puppet__config__main('pluginfactsource').with_value('puppet:///pluginfacts') } it { is_expected.not_to contain_puppet__config__main('server') } end context 'domain fact is unset' do let(:facts) { facts.merge(domain: nil) } it { is_expected.to raise_error(Puppet::Error, /\$::domain fact found to be undefined and \$srv_domain is undefined/) } end context 'is overriden via param' do let :params do super().merge(srv_domain: 'special.example.com') end it { is_expected.to contain_puppet__config__main('use_srv_records').with_value(true) } it { is_expected.to contain_puppet__config__main('srv_domain').with_value('special.example.com') } end end + describe 'client_certname' do + context 'with client_certname => $::clientcert' do + let :facts do + # rspec-puppet(-facts) doesn't mock this + facts.deep_merge(clientcert: 'client.example.com') + end + let :params do + super().merge(client_certname: facts[:clientcert]) + end + + it { is_expected.to contain_puppet__config__main('certname').with_value(facts[:clientcert]) } + end + + context 'with client_certname => "foobar"' do + let :params do + super().merge(client_certname: 'foobar') + end + + it { is_expected.to contain_puppet__config__main('certname').with_value('foobar') } + end + + context 'with client_certname => false' do + let :params do + super().merge(client_certname: false) + end + + it { is_expected.not_to contain_puppet__config__main('certname') } + end + end + context 'puppetmaster' do describe "when puppetmaster => 'mymaster.example.com'" do let :params do super().merge(puppetmaster: 'mymaster.example.com') end it { is_expected.to contain_puppet__config__main('server').with_value('mymaster.example.com') } end describe 'puppetmaster parameter overrides global puppetmaster' do let :params do super().merge(puppetmaster: 'mymaster.example.com') end let :facts do facts.merge(puppetmaster: 'global.example.com') end it { is_expected.to contain_puppet__config__main('server').with_value('mymaster.example.com') } end describe 'global puppetmaster overrides fqdn' do let :facts do facts.merge(puppetmaster: 'global.example.com') end it { is_expected.to contain_puppet__config__main('server').with_value('global.example.com') } end context 'when listen' do let :params do super().merge(listen: true) end describe 'puppetmaster default value is used' do it { is_expected.to contain_file("#{confdir}/auth.conf").with_content(%r{^path /run\nauth any\nmethod save\nallow #{facts[:fqdn]}$}) } end describe 'puppetmaster has value' do let :params do super().merge(puppetmaster: 'mymaster.example.com') end it { is_expected.to contain_file("#{confdir}/auth.conf").with_content(%r{^path /run\nauth any\nmethod save\nallow mymaster.example.com$}) } end describe 'listen_to has values' do let :params do super().merge(listen_to: ['node1.example.com', 'node2.example.com']) end it { is_expected.to contain_file("#{confdir}/auth.conf").with_content(%r{^path /run\nauth any\nmethod save\nallow node1\.example\.com,node2\.example\.com$}) } end end end describe 'with additional settings' do let :params do super().merge(additional_settings: { disable_warnings: 'deprecations' }) end it { is_expected.to contain_puppet__config__main('disable_warnings').with_value('deprecations') } end end end end