summaryrefslogtreecommitdiff
path: root/spec/services/snippets/update_repository_storage_service_spec.rb
blob: 6ba09a9dca97784e301c9b2d4d59f5847c44ccc1 (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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::UpdateRepositoryStorageService do
  include Gitlab::ShellAdapter

  subject { described_class.new(repository_storage_move) }

  describe "#execute" do
    let_it_be_with_reload(:snippet) { create(:snippet, :repository) }
    let_it_be(:destination) { 'test_second_storage' }
    let_it_be(:checksum) { snippet.repository.checksum }

    let(:repository_storage_move_state) { :scheduled }
    let(:repository_storage_move) { create(:snippet_repository_storage_move, repository_storage_move_state, container: snippet, destination_storage_name: destination) }
    let(:snippet_repository_double) { double(:repository) }
    let(:original_snippet_repository_double) { double(:repository) }

    before do
      allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(%w[default test_second_storage])
      allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
      allow(Gitlab::GitalyClient).to receive(:filesystem_id).with(destination).and_return(SecureRandom.uuid)
      allow(Gitlab::Git::Repository).to receive(:new).and_call_original
      allow(Gitlab::Git::Repository).to receive(:new)
        .with(destination, snippet.repository.raw.relative_path, snippet.repository.gl_repository, snippet.repository.full_path)
        .and_return(snippet_repository_double)
      allow(Gitlab::Git::Repository).to receive(:new)
        .with('default', snippet.repository.raw.relative_path, nil, nil)
        .and_return(original_snippet_repository_double)
    end

    context 'when the move succeeds' do
      it 'moves the repository to the new storage and unmarks the repository as read only' do
        old_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
          snippet.repository.path_to_repo
        end

        expect(snippet_repository_double).to receive(:replicate)
          .with(snippet.repository.raw)
        expect(snippet_repository_double).to receive(:checksum)
          .and_return(checksum)
        expect(original_snippet_repository_double).to receive(:remove)

        result = subject.execute
        snippet.reload

        expect(result).to be_success
        expect(snippet).not_to be_repository_read_only
        expect(snippet.repository_storage).to eq(destination)
        expect(gitlab_shell.repository_exists?('default', old_path)).to be(false)
        expect(snippet.snippet_repository.shard_name).to eq(destination)
      end
    end

    context 'when the filesystems are the same' do
      before do
        expect(Gitlab::GitalyClient).to receive(:filesystem_id).twice.and_return(SecureRandom.uuid)
      end

      it 'updates the database without trying to move the repostory', :aggregate_failures do
        result = subject.execute
        snippet.reload

        expect(result).to be_success
        expect(snippet).not_to be_repository_read_only
        expect(snippet.repository_storage).to eq(destination)
        expect(snippet.snippet_repository.shard_name).to eq(destination)
      end
    end

    context 'when the move fails' do
      it 'unmarks the repository as read-only without updating the repository storage' do
        expect(snippet_repository_double).to receive(:replicate)
          .with(snippet.repository.raw)
          .and_raise(Gitlab::Git::CommandError)

        result = subject.execute

        expect(result).to be_error
        expect(snippet).not_to be_repository_read_only
        expect(snippet.repository_storage).to eq('default')
        expect(repository_storage_move).to be_failed
      end
    end

    context 'when the cleanup fails' do
      it 'sets the correct state' do
        expect(snippet_repository_double).to receive(:replicate)
          .with(snippet.repository.raw)
        expect(snippet_repository_double).to receive(:checksum)
          .and_return(checksum)
        expect(original_snippet_repository_double).to receive(:remove)
          .and_raise(Gitlab::Git::CommandError)

        result = subject.execute

        expect(result).to be_error
        expect(repository_storage_move).to be_cleanup_failed
      end
    end

    context 'when the checksum does not match' do
      it 'unmarks the repository as read-only without updating the repository storage' do
        expect(snippet_repository_double).to receive(:replicate)
          .with(snippet.repository.raw)
        expect(snippet_repository_double).to receive(:checksum)
          .and_return('not matching checksum')

        result = subject.execute

        expect(result).to be_error
        expect(snippet).not_to be_repository_read_only
        expect(snippet.repository_storage).to eq('default')
      end
    end

    context 'when the repository move is finished' do
      let(:repository_storage_move_state) { :finished }

      it 'is idempotent' do
        expect do
          result = subject.execute

          expect(result).to be_success
        end.not_to change(repository_storage_move, :state)
      end
    end

    context 'when the repository move is failed' do
      let(:repository_storage_move_state) { :failed }

      it 'is idempotent' do
        expect do
          result = subject.execute

          expect(result).to be_success
        end.not_to change(repository_storage_move, :state)
      end
    end
  end
end