diff --git a/db/docs/batched_background_migrations/backfill_secret_push_protection_enabled.yml b/db/docs/batched_background_migrations/backfill_secret_push_protection_enabled.yml new file mode 100644 index 0000000000000000000000000000000000000000..95c96b49564b90465ae3ab357a8d00eff59a44ee --- /dev/null +++ b/db/docs/batched_background_migrations/backfill_secret_push_protection_enabled.yml @@ -0,0 +1,8 @@ +--- +migration_job_name: BackfillSecretPushProtectionEnabled +description: Backfill missing secret_push_protection_enabled values from pre_receive_secret_detection_enabled +feature_category: secret_detection +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181717 +milestone: '17.10' +queued_migration_version: 20250217193806 +finalized_by: # version of the migration that finalized this BBM diff --git a/db/post_migrate/20250217193806_queue_backfill_secret_push_protection_enabled.rb b/db/post_migrate/20250217193806_queue_backfill_secret_push_protection_enabled.rb new file mode 100644 index 0000000000000000000000000000000000000000..6f6d382600f8b94ee8548df99e3448f3bb63d568 --- /dev/null +++ b/db/post_migrate/20250217193806_queue_backfill_secret_push_protection_enabled.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class QueueBackfillSecretPushProtectionEnabled < Gitlab::Database::Migration[2.2] + milestone '17.10' + + MIGRATION = 'BackfillSecretPushProtectionEnabled' + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 1_000 + SUB_BATCH_SIZE = 100 + MAX_BATCH_SIZE = 10_000 + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + :project_security_settings, + :project_id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE, + max_batch_size: MAX_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, + :project_security_settings, + :project_id, + [] + ) + end +end diff --git a/db/schema_migrations/20250217193806 b/db/schema_migrations/20250217193806 new file mode 100644 index 0000000000000000000000000000000000000000..ed590f06db4b51016a7183b42cba8648fdb07f42 --- /dev/null +++ b/db/schema_migrations/20250217193806 @@ -0,0 +1 @@ +9b63a0d59836f068b8883f34c9f925b0f2eeae91d59a355cb9b94552b9c55a81 \ No newline at end of file diff --git a/lib/gitlab/background_migration/backfill_secret_push_protection_enabled.rb b/lib/gitlab/background_migration/backfill_secret_push_protection_enabled.rb new file mode 100644 index 0000000000000000000000000000000000000000..536e245d3d5767234f595cb3946fc2b01b03b307 --- /dev/null +++ b/lib/gitlab/background_migration/backfill_secret_push_protection_enabled.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + class BackfillSecretPushProtectionEnabled < BatchedMigrationJob + extend ActiveSupport::Concern + + operation_name :backfill_secret_push_protection_enabled + feature_category :secret_detection + + def perform + each_sub_batch do |sub_batch| + sub_batch + .where(secret_push_protection_enabled: nil) + .update_all('secret_push_protection_enabled = pre_receive_secret_detection_enabled') + end + end + end + end +end diff --git a/spec/lib/gitlab/background_migration/backfill_secret_push_protection_enabled_spec.rb b/spec/lib/gitlab/background_migration/backfill_secret_push_protection_enabled_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..c4fa3da8256dc38e5d551ca4942307147e1f54a3 --- /dev/null +++ b/spec/lib/gitlab/background_migration/backfill_secret_push_protection_enabled_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::BackfillSecretPushProtectionEnabled, feature_category: :secret_detection do + let(:project_security_settings) { table(:project_security_settings) } + let!(:connection) { table(:project_security_settings).connection } + let!(:starting_id) { table(:project_security_settings).pluck(:project_id).min } + let!(:end_id) { table(:project_security_settings).pluck(:project_id).max } + + let!(:migration) do + described_class.new( + start_id: starting_id, + end_id: end_id, + batch_table: :project_security_settings, + batch_column: :project_id, + sub_batch_size: 10, + pause_ms: 2, + connection: connection, + job_arguments: [] + ) + end + + let(:organizations) { table(:organizations) } + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + + let!(:organization) { organizations.create!(name: 'organization', path: 'organization') } + let!(:namespace) do + namespaces + .create!(name: 'root-group', path: 'root', type: 'Group', organization_id: organization.id) + .tap do |new_group| + new_group.update!(traversal_ids: [new_group.id]) + end + end + + let!(:group_1) do + namespaces.create!(name: 'random-group', path: 'random', type: 'Group', organization_id: organization.id) + end + + let!(:group_2) do + namespaces.create!(name: 'random-group-2', path: 'random-2', type: 'Group', organization_id: organization.id) + end + + let!(:project_1) do + projects.create!( + organization_id: organization.id, + namespace_id: group_1.id, + project_namespace_id: group_1.id, + name: 'test project', + path: 'test-project' + ) + end + + let!(:project_2) do + projects.create!( + organization_id: organization.id, + namespace_id: group_2.id, + project_namespace_id: group_2.id, + name: 'test project-2', + path: 'test-project-2' + ) + end + + before do + project_security_settings.create!(project_id: project_1.id, pre_receive_secret_detection_enabled: true, + secret_push_protection_enabled: false) + project_security_settings.create!(project_id: project_2.id, pre_receive_secret_detection_enabled: false, + secret_push_protection_enabled: false) + end + + it 'performs without error' do + expect { migration.perform }.not_to raise_error + end + + it 'updates secret_push_protection_enabled to match pre_receive_secret_detection_enabled' do + migration.perform + + security_settings_1 = project_security_settings.find_by(project_id: project_1.id) + security_settings_2 = project_security_settings.find_by(project_id: project_2.id) + expect(security_settings_1.secret_push_protection_enabled).to be(true) + expect(security_settings_2.secret_push_protection_enabled).to be(false) + end +end diff --git a/spec/migrations/20250217193806_queue_backfill_secret_push_protection_enabled_spec.rb b/spec/migrations/20250217193806_queue_backfill_secret_push_protection_enabled_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..5ef6786f76206b4adb05c9d49127f373383ed567 --- /dev/null +++ b/spec/migrations/20250217193806_queue_backfill_secret_push_protection_enabled_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueBackfillSecretPushProtectionEnabled, feature_category: :secret_detection do + let!(:batched_migration) { described_class::MIGRATION } + + it 'schedules a new batched migration' do + reversible_migration do |migration| + migration.before -> { + expect(batched_migration).not_to have_scheduled_batched_migration + } + + migration.after -> { + expect(batched_migration).to have_scheduled_batched_migration( + table_name: :project_security_settings, + column_name: :project_id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE, + max_batch_size: described_class::MAX_BATCH_SIZE, + job_arguments: [] + ) + } + end + end +end