diff --git a/db/post_migrate/20210727113447_backfill_integrations_type_new.rb b/db/post_migrate/20210727113447_backfill_integrations_type_new.rb new file mode 100644 index 0000000000000000000000000000000000000000..8544c236fd7bf439ff072f809653c327d5c7214b --- /dev/null +++ b/db/post_migrate/20210727113447_backfill_integrations_type_new.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class BackfillIntegrationsTypeNew < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + MIGRATION = 'BackfillIntegrationsTypeNew' + INTERVAL = 2.minutes + + def up + queue_batched_background_migration( + MIGRATION, + :integrations, + :id, + job_interval: INTERVAL + ) + end + + def down + Gitlab::Database::BackgroundMigration::BatchedMigration + .for_configuration(MIGRATION, :integrations, :id, []) + .delete_all + end +end diff --git a/db/schema_migrations/20210727113447 b/db/schema_migrations/20210727113447 new file mode 100644 index 0000000000000000000000000000000000000000..236022f5af391320ac7ed9542f4690b7401e501d --- /dev/null +++ b/db/schema_migrations/20210727113447 @@ -0,0 +1 @@ +19e23131949e6056ea9837231fac6a2307fb52a8287eb34cc6e89eed11d52849 \ No newline at end of file diff --git a/lib/gitlab/background_migration/backfill_integrations_type_new.rb b/lib/gitlab/background_migration/backfill_integrations_type_new.rb new file mode 100644 index 0000000000000000000000000000000000000000..6a2d82aaeeebf7060bdce65480cf3d4a906555cd --- /dev/null +++ b/lib/gitlab/background_migration/backfill_integrations_type_new.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # Backfills the new `integrations.type_new` column, which contains + # the real class name, rather than the legacy class name in `type` + # which is mapped via `Gitlab::Integrations::StiType`. + class BackfillIntegrationsTypeNew + def perform(start_id, stop_id, *args) + ActiveRecord::Base.connection.execute(<<~SQL) + WITH mapping(old_type, new_type) AS (VALUES + ('AsanaService', 'Integrations::Asana'), + ('AssemblaService', 'Integrations::Assembla'), + ('BambooService', 'Integrations::Bamboo'), + ('BugzillaService', 'Integrations::Bugzilla'), + ('BuildkiteService', 'Integrations::Buildkite'), + ('CampfireService', 'Integrations::Campfire'), + ('ConfluenceService', 'Integrations::Confluence'), + ('CustomIssueTrackerService', 'Integrations::CustomIssueTracker'), + ('DatadogService', 'Integrations::Datadog'), + ('DiscordService', 'Integrations::Discord'), + ('DroneCiService', 'Integrations::DroneCi'), + ('EmailsOnPushService', 'Integrations::EmailsOnPush'), + ('EwmService', 'Integrations::Ewm'), + ('ExternalWikiService', 'Integrations::ExternalWiki'), + ('FlowdockService', 'Integrations::Flowdock'), + ('HangoutsChatService', 'Integrations::HangoutsChat'), + ('IrkerService', 'Integrations::Irker'), + ('JenkinsService', 'Integrations::Jenkins'), + ('JiraService', 'Integrations::Jira'), + ('MattermostService', 'Integrations::Mattermost'), + ('MattermostSlashCommandsService', 'Integrations::MattermostSlashCommands'), + ('MicrosoftTeamsService', 'Integrations::MicrosoftTeams'), + ('MockCiService', 'Integrations::MockCi'), + ('MockMonitoringService', 'Integrations::MockMonitoring'), + ('PackagistService', 'Integrations::Packagist'), + ('PipelinesEmailService', 'Integrations::PipelinesEmail'), + ('PivotaltrackerService', 'Integrations::Pivotaltracker'), + ('PrometheusService', 'Integrations::Prometheus'), + ('PushoverService', 'Integrations::Pushover'), + ('RedmineService', 'Integrations::Redmine'), + ('SlackService', 'Integrations::Slack'), + ('SlackSlashCommandsService', 'Integrations::SlackSlashCommands'), + ('TeamcityService', 'Integrations::Teamcity'), + ('UnifyCircuitService', 'Integrations::UnifyCircuit'), + ('WebexTeamsService', 'Integrations::WebexTeams'), + ('YoutrackService', 'Integrations::Youtrack'), + + -- EE-only integrations + ('GithubService', 'Integrations::Github'), + ('GitlabSlackApplicationService', 'Integrations::GitlabSlackApplication') + ) + + UPDATE integrations SET type_new = mapping.new_type + FROM mapping + WHERE integrations.id BETWEEN #{start_id} AND #{stop_id} + AND integrations.type = mapping.old_type + SQL + end + end + end +end diff --git a/lib/gitlab/integrations/sti_type.rb b/lib/gitlab/integrations/sti_type.rb index f7a0d115aee4651be557f67abf050647c04062c2..0fa9f435b5c68181eb32d126e1a53fa887622241 100644 --- a/lib/gitlab/integrations/sti_type.rb +++ b/lib/gitlab/integrations/sti_type.rb @@ -7,7 +7,7 @@ class StiType < ActiveRecord::Type::String Asana Assembla Bamboo Bugzilla Buildkite Campfire Confluence CustomIssueTracker Datadog Discord DroneCi EmailsOnPush Ewm ExternalWiki Flowdock HangoutsChat Irker Jenkins Jira Mattermost MattermostSlashCommands MicrosoftTeams MockCi MockMonitoring Packagist PipelinesEmail Pivotaltracker - Prometheus Pushover Redmine Slack SlackSlashCommands Teamcity UnifyCircuit Youtrack WebexTeams + Prometheus Pushover Redmine Slack SlackSlashCommands Teamcity UnifyCircuit WebexTeams Youtrack )).freeze def self.namespaced_integrations diff --git a/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb b/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..eaad5f8158b67d03fd79d50b80581cbab4ab2a32 --- /dev/null +++ b/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsTypeNew do + let(:integrations) { table(:integrations) } + let(:namespaced_integrations) { Gitlab::Integrations::StiType.namespaced_integrations } + + before do + integrations.connection.execute 'ALTER TABLE integrations DISABLE TRIGGER "trigger_type_new_on_insert"' + + namespaced_integrations.each_with_index do |type, i| + integrations.create!(id: i + 1, type: "#{type}Service") + end + ensure + integrations.connection.execute 'ALTER TABLE integrations ENABLE TRIGGER "trigger_type_new_on_insert"' + end + + it 'backfills `type_new` for the selected records' do + described_class.new.perform(2, 10) + + expect(integrations.where(id: 2..10).pluck(:type, :type_new)).to contain_exactly( + ['AssemblaService', 'Integrations::Assembla'], + ['BambooService', 'Integrations::Bamboo'], + ['BugzillaService', 'Integrations::Bugzilla'], + ['BuildkiteService', 'Integrations::Buildkite'], + ['CampfireService', 'Integrations::Campfire'], + ['ConfluenceService', 'Integrations::Confluence'], + ['CustomIssueTrackerService', 'Integrations::CustomIssueTracker'], + ['DatadogService', 'Integrations::Datadog'], + ['DiscordService', 'Integrations::Discord'] + ) + + expect(integrations.where.not(id: 2..10)).to all(have_attributes(type_new: nil)) + end +end diff --git a/spec/migrations/backfill_integrations_type_new_spec.rb b/spec/migrations/backfill_integrations_type_new_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..5b8fbf6f555da49d26b6f7224a5504978fc09c8c --- /dev/null +++ b/spec/migrations/backfill_integrations_type_new_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe BackfillIntegrationsTypeNew do + let_it_be(:migration) { described_class::MIGRATION } + let_it_be(:integrations) { table(:integrations) } + + before do + integrations.create!(id: 1) + integrations.create!(id: 2) + integrations.create!(id: 3) + integrations.create!(id: 4) + integrations.create!(id: 5) + end + + describe '#up' do + it 'schedules background jobs for each batch of integrations' do + migrate! + + expect(migration).to have_scheduled_batched_migration( + table_name: :integrations, + column_name: :id, + interval: described_class::INTERVAL + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/support/matchers/background_migrations_matchers.rb b/spec/support/matchers/background_migrations_matchers.rb index 08bbbcc74382bd9bd8647a1b02ec7662f7c2c8d0..d3833a1e8e8ca746bc903937d4fed909f3cd4237 100644 --- a/spec/support/matchers/background_migrations_matchers.rb +++ b/spec/support/matchers/background_migrations_matchers.rb @@ -64,3 +64,33 @@ def same_arrays?(arg, expected) arg.sort == expected.sort end end + +RSpec::Matchers.define :have_scheduled_batched_migration do |table_name: nil, column_name: nil, job_arguments: [], **attributes| + define_method :matches? do |migration| + # Default arguments passed by BatchedMigrationWrapper (values don't matter here) + expect(migration).to be_background_migration_with_arguments([ + _start_id = 1, + _stop_id = 2, + table_name, + column_name, + _sub_batch_size = 10, + _pause_ms = 100, + *job_arguments + ]) + + batched_migrations = + Gitlab::Database::BackgroundMigration::BatchedMigration + .for_configuration(migration, table_name, column_name, job_arguments) + + expect(batched_migrations.count).to be(1) + expect(batched_migrations).to all(have_attributes(attributes)) if attributes.present? + end + + define_method :does_not_match? do |migration| + batched_migrations = + Gitlab::Database::BackgroundMigration::BatchedMigration + .where(job_class_name: migration) + + expect(batched_migrations.count).to be(0) + end +end