diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index 98db0d8fa9bdc05c5cfeb578683f18bc3dac9de3..6aed65b586ecbed6ea959438a2495ed8e15f6132 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -2825,7 +2825,6 @@ Layout/LineLength: - 'ee/spec/services/ee/boards/issues/list_service_spec.rb' - 'ee/spec/services/ee/boards/lists/max_limits_spec.rb' - 'ee/spec/services/ee/ci/pipeline_processing/atomic_processing_service_spec.rb' - - 'ee/spec/services/ee/commits/create_service_spec.rb' - 'ee/spec/services/ee/git/wiki_push_service_spec.rb' - 'ee/spec/services/ee/groups/autocomplete_service_spec.rb' - 'ee/spec/services/ee/groups/deploy_tokens/create_service_spec.rb' diff --git a/ee/app/services/ee/commits/create_service.rb b/ee/app/services/ee/commits/create_service.rb index af5b9c35bf78277a63d8e42ed15da4251af2222b..fd4286ccee89bbf228cfd547bd6b800a381ab433 100644 --- a/ee/app/services/ee/commits/create_service.rb +++ b/ee/app/services/ee/commits/create_service.rb @@ -15,13 +15,21 @@ def validate! end def validate_repository_size! - size_checker = project.repository_size_checker - if size_checker.above_size_limit? raise_error(size_checker.error_message.commit_error) end end + def size_checker + root_namespace = project.namespace.root_ancestor + + if ::Namespaces::Storage::EnforcementCheckService.enforce_limit?(root_namespace) + ::EE::Namespace::RootStorageSize.new(root_namespace) + else + project.repository_size_checker + end + end + def extracted_paths paths = [] diff --git a/ee/spec/features/ide/user_commits_changes_spec.rb b/ee/spec/features/ide/user_commits_changes_spec.rb index 2673e6a347e422b0a3efaadf7ecba5bdef290570..451ca263bbbfd34dc649a85c8e1b7b6a7718be4a 100644 --- a/ee/spec/features/ide/user_commits_changes_spec.rb +++ b/ee/spec/features/ide/user_commits_changes_spec.rb @@ -4,31 +4,67 @@ RSpec.describe 'EE IDE user commits changes', :js do include WebIdeSpecHelpers + include NamespaceStorageHelpers - let(:project) { create(:project, :custom_repo, files: { 'docs/CODEOWNERS' => "[Backend]\n*.rb @ruby-owner" }) } - let(:ruby_owner) { create(:user, username: 'ruby-owner') } - let(:user) { project.first_owner } + context 'code owners' do + let(:project) { create(:project, :custom_repo, files: { 'docs/CODEOWNERS' => "[Backend]\n*.rb @ruby-owner" }) } + let(:ruby_owner) { create(:user, username: 'ruby-owner') } + let(:user) { project.first_owner } - before do - stub_licensed_features(code_owners: true, code_owner_approval_required: true) + before do + stub_licensed_features(code_owners: true, code_owner_approval_required: true) - project.add_developer(ruby_owner) + project.add_developer(ruby_owner) - create(:protected_branch, - name: 'master', - code_owner_approval_required: true, - project: project) + create(:protected_branch, + name: 'master', + code_owner_approval_required: true, + project: project) - sign_in(user) + sign_in(user) - ide_visit(project) + ide_visit(project) + end + + it 'does not show an error message' do + ide_create_new_file('test.rb', content: '# A ruby file') + + ide_commit + + expect(page).not_to have_content('CODEOWNERS rule violation') + end end - it 'does not show an error message' do - ide_create_new_file('test.rb', content: '# A ruby file') + context 'when namespace storage limits have been exceeded', :saas do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, :repository, group: group) } + let(:expected_message) do + "Your push to this repository has been rejected because " \ + "the namespace storage limit of 10 MB has been reached. " \ + "Reduce your namespace storage or purchase additional storage." + end + + before do + create(:gitlab_subscription, :ultimate, namespace: group) + create(:namespace_root_storage_statistics, namespace: group) + group.add_owner(user) + + enforce_namespace_storage_limit(group) + set_storage_size_limit(group, megabytes: 10) + set_used_storage(group, megabytes: 14) + + sign_in(user) + end + + it 'rejects the commit' do + ide_visit(project) + + ide_create_new_file('test.txt', content: 'A new file') - ide_commit + ide_commit - expect(page).not_to have_content('CODEOWNERS rule violation') + expect(page).to have_content(expected_message) + end end end diff --git a/ee/spec/requests/api/commits_spec.rb b/ee/spec/requests/api/commits_spec.rb index f847db607e77c188e4f2523e556f245cc04d9bd1..1699db54a39d1f518c250dc8ec2add8b573451ee 100644 --- a/ee/spec/requests/api/commits_spec.rb +++ b/ee/spec/requests/api/commits_spec.rb @@ -3,6 +3,8 @@ require "spec_helper" RSpec.describe API::Commits do + include NamespaceStorageHelpers + let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :repository, creator: user, path: "my.project") } @@ -170,6 +172,29 @@ it_behaves_like "handling the codeowners interaction" end end + + context 'with an exceeded namespace storage limit', :saas do + let(:namespace) { project.namespace } + + before do + create(:gitlab_subscription, :ultimate, namespace: namespace) + create(:namespace_root_storage_statistics, namespace: namespace) + enforce_namespace_storage_limit(namespace) + set_storage_size_limit(namespace, megabytes: 5) + set_used_storage(namespace, megabytes: 6) + end + + it "rejects the request" do + post api(route, user), params: { branch: branch } + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq( + 'Your push to this repository has been rejected because the ' \ + 'namespace storage limit of 5 MB has been reached. ' \ + 'Reduce your namespace storage or purchase additional storage.' + ) + end + end end describe "POST :id/repository/commits/:sha/revert" do @@ -204,5 +229,28 @@ it_behaves_like "handling the codeowners interaction" end end + + context 'with an exceeded namespace storage limit', :saas do + let(:namespace) { project.namespace } + + before do + create(:gitlab_subscription, :ultimate, namespace: namespace) + create(:namespace_root_storage_statistics, namespace: namespace) + enforce_namespace_storage_limit(namespace) + set_storage_size_limit(namespace, megabytes: 5) + set_used_storage(namespace, megabytes: 6) + end + + it "rejects the request" do + post api(route, user), params: { branch: branch } + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq( + 'Your push to this repository has been rejected because the ' \ + 'namespace storage limit of 5 MB has been reached. ' \ + 'Reduce your namespace storage or purchase additional storage.' + ) + end + end end end diff --git a/ee/spec/requests/api/files_spec.rb b/ee/spec/requests/api/files_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..ab6de0f4e5b082a2e23a95cc910718dffe8ccdca --- /dev/null +++ b/ee/spec/requests/api/files_spec.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Files do + include NamespaceStorageHelpers + + let(:group) { create(:group) } + let(:user) { create(:user) } + let(:project) { create(:project, :repository, group: group) } + let(:file_path) { "files%2Fruby%2Fpopen%2Erb" } + + before do + project.add_developer(user) + end + + def route(file_path = nil) + "/projects/#{project.id}/repository/files/#{file_path}" + end + + describe "POST /projects/:id/repository/files/:file_path" do + let(:file_path) { "new_subfolder%2Fnewfile%2Erb" } + let(:params) do + { + branch: "master", + content: "puts 8", + commit_message: "Added newfile" + } + end + + context 'with an exceeded namespace storage limit', :saas do + before do + create(:gitlab_subscription, :ultimate, namespace: group) + create(:namespace_root_storage_statistics, namespace: group) + enforce_namespace_storage_limit(group) + set_storage_size_limit(group, megabytes: 5) + set_used_storage(group, megabytes: 6) + end + + it 'rejects the request' do + post api(route(file_path), user), params: params + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq( + 'Your push to this repository has been rejected because the ' \ + 'namespace storage limit of 5 MB has been reached. ' \ + 'Reduce your namespace storage or purchase additional storage.' + ) + end + end + end + + describe "PUT /projects/:id/repository/files/:file_path" do + let(:params) do + { + branch: 'master', + content: 'puts 8', + commit_message: 'Change file' + } + end + + context 'with an exceeded namespace storage limit', :saas do + before do + create(:gitlab_subscription, :ultimate, namespace: group) + create(:namespace_root_storage_statistics, namespace: group) + enforce_namespace_storage_limit(group) + set_storage_size_limit(group, megabytes: 5) + set_used_storage(group, megabytes: 6) + end + + it 'rejects the request' do + put api(route(file_path), user), params: params + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq( + 'Your push to this repository has been rejected because the ' \ + 'namespace storage limit of 5 MB has been reached. ' \ + 'Reduce your namespace storage or purchase additional storage.' + ) + end + end + end + + describe "DELETE /projects/:id/repository/files/:file_path" do + let(:params) do + { + branch: 'master', + commit_message: 'Delete file' + } + end + + context 'with an exceeded namespace storage limit', :saas do + before do + create(:gitlab_subscription, :ultimate, namespace: group) + create(:namespace_root_storage_statistics, namespace: group) + enforce_namespace_storage_limit(group) + set_storage_size_limit(group, megabytes: 5) + set_used_storage(group, megabytes: 6) + end + + it 'rejects the request' do + delete api(route(file_path), user), params: params + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq( + 'Your push to this repository has been rejected because the ' \ + 'namespace storage limit of 5 MB has been reached. ' \ + 'Reduce your namespace storage or purchase additional storage.' + ) + end + end + end +end diff --git a/ee/spec/requests/api/submodules_spec.rb b/ee/spec/requests/api/submodules_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1cdf3c91028f76a9e7808ee3fbbbfb6b5fe6a5bb --- /dev/null +++ b/ee/spec/requests/api/submodules_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Submodules do + include NamespaceStorageHelpers + + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, :repository, group: group) } + let(:submodule) { 'six' } + + let(:params) do + { + submodule: submodule, + commit_sha: 'e25eda1fece24ac7a03624ed1320f82396f35bd8', + branch: 'master', + commit_message: 'update submodule' + } + end + + before do + project.add_developer(user) + end + + def route(submodule) + "/projects/#{project.id}/repository/submodules/#{submodule}" + end + + describe "PUT /projects/:id/repository/submodule/:submodule" do + context 'with an exceeded namespace storage limit', :saas do + before do + create(:gitlab_subscription, :ultimate, namespace: group) + create(:namespace_root_storage_statistics, namespace: group) + enforce_namespace_storage_limit(group) + set_storage_size_limit(group, megabytes: 4) + set_used_storage(group, megabytes: 5) + end + + it 'rejects the request' do + put api(route(submodule), user), params: params + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq( + 'Your push to this repository has been rejected because the ' \ + 'namespace storage limit of 4 MB has been reached. ' \ + 'Reduce your namespace storage or purchase additional storage.' + ) + end + end + end +end diff --git a/ee/spec/services/ee/commits/create_service_spec.rb b/ee/spec/services/ee/commits/create_service_spec.rb index 5240d958f14fbf660e3e7d8e1a2bf9168b5f2179..e3e79c2dcf92cb169572c431a07b0d546fc38c5a 100644 --- a/ee/spec/services/ee/commits/create_service_spec.rb +++ b/ee/spec/services/ee/commits/create_service_spec.rb @@ -3,33 +3,83 @@ require 'spec_helper' RSpec.describe Commits::CreateService do - let(:project) { create(:project) } + include NamespaceStorageHelpers + let(:user) { create(:user) } - let(:branch_name) { 'master' } - let(:extra_params) { {} } + let(:group) { create(:group) } + let(:project) { create(:project, group: group) } before do project.add_maintainer(user) end subject(:service) do - described_class.new(project, user, start_branch: branch_name, branch_name: branch_name, **extra_params) + described_class.new(project, user, start_branch: 'master', branch_name: 'master') end describe '#execute' do - before do - stub_licensed_features(repository_size_limit: true) - project.update!(repository_size_limit: 1) - allow(project.repository_size_checker).to receive(:current_size).and_return(2) + context 'when the repository size limit has been exceeded' do + before do + stub_licensed_features(repository_size_limit: true) + project.update!(repository_size_limit: 1) + allow(project.repository_size_checker).to receive(:current_size).and_return(2) + end + + it 'raises an error' do + expect(Gitlab::ErrorTracking).to receive(:log_exception) + .with(instance_of(Commits::CreateService::ValidationError)).and_call_original + + result = service.execute + + expect(result[:status]).to be(:error) + expect(result[:message]).to eq( + 'Your changes could not be committed, because this ' \ + 'repository has exceeded its size limit of 1 Byte by 1 Byte' + ) + end end - subject(:result) { service.execute } + context 'when the namespace storage limit has been exceeded', :saas do + before do + create(:gitlab_subscription, :ultimate, namespace: group) + create(:namespace_root_storage_statistics, namespace: group) + enforce_namespace_storage_limit(group) + set_storage_size_limit(group, megabytes: 1) + set_used_storage(group, megabytes: 2) + end + + it 'raises an error' do + expect(Gitlab::ErrorTracking).to receive(:log_exception) + .with(instance_of(Commits::CreateService::ValidationError)).and_call_original + + result = service.execute + + expect(result[:status]).to be(:error) + expect(result[:message]).to eq( + 'Your push to this repository has been rejected because ' \ + 'the namespace storage limit of 1 MB has been reached. ' \ + 'Reduce your namespace storage or purchase additional storage.' + ) + end + + context 'with a subgroup project' do + let(:subgroup) { create(:group, parent: group) } + let(:project) { create(:project, group: subgroup) } + + it 'raises an error' do + expect(Gitlab::ErrorTracking).to receive(:log_exception) + .with(instance_of(Commits::CreateService::ValidationError)).and_call_original + + result = service.execute - it 'raises an error if the repositoy exceeds the size limit' do - expect(Gitlab::ErrorTracking).to receive(:log_exception) - .with(instance_of(Commits::CreateService::ValidationError)).and_call_original - expect(result[:status]).to be(:error) - expect(result[:message]).to eq('Your changes could not be committed, because this repository has exceeded its size limit of 1 Byte by 1 Byte') + expect(result[:status]).to be(:error) + expect(result[:message]).to eq( + 'Your push to this repository has been rejected because ' \ + 'the namespace storage limit of 1 MB has been reached. ' \ + 'Reduce your namespace storage or purchase additional storage.' + ) + end + end end end end