From efeaa328def22e7741fec7bc8f97693195c1af6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= <ayufan@ayufan.eu>
Date: Wed, 3 Nov 2021 16:24:54 +0100
Subject: [PATCH] Add `Migration[2.0]` that enforces
 `restrict_gitlab_migration`

This implements a new migration version and validates if correct
migration version is used depending on a cutoff points.

This allows to enforce a usage of a new `restrict_gitlab_migration`.

Changelog: added
---
 lib/gitlab/database/migration.rb | 21 ++++++++++++++-------
 spec/db/migration_spec.rb        | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+), 7 deletions(-)
 create mode 100644 spec/db/migration_spec.rb

diff --git a/lib/gitlab/database/migration.rb b/lib/gitlab/database/migration.rb
index b2248b0f4eb5d..d2e17eab614b8 100644
--- a/lib/gitlab/database/migration.rb
+++ b/lib/gitlab/database/migration.rb
@@ -33,15 +33,22 @@ def enable_lock_retries?
       # We use major version bumps to indicate significant changes and minor version bumps
       # to indicate backwards-compatible or otherwise minor changes (e.g. a Rails version bump).
       # However, this hasn't been strictly formalized yet.
-      MIGRATION_CLASSES = {
-        1.0 => Class.new(ActiveRecord::Migration[6.1]) do
-          include LockRetriesConcern
-          include Gitlab::Database::MigrationHelpers::V2
-        end
-      }.freeze
+
+      class V1_0 < ActiveRecord::Migration[6.1] # rubocop:disable Naming/ClassAndModuleCamelCase
+        include LockRetriesConcern
+        include Gitlab::Database::MigrationHelpers::V2
+      end
+
+      class V2_0 < V1_0 # rubocop:disable Naming/ClassAndModuleCamelCase
+        include Gitlab::Database::MigrationHelpers::RestrictGitlabSchema
+      end
 
       def self.[](version)
-        MIGRATION_CLASSES[version] || raise(ArgumentError, "Unknown migration version: #{version}")
+        version = version.to_s
+        name = "V#{version.tr('.', '_')}"
+        raise ArgumentError, "Unknown migration version: #{version}" unless const_defined?(name, false)
+
+        const_get(name, false)
       end
 
       # The current version to be used in new migrations
diff --git a/spec/db/migration_spec.rb b/spec/db/migration_spec.rb
new file mode 100644
index 0000000000000..ac649925751ff
--- /dev/null
+++ b/spec/db/migration_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Migrations Validation' do
+  using RSpec::Parameterized::TableSyntax
+
+  # The range describes the timestamps that given migration helper can be used
+  let(:all_migration_classes) do
+    {
+      2022_01_26_21_06_58..                    => Gitlab::Database::Migration[2.0],
+      2021_09_01_15_33_24..                    => Gitlab::Database::Migration[1.0],
+      2021_05_31_05_39_16..2021_09_01_15_33_24 => ActiveRecord::Migration[6.1],
+                         ..2021_05_31_05_39_16 => ActiveRecord::Migration[6.0]
+    }
+  end
+
+  where(:migration) do
+    Gitlab::Database.database_base_models.flat_map do |_, model|
+      model.connection.migration_context.migrations
+    end.uniq
+  end
+
+  with_them do
+    let(:migration_instance) { migration.send(:migration) }
+    let(:allowed_migration_classes) { all_migration_classes.select { |r, _| r.include?(migration.version) }.values }
+
+    it 'uses one of the allowed migration classes' do
+      expect(allowed_migration_classes).to include(be > migration_instance.class)
+    end
+  end
+end
-- 
GitLab