Commit 9a20a930 authored by Igor Drozdov's avatar Igor Drozdov

Render gitaly-unavailable error for Tags page

Changelog: performance
parent df77281e
...@@ -18,17 +18,21 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -18,17 +18,21 @@ class Projects::TagsController < Projects::ApplicationController
params[:sort] = params[:sort].presence || sort_value_recently_updated params[:sort] = params[:sort].presence || sort_value_recently_updated
@sort = params[:sort] @sort = params[:sort]
@tags = TagsFinder.new(@repository, params).execute
@tags = Kaminari.paginate_array(@tags).page(params[:page])
@tags, @tags_loading_error = TagsFinder.new(@repository, params).execute
@tags = Kaminari.paginate_array(@tags).page(params[:page])
tag_names = @tags.map(&:name) tag_names = @tags.map(&:name)
@tags_pipelines = @project.ci_pipelines.latest_successful_for_refs(tag_names) @tags_pipelines = @project.ci_pipelines.latest_successful_for_refs(tag_names)
@releases = project.releases.where(tag: tag_names) @releases = project.releases.where(tag: tag_names)
@tag_pipeline_statuses = Ci::CommitStatusesFinder.new(@project, @repository, current_user, @tags).execute @tag_pipeline_statuses = Ci::CommitStatusesFinder.new(@project, @repository, current_user, @tags).execute
respond_to do |format| respond_to do |format|
format.html status = @tags_loading_error ? :service_unavailable : :ok
format.atom { render layout: 'xml.atom' }
format.html { render status: status }
format.atom { render layout: 'xml.atom', status: status }
end end
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -284,9 +284,9 @@ class ProjectsController < Projects::ApplicationController ...@@ -284,9 +284,9 @@ class ProjectsController < Projects::ApplicationController
end end
if find_tags && @repository.tag_count.nonzero? if find_tags && @repository.tag_count.nonzero?
tags = TagsFinder.new(@repository, params).execute.take(100).map(&:name) tags, _ = TagsFinder.new(@repository, params).execute
options['Tags'] = tags options['Tags'] = tags.take(100).map(&:name)
end end
# If reference is commit id - we should add it to branch/tag selectbox # If reference is commit id - we should add it to branch/tag selectbox
......
...@@ -7,6 +7,9 @@ class TagsFinder < GitRefsFinder ...@@ -7,6 +7,9 @@ class TagsFinder < GitRefsFinder
def execute def execute
tags = repository.tags_sorted_by(sort) tags = repository.tags_sorted_by(sort)
by_search(tags)
[by_search(tags), nil]
rescue Gitlab::Git::CommandError => e
[[], e]
end end
end end
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
= render_if_exists 'projects/commits/mirror_status' = render_if_exists 'projects/commits/mirror_status'
- if @tags_loading_error
= render 'shared/errors/gitaly_unavailable', reason: s_('TagsPage|Unable to load tags')
.tags .tags
- if @tags.any? - if @tags.any?
%ul.flex-list.content-list %ul.flex-list.content-list
......
.gl-alert.gl-alert-danger.gl-mb-5.gl-mt-5
.gl-alert-container
= sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-content
.gl-alert-title
= reason
.gl-alert-body
= s_('The git server, Gitaly, is not available at this time. Please contact your administrator.')
...@@ -24,7 +24,7 @@ module API ...@@ -24,7 +24,7 @@ module API
use :pagination use :pagination
end end
get ':id/repository/tags', feature_category: :source_code_management do get ':id/repository/tags', feature_category: :source_code_management do
tags = ::TagsFinder.new(user_project.repository, tags, _ = ::TagsFinder.new(user_project.repository,
sort: "#{params[:order_by]}_#{params[:sort]}", sort: "#{params[:order_by]}_#{params[:sort]}",
search: params[:search]).execute search: params[:search]).execute
......
...@@ -33197,6 +33197,9 @@ msgstr "" ...@@ -33197,6 +33197,9 @@ msgstr ""
msgid "TagsPage|This tag has no release notes." msgid "TagsPage|This tag has no release notes."
msgstr "" msgstr ""
msgid "TagsPage|Unable to load tags"
msgstr ""
msgid "TagsPage|Use git tag command to add a new one:" msgid "TagsPage|Use git tag command to add a new one:"
msgstr "" msgstr ""
...@@ -33779,6 +33782,9 @@ msgstr "" ...@@ -33779,6 +33782,9 @@ msgstr ""
msgid "The form contains the following warning:" msgid "The form contains the following warning:"
msgstr "" msgstr ""
msgid "The git server, Gitaly, is not available at this time. Please contact your administrator."
msgstr ""
msgid "The global settings require you to enable Two-Factor Authentication for your account." msgid "The global settings require you to enable Two-Factor Authentication for your account."
msgstr "" msgstr ""
......
...@@ -17,6 +17,25 @@ RSpec.describe Projects::TagsController do ...@@ -17,6 +17,25 @@ RSpec.describe Projects::TagsController do
expect(assigns(:tags).map(&:name)).to include('v1.1.0', 'v1.0.0') expect(assigns(:tags).map(&:name)).to include('v1.1.0', 'v1.0.0')
end end
context 'when Gitaly is unavailable' do
where(:format) do
[:html, :atom]
end
with_them do
it 'returns 503 status code' do
expect_next_instance_of(TagsFinder) do |finder|
expect(finder).to receive(:execute).and_return([[], Gitlab::Git::CommandError.new])
end
get :index, params: { namespace_id: project.namespace.to_param, project_id: project }, format: format
expect(assigns(:tags)).to eq([])
expect(response).to have_gitlab_http_status(:service_unavailable)
end
end
end
it 'returns releases matching those tags' do it 'returns releases matching those tags' do
subject subject
......
...@@ -8,7 +8,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do ...@@ -8,7 +8,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
context 'tag refs' do context 'tag refs' do
let_it_be(:tags) { TagsFinder.new(project.repository, {}).execute } let_it_be(:tags) { project.repository.tags }
let(:subject) { described_class.new(project, project.repository, user, tags).execute } let(:subject) { described_class.new(project, project.repository, user, tags).execute }
...@@ -131,7 +131,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do ...@@ -131,7 +131,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do
end end
context 'CI pipelines visible to' do context 'CI pipelines visible to' do
let_it_be(:tags) { TagsFinder.new(project.repository, {}).execute } let_it_be(:tags) { project.repository.tags }
let(:subject) { described_class.new(project, project.repository, user, tags).execute } let(:subject) { described_class.new(project, project.repository, user, tags).execute }
...@@ -161,7 +161,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do ...@@ -161,7 +161,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do
context 'when not a member of a private project' do context 'when not a member of a private project' do
let(:private_project) { create(:project, :private, :repository) } let(:private_project) { create(:project, :private, :repository) }
let(:private_tags) { TagsFinder.new(private_tags.repository, {}).execute } let(:private_tags) { private_tags.repository.tags }
let(:private_subject) { described_class.new(private_project, private_project.repository, user, tags).execute } let(:private_subject) { described_class.new(private_project, private_project.repository, user, tags).execute }
before do before do
......
...@@ -3,93 +3,76 @@ ...@@ -3,93 +3,76 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe TagsFinder do RSpec.describe TagsFinder do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let(:repository) { project.repository } let_it_be(:repository) { project.repository }
def load_tags(params)
tags_finder = described_class.new(repository, params)
tags, error = tags_finder.execute
expect(error).to eq(nil)
tags
end
describe '#execute' do describe '#execute' do
context 'sort only' do context 'sort only' do
it 'sorts by name' do it 'sorts by name' do
tags_finder = described_class.new(repository, {}) expect(load_tags({}).first.name).to eq("v1.0.0")
result = tags_finder.execute
expect(result.first.name).to eq("v1.0.0")
end end
it 'sorts by recently_updated' do it 'sorts by recently_updated' do
tags_finder = described_class.new(repository, { sort: 'updated_desc' })
result = tags_finder.execute
recently_updated_tag = repository.tags.max do |a, b| recently_updated_tag = repository.tags.max do |a, b|
repository.commit(a.dereferenced_target).committed_date <=> repository.commit(b.dereferenced_target).committed_date repository.commit(a.dereferenced_target).committed_date <=> repository.commit(b.dereferenced_target).committed_date
end end
expect(result.first.name).to eq(recently_updated_tag.name) params = { sort: 'updated_desc' }
expect(load_tags(params).first.name).to eq(recently_updated_tag.name)
end end
it 'sorts by last_updated' do it 'sorts by last_updated' do
tags_finder = described_class.new(repository, { sort: 'updated_asc' }) params = { sort: 'updated_asc' }
result = tags_finder.execute
expect(result.first.name).to eq('v1.0.0') expect(load_tags(params).first.name).to eq('v1.0.0')
end end
end end
context 'filter only' do context 'filter only' do
it 'filters tags by name' do it 'filters tags by name' do
tags_finder = described_class.new(repository, { search: '1.0.0' }) result = load_tags({ search: '1.0.0' })
result = tags_finder.execute
expect(result.first.name).to eq('v1.0.0') expect(result.first.name).to eq('v1.0.0')
expect(result.count).to eq(1) expect(result.count).to eq(1)
end end
it 'does not find any tags with that name' do it 'does not find any tags with that name' do
tags_finder = described_class.new(repository, { search: 'hey' }) expect(load_tags({ search: 'hey' }).count).to eq(0)
result = tags_finder.execute
expect(result.count).to eq(0)
end end
it 'filters tags by name that begins with' do it 'filters tags by name that begins with' do
params = { search: '^v1.0' } result = load_tags({ search: '^v1.0' })
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
expect(result.first.name).to eq('v1.0.0') expect(result.first.name).to eq('v1.0.0')
expect(result.count).to eq(1) expect(result.count).to eq(1)
end end
it 'filters tags by name that ends with' do it 'filters tags by name that ends with' do
params = { search: '0.0$' } result = load_tags({ search: '0.0$' })
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
expect(result.first.name).to eq('v1.0.0') expect(result.first.name).to eq('v1.0.0')
expect(result.count).to eq(1) expect(result.count).to eq(1)
end end
it 'filters tags by nonexistent name that begins with' do it 'filters tags by nonexistent name that begins with' do
params = { search: '^nope' } result = load_tags({ search: '^nope' })
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
expect(result.count).to eq(0) expect(result.count).to eq(0)
end end
it 'filters tags by nonexistent name that ends with' do it 'filters tags by nonexistent name that ends with' do
params = { search: 'nope$' } result = load_tags({ search: 'nope$' })
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
expect(result.count).to eq(0) expect(result.count).to eq(0)
end end
end end
...@@ -97,7 +80,7 @@ RSpec.describe TagsFinder do ...@@ -97,7 +80,7 @@ RSpec.describe TagsFinder do
context 'filter and sort' do context 'filter and sort' do
let(:tags_to_compare) { %w[v1.0.0 v1.1.0] } let(:tags_to_compare) { %w[v1.0.0 v1.1.0] }
subject { described_class.new(repository, params).execute.select { |tag| tags_to_compare.include?(tag.name) } } subject { load_tags(params).select { |tag| tags_to_compare.include?(tag.name) } }
context 'when sort by updated_desc' do context 'when sort by updated_desc' do
let(:params) { { sort: 'updated_desc', search: 'v1' } } let(:params) { { sort: 'updated_desc', search: 'v1' } }
...@@ -117,5 +100,17 @@ RSpec.describe TagsFinder do ...@@ -117,5 +100,17 @@ RSpec.describe TagsFinder do
end end
end end
end end
context 'when Gitaly is unavailable' do
it 'returns empty list of tags' do
expect(Gitlab::GitalyClient).to receive(:call).and_raise(GRPC::Unavailable)
tags_finder = described_class.new(repository, {})
tags, error = tags_finder.execute
expect(error).to be_a(Gitlab::Git::CommandError)
expect(tags).to eq([])
end
end
end end
end end
...@@ -12,6 +12,7 @@ RSpec.describe Gitlab::Git::WrapsGitalyErrors do ...@@ -12,6 +12,7 @@ RSpec.describe Gitlab::Git::WrapsGitalyErrors do
mapping = { mapping = {
GRPC::NotFound => Gitlab::Git::Repository::NoRepository, GRPC::NotFound => Gitlab::Git::Repository::NoRepository,
GRPC::InvalidArgument => ArgumentError, GRPC::InvalidArgument => ArgumentError,
GRPC::DeadlineExceeded => Gitlab::Git::CommandTimedOut,
GRPC::BadStatus => Gitlab::Git::CommandError GRPC::BadStatus => Gitlab::Git::CommandError
} }
......
...@@ -3,10 +3,11 @@ ...@@ -3,10 +3,11 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'projects/tags/index.html.haml' do RSpec.describe 'projects/tags/index.html.haml' do
let(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let(:tags) { TagsFinder.new(project.repository, {}).execute } let_it_be(:tags) { project.repository.tags }
let(:git_tag) { project.repository.tags.last } let_it_be(:git_tag) { project.repository.tags.last }
let(:release) { create(:release, project: project, sha: git_tag.target_commit.sha) } let_it_be(:release) { create(:release, project: project, sha: git_tag.target_commit.sha) }
let(:pipeline) { create(:ci_pipeline, :success, project: project, ref: git_tag.name, sha: release.sha) } let(:pipeline) { create(:ci_pipeline, :success, project: project, ref: git_tag.name, sha: release.sha) }
before do before do
...@@ -86,4 +87,17 @@ RSpec.describe 'projects/tags/index.html.haml' do ...@@ -86,4 +87,17 @@ RSpec.describe 'projects/tags/index.html.haml' do
expect(page.all('.tags .content-list li')).not_to have_css 'svg.s24' expect(page.all('.tags .content-list li')).not_to have_css 'svg.s24'
end end
end end
context 'when Gitaly is unavailable' do
it 'renders an error' do
assign(:tags_loading_error, GRPC::Unavailable.new)
content = render
expect(content).to include("Unable to load tags")
expect(content).to include(
"The git server, Gitaly, is not available at this time. Please contact your administrator."
)
end
end
end end
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