repository.rb 4.77 KB
Newer Older
1 2
# frozen_string_literal: true

3 4 5 6
require 'yaml'

module Backup
  class Repository
7 8 9 10 11 12
    attr_reader :progress

    def initialize(progress)
      @progress = progress
    end

13 14 15 16
    def dump
      prepare

      Project.find_each(batch_size: 1000) do |project|
17
        progress.print " * #{display_repo_path(project)} ... "
18

19 20 21 22 23
        if project.hashed_storage?(:repository)
          FileUtils.mkdir_p(File.dirname(File.join(backup_repos_path, project.disk_path)))
        else
          FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.full_path)) if project.namespace
        end
24

25 26
        if !empty_repo?(project)
          backup_project(project)
27
          progress.puts "[DONE]".color(:green)
28
        else
29
          progress.puts "[SKIPPED]".color(:cyan)
30
        end
31

32
        wiki = ProjectWiki.new(project)
33

34
        if !empty_repo?(wiki)
35
          backup_project(wiki)
36
          progress.puts "[DONE] Wiki".color(:green)
37 38
        else
          progress.puts "[SKIPPED] Wiki".color(:cyan)
39
        end
40 41 42
      end
    end

43
    def prepare_directories
44 45
      Gitlab.config.repositories.storages.each do |name, _repository_storage|
        Gitlab::GitalyClient::StorageService.new(name).delete_all_repositories
46 47 48
      end
    end

49 50 51 52
    def backup_project(project)
      path_to_project_bundle = path_to_bundle(project)
      Gitlab::GitalyClient::RepositoryService.new(project.repository)
        .create_bundle(path_to_project_bundle)
53

54 55 56
      backup_custom_hooks(project)
    rescue => e
      progress_warn(project, e, 'Failed to backup repo')
57 58
    end

59 60
    def backup_custom_hooks(project)
      FileUtils.mkdir_p(project_backup_path(project))
61

62
      custom_hooks_path = custom_hooks_tar(project)
63 64 65 66
      Gitlab::GitalyClient::RepositoryService.new(project.repository)
        .backup_custom_hooks(custom_hooks_path)
    end

67
    def restore_custom_hooks(project)
68 69 70 71 72 73
      return unless Dir.exist?(project_backup_path(project))
      return if Dir.glob("#{project_backup_path(project)}/custom_hooks*").none?

      custom_hooks_path = custom_hooks_tar(project)
      Gitlab::GitalyClient::RepositoryService.new(project.repository)
        .restore_custom_hooks(custom_hooks_path)
74 75
    end

76 77
    def restore
      prepare_directories
78
      gitlab_shell = Gitlab::Shell.new
79

80
      Project.find_each(batch_size: 1000) do |project|
81
        progress.print " * #{project.full_path} ... "
82
        path_to_project_bundle = path_to_bundle(project)
83
        project.ensure_storage_path_exists
84

85
        restore_repo_success = nil
86 87
        if File.exist?(path_to_project_bundle)
          begin
88 89
            project.repository.create_from_bundle(path_to_project_bundle)
            restore_custom_hooks(project)
90
            restore_repo_success = true
91
          rescue => e
92
            restore_repo_success = false
93 94 95
            progress.puts "Error: #{e}".color(:red)
          end
        else
96
          restore_repo_success = gitlab_shell.create_repository(project.repository_storage, project.disk_path)
97
        end
98

99
        if restore_repo_success
James Lopez's avatar
James Lopez committed
100
          progress.puts "[DONE]".color(:green)
101
        else
102
          progress.puts "[Failed] restoring #{project.full_path} repository".color(:red)
103
        end
104

105
        wiki = ProjectWiki.new(project)
106
        path_to_wiki_bundle = path_to_bundle(wiki)
107

108
        if File.exist?(path_to_wiki_bundle)
109
          progress.print " * #{wiki.full_path} ... "
110
          begin
111
            wiki.repository.create_from_bundle(path_to_wiki_bundle)
112 113
            restore_custom_hooks(wiki)

114
            progress.puts "[DONE]".color(:green)
115
          rescue => e
116
            progress.puts "[Failed] restoring #{wiki.full_path} wiki".color(:red)
117
            progress.puts "Error #{e}".color(:red)
118
          end
119
        end
120 121 122 123 124 125
      end
    end

    protected

    def path_to_bundle(project)
126
      File.join(backup_repos_path, project.disk_path + '.bundle')
127 128
    end

129 130 131
    def project_backup_path(project)
      File.join(backup_repos_path, project.disk_path)
    end
132

133 134
    def custom_hooks_tar(project)
      File.join(project_backup_path(project), "custom_hooks.tar")
135 136 137
    end

    def backup_repos_path
138 139 140
      File.join(Gitlab.config.backup.path, 'repositories')
    end

141 142
    def prepare
      FileUtils.rm_rf(backup_repos_path)
143 144
      FileUtils.mkdir_p(Gitlab.config.backup.path)
      FileUtils.mkdir(backup_repos_path, mode: 0700)
145
    end
146

147 148
    private

149
    def progress_warn(project, cmd, output)
James Lopez's avatar
James Lopez committed
150
      progress.puts "[WARNING] Executing #{cmd}".color(:orange)
151
      progress.puts "Ignoring error on #{display_repo_path(project)} - #{output}".color(:orange)
152 153
    end

James Lopez's avatar
James Lopez committed
154
    def empty_repo?(project_or_wiki)
155 156
      project_or_wiki.repository.expire_emptiness_caches
      project_or_wiki.repository.empty?
157 158
    end

159 160 161
    def display_repo_path(project)
      project.hashed_storage?(:repository) ? "#{project.full_path} (#{project.disk_path})" : project.full_path
    end
162 163
  end
end