Commit 11a340f2 authored by Matthias Käppler's avatar Matthias Käppler

Merge branch 'runner-token-expiration-interval-settings' into 'master'

Add runner token expiration interval settings

See merge request gitlab-org/gitlab!77884
parents 5e0bcd6d d62621aa
......@@ -78,6 +78,10 @@ class ApplicationSetting < ApplicationRecord
chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds
chronic_duration_attr :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval
chronic_duration_attr :group_runner_token_expiration_interval_human_readable, :group_runner_token_expiration_interval
chronic_duration_attr :project_runner_token_expiration_interval_human_readable, :project_runner_token_expiration_interval
validates :grafana_url,
system_hook_url: {
blocked_message: "is blocked: %{exception_message}. " + GRAFANA_URL_ERROR_MESSAGE
......
# frozen_string_literal: true
module RunnerTokenExpirationInterval
extend ActiveSupport::Concern
def enforced_runner_token_expiration_interval_human_readable
interval = enforced_runner_token_expiration_interval
ChronicDuration.output(interval, format: :short) if interval
end
def effective_runner_token_expiration_interval
[
enforced_runner_token_expiration_interval,
runner_token_expiration_interval&.seconds
].compact.min
end
def effective_runner_token_expiration_interval_human_readable
interval = effective_runner_token_expiration_interval
ChronicDuration.output(interval, format: :short) if interval
end
end
......@@ -17,6 +17,8 @@ class Group < Namespace
include GroupAPICompatibility
include EachBatch
include BulkMemberAccessLoad
include ChronicDurationAttribute
include RunnerTokenExpirationInterval
def self.sti_name
'Group'
......@@ -91,6 +93,9 @@ class Group < Namespace
has_many :group_callouts, class_name: 'Users::GroupCallout', foreign_key: :group_id
delegate :prevent_sharing_groups_outside_hierarchy, :new_user_signups_cap, :setup_for_company, :jobs_to_be_done, to: :namespace_settings
delegate :runner_token_expiration_interval, :runner_token_expiration_interval=, :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval_human_readable=, to: :namespace_settings, allow_nil: true
delegate :subgroup_runner_token_expiration_interval, :subgroup_runner_token_expiration_interval=, :subgroup_runner_token_expiration_interval_human_readable, :subgroup_runner_token_expiration_interval_human_readable=, to: :namespace_settings, allow_nil: true
delegate :project_runner_token_expiration_interval, :project_runner_token_expiration_interval=, :project_runner_token_expiration_interval_human_readable, :project_runner_token_expiration_interval_human_readable=, to: :namespace_settings, allow_nil: true
has_one :crm_settings, class_name: 'Group::CrmSettings', inverse_of: :group
......@@ -788,6 +793,17 @@ class Group < Namespace
shared_with_group_links.preload_shared_with_groups.filter { |link| Ability.allowed?(user, :read_group, link.shared_with_group) }
end
def enforced_runner_token_expiration_interval
all_parent_groups = Gitlab::ObjectHierarchy.new(Group.where(id: id)).ancestors
all_group_settings = NamespaceSetting.where(namespace_id: all_parent_groups)
group_interval = all_group_settings.where.not(subgroup_runner_token_expiration_interval: nil).minimum(:subgroup_runner_token_expiration_interval)&.seconds
[
Gitlab::CurrentSettings.group_runner_token_expiration_interval&.seconds,
group_interval
].compact.min
end
private
def max_member_access(user_ids)
......
......@@ -3,6 +3,7 @@
class NamespaceSetting < ApplicationRecord
include CascadingNamespaceSettingAttribute
include Sanitizable
include ChronicDurationAttribute
cascading_attr :delayed_project_removal
......@@ -16,10 +17,15 @@ class NamespaceSetting < ApplicationRecord
enum jobs_to_be_done: { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5 }, _suffix: true
chronic_duration_attr :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval
chronic_duration_attr :subgroup_runner_token_expiration_interval_human_readable, :subgroup_runner_token_expiration_interval
chronic_duration_attr :project_runner_token_expiration_interval_human_readable, :project_runner_token_expiration_interval
NAMESPACE_SETTINGS_PARAMS = [:default_branch_name, :delayed_project_removal,
:lock_delayed_project_removal, :resource_access_token_creation_allowed,
:prevent_sharing_groups_outside_hierarchy, :new_user_signups_cap,
:setup_for_company, :jobs_to_be_done].freeze
:setup_for_company, :jobs_to_be_done, :runner_token_expiration_interval,
:subgroup_runner_token_expiration_interval, :project_runner_token_expiration_interval].freeze
self.primary_key = :namespace_id
......
......@@ -37,6 +37,7 @@ class Project < ApplicationRecord
include EachBatch
include GitlabRoutingHelper
include BulkMemberAccessLoad
include RunnerTokenExpirationInterval
extend Gitlab::Cache::RequestCache
extend Gitlab::Utils::Override
......@@ -454,6 +455,7 @@ class Project < ApplicationRecord
delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :keep_latest_artifact, :keep_latest_artifact=, to: :ci_cd_settings, allow_nil: true
delegate :restrict_user_defined_variables, :restrict_user_defined_variables=, to: :ci_cd_settings, allow_nil: true
delegate :runner_token_expiration_interval, :runner_token_expiration_interval=, :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval_human_readable=, to: :ci_cd_settings, allow_nil: true
delegate :actual_limits, :actual_plan_name, :actual_plan, to: :namespace, allow_nil: true
delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?,
:allow_merge_on_skipped_pipeline=, :has_confluence?, :has_shimo?,
......@@ -2697,6 +2699,10 @@ class Project < ApplicationRecord
ci_cd_settings.keep_latest_artifact?
end
def runner_token_expiration_interval
ci_cd_settings&.runner_token_expiration_interval
end
def group_runners_enabled?
return false unless ci_cd_settings
......@@ -2728,6 +2734,17 @@ class Project < ApplicationRecord
end
end
def enforced_runner_token_expiration_interval
all_parent_groups = Gitlab::ObjectHierarchy.new(Group.where(id: group)).base_and_ancestors
all_group_settings = NamespaceSetting.where(namespace_id: all_parent_groups)
group_interval = all_group_settings.where.not(project_runner_token_expiration_interval: nil).minimum(:project_runner_token_expiration_interval)&.seconds
[
Gitlab::CurrentSettings.project_runner_token_expiration_interval&.seconds,
group_interval
].compact.min
end
private
# overridden in EE
......
# frozen_string_literal: true
class ProjectCiCdSetting < ApplicationRecord
include ChronicDurationAttribute
belongs_to :project, inverse_of: :ci_cd_settings
DEFAULT_GIT_DEPTH = 20
......@@ -17,6 +19,8 @@ class ProjectCiCdSetting < ApplicationRecord
default_value_for :forward_deployment_enabled, true
chronic_duration_attr :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval
def forward_deployment_enabled?
super && ::Feature.enabled?(:forward_deployment_enabled, project, default_enabled: true)
end
......
# frozen_string_literal: true
class AddRunnerTokenExpirationIntervalSettingsToApplicationSettings < Gitlab::Database::Migration[1.0]
def change
[:runner_token_expiration_interval, :group_runner_token_expiration_interval, :project_runner_token_expiration_interval].each do |field|
add_column :application_settings, field, :integer
end
end
end
# frozen_string_literal: true
class AddRunnerTokenExpirationIntervalSettingsToNamespaceSettings < Gitlab::Database::Migration[1.0]
enable_lock_retries!
def change
[:runner_token_expiration_interval, :subgroup_runner_token_expiration_interval, :project_runner_token_expiration_interval].each do |field|
add_column :namespace_settings, field, :integer
end
end
end
# frozen_string_literal: true
class AddRunnerTokenExpirationIntervalSettingsToProjectSettings < Gitlab::Database::Migration[1.0]
enable_lock_retries!
def change
add_column :project_ci_cd_settings, :runner_token_expiration_interval, :integer
end
end
88935eee0781ee1faaf52e3bc89dc5c490c2095d6ccf83e4fd4186641507c173
\ No newline at end of file
aa73b04aa355111564cdc7adb036a2030a28fbb3b524c3b3dbb8248e27b845d7
\ No newline at end of file
4d5adffe1a3e835d6d23f6bd9634993c778fef23d134552d54b003af2a3ff4da
\ No newline at end of file
......@@ -10483,6 +10483,9 @@ CREATE TABLE application_settings (
future_subscriptions jsonb DEFAULT '[]'::jsonb NOT NULL,
user_email_lookup_limit integer DEFAULT 60 NOT NULL,
packages_cleanup_package_file_worker_capacity smallint DEFAULT 2 NOT NULL,
runner_token_expiration_interval integer,
group_runner_token_expiration_interval integer,
project_runner_token_expiration_interval integer,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
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)),
......@@ -16505,6 +16508,9 @@ CREATE TABLE namespace_settings (
new_user_signups_cap integer,
setup_for_company boolean,
jobs_to_be_done smallint,
runner_token_expiration_interval integer,
subgroup_runner_token_expiration_interval integer,
project_runner_token_expiration_interval integer,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255))
);
......@@ -18169,7 +18175,8 @@ CREATE TABLE project_ci_cd_settings (
auto_rollback_enabled boolean DEFAULT false NOT NULL,
keep_latest_artifact boolean DEFAULT true NOT NULL,
restrict_user_defined_variables boolean DEFAULT false NOT NULL,
job_token_scope_enabled boolean DEFAULT false NOT NULL
job_token_scope_enabled boolean DEFAULT false NOT NULL,
runner_token_expiration_interval integer
);
CREATE SEQUENCE project_ci_cd_settings_id_seq
......@@ -132,6 +132,7 @@ module API
Ability.allowed?(options[:current_user], :change_repository_storage, project)
}
expose :keep_latest_artifacts_available?, as: :keep_latest_artifact
expose :runner_token_expiration_interval
# rubocop: disable CodeReuse/ActiveRecord
def self.preload_resource(project)
......
......@@ -120,6 +120,7 @@ included_attributes:
- :name
ci_cd_settings:
- :group_runners_enabled
- :runner_token_expiration_interval
metrics_setting:
- :dashboard_timezone
- :external_dashboard_url
......
......@@ -49,6 +49,8 @@ FactoryBot.define do
forward_deployment_enabled { nil }
restrict_user_defined_variables { nil }
ci_job_token_scope_enabled { nil }
runner_token_expiration_interval { nil }
runner_token_expiration_interval_human_readable { nil }
end
after(:build) do |project, evaluator|
......@@ -92,6 +94,8 @@ FactoryBot.define do
project.keep_latest_artifact = evaluator.keep_latest_artifact unless evaluator.keep_latest_artifact.nil?
project.restrict_user_defined_variables = evaluator.restrict_user_defined_variables unless evaluator.restrict_user_defined_variables.nil?
project.ci_job_token_scope_enabled = evaluator.ci_job_token_scope_enabled unless evaluator.ci_job_token_scope_enabled.nil?
project.runner_token_expiration_interval = evaluator.runner_token_expiration_interval unless evaluator.runner_token_expiration_interval.nil?
project.runner_token_expiration_interval_human_readable = evaluator.runner_token_expiration_interval_human_readable unless evaluator.runner_token_expiration_interval_human_readable.nil?
if evaluator.import_status
import_state = project.import_state || project.build_import_state
......
......@@ -692,6 +692,7 @@ Badge:
- type
ProjectCiCdSetting:
- group_runners_enabled
- runner_token_expiration_interval
ProjectSetting:
- allow_merge_on_skipped_pipeline
- has_confluence
......
......@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Group do
include ReloadHelpers
include StubGitlabCalls
let!(:group) { create(:group) }
......@@ -2930,4 +2931,222 @@ RSpec.describe Group do
end
end
end
describe '#enforced_runner_token_expiration_interval and #effective_runner_token_expiration_interval' do
shared_examples 'no enforced expiration interval' do
it { expect(subject.enforced_runner_token_expiration_interval).to be_nil }
end
shared_examples 'enforced expiration interval' do |enforced_interval:|
it { expect(subject.enforced_runner_token_expiration_interval).to eq(enforced_interval) }
end
shared_examples 'no effective expiration interval' do
it { expect(subject.effective_runner_token_expiration_interval).to be_nil }
end
shared_examples 'effective expiration interval' do |effective_interval:|
it { expect(subject.effective_runner_token_expiration_interval).to eq(effective_interval) }
end
context 'when there is no interval in group settings' do
let_it_be(:group) { create(:group) }
subject { group }
it_behaves_like 'no enforced expiration interval'
it_behaves_like 'no effective expiration interval'
end
context 'when there is a group interval' do
let(:group_settings) { create(:namespace_settings, runner_token_expiration_interval: 3.days.to_i) }
subject { create(:group, namespace_settings: group_settings) }
it_behaves_like 'no enforced expiration interval'
it_behaves_like 'effective expiration interval', effective_interval: 3.days
end
# runner_token_expiration_interval should not affect the expiration interval, only
# group_runner_token_expiration_interval should.
context 'when there is a site-wide enforced shared interval' do
before do
stub_application_setting(runner_token_expiration_interval: 5.days.to_i)
end
let_it_be(:group) { create(:group) }
subject { group }
it_behaves_like 'no enforced expiration interval'
it_behaves_like 'no effective expiration interval'
end
context 'when there is a site-wide enforced group interval' do
before do
stub_application_setting(group_runner_token_expiration_interval: 5.days.to_i)
end
let_it_be(:group) { create(:group) }
subject { group }
it_behaves_like 'enforced expiration interval', enforced_interval: 5.days
it_behaves_like 'effective expiration interval', effective_interval: 5.days
end
# project_runner_token_expiration_interval should not affect the expiration interval, only
# group_runner_token_expiration_interval should.
context 'when there is a site-wide enforced project interval' do
before do
stub_application_setting(project_runner_token_expiration_interval: 5.days.to_i)
end
let_it_be(:group) { create(:group) }
subject { group }
it_behaves_like 'no enforced expiration interval'
it_behaves_like 'no effective expiration interval'
end
# runner_token_expiration_interval should not affect the expiration interval, only
# subgroup_runner_token_expiration_interval should.
context 'when there is a grandparent group enforced group interval' do
let_it_be(:grandparent_group_settings) { create(:namespace_settings, runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:grandparent_group) { create(:group, namespace_settings: grandparent_group_settings) }
let_it_be(:parent_group) { create(:group, parent: grandparent_group) }
let_it_be(:subgroup) { create(:group, parent: parent_group) }
subject { subgroup }
it_behaves_like 'no enforced expiration interval'
it_behaves_like 'no effective expiration interval'
end
context 'when there is a grandparent group enforced subgroup interval' do
let_it_be(:grandparent_group_settings) { create(:namespace_settings, subgroup_runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:grandparent_group) { create(:group, namespace_settings: grandparent_group_settings) }
let_it_be(:parent_group) { create(:group, parent: grandparent_group) }
let_it_be(:subgroup) { create(:group, parent: parent_group) }
subject { subgroup }
it_behaves_like 'enforced expiration interval', enforced_interval: 4.days
it_behaves_like 'effective expiration interval', effective_interval: 4.days
end
# project_runner_token_expiration_interval should not affect the expiration interval, only
# subgroup_runner_token_expiration_interval should.
context 'when there is a grandparent group enforced project interval' do
let_it_be(:grandparent_group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:grandparent_group) { create(:group, namespace_settings: grandparent_group_settings) }
let_it_be(:parent_group) { create(:group, parent: grandparent_group) }
let_it_be(:subgroup) { create(:group, parent: parent_group) }
subject { subgroup }
it_behaves_like 'no enforced expiration interval'
it_behaves_like 'no effective expiration interval'
end
context 'when there is a parent group enforced interval overridden by group interval' do
let_it_be(:parent_group_settings) { create(:namespace_settings, subgroup_runner_token_expiration_interval: 5.days.to_i) }
let_it_be(:parent_group) { create(:group, namespace_settings: parent_group_settings) }
let_it_be(:group_settings) { create(:namespace_settings, runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:subgroup_with_settings) { create(:group, parent: parent_group, namespace_settings: group_settings) }
subject { subgroup_with_settings }
it_behaves_like 'enforced expiration interval', enforced_interval: 5.days
it_behaves_like 'effective expiration interval', effective_interval: 4.days
it 'has human-readable expiration intervals' do
expect(subject.enforced_runner_token_expiration_interval_human_readable).to eq('5d')
expect(subject.effective_runner_token_expiration_interval_human_readable).to eq('4d')
end
end
context 'when site-wide enforced interval overrides group interval' do
before do
stub_application_setting(group_runner_token_expiration_interval: 3.days.to_i)
end
let_it_be(:group_settings) { create(:namespace_settings, runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:group_with_settings) { create(:group, namespace_settings: group_settings) }
subject { group_with_settings }
it_behaves_like 'enforced expiration interval', enforced_interval: 3.days
it_behaves_like 'effective expiration interval', effective_interval: 3.days
end
context 'when group interval overrides site-wide enforced interval' do
before do
stub_application_setting(group_runner_token_expiration_interval: 5.days.to_i)
end
let_it_be(:group_settings) { create(:namespace_settings, runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:group_with_settings) { create(:group, namespace_settings: group_settings) }
subject { group_with_settings }
it_behaves_like 'enforced expiration interval', enforced_interval: 5.days
it_behaves_like 'effective expiration interval', effective_interval: 4.days
end
context 'when site-wide enforced interval overrides parent group enforced interval' do
before do
stub_application_setting(group_runner_token_expiration_interval: 3.days.to_i)
end
let_it_be(:parent_group_settings) { create(:namespace_settings, subgroup_runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:parent_group) { create(:group, namespace_settings: parent_group_settings) }
let_it_be(:subgroup) { create(:group, parent: parent_group) }
subject { subgroup }
it_behaves_like 'enforced expiration interval', enforced_interval: 3.days
it_behaves_like 'effective expiration interval', effective_interval: 3.days
end
context 'when parent group enforced interval overrides site-wide enforced interval' do
before do
stub_application_setting(group_runner_token_expiration_interval: 5.days.to_i)
end
let_it_be(:parent_group_settings) { create(:namespace_settings, subgroup_runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:parent_group) { create(:group, namespace_settings: parent_group_settings) }
let_it_be(:subgroup) { create(:group, parent: parent_group) }
subject { subgroup }
it_behaves_like 'enforced expiration interval', enforced_interval: 4.days
it_behaves_like 'effective expiration interval', effective_interval: 4.days
end
# Unrelated groups should not affect the expiration interval.
context 'when there is an enforced group interval in an unrelated group' do
let_it_be(:unrelated_group_settings) { create(:namespace_settings, subgroup_runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:unrelated_group) { create(:group, namespace_settings: unrelated_group_settings) }
let_it_be(:group) { create(:group) }
subject { group }
it_behaves_like 'no enforced expiration interval'
it_behaves_like 'no effective expiration interval'
end
# Subgroups should not affect the parent group expiration interval.
context 'when there is an enforced group interval in a subgroup' do
let_it_be(:subgroup_settings) { create(:namespace_settings, subgroup_runner_token_expiration_interval: 4.days.to_i) }
let_it_be(:subgroup) { create(:group, parent: group, namespace_settings: subgroup_settings) }
let_it_be(:group) { create(:group) }
subject { group }
it_behaves_like 'no enforced expiration interval'
it_behaves_like 'no effective expiration interval'
end
end
end
This diff is collapsed.
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