diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 0bf5f9e8409..ba2bb030f19 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -283,6 +283,7 @@ module ApplicationSettingsHelper :max_export_size, :max_import_size, :max_pages_size, + :max_pages_custom_domains_per_project, :max_yaml_size_bytes, :max_yaml_depth, :metrics_method_call_threshold, diff --git a/app/helpers/projects/pages_helper.rb b/app/helpers/projects/pages_helper.rb new file mode 100644 index 00000000000..f46c11db1db --- /dev/null +++ b/app/helpers/projects/pages_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Projects + module PagesHelper + def can_create_pages_custom_domains?(current_user, project) + current_user.can?(:update_pages, project) && + (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https) && + project.can_create_custom_domains? + end + end +end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 2a04cc35ece..3590faa818a 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -217,6 +217,10 @@ class ApplicationSetting < ApplicationRecord numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than: ::Gitlab::Pages::MAX_SIZE / 1.megabyte } + validates :max_pages_custom_domains_per_project, + presence: true, + numericality: { only_integer: true, greater_than_or_equal_to: 0 } + validates :jobs_per_stage_page_size, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 2e25839c47f..569f56fa982 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -33,6 +33,7 @@ class PagesDomain < ApplicationRecord validate :validate_pages_domain validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? } validate :validate_intermediates, if: ->(domain) { domain.certificate.present? && domain.certificate_changed? } + validate :validate_custom_domain_count_per_project, on: :create default_value_for(:auto_ssl_enabled, allows_nil: false) { ::Gitlab::LetsEncrypt.enabled? } default_value_for :scope, allows_nil: false, value: :project @@ -224,6 +225,16 @@ class PagesDomain < ApplicationRecord self.auto_ssl_failed = false end + def validate_custom_domain_count_per_project + return unless project + + unless project.can_create_custom_domains? + self.errors.add( + :base, + _("This project reached the limit of custom domains. (Max %d)") % Gitlab::CurrentSettings.max_pages_custom_domains_per_project) + end + end + private def pages_deployed? diff --git a/app/models/project.rb b/app/models/project.rb index 5130a476332..46295545e91 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -3036,6 +3036,12 @@ class Project < ApplicationRecord deployments.where(id: deployment_id).fast_destroy_all end + def can_create_custom_domains? + return true if Gitlab::CurrentSettings.max_pages_custom_domains_per_project == 0 + + pages_domains.count < Gitlab::CurrentSettings.max_pages_custom_domains_per_project + end + private # overridden in EE diff --git a/app/views/admin/application_settings/_pages.html.haml b/app/views/admin/application_settings/_pages.html.haml index 5ef8a24ba39..cf43d3ddeca 100644 --- a/app/views/admin/application_settings/_pages.html.haml +++ b/app/views/admin/application_settings/_pages.html.haml @@ -22,6 +22,13 @@ - pages_link_url = help_page_path('administration/pages/index', anchor: 'set-maximum-size-of-gitlab-pages-site-in-a-project') - pages_link_start = ''.html_safe % { url: pages_link_url } = s_('AdminSettings|Set the maximum size of GitLab Pages per project (0 for unlimited). %{link_start}Learn more.%{link_end}').html_safe % { link_start: pages_link_start, link_end: ''.html_safe } + .form-group + = f.label :max_pages_custom_domains_per_project, s_('AdminSettings|Maximum number of custom domains per project'), class: 'label-bold' + = f.number_field :max_pages_custom_domains_per_project, class: 'form-control gl-form-input' + .form-text.text-muted + - pages_link_url = help_page_path('administration/pages/index', anchor: 'set-maximum-number-of-gitlab-pages-custom-domains-for-a-project') + - pages_link_start = ''.html_safe % { url: pages_link_url } + = s_('AdminSettings|Set the maximum number of GitLab Pages custom domains per project (0 for unlimited). %{link_start}Learn more.%{link_end}').html_safe % { link_start: pages_link_start, link_end: ''.html_safe } %h5 = s_("AdminSettings|Configure Let's Encrypt") %p diff --git a/app/views/projects/pages/_header.html.haml b/app/views/projects/pages/_header.html.haml index da35f2fdf09..cf51796e878 100644 --- a/app/views/projects/pages/_header.html.haml +++ b/app/views/projects/pages/_header.html.haml @@ -1,4 +1,4 @@ -- can_add_new_domain = can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https) +- can_add_new_domain = can_create_pages_custom_domains?(current_user, @project) %h1.page-title.gl-font-size-h-display.with-button = s_('GitLabPages|Pages') diff --git a/db/migrate/20220818095225_add_max_pages_custom_domains_per_project.rb b/db/migrate/20220818095225_add_max_pages_custom_domains_per_project.rb new file mode 100644 index 00000000000..c5e1f5aede6 --- /dev/null +++ b/db/migrate/20220818095225_add_max_pages_custom_domains_per_project.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddMaxPagesCustomDomainsPerProject < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + CONSTRAINT_NAME = "app_settings_max_pages_custom_domains_per_project_check" + + def up + return if column_exists?(:application_settings, :max_pages_custom_domains_per_project) + + add_column :application_settings, :max_pages_custom_domains_per_project, :integer, null: false, default: 0 + add_check_constraint :application_settings, "max_pages_custom_domains_per_project >= 0", CONSTRAINT_NAME + end + + def down + return unless column_exists?(:application_settings, :max_pages_custom_domains_per_project) + + remove_column :application_settings, :max_pages_custom_domains_per_project + end +end diff --git a/db/schema_migrations/20220818095225 b/db/schema_migrations/20220818095225 new file mode 100644 index 00000000000..9f420931b9d --- /dev/null +++ b/db/schema_migrations/20220818095225 @@ -0,0 +1 @@ +ec31d14ce1a9f7b08985c2d304ab768a41139e81b694dcb1ec920623201504e6 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 7cc22d2150b..69ab2d880da 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -11510,11 +11510,13 @@ CREATE TABLE application_settings ( package_registry_cleanup_policies_worker_capacity integer DEFAULT 2 NOT NULL, deactivate_dormant_users_period integer DEFAULT 90 NOT NULL, auto_ban_user_on_excessive_projects_download boolean DEFAULT false NOT NULL, + max_pages_custom_domains_per_project integer DEFAULT 0 NOT NULL, CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)), CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)), CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)), CONSTRAINT app_settings_git_rate_limit_users_allowlist_max_usernames CHECK ((cardinality(git_rate_limit_users_allowlist) <= 100)), + CONSTRAINT app_settings_max_pages_custom_domains_per_project_check CHECK ((max_pages_custom_domains_per_project >= 0)), CONSTRAINT app_settings_p_cleanup_package_file_worker_capacity_positive CHECK ((packages_cleanup_package_file_worker_capacity >= 0)), CONSTRAINT app_settings_pkg_registry_cleanup_pol_worker_capacity_gte_zero CHECK ((package_registry_cleanup_policies_worker_capacity >= 0)), CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)), diff --git a/doc/administration/gitaly/recovery.md b/doc/administration/gitaly/recovery.md index bd4846a986d..4bbf25d7cdd 100644 --- a/doc/administration/gitaly/recovery.md +++ b/doc/administration/gitaly/recovery.md @@ -430,6 +430,44 @@ This command fails if: - The repository is already being tracked by the Praefect database. - The repository does not exist on disk. +### Manually track large numbers of repositories + +> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/6319) in GitLab 15.4. + +The `track-repositories` Praefect sub-command adds large batches of on-disk repositories to the Praefect database for tracking. This can +be useful when migrating an existing instance to new infrastructure and ingesting all existing repositories into a fresh Gitaly Cluster. + +```shell +# Omnibus GitLab install +sudo gitlab-ctl praefect track-repositories --input-path /path/to/input.json + +# Source install +sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml track-repositories -input-path /path/to/input.json +``` + +The command validates that all entries: + +- Are formatted correctly and contain required fields. +- Correspond to a valid Git repository on disk. +- Are not currently tracked in the Praefect database. + +If any entry fails these checks, the command aborts prior to attempting to track a repository. + +- `input-path` is the path to a file containing a list of repositories formatted as newline-delimited JSON objects. Objects must contain the following keys: + - `relative_path`: corresponds with `repository` in [track-repositories](#manually-track-repositories). + - `authoritative-storage`: the storage Praefect is to treat as the primary. + - `virtual-storage`: the virtual storage the repository is located in. + + For example: + + ```json + {"relative_path":"@hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git","authoritative_storage":"gitaly-1","virtual_storage":"default"} + {"relative_path":"@hashed/f8/9f/f89f8d0e735a91c5269ab08d72fa27670d000e7561698d6e664e7b603f5c4e40.git","authoritative_storage":"gitaly-2","virtual_storage":"default"} + ``` + +- `-replicate-immediately`, causes the command to replicate the repository to its secondaries immediately. + Otherwise, replication jobs are scheduled for execution in the database and are picked up by a Praefect background process. + ### List virtual storage details > [Introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4609) in GitLab 15.1. diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index bea6328ccdd..c5b4852eda8 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -738,6 +738,19 @@ To set the maximum size of GitLab Pages site in a project, overriding the inheri 1. Enter a value under **Maximum size of pages** in MB. 1. Select **Save changes**. +## Set maximum number of GitLab Pages custom domains for a project + +Prerequisite: + +- You must be an administrator of a self-managed GitLab instance. + +To set the maximum number of GitLab Pages custom domains for a project: + +1. On the top bar, select **Menu > Admin**. +1. On the left sidebar, select **Settings > Preferences**, and expand **Pages**. +1. Enter a value for **Maximum number of custom domains per project**. Use `0` for unlimited domains. +1. Select **Save changes**. + ## Running GitLab Pages on a separate server You can run the GitLab Pages daemon on a separate server to decrease the load on diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md index 07bda2baadf..9269b8886c7 100644 --- a/doc/ci/caching/index.md +++ b/doc/ci/caching/index.md @@ -84,7 +84,8 @@ test-job: paths: - .yarn-cache/ script: - - bundle install --path=vendor + - bundle config set --local path 'vendor/ruby' + - bundle install - yarn install --cache-folder .yarn-cache - echo Run tests... ``` @@ -353,7 +354,8 @@ cache: before_script: - ruby -v # Print out ruby version for debugging - - bundle install -j $(nproc) --path vendor/ruby # Install dependencies into ./vendor/ruby + - bundle config set --local path 'vendor/ruby' # The location to install the specified gems to + - bundle install -j $(nproc) # Install dependencies into ./vendor/ruby rspec: script: @@ -379,14 +381,16 @@ cache: test_job: stage: test before_script: - - bundle install --without production --path vendor/ruby + - bundle config set --local path 'vendor/ruby' + - bundle install --without production script: - bundle exec rspec deploy_job: stage: production before_script: - - bundle install --without test --path vendor/ruby + - bundle config set --local path 'vendor/ruby' # The location to install the specified gems to + - bundle install --without test script: - bundle exec deploy ``` diff --git a/lib/api/settings.rb b/lib/api/settings.rb index c25a56d5f08..f393f862f55 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -98,6 +98,7 @@ module API optional :max_export_size, type: Integer, desc: 'Maximum export size in MB' optional :max_import_size, type: Integer, desc: 'Maximum import size in MB' optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB' + optional :max_pages_custom_domains_per_project, type: Integer, desc: 'Maximum number of GitLab Pages custom domains per project' optional :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.' optional :password_authentication_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5 optional :password_authentication_enabled_for_web, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' diff --git a/lib/gitlab/github_import/importer/events/changed_assignee.rb b/lib/gitlab/github_import/importer/events/changed_assignee.rb index 7b18ea807a5..b75d41f40de 100644 --- a/lib/gitlab/github_import/importer/events/changed_assignee.rb +++ b/lib/gitlab/github_import/importer/events/changed_assignee.rb @@ -7,22 +7,22 @@ module Gitlab class ChangedAssignee < BaseImporter def execute(issue_event) assignee_id = author_id(issue_event, author_key: :assignee) - assigner_id = author_id(issue_event, author_key: :assigner) + author_id = author_id(issue_event, author_key: :actor) - note_body = parse_body(issue_event, assigner_id, assignee_id) + note_body = parse_body(issue_event, assignee_id) - create_note(issue_event, note_body, assigner_id) + create_note(issue_event, note_body, author_id) end private - def create_note(issue_event, note_body, assigner_id) + def create_note(issue_event, note_body, author_id) Note.create!( system: true, noteable_type: issuable_type(issue_event), noteable_id: issuable_db_id(issue_event), project: project, - author_id: assigner_id, + author_id: author_id, note: note_body, system_note_metadata: SystemNoteMetadata.new( { @@ -36,12 +36,14 @@ module Gitlab ) end - def parse_body(issue_event, assigner_id, assignee_id) + def parse_body(issue_event, assignee_id) + assignee = User.find(assignee_id).to_reference + Gitlab::I18n.with_default_locale do if issue_event.event == "unassigned" - "unassigned #{User.find(assigner_id).to_reference}" + "unassigned #{assignee}" else - "assigned to #{User.find(assignee_id).to_reference}" + "assigned to #{assignee}" end end end diff --git a/lib/gitlab/github_import/representation/issue_event.rb b/lib/gitlab/github_import/representation/issue_event.rb index 67a5df73a97..3dfebbc7d29 100644 --- a/lib/gitlab/github_import/representation/issue_event.rb +++ b/lib/gitlab/github_import/representation/issue_event.rb @@ -10,7 +10,7 @@ module Gitlab attr_reader :attributes expose_attribute :id, :actor, :event, :commit_id, :label_title, :old_title, :new_title, - :milestone_title, :issue, :source, :assignee, :assigner, :created_at + :milestone_title, :issue, :source, :assignee, :created_at # attributes - A Hash containing the event details. The keys of this # Hash (and any nested hashes) must be symbols. @@ -47,7 +47,6 @@ module Gitlab issue: event.issue&.to_h&.symbolize_keys, source: event.source, assignee: user_representation(event.assignee), - assigner: user_representation(event.assigner), created_at: event.created_at ) end @@ -57,7 +56,6 @@ module Gitlab hash = Representation.symbolize_hash(raw_hash) hash[:actor] = user_representation(hash[:actor], source: :hash) hash[:assignee] = user_representation(hash[:assignee], source: :hash) - hash[:assigner] = user_representation(hash[:assigner], source: :hash) new(hash) end diff --git a/lib/gitlab/github_import/user_finder.rb b/lib/gitlab/github_import/user_finder.rb index 6d6a00d260d..1805939dfef 100644 --- a/lib/gitlab/github_import/user_finder.rb +++ b/lib/gitlab/github_import/user_finder.rb @@ -45,8 +45,6 @@ module Gitlab object&.actor when :assignee object&.assignee - when :assigner - object&.assigner else object&.author end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 3d138d02cc0..7bfa1123c81 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2802,6 +2802,9 @@ msgstr "" msgid "AdminSettings|Maximum number of active pipelines per project" msgstr "" +msgid "AdminSettings|Maximum number of custom domains per project" +msgstr "" + msgid "AdminSettings|Maximum number of jobs in a single pipeline" msgstr "" @@ -2892,6 +2895,9 @@ msgstr "" msgid "AdminSettings|Set the initial name and protections for the default branch of new repositories created in the instance." msgstr "" +msgid "AdminSettings|Set the maximum number of GitLab Pages custom domains per project (0 for unlimited). %{link_start}Learn more.%{link_end}" +msgstr "" + msgid "AdminSettings|Set the maximum size of GitLab Pages per project (0 for unlimited). %{link_start}Learn more.%{link_end}" msgstr "" @@ -40657,6 +40663,9 @@ msgstr "" msgid "This project path either does not exist or you do not have access." msgstr "" +msgid "This project reached the limit of custom domains. (Max %d)" +msgstr "" + msgid "This project will be deleted on %{date}" msgstr "" diff --git a/spec/helpers/projects/pages_helper_spec.rb b/spec/helpers/projects/pages_helper_spec.rb new file mode 100644 index 00000000000..4a4cebc9d70 --- /dev/null +++ b/spec/helpers/projects/pages_helper_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::PagesHelper do + let(:user) { create(:user) } + let(:project) { create(:project) } + + before do + stub_config(pages: { + access_control: true, + external_http: true, + external_https: true, + host: "new.domain.com" + }) + end + + context 'when the user have permission' do + before do + project.add_maintainer(user) + end + + context 'on custom domain' do + using RSpec::Parameterized::TableSyntax + + where(:external_http, :external_https, :can_create) do + false | false | false + false | true | true + true | false | true + true | true | true + end + + with_them do + it do + stub_config(pages: { external_http: external_http, external_https: external_https }) + + expect(can_create_pages_custom_domains?(user, project)).to be can_create + end + end + end + + context 'on domain limit' do + it 'can create new domains when the limit is 0' do + Gitlab::CurrentSettings.update!(max_pages_custom_domains_per_project: 0) + + expect(can_create_pages_custom_domains?(user, project)).to be true + end + + it 'validates custom domain creation is only allowed upto max value' do + Gitlab::CurrentSettings.update!(max_pages_custom_domains_per_project: 1) + + expect(can_create_pages_custom_domains?(user, project)).to be true + create(:pages_domain, project: project) + expect(can_create_pages_custom_domains?(user, project)).to be false + end + end + end + + context 'when the user does not have permission' do + before do + project.add_guest(user) + end + + it 'validates user cannot create domain' do + expect(can_create_pages_custom_domains?(user, project)).to be false + end + end +end diff --git a/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb index d840227992f..dbc72574ec2 100644 --- a/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb +++ b/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb @@ -6,8 +6,8 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do subject(:importer) { described_class.new(project, client) } let_it_be(:project) { create(:project, :repository) } + let_it_be(:author) { create(:user) } let_it_be(:assignee) { create(:user) } - let_it_be(:assigner) { create(:user) } let(:client) { instance_double('Gitlab::GithubImport::Client') } let(:issuable) { create(:issue, project: project) } @@ -15,11 +15,10 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do let(:issue_event) do Gitlab::GithubImport::Representation::IssueEvent.from_json_hash( 'id' => 6501124486, - 'actor' => { 'id' => 4, 'login' => 'alice' }, + 'actor' => { 'id' => author.id, 'login' => author.username }, 'event' => event_type, 'commit_id' => nil, 'created_at' => '2022-04-26 18:30:53 UTC', - 'assigner' => { 'id' => assigner.id, 'login' => assigner.username }, 'assignee' => { 'id' => assignee.id, 'login' => assignee.username }, 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) } ) @@ -30,7 +29,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do noteable_id: issuable.id, noteable_type: issuable.class.name, project_id: project.id, - author_id: assigner.id, + author_id: author.id, system: true, created_at: issue_event.created_at, updated_at: issue_event.created_at @@ -77,7 +76,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do context 'when importing an unassigned event' do let(:event_type) { 'unassigned' } - let(:expected_note_attrs) { note_attrs.merge(note: "unassigned @#{assigner.username}") } + let(:expected_note_attrs) { note_attrs.merge(note: "unassigned @#{assignee.username}") } it_behaves_like 'create expected notes' end @@ -89,8 +88,8 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do allow(finder).to receive(:database_id).and_return(issuable.id) end allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder| + allow(finder).to receive(:find).with(author.id, author.username).and_return(author.id) allow(finder).to receive(:find).with(assignee.id, assignee.username).and_return(assignee.id) - allow(finder).to receive(:find).with(assigner.id, assigner.username).and_return(assigner.id) end end diff --git a/spec/lib/gitlab/github_import/representation/issue_event_spec.rb b/spec/lib/gitlab/github_import/representation/issue_event_spec.rb index d3a98035e73..2414e46470a 100644 --- a/spec/lib/gitlab/github_import/representation/issue_event_spec.rb +++ b/spec/lib/gitlab/github_import/representation/issue_event_spec.rb @@ -91,26 +91,20 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do end end - context 'when assignee and assigner data is present' do - it 'includes assignee and assigner details' do + context 'when assignee data is present' do + it 'includes assignee details' do expect(issue_event.assignee) .to be_an_instance_of(Gitlab::GithubImport::Representation::User) expect(issue_event.assignee.id).to eq(5) expect(issue_event.assignee.login).to eq('tom') - - expect(issue_event.assigner) - .to be_an_instance_of(Gitlab::GithubImport::Representation::User) - expect(issue_event.assigner.id).to eq(6) - expect(issue_event.assigner.login).to eq('jerry') end end - context 'when assignee and assigner data is empty' do + context 'when assignee data is empty' do let(:with_assignee) { false } it 'does not return such info' do expect(issue_event.assignee).to eq nil - expect(issue_event.assigner).to eq nil end end @@ -148,7 +142,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do let(:response) do event_resource = Struct.new( :id, :node_id, :url, :actor, :event, :commit_id, :commit_url, :label, :rename, :milestone, - :source, :assignee, :assigner, :issue, :created_at, :performed_via_github_app, + :source, :assignee, :issue, :created_at, :performed_via_github_app, keyword_init: true ) user_resource = Struct.new(:id, :login, keyword_init: true) @@ -166,7 +160,6 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do milestone: with_milestone ? { title: 'milestone title' } : nil, source: { type: 'issue', id: 123456 }, assignee: with_assignee ? user_resource.new(id: 5, login: 'tom') : nil, - assigner: with_assignee ? user_resource.new(id: 6, login: 'jerry') : nil, issue: { 'number' => 2, 'pull_request' => pull_request }, created_at: '2022-04-26 18:30:53 UTC', performed_via_github_app: nil @@ -203,7 +196,6 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do 'milestone_title' => (with_milestone ? 'milestone title' : nil), 'source' => { 'type' => 'issue', 'id' => 123456 }, 'assignee' => (with_assignee ? { 'id' => 5, 'login' => 'tom' } : nil), - 'assigner' => (with_assignee ? { 'id' => 6, 'login' => 'jerry' } : nil), 'issue' => { 'number' => 2, 'pull_request' => pull_request }, 'created_at' => '2022-04-26 18:30:53 UTC', 'performed_via_github_app' => nil diff --git a/spec/lib/gitlab/github_import/user_finder_spec.rb b/spec/lib/gitlab/github_import/user_finder_spec.rb index d85e298785c..27426763232 100644 --- a/spec/lib/gitlab/github_import/user_finder_spec.rb +++ b/spec/lib/gitlab/github_import/user_finder_spec.rb @@ -67,12 +67,6 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do it_behaves_like 'user ID finder', :assignee end - - context 'when the author_key parameter is :assigner' do - let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', assigner: user) } - - it_behaves_like 'user ID finder', :assigner - end end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 16e1d8fbc4d..b5f153e7add 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -116,12 +116,20 @@ RSpec.describe ApplicationSetting do it { is_expected.to validate_presence_of(:max_yaml_depth) } it { is_expected.to validate_numericality_of(:max_yaml_depth).only_integer.is_greater_than(0) } it { is_expected.to validate_presence_of(:max_pages_size) } + it { is_expected.to validate_presence_of(:max_pages_custom_domains_per_project) } it 'ensures max_pages_size is an integer greater than 0 (or equal to 0 to indicate unlimited/maximum)' do is_expected.to validate_numericality_of(:max_pages_size).only_integer.is_greater_than_or_equal_to(0) .is_less_than(::Gitlab::Pages::MAX_SIZE / 1.megabyte) end + it 'ensures max_pages_custom_domains_per_project is an integer greater than 0 (or equal to 0 to indicate unlimited/maximum)' do + is_expected + .to validate_numericality_of(:max_pages_custom_domains_per_project) + .only_integer + .is_greater_than_or_equal_to(0) + end + it { is_expected.to validate_presence_of(:jobs_per_stage_page_size) } it { is_expected.to validate_numericality_of(:jobs_per_stage_page_size).only_integer.is_greater_than_or_equal_to(0) } diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index 4691d38184a..644b2ce172a 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -62,7 +62,7 @@ RSpec.describe PagesDomain do let(:domain) { 'my.domain.com' } let(:project) do - instance_double(Project, pages_https_only?: pages_https_only) + instance_double(Project, pages_https_only?: pages_https_only, can_create_custom_domains?: true) end let(:pages_domain) do @@ -571,6 +571,32 @@ RSpec.describe PagesDomain do end end + describe '#validate_custom_domain_count_per_project' do + let_it_be(:project) { create(:project) } + + context 'when max custom domain setting is set to 0' do + it 'returns without an error' do + pages_domain = create(:pages_domain, project: project) + + expect(pages_domain).to be_valid + end + end + + context 'when max custom domain setting is not set to 0' do + it 'returns with an error for extra domains' do + Gitlab::CurrentSettings.update!(max_pages_custom_domains_per_project: 1) + + pages_domain = create(:pages_domain, project: project) + expect(pages_domain).to be_valid + + pages_domain = build(:pages_domain, project: project) + expect(pages_domain).not_to be_valid + expect(pages_domain.errors.full_messages) + .to contain_exactly('This project reached the limit of custom domains. (Max 1)') + end + end + end + describe '.find_by_domain_case_insensitive' do it 'lookup is case-insensitive' do pages_domain = create(:pages_domain, domain: "Pages.IO") diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 77abb7c4dae..664882fd782 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -8337,6 +8337,25 @@ RSpec.describe Project, factory_default: :keep do end end + describe '#can_create_custom_domains?' do + let_it_be(:project) { create(:project) } + let_it_be(:pages_domain) { create(:pages_domain, project: project) } + + subject { project.can_create_custom_domains? } + + context 'when max custom domain setting is set to 0' do + it { is_expected.to be true } + end + + context 'when max custom domain setting is not set to 0' do + before do + Gitlab::CurrentSettings.update!(max_pages_custom_domains_per_project: 1) + end + + it { is_expected.to be false } + end + end + private def finish_job(export_job)