Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2018-11-28 15:14:04 +0300
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2018-11-28 15:14:04 +0300
commit0b8a2779e788bd05180176a11ac585fd7999b76b (patch)
tree9426c5fe4eb41513af5066889891c10187da6f9b /lib/gitlab/background_migration
parent4c7665f2f930bba855646143684070544044de10 (diff)
parent0f800a5c0532508c84cee24bf44a4b9ce68168a2 (diff)
Merge branch 'master' into fix/gb/encrypt-runners-tokens
* master: (243 commits) Conflicts: db/schema.rb lib/gitlab/import_export/import_export.yml
Diffstat (limited to 'lib/gitlab/background_migration')
-rw-r--r--lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb209
-rw-r--r--lib/gitlab/background_migration/encrypt_columns.rb14
2 files changed, 223 insertions, 0 deletions
diff --git a/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb b/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb
new file mode 100644
index 00000000000..29fa0f18448
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb
@@ -0,0 +1,209 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # This module is used to write the full path of all projects to
+ # the git repository config file.
+ # Storing the full project path in the git config allows admins to
+ # easily identify a project when it is using hashed storage.
+ module BackfillProjectFullpathInRepoConfig
+ OrphanedNamespaceError = Class.new(StandardError)
+
+ module Storage
+ # Class that returns the disk path for a project using hashed storage
+ class HashedProject
+ attr_accessor :project
+
+ ROOT_PATH_PREFIX = '@hashed'
+
+ def initialize(project)
+ @project = project
+ end
+
+ def disk_path
+ "#{ROOT_PATH_PREFIX}/#{disk_hash[0..1]}/#{disk_hash[2..3]}/#{disk_hash}"
+ end
+
+ def disk_hash
+ @disk_hash ||= Digest::SHA2.hexdigest(project.id.to_s) if project.id
+ end
+ end
+
+ # Class that returns the disk path for a project using legacy storage
+ class LegacyProject
+ attr_accessor :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def disk_path
+ project.full_path
+ end
+ end
+ end
+
+ # Concern used by Project and Namespace to determine the full
+ # route to the project
+ module Routable
+ extend ActiveSupport::Concern
+
+ def full_path
+ @full_path ||= build_full_path
+ end
+
+ def build_full_path
+ return path unless has_parent?
+
+ raise OrphanedNamespaceError if parent.nil?
+
+ parent.full_path + '/' + path
+ end
+
+ def has_parent?
+ read_attribute(association(:parent).reflection.foreign_key)
+ end
+ end
+
+ # Class used to interact with repository using Gitaly
+ class Repository
+ attr_reader :storage
+
+ def initialize(storage, relative_path)
+ @storage = storage
+ @relative_path = relative_path
+ end
+
+ def gitaly_repository
+ Gitaly::Repository.new(storage_name: @storage, relative_path: @relative_path)
+ end
+ end
+
+ # Namespace can be a user or group. It can be the root or a
+ # child of another namespace.
+ class Namespace < ActiveRecord::Base
+ self.table_name = 'namespaces'
+ self.inheritance_column = nil
+
+ include Routable
+
+ belongs_to :parent, class_name: 'Namespace', inverse_of: 'namespaces'
+ has_many :projects, inverse_of: :parent
+ has_many :namespaces, inverse_of: :parent
+ end
+
+ # Project is where the repository (etc.) is stored
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+
+ include Routable
+ include EachBatch
+
+ FULLPATH_CONFIG_KEY = 'gitlab.fullpath'
+
+ belongs_to :parent, class_name: 'Namespace', foreign_key: :namespace_id, inverse_of: 'projects'
+ delegate :disk_path, to: :storage
+
+ def add_fullpath_config
+ entries = { FULLPATH_CONFIG_KEY => full_path }
+
+ repository_service.set_config(entries)
+ end
+
+ def remove_fullpath_config
+ repository_service.delete_config([FULLPATH_CONFIG_KEY])
+ end
+
+ def cleanup_repository
+ repository_service.cleanup
+ end
+
+ def storage
+ @storage ||=
+ if hashed_storage?
+ Storage::HashedProject.new(self)
+ else
+ Storage::LegacyProject.new(self)
+ end
+ end
+
+ def hashed_storage?
+ self.storage_version && self.storage_version >= 1
+ end
+
+ def repository
+ @repository ||= Repository.new(repository_storage, disk_path + '.git')
+ end
+
+ def repository_service
+ @repository_service ||= Gitlab::GitalyClient::RepositoryService.new(repository)
+ end
+ end
+
+ # Base class for Up and Down migration classes
+ class BackfillFullpathMigration
+ RETRY_DELAY = 15.minutes
+ MAX_RETRIES = 2
+
+ # Base class for retrying one project
+ class BaseRetryOne
+ def perform(project_id, retry_count)
+ project = Project.find(project_id)
+
+ return unless project
+
+ migration_class.new.safe_perform_one(project, retry_count)
+ end
+ end
+
+ def perform(start_id, end_id)
+ Project.includes(:parent).where(id: start_id..end_id).each do |project|
+ safe_perform_one(project)
+ end
+ end
+
+ def safe_perform_one(project, retry_count = 0)
+ perform_one(project)
+ rescue GRPC::NotFound, GRPC::InvalidArgument, OrphanedNamespaceError
+ nil
+ rescue GRPC::BadStatus
+ schedule_retry(project, retry_count + 1) if retry_count < MAX_RETRIES
+ end
+
+ def schedule_retry(project, retry_count)
+ BackgroundMigrationWorker.perform_in(RETRY_DELAY, self.class::RetryOne.name, [project.id, retry_count])
+ end
+ end
+
+ # Class to add the fullpath to the git repo config
+ class Up < BackfillFullpathMigration
+ # Class used to retry
+ class RetryOne < BaseRetryOne
+ def migration_class
+ Up
+ end
+ end
+
+ def perform_one(project)
+ project.cleanup_repository
+ project.add_fullpath_config
+ end
+ end
+
+ # Class to rollback adding the fullpath to the git repo config
+ class Down < BackfillFullpathMigration
+ # Class used to retry
+ class RetryOne < BaseRetryOne
+ def migration_class
+ Down
+ end
+ end
+
+ def perform_one(project)
+ project.cleanup_repository
+ project.remove_fullpath_config
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/encrypt_columns.rb b/lib/gitlab/background_migration/encrypt_columns.rb
index ba806c869c9..c7549da96a8 100644
--- a/lib/gitlab/background_migration/encrypt_columns.rb
+++ b/lib/gitlab/background_migration/encrypt_columns.rb
@@ -19,6 +19,12 @@ module Gitlab
class EncryptColumns
def perform(model, attributes, from, to)
model = model.constantize if model.is_a?(String)
+
+ # If sidekiq hasn't undergone a restart, its idea of what columns are
+ # present may be inaccurate, so ensure this is as fresh as possible
+ model.reset_column_information
+ model.define_attribute_methods
+
attributes = expand_attributes(model, Array(attributes).map(&:to_sym))
model.transaction do
@@ -43,6 +49,14 @@ module Gitlab
raise "Couldn't determine encrypted column for #{klass}##{attribute}" if
crypt_column_name.nil?
+ raise "#{klass} source column: #{attribute} is missing" unless
+ klass.column_names.include?(attribute.to_s)
+
+ # Running the migration without the destination column being present
+ # leads to data loss
+ raise "#{klass} destination column: #{crypt_column_name} is missing" unless
+ klass.column_names.include?(crypt_column_name.to_s)
+
[attribute, crypt_column_name]
end