diff --git a/spec/acceptance/basic_webhook_spec.rb b/spec/acceptance/basic_webhook_spec.rb index ac23398b..a7e605d5 100644 --- a/spec/acceptance/basic_webhook_spec.rb +++ b/spec/acceptance/basic_webhook_spec.rb @@ -13,7 +13,7 @@ class {'r10k::webhook::config': use_mcollective => false, notify => Service['webhook'], } - + class {'r10k::webhook': require => Class['r10k::webhook::config'], } @@ -58,5 +58,16 @@ class {'r10k::webhook': expect(r.exit_code).to eq(0) end end + # rubocop:disable RSpec/MultipleExpectations + it 'should successfully lock when hammered with multiple requests' do + 4.times do + Thread.new do + shell('/usr/bin/curl -d \'{ "ref": "refs/heads/production" }\' -H "Accept: application/json" "http://localhost:8088/payload" -k -q') do |r| + expect(r.stdout).to match(/^.*success.*$/) + expect(r.exit_code).to eq(0) + end + end + end + end end end diff --git a/templates/webhook.bin.erb b/templates/webhook.bin.erb index 4920f800..9d7f3b02 100755 --- a/templates/webhook.bin.erb +++ b/templates/webhook.bin.erb @@ -20,6 +20,7 @@ require 'open3' WEBHOOK_CONFIG = '/etc/webhook.yaml' PIDFILE = '/var/run/webhook/webhook.pid' +LOCKFILE = '/var/run/webhook/webhook.lock' APP_ROOT = '/var/run/webhook' if (File.exists?(WEBHOOK_CONFIG)) @@ -181,15 +182,23 @@ end end def run_command(command) - if Open3.respond_to?('capture3') - stdout, stderr, exit_status = Open3.capture3(command) - message = "triggered: #{command}\n#{stdout}\n#{stderr}" - else - message = "forked: #{command}" - Process.detach(fork{ exec "#{command} &"}) - exit_status = 0 + message = '' + File.open(LOCKFILE, 'w+') do |file| + # r10k has a small race condition which can cause failed deploys if two happen + # more or less simultaneously. To mitigate, we just lock on a file and wait for + # the other one to complete. + file.flock(File::LOCK_EX) + + if Open3.respond_to?('capture3') + stdout, stderr, exit_status = Open3.capture3(command) + message = "triggered: #{command}\n#{stdout}\n#{stderr}" + else + message = "forked: #{command}" + Process.detach(fork{ exec "#{command} &"}) + exit_status = 0 + end + raise "#{stdout}\n#{stderr}" if exit_status != 0 end - raise "#{stdout}\n#{stderr}" if exit_status != 0 message end