summaryrefslogtreecommitdiff
path: root/lib/gitlab/background_migration/migrate_fingerprint_sha256_within_keys.rb
blob: 36a339c6b809be0bc2aa931a4caf90eeaf7f9406 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# frozen_string_literal: true

module Gitlab
  module BackgroundMigration
    # This class is responsible to update all sha256 fingerprints within the keys table
    class MigrateFingerprintSha256WithinKeys
      # Temporary AR table for keys
      class Key < ActiveRecord::Base
        include EachBatch

        self.table_name = 'keys'
        self.inheritance_column = :_type_disabled
      end

      TEMP_TABLE = 'tmp_fingerprint_sha256_migration'

      def perform(start_id, stop_id)
        ActiveRecord::Base.transaction do
          execute(<<~SQL)
            CREATE TEMPORARY TABLE #{TEMP_TABLE}
              (id bigint primary key, fingerprint_sha256 bytea not null)
              ON COMMIT DROP
          SQL

          fingerprints = []
          Key.where(id: start_id..stop_id, fingerprint_sha256: nil).find_each do |regular_key|
            if fingerprint = generate_ssh_public_key(regular_key.key)
              bytea = ActiveRecord::Base.connection.escape_bytea(Base64.decode64(fingerprint))

              fingerprints << {
                id: regular_key.id,
                fingerprint_sha256: bytea
              }
            end
          end

          ApplicationRecord.legacy_bulk_insert(TEMP_TABLE, fingerprints) # rubocop:disable Gitlab/BulkInsert

          execute("ANALYZE #{TEMP_TABLE}")

          execute(<<~SQL)
            UPDATE keys
              SET fingerprint_sha256 = t.fingerprint_sha256
              FROM #{TEMP_TABLE} t
              WHERE keys.id = t.id
          SQL
        end
      end

      private

      def generate_ssh_public_key(regular_key)
        Gitlab::SSHPublicKey.new(regular_key).fingerprint("SHA256")&.gsub("SHA256:", "")
      end

      def execute(query)
        ActiveRecord::Base.connection.execute(query)
      end
    end
  end
end