From 3b537b278bbcf4ddacf41bc16ef6d66c5b6f76f0 Mon Sep 17 00:00:00 2001 From: karthik nayak <knayak@gitlab.com> Date: Thu, 15 Jun 2023 18:39:20 +0000 Subject: [PATCH] gemfile: Update `gitaly` to `16.1.0-rc2` Update the gitaly gem from `16.1.0-rc1` to `16.1.0-rc2`. The new gem has the updated protobuf for `GetTreeEntries` RPC, which includes new structured error type `GetTreeEntriesError`. In the following commit[s], we will use this error for better response handling. The new release of the gitaly gem also deprecates the `Gitaly::FindLocalBranchResponse` field. So modify `ref_service_spec.rb` to remove any usage of the field. --- Gemfile | 2 +- Gemfile.checksum | 2 +- Gemfile.lock | 4 +- lib/gitlab/gitaly_client/commit_service.rb | 26 ++++ .../gitaly_client/commit_service_spec.rb | 123 ++++++++++++++++++ .../gitlab/gitaly_client/ref_service_spec.rb | 105 +++++---------- 6 files changed, 186 insertions(+), 76 deletions(-) diff --git a/Gemfile b/Gemfile index a66c0433c7bb5..c7d487f128e88 100644 --- a/Gemfile +++ b/Gemfile @@ -509,7 +509,7 @@ gem 'ssh_data', '~> 1.3' gem 'spamcheck', '~> 1.3.0' # Gitaly GRPC protocol definitions -gem 'gitaly', '~> 16.1.0-rc1' +gem 'gitaly', '~> 16.1.0-rc2' # KAS GRPC protocol definitions gem 'kas-grpc', '~> 0.1.0' diff --git a/Gemfile.checksum b/Gemfile.checksum index a0cc86f4a0166..932fdf01fc101 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -204,7 +204,7 @@ {"name":"gettext_i18n_rails","version":"1.8.0","platform":"ruby","checksum":"95e5cf8440b1e08705b27f2bccb56143272c5a7a0dabcf54ea1bd701140a496f"}, {"name":"gettext_i18n_rails_js","version":"1.3.0","platform":"ruby","checksum":"5d10afe4be3639bff78c50a56768c20f39aecdabc580c08aa45573911c2bd687"}, {"name":"git","version":"1.11.0","platform":"ruby","checksum":"7e95ba4da8298a0373ef1a6862aa22007d761f3c8274b675aa787966fecea0f1"}, -{"name":"gitaly","version":"16.1.0.pre.rc1","platform":"ruby","checksum":"3f61ecd96baf283a3365943dac58c3fb81221fee74d1ccc7931e1adea5403405"}, +{"name":"gitaly","version":"16.1.0.pre.rc2","platform":"ruby","checksum":"0007db161bd3f12321813e7095c8d3266d74b0a22b84d7cba8df864c0b88e3af"}, {"name":"gitlab","version":"4.19.0","platform":"ruby","checksum":"3f645e3e195dbc24f0834fbf83e8ccfb2056d8e9712b01a640aad418a6949679"}, {"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"}, {"name":"gitlab-dangerfiles","version":"3.10.0","platform":"ruby","checksum":"df4cfe051f52529c0256346d89d06d5ef2bb630928754eb620b5233eb9b14041"}, diff --git a/Gemfile.lock b/Gemfile.lock index 9eab52f40a32e..ce718a641885c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -575,7 +575,7 @@ GEM rails (>= 3.2.0) git (1.11.0) rchardet (~> 1.8) - gitaly (16.1.0.pre.rc1) + gitaly (16.1.0.pre.rc2) grpc (~> 1.0) gitlab (4.19.0) httparty (~> 0.20) @@ -1748,7 +1748,7 @@ DEPENDENCIES gettext (~> 3.3) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly (~> 16.1.0.pre.rc1) + gitaly (~> 16.1.0.pre.rc2) gitlab-chronic (~> 0.10.5) gitlab-dangerfiles (~> 3.10.0) gitlab-experiment (~> 0.7.1) diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index f7d6b2402e439..b1f3c2ca38646 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -159,6 +159,17 @@ def tree_entries(repository, revision, path, recursive, skip_flat_paths, paginat end [entries, cursor] + rescue GRPC::BadStatus => e + detailed_error = GitalyClient.decode_detailed_error(e) + + case detailed_error.try(:error) + when :path + raise Gitlab::Git::Index::IndexError, path_error_message(detailed_error.path) + when :resolve_tree + raise Gitlab::Git::Index::IndexError, e.details + else + raise e + end end def commit_count(ref, options = {}) @@ -608,6 +619,21 @@ def find_changed_paths_request(commits) Gitaly::FindChangedPathsRequest.new(repository: @gitaly_repo, requests: commit_requests) end + + def path_error_message(path_error) + case path_error.error_type + when :ERROR_TYPE_EMPTY_PATH + "You must provide a file path" + when :ERROR_TYPE_RELATIVE_PATH_ESCAPES_REPOSITORY + "Path cannot include traversal syntax" + when :ERROR_TYPE_ABSOLUTE_PATH + "Only relative path is accepted" + when :ERROR_TYPE_LONG_PATH + "Path is too long" + else + "Unknown path error" + end + end end end end diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 0bdb9f7938d5f..ddec335a54ad3 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -287,6 +287,129 @@ is_expected.to eq([[], pagination_cursor]) end end + + context 'with structured errors' do + context 'with ResolveTree error' do + before do + expect_any_instance_of(Gitaly::CommitService::Stub) + .to receive(:get_tree_entries) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_raise(raised_error) + end + + let(:raised_error) do + new_detailed_error( + GRPC::Core::StatusCodes::INVALID_ARGUMENT, + "invalid revision or path", + Gitaly::GetTreeEntriesError.new( + resolve_tree: Gitaly::ResolveRevisionError.new( + revision: "incorrect revision" + ))) + end + + it 'raises an IndexError' do + expect { subject }.to raise_error do |error| + expect(error).to be_a(Gitlab::Git::Index::IndexError) + expect(error.message).to eq("invalid revision or path") + end + end + end + + context 'with Path error' do + let(:status_code) { nil } + let(:expected_error) { nil } + + let(:structured_error) do + new_detailed_error( + status_code, + "invalid revision or path", + expected_error) + end + + shared_examples '#get_tree_entries path failure' do + it 'raises an IndexError' do + expect_any_instance_of(Gitaly::CommitService::Stub) + .to receive(:get_tree_entries).with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_raise(structured_error) + + expect { subject }.to raise_error do |error| + expect(error).to be_a(Gitlab::Git::Index::IndexError) + expect(error.message).to eq(expected_message) + end + end + end + + context 'with missing file' do + let(:status_code) { GRPC::Core::StatusCodes::INVALID_ARGUMENT } + let(:expected_message) { "You must provide a file path" } + let(:expected_error) do + Gitaly::GetTreeEntriesError.new( + path: Gitaly::PathError.new( + path: "random path", + error_type: :ERROR_TYPE_EMPTY_PATH + )) + end + + it_behaves_like '#get_tree_entries path failure' + end + + context 'with path including traversal' do + let(:status_code) { GRPC::Core::StatusCodes::INVALID_ARGUMENT } + let(:expected_message) { "Path cannot include traversal syntax" } + let(:expected_error) do + Gitaly::GetTreeEntriesError.new( + path: Gitaly::PathError.new( + path: "foo/../bar", + error_type: :ERROR_TYPE_RELATIVE_PATH_ESCAPES_REPOSITORY + )) + end + + it_behaves_like '#get_tree_entries path failure' + end + + context 'with absolute path' do + let(:status_code) { GRPC::Core::StatusCodes::INVALID_ARGUMENT } + let(:expected_message) { "Only relative path is accepted" } + let(:expected_error) do + Gitaly::GetTreeEntriesError.new( + path: Gitaly::PathError.new( + path: "/bar/foo", + error_type: :ERROR_TYPE_ABSOLUTE_PATH + )) + end + + it_behaves_like '#get_tree_entries path failure' + end + + context 'with long path' do + let(:status_code) { GRPC::Core::StatusCodes::INVALID_ARGUMENT } + let(:expected_message) { "Path is too long" } + let(:expected_error) do + Gitaly::GetTreeEntriesError.new( + path: Gitaly::PathError.new( + path: "long/path/", + error_type: :ERROR_TYPE_LONG_PATH + )) + end + + it_behaves_like '#get_tree_entries path failure' + end + + context 'with unkown path error' do + let(:status_code) { GRPC::Core::StatusCodes::INVALID_ARGUMENT } + let(:expected_message) { "Unknown path error" } + let(:expected_error) do + Gitaly::GetTreeEntriesError.new( + path: Gitaly::PathError.new( + path: "unkown error", + error_type: :ERROR_TYPE_UNSPECIFIED + )) + end + + it_behaves_like '#get_tree_entries path failure' + end + end + end end describe '#commit_count' do diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb index 7bdfa8922d363..89ac0c119bfd8 100644 --- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb @@ -147,92 +147,53 @@ describe '#local_branches' do let(:remote_name) { 'my_remote' } - shared_examples 'common examples' do - it 'sends a find_local_branches message' do - target_commits = create_list(:gitaly_commit, 4) - branches = target_commits.each_with_index.map do |gitaly_commit, i| - Gitaly::FindLocalBranchResponse.new( - name: "#{remote_name}/#{i}", - commit: gitaly_commit, - commit_author: Gitaly::FindLocalBranchCommitAuthor.new( - name: gitaly_commit.author.name, - email: gitaly_commit.author.email, - date: gitaly_commit.author.date, - timezone: gitaly_commit.author.timezone - ), - commit_committer: Gitaly::FindLocalBranchCommitAuthor.new( - name: gitaly_commit.committer.name, - email: gitaly_commit.committer.email, - date: gitaly_commit.committer.date, - timezone: gitaly_commit.committer.timezone - ) - ) - end - - local_branches = target_commits.each_with_index.map do |gitaly_commit, i| - Gitaly::Branch.new(name: "#{remote_name}/#{i}", target_commit: gitaly_commit) - end - - response = if set_local_branches - [ - Gitaly::FindLocalBranchesResponse.new(local_branches: local_branches[0, 2]), - Gitaly::FindLocalBranchesResponse.new(local_branches: local_branches[2, 2]) - ] - else - [ - Gitaly::FindLocalBranchesResponse.new(branches: branches[0, 2]), - Gitaly::FindLocalBranchesResponse.new(branches: branches[2, 2]) - ] - end - - expect_any_instance_of(Gitaly::RefService::Stub) - .to receive(:find_local_branches) - .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) - .and_return(response) - - subject = client.local_branches + it 'sends a find_local_branches message' do + target_commits = create_list(:gitaly_commit, 4) - expect(subject.length).to be(target_commits.length) + local_branches = target_commits.each_with_index.map do |gitaly_commit, i| + Gitaly::Branch.new(name: "#{remote_name}/#{i}", target_commit: gitaly_commit) end - it 'parses and sends the sort parameter' do - expect_any_instance_of(Gitaly::RefService::Stub) - .to receive(:find_local_branches) - .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash)) - .and_return([]) + response = [ + Gitaly::FindLocalBranchesResponse.new(local_branches: local_branches[0, 2]), + Gitaly::FindLocalBranchesResponse.new(local_branches: local_branches[2, 2]) + ] - client.local_branches(sort_by: 'updated_desc') - end + expect_any_instance_of(Gitaly::RefService::Stub) + .to receive(:find_local_branches) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(response) - it 'translates known mismatches on sort param values' do - expect_any_instance_of(Gitaly::RefService::Stub) - .to receive(:find_local_branches) - .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash)) - .and_return([]) + subject = client.local_branches - client.local_branches(sort_by: 'name_asc') - end + expect(subject.length).to be(target_commits.length) + end - it 'uses default sort by name' do - expect_any_instance_of(Gitaly::RefService::Stub) - .to receive(:find_local_branches) - .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash)) - .and_return([]) + it 'parses and sends the sort parameter' do + expect_any_instance_of(Gitaly::RefService::Stub) + .to receive(:find_local_branches) + .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash)) + .and_return([]) - client.local_branches(sort_by: 'invalid') - end + client.local_branches(sort_by: 'updated_desc') end - context 'when local_branches variable is not set' do - let(:set_local_branches) { false } + it 'translates known mismatches on sort param values' do + expect_any_instance_of(Gitaly::RefService::Stub) + .to receive(:find_local_branches) + .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash)) + .and_return([]) - it_behaves_like 'common examples' + client.local_branches(sort_by: 'name_asc') end - context 'when local_branches variable is set' do - let(:set_local_branches) { true } + it 'uses default sort by name' do + expect_any_instance_of(Gitaly::RefService::Stub) + .to receive(:find_local_branches) + .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash)) + .and_return([]) - it_behaves_like 'common examples' + client.local_branches(sort_by: 'invalid') end end -- GitLab