From af2a9da12e96e9f455238131212e53c80fe7bfa6 Mon Sep 17 00:00:00 2001 From: Vijay Hawoldar <vhawoldar@gitlab.com> Date: Thu, 20 Jul 2023 17:20:33 +0000 Subject: [PATCH] Fix repository push error message Adds a new error message handling class to return the correct push error/warning messages for repository size limits Changelog: fixed EE: true --- .../namespaces/storage/root_excess_size.rb | 8 +-- .../gitlab/root_excess_size_error_message.rb | 53 ++++++++++++++++++ .../root_excess_size_error_message_spec.rb | 55 +++++++++++++++++++ .../services/ee/post_receive_service_spec.rb | 54 +++++++++++------- .../helpers/repository_storage_helpers.rb | 16 ------ lib/gitlab/repository_size_error_message.rb | 32 +---------- 6 files changed, 145 insertions(+), 73 deletions(-) create mode 100644 ee/lib/gitlab/root_excess_size_error_message.rb create mode 100644 ee/spec/lib/gitlab/root_excess_size_error_message_spec.rb delete mode 100644 ee/spec/support/helpers/repository_storage_helpers.rb diff --git a/ee/app/models/namespaces/storage/root_excess_size.rb b/ee/app/models/namespaces/storage/root_excess_size.rb index 60c8e7dcd6c0..1dcbb1e6edc2 100644 --- a/ee/app/models/namespaces/storage/root_excess_size.rb +++ b/ee/app/models/namespaces/storage/root_excess_size.rb @@ -5,6 +5,8 @@ module Storage class RootExcessSize include ::Gitlab::Utils::StrongMemoize + attr_reader :root_namespace + def initialize(root_namespace) @root_namespace = root_namespace.root_ancestor # just in case the true root isn't passed end @@ -46,16 +48,12 @@ def enforce_limit? def error_message message_params = { namespace_name: root_namespace.name } - @error_message_object ||= ::Gitlab::RepositorySizeErrorMessage.new(self, message_params) + @error_message_object ||= ::Gitlab::RootExcessSizeErrorMessage.new(self, message_params) end def enforcement_type :project_repository_limit end - - private - - attr_reader :root_namespace end end end diff --git a/ee/lib/gitlab/root_excess_size_error_message.rb b/ee/lib/gitlab/root_excess_size_error_message.rb new file mode 100644 index 000000000000..57d8206ca937 --- /dev/null +++ b/ee/lib/gitlab/root_excess_size_error_message.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Gitlab + class RootExcessSizeErrorMessage # rubocop:disable Gitlab/NamespacedClass + include ActiveSupport::NumberHelper + + def initialize(checker, message_params = {}) + @checker = checker + @current_size = formatted(checker.current_size) + @size_limit = formatted(checker.limit) + @namespace_name = message_params[:namespace_name] + end + + def push_warning + <<~MSG.squish + ##### WARNING ##### You have used #{usage_percentage} of the storage quota for this project + (#{current_size} of #{size_limit}). If a project reaches 100% of the storage quota (#{size_limit}) + the project will be in a read-only state, and you won't be able to push to your repository or add large files. + To reduce storage usage, reduce git repository and git LFS storage. For more information about storage limits, + see our docs: #{limit_docs_url}. + MSG + end + + def push_error + <<~MSG.squish + You have reached the free storage limit of #{repository_limit} on one or more projects. + To unlock your projects over the free #{repository_limit} project limit, you must purchase + additional storage. You can't push to your repository, create pipelines, create issues, or add comments. + To reduce storage capacity, you can delete unused repositories, artifacts, wikis, issues, and pipelines. + MSG + end + + private + + attr_reader :checker, :current_size, :size_limit, :namespace_name + + def usage_percentage + number_to_percentage(checker.usage_ratio * 100, precision: 0) + end + + def limit_docs_url + ::Gitlab::Routing.url_helpers.help_page_url('user/usage_quotas', anchor: 'project-storage-limit') + end + + def formatted(number) + number_to_human_size(number, delimiter: ',', precision: 2) + end + + def repository_limit + formatted(checker.root_namespace.actual_repository_size_limit) + end + end +end diff --git a/ee/spec/lib/gitlab/root_excess_size_error_message_spec.rb b/ee/spec/lib/gitlab/root_excess_size_error_message_spec.rb new file mode 100644 index 000000000000..af0afe855b9f --- /dev/null +++ b/ee/spec/lib/gitlab/root_excess_size_error_message_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::RootExcessSizeErrorMessage, feature_category: :consumables_cost_management do + let(:namespace) { build(:namespace, additional_purchased_storage_size: limit) } + let(:checker) { Namespaces::Storage::RootExcessSize.new(namespace) } + let(:current_size) { 9.megabytes } + let(:limit) { 10 } + let(:message_params) do + { + size_limit: limit, + namespace_name: namespace.name + } + end + + before do + allow(namespace).to receive(:total_repository_size_excess).and_return(current_size) + end + + subject(:message) { described_class.new(checker, message_params) } + + describe '#push_warning' do + it 'returns the correct message' do + expect(message.push_warning) + .to eq( + <<~MSG.squish + ##### WARNING ##### You have used 90% of the storage quota for this project + (9 MiB of 10 MiB). If a project reaches 100% of the storage quota (10 MiB) + the project will be in a read-only state, and you won't be able to push to your repository or add large files. + To reduce storage usage, reduce git repository and git LFS storage. For more information about storage limits, + see our docs: http://localhost/help/user/usage_quotas#project-storage-limit. + MSG + ) + end + end + + describe '#push_error' do + before do + stub_ee_application_setting(repository_size_limit: 10.gigabytes) + end + + it 'returns the correct message' do + expect(message.push_error) + .to eq( + <<~MSG.squish + You have reached the free storage limit of 10 GiB on one or more projects. + To unlock your projects over the free 10 GiB project limit, you must purchase + additional storage. You can't push to your repository, create pipelines, create issues, or add comments. + To reduce storage capacity, you can delete unused repositories, artifacts, wikis, issues, and pipelines. + MSG + ) + end + end +end diff --git a/ee/spec/services/ee/post_receive_service_spec.rb b/ee/spec/services/ee/post_receive_service_spec.rb index 2aa07666f499..0ba141f6851d 100644 --- a/ee/spec/services/ee/post_receive_service_spec.rb +++ b/ee/spec/services/ee/post_receive_service_spec.rb @@ -125,26 +125,37 @@ context 'when repository size limit enforcement' do let(:user) { project.namespace.owner } - include RepositoryStorageHelpers + before do + stub_feature_flags(namespace_storage_limit: false) + end - it 'returns error message' do - stub_over_repository_limit(project.root_ancestor) + context 'when a project in the namespace is over the limit' do + before do + stub_ee_application_setting(repository_size_limit: 10.gigabytes) - expect(subject).to match_array([ - { - "message" => - "Your push has been rejected, because this repository " \ - "has exceeded its size limit of 10 B by 45 B. " \ - "Please contact your GitLab administrator for more information.", - "type" => "alert" - } - ]) + allow_next_instance_of(Namespaces::Storage::RootExcessSize) do |root_storage_size| + allow(root_storage_size).to receive(:current_size).and_return(55) + allow(root_storage_size).to receive(:limit).and_return(10) + end + end + + it 'returns error message' do + expect(subject).to match_array([ + { + "message" => + <<~MSG.squish, + You have reached the free storage limit of 10 GiB on one or more projects. + To unlock your projects over the free 10 GiB project limit, you must purchase + additional storage. You can't push to your repository, create pipelines, create issues, or add comments. + To reduce storage capacity, you can delete unused repositories, artifacts, wikis, issues, and pipelines. + MSG + "type" => "alert" + } + ]) + end end it 'returns warning message when under storage limit' do - namespace = project.root_ancestor - - stub_feature_flags(namespace_storage_limit: false) allow_next_instance_of(Namespaces::Storage::RootExcessSize) do |root_storage_size| allow(root_storage_size).to receive(:usage_ratio).and_return(0.95) end @@ -152,12 +163,13 @@ expect(subject).to match_array([ { "message" => - "##### WARNING ##### You have used 95% of the storage quota for #{namespace.name} " \ - "(0 B of 0 B). If #{namespace.name} exceeds the storage quota, all projects " \ - "in the namespace will be locked and actions will be restricted. To manage storage, " \ - "or purchase additional storage, see " \ - "http://localhost/help/user/usage_quotas#manage-your-storage-usage. To learn more about " \ - "restricted actions, see http://localhost/help/user/read_only_namespaces#restricted-actions", + <<~MSG.squish, + ##### WARNING ##### You have used 95% of the storage quota for this project + (0 B of 0 B). If a project reaches 100% of the storage quota (0 B) + the project will be in a read-only state, and you won't be able to push to your repository or add large files. + To reduce storage usage, reduce git repository and git LFS storage. For more information about storage limits, + see our docs: http://localhost/help/user/usage_quotas#project-storage-limit. + MSG "type" => "alert" } ]) diff --git a/ee/spec/support/helpers/repository_storage_helpers.rb b/ee/spec/support/helpers/repository_storage_helpers.rb deleted file mode 100644 index 0b5ea08d79c6..000000000000 --- a/ee/spec/support/helpers/repository_storage_helpers.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module RepositoryStorageHelpers - def stub_over_repository_limit(namespace) - stub_feature_flags(namespace_storage_limit: false) - allow(namespace.root_ancestor).to receive(:contains_locked_projects?).and_return(true) - allow(namespace.root_ancestor).to receive(:repository_size_excess_project_count).and_return(5) - allow(namespace.root_ancestor).to receive(:actual_repository_size_limit).and_return(10) - allow_next_instance_of(Namespaces::Storage::RootExcessSize) do |root_storage_size| - allow(root_storage_size).to receive(:above_size_limit?).and_return(true) - allow(root_storage_size).to receive(:usage_ratio).and_return(5.5).at_least(:once) - allow(root_storage_size).to receive(:current_size).and_return(55) - allow(root_storage_size).to receive(:limit).and_return(10) - end - end -end diff --git a/lib/gitlab/repository_size_error_message.rb b/lib/gitlab/repository_size_error_message.rb index e7d527dd4ce5..fc700c3c62e3 100644 --- a/lib/gitlab/repository_size_error_message.rb +++ b/lib/gitlab/repository_size_error_message.rb @@ -7,8 +7,7 @@ class RepositorySizeErrorMessage delegate :current_size, :limit, :exceeded_size, :additional_repo_storage_available?, to: :@checker # @param checker [RepositorySizeChecker] - def initialize(checker, message_params = {}) - @message_params = message_params + def initialize(checker) @checker = checker end @@ -20,14 +19,6 @@ def merge_error "This merge request cannot be merged, #{base_message}" end - def push_warning - _("##### WARNING ##### You have used %{usage_percentage} of the storage quota for %{namespace_name} " \ - "(%{current_size} of %{size_limit}). If %{namespace_name} exceeds the storage quota, " \ - "all projects in the namespace will be locked and actions will be restricted. " \ - "To manage storage, or purchase additional storage, see %{manage_storage_url}. " \ - "To learn more about restricted actions, see %{restricted_actions_url}") % push_message_params - end - def push_error(change_size = 0) "Your push has been rejected, #{base_message(change_size)}. #{more_info_message}" end @@ -50,19 +41,6 @@ def above_size_limit_message private - attr_reader :message_params - - def push_message_params - { - namespace_name: message_params[:namespace_name], - manage_storage_url: help_page_url('user/usage_quotas', 'manage-your-storage-usage'), - restricted_actions_url: help_page_url('user/read_only_namespaces', 'restricted-actions'), - current_size: formatted(current_size), - size_limit: formatted(limit), - usage_percentage: usage_percentage - } - end - def base_message(change_size = 0) "because this repository has exceeded its size limit of #{formatted(limit)} by #{formatted(exceeded_size(change_size))}" end @@ -70,13 +48,5 @@ def base_message(change_size = 0) def formatted(number) number_to_human_size(number, delimiter: ',', precision: 2) end - - def usage_percentage - number_to_percentage(@checker.usage_ratio * 100, precision: 0) - end - - def help_page_url(path, anchor = nil) - ::Gitlab::Routing.url_helpers.help_page_url(path, anchor: anchor) - end end end -- GitLab