spec/lib/cell_manager_spec.rb (390 lines of code) (raw):

# frozen_string_literal: true RSpec.describe CellManager do include ShelloutHelper let(:cell_count) { 2 } let(:enabled) { true } subject { described_class.new } before do stub_gdk_yaml({ 'cells' => { 'enabled' => enabled, 'instance_count' => cell_count } }) stub_no_color_env('true') end shared_examples 'does nothing when cells disabled' do |method, args = []| context 'with cells disabled' do let(:enabled) { false } it 'does nothing' do expect_no_gdk_shellout expect(GDK::Output).not_to receive(:error) expect(subject.method(method).call(*args)).to be(true) end end context 'with no cell instances' do let(:cell_count) { 0 } it 'does nothing' do expect_no_gdk_shellout expect(GDK::Output).not_to receive(:error) expect(subject.method(method).call(*args)).to be(true) end end end shared_examples 'prints error when cell is not set up' do |method, args = []| let(:enabled) { true } let(:cell_count) { 1 } it 'prints an error to run "gdk cells up"', :hide_output do expect(Dir).to receive(:exist?).with(cell_dir(2)).and_return(false) expect(GDK::Output).to receive(:error).with('Cell 2 doesn’t exist yet, run `gdk cells up` first.') expect(subject.method(method).call(*args)).to be(false) end end describe '#up' do it_behaves_like 'does nothing when cells disabled', 'up' context 'when enabled' do let(:cell_2_config) do <<~YAML port_offset: 12000 cells: enabled: false port_offset: 12000 gitlab_topology_service: enabled: false gitlab_http_router: enabled: false gitlab: cell: id: 2 database: { skip_sequence_alteration: false } rails: port: #{GDK.config.port} hostname: #{GDK.config.hostname} session_store: unique_cookie_key_postfix: false session_cookie_token_prefix: cell-2 topology_service: address: other address enabled: true ca_file: #{GDK.config.gitlab.topology_service.ca_file} private_key_file: #{GDK.config.gitlab.topology_service.private_key_file} certificate_file: #{GDK.config.gitlab.topology_service.certificate_file} YAML end let(:cell_3_config) do <<~YAML port_offset: 12150 cells: enabled: false port_offset: 12150 gitlab_topology_service: enabled: true gitlab_http_router: enabled: false gitlab: cell: id: 3 database: { skip_sequence_alteration: false } rails: port: #{GDK.config.port} hostname: #{GDK.config.hostname} session_store: unique_cookie_key_postfix: false session_cookie_token_prefix: cell-3 topology_service: address: #{GDK.config.gitlab.topology_service.address} enabled: true ca_file: other ca_file private_key_file: #{GDK.config.gitlab.topology_service.private_key_file} certificate_file: #{GDK.config.gitlab.topology_service.certificate_file} YAML end let(:cell_4_config) do <<~YAML port_offset: 12300 cells: enabled: false port_offset: 12300 gitlab_topology_service: enabled: false gitlab_http_router: enabled: false gitlab: cell: id: 4 database: { skip_sequence_alteration: false } rails: hostname: #{GDK.config.hostname} port: #{GDK.config.port} session_store: unique_cookie_key_postfix: false session_cookie_token_prefix: cell-4 topology_service: address: #{GDK.config.gitlab.topology_service.address} enabled: true ca_file: #{GDK.config.gitlab.topology_service.ca_file} private_key_file: #{GDK.config.gitlab.topology_service.private_key_file} certificate_file: #{GDK.config.gitlab.topology_service.certificate_file} YAML end before do stub_gdk_yaml <<~YAML gitlab_http_router: enabled: true gitlab_topology_service: enabled: true gitlab: rails: session_store: unique_cookie_key_postfix: false session_cookie_token_prefix: cell-1 cells: enabled: true instance_count: 3 instances: - config: gitlab: topology_service: address: other address - config: gitlab: topology_service: ca_file: other ca_file gitlab_topology_service: enabled: true - # cell 4 has no overrides YAML allow_any_instance_of(GDK::PostgresqlUpgrader).to receive('bin_path_or_fallback').and_return(nil) allow(Dir).to receive(:exist?).and_call_original end context 'when cells exist' do it 'writes cell specific configuration', :aggregate_failures, :hide_output do stub_cell_exists(2) expect_gdk_cells_shellout(2, 'reconfigure') expect_gdk_config(2, cell_2_config) stub_cell_exists(3) expect_gdk_cells_shellout(3, 'reconfigure') expect_gdk_config(3, cell_3_config) stub_cell_exists(4) expect_gdk_cells_shellout(4, 'reconfigure') expect_gdk_config(4, cell_4_config) subject.up end context 'when cells do not exist', :hide_output do it 'writes cell specific configuration' do stub_cell_exists(2, exists: false) stub_cell_exists(2, sub_directory: 'gitlab', exists: false) expect_gdk_command('git', 'clone', GDK.root.to_s, cell_dir(2)) expect_gdk_command(*%w[git remote get-url origin], chdir: GDK.root, success: false) expect_gdk_command(*%w[git remote set-url origin https://gitlab.com/gitlab-org/gitlab-development-kit.git], chdir: cell_dir(2)) expect_gdk_cells_shellout(2, "install gitlab_repo=#{GDK.root}/gitlab") expect_gdk_cells_shellout(2, 'reconfigure') expect_gdk_config(2, cell_2_config) stub_cell_exists(3, exists: false) stub_cell_exists(3, sub_directory: 'gitlab', exists: false) expect_gdk_command('git', 'clone', GDK.root.to_s, cell_dir(3)) expect_gdk_command(*%w[git remote get-url origin], chdir: GDK.root, stdout: "https://gitlab.com/gitlab-community/gitlab-org/gitlab-development-kit.git") expect_gdk_command(*%w[git remote set-url origin https://gitlab.com/gitlab-community/gitlab-org/gitlab-development-kit.git], chdir: cell_dir(3)) expect_gdk_cells_shellout(3, "install gitlab_repo=#{GDK.root}/gitlab") expect_gdk_cells_shellout(3, 'reconfigure') expect_gdk_config(3, cell_3_config) stub_cell_exists(4, exists: false) stub_cell_exists(4, sub_directory: 'gitlab', exists: false) expect_gdk_command('git', 'clone', GDK.root.to_s, cell_dir(4)) expect_gdk_command(*%w[git remote get-url origin], chdir: GDK.root, success: false) expect_gdk_command(*%w[git remote set-url origin https://gitlab.com/gitlab-org/gitlab-development-kit.git], chdir: cell_dir(4)) expect_gdk_cells_shellout(4, "install gitlab_repo=#{GDK.root}/gitlab") expect_gdk_cells_shellout(4, 'reconfigure') expect_gdk_config(4, cell_4_config) subject.up end end end def expect_gdk_config(cell_id, expected_yaml) cell_gdk_yml = "#{cell_dir(cell_id)}/gdk.yml" expected_yaml = YAML.safe_load(expected_yaml) expect(File).to receive(:write) .with(cell_gdk_yml, be_a_kind_of(String)).twice do |_file, content| actual_yaml = YAML.safe_load(content) expect(actual_yaml).to match(expected_yaml) if actual_yaml end expect(File).to receive(:read).with(cell_gdk_yml).and_return('') end end end describe '#update' do it_behaves_like 'does nothing when cells disabled', 'update' it_behaves_like 'prints error when cell is not set up', 'update' context 'with cells enabled' do it 'updates every cell' do stub_cell_exists(2) stub_cell_exists(3) expect_gdk_cells_shellout(2, 'update') expect_gdk_cells_shellout(3, 'update') expect { subject.update }.to output(/Updating cell 2\n.*Updating cell 3/m).to_stdout end context 'when a cell does not exist' do it 'prints an error' do stub_cell_exists(2) stub_cell_exists(3, exists: false) expect_gdk_cells_shellout(2, 'update') expect { subject.update }.to output(/Updating cell 2\n.*Updating cell 3/m).to_stdout.and output(/Cell 3 doesn’t exist yet, run `gdk cells up` first./).to_stderr end end context 'when the first update fails' do it 'skips subsequent updates' do stub_cell_exists(2) stub_cell_exists(3) expect_gdk_cells_shellout(2, 'update', success: false) expect { subject.update }.to output(/Updating cell 2\n$/).to_stdout end end end end describe '#start' do it_behaves_like 'does nothing when cells disabled', 'start' it_behaves_like 'prints error when cell is not set up', 'start' context 'with cells enabled' do it 'starts every cell' do stub_cell_exists(2) stub_cell_exists(3) expect_gdk_cells_shellout(2, 'start') expect_gdk_cells_shellout(3, 'start') expect { subject.start }.not_to output.to_stderr end end end describe '#stop' do it_behaves_like 'does nothing when cells disabled', 'stop' it_behaves_like 'prints error when cell is not set up', 'stop' context 'with cells enabled' do it 'stops every cell' do stub_cell_exists(2) stub_cell_exists(3) expect_gdk_cells_shellout(2, 'stop') expect_gdk_cells_shellout(3, 'stop') expect { subject.stop }.not_to output.to_stderr end end end describe '#restart' do it_behaves_like 'does nothing when cells disabled', 'restart' it_behaves_like 'prints error when cell is not set up', 'restart' context 'with cells enabled' do it 'restarts every cell' do stub_cell_exists(2) stub_cell_exists(3) expect_gdk_cells_shellout(2, 'restart') expect_gdk_cells_shellout(3, 'restart') expect { subject.restart }.not_to output.to_stderr end end end describe '#status' do it_behaves_like 'does nothing when cells disabled', 'status' context 'with cells enabled' do it 'prints the status for every cell' do stub_cell_exists(2) stub_cell_exists(3) expect_gdk_cells_shellout(2, 'status') expect_gdk_cells_shellout(3, 'status') expect { subject.status }.to output("cell-2\ncell-3\n").to_stdout end end end describe '#run_in_cell' do context 'with cells disabled' do let(:enabled) { false } it 'does nothing' do expect_no_gdk_shellout expect(GDK::Output).not_to receive(:error) subject.run_in_cell(2, %w[config list]) end end context 'with no cell instances' do let(:cell_count) { 0 } it 'prints an error' do expect_no_gdk_shellout expect { subject.run_in_cell(2, %w[config list]) }.to output( %r{Cell 2 not found. Check doc/howto/cells.md on how to add local cell instances.} ).to_stderr end end context 'with cells enabled' do context 'with an unknown cell' do it 'prints an error' do stub_cell_exists(3, exists: false) expect_no_gdk_shellout expect { subject.run_in_cell(5, %w[config list]) }.to output( /Cell 5 not found. Found: 2, 3./ ).to_stderr end end context 'when the cell is not set up yet' do it 'prints an error' do stub_cell_exists(3, exists: false) expect_no_gdk_shellout expect { subject.run_in_cell(3, %w[config list]) }.to output( /Cell 3 doesn’t exist yet, run `gdk cells up` first./ ).to_stderr end end it 'runs a command in the cell' do stub_cell_exists(3) expect_gdk_cells_shellout(3, "config list") subject.run_in_cell(3, %w[config list]) end end end describe '#get_config_for' do context 'when cell is not configured' do it 'raises an error' do id = 15 expect do subject.get_config_for(id) end.to raise_error(/No config for cell `#{id}` found/) end end context 'when cell is configured' do before do stub_gdk_yaml({ 'cells' => { 'enabled' => enabled, 'instance_count' => 1, 'instances' => [{ 'config' => { 'hostname' => 'cell-2.test' } }] } }) end it 'returns a config' do id = GDK.config.cells.instances.first.id config = subject.get_config_for(id) expect(config.hostname).to eq('cell-2.test') end end end private def stub_cell_exists(cell_id, sub_directory: nil, exists: true) directory = cell_dir(cell_id) directory << "/#{sub_directory}" if sub_directory allow(Dir).to receive(:exist?).with(directory).and_return(exists) end def expect_gdk_cells_shellout(cell_id, arg_str, quiet: false, success: true) shellout_double = gdk_shellout_double(success?: success) expect_gdk_shellout_command("gdk #{arg_str};", chdir: cell_dir(cell_id)).and_return(shellout_double) expect(shellout_double).to receive(:execute).with(display_output: !quiet).and_return(true) end def expect_gdk_command(*commands, success: true, chdir: nil, stdout: nil) shellout_double = gdk_shellout_double(success?: success) args = {} args[:chdir] = chdir if chdir expect_gdk_shellout_command(*commands, **args).and_return(shellout_double) expect(shellout_double).to receive(:execute).and_return(success) allow(shellout_double).to receive(:read_stdout).and_return(stdout) end def cell_dir(cell_id) "#{GDK.root}/gitlab-cells/cell-#{cell_id}" end end