Commit 81a54f29 authored by nnelson's avatar nnelson Committed by Stan Hu

Support retrieval of disk statistics from Gitaly

Add a rudimentary client implementation for the new gitaly
disk_statistics grpc.

Add unit tests.

New gitaly server methods include disk_used and
disk_available.  This commit adds unit testing
for those methods.

Add default server response on error

When the remote service is unavailable, times out,
or otherwise causes a service call failure, return
a default response with trivial values set to its
fields.
parent 9c5e879b
---
title: "Support retrieval of disk statistics from Gitaly"
merge_request: 22226
author: Nels Nelson
type: added
...@@ -53,6 +53,20 @@ module Gitaly ...@@ -53,6 +53,20 @@ module Gitaly
storage_status&.fs_type storage_status&.fs_type
end end
def disk_used
disk_statistics_storage_status&.used
end
def disk_available
disk_statistics_storage_status&.available
end
# Simple convenience method for when obtaining both used and available
# statistics at once is preferred.
def disk_stats
disk_statistics_storage_status
end
def address def address
Gitlab::GitalyClient.address(@storage) Gitlab::GitalyClient.address(@storage)
rescue RuntimeError => e rescue RuntimeError => e
...@@ -65,6 +79,10 @@ module Gitaly ...@@ -65,6 +79,10 @@ module Gitaly
@storage_status ||= info.storage_statuses.find { |s| s.storage_name == storage } @storage_status ||= info.storage_statuses.find { |s| s.storage_name == storage }
end end
def disk_statistics_storage_status
@disk_statistics_storage_status ||= disk_statistics.storage_statuses.find { |s| s.storage_name == storage }
end
def matches_sha? def matches_sha?
match = server_version.match(SHA_VERSION_REGEX) match = server_version.match(SHA_VERSION_REGEX)
return false unless match return false unless match
...@@ -76,7 +94,19 @@ module Gitaly ...@@ -76,7 +94,19 @@ module Gitaly
@info ||= @info ||=
begin begin
Gitlab::GitalyClient::ServerService.new(@storage).info Gitlab::GitalyClient::ServerService.new(@storage).info
rescue GRPC::Unavailable, GRPC::DeadlineExceeded rescue GRPC::Unavailable, GRPC::DeadlineExceeded => ex
Gitlab::ErrorTracking.track_exception(ex)
# This will show the server as being out of date
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
end
end
def disk_statistics
@disk_statistics ||=
begin
Gitlab::GitalyClient::ServerService.new(@storage).disk_statistics
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => ex
Gitlab::ErrorTracking.track_exception(ex)
# This will show the server as being out of date # This will show the server as being out of date
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: []) Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
end end
......
...@@ -432,10 +432,7 @@ module Gitlab ...@@ -432,10 +432,7 @@ module Gitlab
end end
def self.filesystem_id(storage) def self.filesystem_id(storage)
response = Gitlab::GitalyClient::ServerService.new(storage).info Gitlab::GitalyClient::ServerService.new(storage).storage_info&.filesystem_id
storage_status = response.storage_statuses.find { |status| status.storage_name == storage }
storage_status&.filesystem_id
end end
def self.filesystem_id_from_disk(storage) def self.filesystem_id_from_disk(storage)
...@@ -446,6 +443,14 @@ module Gitlab ...@@ -446,6 +443,14 @@ module Gitlab
nil nil
end end
def self.filesystem_disk_available(storage)
Gitlab::GitalyClient::ServerService.new(storage).storage_disk_statistics&.available
end
def self.filesystem_disk_used(storage)
Gitlab::GitalyClient::ServerService.new(storage).storage_disk_statistics&.used
end
def self.timeout(timeout_name) def self.timeout(timeout_name)
Gitlab::CurrentSettings.current_application_settings[timeout_name] Gitlab::CurrentSettings.current_application_settings[timeout_name]
end end
......
...@@ -13,6 +13,24 @@ module Gitlab ...@@ -13,6 +13,24 @@ module Gitlab
def info def info
GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new, timeout: GitalyClient.fast_timeout) GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new, timeout: GitalyClient.fast_timeout)
end end
def disk_statistics
GitalyClient.call(@storage, :server_service, :disk_statistics, Gitaly::DiskStatisticsRequest.new, timeout: GitalyClient.fast_timeout)
end
def storage_info
storage_specific(info)
end
def storage_disk_statistics
storage_specific(disk_statistics)
end
private
def storage_specific(response)
response.storage_statuses.find { |status| status.storage_name == @storage }
end
end end
end end
end end
...@@ -66,6 +66,53 @@ describe Gitaly::Server do ...@@ -66,6 +66,53 @@ describe Gitaly::Server do
end end
end end
context "when examining disk statistics for a given server" do
let(:disk_available) { 42 }
let(:disk_used) { 42 }
let(:storage_status) { double('storage_status') }
before do
allow(storage_status).to receive(:storage_name).and_return('default')
allow(storage_status).to receive(:available).and_return(disk_available)
allow(storage_status).to receive(:used).and_return(disk_used)
response = double("response")
allow(response).to receive(:storage_statuses).and_return([storage_status])
allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
allow(instance).to receive(:disk_statistics).and_return(response)
end
end
describe '#disk_available' do
subject { server.disk_available }
it { is_expected.to be_present }
it "returns disk available for the storage of the instantiated server" do
is_expected.to eq(disk_available)
end
end
describe '#disk_used' do
subject { server.disk_used }
it { is_expected.to be_present }
it "returns disk used for the storage of the instantiated server" do
is_expected.to eq(disk_used)
end
end
describe '#disk_stats' do
subject { server.disk_stats }
it { is_expected.to be_present }
it "returns the storage of the instantiated server" do
is_expected.to eq(storage_status)
end
end
end
describe '#expected_version?' do describe '#expected_version?' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
......
...@@ -52,7 +52,7 @@ describe Gitlab::GitalyClient do ...@@ -52,7 +52,7 @@ describe Gitlab::GitalyClient do
end end
describe '.filesystem_id' do describe '.filesystem_id' do
it 'returns an empty string when the storage is not found in the response' do it 'returns an empty string when the relevant storage status is not found in the response' do
response = double("response") response = double("response")
allow(response).to receive(:storage_statuses).and_return([]) allow(response).to receive(:storage_statuses).and_return([])
allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance| allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
...@@ -63,6 +63,63 @@ describe Gitlab::GitalyClient do ...@@ -63,6 +63,63 @@ describe Gitlab::GitalyClient do
end end
end end
context 'when the relevant storage status is not found' do
before do
response = double('response')
allow(response).to receive(:storage_statuses).and_return([])
allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
allow(instance).to receive(:disk_statistics).and_return(response)
expect(instance).to receive(:storage_disk_statistics)
end
end
describe '.filesystem_disk_available' do
it 'returns nil when the relevant storage status is not found in the response' do
expect(described_class.filesystem_disk_available('default')).to eq(nil)
end
end
describe '.filesystem_disk_used' do
it 'returns nil when the relevant storage status is not found in the response' do
expect(described_class.filesystem_disk_used('default')).to eq(nil)
end
end
end
context 'when the relevant storage status is found' do
let(:disk_available) { 42 }
let(:disk_used) { 42 }
let(:storage_status) { double('storage_status') }
before do
allow(storage_status).to receive(:storage_name).and_return('default')
allow(storage_status).to receive(:used).and_return(disk_used)
allow(storage_status).to receive(:available).and_return(disk_available)
response = double('response')
allow(response).to receive(:storage_statuses).and_return([storage_status])
allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
allow(instance).to receive(:disk_statistics).and_return(response)
end
expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
expect(instance).to receive(:storage_disk_statistics).and_return(storage_status)
end
end
describe '.filesystem_disk_available' do
it 'returns disk available when the relevant storage status is found in the response' do
expect(storage_status).to receive(:available)
expect(described_class.filesystem_disk_available('default')).to eq(disk_available)
end
end
describe '.filesystem_disk_used' do
it 'returns disk used when the relevant storage status is found in the response' do
expect(storage_status).to receive(:used)
expect(described_class.filesystem_disk_used('default')).to eq(disk_used)
end
end
end
describe '.stub_class' do describe '.stub_class' do
it 'returns the gRPC health check stub' do it 'returns the gRPC health check stub' do
expect(described_class.stub_class(:health_check)).to eq(::Grpc::Health::V1::Health::Stub) expect(described_class.stub_class(:health_check)).to eq(::Grpc::Health::V1::Health::Stub)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment