diff --git a/db/docs/batched_background_migrations/cleanup_personal_access_tokens_with_nil_expires_at.yml b/db/docs/batched_background_migrations/cleanup_personal_access_tokens_with_nil_expires_at.yml new file mode 100644 index 0000000000000000000000000000000000000000..630aeccd6e1fc605a0d716d2882be6eabcd37064 --- /dev/null +++ b/db/docs/batched_background_migrations/cleanup_personal_access_tokens_with_nil_expires_at.yml @@ -0,0 +1,6 @@ +--- +migration_job_name: CleanupPersonalAccessTokensWithInvalidExpiresAt +description: Updates value of expires_at column to 365 days from now when it's nil for PersonalAccessTokens +feature_category: system_access +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120239 +milestone: 16.0 diff --git a/db/post_migrate/20230510062503_queue_cleanup_personal_access_tokens_with_nil_expires_at.rb b/db/post_migrate/20230510062503_queue_cleanup_personal_access_tokens_with_nil_expires_at.rb new file mode 100644 index 0000000000000000000000000000000000000000..a7dd1bda9dbd099147c44cf25bdeed229465ba56 --- /dev/null +++ b/db/post_migrate/20230510062503_queue_cleanup_personal_access_tokens_with_nil_expires_at.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class QueueCleanupPersonalAccessTokensWithNilExpiresAt < Gitlab::Database::Migration[2.1] + MIGRATION = "CleanupPersonalAccessTokensWithNilExpiresAt" + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 50_000 + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + :personal_access_tokens, + :id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, :personal_access_tokens, :id, []) + end +end diff --git a/db/schema_migrations/20230510062503 b/db/schema_migrations/20230510062503 new file mode 100644 index 0000000000000000000000000000000000000000..f6be2a733925ac19d2ee0a5e5bfb883911610b78 --- /dev/null +++ b/db/schema_migrations/20230510062503 @@ -0,0 +1 @@ +2bd476bf0389b70aa5736ff69023993d37d54c4d333e3a91de9e57370935d6ec \ No newline at end of file diff --git a/lib/gitlab/background_migration/cleanup_personal_access_tokens_with_nil_expires_at.rb b/lib/gitlab/background_migration/cleanup_personal_access_tokens_with_nil_expires_at.rb new file mode 100644 index 0000000000000000000000000000000000000000..e8ee2a4c2518908b55651b19e84872c5525eb176 --- /dev/null +++ b/lib/gitlab/background_migration/cleanup_personal_access_tokens_with_nil_expires_at.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # Clean up personal access tokens with expires_at value is nil + # and set the value to new default 365 days + class CleanupPersonalAccessTokensWithNilExpiresAt < BatchedMigrationJob + feature_category :system_access + + EXPIRES_AT_DEFAULT = 365.days.from_now + + scope_to ->(relation) { relation.where(expires_at: nil) } + operation_name :update_all + + def perform + each_sub_batch do |sub_batch| + sub_batch.update_all(expires_at: EXPIRES_AT_DEFAULT) + end + end + end + end +end diff --git a/spec/lib/gitlab/background_migration/cleanup_personal_access_tokens_with_nil_expires_at_spec.rb b/spec/lib/gitlab/background_migration/cleanup_personal_access_tokens_with_nil_expires_at_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..ade16c0a780a7b4686edbadf8d2042b6677ca710 --- /dev/null +++ b/spec/lib/gitlab/background_migration/cleanup_personal_access_tokens_with_nil_expires_at_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::CleanupPersonalAccessTokensWithNilExpiresAt, schema: 20230510062503, feature_category: :system_access do # rubocop:disable Layout/LineLength + let(:personal_access_tokens_table) { table(:personal_access_tokens) } + let(:users_table) { table(:users) } + let(:expires_at_default) { described_class::EXPIRES_AT_DEFAULT } + + subject(:perform_migration) do + described_class.new( + start_id: 1, + end_id: 30, + batch_table: :personal_access_tokens, + batch_column: :id, + sub_batch_size: 3, + pause_ms: 0, + connection: ActiveRecord::Base.connection + ).perform + end + + before do + user = users_table.create!(name: 'PAT_USER', email: 'pat_user@gmail.com', username: "pat_user1", projects_limit: 0) + personal_access_tokens_table.create!(user_id: user.id, name: "PAT#1", expires_at: expires_at_default + 1.day) + personal_access_tokens_table.create!(user_id: user.id, name: "PAT#2", expires_at: nil) + personal_access_tokens_table.create!(user_id: user.id, name: "PAT#3", expires_at: Time.zone.now + 2.days) + end + + it 'adds expiry to personal access tokens', :aggregate_failures do + freeze_time do + expect(ActiveRecord::QueryRecorder.new { perform_migration }.count).to eq(3) + + expect(personal_access_tokens_table.find_by_name("PAT#1").expires_at).to eq(expires_at_default.to_date + 1.day) + expect(personal_access_tokens_table.find_by_name("PAT#2").expires_at).to eq(expires_at_default.to_date) + expect(personal_access_tokens_table.find_by_name("PAT#3").expires_at).to eq(Time.zone.now.to_date + 2.days) + end + end +end diff --git a/spec/migrations/20230510062502_queue_cleanup_personal_access_tokens_with_nil_expires_at_spec.rb b/spec/migrations/20230510062502_queue_cleanup_personal_access_tokens_with_nil_expires_at_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..45ef85a49cf11f93a1c551cc78f7eef3c4b51487 --- /dev/null +++ b/spec/migrations/20230510062502_queue_cleanup_personal_access_tokens_with_nil_expires_at_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueCleanupPersonalAccessTokensWithNilExpiresAt, feature_category: :system_access 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: :personal_access_tokens, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + } + end + end +end