after_rename_service_spec.rb 7.7 KB
Newer Older
1 2 3 4 5 6
# frozen_string_literal: true

require 'spec_helper'

describe Projects::AfterRenameService do
  let(:rugged_config) { rugged_repo(project.repository).config }
7 8 9
  let(:legacy_storage) { Storage::LegacyProject.new(project) }
  let(:hashed_storage) { Storage::HashedProject.new(project) }
  let!(:path_before_rename) { project.path }
10
  let!(:full_path_before_rename) { project.full_path }
11
  let!(:path_after_rename) { "#{project.path}-renamed" }
12
  let!(:full_path_after_rename) { "#{project.full_path}-renamed" }
13 14 15

  describe '#execute' do
    context 'using legacy storage' do
16
      let(:project) { create(:project, :repository, :wiki_repo, :legacy_storage) }
17
      let(:project_storage) { project.send(:storage) }
18
      let(:gitlab_shell) { Gitlab::Shell.new }
19 20 21 22 23 24 25

      before do
        # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
        # call. This makes testing a bit easier.
        allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)

        stub_feature_flags(skip_hashed_storage_upgrade: false)
26
        stub_application_setting(hashed_storage_enabled: false)
27 28 29 30 31 32 33 34 35 36 37
      end

      it 'renames a repository' do
        stub_container_registry_config(enabled: false)

        expect_any_instance_of(SystemHooksService)
          .to receive(:execute_hooks_for)
          .with(project, :rename)

        expect_any_instance_of(Gitlab::UploadsTransfer)
          .to receive(:rename_project)
38
          .with(path_before_rename, path_after_rename, project.namespace.full_path)
39

40 41
        expect_repository_exist("#{full_path_before_rename}.git")
        expect_repository_exist("#{full_path_before_rename}.wiki.git")
42

43
        service_execute
44 45 46

        expect_repository_exist("#{full_path_after_rename}.git")
        expect_repository_exist("#{full_path_after_rename}.wiki.git")
47 48 49 50 51 52 53 54 55 56 57 58
      end

      context 'container registry with images' do
        let(:container_repository) { create(:container_repository) }

        before do
          stub_container_registry_config(enabled: true)
          stub_container_registry_tags(repository: :any, tags: ['tag'])
          project.container_repositories << container_repository
        end

        it 'raises a RenameFailedError' do
59
          expect { service_execute }.to raise_error(described_class::RenameFailedError)
60 61 62 63 64 65 66 67 68 69 70
        end
      end

      context 'gitlab pages' do
        before do
          expect(project_storage).to receive(:rename_repo) { true }
        end

        it 'moves pages folder to new location' do
          expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)

71
          service_execute
72 73 74 75 76 77 78 79 80 81 82
        end
      end

      context 'attachments' do
        before do
          expect(project_storage).to receive(:rename_repo) { true }
        end

        it 'moves uploads folder to new location' do
          expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)

83
          service_execute
84 85 86 87
        end
      end

      it 'updates project full path in .git/config' do
88
        service_execute
89 90 91

        expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
      end
92 93 94 95

      it 'updates storage location' do
        allow(project_storage).to receive(:rename_repo).and_return(true)

96
        service_execute
97 98 99 100 101 102

        expect(project.project_repository).to have_attributes(
          disk_path: project.disk_path,
          shard_name: project.repository_storage
        )
      end
103 104

      context 'with hashed storage upgrade when renaming enabled' do
105
        it 'calls HashedStorage::MigrationService with correct options' do
106 107
          stub_application_setting(hashed_storage_enabled: true)

108
          expect_next_instance_of(::Projects::HashedStorage::MigrationService) do |service|
109 110 111 112 113 114
            expect(service).to receive(:execute).and_return(true)
          end

          service_execute
        end
      end
115 116 117
    end

    context 'using hashed storage' do
118
      let(:project) { create(:project, :repository, skip_disk_validation: true) }
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
      let(:gitlab_shell) { Gitlab::Shell.new }
      let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
      let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) }
      let(:hashed_path) { File.join(hashed_prefix, hash) }

      before do
        # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
        # call. This makes testing a bit easier.
        allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)

        stub_feature_flags(skip_hashed_storage_upgrade: false)
        stub_application_setting(hashed_storage_enabled: true)
      end

      it 'renames a repository' do
        stub_container_registry_config(enabled: false)

        expect(gitlab_shell).not_to receive(:mv_repository)

        expect_any_instance_of(SystemHooksService)
          .to receive(:execute_hooks_for)
          .with(project, :rename)

        expect(project).to receive(:expire_caches_before_rename)

144
        service_execute
145 146 147 148 149 150 151 152 153 154 155 156
      end

      context 'container registry with images' do
        let(:container_repository) { create(:container_repository) }

        before do
          stub_container_registry_config(enabled: true)
          stub_container_registry_tags(repository: :any, tags: ['tag'])
          project.container_repositories << container_repository
        end

        it 'raises a RenameFailedError' do
157
          expect { service_execute }
158 159 160 161 162 163 164 165
            .to raise_error(described_class::RenameFailedError)
        end
      end

      context 'gitlab pages' do
        it 'moves pages folder to new location' do
          expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)

166
          service_execute
167 168 169 170
        end
      end

      context 'attachments' do
171 172 173 174 175
        let(:uploader) { create(:upload, :issuable_upload, :with_file, model: project) }
        let(:file_uploader) { build(:file_uploader, project: project) }
        let(:legacy_storage_path) { File.join(file_uploader.root, legacy_storage.disk_path) }
        let(:hashed_storage_path) { File.join(file_uploader.root, hashed_storage.disk_path) }

176 177 178
        it 'keeps uploads folder location unchanged' do
          expect_any_instance_of(Gitlab::UploadsTransfer).not_to receive(:rename_project)

179
          service_execute
180 181 182 183 184
        end

        context 'when not rolled out' do
          let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) }

185 186 187 188 189 190
          it 'moves attachments folder to hashed storage' do
            expect(File.directory?(legacy_storage_path)).to be_truthy
            expect(File.directory?(hashed_storage_path)).to be_falsey

            service_execute
            expect(project.reload.hashed_storage?(:attachments)).to be_truthy
191

192 193
            expect(File.directory?(legacy_storage_path)).to be_falsey
            expect(File.directory?(hashed_storage_path)).to be_truthy
194 195 196 197 198
          end
        end
      end

      it 'updates project full path in .git/config' do
199
        service_execute
200 201 202

        expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
      end
203 204

      it 'updates storage location' do
205
        service_execute
206 207 208 209 210 211

        expect(project.project_repository).to have_attributes(
          disk_path: project.disk_path,
          shard_name: project.repository_storage
        )
      end
212 213
    end
  end
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

  def service_execute
    # AfterRenameService is called by UpdateService after a successful model.update
    # the initialization will include before and after paths values
    project.update(path: path_after_rename)

    described_class.new(project, path_before: path_before_rename, full_path_before: full_path_before_rename).execute
  end

  def expect_repository_exist(full_path_with_extension)
    expect(
      gitlab_shell.exists?(
        project.repository_storage,
        full_path_with_extension
      )
    ).to be_truthy
  end
231
end