20161226122833_remove_dot_git_from_usernames.rb 3.52 KB
Newer Older
1
class RemoveDotGitFromUsernames < ActiveRecord::Migration[4.2]
2 3 4 5 6 7 8 9 10 11 12
  include Gitlab::Database::MigrationHelpers
  include Gitlab::ShellAdapter

  DOWNTIME = false

  def up
    invalid_users.each do |user|
      id = user['id']
      namespace_id = user['namespace_id']
      path_was = user['username']
      path_was_wildcard = quote_string("#{path_was}/%")
13
      path = quote_string(new_path(path_was))
14

15
      move_namespace(namespace_id, path_was, path)
16 17 18 19 20 21 22 23 24 25 26

      begin
        execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{namespace_id}"
        execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{namespace_id}"
        execute "UPDATE users SET username = '#{path}' WHERE id = #{id}"

        select_all("SELECT id, path FROM routes WHERE path LIKE '#{path_was_wildcard}'").each do |route|
          new_path = "#{path}/#{route['path'].split('/').last}"
          execute "UPDATE routes SET path = '#{new_path}' WHERE id = #{route['id']}"
        end
      rescue => e
27
        say("Couldn't update routes for path #{path_was} to #{path}")
28 29
        # Move namespace back
        move_namespace(namespace_id, path, path_was)
30 31

        raise e
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
      end
    end
  end

  def down
    # nothing to do here
  end

  private

  def invalid_users
    select_all("SELECT u.id, u.username, n.path AS namespace_path, n.id AS namespace_id FROM users u
                INNER JOIN namespaces n ON n.owner_id = u.id
                WHERE n.type is NULL AND n.path LIKE '%.git'")
  end

  def route_exists?(path)
    select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(path)}'").present?
  end

52 53
  def path_exists?(shard, repository_storage_path)
    repository_storage_path && gitlab_shell.exists?(shard, repository_storage_path)
54 55
  end

56 57
  # Accepts invalid path like test.git and returns test_git or
  # test_git1 if test_git already taken
58
  def new_path(path)
59 60 61 62
    # To stay closer with original name and reduce risk of duplicates
    # we rename suffix instead of removing it
    path = path.sub(/\.git\z/, '_git')

63
    check_routes(path.dup, 0, path)
64 65 66
  end

  def check_routes(base, counter, path)
James Lopez's avatar
James Lopez committed
67 68
    route_exists = route_exists?(path)

69 70
    Gitlab.config.repositories.storages.each do |shard, _storage|
      if route_exists || path_exists?(shard, path)
71 72
        counter += 1
        path = "#{base}#{counter}"
73

74
        return check_routes(base, counter, path)
75
      end
76 77 78 79 80
    end

    path
  end

81
  def move_namespace(namespace_id, path_was, path)
82 83
    repository_storages = select_all("SELECT distinct(repository_storage) FROM projects WHERE namespace_id = #{namespace_id}").map do |row|
      row['repository_storage']
84 85
    end.compact

86 87
    # Move the namespace directory in all storages used by member projects
    repository_storages.each do |repository_storage|
88
      # Ensure old directory exists before moving it
89
      gitlab_shell.add_namespace(repository_storage, path_was)
90

91 92
      unless gitlab_shell.mv_namespace(repository_storage, path_was, path)
        Rails.logger.error "Exception moving on shard #{repository_storage} from #{path_was} to #{path}"
93 94 95 96 97 98 99

        # if we cannot move namespace directory we should rollback
        # db changes in order to prevent out of sync between db and fs
        raise Exception.new('namespace directory cannot be moved')
      end
    end

100 101 102 103 104 105 106 107 108
    begin
      Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
    rescue => e
      if path.nil?
        say("Couldn't find a storage path for #{namespace_id}, #{path_was} -- skipping")
      else
        raise e
      end
    end
109 110
  end
end