Commit 8ed789d5 authored by Fabio Pitino's avatar Fabio Pitino Committed by Jose Ivan Vargas

Allow administrators to change plan limits via the UI

`Admin Area > CI/CD > Continuous Integration and Deployment` now
has a section to inspect and edit the each CI/CD plan limit.

Changelog: added
parent 5a0af135
...@@ -38,6 +38,13 @@ class Admin::PlanLimitsController < Admin::ApplicationController ...@@ -38,6 +38,13 @@ class Admin::PlanLimitsController < Admin::ApplicationController
pypi_max_file_size pypi_max_file_size
terraform_module_max_file_size terraform_module_max_file_size
generic_packages_max_file_size generic_packages_max_file_size
ci_pipeline_size
ci_active_jobs
ci_project_subscriptions
ci_pipeline_schedules
ci_needs_size_limit
ci_registered_group_runners
ci_registered_project_runners
]) ])
end end
end end
= form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-ci-cd-settings'), html: { class: 'fieldset-form' } do |f| .settings-content
= form_errors(@application_setting) = form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-ci-cd-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset %fieldset
.form-group .form-group
.form-check .form-check
= f.check_box :auto_devops_enabled, class: 'form-check-input' = f.check_box :auto_devops_enabled, class: 'form-check-input'
= f.label :auto_devops_enabled, class: 'form-check-label' do = f.label :auto_devops_enabled, class: 'form-check-label' do
= s_('CICD|Default to Auto DevOps pipeline for all projects') = s_('CICD|Default to Auto DevOps pipeline for all projects')
.form-text.text-muted
= s_('CICD|The Auto DevOps pipeline runs by default in all projects with no CI/CD configuration file.')
= link_to _('What is Auto DevOps?'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.label :auto_devops_domain, s_('AdminSettings|Auto DevOps domain'), class: 'label-bold'
= f.text_field :auto_devops_domain, class: 'form-control gl-form-input', placeholder: 'example.com'
.form-text.text-muted .form-text.text-muted
= s_('CICD|The Auto DevOps pipeline runs by default in all projects with no CI/CD configuration file.') = s_("AdminSettings|The default domain to use for Auto Review Apps and Auto Deploy stages in all projects.")
= link_to _('What is Auto DevOps?'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer' = link_to _('Learn more.'), help_page_path('topics/autodevops/stages.md', anchor: 'auto-review-apps'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.label :auto_devops_domain, s_('AdminSettings|Auto DevOps domain'), class: 'label-bold'
= f.text_field :auto_devops_domain, class: 'form-control gl-form-input', placeholder: 'example.com'
.form-text.text-muted
= s_("AdminSettings|The default domain to use for Auto Review Apps and Auto Deploy stages in all projects.")
= link_to _('Learn more.'), help_page_path('topics/autodevops/stages.md', anchor: 'auto-review-apps'), target: '_blank', rel: 'noopener noreferrer'
.form-group .form-group
.form-check .form-check
= f.check_box :shared_runners_enabled, class: 'form-check-input' = f.check_box :shared_runners_enabled, class: 'form-check-input'
= f.label :shared_runners_enabled, class: 'form-check-label' do = f.label :shared_runners_enabled, class: 'form-check-label' do
= s_("AdminSettings|Enable shared runners for new projects") = s_("AdminSettings|Enable shared runners for new projects")
.form-text.text-muted .form-text.text-muted
= s_("AdminSettings|All new projects can use the instance's shared runners by default.") = s_("AdminSettings|All new projects can use the instance's shared runners by default.")
= render_if_exists 'admin/application_settings/shared_runners_minutes_setting', form: f = render_if_exists 'admin/application_settings/shared_runners_minutes_setting', form: f
.form-group .form-group
= f.label :shared_runners_text, _('Shared runners details'), class: 'label-bold' = f.label :shared_runners_text, _('Shared runners details'), class: 'label-bold'
= f.text_area :shared_runners_text, class: 'form-control gl-form-input', rows: 4 = f.text_area :shared_runners_text, class: 'form-control gl-form-input', rows: 4
.form-text.text-muted= _("Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported.") .form-text.text-muted= _("Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported.")
.form-group .form-group
= f.label :max_artifacts_size, _('Maximum artifacts size (MB)'), class: 'label-bold' = f.label :max_artifacts_size, _('Maximum artifacts size (MB)'), class: 'label-bold'
= f.number_field :max_artifacts_size, class: 'form-control gl-form-input' = f.number_field :max_artifacts_size, class: 'form-control gl-form-input'
.form-text.text-muted
= _("The maximum file size for job artifacts.")
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
.form-group
= f.label :default_artifacts_expire_in, _('Default artifacts expiration'), class: 'label-bold'
= f.text_field :default_artifacts_expire_in, class: 'form-control gl-form-input'
.form-text.text-muted
= html_escape(_("Set the default expiration time for job artifacts in all projects. Set to %{code_open}0%{code_close} to never expire artifacts by default. If no unit is written, it defaults to seconds. For example, these are all equivalent: %{code_open}3600%{code_close}, %{code_open}60 minutes%{code_close}, or %{code_open}one hour%{code_close}.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
.form-group
.form-check
= f.check_box :keep_latest_artifact, class: 'form-check-input'
= f.label :keep_latest_artifact, class: 'form-check-label' do
= s_('AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines')
.form-text.text-muted .form-text.text-muted
= s_('AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire.') = _("The maximum file size for job artifacts.")
.form-group = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
= f.label :archive_builds_in_human_readable, _('Archive jobs'), class: 'label-bold' .form-group
= f.text_field :archive_builds_in_human_readable, class: 'form-control gl-form-input' = f.label :default_artifacts_expire_in, _('Default artifacts expiration'), class: 'label-bold'
.form-text.text-muted = f.text_field :default_artifacts_expire_in, class: 'form-control gl-form-input'
= html_escape(_("Jobs older than the configured time are considered expired and are archived. Archived jobs can no longer be retried. Leave empty to never archive jobs automatically. The default unit is in days, but you can use other units, for example %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}2 years%{code_close}. Minimum value is 1 day.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'archive-jobs')
.form-group
.form-check
= f.check_box :protected_ci_variables, class: 'form-check-input'
= f.label :protected_ci_variables, class: 'form-check-label' do
= s_('AdminSettings|Protect CI/CD variables by default')
.form-text.text-muted .form-text.text-muted
= s_('AdminSettings|New CI/CD variables in projects and groups default to protected.') = html_escape(_("Set the default expiration time for job artifacts in all projects. Set to %{code_open}0%{code_close} to never expire artifacts by default. If no unit is written, it defaults to seconds. For example, these are all equivalent: %{code_open}3600%{code_close}, %{code_open}60 minutes%{code_close}, or %{code_open}one hour%{code_close}.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
.form-group = link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
= f.label :ci_config_path, _('Default CI/CD configuration file'), class: 'label-bold' .form-group
= f.text_field :default_ci_config_path, class: 'form-control gl-form-input', placeholder: '.gitlab-ci.yml' .form-check
%p.form-text.text-muted = f.check_box :keep_latest_artifact, class: 'form-check-input'
= _("The default CI/CD configuration file and path for new projects.").html_safe = f.label :keep_latest_artifact, class: 'form-check-label' do
= link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'specify-a-custom-cicd-configuration-file'), target: '_blank', rel: 'noopener noreferrer' = s_('AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines')
.form-group .form-text.text-muted
.form-check = s_('AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire.')
= f.check_box :suggest_pipeline_enabled, class: 'form-check-input' .form-group
= f.label :suggest_pipeline_enabled, class: 'form-check-label' do = f.label :archive_builds_in_human_readable, _('Archive jobs'), class: 'label-bold'
= s_('AdminSettings|Enable pipeline suggestion banner') = f.text_field :archive_builds_in_human_readable, class: 'form-control gl-form-input'
.form-text.text-muted .form-text.text-muted
= s_('AdminSettings|Display a banner on merge requests in projects with no pipelines to initiate steps to add a .gitlab-ci.yml file.') = html_escape(_("Jobs older than the configured time are considered expired and are archived. Archived jobs can no longer be retried. Leave empty to never archive jobs automatically. The default unit is in days, but you can use other units, for example %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}2 years%{code_close}. Minimum value is 1 day.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'archive-jobs')
.form-group
.form-check
= f.check_box :protected_ci_variables, class: 'form-check-input'
= f.label :protected_ci_variables, class: 'form-check-label' do
= s_('AdminSettings|Protect CI/CD variables by default')
.form-text.text-muted
= s_('AdminSettings|New CI/CD variables in projects and groups default to protected.')
.form-group
= f.label :ci_config_path, _('Default CI/CD configuration file'), class: 'label-bold'
= f.text_field :default_ci_config_path, class: 'form-control gl-form-input', placeholder: '.gitlab-ci.yml'
%p.form-text.text-muted
= _("The default CI/CD configuration file and path for new projects.").html_safe
= link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'specify-a-custom-cicd-configuration-file'), target: '_blank', rel: 'noopener noreferrer'
.form-group
.form-check
= f.check_box :suggest_pipeline_enabled, class: 'form-check-input'
= f.label :suggest_pipeline_enabled, class: 'form-check-label' do
= s_('AdminSettings|Enable pipeline suggestion banner')
.form-text.text-muted
= s_('AdminSettings|Display a banner on merge requests in projects with no pipelines to initiate steps to add a .gitlab-ci.yml file.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
= f.submit _('Save changes'), class: "gl-button btn btn-confirm" .settings-content
%h4
= s_('AdminSettings|CI/CD limits')
%p
= s_('AdminSettings|Set limit to 0 to disable it.')
.scrolling-tabs-container.inner-page-scroll-tabs
- if @plans.size > 1
%ul.nav-links.scrolling-tabs.mobile-separator.nav.nav-tabs.gl-mb-5
- @plans.each_with_index do |plan, index|
%li
= link_to admin_plan_limits_path(anchor: 'js-ci-cd-settings'), data: { target: "div#plan#{index}", action: "plan#{index}", toggle: 'tab'}, class: index == 0 ? 'active': '' do
= plan.name.capitalize
.tab-content.gl-tab-content
- @plans.each_with_index do |plan, index|
.tab-pane{ :id => "plan#{index}", class: index == 0 ? 'active': '' }
= form_for plan.actual_limits, url: admin_plan_limits_path(anchor: 'js-ci-cd-settings'), html: { class: 'fieldset-form' }, method: :post do |f|
= form_errors(plan)
%fieldset
= f.hidden_field(:plan_id, value: plan.id)
.form-group
= f.label :ci_pipeline_size, s_('AdminSettings|Maximum number of jobs in a single pipeline')
= f.number_field :ci_pipeline_size, class: 'form-control gl-form-input'
.form-group
= f.label :ci_active_jobs, s_('AdminSettings|Total number of jobs in currently active pipelines')
= f.number_field :ci_active_jobs, class: 'form-control gl-form-input'
.form-group
= f.label :ci_project_subscriptions, s_('AdminSettings|Maximum number of pipeline subscriptions to and from a project')
= f.number_field :ci_project_subscriptions, class: 'form-control gl-form-input'
.form-group
= f.label :ci_pipeline_schedules, s_('AdminSettings|Maximum number of pipeline schedules')
= f.number_field :ci_pipeline_schedules, class: 'form-control gl-form-input'
.form-group
= f.label :ci_needs_size_limit, s_('AdminSettings|Maximum number of DAG dependencies that a job can have')
= f.number_field :ci_needs_size_limit, class: 'form-control gl-form-input'
.form-group
= f.label :ci_registered_group_runners, s_('AdminSettings|Maximum number of runners registered per group')
= f.number_field :ci_registered_group_runners, class: 'form-control gl-form-input'
.form-group
= f.label :ci_registered_project_runners, s_('AdminSettings|Maximum number of runners registered per project')
= f.number_field :ci_registered_project_runners, class: 'form-control gl-form-input'
= f.submit s_('AdminSettings|Save %{name} limits').html_safe % { name: plan.name.capitalize }, class: 'btn gl-button btn-confirm'
...@@ -20,8 +20,7 @@ ...@@ -20,8 +20,7 @@
= expanded_by_default? ? _('Collapse') : _('Expand') = expanded_by_default? ? _('Collapse') : _('Expand')
%p %p
= _('Customize CI/CD settings, including Auto DevOps, shared runners, and job artifacts.') = _('Customize CI/CD settings, including Auto DevOps, shared runners, and job artifacts.')
.settings-content = render 'ci_cd'
= render 'ci_cd'
= render_if_exists 'admin/application_settings/required_instance_ci_setting', expanded: expanded_by_default? = render_if_exists 'admin/application_settings/required_instance_ci_setting', expanded: expanded_by_default?
......
...@@ -2606,6 +2606,9 @@ msgstr "" ...@@ -2606,6 +2606,9 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain" msgid "AdminSettings|Auto DevOps domain"
msgstr "" msgstr ""
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt" msgid "AdminSettings|Configure Let's Encrypt"
msgstr "" msgstr ""
...@@ -2645,6 +2648,24 @@ msgstr "" ...@@ -2645,6 +2648,24 @@ msgstr ""
msgid "AdminSettings|Maximum duration of a session for Git operations when 2FA is enabled." msgid "AdminSettings|Maximum duration of a session for Git operations when 2FA is enabled."
msgstr "" msgstr ""
msgid "AdminSettings|Maximum number of DAG dependencies that a job can have"
msgstr ""
msgid "AdminSettings|Maximum number of jobs in a single pipeline"
msgstr ""
msgid "AdminSettings|Maximum number of pipeline schedules"
msgstr ""
msgid "AdminSettings|Maximum number of pipeline subscriptions to and from a project"
msgstr ""
msgid "AdminSettings|Maximum number of runners registered per group"
msgstr ""
msgid "AdminSettings|Maximum number of runners registered per project"
msgstr ""
msgid "AdminSettings|New CI/CD variables in projects and groups default to protected." msgid "AdminSettings|New CI/CD variables in projects and groups default to protected."
msgstr "" msgstr ""
...@@ -2660,6 +2681,9 @@ msgstr "" ...@@ -2660,6 +2681,9 @@ msgstr ""
msgid "AdminSettings|Required pipeline configuration" msgid "AdminSettings|Required pipeline configuration"
msgstr "" msgstr ""
msgid "AdminSettings|Save %{name} limits"
msgstr ""
msgid "AdminSettings|Select a CI/CD template" msgid "AdminSettings|Select a CI/CD template"
msgstr "" msgstr ""
...@@ -2675,6 +2699,9 @@ msgstr "" ...@@ -2675,6 +2699,9 @@ msgstr ""
msgid "AdminSettings|Set a CI/CD template as the required pipeline configuration for all projects in the instance. Project CI/CD configuration merges into the required pipeline configuration when the pipeline runs. %{link_start}What is a required pipeline configuration?%{link_end}" msgid "AdminSettings|Set a CI/CD template as the required pipeline configuration for all projects in the instance. Project CI/CD configuration merges into the required pipeline configuration when the pipeline runs. %{link_start}What is a required pipeline configuration?%{link_end}"
msgstr "" msgstr ""
msgid "AdminSettings|Set limit to 0 to disable it."
msgstr ""
msgid "AdminSettings|Set the initial name and protections for the default branch of new repositories created in the instance." msgid "AdminSettings|Set the initial name and protections for the default branch of new repositories created in the instance."
msgstr "" msgstr ""
...@@ -2696,6 +2723,9 @@ msgstr "" ...@@ -2696,6 +2723,9 @@ msgstr ""
msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}" msgid "AdminSettings|The template for the required pipeline configuration can be one of the GitLab-provided templates, or a custom template added to an instance template repository. %{link_start}How do I create an instance template repository?%{link_end}"
msgstr "" msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
msgid "AdminStatistics|Active Users" msgid "AdminStatistics|Active Users"
msgstr "" msgstr ""
......
...@@ -311,7 +311,9 @@ RSpec.describe 'Admin updates settings' do ...@@ -311,7 +311,9 @@ RSpec.describe 'Admin updates settings' do
end end
context 'CI/CD page' do context 'CI/CD page' do
it 'change CI/CD settings' do let_it_be(:default_plan) { create(:default_plan) }
it 'changes CI/CD settings' do
visit ci_cd_admin_application_settings_path visit ci_cd_admin_application_settings_path
page.within('.as-ci-cd') do page.within('.as-ci-cd') do
...@@ -329,6 +331,31 @@ RSpec.describe 'Admin updates settings' do ...@@ -329,6 +331,31 @@ RSpec.describe 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully" expect(page).to have_content "Application settings saved successfully"
end end
it 'changes CI/CD limits', :aggregate_failures do
visit ci_cd_admin_application_settings_path
page.within('.as-ci-cd') do
fill_in 'plan_limits_ci_pipeline_size', with: 10
fill_in 'plan_limits_ci_active_jobs', with: 20
fill_in 'plan_limits_ci_project_subscriptions', with: 30
fill_in 'plan_limits_ci_pipeline_schedules', with: 40
fill_in 'plan_limits_ci_needs_size_limit', with: 50
fill_in 'plan_limits_ci_registered_group_runners', with: 60
fill_in 'plan_limits_ci_registered_project_runners', with: 70
click_button 'Save Default limits'
end
limits = default_plan.reload.limits
expect(limits.ci_pipeline_size).to eq(10)
expect(limits.ci_active_jobs).to eq(20)
expect(limits.ci_project_subscriptions).to eq(30)
expect(limits.ci_pipeline_schedules).to eq(40)
expect(limits.ci_needs_size_limit).to eq(50)
expect(limits.ci_registered_group_runners).to eq(60)
expect(limits.ci_registered_project_runners).to eq(70)
expect(page).to have_content 'Application limits saved successfully'
end
context 'Runner Registration' do context 'Runner Registration' do
context 'when feature is enabled' do context 'when feature is enabled' do
before do before do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'admin/application_settings/_ci_cd' do
let_it_be(:admin) { create(:admin) }
let_it_be(:application_setting) { build(:application_setting) }
let_it_be(:limits_attributes) do
{
ci_pipeline_size: 10,
ci_active_jobs: 20,
ci_project_subscriptions: 30,
ci_pipeline_schedules: 40,
ci_needs_size_limit: 50,
ci_registered_group_runners: 60,
ci_registered_project_runners: 70
}
end
let_it_be(:default_plan_limits) { create(:plan_limits, :default_plan, **limits_attributes) }
let(:page) { Capybara::Node::Simple.new(rendered) }
before do
assign(:application_setting, application_setting)
allow(view).to receive(:current_user) { admin }
allow(view).to receive(:expanded) { true }
end
subject { render partial: 'admin/application_settings/ci_cd' }
context 'limits' do
before do
assign(:plans, [default_plan_limits.plan])
end
it 'has fields for CI/CD limits', :aggregate_failures do
subject
expect(rendered).to have_field('Maximum number of jobs in a single pipeline', type: 'number')
expect(page.find_field('Maximum number of jobs in a single pipeline').value).to eq('10')
expect(rendered).to have_field('Total number of jobs in currently active pipelines', type: 'number')
expect(page.find_field('Total number of jobs in currently active pipelines').value).to eq('20')
expect(rendered).to have_field('Maximum number of pipeline subscriptions to and from a project', type: 'number')
expect(page.find_field('Maximum number of pipeline subscriptions to and from a project').value).to eq('30')
expect(rendered).to have_field('Maximum number of pipeline schedules', type: 'number')
expect(page.find_field('Maximum number of pipeline schedules').value).to eq('40')
expect(rendered).to have_field('Maximum number of DAG dependencies that a job can have', type: 'number')
expect(page.find_field('Maximum number of DAG dependencies that a job can have').value).to eq('50')
expect(rendered).to have_field('Maximum number of runners registered per group', type: 'number')
expect(page.find_field('Maximum number of runners registered per group').value).to eq('60')
expect(rendered).to have_field('Maximum number of runners registered per project', type: 'number')
expect(page.find_field('Maximum number of runners registered per project').value).to eq('70')
end
it 'does not display the plan name when there is only one plan' do
subject
expect(page).not_to have_selector('a[data-action="plan0"]')
end
end
context 'with multiple plans' do
let_it_be(:plan) { create(:plan, name: 'Ultimate') }
let_it_be(:ultimate_plan_limits) { create(:plan_limits, plan: plan, **limits_attributes) }
before do
assign(:plans, [default_plan_limits.plan, ultimate_plan_limits.plan])
end
it 'displays the plan name when there is more than one plan' do
subject
expect(page).to have_content('Default')
expect(page).to have_content('Ultimate')
expect(page).to have_selector('a[data-action="plan0"]')
expect(page).to have_selector('a[data-action="plan1"]')
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment