summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/gitlab/health_checks/fs_shards_check.rb15
-rw-r--r--spec/lib/gitlab/health_checks/fs_shards_check_spec.rb42
-rw-r--r--spec/support/timeout_helper.rb23
3 files changed, 73 insertions, 7 deletions
diff --git a/lib/gitlab/health_checks/fs_shards_check.rb b/lib/gitlab/health_checks/fs_shards_check.rb
index df962d203b7..90612af3d63 100644
--- a/lib/gitlab/health_checks/fs_shards_check.rb
+++ b/lib/gitlab/health_checks/fs_shards_check.rb
@@ -42,6 +42,7 @@ module Gitlab
private
RANDOM_STRING = SecureRandom.hex(1000).freeze
+ COMMAND_TIMEOUT = 1.second
def operation_metrics(ok_metric, latency_metric, operation, **labels)
with_timing operation do |result, elapsed|
@@ -64,7 +65,11 @@ module Gitlab
end
def with_timeout(args)
- %w{timeout 1}.concat(args)
+ %W{timeout #{COMMAND_TIMEOUT.to_i}}.concat(args)
+ end
+
+ def exec_with_timeout(cmd_args, *args, &block)
+ Gitlab::Popen.popen(with_timeout(cmd_args), *args, &block)
end
def tmp_file_path(storage_name)
@@ -78,7 +83,7 @@ module Gitlab
def storage_stat_test(storage_name)
stat_path = File.join(path(storage_name), '.')
begin
- _, status = Gitlab::Popen.popen(with_timeout(%W{ stat #{stat_path} }))
+ _, status = exec_with_timeout(%W{ stat #{stat_path} })
status == 0
rescue Errno::ENOENT
File.exist?(stat_path) && File::Stat.new(stat_path).readable?
@@ -86,7 +91,7 @@ module Gitlab
end
def storage_write_test(tmp_path)
- _, status = Gitlab::Popen.popen(with_timeout(%W{ tee #{tmp_path} })) do |stdin|
+ _, status = exec_with_timeout(%W{ tee #{tmp_path} }) do |stdin|
stdin.write(RANDOM_STRING)
end
status == 0
@@ -96,7 +101,7 @@ module Gitlab
end
def storage_read_test(tmp_path)
- _, status = Gitlab::Popen.popen(with_timeout(%W{ diff #{tmp_path} - })) do |stdin|
+ _, status = exec_with_timeout(%W{ diff #{tmp_path} - }) do |stdin|
stdin.write(RANDOM_STRING)
end
status == 0
@@ -106,7 +111,7 @@ module Gitlab
end
def delete_test_file(tmp_path)
- _, status = Gitlab::Popen.popen(with_timeout(%W{ rm -f #{tmp_path} }))
+ _, status = exec_with_timeout(%W{ rm -f #{tmp_path} })
status == 0
rescue Errno::ENOENT
File.delete(tmp_path) rescue Errno::ENOENT
diff --git a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb
index 45ccd3d6459..289c93ecde7 100644
--- a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Gitlab::HealthChecks::FsShardsCheck do
+ include TimeoutHelper
+
let(:metric_class) { Gitlab::HealthChecks::Metric }
let(:result_class) { Gitlab::HealthChecks::Result }
let(:repository_storages) { [:default] }
@@ -103,15 +105,51 @@ describe Gitlab::HealthChecks::FsShardsCheck do
end
end
+ context 'when timeout kills fs checks' do
+ let(:timeout_seconds) { 1.to_s }
+
+ before do
+ skip 'timeout or gtimeout not available' unless any_timeout_command_exists?
+
+ allow(described_class).to receive(:with_timeout) { [timeout_command, timeout_seconds].concat(%w{ sleep 2 }) }
+ FileUtils.chmod_R(0755, tmp_dir)
+ end
+
+ describe '#readiness' do
+ subject { described_class.readiness }
+
+ it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: :default)) }
+ end
+
+ describe '#metrics' do
+ subject { described_class.metrics }
+
+ it { is_expected.to include(metric_class.new(:filesystem_accessible, 0, shard: :default)) }
+ it { is_expected.to include(metric_class.new(:filesystem_readable, 0, shard: :default)) }
+ it { is_expected.to include(metric_class.new(:filesystem_writable, 0, shard: :default)) }
+
+ it { is_expected.to include(have_attributes(name: :filesystem_access_latency, value: be >= 0, labels: { shard: :default })) }
+ it { is_expected.to include(have_attributes(name: :filesystem_read_latency, value: be >= 0, labels: { shard: :default })) }
+ it { is_expected.to include(have_attributes(name: :filesystem_write_latency, value: be >= 0, labels: { shard: :default })) }
+ end
+ end
+
context 'when popen always finds required binaries' do
+ let(:timeout_seconds) { 30.to_s }
before do
- allow(Gitlab::Popen).to receive(:popen).and_wrap_original do |method, *args, &block|
+ skip 'timeout or gtimeout not available' unless any_timeout_command_exists?
+
+ allow(described_class).to receive(:exec_with_timeout).and_wrap_original do |method, *args, &block|
begin
method.call(*args, &block)
- rescue RuntimeError
+ rescue RuntimeError, Errno::ENOENT
raise 'expected not to happen'
end
end
+
+ allow(described_class).to receive(:with_timeout) do |args, &block|
+ [timeout_command, timeout_seconds].concat(args)
+ end
end
it_behaves_like 'filesystem checks'
diff --git a/spec/support/timeout_helper.rb b/spec/support/timeout_helper.rb
new file mode 100644
index 00000000000..8b1c14ad2d5
--- /dev/null
+++ b/spec/support/timeout_helper.rb
@@ -0,0 +1,23 @@
+module TimeoutHelper
+ def command_exists?(command)
+ _, status = Gitlab::Popen.popen(%W{ #{command} 1 echo })
+ status == 0
+ rescue Errno::ENOENT
+ false
+ end
+
+ def any_timeout_command_exists?
+ command_exists?('timeout') || command_exists?('gtimeout')
+ end
+
+ def timeout_command
+ @timeout_command ||=
+ if command_exists?('timeout')
+ 'timeout'
+ elsif command_exists?('gtimeout')
+ 'gtimeout'
+ else
+ ''
+ end
+ end
+end