From c46ee319cd7f647ecc2d4d384f24a47352cfb2c6 Mon Sep 17 00:00:00 2001 From: Otheus Date: Fri, 27 Jan 2017 16:17:45 +0100 Subject: [PATCH 1/3] patch: make cronjob time dependent on IP address --- manifests/agent.pp | 38 +++++++++++++++- spec/acceptance/agent_spec.rb | 39 +++++++++++++++- spec/classes/puppet_agent_spec.rb | 74 ++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 5 deletions(-) diff --git a/manifests/agent.pp b/manifests/agent.pp index aadfc5d16..0556102c3 100644 --- a/manifests/agent.pp +++ b/manifests/agent.pp @@ -200,21 +200,55 @@ $service_enable = false # Default to every 30 minutes - random around the clock - if $cron_minute == undef { + if $cron_minute == undef or $cron_minute == 'random' { $time1 = fqdn_rand(30) $time2 = $time1 + 30 $minute = [ $time1, $time2 ] } + elsif $cron_minute =~ /^ip(?::(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?(?:\/(\d+))?(?:%(\d+))?$/ { + # Determine the minute to run puppet based on this hosts' IP address + # with fine-grained control based on optionally provided IP, mask and modulo. + # Defaults: + # node ip (based on fact), + # ignore most-significatnt 24 bits, + # modulo 60. + # Thus, if this node's IP is 10.2.7.63, mask is 24, mod is 60, + # minute <- 3 + # If IP is 10.2.9.63, mask is 16, mod is 300, then + # minute <- 267 + + # Test cases (TODO) + # cron_minute = 'ip' # node's ip , mask 24, mod 60 + # cron_minute = 'ip%180' # node's ip, mask 24, mod 180 + # cron_minute = 'ip/22' # node's ip, mask 22,mod 60 + # cron_minute = 'ip/16%600' # node's ip, mask 16,mod 600 + # cron_minute = 'ip:130.10.21.2' + # cron_minute = 'ip:130.10.21.2/24' + # cron_minute = 'ip:130.10.21.2/16%300' # mod 300 + # cron_minute = 'ip:%{::ip_address}/22' + + $cron_minute_ip = pick($1,$::ipaddress,"127.0.0.1") + $cron_minute_mask = pick($2,24) + $cron_minute_mod = pick($3,60) + $minute = inline_template('<%= + require "ipaddr"; + a=@cron_minute_ip; b=@cron_minute_mask.to_i;c=@cron_minute_mod.to_i + # Convert ip address to int, then mask by inverted subnet mask, then mod + ((IPAddr.new(a).to_i & ~(0xFFFFFFFF << (32-b))) % c) + %>') + } else { $minute = $cron_minute } - cron { 'puppet-client': + cron { 'puppet-agent': command => $puppet_run_command, user => 'root', hour => $cron_hour, minute => $minute, } + cron { 'puppet-client': ensure => 'absent', } # Why did someone name this "client"? + } # Run Puppet through external tooling, like MCollective 'external': { diff --git a/spec/acceptance/agent_spec.rb b/spec/acceptance/agent_spec.rb index d11d8838d..e5bf6d1c7 100644 --- a/spec/acceptance/agent_spec.rb +++ b/spec/acceptance/agent_spec.rb @@ -48,10 +48,47 @@ class { 'puppet::agent': describe cron do # Note: This only has four *'s since the minute part is randomized # by the agent module. - it { should have_entry "* * * * \/usr\/bin\/puppet agent --no-daemonize --onetime --logdest syslog > \/dev\/null 2>&1" } + it { should have_entry "% * * * \/usr\/bin\/puppet agent --no-daemonize --onetime --logdest syslog > \/dev\/null 2>&1" } end end + context 'agent run from cron-with-ip1' do + it 'should work with no errors' do + pp = <<-EOS + class { 'puppet::agent': + puppet_run_style => 'cron', + cron_minute = 'ip:10.2.3.44/8%60', # (2*256*256+3*256+44)%60 = 24 + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe cron do + it { should have_entry "22 * * * \/usr\/bin\/puppet agent --no-daemonize --onetime --logdest syslog > \/dev\/null 2>&1" } + end + end + + context 'agent run from cron-with-ip2' do + it 'should work with no errors' do + pp = <<-EOS + class { 'puppet::agent': + puppet_run_style => 'cron', + cron_minute = 'ip:10.2.3.44/16%300', # .. (3*256+44)%300 = 212 + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe cron do + it { should have_entry "122 * * * \/usr\/bin\/puppet agent --no-daemonize --onetime --logdest syslog > \/dev\/null 2>&1" } + end + end + + + + context 'agent run style manual' do it 'should work with no errors' do pp = <<-EOS diff --git a/spec/classes/puppet_agent_spec.rb b/spec/classes/puppet_agent_spec.rb index d1d947b08..55fd8d575 100644 --- a/spec/classes/puppet_agent_spec.rb +++ b/spec/classes/puppet_agent_spec.rb @@ -69,7 +69,7 @@ :enable => false, :require => "Package[#{params[:puppet_agent_package]}]" ) - should contain_cron('puppet-client').with( + should contain_cron('puppet-agent').with( :command => '/usr/bin/puppet agent --no-daemonize --onetime --logdest syslog > /dev/null 2>&1', :user => 'root', :hour => '5', @@ -77,6 +77,76 @@ ) } end + context 'using cron-with-ip1' do + let(:params) do + { + :puppet_server => 'test.exaple.com', + :puppet_agent_service => 'puppet', + :puppet_agent_package => 'puppet', + :version => '/etc/puppet/manifests/site.pp', + :puppet_run_style => 'cron', + :splay => 'true', + :environment => 'production', + :puppet_server_port => 8140, + :cron_minute => 'ip:10.2.3.44/8%60', # (2*256*256+3*256+44)%60 = 4 + } + end + it{ + should contain_cron('puppet-agent').with( + :command => '/usr/bin/puppet agent --no-daemonize --onetime --logdest syslog > /dev/null 2>&1', + :user => 'root', + :hour => '*', + :minute => '4' + ) + } + end + context 'using cron-with-ip2' do + let(:params) do + { + :puppet_server => 'test.exaple.com', + :puppet_agent_service => 'puppet', + :puppet_agent_package => 'puppet', + :version => '/etc/puppet/manifests/site.pp', + :puppet_run_style => 'cron', + :splay => 'true', + :environment => 'production', + :puppet_server_port => 8140, + :cron_minute => 'ip:10.2.3.44/16%300', # .. (3*256+44)%300 = 212 + } + end + it{ + should contain_cron('puppet-agent').with( + :command => '/usr/bin/puppet agent --no-daemonize --onetime --logdest syslog > /dev/null 2>&1', + :user => 'root', + :hour => '*', + :minute => '212' + ) + } + end + context 'using cron-with-ip3' do + let(:params) do + { + :puppet_server => 'test.exaple.com', + :puppet_agent_service => 'puppet', + :puppet_agent_package => 'puppet', + :version => '/etc/puppet/manifests/site.pp', + :puppet_run_style => 'cron', + :splay => 'true', + :environment => 'production', + :puppet_server_port => 8140, + :cron_minute => 'ip/0', # .. (127*256*256+1)%60 = 53 + } + end + it{ + should contain_cron('puppet-agent').with( + :command => '/usr/bin/puppet agent --no-daemonize --onetime --logdest syslog > /dev/null 2>&1', + :user => 'root', + :hour => '*', + :minute => '53' + ) + } + end + end describe 'srv records on Debian' do @@ -230,7 +300,7 @@ :enable => false, :require => "Package[#{params[:puppet_agent_package]}]" ) - should contain_cron('puppet-client').with( + should contain_cron('puppet-agent').with( :command => '/usr/bin/puppet agent --no-daemonize --onetime --logdest syslog > /dev/null 2>&1', :user => 'root', :hour => '*' From 219277e86b675918103e330372fbbf2f9646bb67 Mon Sep 17 00:00:00 2001 From: Otheus Date: Fri, 27 Jan 2017 17:56:11 +0100 Subject: [PATCH 2/3] strict_variables mode is stupid --- manifests/agent.pp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/agent.pp b/manifests/agent.pp index 0556102c3..768976ef8 100644 --- a/manifests/agent.pp +++ b/manifests/agent.pp @@ -227,7 +227,7 @@ # cron_minute = 'ip:130.10.21.2/16%300' # mod 300 # cron_minute = 'ip:%{::ip_address}/22' - $cron_minute_ip = pick($1,$::ipaddress,"127.0.0.1") + $cron_minute_ip = pick($1,getvar($::ipaddress),"127.0.0.1") $cron_minute_mask = pick($2,24) $cron_minute_mod = pick($3,60) $minute = inline_template('<%= From f5d125bcd9df617215f8e21790791a6a7c04b892 Mon Sep 17 00:00:00 2001 From: Otheus Date: Fri, 27 Jan 2017 18:02:34 +0100 Subject: [PATCH 3/3] strict_variables mode is still stupid --- manifests/agent.pp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/agent.pp b/manifests/agent.pp index 768976ef8..c8b8eb9c3 100644 --- a/manifests/agent.pp +++ b/manifests/agent.pp @@ -227,7 +227,7 @@ # cron_minute = 'ip:130.10.21.2/16%300' # mod 300 # cron_minute = 'ip:%{::ip_address}/22' - $cron_minute_ip = pick($1,getvar($::ipaddress),"127.0.0.1") + $cron_minute_ip = pick($1,getvar('::ipaddress'),"127.0.0.1") $cron_minute_mask = pick($2,24) $cron_minute_mod = pick($3,60) $minute = inline_template('<%=