From b6e5f5df140a172682b630075e9c98fde6b87c01 Mon Sep 17 00:00:00 2001 From: Johannes Bauer <jobauer@gitlab.com> Date: Thu, 6 Mar 2025 06:21:51 +0100 Subject: [PATCH] Extend Package Registry DB Table --- app/finders/packages/helm/packages_finder.rb | 11 ++-- app/helpers/application_settings_helper.rb | 1 + app/models/application_setting.rb | 10 +++- .../application_setting_implementation.rb | 1 + .../application_setting_package_registry.json | 5 ++ .../_package_registry.html.haml | 7 +-- .../_packages_limits_settings.html.haml | 21 ++++++++ config/routes/admin.rb | 1 + doc/api/settings.md | 1 + doc/development/packages/settings.md | 1 + doc/user/packages/helm_repository/_index.md | 2 +- .../_ee_package_registry.haml | 2 +- locale/gitlab.pot | 14 +++++- .../packages/helm/packages_finder_spec.rb | 23 +++++++-- spec/models/application_setting_spec.rb | 2 + ...packages_limits_settings.html.haml_spec.rb | 50 +++++++++++++++++++ 16 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 app/views/admin/application_settings/_packages_limits_settings.html.haml create mode 100644 spec/views/admin/application_settings/_packages_limits_settings.html.haml_spec.rb diff --git a/app/finders/packages/helm/packages_finder.rb b/app/finders/packages/helm/packages_finder.rb index 808c9f5eb1f35..91dd3b8005bfc 100644 --- a/app/finders/packages/helm/packages_finder.rb +++ b/app/finders/packages/helm/packages_finder.rb @@ -5,8 +5,6 @@ module Helm class PackagesFinder include ::Packages::FinderHelper - MAX_PACKAGES_COUNT = 1000 - def initialize(project, channel) @project = project @channel = channel @@ -20,9 +18,16 @@ def execute # we use a subquery to get unique packages and at the same time # order + limit them. ::Packages::Package - .limit_recent(MAX_PACKAGES_COUNT) + .limit_recent(max_packages_count) .id_in(pkg_files.select(:package_id)) end + + private + + def max_packages_count + ::Gitlab::CurrentSettings.package_registry.fetch('helm_max_packages_count', + ::ApplicationSetting::DEFAULT_HELM_MAX_PACKAGES_COUNT) + end end end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index ff75ee9c89509..4db587b6649ae 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -593,6 +593,7 @@ def visible_attributes settings << :deactivate_dormant_users settings << :deactivate_dormant_users_period settings << :nuget_skip_metadata_url_validation + settings << :helm_max_packages_count end end end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 5f299cf243501..17adf898ff366 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -742,7 +742,15 @@ def self.kroki_formats_attributes validates :importers, json_schema: { filename: "application_setting_importers" } - jsonb_accessor :package_registry, nuget_skip_metadata_url_validation: [:boolean, { default: false }] + DEFAULT_HELM_MAX_PACKAGES_COUNT = 1000 + + jsonb_accessor :package_registry, + nuget_skip_metadata_url_validation: [:boolean, { default: false }], + helm_max_packages_count: [:integer, { default: DEFAULT_HELM_MAX_PACKAGES_COUNT }] + + validates :helm_max_packages_count, + presence: true, + numericality: { only_integer: true, greater_than: 0 } jsonb_accessor :oauth_provider, ropc_without_client_credentials: [:boolean, { default: true }] diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 26603f08c9648..de0f6de43af87 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -309,6 +309,7 @@ def defaults # rubocop:disable Metrics/AbcSize users_api_limit_gpg_keys: 120, users_api_limit_gpg_key: 120, nuget_skip_metadata_url_validation: false, + helm_max_packages_count: 1000, ai_action_api_rate_limit: 160, code_suggestions_api_rate_limit: 60, require_personal_access_token_expiry: true, diff --git a/app/validators/json_schemas/application_setting_package_registry.json b/app/validators/json_schemas/application_setting_package_registry.json index bf2e67c6f4215..4597d6ab6fb6d 100644 --- a/app/validators/json_schemas/application_setting_package_registry.json +++ b/app/validators/json_schemas/application_setting_package_registry.json @@ -6,6 +6,11 @@ "nuget_skip_metadata_url_validation": { "type": "boolean", "description": "Indicates whether to skip metadata URL validation for the NuGet package" + }, + "helm_max_packages_count": { + "type": ["integer", "null"], + "minimum": 1, + "description": "Maximum number of Helm packages that can be listed per channel. If null, defaults to 1000" } }, "additionalProperties": false diff --git a/app/views/admin/application_settings/_package_registry.html.haml b/app/views/admin/application_settings/_package_registry.html.haml index 2a703a6a01cea..48f8613f4ce96 100644 --- a/app/views/admin/application_settings/_package_registry.html.haml +++ b/app/views/admin/application_settings/_package_registry.html.haml @@ -3,15 +3,16 @@ id: 'js-package-settings', expanded: expanded_by_default?) do |c| - c.with_description do - = s_('PackageRegistry|Configure package forwarding and package file size limits.') + = s_('PackageRegistry|Configure package forwarding, package limits, and package file size limits.') - c.with_body do = render_if_exists 'admin/application_settings/ee_package_registry' = render 'admin/application_settings/nuget_skip_metadata_url_validation' unless Gitlab.ee? + = render 'admin/application_settings/packages_limits_settings' .gl-mt-7 - %h4 + %h4.gl-mb-1 = _('Package file size limits') - %p + %p.gl-text-subtle = _('Set limit to 0 to allow any file size.') .scrolling-tabs-container.inner-page-scroll-tabs - if @plans.size > 1 diff --git a/app/views/admin/application_settings/_packages_limits_settings.html.haml b/app/views/admin/application_settings/_packages_limits_settings.html.haml new file mode 100644 index 0000000000000..e350806eb94b6 --- /dev/null +++ b/app/views/admin/application_settings/_packages_limits_settings.html.haml @@ -0,0 +1,21 @@ +.gl-mt-7 + %h4.gl-mb-1 + = _('Package limits') + %p.gl-text-subtle + = _('Setting high package limits can impact database performance. Consider the size of your instance when configuring these values.') + .scrolling-tabs-container.inner-page-scroll-tabs + .tab-content + = gitlab_ui_form_for @application_setting, + url: ci_cd_admin_application_settings_path(anchor: 'js-package-settings'), + method: :patch, html: { class: 'fieldset-form' }, data: { testid: 'package-limits-form' } do |f| + = form_errors(@application_setting) + %fieldset + + .form-group + = f.label :helm_max_packages_count, _('Maximum number of Helm packages per channel'), class: 'label-bold' + = f.number_field :helm_max_packages_count, + class: 'form-control gl-form-input', + placeholder: ::ApplicationSetting::DEFAULT_HELM_MAX_PACKAGES_COUNT + .form-text.text-muted + = _('Maximum number of Helm packages that can be listed per channel. Must be at least 1.') + = f.submit _('Save changes'), pajamas_button: true diff --git a/config/routes/admin.rb b/config/routes/admin.rb index a00f316e85cd0..39a2e44ba64b7 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -185,6 +185,7 @@ end resources :plan_limits, only: :create + resource :packages_limits, only: :update resources :labels diff --git a/doc/api/settings.md b/doc/api/settings.md index 33d6341d9ef94..f988b968bd576 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -764,6 +764,7 @@ to configure other related settings. These requirements are | `duo_features_enabled` | boolean | no | Indicates whether GitLab Duo features are enabled for this instance. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144931) in GitLab 16.10. GitLab Self-Managed, Premium and Ultimate only. | | `lock_duo_features_enabled` | boolean | no | Indicates whether the GitLab Duo features enabled setting is enforced for all subgroups. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144931) in GitLab 16.10. GitLab Self-Managed, Premium and Ultimate only. | | `nuget_skip_metadata_url_validation` | boolean | no | Indicates whether to skip metadata URL validation for the NuGet package. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145887) in GitLab 17.0. | +| `helm_max_packages_count` | integer | no | Maximum number of Helm packages that can be listed per channel. Must be at least 1. Default is 1000. | | `require_admin_two_factor_authentication` | boolean | no | Allow administrators to require 2FA for all administrators on the instance. | | `secret_push_protection_available` | boolean | no | Allow projects to enable secret push protection. This does not enable secret push protection. Ultimate only. | diff --git a/doc/development/packages/settings.md b/doc/development/packages/settings.md index 87ea11b88e1d9..fe2a56cc92c6f 100644 --- a/doc/development/packages/settings.md +++ b/doc/development/packages/settings.md @@ -35,6 +35,7 @@ This page includes an exhaustive list of settings related to and maintained by t | `rubygems_max_file_size` | `plan_limits` | Maximum file size for a RubyGems package file. | | `terraform_module_max_file_size` | `plan_limits` | Maximum file size for a Terraform package file. | | `helm_max_file_size` | `plan_limits` | Maximum file size for a Helm package file. | +| `helm_max_packages_count` | `application_settings` | Maximum number of Helm packages that can be listed per channel. Must be at least 1. Default is 1000. | ### Container registry diff --git a/doc/user/packages/helm_repository/_index.md b/doc/user/packages/helm_repository/_index.md index 2704c38016b69..34a2cd971664b 100644 --- a/doc/user/packages/helm_repository/_index.md +++ b/doc/user/packages/helm_repository/_index.md @@ -111,7 +111,7 @@ upload: {{< alert type="note" >}} -When requesting a package, GitLab considers only the 1000 most recent packages created. +When requesting a package, GitLab considers only the most recent packages up to the configured limit (default is 1000 packages, configurable by administrators). For each package, only the most recent package file is returned. {{< /alert >}} diff --git a/ee/app/views/admin/application_settings/_ee_package_registry.haml b/ee/app/views/admin/application_settings/_ee_package_registry.haml index 96e95aa3f83e8..3fc39e544018c 100644 --- a/ee/app/views/admin/application_settings/_ee_package_registry.haml +++ b/ee/app/views/admin/application_settings/_ee_package_registry.haml @@ -10,7 +10,7 @@ - docs_link = link_to('', help_page_path('user/packages/package_registry/supported_functionality.md', { anchor: 'deleting-packages' })) = safe_format(s_('PackageRegistry|There are security risks if packages are deleted while request forwarding is enabled. %{docs_link_start}What are the risks?%{docs_link_end}'), tag_pair(docs_link, :docs_link_start, :docs_link_end)) - %fieldset + %fieldset{ class: '!gl-mb-0' } .form-group = f.gitlab_ui_checkbox_component :npm_package_requests_forwarding, s_('PackageRegistry|Forward %{package_type} package requests') % { package_type: 'npm' } diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 49c51f2324458..73c35e7d72480 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -35266,6 +35266,12 @@ msgstr "" msgid "Maximum number of %{name} (%{count}) exceeded" msgstr "" +msgid "Maximum number of Helm packages per channel" +msgstr "" + +msgid "Maximum number of Helm packages that can be listed per channel. Must be at least 1." +msgstr "" + msgid "Maximum number of changes (branches or tags) in a single push above which a bulk push event is created (default is 3). Setting to 0 does not disable throttling." msgstr "" @@ -41033,6 +41039,9 @@ msgstr "" msgid "Package forwarding" msgstr "" +msgid "Package limits" +msgstr "" + msgid "Package name of the app in Google Play." msgstr "" @@ -41135,7 +41144,7 @@ msgstr "" msgid "PackageRegistry|Configure in settings" msgstr "" -msgid "PackageRegistry|Configure package forwarding and package file size limits." +msgid "PackageRegistry|Configure package forwarding, package limits, and package file size limits." msgstr "" msgid "PackageRegistry|Copy .pypirc content" @@ -54923,6 +54932,9 @@ msgstr "" msgid "Setting enforced" msgstr "" +msgid "Setting high package limits can impact database performance. Consider the size of your instance when configuring these values." +msgstr "" + msgid "Settings" msgstr "" diff --git a/spec/finders/packages/helm/packages_finder_spec.rb b/spec/finders/packages/helm/packages_finder_spec.rb index 5037a9e620581..f1f5eb16355a8 100644 --- a/spec/finders/packages/helm/packages_finder_spec.rb +++ b/spec/finders/packages/helm/packages_finder_spec.rb @@ -64,11 +64,26 @@ let_it_be(:helm_package3) { create(:helm_package, project: project1) } let_it_be(:helm_package4) { create(:helm_package, project: project1) } - before do - stub_const("#{described_class}::MAX_PACKAGES_COUNT", 2) - end + context 'with max_packages_count set to 2' do + before do + allow(::Gitlab::CurrentSettings) + .to receive_message_chain(:package_registry, :fetch) + .with('helm_max_packages_count', anything) + .and_return(2) + end + + subject(:limited_packages_finder) { finder.execute } + + it 'returns only 2 packages' do + packages = limited_packages_finder - it { is_expected.to eq([helm_package4, helm_package3]) } + aggregate_failures do + expect(packages.size).to eq(2) + expect(packages).to all(be_a(Packages::Helm::Package)) + expect(packages).to all(have_attributes(project_id: project.id)) + end + end + end end end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 110afe6b20cf9..be7d08e9273e4 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -25,6 +25,7 @@ it { expect(setting.kroki_formats).to eq({}) } it { expect(setting.default_branch_protection_defaults).to eq({}) } it { expect(setting.enforce_email_subaddress_restrictions).to be(false) } + it { expect(setting.helm_max_packages_count).to eq(1000) } it { expect(setting.max_decompressed_archive_size).to eq(25600) } it { expect(setting.decompress_archive_file_timeout).to eq(210) } it { expect(setting.bulk_import_concurrent_pipeline_batch_limit).to eq(25) } @@ -335,6 +336,7 @@ def many_usernames(num = 100) context 'for non-null integer attributes starting from 1' do where(:attribute) do %i[ + helm_max_packages_count bulk_import_concurrent_pipeline_batch_limit code_suggestions_api_rate_limit concurrent_bitbucket_import_jobs_limit diff --git a/spec/views/admin/application_settings/_packages_limits_settings.html.haml_spec.rb b/spec/views/admin/application_settings/_packages_limits_settings.html.haml_spec.rb new file mode 100644 index 0000000000000..2ae28041a1c66 --- /dev/null +++ b/spec/views/admin/application_settings/_packages_limits_settings.html.haml_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'admin/application_settings/_packages_limits_settings', feature_category: :package_registry do + let_it_be(:application_setting) { build(:application_setting) } + + before do + assign(:application_setting, application_setting) + allow(view).to receive(:form_errors).and_return('') + end + + it 'renders package limits settings form' do + render + + expect(rendered).to have_selector('.gl-mt-7 h4', text: 'Package limits') + expect(rendered).to have_selector('p', + text: 'Setting high package limits can impact database performance. ' \ + 'Consider the size of your instance when configuring these values.') + expect(rendered).to have_selector( + 'form.fieldset-form[action="/admin/application_settings/ci_cd#js-package-settings"]' \ + '[method="post"][data-testid="package-limits-form"]' + ) do |form| + expect(form).to have_field('_method', type: 'hidden', with: 'patch') + end + end + + it 'renders helm packages count field with default value' do + render + + expect(rendered).to have_selector('form') do |form| + expect(form).to have_selector('label', text: 'Maximum number of Helm packages per channel') + expect(form).to have_field( + 'application_setting[helm_max_packages_count]', + type: 'number' + ) + expect(form).to have_selector( + "input[placeholder='#{ApplicationSetting::DEFAULT_HELM_MAX_PACKAGES_COUNT}']" + ) + expect(form).to have_selector('.form-text', + text: 'Maximum number of Helm packages that can be listed per channel. Must be at least 1.') + end + end + + it 'renders save changes button with pajamas' do + render + + expect(rendered).to have_button('Save changes', class: ['gl-button']) + end +end -- GitLab