diff --git a/.projections.json.example b/.projections.json.example index f8db0051e33f2ed7cec00f743c123af4a4185914_LnByb2plY3Rpb25zLmpzb24uZXhhbXBsZQ==..120b23950e6893931eef61fecada8c9387ee8623_LnByb2plY3Rpb25zLmpzb24uZXhhbXBsZQ== 100644 --- a/.projections.json.example +++ b/.projections.json.example @@ -7,5 +7,29 @@ "alternate": "lib/{}.rb", "type": "test", "dispatch": "bundle exec rspec {file}" + }, + "files/gitlab-cookbooks/*.rb" : { + "alternate": "spec/chef/cookbooks/{}_spec.rb", + "type": "source" + }, + "spec/chef/cookbooks/*_spec.rb": { + "alternate": "files/gitlab-cookbooks/{}.rb", + "type": "test" + }, + "files/gitlab-ctl-commands/*.rb" : { + "alternate": "spec/chef/gitlab-ctl-commands/{}_spec.rb", + "type": "source" + }, + "spec/chef/gitlab-ctl-commands/*_spec.rb": { + "alternate": "files/gitlab-ctl-commands/{}.rb", + "type": "test" + }, + "files/gitlab-ctl-commands-ee/*.rb" : { + "alternate": "spec/chef/gitlab-ctl-commands-ee/{}_spec.rb", + "type": "source" + }, + "spec/chef/gitlab-ctl-commands-ee/*_spec.rb": { + "alternate": "files/gitlab-ctl-commands-ee/{}.rb", + "type": "test" } } diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f8db0051e33f2ed7cec00f743c123af4a4185914_LnJ1Ym9jb3BfdG9kby55bWw=..120b23950e6893931eef61fecada8c9387ee8623_LnJ1Ym9jb3BfdG9kby55bWw= 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -51,9 +51,9 @@ # Cop supports --auto-correct. Layout/SpaceAroundMethodCallOperator: Exclude: - - 'spec/chef/recipes/postgresql_spec.rb' - - 'spec/gitlab-ctl-commands-ee/lib/postgresql/ee_spec.rb' - - 'spec/libraries/logging_helper_spec.rb' + - 'spec/chef/cookbooks/postgresql/recipes/postgresql_spec.rb' + - 'spec/chef/gitlab-ctl-commands-ee/lib/postgresql/ee_spec.rb' + - 'spec/chef/cookbooks/package/libraries/helpers/logging_helper_spec.rb' # Offense count: 1 # Cop supports --auto-correct. @@ -66,7 +66,7 @@ Lint/ConstantDefinitionInBlock: Exclude: - 'files/gitlab-ctl-commands/upgrade.rb' - - 'spec/chef/secrets_spec.rb' - - 'spec/gitlab-ctl-commands/gitlab_ctl_spec.rb' + - 'spec/chef/cookbooks/package/libraries/secrets_helper_spec.rb' + - 'spec/chef/gitlab-ctl-commands/gitlab_ctl_spec.rb' - 'spec/lib/gitlab/build/image_spec.rb' - 'spec/lib/gitlab/tasks/qa_spec.rb' @@ -71,7 +71,7 @@ - 'spec/lib/gitlab/build/image_spec.rb' - 'spec/lib/gitlab/tasks/qa_spec.rb' - - 'spec/support/shared_examples/geo_promotion_command_accepts_option_shared_examples.rb' - - 'spec/support/shared_examples/gitlab_geo_promotion_commands_shared_examples.rb' + - 'spec/chef/support/shared_examples/geo_promotion_command_accepts_option_shared_examples.rb' + - 'spec/chef/support/shared_examples/gitlab_geo_promotion_commands_shared_examples.rb' # Offense count: 1 # Cop supports --auto-correct. @@ -226,11 +226,11 @@ - 'files/gitlab-ctl-commands-ee/lib/geo/replication.rb' - 'files/gitlab-ctl-commands/lib/gitlab_ctl/util.rb' - 'lib/gitlab/build/trigger.rb' - - 'spec/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb' - - 'spec/gitlab-ctl-commands-ee/lib/geo/promotion_preflight_checks_spec.rb' - - 'spec/gitlab-ctl-commands-ee/lib/geo/replication_spec.rb' - - 'spec/gitlab-ctl-commands/lib/gitlab_ctl_backup_spec.rb' - - 'spec/gitlab-ctl-commands/lib/util_spec.rb' + - 'spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb' + - 'spec/chef/gitlab-ctl-commands-ee/lib/geo/promotion_preflight_checks_spec.rb' + - 'spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_spec.rb' + - 'spec/chef/gitlab-ctl-commands/lib/gitlab_ctl_backup_spec.rb' + - 'spec/chef/gitlab-ctl-commands/lib/util_spec.rb' # Offense count: 1 # Cop supports --auto-correct. @@ -311,11 +311,11 @@ Exclude: - 'files/gitlab-cookbooks/gitlab/libraries/registry.rb' - 'files/gitlab-cookbooks/package/libraries/helpers/redhat_helper.rb' - - 'spec/chef/recipes/add_trusted_certs_spec.rb' - - 'spec/chef/recipes/gitlab-workhorse_spec.rb' - - 'spec/chef/recipes/mailroom_spec.rb' - - 'spec/chef/recipes/puma_spec.rb' - - 'spec/chef/recipes/registry_spec.rb' - - 'spec/chef/recipes/sidekiq_spec.rb' - - 'spec/chef/recipes/unicorn_spec.rb' + - 'spec/chef/cookbooks/gitlab/recipes/add_trusted_certs_spec.rb' + - 'spec/chef/cookbooks/gitlab/recipes/gitlab-workhorse_spec.rb' + - 'spec/chef/cookbooks/gitlab/recipes/mailroom_spec.rb' + - 'spec/chef/cookbooks/gitlab/recipes/puma_spec.rb' + - 'spec/chef/cookbooks/registry/recipes/registry_spec.rb' + - 'spec/chef/cookbooks/gitlab/recipes/sidekiq_spec.rb' + - 'spec/chef/cookbooks/gitlab/recipes/unicorn_spec.rb' - 'spec/chef_helper.rb' @@ -321,5 +321,5 @@ - 'spec/chef_helper.rb' - - 'spec/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb' + - 'spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb' # Offense count: 249 # Cop supports --auto-correct. diff --git a/spec/chef/consul/libraries/consul_helper_spec.rb b/spec/chef/consul/libraries/consul_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2NvbnN1bC9saWJyYXJpZXMvY29uc3VsX2hlbHBlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/consul/libraries/consul_helper_spec.rb +++ /dev/null @@ -1,162 +0,0 @@ -require 'chef_helper' - -RSpec.describe ConsulHelper do - let(:chef_run) { converge_config } - subject { described_class.new(chef_run.node) } - - describe '#running_version' do - let(:consul_api_output) { instance_double('response', code: '200', body: '{"Config": { "Version": "1.8.10" }}') } - - before do - # Un-doing the stub added in chef_helper - allow_any_instance_of(described_class).to receive(:running_version).and_call_original - allow(Gitlab).to receive(:[]).and_call_original - allow(subject).to receive(:get_api).and_yield(consul_api_output) - end - - context 'when consul is not running' do - it 'returns nil' do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('consul').and_return(false) - - expect(subject.running_version).to be_nil - end - end - - context 'when consul is running' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('consul').and_return(true) - end - - it 'parses version from consul api output properly' do - expect(subject.running_version).to eq('1.8.10') - end - end - end - - describe '#installed_version' do - let(:consul_cli_output) do - <<~MSG - Consul v1.7.8 - Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents) - MSG - end - - before do - # Un-doing the stub added in chef_helper - allow_any_instance_of(described_class).to receive(:installed_version).and_call_original - allow(Gitlab).to receive(:[]).and_call_original - allow(VersionHelper).to receive(:version).with(/consul version/).and_return(consul_cli_output) - end - - context 'when consul is not running' do - it 'returns nil' do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('consul').and_return(false) - - expect(subject.installed_version).to be_nil - end - end - - context 'when consul is running' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('consul').and_return(true) - end - - it 'parses consul output properly' do - expect(subject.installed_version).to eq('1.7.8') - end - end - end - - describe '#api_port' do - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - it 'falls back to default ports' do - stub_gitlab_rb( - consul: { - enable: true - } - ) - - expect(subject.api_port('http')).to eq(8500) - expect(subject.api_port('https')).to eq(8501) - expect(subject.configuration).not_to match(%r{"http":8500}) - expect(subject.configuration).not_to match(%r{"https":8501}) - end - - it 'uses the configured ports' do - stub_gitlab_rb( - consul: { - enable: true, - http_port: 18500, - https_port: 18501 - } - ) - - expect(subject.api_port('http')).to eq(18500) - expect(subject.api_port('https')).to eq(18501) - expect(subject.configuration).to match(%r{"http":18500}) - expect(subject.configuration).to match(%r{"https":18501}) - end - - it 'uses the ports from configuration override' do - stub_gitlab_rb( - consul: { - enable: true, - http_port: 18500, - https_port: 18501, - configuration: { - ports: { - http: 28500, - https: 28501, - } - } - } - ) - - expect(subject.api_port('http')).to eq(28500) - expect(subject.api_port('https')).to eq(28501) - expect(subject.configuration).to match(%r{"http":28500}) - expect(subject.configuration).to match(%r{"https":28501}) - end - end - - describe '#api_url' do - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - it 'uses http by default' do - stub_gitlab_rb( - consul: { - enable: true - } - ) - - expect(subject.api_url).to eq('http://localhost:8500') - end - - it 'uses https when tls is enabled' do - stub_gitlab_rb( - consul: { - enable: true, - use_tls: true - } - ) - - expect(subject.api_url).to eq('https://localhost:8501') - end - - it 'uses https when http port is disabled' do - stub_gitlab_rb( - consul: { - enable: true, - http_port: -1 - } - ) - - expect(subject.api_url).to eq('https://localhost:8501') - end - end -end diff --git a/spec/chef/consul/libraries/failover_helper_spec.rb b/spec/chef/consul/libraries/failover_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2NvbnN1bC9saWJyYXJpZXMvZmFpbG92ZXJfaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/consul/libraries/failover_helper_spec.rb +++ /dev/null @@ -1,176 +0,0 @@ -require 'chef_helper' - -require 'json' -require 'logger' - -NodeData = Struct.new('NodeData', :name, :address, :leader?) - -RSpec.describe FailoverHelper::SplitBrain do - let(:node_list) { %w(cats kittens) } - - context 'when passed a list of nodes' do - it 'sets primary_nodes to that list' do - brain = FailoverHelper::SplitBrain.new("Nobody here but us felines", node_list) - expect(brain.primary_nodes).to match_array(node_list) - end - end -end - -RSpec.describe FailoverHelper::LeaderFinder do - let(:total_nodes) { 3 } - let(:failover) { FailoverHelper::LeaderFinder.new("{}", service_data) } - let(:primary_node) { NodeData.new("example_primary_node", "10.0.0.42", true) } - let(:splitbrain_primary_node) { NodeData.new("example_splitbrain_node", "10.0.0.41", true) } - let(:follower_node_01) { NodeData.new("example_node_02", "10.0.0.43", false) } - let(:follower_node_02) { NodeData.new("example_node_03", "10.0.0.44", false) } - - def service_data - service_data = FailoverHelper::ServiceData.new - service_data.service_name = "example_service" - service_data.check_field = "Status" - service_data.leader_value = "passing" - - service_data - end - - def generate_parsed_data(unhealthy_primary: false, multiple_primaries: false) - parsed_data = [] - nodes = [primary_node, follower_node_01, follower_node_02] - - nodes.push(splitbrain_primary_node) if multiple_primaries - - nodes.each do |node| - if node.leader? - leader_status = "passing" - health = unhealthy_primary ? "failing" : "passing" - else - leader_status = "warning" - health = "passing" - end - - node_info = { - "Node" => { - "Node" => node.name, - "Address" => node.address - }, - "Checks" => [ - { - "Node" => node.name, - "CheckID" => "serfHealth", - "Status" => health - }, - { - "Node" => node.name, - "CheckID" => "example_service", - "Status" => leader_status - } - ] - } - - parsed_data.push(node_info) - end - - parsed_data - end - - def stub_healthy_primary_watcher_data - parsed_data = generate_parsed_data - - allow_any_instance_of(FailoverHelper::LeaderFinder).to receive(:parse).and_return(parsed_data) - end - - def stub_unhealthy_primary_watcher_data - parsed_data = generate_parsed_data(unhealthy_primary: true) - - allow_any_instance_of(FailoverHelper::LeaderFinder).to receive(:parse).and_return(parsed_data) - end - - def stub_multiple_primary_nodes_watcher_data - parsed_data = generate_parsed_data(multiple_primaries: true) - - allow_any_instance_of(FailoverHelper::LeaderFinder).to receive(:parse).and_return(parsed_data) - end - - describe "#parse" do - let(:bad_json) { "Nobody here but us errors!" } - - context 'when passed invalid JSON data' do - it 'throws a JSON::Parser error' do - expect { FailoverHelper::LeaderFinder.new(bad_json, service_data) }.to raise_error(JSON::ParserError) - end - end - end - - context 'when valid JSON is received from watch' do - context 'when there is more than one primary node found' do - before do - stub_multiple_primary_nodes_watcher_data - end - - describe "#primary_node" do - it 'throws a SplitBrain error' do - expect { failover.primary_node }.to raise_error(FailoverHelper::SplitBrain, "Split brain detected, multiple primary nodes found!") - end - end - end - - context 'when all nodes are healthy' do - before do - stub_healthy_primary_watcher_data - end - - describe "#healthy_nodes" do - it 'returns the correct number of healthy nodes' do - expect(failover.healthy_nodes.length).to eq(total_nodes) - end - end - - describe "#leader_nodes" do - context 'when there is one leader' do - it 'returns only one leader' do - expect(failover.leader_nodes.length).to eq(1) - end - - it 'returns the correct node' do - primary_node = failover.leader_nodes.first - expect(primary_node.name).to eq(primary_node.name) - end - end - end - - describe "#primary_node_address" do - context 'when the node name resolves via DNS' do - it 'returns the machine name' do - allow_any_instance_of(Resolv::DNS).to receive(:getaddress).and_return(true) - expect(failover.primary_node_address).to eq(primary_node.name) - end - end - - context 'when the node name does not resolve via DNS' do - it 'returns the machine IP address' do - allow_any_instance_of(Resolv::DNS).to receive(:getaddress).and_raise(Resolv::ResolvError) - expect(failover.primary_node_address).to eq(primary_node.address) - end - end - end - end - - context 'when the primary node is not healthy in a single cluster setup' do - before do - stub_unhealthy_primary_watcher_data - end - - describe "#healthy_nodes" do - it 'returns the correct number of healthy nodes' do - expect(failover.healthy_nodes.length).to eq(total_nodes - 1) - end - end - - describe "#leader_nodes" do - it 'throws a PrimaryMissing exception' do - expect { failover.leader_nodes }.to raise_error(FailoverHelper::PrimaryMissing, "No healthy primary node found.") - end - end - end - end -end diff --git a/spec/chef/consul/recipes/configure_services_spec.rb b/spec/chef/consul/recipes/configure_services_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2NvbnN1bC9yZWNpcGVzL2NvbmZpZ3VyZV9zZXJ2aWNlc19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/consul/recipes/configure_services_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'consul::configure_services' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'enabling services' do - before do - stub_gitlab_rb( - consul: { - enable: true, - services: %w(postgresql) - } - ) - end - - it 'includes the enable_service recipe for the corresponding service' do - expect(chef_run).to include_recipe('consul::enable_service_postgresql') - end - end - - describe 'disabling services' do - before do - stub_gitlab_rb( - consul: { - enable: true, - services: [] - } - ) - end - - it 'includes the disable_service for all known services' do - expect(chef_run).to include_recipe('consul::disable_service_postgresql') - end - end -end diff --git a/spec/chef/consul/recipes/consul_spec.rb b/spec/chef/consul/recipes/consul_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2NvbnN1bC9yZWNpcGVzL2NvbnN1bF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/consul/recipes/consul_spec.rb +++ /dev/null @@ -1,359 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'consul' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - let(:consul_conf) { '/var/opt/gitlab/consul/config.json' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'disabled by default' do - it 'includes the disable recipe' do - expect(chef_run).to include_recipe('consul::disable') - end - end - - describe 'consul::disable' do - it_behaves_like 'disabled runit service', 'consul' - end - - context 'when enabled' do - before do - stub_gitlab_rb( - consul: { - enable: true, - config_dir: '/fake/config.d', - data_dir: '/fake/data', - custom_config_dir: '/custom/dir' - } - ) - end - - it 'includes the enable recipe' do - expect(chef_run).to include_recipe('consul::enable') - end - - describe 'consul::enable' do - it_behaves_like 'enabled runit service', 'consul', 'gitlab-consul', 'gitlab-consul', 'gitlab-consul', 'gitlab-consul' - - it 'creates the consul system user and group' do - expect(chef_run).to create_account('Consul user and group').with(username: 'gitlab-consul', groupname: 'gitlab-consul') - end - - it 'includes the configure_services recipe' do - expect(chef_run).to include_recipe('consul::configure_services') - end - - it 'only enables the agent by default' do - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"server":false}) - } - end - - it 'does not include nil values in its configuration' do - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).not_to match(%r{"encryption":}) - } - end - - it 'does not include server default values in its configuration' do - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).not_to match(%r{"bootstrap_expect":3}) - } - end - - it 'creates the necessary directories' do - expect(chef_run).to create_directory('/fake/config.d') - expect(chef_run).to create_directory('/fake/data') - expect(chef_run).to create_directory('/var/log/gitlab/consul') - end - - it 'notifies other resources on configuration change' do - config_json = chef_run.file('/var/opt/gitlab/consul/config.json') - expect(config_json).to notify('execute[reload consul]').to(:run) - expect(config_json).to notify('ruby_block[consul config change]').to(:run) - end - - it 'renders run file with specified options' do - expect(chef_run).to render_file('/opt/gitlab/sv/consul/run').with_content(%r{-config-dir /fake/config.d}) - expect(chef_run).to render_file('/opt/gitlab/sv/consul/run').with_content(%r{-config-dir /custom/dir}) - expect(chef_run).to render_file('/opt/gitlab/sv/consul/run').with_content(%r{-data-dir /fake/data}) - end - end - - context 'with default options' do - it 'allows the user to specify node name' do - stub_gitlab_rb( - consul: { - enable: true - } - ) - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"datacenter":"gitlab_consul"}) - expect(content).to match(%r{"disable_update_check":true}) - expect(content).to match(%r{"enable_script_checks":true}) - expect(content).to match(%r{"node_name":"fauxhai.local"}) - expect(content).to match(%r{"rejoin_after_leave":true}) - expect(content).to match(%r{"server":false}) - } - end - end - - context 'with non-default options' do - before do - stub_gitlab_rb( - consul: { - enable: true, - node_name: 'fakenodename', - username: 'foo', - group: 'bar', - } - ) - end - - it 'allows the user to specify node name' do - expect(chef_run).to render_file(consul_conf).with_content('"node_name":"fakenodename"') - end - - it 'creates the consul system user and group' do - expect(chef_run).to create_account('Consul user and group').with(username: 'foo', groupname: 'bar') - end - - it_behaves_like 'enabled runit service', 'consul', 'foo', 'bar', 'foo', 'bar' - end - - context 'server enabled' do - before do - stub_gitlab_rb( - consul: { - enable: true, - configuration: { - server: true - } - } - ) - end - - it 'enables the server functionality' do - expect(chef_run.node['consul']['configuration']['server']).to eq true - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"server":true}) - expect(content).to match(%r{"bootstrap_expect":3}) - } - end - end - - describe 'pending restart check' do - context 'when running version is same as installed version' do - before do - allow_any_instance_of(ConsulHelper).to receive(:running_version).and_return('1.9.6') - allow_any_instance_of(ConsulHelper).to receive(:installed_version).and_return('1.9.6') - end - - it 'does not raise a warning' do - expect(chef_run).not_to run_ruby_block('warn pending consul restart') - end - end - - context 'when running version is different than installed version' do - before do - allow_any_instance_of(ConsulHelper).to receive(:running_version).and_return('1.6.4') - allow_any_instance_of(ConsulHelper).to receive(:installed_version).and_return('1.9.6') - end - - it 'raises a warning' do - expect(chef_run).to run_ruby_block('warn pending consul restart') - end - end - end - end - - describe 'encryption' do - it 'is not enabled by default' do - stub_gitlab_rb( - consul: { - enable: true, - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).not_to match(%r{"encrypt":}) - expect(content).not_to match(%r{"encrypt_verify_incoming":}) - expect(content).not_to match(%r{"encrypt_verify_outgoing":}) - } - end - - context 'new datacenter' do - it 'uses encryption key and falls back to defaults' do - stub_gitlab_rb( - consul: { - enable: true, - encryption_key: 'fake_key' - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"encrypt":"fake_key"}) - expect(content).not_to match(%r{"encrypt_verify_incoming":}) - expect(content).not_to match(%r{"encrypt_verify_outgoing":}) - } - end - end - - context 'existing datacenter' do - it 'uses encryption key and specified verification settings' do - stub_gitlab_rb( - consul: { - enable: true, - encryption_key: 'fake_key', - encryption_verify_incoming: false, - encryption_verify_outgoing: true, - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"encrypt":"fake_key"}) - expect(content).to match(%r{"encrypt_verify_incoming":false}) - expect(content).to match(%r{"encrypt_verify_outgoing":true}) - } - end - end - - describe 'secure configuration' do - context 'client mode' do - it 'use tls for outgoing' do - stub_gitlab_rb( - consul: { - enable: true, - use_tls: true, - tls_ca_file: '/fake/ca.crt.pem', - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) - expect(content).to match(%r{"verify_outgoing":true}) - expect(content).to match(%r{"verify_incoming":false}) - expect(content).not_to match(%r{"https":8501}) - expect(content).not_to match(%r{"cert_file":}) - expect(content).not_to match(%r{"key_file":}) - } - end - - it 'use tls client authentication' do - stub_gitlab_rb( - consul: { - enable: true, - use_tls: true, - tls_ca_file: '/fake/ca.crt.pem', - tls_certificate_file: '/fake/client.crt.pem', - tls_key_file: '/fake/client.key.pem' - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) - expect(content).to match(%r{"cert_file":"/fake/client.crt.pem"}) - expect(content).to match(%r{"key_file":"/fake/client.key.pem"}) - expect(content).to match(%r{"verify_outgoing":true}) - expect(content).to match(%r{"verify_incoming":false}) - expect(content).not_to match(%r{"https":8501}) - } - end - - it 'use tls for incoming and outgoing' do - stub_gitlab_rb( - consul: { - enable: true, - use_tls: true, - tls_ca_file: '/fake/ca.crt.pem', - tls_certificate_file: '/fake/server.crt.pem', - tls_key_file: '/fake/server.key.pem', - https_port: 8501, - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) - expect(content).to match(%r{"cert_file":"/fake/server.crt.pem"}) - expect(content).to match(%r{"key_file":"/fake/server.key.pem"}) - expect(content).to match(%r{"verify_outgoing":true}) - expect(content).to match(%r{"verify_incoming":false}) - expect(content).to match(%r{"https":8501}) - } - end - - it 'use tls for incoming and outgoing and verify incoming' do - stub_gitlab_rb( - consul: { - enable: true, - use_tls: true, - tls_ca_file: '/fake/ca.crt.pem', - tls_certificate_file: '/fake/server.crt.pem', - tls_key_file: '/fake/server.key.pem', - tls_verify_client: true, - https_port: 8501 - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) - expect(content).to match(%r{"cert_file":"/fake/server.crt.pem"}) - expect(content).to match(%r{"key_file":"/fake/server.key.pem"}) - expect(content).to match(%r{"verify_outgoing":true}) - expect(content).to match(%r{"verify_incoming":true}) - expect(content).to match(%r{"https":8501}) - } - end - end - - context 'server mode' do - it 'use tls for incoming and outgoing and verify incoming by default' do - stub_gitlab_rb( - consul: { - enable: true, - configuration: { server: true }, - use_tls: true, - tls_ca_file: '/fake/ca.crt.pem', - tls_certificate_file: '/fake/server.crt.pem', - tls_key_file: '/fake/server.key.pem' - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) - expect(content).to match(%r{"cert_file":"/fake/server.crt.pem"}) - expect(content).to match(%r{"key_file":"/fake/server.key.pem"}) - expect(content).to match(%r{"verify_outgoing":true}) - expect(content).to match(%r{"verify_incoming":true}) - expect(content).to match(%r{"https":8501}) - } - end - - it 'use tls for incoming and outgoing without verifying incoming' do - stub_gitlab_rb( - consul: { - enable: true, - configuration: { server: true }, - use_tls: true, - tls_ca_file: '/fake/ca.crt.pem', - tls_certificate_file: '/fake/server.crt.pem', - tls_key_file: '/fake/server.key.pem', - tls_verify_client: false - } - ) - - expect(chef_run).to render_file(consul_conf).with_content { |content| - expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) - expect(content).to match(%r{"cert_file":"/fake/server.crt.pem"}) - expect(content).to match(%r{"key_file":"/fake/server.key.pem"}) - expect(content).to match(%r{"verify_outgoing":true}) - expect(content).to match(%r{"verify_incoming":false}) - expect(content).to match(%r{"https":8501}) - } - end - end - end - end -end diff --git a/spec/chef/consul/recipes/consul_watcher_spec.rb b/spec/chef/consul/recipes/consul_watcher_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2NvbnN1bC9yZWNpcGVzL2NvbnN1bF93YXRjaGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/consul/recipes/consul_watcher_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'consul::watchers' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - let(:watcher_conf) { '/var/opt/gitlab/consul/config.d/watcher_postgresql.json' } - let(:watcher_check) { '/var/opt/gitlab/consul/scripts/failover_pgbouncer' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - - stub_gitlab_rb( - consul: { - enable: true, - watchers: %w(postgresql) - } - ) - end - - it 'includes the watcher recipe' do - expect(chef_run).to include_recipe('consul::watchers') - end - - it 'creates the watcher config file' do - rendered = { - 'watches' => [ - { - 'type' => 'service', - 'service' => 'postgresql', - 'args' => [watcher_check] - } - ] - } - - expect(chef_run).to render_file(watcher_conf).with_content { |content| - expect(JSON.parse(content)).to eq(rendered) - } - end - - it 'creates the watcher handler file' do - expect(chef_run).to render_file(watcher_check) - end -end diff --git a/spec/chef/consul/recipes/disable_service_postgresql_spec.rb b/spec/chef/consul/recipes/disable_service_postgresql_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2NvbnN1bC9yZWNpcGVzL2Rpc2FibGVfc2VydmljZV9wb3N0Z3Jlc3FsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/consul/recipes/disable_service_postgresql_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'consul::disable_service_postgresql' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default', 'consul::disable_service_postgresql') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'default' do - before do - stub_gitlab_rb( - consul: { - enable: true, - services: [] - } - ) - end - - it 'deletes the service configuration file' do - expect(chef_run).to delete_file('/var/opt/gitlab/consul/config.d/postgresql_service.json') - end - end -end diff --git a/spec/chef/consul/recipes/enable_service_postgresq_spec.rb b/spec/chef/consul/recipes/enable_service_postgresq_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2NvbnN1bC9yZWNpcGVzL2VuYWJsZV9zZXJ2aWNlX3Bvc3RncmVzcV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/consul/recipes/enable_service_postgresq_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'consul::enable_service_postgresql' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'default' do - context 'on a regular cluster' do - before do - stub_gitlab_rb( - patroni: { - enable: true - }, - consul: { - enable: true, - services: %w(postgresql) - } - ) - end - - it 'renders the service configuration file' do - rendered = { - 'service' => { - 'name' => 'postgresql', - 'address' => '', - 'port' => 5432, - 'check' => { - 'id' => 'service:postgresql', - 'args' => ['/opt/gitlab/bin/gitlab-ctl', 'patroni', 'check-leader'], - 'interval' => '10s', - 'status' => 'failing' - } - } - } - expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/postgresql_service.json').with_content { |content| - expect(JSON.parse(content)).to eq(rendered) - } - end - end - - context 'on a standby cluster' do - before do - stub_gitlab_rb( - patroni: { - enable: true, - standby_cluster: { - enable: true - } - }, - consul: { - enable: true, - services: %w(postgresql) - } - ) - end - - it 'renders the service configuration file' do - rendered = { - 'service' => { - 'name' => 'postgresql', - 'address' => '', - 'port' => 5432, - 'check' => { - 'id' => 'service:postgresql', - 'args' => ['/opt/gitlab/bin/gitlab-ctl', 'patroni', 'check-standby-leader'], - 'interval' => '10s', - 'status' => 'failing' - } - } - } - expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/postgresql_service.json').with_content { |content| - expect(JSON.parse(content)).to eq(rendered) - } - end - end - end -end diff --git a/spec/chef/consul/resources/consul_service_spec.rb b/spec/chef/consul/resources/consul_service_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2NvbnN1bC9yZXNvdXJjZXMvY29uc3VsX3NlcnZpY2Vfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/consul/resources/consul_service_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'consul_service' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(consul_service)) - end - - context 'create' do - before do - runner.node.automatic['ipaddress'] = '10.1.1.1' - end - - context 'with service address and port properties' do - let(:chef_run) { runner.converge('test_consul::consul_service_address_port') } - - it 'creates the Consul service file' do - expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/node-exporter-service.json') - .with_content('{"service":{"name":"node-exporter","address":"10.1.1.1","port":1234}}') - end - - it 'notifies the Consul service to reload' do - expect(chef_run.file('/var/opt/gitlab/consul/config.d/node-exporter-service.json')) - .to notify 'execute[reload consul]' - end - end - - context 'with a socket property and no reload' do - let(:chef_run) { runner.converge('test_consul::consul_service_socket') } - - it 'creates the Consul service file' do - expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/node-exporter-service.json') - .with_content('{"service":{"name":"node-exporter","address":"10.1.1.1","port":5678}}') - end - - it 'does not notify the Consul service to reload' do - expect(chef_run.file('/var/opt/gitlab/consul/config.d/node-exporter-service.json')) - .not_to notify 'execute[reload consul]' - end - end - - context 'with symantic metadata' do - let(:chef_run) { runner.converge('test_consul::consul_service_meta') } - - it 'creates the Consul service file' do - expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/node-exporter-service.json') - .with_content('{"service":{"name":"node-exporter","address":"10.1.1.1","port":1234,"meta":{"some_key":"value"}}}') - end - end - end - - context 'delete' do - context 'default do reload' do - let(:chef_run) { runner.converge('test_consul::consul_service_delete') } - - it 'deletes the Consul service file' do - expect(chef_run).to delete_file('/var/opt/gitlab/consul/config.d/delete-me-service.json') - end - - it 'notifies the Consul service to reload' do - expect(chef_run.file('/var/opt/gitlab/consul/config.d/delete-me-service.json')) - .to notify 'execute[reload consul]' - end - end - - context 'do not reload' do - let(:chef_run) { runner.converge('test_consul::consul_service_delete_no_reload') } - - it 'does not notify the Consul service to reload' do - expect(chef_run.file('/var/opt/gitlab/consul/config.d/delete-no-reload-service.json')) - .not_to notify 'execute[reload consul]' - end - end - end -end diff --git a/spec/chef/cookbooks/consul/libraries/consul_helper_spec.rb b/spec/chef/cookbooks/consul/libraries/consul_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jb25zdWwvbGlicmFyaWVzL2NvbnN1bF9oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/consul/libraries/consul_helper_spec.rb @@ -0,0 +1,162 @@ +require 'chef_helper' + +RSpec.describe ConsulHelper do + let(:chef_run) { converge_config } + subject { described_class.new(chef_run.node) } + + describe '#running_version' do + let(:consul_api_output) { instance_double('response', code: '200', body: '{"Config": { "Version": "1.8.10" }}') } + + before do + # Un-doing the stub added in chef_helper + allow_any_instance_of(described_class).to receive(:running_version).and_call_original + allow(Gitlab).to receive(:[]).and_call_original + allow(subject).to receive(:get_api).and_yield(consul_api_output) + end + + context 'when consul is not running' do + it 'returns nil' do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('consul').and_return(false) + + expect(subject.running_version).to be_nil + end + end + + context 'when consul is running' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('consul').and_return(true) + end + + it 'parses version from consul api output properly' do + expect(subject.running_version).to eq('1.8.10') + end + end + end + + describe '#installed_version' do + let(:consul_cli_output) do + <<~MSG + Consul v1.7.8 + Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents) + MSG + end + + before do + # Un-doing the stub added in chef_helper + allow_any_instance_of(described_class).to receive(:installed_version).and_call_original + allow(Gitlab).to receive(:[]).and_call_original + allow(VersionHelper).to receive(:version).with(/consul version/).and_return(consul_cli_output) + end + + context 'when consul is not running' do + it 'returns nil' do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('consul').and_return(false) + + expect(subject.installed_version).to be_nil + end + end + + context 'when consul is running' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('consul').and_return(true) + end + + it 'parses consul output properly' do + expect(subject.installed_version).to eq('1.7.8') + end + end + end + + describe '#api_port' do + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + it 'falls back to default ports' do + stub_gitlab_rb( + consul: { + enable: true + } + ) + + expect(subject.api_port('http')).to eq(8500) + expect(subject.api_port('https')).to eq(8501) + expect(subject.configuration).not_to match(%r{"http":8500}) + expect(subject.configuration).not_to match(%r{"https":8501}) + end + + it 'uses the configured ports' do + stub_gitlab_rb( + consul: { + enable: true, + http_port: 18500, + https_port: 18501 + } + ) + + expect(subject.api_port('http')).to eq(18500) + expect(subject.api_port('https')).to eq(18501) + expect(subject.configuration).to match(%r{"http":18500}) + expect(subject.configuration).to match(%r{"https":18501}) + end + + it 'uses the ports from configuration override' do + stub_gitlab_rb( + consul: { + enable: true, + http_port: 18500, + https_port: 18501, + configuration: { + ports: { + http: 28500, + https: 28501, + } + } + } + ) + + expect(subject.api_port('http')).to eq(28500) + expect(subject.api_port('https')).to eq(28501) + expect(subject.configuration).to match(%r{"http":28500}) + expect(subject.configuration).to match(%r{"https":28501}) + end + end + + describe '#api_url' do + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + it 'uses http by default' do + stub_gitlab_rb( + consul: { + enable: true + } + ) + + expect(subject.api_url).to eq('http://localhost:8500') + end + + it 'uses https when tls is enabled' do + stub_gitlab_rb( + consul: { + enable: true, + use_tls: true + } + ) + + expect(subject.api_url).to eq('https://localhost:8501') + end + + it 'uses https when http port is disabled' do + stub_gitlab_rb( + consul: { + enable: true, + http_port: -1 + } + ) + + expect(subject.api_url).to eq('https://localhost:8501') + end + end +end diff --git a/spec/chef/cookbooks/consul/libraries/failover_helper_spec.rb b/spec/chef/cookbooks/consul/libraries/failover_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jb25zdWwvbGlicmFyaWVzL2ZhaWxvdmVyX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/consul/libraries/failover_helper_spec.rb @@ -0,0 +1,176 @@ +require 'chef_helper' + +require 'json' +require 'logger' + +NodeData = Struct.new('NodeData', :name, :address, :leader?) + +RSpec.describe FailoverHelper::SplitBrain do + let(:node_list) { %w(cats kittens) } + + context 'when passed a list of nodes' do + it 'sets primary_nodes to that list' do + brain = FailoverHelper::SplitBrain.new("Nobody here but us felines", node_list) + expect(brain.primary_nodes).to match_array(node_list) + end + end +end + +RSpec.describe FailoverHelper::LeaderFinder do + let(:total_nodes) { 3 } + let(:failover) { FailoverHelper::LeaderFinder.new("{}", service_data) } + let(:primary_node) { NodeData.new("example_primary_node", "10.0.0.42", true) } + let(:splitbrain_primary_node) { NodeData.new("example_splitbrain_node", "10.0.0.41", true) } + let(:follower_node_01) { NodeData.new("example_node_02", "10.0.0.43", false) } + let(:follower_node_02) { NodeData.new("example_node_03", "10.0.0.44", false) } + + def service_data + service_data = FailoverHelper::ServiceData.new + service_data.service_name = "example_service" + service_data.check_field = "Status" + service_data.leader_value = "passing" + + service_data + end + + def generate_parsed_data(unhealthy_primary: false, multiple_primaries: false) + parsed_data = [] + nodes = [primary_node, follower_node_01, follower_node_02] + + nodes.push(splitbrain_primary_node) if multiple_primaries + + nodes.each do |node| + if node.leader? + leader_status = "passing" + health = unhealthy_primary ? "failing" : "passing" + else + leader_status = "warning" + health = "passing" + end + + node_info = { + "Node" => { + "Node" => node.name, + "Address" => node.address + }, + "Checks" => [ + { + "Node" => node.name, + "CheckID" => "serfHealth", + "Status" => health + }, + { + "Node" => node.name, + "CheckID" => "example_service", + "Status" => leader_status + } + ] + } + + parsed_data.push(node_info) + end + + parsed_data + end + + def stub_healthy_primary_watcher_data + parsed_data = generate_parsed_data + + allow_any_instance_of(FailoverHelper::LeaderFinder).to receive(:parse).and_return(parsed_data) + end + + def stub_unhealthy_primary_watcher_data + parsed_data = generate_parsed_data(unhealthy_primary: true) + + allow_any_instance_of(FailoverHelper::LeaderFinder).to receive(:parse).and_return(parsed_data) + end + + def stub_multiple_primary_nodes_watcher_data + parsed_data = generate_parsed_data(multiple_primaries: true) + + allow_any_instance_of(FailoverHelper::LeaderFinder).to receive(:parse).and_return(parsed_data) + end + + describe "#parse" do + let(:bad_json) { "Nobody here but us errors!" } + + context 'when passed invalid JSON data' do + it 'throws a JSON::Parser error' do + expect { FailoverHelper::LeaderFinder.new(bad_json, service_data) }.to raise_error(JSON::ParserError) + end + end + end + + context 'when valid JSON is received from watch' do + context 'when there is more than one primary node found' do + before do + stub_multiple_primary_nodes_watcher_data + end + + describe "#primary_node" do + it 'throws a SplitBrain error' do + expect { failover.primary_node }.to raise_error(FailoverHelper::SplitBrain, "Split brain detected, multiple primary nodes found!") + end + end + end + + context 'when all nodes are healthy' do + before do + stub_healthy_primary_watcher_data + end + + describe "#healthy_nodes" do + it 'returns the correct number of healthy nodes' do + expect(failover.healthy_nodes.length).to eq(total_nodes) + end + end + + describe "#leader_nodes" do + context 'when there is one leader' do + it 'returns only one leader' do + expect(failover.leader_nodes.length).to eq(1) + end + + it 'returns the correct node' do + primary_node = failover.leader_nodes.first + expect(primary_node.name).to eq(primary_node.name) + end + end + end + + describe "#primary_node_address" do + context 'when the node name resolves via DNS' do + it 'returns the machine name' do + allow_any_instance_of(Resolv::DNS).to receive(:getaddress).and_return(true) + expect(failover.primary_node_address).to eq(primary_node.name) + end + end + + context 'when the node name does not resolve via DNS' do + it 'returns the machine IP address' do + allow_any_instance_of(Resolv::DNS).to receive(:getaddress).and_raise(Resolv::ResolvError) + expect(failover.primary_node_address).to eq(primary_node.address) + end + end + end + end + + context 'when the primary node is not healthy in a single cluster setup' do + before do + stub_unhealthy_primary_watcher_data + end + + describe "#healthy_nodes" do + it 'returns the correct number of healthy nodes' do + expect(failover.healthy_nodes.length).to eq(total_nodes - 1) + end + end + + describe "#leader_nodes" do + it 'throws a PrimaryMissing exception' do + expect { failover.leader_nodes }.to raise_error(FailoverHelper::PrimaryMissing, "No healthy primary node found.") + end + end + end + end +end diff --git a/spec/chef/cookbooks/consul/recipes/configure_services_spec.rb b/spec/chef/cookbooks/consul/recipes/configure_services_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jb25zdWwvcmVjaXBlcy9jb25maWd1cmVfc2VydmljZXNfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/consul/recipes/configure_services_spec.rb @@ -0,0 +1,39 @@ +require 'chef_helper' + +RSpec.describe 'consul::configure_services' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'enabling services' do + before do + stub_gitlab_rb( + consul: { + enable: true, + services: %w(postgresql) + } + ) + end + + it 'includes the enable_service recipe for the corresponding service' do + expect(chef_run).to include_recipe('consul::enable_service_postgresql') + end + end + + describe 'disabling services' do + before do + stub_gitlab_rb( + consul: { + enable: true, + services: [] + } + ) + end + + it 'includes the disable_service for all known services' do + expect(chef_run).to include_recipe('consul::disable_service_postgresql') + end + end +end diff --git a/spec/chef/cookbooks/consul/recipes/consul_spec.rb b/spec/chef/cookbooks/consul/recipes/consul_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jb25zdWwvcmVjaXBlcy9jb25zdWxfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/consul/recipes/consul_spec.rb @@ -0,0 +1,359 @@ +require 'chef_helper' + +RSpec.describe 'consul' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + let(:consul_conf) { '/var/opt/gitlab/consul/config.json' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'disabled by default' do + it 'includes the disable recipe' do + expect(chef_run).to include_recipe('consul::disable') + end + end + + describe 'consul::disable' do + it_behaves_like 'disabled runit service', 'consul' + end + + context 'when enabled' do + before do + stub_gitlab_rb( + consul: { + enable: true, + config_dir: '/fake/config.d', + data_dir: '/fake/data', + custom_config_dir: '/custom/dir' + } + ) + end + + it 'includes the enable recipe' do + expect(chef_run).to include_recipe('consul::enable') + end + + describe 'consul::enable' do + it_behaves_like 'enabled runit service', 'consul', 'gitlab-consul', 'gitlab-consul', 'gitlab-consul', 'gitlab-consul' + + it 'creates the consul system user and group' do + expect(chef_run).to create_account('Consul user and group').with(username: 'gitlab-consul', groupname: 'gitlab-consul') + end + + it 'includes the configure_services recipe' do + expect(chef_run).to include_recipe('consul::configure_services') + end + + it 'only enables the agent by default' do + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"server":false}) + } + end + + it 'does not include nil values in its configuration' do + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).not_to match(%r{"encryption":}) + } + end + + it 'does not include server default values in its configuration' do + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).not_to match(%r{"bootstrap_expect":3}) + } + end + + it 'creates the necessary directories' do + expect(chef_run).to create_directory('/fake/config.d') + expect(chef_run).to create_directory('/fake/data') + expect(chef_run).to create_directory('/var/log/gitlab/consul') + end + + it 'notifies other resources on configuration change' do + config_json = chef_run.file('/var/opt/gitlab/consul/config.json') + expect(config_json).to notify('execute[reload consul]').to(:run) + expect(config_json).to notify('ruby_block[consul config change]').to(:run) + end + + it 'renders run file with specified options' do + expect(chef_run).to render_file('/opt/gitlab/sv/consul/run').with_content(%r{-config-dir /fake/config.d}) + expect(chef_run).to render_file('/opt/gitlab/sv/consul/run').with_content(%r{-config-dir /custom/dir}) + expect(chef_run).to render_file('/opt/gitlab/sv/consul/run').with_content(%r{-data-dir /fake/data}) + end + end + + context 'with default options' do + it 'allows the user to specify node name' do + stub_gitlab_rb( + consul: { + enable: true + } + ) + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"datacenter":"gitlab_consul"}) + expect(content).to match(%r{"disable_update_check":true}) + expect(content).to match(%r{"enable_script_checks":true}) + expect(content).to match(%r{"node_name":"fauxhai.local"}) + expect(content).to match(%r{"rejoin_after_leave":true}) + expect(content).to match(%r{"server":false}) + } + end + end + + context 'with non-default options' do + before do + stub_gitlab_rb( + consul: { + enable: true, + node_name: 'fakenodename', + username: 'foo', + group: 'bar', + } + ) + end + + it 'allows the user to specify node name' do + expect(chef_run).to render_file(consul_conf).with_content('"node_name":"fakenodename"') + end + + it 'creates the consul system user and group' do + expect(chef_run).to create_account('Consul user and group').with(username: 'foo', groupname: 'bar') + end + + it_behaves_like 'enabled runit service', 'consul', 'foo', 'bar', 'foo', 'bar' + end + + context 'server enabled' do + before do + stub_gitlab_rb( + consul: { + enable: true, + configuration: { + server: true + } + } + ) + end + + it 'enables the server functionality' do + expect(chef_run.node['consul']['configuration']['server']).to eq true + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"server":true}) + expect(content).to match(%r{"bootstrap_expect":3}) + } + end + end + + describe 'pending restart check' do + context 'when running version is same as installed version' do + before do + allow_any_instance_of(ConsulHelper).to receive(:running_version).and_return('1.9.6') + allow_any_instance_of(ConsulHelper).to receive(:installed_version).and_return('1.9.6') + end + + it 'does not raise a warning' do + expect(chef_run).not_to run_ruby_block('warn pending consul restart') + end + end + + context 'when running version is different than installed version' do + before do + allow_any_instance_of(ConsulHelper).to receive(:running_version).and_return('1.6.4') + allow_any_instance_of(ConsulHelper).to receive(:installed_version).and_return('1.9.6') + end + + it 'raises a warning' do + expect(chef_run).to run_ruby_block('warn pending consul restart') + end + end + end + end + + describe 'encryption' do + it 'is not enabled by default' do + stub_gitlab_rb( + consul: { + enable: true, + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).not_to match(%r{"encrypt":}) + expect(content).not_to match(%r{"encrypt_verify_incoming":}) + expect(content).not_to match(%r{"encrypt_verify_outgoing":}) + } + end + + context 'new datacenter' do + it 'uses encryption key and falls back to defaults' do + stub_gitlab_rb( + consul: { + enable: true, + encryption_key: 'fake_key' + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"encrypt":"fake_key"}) + expect(content).not_to match(%r{"encrypt_verify_incoming":}) + expect(content).not_to match(%r{"encrypt_verify_outgoing":}) + } + end + end + + context 'existing datacenter' do + it 'uses encryption key and specified verification settings' do + stub_gitlab_rb( + consul: { + enable: true, + encryption_key: 'fake_key', + encryption_verify_incoming: false, + encryption_verify_outgoing: true, + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"encrypt":"fake_key"}) + expect(content).to match(%r{"encrypt_verify_incoming":false}) + expect(content).to match(%r{"encrypt_verify_outgoing":true}) + } + end + end + + describe 'secure configuration' do + context 'client mode' do + it 'use tls for outgoing' do + stub_gitlab_rb( + consul: { + enable: true, + use_tls: true, + tls_ca_file: '/fake/ca.crt.pem', + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) + expect(content).to match(%r{"verify_outgoing":true}) + expect(content).to match(%r{"verify_incoming":false}) + expect(content).not_to match(%r{"https":8501}) + expect(content).not_to match(%r{"cert_file":}) + expect(content).not_to match(%r{"key_file":}) + } + end + + it 'use tls client authentication' do + stub_gitlab_rb( + consul: { + enable: true, + use_tls: true, + tls_ca_file: '/fake/ca.crt.pem', + tls_certificate_file: '/fake/client.crt.pem', + tls_key_file: '/fake/client.key.pem' + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) + expect(content).to match(%r{"cert_file":"/fake/client.crt.pem"}) + expect(content).to match(%r{"key_file":"/fake/client.key.pem"}) + expect(content).to match(%r{"verify_outgoing":true}) + expect(content).to match(%r{"verify_incoming":false}) + expect(content).not_to match(%r{"https":8501}) + } + end + + it 'use tls for incoming and outgoing' do + stub_gitlab_rb( + consul: { + enable: true, + use_tls: true, + tls_ca_file: '/fake/ca.crt.pem', + tls_certificate_file: '/fake/server.crt.pem', + tls_key_file: '/fake/server.key.pem', + https_port: 8501, + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) + expect(content).to match(%r{"cert_file":"/fake/server.crt.pem"}) + expect(content).to match(%r{"key_file":"/fake/server.key.pem"}) + expect(content).to match(%r{"verify_outgoing":true}) + expect(content).to match(%r{"verify_incoming":false}) + expect(content).to match(%r{"https":8501}) + } + end + + it 'use tls for incoming and outgoing and verify incoming' do + stub_gitlab_rb( + consul: { + enable: true, + use_tls: true, + tls_ca_file: '/fake/ca.crt.pem', + tls_certificate_file: '/fake/server.crt.pem', + tls_key_file: '/fake/server.key.pem', + tls_verify_client: true, + https_port: 8501 + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) + expect(content).to match(%r{"cert_file":"/fake/server.crt.pem"}) + expect(content).to match(%r{"key_file":"/fake/server.key.pem"}) + expect(content).to match(%r{"verify_outgoing":true}) + expect(content).to match(%r{"verify_incoming":true}) + expect(content).to match(%r{"https":8501}) + } + end + end + + context 'server mode' do + it 'use tls for incoming and outgoing and verify incoming by default' do + stub_gitlab_rb( + consul: { + enable: true, + configuration: { server: true }, + use_tls: true, + tls_ca_file: '/fake/ca.crt.pem', + tls_certificate_file: '/fake/server.crt.pem', + tls_key_file: '/fake/server.key.pem' + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) + expect(content).to match(%r{"cert_file":"/fake/server.crt.pem"}) + expect(content).to match(%r{"key_file":"/fake/server.key.pem"}) + expect(content).to match(%r{"verify_outgoing":true}) + expect(content).to match(%r{"verify_incoming":true}) + expect(content).to match(%r{"https":8501}) + } + end + + it 'use tls for incoming and outgoing without verifying incoming' do + stub_gitlab_rb( + consul: { + enable: true, + configuration: { server: true }, + use_tls: true, + tls_ca_file: '/fake/ca.crt.pem', + tls_certificate_file: '/fake/server.crt.pem', + tls_key_file: '/fake/server.key.pem', + tls_verify_client: false + } + ) + + expect(chef_run).to render_file(consul_conf).with_content { |content| + expect(content).to match(%r{"ca_file":"/fake/ca.crt.pem"}) + expect(content).to match(%r{"cert_file":"/fake/server.crt.pem"}) + expect(content).to match(%r{"key_file":"/fake/server.key.pem"}) + expect(content).to match(%r{"verify_outgoing":true}) + expect(content).to match(%r{"verify_incoming":false}) + expect(content).to match(%r{"https":8501}) + } + end + end + end + end +end diff --git a/spec/chef/cookbooks/consul/recipes/consul_watcher_spec.rb b/spec/chef/cookbooks/consul/recipes/consul_watcher_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jb25zdWwvcmVjaXBlcy9jb25zdWxfd2F0Y2hlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/consul/recipes/consul_watcher_spec.rb @@ -0,0 +1,42 @@ +require 'chef_helper' + +RSpec.describe 'consul::watchers' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + let(:watcher_conf) { '/var/opt/gitlab/consul/config.d/watcher_postgresql.json' } + let(:watcher_check) { '/var/opt/gitlab/consul/scripts/failover_pgbouncer' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + + stub_gitlab_rb( + consul: { + enable: true, + watchers: %w(postgresql) + } + ) + end + + it 'includes the watcher recipe' do + expect(chef_run).to include_recipe('consul::watchers') + end + + it 'creates the watcher config file' do + rendered = { + 'watches' => [ + { + 'type' => 'service', + 'service' => 'postgresql', + 'args' => [watcher_check] + } + ] + } + + expect(chef_run).to render_file(watcher_conf).with_content { |content| + expect(JSON.parse(content)).to eq(rendered) + } + end + + it 'creates the watcher handler file' do + expect(chef_run).to render_file(watcher_check) + end +end diff --git a/spec/chef/cookbooks/consul/recipes/disable_service_postgresql_spec.rb b/spec/chef/cookbooks/consul/recipes/disable_service_postgresql_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jb25zdWwvcmVjaXBlcy9kaXNhYmxlX3NlcnZpY2VfcG9zdGdyZXNxbF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/consul/recipes/disable_service_postgresql_spec.rb @@ -0,0 +1,24 @@ +require 'chef_helper' + +RSpec.describe 'consul::disable_service_postgresql' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default', 'consul::disable_service_postgresql') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'default' do + before do + stub_gitlab_rb( + consul: { + enable: true, + services: [] + } + ) + end + + it 'deletes the service configuration file' do + expect(chef_run).to delete_file('/var/opt/gitlab/consul/config.d/postgresql_service.json') + end + end +end diff --git a/spec/chef/cookbooks/consul/recipes/enable_service_postgresq_spec.rb b/spec/chef/cookbooks/consul/recipes/enable_service_postgresq_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jb25zdWwvcmVjaXBlcy9lbmFibGVfc2VydmljZV9wb3N0Z3Jlc3Ffc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/consul/recipes/enable_service_postgresq_spec.rb @@ -0,0 +1,80 @@ +require 'chef_helper' + +RSpec.describe 'consul::enable_service_postgresql' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'default' do + context 'on a regular cluster' do + before do + stub_gitlab_rb( + patroni: { + enable: true + }, + consul: { + enable: true, + services: %w(postgresql) + } + ) + end + + it 'renders the service configuration file' do + rendered = { + 'service' => { + 'name' => 'postgresql', + 'address' => '', + 'port' => 5432, + 'check' => { + 'id' => 'service:postgresql', + 'args' => ['/opt/gitlab/bin/gitlab-ctl', 'patroni', 'check-leader'], + 'interval' => '10s', + 'status' => 'failing' + } + } + } + expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/postgresql_service.json').with_content { |content| + expect(JSON.parse(content)).to eq(rendered) + } + end + end + + context 'on a standby cluster' do + before do + stub_gitlab_rb( + patroni: { + enable: true, + standby_cluster: { + enable: true + } + }, + consul: { + enable: true, + services: %w(postgresql) + } + ) + end + + it 'renders the service configuration file' do + rendered = { + 'service' => { + 'name' => 'postgresql', + 'address' => '', + 'port' => 5432, + 'check' => { + 'id' => 'service:postgresql', + 'args' => ['/opt/gitlab/bin/gitlab-ctl', 'patroni', 'check-standby-leader'], + 'interval' => '10s', + 'status' => 'failing' + } + } + } + expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/postgresql_service.json').with_content { |content| + expect(JSON.parse(content)).to eq(rendered) + } + end + end + end +end diff --git a/spec/chef/cookbooks/consul/resources/consul_service_spec.rb b/spec/chef/cookbooks/consul/resources/consul_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jb25zdWwvcmVzb3VyY2VzL2NvbnN1bF9zZXJ2aWNlX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/consul/resources/consul_service_spec.rb @@ -0,0 +1,74 @@ +require 'chef_helper' + +RSpec.describe 'consul_service' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(consul_service)) + end + + context 'create' do + before do + runner.node.automatic['ipaddress'] = '10.1.1.1' + end + + context 'with service address and port properties' do + let(:chef_run) { runner.converge('test_consul::consul_service_address_port') } + + it 'creates the Consul service file' do + expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/node-exporter-service.json') + .with_content('{"service":{"name":"node-exporter","address":"10.1.1.1","port":1234}}') + end + + it 'notifies the Consul service to reload' do + expect(chef_run.file('/var/opt/gitlab/consul/config.d/node-exporter-service.json')) + .to notify 'execute[reload consul]' + end + end + + context 'with a socket property and no reload' do + let(:chef_run) { runner.converge('test_consul::consul_service_socket') } + + it 'creates the Consul service file' do + expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/node-exporter-service.json') + .with_content('{"service":{"name":"node-exporter","address":"10.1.1.1","port":5678}}') + end + + it 'does not notify the Consul service to reload' do + expect(chef_run.file('/var/opt/gitlab/consul/config.d/node-exporter-service.json')) + .not_to notify 'execute[reload consul]' + end + end + + context 'with symantic metadata' do + let(:chef_run) { runner.converge('test_consul::consul_service_meta') } + + it 'creates the Consul service file' do + expect(chef_run).to render_file('/var/opt/gitlab/consul/config.d/node-exporter-service.json') + .with_content('{"service":{"name":"node-exporter","address":"10.1.1.1","port":1234,"meta":{"some_key":"value"}}}') + end + end + end + + context 'delete' do + context 'default do reload' do + let(:chef_run) { runner.converge('test_consul::consul_service_delete') } + + it 'deletes the Consul service file' do + expect(chef_run).to delete_file('/var/opt/gitlab/consul/config.d/delete-me-service.json') + end + + it 'notifies the Consul service to reload' do + expect(chef_run.file('/var/opt/gitlab/consul/config.d/delete-me-service.json')) + .to notify 'execute[reload consul]' + end + end + + context 'do not reload' do + let(:chef_run) { runner.converge('test_consul::consul_service_delete_no_reload') } + + it 'does not notify the Consul service to reload' do + expect(chef_run.file('/var/opt/gitlab/consul/config.d/delete-no-reload-service.json')) + .not_to notify 'execute[reload consul]' + end + end + end +end diff --git a/spec/chef/cookbooks/crond/recipes/crond_enable_spec.rb b/spec/chef/cookbooks/crond/recipes/crond_enable_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jcm9uZC9yZWNpcGVzL2Nyb25kX2VuYWJsZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/crond/recipes/crond_enable_spec.rb @@ -0,0 +1,24 @@ +require 'chef_helper' + +RSpec.describe 'crond::enable' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('crond::enable') } + + it "should create a log directory" do + expect(chef_run).to create_directory("/var/log/gitlab/crond").with( + owner: "root" + ) + end + + it "should create a cron.d directory" do + expect(chef_run).to create_directory("/var/opt/gitlab/crond").with( + recursive: true, + owner: "root" + ) + end + + it "should pass correct options to runit_service" do + expect(chef_run).to render_file("/opt/gitlab/sv/crond/run").with_content(/--include=\/var\/opt\/gitlab\/crond/) + end + + it_behaves_like "enabled runit service", "crond", "root", "root" +end diff --git a/spec/chef/cookbooks/crond/resources/crond_job_spec.rb b/spec/chef/cookbooks/crond/resources/crond_job_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9jcm9uZC9yZXNvdXJjZXMvY3JvbmRfam9iX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/crond/resources/crond_job_spec.rb @@ -0,0 +1,69 @@ +require 'chef_helper' + +RSpec.describe 'crond_job' do + context 'crond enabled' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(crond_job)) do |node| + node.normal['crond']['cron_d'] = "prefix" + end + end + + context 'delete' do + let(:chef_run) { runner.converge("crond::enable", "test_crond::crond_job_delete") } + + it 'should delete the file' do + expect(chef_run).to delete_file("prefix/delete") + end + + it 'should notify the service' do + expect(chef_run.file("prefix/delete")) + .to notify("runit_service[crond]").to(:restart) + end + end + + context 'minimal' do + let(:chef_run) { runner.converge("crond::enable", "test_crond::crond_job_minimal") } + + it 'should set up the file' do + expect(chef_run).to create_file("prefix/minimal").with( + owner: "root", + content: "* * * * * rspec echo 'Hello world'\n" + ) + end + + it 'should notify the service' do + expect(chef_run.file("prefix/minimal")) + .to notify("runit_service[crond]").to(:restart) + end + end + end + + context 'crond disabled' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(crond_job)) do |node| + node.normal['crond']['cron_d'] = "prefix" + end + end + + context 'delete' do + let(:chef_run) { runner.converge("test_crond::crond_job_delete") } + + it 'should delete the file' do + expect(chef_run).to delete_file("prefix/delete") + end + + it 'should not try and notify the service' do + expect(chef_run.file("prefix/delete")) + .not_to notify("runit_service[crond]") + end + end + + context 'minimal' do + let(:chef_run) { runner.converge("test_crond::crond_job_minimal") } + + it 'should not set up the file' do + expect(chef_run).not_to create_file("prefix/minimal") + end + end + end +end diff --git a/spec/chef/cookbooks/gitaly/libraries/gitaly_spec.rb b/spec/chef/cookbooks/gitaly/libraries/gitaly_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRhbHkvbGlicmFyaWVzL2dpdGFseV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitaly/libraries/gitaly_spec.rb @@ -0,0 +1,19 @@ +require 'chef_helper' + +RSpec.describe Gitaly do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + before { allow(Gitlab).to receive(:[]).and_call_original } + + describe 'by default' do + it 'provides settings needed for gitaly to run' do + expect(chef_run.node['gitaly']['env']).to include( + 'HOME' => '/var/opt/gitlab', + 'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin' + ) + end + + it 'does not include known settings in the environment' do + expect(chef_run.node['gitaly']['env']).not_to include('GITALY_ENABLE') + end + end +end diff --git a/spec/chef/cookbooks/gitaly/recipes/gitaly_spec.rb b/spec/chef/cookbooks/gitaly/recipes/gitaly_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRhbHkvcmVjaXBlcy9naXRhbHlfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitaly/recipes/gitaly_spec.rb @@ -0,0 +1,890 @@ +require 'chef_helper' + +RSpec.describe 'gitaly' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:config_path) { '/var/opt/gitlab/gitaly/config.toml' } + let(:gitaly_config) { chef_run.template(config_path) } + let(:internal_socket_dir) { '/var/opt/gitlab/gitaly/user_defined/internal_sockets' } + let(:socket_path) { '/tmp/gitaly.socket' } + let(:listen_addr) { 'localhost:7777' } + let(:tls_listen_addr) { 'localhost:8888' } + let(:certificate_path) { '/path/to/cert.pem' } + let(:key_path) { '/path/to/key.pem' } + let(:prometheus_listen_addr) { 'localhost:9000' } + let(:logging_level) { 'warn' } + let(:logging_format) { 'default' } + let(:logging_sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project' } + let(:logging_ruby_sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project-ruby' } + let(:logging_sentry_environment) { 'production' } + let(:prometheus_grpc_latency_buckets) do + '[0.001, 0.005, 0.025, 0.1, 0.5, 1.0, 10.0, 30.0, 60.0, 300.0, 1500.0]' + end + let(:auth_token) { '123secret456' } + let(:auth_transitioning) { true } + let(:ruby_max_rss) { 1000000 } + let(:graceful_restart_timeout) { '20m' } + let(:ruby_graceful_restart_timeout) { '30m' } + let(:ruby_restart_delay) { '10m' } + let(:ruby_num_workers) { 5 } + let(:ruby_rugged_git_config_search_path) { '/path/to/opt/gitlab/embedded/etc' } + let(:git_catfile_cache_size) { 50 } + let(:git_bin_path) { '/path/to/usr/bin/git' } + let(:use_bundled_git) { true } + let(:open_files_ulimit) { 10000 } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + 'TZ' => ':/etc/localtime', + 'HOME' => '/var/opt/gitlab', + 'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin', + 'ICU_DATA' => '/opt/gitlab/embedded/share/icu/current', + 'PYTHONPATH' => '/opt/gitlab/embedded/lib/python3.9/site-packages', + 'WRAPPER_JSON_LOGGING' => 'true', + "GITALY_PID_FILE" => '/var/opt/gitlab/gitaly/gitaly.pid', + } + end + + let(:gitlab_url) { 'http://localhost:3000' } + let(:workhorse_addr) { 'localhost:4000' } + let(:gitaly_custom_hooks_dir) { '/path/to/gitaly/custom/hooks' } + let(:gitlab_shell_custom_hooks_dir) { '/path/to/gitlab-shell/custom/hooks' } + let(:user) { 'user123' } + let(:password) { 'password321' } + let(:ca_file) { '/path/to/ca_file' } + let(:ca_path) { '/path/to/ca_path' } + let(:self_signed_cert) { true } + let(:read_timeout) { 123 } + let(:daily_maintenance_start_hour) { 21 } + let(:daily_maintenance_start_minute) { 9 } + let(:daily_maintenance_duration) { '45m' } + let(:daily_maintenance_storages) { ["default"] } + let(:daily_maintenance_disabled) { false } + let(:cgroups_count) { 10 } + let(:cgroups_mountpoint) { '/sys/fs/cgroup' } + let(:cgroups_hierarchy_root) { 'gitaly' } + let(:cgroups_memory_enabled) { true } + let(:cgroups_memory_limit) { 1048576 } + let(:cgroups_cpu_enabled) { true } + let(:cgroups_cpu_shares) { 512 } + let(:pack_objects_cache_enabled) { true } + let(:pack_objects_cache_dir) { '/pack-objects-cache' } + let(:pack_objects_cache_max_age) { '10m' } + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'by default' do + it_behaves_like "enabled runit service", "gitaly", "root", "root" + + it 'creates expected directories with correct permissions' do + expect(chef_run).to create_directory('/var/opt/gitlab/gitaly').with(user: 'git', mode: '0700') + expect(chef_run).to create_directory('/var/log/gitlab/gitaly').with(user: 'git', mode: '0700') + end + + it 'creates a default VERSION file and restarts service' do + expect(chef_run).to create_version_file('Create version file for Gitaly').with( + version_file_path: '/var/opt/gitlab/gitaly/VERSION', + version_check_cmd: "/opt/gitlab/embedded/bin/ruby -rdigest/sha2 -e 'puts %(sha256:) + Digest::SHA256.file(%(/opt/gitlab/embedded/bin/gitaly)).hexdigest'" + ) + + expect(chef_run.version_file('Create version file for Gitaly')).to notify('runit_service[gitaly]').to(:hup) + end + + it 'creates a default RUBY_VERSION file and restarts service' do + expect(chef_run).to create_version_file('Create Ruby version file for Gitaly').with( + version_file_path: '/var/opt/gitlab/gitaly/RUBY_VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/ruby --version' + ) + + expect(chef_run.version_file('Create Ruby version file for Gitaly')).to notify('runit_service[gitaly]').to(:hup) + end + + it 'populates gitaly config.toml with defaults' do + expect(chef_run).to render_file(config_path).with_content { |content| + expect(content).to include("socket_path = '/var/opt/gitlab/gitaly/gitaly.socket'") + expect(content).to include("internal_socket_dir = '/var/opt/gitlab/gitaly/internal_sockets'") + expect(content).to include("bin_dir = '/opt/gitlab/embedded/bin'") + expect(content).to include(%(rugged_git_config_search_path = "/opt/gitlab/embedded/etc")) + } + + expect(chef_run).not_to render_file(config_path) + .with_content("listen_addr = '#{listen_addr}'") + expect(chef_run).not_to render_file(config_path) + .with_content("tls_listen_addr =") + expect(chef_run).not_to render_file(config_path) + .with_content("certificate_path =") + expect(chef_run).not_to render_file(config_path) + .with_content("key_path =") + expect(chef_run).not_to render_file(config_path) + .with_content("prometheus_listen_addr = '#{prometheus_listen_addr}'") + expect(chef_run).not_to render_file(config_path) + .with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+sentry_dsn = '#{logging_sentry_dsn}'}) + expect(chef_run).not_to render_file(config_path) + .with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+ruby_sentry_dsn = '#{logging_ruby_sentry_dsn}'}) + expect(chef_run).not_to render_file(config_path) + .with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+sentry_environment = '#{logging_sentry_environment}'}) + expect(chef_run).not_to render_file(config_path) + .with_content(%r{\[prometheus\]\s+grpc_latency_buckets = #{Regexp.escape(prometheus_grpc_latency_buckets)}}) + expect(chef_run).not_to render_file(config_path) + .with_content(%r{\[auth\]\s+token = }) + expect(chef_run).not_to render_file(config_path) + .with_content('transitioning =') + expect(chef_run).not_to render_file(config_path) + .with_content('max_rss =') + expect(chef_run).not_to render_file(config_path) + .with_content('graceful_restart_timeout =') + expect(chef_run).not_to render_file(config_path) + .with_content('restart_delay =') + expect(chef_run).not_to render_file(config_path) + .with_content('num_workers =') + expect(chef_run).not_to render_file(config_path) + .with_content(%r{\[logging\]\s+level}) + expect(chef_run).not_to render_file(config_path) + .with_content(%r{catfile_cache_size}) + expect(chef_run).not_to render_file(config_path) + .with_content('[pack_objects_cache]') + end + + it 'populates gitaly config.toml with default git binary path' do + expect(chef_run).to render_file(config_path) + .with_content("bin_path = '/opt/gitlab/embedded/bin/git'") + end + + it 'populates gitaly config.toml with default storages' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/var/opt/gitlab/git-data/repositories'}) + end + + it 'renders the runit run script with defaults' do + expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run') + .with_content(%r{ulimit -n 15000}) + end + + it 'does not append timestamp in logs if logging format is json' do + expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/log/run') + .with_content(/exec svlogd \/var\/log\/gitlab\/gitaly/) + end + + it 'populates gitaly config.toml with gitlab-shell values' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[gitlab-shell\]\s+dir = "/opt/gitlab/embedded/service/gitlab-shell"}) + end + + it 'populates gitaly config.toml with gitlab-workhorse socket' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket'\s+relative_url_root = ''}) + end + end + + context 'with user settings' do + before do + stub_gitlab_rb( + gitaly: { + socket_path: socket_path, + internal_socket_dir: internal_socket_dir, + listen_addr: listen_addr, + tls_listen_addr: tls_listen_addr, + certificate_path: certificate_path, + key_path: key_path, + prometheus_listen_addr: prometheus_listen_addr, + logging_level: logging_level, + logging_format: logging_format, + logging_sentry_dsn: logging_sentry_dsn, + logging_ruby_sentry_dsn: logging_ruby_sentry_dsn, + logging_sentry_environment: logging_sentry_environment, + prometheus_grpc_latency_buckets: prometheus_grpc_latency_buckets, + auth_token: auth_token, + auth_transitioning: auth_transitioning, + graceful_restart_timeout: graceful_restart_timeout, + ruby_max_rss: ruby_max_rss, + ruby_graceful_restart_timeout: ruby_graceful_restart_timeout, + ruby_restart_delay: ruby_restart_delay, + ruby_num_workers: ruby_num_workers, + git_catfile_cache_size: git_catfile_cache_size, + git_bin_path: git_bin_path, + use_bundled_git: true, + open_files_ulimit: open_files_ulimit, + ruby_rugged_git_config_search_path: ruby_rugged_git_config_search_path, + daily_maintenance_start_hour: daily_maintenance_start_hour, + daily_maintenance_start_minute: daily_maintenance_start_minute, + daily_maintenance_duration: daily_maintenance_duration, + daily_maintenance_storages: daily_maintenance_storages, + daily_maintenance_disabled: daily_maintenance_disabled, + cgroups_count: cgroups_count, + cgroups_mountpoint: cgroups_mountpoint, + cgroups_hierarchy_root: cgroups_hierarchy_root, + cgroups_memory_enabled: cgroups_memory_enabled, + cgroups_memory_limit: cgroups_memory_limit, + cgroups_cpu_enabled: cgroups_cpu_enabled, + cgroups_cpu_shares: cgroups_cpu_shares, + pack_objects_cache_enabled: pack_objects_cache_enabled, + pack_objects_cache_dir: pack_objects_cache_dir, + pack_objects_cache_max_age: pack_objects_cache_max_age, + custom_hooks_dir: gitaly_custom_hooks_dir + }, + gitlab_rails: { + internal_api_url: gitlab_url + }, + gitlab_shell: { + http_settings: { + read_timeout: read_timeout, + user: user, + password: password, + ca_file: ca_file, + ca_path: ca_path, + self_signed_cert: self_signed_cert + } + }, + gitlab_workhorse: { + listen_network: 'tcp', + listen_addr: workhorse_addr, + }, + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like "enabled runit service", "gitaly", "root", "root" + + it 'creates expected directories with correct permissions' do + expect(chef_run).to create_directory(internal_socket_dir).with(user: 'foo', mode: '0700') + end + + it 'populates gitaly config.toml with custom values' do + expect(chef_run).to render_file(config_path) + .with_content("socket_path = '#{socket_path}'") + expect(chef_run).to render_file(config_path) + .with_content("internal_socket_dir = '#{internal_socket_dir}'") + expect(chef_run).to render_file(config_path) + .with_content("bin_dir = '/opt/gitlab/embedded/bin'") + expect(chef_run).to render_file(config_path) + .with_content("listen_addr = 'localhost:7777'") + expect(chef_run).to render_file(config_path) + .with_content("graceful_restart_timeout = '#{graceful_restart_timeout}'") + expect(chef_run).to render_file(config_path) + .with_content("bin_path = '#{git_bin_path}'") + expect(chef_run).to render_file(config_path) + .with_content("use_bundled_binaries = true") + + gitaly_logging_section = Regexp.new([ + %r{\[logging\]}, + %r{level = '#{logging_level}'}, + %r{format = '#{logging_format}'}, + %r{sentry_dsn = '#{logging_sentry_dsn}'}, + %r{ruby_sentry_dsn = '#{logging_ruby_sentry_dsn}'}, + %r{sentry_environment = '#{logging_sentry_environment}'}, + ].map(&:to_s).join('\s+')) + + gitaly_ruby_section = Regexp.new([ + %r{\[gitaly-ruby\]}, + %r{dir = "/opt/gitlab/embedded/service/gitaly-ruby"}, + %r{max_rss = #{ruby_max_rss}}, + %r{graceful_restart_timeout = '#{Regexp.escape(ruby_graceful_restart_timeout)}'}, + %r{restart_delay = '#{Regexp.escape(ruby_restart_delay)}'}, + %r{num_workers = #{ruby_num_workers}}, + ].map(&:to_s).join('\s+')) + + gitlab_shell_section = Regexp.new([ + %r{\[gitlab-shell\]}, + %r{dir = "/opt/gitlab/embedded/service/gitlab-shell"}, + ].map(&:to_s).join('\s+')) + + gitlab_section = Regexp.new([ + %r{\[gitlab\]}, + %r{url = '#{Regexp.escape(gitlab_url)}'}, + %r{\[gitlab.http-settings\]}, + %r{read_timeout = #{read_timeout}}, + %r{user = '#{Regexp.escape(user)}'}, + %r{password = '#{Regexp.escape(password)}'}, + %r{ca_file = '#{Regexp.escape(ca_file)}'}, + %r{ca_path = '#{Regexp.escape(ca_path)}'}, + %r{self_signed_cert = #{self_signed_cert}}, + ].map(&:to_s).join('\s+')) + + hooks_section = Regexp.new([ + %r{\[hooks\]}, + %r{custom_hooks_dir = '#{Regexp.escape(gitaly_custom_hooks_dir)}'}, + ].map(&:to_s).join('\s+')) + + maintenance_section = Regexp.new([ + %r{\[daily_maintenance\]}, + %r{start_hour = #{daily_maintenance_start_hour}}, + %r{start_minute = #{daily_maintenance_start_minute}}, + %r{duration = '#{daily_maintenance_duration}'}, + %r{storages = #{Regexp.escape(daily_maintenance_storages.to_s)}}, + ].map(&:to_s).join('\s+')) + + cgroups_section = Regexp.new([ + %r{\[cgroups\]}, + %r{count = #{cgroups_count}}, + %r{mountpoint = '#{cgroups_mountpoint}'}, + %r{hierarchy_root = '#{cgroups_hierarchy_root}'}, + ].map(&:to_s).join('\s+')) + + cgroups_memory_section = Regexp.new([ + %r{\[cgroups\.memory\]}, + %r{enabled = #{cgroups_memory_enabled}}, + %r{limit = #{cgroups_memory_limit}}, + ].map(&:to_s).join('\s+')) + + cgroups_cpu_section = Regexp.new([ + %r{\[cgroups\.cpu\]}, + %r{enabled = #{cgroups_cpu_enabled}}, + %r{shares = #{cgroups_cpu_shares}}, + ].map(&:to_s).join('\s+')) + + pack_objects_cache_section = Regexp.new([ + %r{\[pack_objects_cache\]\nenabled = true}, + %r{dir = '#{pack_objects_cache_dir}'}, + %r{max_age = '#{pack_objects_cache_max_age}'}, + ].map(&:to_s).join('\s+')) + + expect(chef_run).to render_file(config_path).with_content { |content| + expect(content).to include("tls_listen_addr = 'localhost:8888'") + expect(content).to include("certificate_path = '/path/to/cert.pem'") + expect(content).to include("key_path = '/path/to/key.pem'") + expect(content).to include(%(rugged_git_config_search_path = "#{ruby_rugged_git_config_search_path}")) + expect(content).to include("prometheus_listen_addr = 'localhost:9000'") + expect(content).to match(gitaly_logging_section) + expect(content).to match(%r{\[prometheus\]\s+grpc_latency_buckets = #{Regexp.escape(prometheus_grpc_latency_buckets)}}) + expect(content).to match(%r{\[auth\]\s+token = '#{Regexp.escape(auth_token)}'\s+transitioning = true}) + expect(content).to match(gitaly_ruby_section) + expect(content).to match(%r{\[git\]\s+catfile_cache_size = 50}) + expect(content).to match(gitlab_shell_section) + expect(content).to match(gitlab_section) + expect(content).to match(hooks_section) + expect(content).to match(maintenance_section) + expect(content).to match(cgroups_section) + expect(content).to match(cgroups_memory_section) + expect(content).to match(cgroups_cpu_section) + expect(content).to match(pack_objects_cache_section) + } + end + + it 'populates gitaly config.toml with custom deprecated values' do + stub_gitlab_rb( + gitlab_shell: { + custom_hooks_dir: gitlab_shell_custom_hooks_dir + } + ) + + hooks_section = Regexp.new([ + %r{\[hooks\]}, + %r{custom_hooks_dir = '#{Regexp.escape(gitlab_shell_custom_hooks_dir)}'}, + ].map(&:to_s).join('\s+')) + + expect(chef_run).to render_file(config_path).with_content { |content| + expect(content).to match(hooks_section) + } + end + + it 'renders the runit run script with custom values' do + expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run') + .with_content(%r{ulimit -n #{open_files_ulimit}}) + end + + it 'renders the runit run script with cgroup root creation' do + expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run').with_content { |content| + expect(content).to match(%r{mkdir -m 0700 -p #{cgroups_mountpoint}/memory/#{cgroups_hierarchy_root}}) + expect(content).to match(%r{mkdir -m 0700 -p #{cgroups_mountpoint}/cpu/#{cgroups_hierarchy_root}}) + expect(content).to match(%r{chown foo:bar #{cgroups_mountpoint}/memory/#{cgroups_hierarchy_root}}) + expect(content).to match(%r{chown foo:bar #{cgroups_mountpoint}/cpu/#{cgroups_hierarchy_root}}) + } + end + + it 'populates sv related log files' do + expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/gitaly/) + end + + context 'when using multiple maintenance storage entries' do + let(:daily_maintenance_storages) { %w(storage0 storage1 storage2) } + + it 'renders daily_maintenance with multiple storage entries' do + expect(chef_run).to render_file(config_path).with_content { |content| + expect(content).to include("storages = #{daily_maintenance_storages}") + } + end + end + + context 'when maintenance is disabled' do + let(:daily_maintenance_disabled) { true } + + it 'renders daily_maintenance with disabled set to true' do + expect(chef_run).to render_file(config_path).with_content { |content| + expect(content).to include("[daily_maintenance]\ndisabled = true\n\n") + } + end + end + + context 'when using gitaly storage configuration' do + before do + stub_gitlab_rb( + gitaly: { + storage: [ + { + 'name' => 'default', + 'path' => '/tmp/path-1' + }, + { + 'name' => 'nfs1', + 'path' => '/mnt/nfs1' + } + ] + } + ) + end + + it 'populates gitaly config.toml with custom storages' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/tmp/path-1'}) + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[storage\]\]\s+name = 'nfs1'\s+path = '/mnt/nfs1'}) + end + end + + context 'when using git_data_dirs storage configuration' do + context 'using local gitaly' do + before do + stub_gitlab_rb( + { + git_data_dirs: + { + 'default' => { 'path' => '/tmp/default/git-data' }, + 'nfs1' => { 'path' => '/mnt/nfs1' } + } + } + ) + end + + it 'populates gitaly config.toml with custom storages' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/tmp/default/git-data/repositories'}) + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[storage\]\]\s+name = 'nfs1'\s+path = '/mnt/nfs1/repositories'}) + expect(chef_run).not_to render_file(config_path) + .with_content('gitaly_address: "/var/opt/gitlab/gitaly/gitaly.socket"') + end + end + + context 'using external gitaly' do + before do + stub_gitlab_rb( + { + git_data_dirs: + { + 'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' }, + } + } + ) + end + + it 'populates gitaly config.toml with custom storages' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/var/opt/gitlab/git-data/repositories'}) + expect(chef_run).not_to render_file(config_path) + .with_content('gitaly_address: "tcp://gitaly.internal:8075"') + end + end + end + end + + context 'when gitaly is disabled' do + before do + stub_gitlab_rb(gitaly: { enable: false }) + end + + it_behaves_like "disabled runit service", "gitaly" + + it 'does not create the gitaly directories' do + expect(chef_run).not_to create_directory('/var/opt/gitlab/gitaly') + expect(chef_run).not_to create_directory('/var/log/gitlab/gitaly') + expect(chef_run).not_to create_directory('/opt/gitlab/etc/gitaly') + expect(chef_run).not_to create_file('/var/opt/gitlab/gitaly/config.toml') + end + end + + context 'when using concurrency configuration' do + before do + stub_gitlab_rb( + { + gitaly: { + concurrency: [ + { + 'rpc' => "/gitaly.SmartHTTPService/PostReceivePack", + 'max_per_repo' => 20 + }, { + 'rpc' => "/gitaly.SSHService/SSHUploadPack", + 'max_per_repo' => 5 + } + ] + } + } + ) + end + + it 'populates gitaly config.toml with custom concurrency configurations' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SmartHTTPService/PostReceivePack"\s+max_per_repo = 20}) + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SSHService/SSHUploadPack"\s+max_per_repo = 5}) + end + end + + shared_examples 'empty concurrency configuration' do + it 'does not generate a gitaly concurrency configuration' do + expect(chef_run).not_to render_file(config_path) + .with_content(%r{\[\[concurrency\]\]}) + end + end + + context 'when not using concurrency configuration' do + context 'when concurrency configuration is not set' do + before do + stub_gitlab_rb( + { + gitaly: { + } + } + ) + end + + it_behaves_like 'empty concurrency configuration' + end + + context 'when concurrency configuration is empty' do + before do + stub_gitlab_rb( + { + gitaly: { + concurrency: [] + } + } + ) + end + + it_behaves_like 'empty concurrency configuration' + end + + context 'when max_queue_size and max_queue_wait are empty' do + before do + stub_gitlab_rb( + { + gitaly: { + concurrency: [ + { + 'rpc' => "/gitaly.SmartHTTPService/PostReceivePack", + 'max_per_repo' => 20, + }, { + 'rpc' => "/gitaly.SSHService/SSHUploadPack", + 'max_per_repo' => 5, + } + ] + } + } + ) + end + + it 'populates gitaly config.toml without max_queue_size and max_queue_wait' do + expect(chef_run).to render_file(config_path).with_content { |content| + expect(content).not_to include("max_queue_size") + expect(content).not_to include("max_queue_wait") + } + end + end + + context 'when max_per_repo is empty' do + before do + stub_gitlab_rb( + { + gitaly: { + concurrency: [ + { + 'rpc' => "/gitaly.SmartHTTPService/PostReceivePack", + 'max_queue_size' => '10s' + }, { + 'rpc' => "/gitaly.SSHService/SSHUploadPack", + 'max_queue_size' => '10s' + } + ] + } + } + ) + end + + it 'populates gitaly config.toml without max_per_repo' do + expect(chef_run).to render_file(config_path).with_content { |content| + expect(content).not_to include("max_per_repo") + } + end + end + + context 'when max_queue_wait is set' do + before do + stub_gitlab_rb( + { + gitaly: { + concurrency: [ + { + 'rpc' => "/gitaly.SmartHTTPService/PostReceivePack", + 'max_queue_wait' => "10s", + } + ] + } + } + ) + end + + it 'populates gitaly config.toml with quoted max_queue_wait' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SmartHTTPService/PostReceivePack"\s+max_queue_wait = "10s"}) + end + end + end + + context 'populates default env variables' do + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitaly/env').with_variables(default_vars) + end + end + + context 'computes env variables based on other values' do + before do + stub_gitlab_rb( + { + user: { + home: "/my/random/path" + } + } + ) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitaly/env').with_variables( + default_vars.merge( + { + 'HOME' => '/my/random/path', + } + ) + ) + end + end + + context 'with a non-default workhorse unix socket' do + context 'with only a listen address set' do + before do + stub_gitlab_rb(gitlab_workhorse: { listen_addr: '/fake/workhorse/socket' }) + end + + it 'create config file with provided values' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Ffake%2Fworkhorse%2Fsocket'\s+relative_url_root = ''}) + end + end + + context 'with only a socket directory set' do + before do + stub_gitlab_rb(gitlab_workhorse: { sockets_directory: '/fake/workhorse/sockets' }) + end + + it 'create config file with provided values' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Ffake%2Fworkhorse%2Fsockets%2Fsocket'\s+relative_url_root = ''}) + end + end + + context 'with a listen_address and a sockets_directory set' do + before do + stub_gitlab_rb(gitlab_workhorse: { listen_addr: '/sockets/in/the/wind', sockets_directory: '/sockets/in/the' }) + end + + it 'create config file with provided values' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fsockets%2Fin%2Fthe%2Fwind'\s+relative_url_root = ''}) + end + end + end + + context 'with a tcp workhorse listener' do + before do + stub_gitlab_rb( + external_url: 'http://example.com/gitlab', + gitlab_workhorse: { + listen_network: 'tcp', + listen_addr: 'localhost:1234' + } + ) + end + + it 'create config file with only the URL set' do + expect(chef_run).to render_file(config_path).with_content { |content| + expect(content).to match(%r{\[gitlab\]\s+url = 'http://localhost:1234/gitlab'}) + expect(content).not_to match(/relative_url_root/) + } + end + end + + context 'with relative path in external_url' do + before do + stub_gitlab_rb(external_url: 'http://example.com/gitlab') + end + + it 'create config file with the relative_url_root set' do + expect(chef_run).to render_file(config_path) + .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket'\s+relative_url_root = '/gitlab'}) + end + end + + context 'with cgroups mountpoint and hierarchy_root' do + before do + stub_gitlab_rb( + gitaly: { + cgroups_mountpoint: '/sys/fs/cgroup', + cgroups_hierarchy_root: 'gitaly' + } + ) + end + end + + include_examples "consul service discovery", "gitaly", "gitaly" +end + +RSpec.describe 'gitaly::git_data_dirs' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when user has not specified git_data_dir' do + it 'defaults to correct path' do + expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']) + .to eql('default' => { 'path' => '/var/opt/gitlab/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }) + end + end + + context 'when gitaly is set to use a listen_addr' do + before do + stub_gitlab_rb(git_data_dirs: { + 'default' => { + 'path' => '/tmp/user/git-data' + } + }, gitaly: { + socket_path: '', + listen_addr: 'localhost:8123' + }) + end + + it 'correctly sets the repository storage directories' do + expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']) + .to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tcp://localhost:8123' }) + end + end + + context 'when gitaly is set to use a tls_listen_addr' do + before do + stub_gitlab_rb(git_data_dirs: { + 'default' => { + 'path' => '/tmp/user/git-data' + } + }, gitaly: { + socket_path: '', tls_listen_addr: 'localhost:8123' + }) + end + + it 'correctly sets the repository storage directories' do + expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']) + .to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tls://localhost:8123' }) + end + end + + context 'when both tls and socket' do + before do + stub_gitlab_rb(git_data_dirs: { + 'default' => { + 'path' => '/tmp/user/git-data' + } + }, gitaly: { + socket_path: '/some/socket/path.socket', tls_listen_addr: 'localhost:8123' + }) + end + + it 'TlS should take precedence' do + expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']) + .to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tls://localhost:8123' }) + end + end + + context 'when git_data_dirs is set to multiple directories' do + before do + stub_gitlab_rb({ + git_data_dirs: { + 'default' => { 'path' => '/tmp/default/git-data' }, + 'overflow' => { 'path' => '/tmp/other/git-overflow-data' } + } + }) + end + + it 'correctly sets the repository storage directories' do + expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ + 'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }, + 'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' } + }) + end + end + + context 'when git_data_dirs is set to multiple directories with different gitaly addresses' do + before do + stub_gitlab_rb({ + git_data_dirs: { + 'default' => { 'path' => '/tmp/default/git-data' }, + 'overflow' => { 'path' => '/tmp/other/git-overflow-data', 'gitaly_address' => 'tcp://localhost:8123', 'gitaly_token' => '123secret456gitaly' } + } + }) + end + + it 'correctly sets the repository storage directories' do + expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ + 'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }, + 'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'tcp://localhost:8123', 'gitaly_token' => '123secret456gitaly' } + }) + end + end + + context 'when path not defined in git_data_dirs' do + before do + stub_gitlab_rb( + { + git_data_dirs: + { + 'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' }, + } + } + ) + end + + it 'correctly sets the repository storage directories' do + expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ 'default' => { 'path' => '/var/opt/gitlab/git-data/repositories', 'gitaly_address' => 'tcp://gitaly.internal:8075' } }) + end + end + + context 'when git_data_dirs is set with symbol keys rather than string keys' do + before do + with_symbol_keys = { + default: { path: '/tmp/default/git-data' }, + overflow: { path: '/tmp/other/git-overflow-data' } + } + + allow(Gitlab).to receive(:[]).with('git_data_dirs').and_return(with_symbol_keys) + end + + it 'correctly sets the repository storage directories' do + expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ + 'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }, + 'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' } + }) + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/libraries/gitlab_geo_helper_spec.rb b/spec/chef/cookbooks/gitlab-ee/libraries/gitlab_geo_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvbGlicmFyaWVzL2dpdGxhYl9nZW9faGVscGVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/libraries/gitlab_geo_helper_spec.rb @@ -0,0 +1,59 @@ +require 'chef_helper' + +RSpec.describe GitlabGeoHelper do + cached(:chef_run) { converge_config(is_ee: true) } + subject { described_class.new(chef_run.node) } + + describe '#migrated?' do + let(:state_file) { '/tmp/geo-db-migrate-xyz-123' } + + before do + allow(subject).to receive(:db_migrate_status_file) { state_file } + end + + it 'returns true when migration file exists and content is zero' do + stub_file_content(state_file, '0') + + expect(subject.migrated?).to eq true + end + + it 'returns false when migration file doesnt exist' do + allow(File).to receive(:exist?).with(state_file) { false } + + expect(subject.migrated?).to eq false + end + + it 'returns false when content is not zero' do + stub_file_content(state_file, '1') + + expect(subject.migrated?).to eq false + end + end + + describe '#db_migrate_status_file' do + let(:connection_digest) { '32596e8376077c3ef8d5cf52f15279ba' } + let(:revision) { '8b21d70' } + + before do + allow(subject).to receive(:connection_digest) { connection_digest } + allow(subject).to receive(:revision) { revision } + end + + it 'contains connection digest' do + expect(subject.db_migrate_status_file).to include(connection_digest) + end + + it 'contains revision number' do + expect(subject.db_migrate_status_file).to include(revision) + end + + it 'follows specific pattern' do + expect(subject.db_migrate_status_file).to include("geo-db-migrate-#{connection_digest}-#{revision}") + end + end + + def stub_file_content(fullpath, content) + allow(File).to receive(:exist?).with(fullpath) { true } + allow(IO).to receive(:read).with(fullpath) { content } + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_helper_spec.rb b/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvbGlicmFyaWVzL3NlbnRpbmVsX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_helper_spec.rb @@ -0,0 +1,46 @@ +require 'chef_helper' + +RSpec.describe SentinelHelper do + let(:chef_run) { converge_config(is_ee: true) } + subject { described_class.new(chef_run.node) } + before { allow(Gitlab).to receive(:[]).and_call_original } + + context '#myid' do + context 'when retrieving from config' do + it 'fails when myid is not 40 hex-characters long' do + stub_gitlab_rb( + sentinel: { + myid: 'wrongid' + } + ) + + expect { subject.myid }.to raise_error RuntimeError + end + + it 'works when myid is 40 hex-characters long' do + stub_gitlab_rb( + sentinel: { + myid: '1234567890abcdef1234567890abcdef12345678' + } + ) + + expect { subject.myid }.not_to raise_error + end + end + + context 'when no config is defined' do + let(:myid) { 'abcdef1234567890abcdef1234567890abcdef1' } + + it 'generates a random myid' do + expect(subject.myid).not_to be_empty + end + + it 'persist generated value into JSON file' do + allow(subject).to receive(:generate_myid).at_least(:once) { myid } + + expect(subject).to receive(:save_to_file).with({ 'myid' => myid }) + subject.myid + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_spec.rb b/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvbGlicmFyaWVzL3NlbnRpbmVsX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_spec.rb @@ -0,0 +1,68 @@ +# This spec is to test the Sentinel helper and whether the values parsed +# are the ones we expect +require 'chef_helper' + +RSpec.describe 'Sentinel' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config') } + let(:node) { chef_run.node } + subject { ::Sentinel } + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context '.parse_variables' do + context 'When sentinel is enabled' do + before do + stub_gitlab_rb( + redis_sentinel_role: { + enable: true, + } + ) + end + it 'delegates to parse_sentinel_settings' do + expect(subject).to receive(:parse_sentinel_settings) + + subject.parse_variables + end + + context 'when redis announce_ip is defined' do + let(:redis_master_ip) { '1.1.1.1' } + let(:redis_announce_ip) { '10.10.10.10' } + before do + stub_gitlab_rb( + redis: { + master_ip: redis_master_ip, + announce_ip: redis_announce_ip + } + ) + end + + it 'Sentinel announce_ip is autofilled based on redis announce_ip' do + expect(node['gitlab']['sentinel']['announce_ip']).to eq redis_announce_ip + + subject.parse_sentinel_settings + end + end + + context 'when redis announce_port is defined' do + let(:redis_announce_port) { 6370 } + let(:sentinel_port) { 26370 } + before do + stub_gitlab_rb( + sentinel: { + port: sentinel_port + }, + redis: { + announce_port: redis_announce_port + } + ) + end + it 'Sentinel announce_port is autofilled based on sentinel port' do + expect(node['gitlab']['sentinel']['announce_port']).to eq sentinel_port + + subject.parse_sentinel_settings + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/default_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/default_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9kZWZhdWx0X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/recipes/default_spec.rb @@ -0,0 +1,33 @@ +require 'chef_helper' + +RSpec.describe 'gitlab-ee::default' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'postgresql is enabled' do + context 'pgbouncer will not connect to postgresql' do + it 'should always include the pgbouncer_user recipe' do + expect(chef_run).to include_recipe('pgbouncer::user') + end + end + + context 'pgbouncer will connect to postgresql' do + before do + stub_gitlab_rb( + { + postgresql: { + pgbouncer_user_password: 'fakepassword' + } + } + ) + end + + it 'should include the pgbouncer_user recipe' do + expect(chef_run).to include_recipe('pgbouncer::user') + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/geo-logcursor_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/geo-logcursor_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9nZW8tbG9nY3Vyc29yX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/recipes/geo-logcursor_spec.rb @@ -0,0 +1,41 @@ +# +# Copyright:: Copyright (c) 2017 GitLab Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef_helper' + +RSpec.describe 'gitlab-ee::geo-logcursor' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'when disabled' do + it_behaves_like 'disabled runit service', 'geo-logcursor' + end + + describe 'when enabled' do + before do + stub_gitlab_rb( + geo_logcursor: { + enable: true, + } + ) + end + + it_behaves_like 'enabled runit service', 'geo-logcursor', 'root', 'root' + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/geo-postgresql_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/geo-postgresql_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9nZW8tcG9zdGdyZXNxbF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/recipes/geo-postgresql_spec.rb @@ -0,0 +1,388 @@ +require 'chef_helper' + +RSpec.describe 'geo postgresql' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + let(:postgresql_conf) { '/var/opt/gitlab/geo-postgresql/data/postgresql.conf' } + let(:runtime_conf) { '/var/opt/gitlab/geo-postgresql/data/runtime.conf' } + let(:pg_hba_conf) { '/var/opt/gitlab/geo-postgresql/data/pg_hba.conf' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('best_version')) + allow_any_instance_of(GeoPgHelper).to receive(:running_version).and_return(PGVersion.new('best_version')) + allow_any_instance_of(GeoPgHelper).to receive(:database_version).and_return(PGVersion.new('best_version')) + + # Workaround for Chef reloading instances across different examples + allow_any_instance_of(GeoPgHelper).to receive(:bootstrapped?).and_return(true) + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('geo-postgresql').and_return(true) + end + + context 'when geo postgres is enabled' do + before do + stub_gitlab_rb(geo_postgresql: { enable: true }) + end + + it 'includes the postgresql::bin recipe' do + expect(chef_run).to include_recipe('postgresql::bin') + end + + it 'includes the postgresql_user recipe' do + expect(chef_run).to include_recipe('postgresql::user') + end + + it 'includes the postgresql_sysctl recipe' do + expect(chef_run).to include_recipe('postgresql::sysctl') + end + + it 'does not warn the user that a restart is needed by default' do + allow_any_instance_of(GeoPgHelper).to receive(:is_running?).and_return(true) + expect(chef_run).not_to run_ruby_block('warn pending geo-postgresql restart') + end + + it 'notifies restarts postgresql when the postgresql runit run file changes' do + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('geo-postgresql').and_return(true) + + psql_service = chef_run.service('geo-postgresql') + expect(psql_service).not_to subscribe_to('template[/opt/gitlab/sv/geo-postgresql/run]').on(:restart).delayed + end + + it 'includes runtime.conf in postgresql.conf' do + expect(chef_run).to render_file(postgresql_conf) + .with_content(/include 'runtime.conf'/) + end + + it 'creates the gitlab_geo role in the geo-postgresql database, without a password' do + expect(chef_run).to create_postgresql_user('gitlab_geo').with(password: nil) + end + + it 'creates gitlabhq_geo_production database' do + params = { + owner: 'gitlab_geo' + } + expect(chef_run).to create_postgresql_database('gitlabhq_geo_production').with(params) + end + + context 'with default settings' do + it_behaves_like 'enabled runit service', 'geo-postgresql', 'root', 'root' + + context 'when rendering postgresql.conf' do + it 'correctly sets the shared_preload_libraries default setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['shared_preload_libraries']).to be_nil + + expect(chef_run).to render_file(postgresql_conf) + .with_content(/shared_preload_libraries = ''/) + end + + it 'sets archive settings' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/archive_mode = off/) + end + + it 'sets the max_replication_slots setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['max_replication_slots']).to eq(0) + + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/max_replication_slots = 0/) + end + + it 'sets the synchronous_commit setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['synchronous_standby_names']).to eq('') + + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/synchronous_standby_names = ''/) + end + + it 'does not set dynamic_shared_memory_type by default' do + expect(chef_run).not_to render_file( + postgresql_conf + ).with_content(/^dynamic_shared_memory_type = /) + end + + it 'sets the max_locks_per_transaction setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['max_locks_per_transaction']) + .to eq(128) + + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/max_locks_per_transaction = 128/) + end + end + context 'when rendering runtime.conf' do + it 'correctly sets the log_line_prefix default setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['log_line_prefix']).to be_nil + + expect(chef_run).to render_file(runtime_conf) + .with_content(/log_line_prefix = ''/) + end + + it 'sets max_standby settings' do + expect(chef_run).to render_file( + runtime_conf + ).with_content(/max_standby_archive_delay = 30s/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/max_standby_streaming_delay = 30s/) + end + + it 'sets archive settings' do + expect(chef_run).to render_file( + runtime_conf + ).with_content(/archive_command = ''/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/archive_timeout = 0/) + end + + it 'sets the synchronous_commit setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['synchronous_commit']).to eq('on') + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/synchronous_commit = on/) + end + + it 'sets the hot_standby_feedback setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['hot_standby_feedback']) + .to eq('off') + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/hot_standby_feedback = off/) + end + + it 'sets the random_page_cost setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['random_page_cost']) + .to eq(2.0) + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/random_page_cost = 2\.0/) + end + + it 'sets the log_temp_files setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['log_temp_files']) + .to eq(-1) + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/log_temp_files = -1/) + end + + it 'sets the log_checkpoints setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['log_checkpoints']) + .to eq('off') + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/log_checkpoints = off/) + end + + it 'sets idle_in_transaction_session_timeout' do + expect(chef_run.node['gitlab']['geo-postgresql']['idle_in_transaction_session_timeout']) + .to eq('60000') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/idle_in_transaction_session_timeout = 60000/) + end + + it 'sets effective_io_concurrency' do + expect(chef_run.node['gitlab']['geo-postgresql']['effective_io_concurrency']) + .to eq(1) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/effective_io_concurrency = 1/) + end + + it 'sets max_worker_processes' do + expect(chef_run.node['gitlab']['geo-postgresql']['max_worker_processes']) + .to eq(8) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/max_worker_processes = 8/) + end + + it 'sets max_parallel_workers_per_gather' do + expect(chef_run.node['gitlab']['geo-postgresql']['max_parallel_workers_per_gather']) + .to eq(0) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/max_parallel_workers_per_gather = 0/) + end + + it 'sets log_lock_waits' do + expect(chef_run.node['gitlab']['geo-postgresql']['log_lock_waits']) + .to eq(1) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/log_lock_waits = 1/) + end + + it 'sets deadlock_timeout' do + expect(chef_run.node['gitlab']['geo-postgresql']['deadlock_timeout']) + .to eq('5s') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/deadlock_timeout = '5s'/) + end + + it 'sets track_io_timing' do + expect(chef_run.node['gitlab']['geo-postgresql']['track_io_timing']) + .to eq('off') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/track_io_timing = 'off'/) + end + end + end + + context 'with user specified settings' do + before do + stub_gitlab_rb(geo_postgresql: { + enable: true, + dynamic_shared_memory_type: 'none', + custom_pg_hba_entries: { + foo: [ + type: 'host', + database: 'foo', + user: 'bar', + cidr: '127.0.0.1/32', + method: 'trust' + ] + }, + sql_user_password: 'fakepasswordhash', + shared_preload_libraries: 'pg_stat_statements', + log_line_prefix: '%a', + max_standby_archive_delay: '60s', + max_standby_streaming_delay: '120s', + archive_mode: 'on', + archive_command: 'command', + archive_timeout: '120', + }, + postgresql: { + username: 'foo', + group: 'bar' + } + ) + end + + it 'notifies geo-postgresql reload' do + runtime_resource = chef_run.template(runtime_conf) + expect(runtime_resource).to notify('execute[reload geo-postgresql]').to(:run).immediately + end + + it 'creates the gitlab_geo role in the geo-postgresql database with the specified password' do + expect(chef_run).to create_postgresql_user('gitlab_geo').with(password: 'md5fakepasswordhash') + end + + context 'when rendering postgresql.conf' do + it 'sets the dynamic_shared_memory_type' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/^dynamic_shared_memory_type = none/) + end + + it 'correctly sets the shared_preload_libraries setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['shared_preload_libraries']).to eql('pg_stat_statements') + + expect(chef_run).to render_file(postgresql_conf) + .with_content(/shared_preload_libraries = 'pg_stat_statements'/) + end + + it 'sets archive settings' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/archive_mode = on/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/archive_command = 'command'/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/archive_timeout = 120/) + end + end + + context 'when rendering runtime.conf' do + it 'correctly sets the log_line_prefix setting' do + expect(chef_run.node['gitlab']['geo-postgresql']['log_line_prefix']).to eql('%a') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/log_line_prefix = '%a'/) + end + + it 'sets max_standby settings' do + expect(chef_run).to render_file( + runtime_conf + ).with_content(/max_standby_archive_delay = 60s/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/max_standby_streaming_delay = 120s/) + end + + context 'when rendering pg_hba.conf' do + it 'creates a standard pg_hba.conf' do + expect(chef_run).to render_file(pg_hba_conf) + .with_content('local all all peer map=gitlab') + end + + it 'adds users custom entries to pg_hba.conf' do + expect(chef_run).to render_file(pg_hba_conf) + .with_content('host foo bar 127.0.0.1/32 trust') + end + end + end + end + + context 'when geo postgres is disabled' do + before do + stub_gitlab_rb(geo_postgresql: { enable: false }) + end + + it_behaves_like 'disabled runit service', 'geo-postgresql' + end + end +end + +RSpec.describe 'geo postgresql when version mismatches occur' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + let(:postgresql_conf) { '/var/opt/gitlab/geo-postgresql/data/postgresql.conf' } + let(:runtime_conf) { '/var/opt/gitlab/geo-postgresql/data/runtime.conf' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb(geo_postgresql: { enable: true }) + end + + context 'running version differs from installed version' do + before do + allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('expectation')) + allow_any_instance_of(GeoPgHelper).to receive(:running_version).and_return(PGVersion.new('reality')) + end + + it 'warns the user that a restart is needed' do + allow_any_instance_of(GeoPgHelper).to receive(:is_running?).and_return(true) + expect(chef_run).to run_ruby_block('warn pending geo-postgresql restart') + end + + it 'does not warns the user that a restart is needed when geo-postgres is stopped' do + expect(chef_run).not_to run_ruby_block('warn pending geo-postgresql restart') + end + end + + context 'running version differs from data version' do + before do + allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('expectation')) + allow_any_instance_of(GeoPgHelper).to receive(:running_version).and_return(PGVersion.new('expectation')) + allow_any_instance_of(GeoPgHelper).to receive(:database_version).and_return(PGVersion.new('reality')) + end + + it 'does not warn the user that a restart is needed' do + allow_any_instance_of(GeoPgHelper).to receive(:is_running?).and_return(true) + expect(chef_run).not_to run_ruby_block('warn pending geo-postgresql restart') + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/geo-secondary_disable_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/geo-secondary_disable_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9nZW8tc2Vjb25kYXJ5X2Rpc2FibGVfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/recipes/geo-secondary_disable_spec.rb @@ -0,0 +1,19 @@ +require 'chef_helper' + +RSpec.describe 'gitlab-ee::geo-secondary_disable' do + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'when geo_secondary_role is disabled' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before { stub_gitlab_rb(geo_secondary_role: { enable: false }) } + + it 'removes database_geo.yml symlink' do + expect(chef_run).to delete_templatesymlink('Removes database_geo.yml symlink') + .with(link_to: '/var/opt/gitlab/gitlab-rails/etc/database_geo.yml', + link_from: '/opt/gitlab/embedded/service/gitlab-rails/config/database_geo.yml') + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/geo-secondary_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/geo-secondary_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9nZW8tc2Vjb25kYXJ5X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/recipes/geo-secondary_spec.rb @@ -0,0 +1,388 @@ +require 'chef_helper' + +RSpec.describe 'gitlab-ee::geo-secondary' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab-ee::default') } + let(:database_geo_yml) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/database_geo.yml') } + let(:database_geo_yml_content) { ChefSpec::Renderer.new(chef_run, database_geo_yml).content } + let(:generated_yml_content) { YAML.safe_load(database_geo_yml_content, [], [], true, symbolize_names: true) } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'when geo_secondary_role is disabled' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before { stub_gitlab_rb(geo_secondary_role: { enable: false }) } + + it 'does not render the geo-secondary files' do + expect(chef_run).not_to render_file('/opt/gitlab/embedded/service/gitlab-rails/config/database_geo.yml') + expect(chef_run).not_to render_file('/var/opt/gitlab/gitlab-rails/etc/database_geo.yml') + end + end + + describe 'when geo_secondary_role is disabled but geo-postgresql enabled' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + stub_gitlab_rb(geo_secondary_role: { enable: false }, + geo_postgresql: { enable: true }) + end + + it 'does not render the geo-secondary files' do + expect(chef_run).not_to render_file('/opt/gitlab/embedded/service/gitlab-rails/config/database_geo.yml') + expect(chef_run).not_to render_file('/var/opt/gitlab/gitlab-rails/etc/database_geo.yml') + end + end + + describe 'when gitlab_rails is disabled, but geo_secondary_role is enabled' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + stub_gitlab_rb(geo_secondary_role: { enable: true }, + gitlab_rails: { enable: false }) + end + + it 'does not render the geo-secondary files' do + expect(chef_run).not_to create_templatesymlink('Create a database_geo.yml and create a symlink to Rails root') + end + end + + describe 'when gitlab_rails is enabled' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + stub_gitlab_rb(geo_secondary_role: { enable: true }, + geo_postgresql: { enable: true }, + puma: { enable: false }, + sidekiq: { enable: false }, + geo_logcursor: { enable: false }, + gitlab_rails: { enable: true }) + end + + it 'allows gitlab_rails to be overriden' do + expect(chef_run.node['gitlab']['gitlab-rails']['enable']).to be true + end + end + + context 'when gitaly is enabled' do + describe 'when gitlab_rails enable is not set' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + stub_gitlab_rb(geo_secondary_role: { enable: true }, + geo_secondary: { enable: true }, + geo_postgresql: { enable: false }, + puma: { enable: false }, + sidekiq: { enable: false }, + geo_logcursor: { enable: false }, + gitaly: { enable: true }) + end + + it 'ensures gitlab_rails is enabled' do + chef_run + expect(Gitlab['gitlab_rails']['enable']).to be true + end + end + + describe 'when gitlab_rails enable is provided' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + stub_gitlab_rb(geo_secondary_role: { enable: true }, + geo_secondary: { enable: true }, + geo_postgresql: { enable: false }, + puma: { enable: false }, + sidekiq: { enable: false }, + geo_logcursor: { enable: false }, + gitaly: { enable: true }, + gitlab_rails: { enable: false }) + end + + it 'does not override gitlab_rails enable' do + chef_run + expect(Gitlab['gitlab_rails']['enable']).to be false + end + end + end + + context 'when geo_secondary_role is enabled but geo-postgresql is disabled' do + before do + stub_gitlab_rb(geo_secondary_role: { enable: true }, + geo_postgresql: { enable: false }, + geo_secondary: { + db_host: '1.1.1.1', + db_password: 'password', + db_port: '5431' + }) + end + + describe 'migrations' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + it 'includes the database migration recipe' do + expect(chef_run).to include_recipe('gitlab-ee::geo_database_migrations') + end + end + + describe 'database.yml' do + it 'creates the database_geo.yml template not using many database structure' do + expect(generated_yml_content).to eq( + production: { + adapter: 'postgresql', + application_name: nil, + collation: nil, + connect_timeout: nil, + database: 'gitlabhq_geo_production', + encoding: 'unicode', + host: '1.1.1.1', + keepalives: nil, + keepalives_count: nil, + keepalives_idle: nil, + keepalives_interval: nil, + load_balancing: { + hosts: [] + }, + password: 'password', + port: 5431, + prepared_statements: false, + socket: nil, + sslca: nil, + sslcompression: 0, + sslmode: nil, + sslrootcert: nil, + statement_limit: nil, + tcp_user_timeout: nil, + username: 'gitlab_geo', + variables: { + statement_timeout: nil, + } + } + ) + end + + context 'when SSL compression is enabled' do + before do + stub_gitlab_rb(geo_secondary: { db_sslcompression: 1 }) + end + + it 'uses provided value in database.yml' do + expect(generated_yml_content[:production][:sslcompression]).to eq(1) + end + end + end + end + + context 'when geo_secondary_role is enabled' do + before do + stub_gitlab_rb(geo_secondary_role: { enable: true }) + + # Make sure other calls to `File.symlink?` are allowed. + allow(File).to receive(:symlink?).and_call_original + %w( + alertmanager + gitlab-exporter + gitlab-workhorse + logrotate + nginx + node-exporter + postgres-exporter + postgresql + prometheus + redis + redis-exporter + sidekiq + puma + gitaly + geo-postgresql + gitlab-pages + geo-logcursor + patroni + ).map { |svc| stub_should_notify?(svc, true) } + end + + describe 'database.yml' do + let(:templatesymlink) { chef_run.templatesymlink('Create a database_geo.yml and create a symlink to Rails root') } + + it 'creates the database_geo.yml template not using many database structure' do + expect(generated_yml_content).to eq( + production: { + adapter: 'postgresql', + application_name: nil, + collation: nil, + connect_timeout: nil, + database: 'gitlabhq_geo_production', + encoding: 'unicode', + host: '/var/opt/gitlab/geo-postgresql', + keepalives: nil, + keepalives_count: nil, + keepalives_idle: nil, + keepalives_interval: nil, + load_balancing: { + hosts: [] + }, + password: nil, + port: 5431, + prepared_statements: false, + socket: nil, + sslca: nil, + sslcompression: 0, + sslmode: nil, + sslrootcert: nil, + statement_limit: nil, + tcp_user_timeout: nil, + username: 'gitlab_geo', + variables: { + statement_timeout: nil, + } + } + ) + end + + it 'template triggers dependent services notifications' do + expect(templatesymlink).to notify('ruby_block[Restart geo-secondary dependent services]').to(:run).delayed + end + end + + describe 'PostgreSQL gitlab-geo.conf' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + let(:geo_conf) { '/var/opt/gitlab/postgresql/data/gitlab-geo.conf' } + let(:postgresql_conf) { '/var/opt/gitlab/postgresql/data/postgresql.conf' } + + context 'when postgresql enabled on the node' do + it 'renders gitlab-geo.conf' do + expect(chef_run).to render_file(geo_conf) + end + end + + context 'when postgresql disabled on the node' do + before { stub_gitlab_rb(postgresql: { enable: false }) } + + it 'does not render gitlab-geo.conf' do + expect(chef_run).not_to render_file(geo_conf) + end + end + end + + describe 'Restart geo-secondary dependent services' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + let(:ruby_block) { chef_run.ruby_block('Restart geo-secondary dependent services') } + + it 'does not run' do + expect(chef_run).not_to run_ruby_block('Restart geo-secondary dependent services') + expect(ruby_block).to do_nothing + end + + it 'ruby_block triggers dependent services notifications' do + allow(ruby_block).to receive(:notifies) + ruby_block.block.call + + %w( + puma + geo-logcursor + ).each do |svc| + expect(ruby_block).to have_received(:notifies).with(:restart, "runit_service[#{svc}]") + end + expect(ruby_block).to have_received(:notifies).with(:restart, "sidekiq_service[sidekiq]") + end + end + + describe 'include_recipe' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + it 'includes the Geo tracking DB recipe' do + expect(chef_run).to include_recipe('gitlab-ee::geo-postgresql') + end + + it 'includes the Geo secondary recipes for Rails' do + expect(chef_run).to include_recipe('gitlab-ee::geo-secondary') + expect(chef_run).to include_recipe('gitlab-ee::geo_database_migrations') + end + + it 'does not include the Geo database migrations recipe if Rails not needed' do + stub_gitlab_rb(geo_secondary_role: { enable: true }, + nginx: { enable: false }, + puma: { enable: false }, + sidekiq: { enable: false }, + gitaly: { enable: false }, + postgresql: { enable: false }, + geo_logcursor: { enable: false }, + redis: { enable: true }) + + expect(chef_run).not_to include_recipe('gitlab-ee::geo_database_migrations') + end + end + + describe 'migrations' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + it 'runs the migrations' do + expect(chef_run).to run_rails_migration('gitlab-geo tracking') + end + end + + describe 'rails_needed?' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab-ee::default') } + + context 'manually enabled services' do + before do + stub_gitlab_rb( + # Everything but puma is disabled + puma: { enable: true }, + sidekiq: { enable: false }, + geo_logcursor: { enable: false }, + gitaly: { enable: false } + ) + end + + it 'should need rails' do + expect(chef_run).to include_recipe('gitlab::gitlab-rails') + end + end + + context 'manually disabled services' do + before do + stub_gitlab_rb( + gitaly: { enable: false }, + puma: { enable: false }, + sidekiq: { enable: false }, + geo_logcursor: { enable: false } + ) + end + + it 'should not need rails' do + expect(chef_run).not_to include_recipe('gitlab::gitlab-rails') + end + end + end + end + + context 'puma worker_processes' do + let(:chef_run) do + ChefSpec::SoloRunner.new do |node| + node.automatic['cpu']['total'] = 16 + node.automatic['memory']['total'] = '8388608KB' # 8GB + end.converge('gitlab-ee::default') + end + + it 'reduces the number of puma workers on secondary node' do + stub_gitlab_rb(geo_secondary_role: { enable: true }) + + expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 5 + end + + it 'uses the specified number of puma workers' do + stub_gitlab_rb(geo_secondary_role: { enable: true }, + puma: { worker_processes: 1 }) + + expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 1 + end + + it 'does not reduce the number of puma workers on primary node' do + stub_gitlab_rb(geo_primary_role: { enable: true }) + + expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 6 + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/geo_database_migrations_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/geo_database_migrations_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9nZW9fZGF0YWJhc2VfbWlncmF0aW9uc19zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/recipes/geo_database_migrations_spec.rb @@ -0,0 +1,71 @@ +require 'chef_helper' + +# NOTE: These specs do not verify whether the code actually ran +# Nor whether the resource inside of the recipe was notified correctly. +# At this moment they only verify whether the expected commands are passed +# to the bash block. +# + +RSpec.describe 'gitlab-ee::geo-database-migrations' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when migration should run' do + before do + stub_default_not_listening?(false) + stub_gitlab_rb(geo_secondary_role: { enable: true }) + + # Make sure other calls to `File.symlink?` are allowed. + allow(File).to receive(:symlink?).and_call_original + %w( + alertmanager + gitlab-exporter + gitlab-workhorse + logrotate + nginx + node-exporter + postgres-exporter + postgresql + prometheus + redis + redis-exporter + sidekiq + puma + gitaly + geo-postgresql + gitlab-pages + geo-logcursor + ).map { |svc| stub_should_notify?(svc, true) } + end + + it 'runs the migrations with expected attributes' do + expect(chef_run).to run_rails_migration('gitlab-geo tracking') do |resource| + expect(resource.dependent_services).to include_array(%w(runit_service[puma] sidekiq_service[sidekiq])) + expect(resource.rake_task).to eq('geo:db:migrate') + expect(resource.logfile_prefix).to eq('gitlab-geo-db-migrate') + expect(resource.helper).to be_a(GitlabGeoHelper) + end + end + + it 'starts geo-postgresql if its not running' do + stub_not_listening?('geo-postgresql', true) + + expect(chef_run.rails_migration('gitlab-geo tracking')).to notify('execute[start geo-postgresql]').to(:run) + end + end + + context 'with auto_migrate off' do + it 'skips running the migrations' do + stub_gitlab_rb({ + geo_secondary_role: { enable: true }, + geo_secondary: { auto_migrate: false } + }) + + expect(chef_run).to include_recipe('gitlab-ee::geo_database_migrations') + expect(chef_run).not_to run_rails_migration('gitlab-geo tracking') + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/geo_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/geo_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9nZW9fc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/recipes/geo_spec.rb @@ -0,0 +1,166 @@ +require 'chef_helper' + +RSpec.describe 'gitlab-ee::geo' do + let(:node) { chef_run.node } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + shared_examples 'default services' do + it 'remain enabled', :aggregate_failures do + chef_run + + default_services = Services.find_by_group(Services::DEFAULT_GROUP) + omnibus_helper = OmnibusHelper.new(node) + + expect(default_services.count).to be > 0 + default_services.each do |service| + expect(omnibus_helper.service_enabled?(service.tr('_', '-'))).to be(true), "#{service} was not enabled" + end + end + end + + context 'when geo_primary_role enabled' do + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb(geo_primary_role: { enable: true }) + end + ChefSpec::SoloRunner.converge('gitlab-ee::default') + end + + it_behaves_like 'default services' + + context 'in geo_logcursor settings' do + it 'is not enabled' do + expect(node['gitlab']['geo-logcursor']['enable']).to eq(nil) + end + end + + context 'in postgres settings' do + let(:config_attrs) { node['postgresql'] } + + it 'defines sql_replication_user' do + expect(config_attrs['sql_replication_user']).to eq('gitlab_replicator') + end + + it 'defines wal_level' do + expect(config_attrs['wal_level']).to eq('hot_standby') + end + + it 'defines wal_log_hints' do + expect(config_attrs['wal_log_hints']).to eq('off') + end + + it 'defines max_wal_senders' do + expect(config_attrs['max_wal_senders']).to eq(10) + end + + it 'defines wal_keep_segments' do + expect(config_attrs['wal_keep_segments']).to eq(50) + end + + it 'defines max_replication_slots' do + expect(config_attrs['max_replication_slots']).to eq(1) + end + + it 'defines hot_standby' do + expect(config_attrs['hot_standby']).to eq('on') + end + end + end + + context 'geo_secondary_role enabled' do + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb(geo_secondary_role: { enable: true }) + end + ChefSpec::SoloRunner.converge('gitlab-ee::default') + end + + it_behaves_like 'default services' + + context 'in geo_postgres settings' do + it 'is enabled' do + expect(node['gitlab']['geo-postgresql']['enable']).to eq(true) + end + end + + context 'in geo_logcursor settings' do + it 'is enabled' do + expect(node['gitlab']['geo-logcursor']['enable']).to eq(true) + end + end + + context 'in postgres settings' do + let(:config_attrs) { node['postgresql'] } + + it 'defines wal_level' do + expect(config_attrs['wal_level']).to eq('hot_standby') + end + + it 'defines wal_log_hints' do + expect(config_attrs['wal_log_hints']).to eq('off') + end + + it 'defines max_wal_senders' do + expect(config_attrs['max_wal_senders']).to eq(10) + end + + it 'defines wal_keep_segments' do + expect(config_attrs['wal_keep_segments']).to eq(10) + end + + it 'defines max_replication_slots' do + expect(config_attrs['max_replication_slots']).to eq(0) + end + + it 'defines hot_standby' do + expect(config_attrs['hot_standby']).to eq('on') + end + + it 'defines standby settings' do + expect(config_attrs['max_standby_archive_delay']).to eq('60s') + expect(config_attrs['max_standby_streaming_delay']).to eq('60s') + end + end + + context 'postgresql 13' do + let(:runtime_conf) { '/var/opt/gitlab/geo-postgresql/data/runtime.conf' } + + before do + allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('13.0')) + allow_any_instance_of(GeoPgHelper).to receive(:database_version).and_return(PGVersion.new('13.0')) + end + + it 'configures wal_keep_size instead of wal_keep_segments' do + expect(chef_run).to render_file(runtime_conf).with_content { |content| + expect(content).to include("wal_keep_size") + expect(content).not_to include("wal_keep_segments") + } + end + end + + context 'postgresql 12' do + let(:runtime_conf) { '/var/opt/gitlab/geo-postgresql/data/runtime.conf' } + + before do + allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('12.0')) + allow_any_instance_of(GeoPgHelper).to receive(:database_version).and_return(PGVersion.new('12.0')) + end + + it 'configures wal_keep_segments instead of wal_keep_size' do + expect(chef_run).to render_file(runtime_conf).with_content { |content| + expect(content).to include("wal_keep_segments") + expect(content).to_not include("wal_keep_size") + } + end + end + + context 'in gitlab-rails' do + it 'disables auto_migrate' do + expect(node['gitlab']['gitlab-rails']['auto_migrate']).to eq(false) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/sentinel_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/sentinel_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9zZW50aW5lbF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/recipes/sentinel_spec.rb @@ -0,0 +1,186 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::redis' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(sentinel_service runit_service)).converge('gitlab-ee::default') } + let(:redis_master_ip) { '1.1.1.1' } + let(:redis_announce_ip) { '10.10.10.10' } + let(:redis_master_password) { 'blahblahblah' } + let(:sentinel_conf) { '/var/opt/gitlab/sentinel/sentinel.conf' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'When sentinel is disabled' do + before do + stub_gitlab_rb( + redis: { + master_ip: redis_master_ip, + announce_ip: redis_announce_ip, + master_password: redis_master_password + }, + redis_sentinel_role: { + enable: false, + } + ) + end + + it_behaves_like 'disabled runit service', 'sentinel', 'root', 'root' + end + + describe 'When sentinel is enabled' do + context 'default values' do + before do + stub_gitlab_rb( + redis: { + master_ip: redis_master_ip, + announce_ip: redis_announce_ip, + master_password: redis_master_password + }, + redis_sentinel_role: { + enable: true, + } + ) + end + it 'creates redis user and group' do + expect(chef_run).to create_account('user and group for sentinel').with(username: 'gitlab-redis', groupname: 'gitlab-redis') + end + + it 'renders sentinel config file with default values' do + expect(chef_run).to render_file('/var/opt/gitlab/sentinel/sentinel.conf') + .with_content { |content| + expect(content).to match(%r{bind 0.0.0.0}) + expect(content).to match(%r{port 26379}) + expect(content).to match(%r{sentinel announce-ip 10.10.10.10}) + expect(content).to match(%r{sentinel monitor gitlab-redis 1.1.1.1 6379 1}) + expect(content).to match(%r{sentinel down-after-milliseconds gitlab-redis 10000}) + expect(content).to match(%r{sentinel failover-timeout gitlab-redis 60000}) + expect(content).to match(%r{sentinel auth-pass gitlab-redis blahblahblah}) + expect(content).not_to match(%r{^tls}) + expect(content).to match(%r{SENTINEL resolve-hostnames no}) + expect(content).to match(%r{SENTINEL announce-hostnames no}) + } + end + + it_behaves_like 'enabled runit service', 'sentinel', 'root', 'root' + + context 'user overrides sentinel_use_hostnames' do + before do + stub_gitlab_rb( + sentinel: { + use_hostnames: true + } + ) + end + + it 'uses hostnames' do + expect(chef_run).to render_file(sentinel_conf).with_content { |content| + expect(content).to match(%r{SENTINEL resolve-hostnames yes}) + expect(content).to match(%r{SENTINEL announce-hostnames yes}) + } + end + end + end + + context 'user specified values' do + before do + stub_gitlab_rb( + redis_sentinel_role: { + enable: true, + }, + redis: { + username: 'foo', + group: 'bar', + master_ip: redis_master_ip, + announce_ip: 'fake.hostname.local', + master_password: redis_master_password + } + ) + end + it 'creates redis user and group' do + expect(chef_run).to create_account('user and group for sentinel').with(username: 'foo', groupname: 'bar') + end + + it_behaves_like 'enabled runit service', 'sentinel', 'root', 'root' + + it 'uses hostnames' do + expect(chef_run).to render_file(sentinel_conf).with_content { |content| + expect(content).to match(%r{SENTINEL resolve-hostnames yes}) + expect(content).to match(%r{SENTINEL announce-hostnames yes}) + } + end + + context 'user overrides sentinel_use_hostnames' do + before do + stub_gitlab_rb( + sentinel: { + use_hostnames: false + } + ) + end + + it 'does not use hostnames' do + expect(chef_run).to render_file(sentinel_conf).with_content { |content| + expect(content).to match(%r{SENTINEL resolve-hostnames no}) + expect(content).to match(%r{SENTINEL announce-hostnames no}) + } + end + end + end + + context 'with tls settings specified' do + before do + stub_gitlab_rb( + redis: { + master_ip: redis_master_ip, + announce_ip: redis_announce_ip, + master_password: redis_master_password, + }, + redis_sentinel_role: { + enable: true + }, + sentinel: { + tls_port: 6380, + tls_cert_file: '/etc/gitlab/ssl/redis.crt', + tls_key_file: '/etc/gitlab/ssl/redis.key', + tls_dh_params_file: '/etc/gitlab/ssl/redis-dhparams', + tls_ca_cert_file: '/etc/gitlab/ssl/redis-ca.crt', + tls_ca_cert_dir: '/opt/gitlab/embedded/ssl/certs', + tls_auth_clients: 'no', + tls_replication: 'yes', + tls_cluster: 'yes', + tls_protocols: 'TLSv1.2 TLSv1.3', + tls_ciphers: 'DEFAULT:!MEDIUM', + tls_ciphersuites: 'TLS_CHACHA20_POLY1305_SHA256', + tls_prefer_server_ciphers: 'yes', + tls_session_caching: 'no', + tls_session_cache_size: 10000, + tls_session_cache_timeout: 120 + } + ) + end + + it 'renders sentinel config file with specified tls values' do + expect(chef_run).to render_file('/var/opt/gitlab/sentinel/sentinel.conf') + .with_content { |content| + expect(content).to match(%r{^tls-port 6380$}) + expect(content).to match(%r{^tls-cert-file /etc/gitlab/ssl/redis.crt$}) + expect(content).to match(%r{^tls-key-file /etc/gitlab/ssl/redis.key$}) + expect(content).to match(%r{^tls-dh-params-file /etc/gitlab/ssl/redis-dhparams$}) + expect(content).to match(%r{^tls-ca-cert-file /etc/gitlab/ssl/redis-ca.crt$}) + expect(content).to match(%r{^tls-ca-cert-dir /opt/gitlab/embedded/ssl/certs$}) + expect(content).to match(%r{^tls-auth-clients no$}) + expect(content).to match(%r{^tls-replication yes$}) + expect(content).to match(%r{^tls-cluster yes$}) + expect(content).to match(%r{^tls-protocols "TLSv1.2 TLSv1.3"$}) + expect(content).to match(%r{^tls-ciphers DEFAULT:!MEDIUM$}) + expect(content).to match(%r{^tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256$}) + expect(content).to match(%r{^tls-prefer-server-ciphers yes$}) + expect(content).to match(%r{^tls-session-caching no$}) + expect(content).to match(%r{^tls-session-cache-size 10000$}) + expect(content).to match(%r{^tls-session-cache-timeout 120$}) + } + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab-ee/resources/pgbouncer_user_spec.rb b/spec/chef/cookbooks/gitlab-ee/resources/pgbouncer_user_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVzb3VyY2VzL3BnYm91bmNlcl91c2VyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab-ee/resources/pgbouncer_user_spec.rb @@ -0,0 +1,48 @@ +require 'chef_helper' +RSpec.describe 'pgbouncer_user' do + let(:runner) { ChefSpec::SoloRunner.new(step_into: ['pgbouncer_user']) } + + context 'create geo' do + let(:chef_run) { runner.converge('test_gitlab_ee::pgbouncer_user_create_geo') } + it 'should create the pgbouncer user' do + expect(chef_run).to create_postgresql_user('pgbouncer-geo').with( + helper: an_instance_of(GeoPgHelper), + username: 'pgbouncer-geo', + password: 'md5fakepassword-geo' + ) + end + + it 'should create the pg_shadow_lookup function' do + postgresql_user = chef_run.postgresql_user('pgbouncer-geo') + expect(postgresql_user).to notify('execute[Add pgbouncer auth function]') + resource = chef_run.execute('Add pgbouncer auth function') + expect(resource.command).to match(%r{^/opt/gitlab/bin/gitlab-geo-psql -d fakedb-geo}) + end + + it 'should not try and recreate the function' do + expect(chef_run).not_to run_execute('Add pgbouncer auth function') + end + end + + context 'create rails' do + let(:chef_run) { runner.converge('test_gitlab_ee::pgbouncer_user_create_rails') } + it 'should create the pgbouncer user' do + expect(chef_run).to create_postgresql_user('pgbouncer-rails').with( + helper: an_instance_of(PgHelper), + username: 'pgbouncer-rails', + password: 'md5fakepassword-rails' + ) + end + + it 'should create the pg_shadow_lookup function' do + postgresql_user = chef_run.postgresql_user('pgbouncer-rails') + expect(postgresql_user).to notify('execute[Add pgbouncer auth function]') + resource = chef_run.execute('Add pgbouncer auth function') + expect(resource.command).to match(%r{^/opt/gitlab/bin/gitlab-psql -d fakedb-rails}) + end + + it 'should not try and recreate the function' do + expect(chef_run).not_to run_execute('Add pgbouncer auth function') + end + end +end diff --git a/spec/chef/cookbooks/gitlab-kas/recipes/gitlab-kas_spec.rb b/spec/chef/cookbooks/gitlab-kas/recipes/gitlab-kas_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIta2FzL3JlY2lwZXMvZ2l0bGFiLWthc19zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab-kas/recipes/gitlab-kas_spec.rb @@ -0,0 +1,508 @@ +require 'chef_helper' + +RSpec.describe 'gitlab-kas' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service env_dir templatesymlink)).converge('gitlab::default') } + let(:gitlab_kas_config_yml) { chef_run_load_yaml_template(chef_run, '/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when application_role is configured' do + before do + stub_gitlab_rb(roles: %w(application_role)) + end + + it 'should be enabled' do + expect(chef_run).to include_recipe('gitlab-kas::enable') + end + end + + context 'with defaults' do + before do + stub_gitlab_rb(external_url: 'https://gitlab.example.com') + end + + it 'creates a default VERSION file and restarts service' do + expect(chef_run).to create_version_file('Create version file for Gitlab KAS').with( + version_file_path: '/var/opt/gitlab/gitlab-kas/VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/gitlab-kas --version' + ) + + expect(chef_run.version_file('Create version file for Gitlab KAS')).to notify('runit_service[gitlab-kas]').to(:restart) + end + + it 'correctly renders the KAS service run file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-kas/run").with_content(%r{--configuration-file /var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml}) + end + + it 'correctly renders the KAS log run file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-kas/log/run").with_content(%r{exec svlogd -tt /var/log/gitlab/gitlab-kas}) + end + + it 'correctly renders the KAS config file' do + expect(gitlab_kas_config_yml).to( + include( + agent: hash_including( + listen: { + network: 'tcp', + address: 'localhost:8150', + websocket: true, + }, + kubernetes_api: { + listen: { + address: 'localhost:8154', + }, + url_path_prefix: '/' + } + ), + observability: { + logging: { + level: 'info', + }, + usage_reporting_period: '60s' + }, + private_api: { + listen: { + address: "localhost:8155", + authentication_secret_file: "/var/opt/gitlab/gitlab-kas/private_api_authentication_secret_file", + network: "tcp" + }, + } + ) + ) + end + + it 'correctly renders the KAS authentication secret files' do + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-kas/authentication_secret_file").with_content { |content| Base64.strict_decode64(content).size == 32 } + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-kas/private_api_authentication_secret_file").with_content { |content| Base64.strict_decode64(content).size == 32 } + end + + it 'sets OWN_PRIVATE_API_URL and SSL_CERT_DIR' do + expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-kas/env/OWN_PRIVATE_API_URL').with_content('grpc://localhost:8155') + expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-kas/env/SSL_CERT_DIR').with_content('/opt/gitlab/embedded/ssl/certs/') + end + end + + context 'with user settings' do + let(:api_secret_key) { Base64.strict_encode64('1' * 32) } + let(:private_api_secret_key) { Base64.strict_encode64('2' * 32) } + + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_kas: { + api_secret_key: api_secret_key, + private_api_secret_key: private_api_secret_key, + listen_address: 'localhost:5006', + listen_websocket: false, + metrics_usage_reporting_period: '120', + sentry_dsn: 'https://my_key:my_secret@sentry.io/test_project', + sentry_environment: 'production', + log_level: 'debug', + } + ) + end + + it 'correctly renders the KAS service run file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-kas/run").with_content(%r{--configuration-file /var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml}) + end + + it 'correctly renders the KAS log run file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-kas/log/run").with_content(%r{exec svlogd -tt /var/log/gitlab/gitlab-kas}) + end + + it 'correctly renders the KAS config file' do + expect(gitlab_kas_config_yml).to( + include( + agent: hash_including( + listen: { + network: 'tcp', + address: 'localhost:5006', + websocket: false + } + ), + observability: { + logging: { + level: 'debug' + }, + usage_reporting_period: '120s', + sentry: { + dsn: 'https://my_key:my_secret@sentry.io/test_project', + environment: 'production' + } + } + ) + ) + end + + it 'correctly renders the KAS authentication secret files' do + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-kas/authentication_secret_file").with_content(api_secret_key) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-kas/private_api_authentication_secret_file").with_content(private_api_secret_key) + end + end + + describe 'gitlab.yml configuration' do + let(:gitlab_yml) { chef_run_load_yaml_template(chef_run, '/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } + + context 'with defaults' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com' + ) + end + + it 'renders gitlab_kas enabled with default URLs in config/gitlab.yml' do + expect(gitlab_yml[:production][:gitlab_kas]).to include( + enabled: true, + external_url: 'wss://gitlab.example.com/-/kubernetes-agent/', + internal_url: 'grpc://localhost:8153', + external_k8s_proxy_url: 'https://gitlab.example.com/-/kubernetes-agent/k8s-proxy/' + ) + end + end + + context 'when not https' do + before do + stub_gitlab_rb( + external_url: 'http://gitlab.example.com' + ) + end + + it 'has exernal URL with scheme `ws` instead of `wss`' do + expect(gitlab_yml[:production][:gitlab_kas]).to include( + external_url: 'ws://gitlab.example.com/-/kubernetes-agent/' + ) + end + end + + context 'with custom listen addresses' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_kas: { + enable: true, + listen_address: 'custom-address:1234', + internal_api_listen_address: 'custom-api-address:9999' + } + ) + end + + it 'derives the external URLs from the top level external URL, and the internal URL from the listen address' do + expect(gitlab_yml[:production][:gitlab_kas]).to include( + enabled: true, + external_url: 'wss://gitlab.example.com/-/kubernetes-agent/', + internal_url: 'grpc://custom-api-address:9999', + external_k8s_proxy_url: 'https://gitlab.example.com/-/kubernetes-agent/k8s-proxy/' + ) + end + end + + context 'with explicitly configured URLs' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_rails: { + gitlab_kas_external_url: 'wss://kas.example.com', + gitlab_kas_internal_url: 'grpc://kas.internal', + gitlab_kas_external_k8s_proxy_url: 'https://kas.example.com/k8s-proxy' + } + ) + end + + it 'uses the explicitly configured URL' do + expect(gitlab_yml[:production][:gitlab_kas]).to include( + external_url: 'wss://kas.example.com', + internal_url: 'grpc://kas.internal', + external_k8s_proxy_url: 'https://kas.example.com/k8s-proxy' + ) + end + end + end + + describe 'logrotate settings' do + context 'default values' do + it_behaves_like 'configured logrotate service', 'gitlab-kas', 'git', 'git' + end + + context 'specified username and group' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'configured logrotate service', 'gitlab-kas', 'foo', 'bar' + end + end + + describe 'redis config' do + context 'when there is a password' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_kas: { enable: true }, + gitlab_rails: { redis_password: 'the-password' } + ) + end + + it 'writes password_file into the kas config' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| + kas_redis_cfg = YAML.safe_load(content)['redis'] + expect(kas_redis_cfg).to( + include( + 'password_file' => '/var/opt/gitlab/gitlab-kas/redis_password_file' + ) + ) + } + end + it 'renders the password file' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/redis_password_file').with_content('the-password') + end + end + + context 'when there is no password' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_kas: { enable: true } + ) + end + + it 'does not write password_file into the config' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| + kas_cfg = YAML.safe_load(content) + expect(kas_cfg['redis']).not_to include('password_file') + } + end + + it 'renders no password file' do + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-kas/redis_password_file") + end + end + + context 'without sentinel host only' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_kas: { + enable: true + }, + gitlab_rails: { + redis_host: 'the-host' + } + ) + end + + it 'renders a single server configuration in to the kas config' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| + kas_redis_cfg = YAML.safe_load(content)['redis'] + expect(kas_redis_cfg).to( + include( + 'network' => 'tcp', + 'server' => { + 'address' => 'the-host:6379' + } + ) + ) + expect(kas_redis_cfg).not_to(include('sentinel')) + } + end + end + + context 'without sentinel host and port' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_rails: { + redis_host: 'the-host', + redis_port: 12345, + } + ) + end + + it 'renders a single server configuration in to the kas config' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| + kas_redis_cfg = YAML.safe_load(content)['redis'] + expect(kas_redis_cfg).to( + include( + 'network' => 'tcp', + 'server' => { + 'address' => 'the-host:12345' + } + ) + ) + expect(kas_redis_cfg).not_to(include('sentinel')) + } + end + end + + context 'without sentinel but with tls enabled' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_rails: { + redis_host: 'the-host', + redis_port: 12345, + redis_ssl: true, + } + ) + end + + it 'renders a configuration with tls enabled in to the kas config' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| + kas_redis_cfg = YAML.safe_load(content)['redis'] + expect(kas_redis_cfg).to( + include( + 'network' => 'tcp', + 'tls' => { + 'enabled' => true + }, + 'server' => { + 'address' => 'the-host:12345' + } + ) + ) + } + end + end + + context 'with sentinel' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_rails: { + redis_sentinels: [ + { host: 'a', port: 1 }, + { host: 'b', port: 2 }, + { host: 'c' } + ] + }, + redis: { + master_name: 'example-redis' + } + ) + end + + it 'renders a single server configuration in to the kas config' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| + kas_redis_cfg = YAML.safe_load(content)['redis'] + expect(kas_redis_cfg).to( + include( + 'network' => 'tcp', + 'sentinel' => { + 'master_name' => 'example-redis', + 'addresses' => [ + 'a:1', + 'b:2', + 'c:6379' + ] + } + ) + ) + expect(kas_redis_cfg).not_to(include('server')) + } + end + end + end + + describe 'TLS listen config' do + context 'when all certificates and keys are defined' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_kas: { + enable: true, + listen_websocket: false, + certificate_file: '/path/to/cert.pem', + key_file: '/path/to/key.pem', + internal_api_certificate_file: '/path/to/internal-api-cert.pem', + internal_api_key_file: '/path/to/internal-api-key.pem', + kubernetes_api_certificate_file: '/path/to/kubernetes-api-cert.pem', + kubernetes_api_key_file: '/path/to/kubernetes-api-key.pem', + private_api_certificate_file: '/path/to/private-api-cert.pem', + private_api_key_file: '/path/to/private-api-key.pem' + } + ) + end + + it 'correctly renders the KAS config file' do + expect(gitlab_kas_config_yml).to( + include( + agent: hash_including( + listen: hash_including( + certificate_file: '/path/to/cert.pem', + key_file: '/path/to/key.pem' + ), + kubernetes_api: hash_including( + listen: hash_including( + certificate_file: '/path/to/kubernetes-api-cert.pem', + key_file: '/path/to/kubernetes-api-key.pem' + ) + ) + ), + api: { + listen: hash_including( + certificate_file: '/path/to/internal-api-cert.pem', + key_file: '/path/to/internal-api-key.pem' + ), + }, + private_api: { + listen: hash_including( + certificate_file: '/path/to/private-api-cert.pem', + key_file: '/path/to/private-api-key.pem' + ) + } + ) + ) + end + end + + context 'when certificate/key bundles are not correctly defined' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_kas: { + enable: true, + certificate_file: '/path/to/file.pem', + internal_api_certificate_file: '/path/to/file.pem', + kubernetes_api_key_file: '/path/to/file.pem', + private_api_key_file: '/path/to/file.pem' + } + ) + end + + it 'renders no certificate or key configuration' do + expect(gitlab_kas_config_yml).not_to(include('/path/to/file.pem')) + end + end + + context 'when the certificate/key bundle is defined and websocket tunneling is enabled' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + gitlab_kas: { + enable: true, + listen_websocket: true, + certificate_file: '/path/to/cert.pem', + key_file: '/path/to/key.pem', + } + ) + end + + it 'logs a warning' do + expect(chef_run).to run_ruby_block('websocket TLS termination') + end + end + end + + def chef_run_load_yaml_template(chef_run, path) + template = chef_run.template(path) + file_content = ChefSpec::Renderer.new(chef_run, template).content + YAML.safe_load(file_content, [], [], true, symbolize_names: true) + end +end diff --git a/spec/chef/cookbooks/gitlab-pages/recipes/gitlab-pages_spec.rb b/spec/chef/cookbooks/gitlab-pages/recipes/gitlab-pages_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItcGFnZXMvcmVjaXBlcy9naXRsYWItcGFnZXNfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab-pages/recipes/gitlab-pages_spec.rb @@ -0,0 +1,333 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-pages' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service env_dir)).converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(Etc).to receive(:getpwnam).with('git').and_return(spy('getpwnam spy', uid: 1000, gid: 1000)) + end + + context 'with default values' do + it 'does not include Pages recipe' do + expect(chef_run).not_to include_recipe('gitlab-pages::enable') + expect(chef_run).to include_recipe('gitlab-pages::disable') + end + end + + context 'with Pages enabled' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com' + ) + end + + it 'includes Pages recipe' do + expect(chef_run).to include_recipe('gitlab-pages::enable') + end + + it 'creates a VERSION file and restarts the service' do + expect(chef_run).to create_version_file('Create version file for Gitlab Pages').with( + version_file_path: '/var/opt/gitlab/gitlab-pages/VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/gitlab-pages --version' + ) + + expect(chef_run.version_file('Create version file for Gitlab Pages')).to notify('runit_service[gitlab-pages]').to(:restart) + end + + it 'renders the env dir files' do + expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-pages/env/SSL_CERT_DIR') + .with_content('/opt/gitlab/embedded/ssl/certs') + end + + it 'renders the pages service run file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-config="/var/opt/gitlab/gitlab-pages/gitlab-pages-config"}) + end + + it 'renders the pages log run file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/log/run").with_content(%r{exec svlogd /var/log/gitlab/gitlab-pages}) + end + + it 'deletes old admin.secret file' do + expect(chef_run).to delete_file("/var/opt/gitlab/gitlab-pages/admin.secret") + end + + it 'renders pages config file' do + default_content = <<~EOS + pages-domain=pages.example.com + pages-root=/var/opt/gitlab/gitlab-rails/shared/pages + api-secret-key=/var/opt/gitlab/gitlab-pages/.gitlab_pages_secret + listen-proxy=localhost:8090 + log-format=json + use-http2=true + artifacts-server=https://gitlab.example.com/api/v4 + artifacts-server-timeout=10 + gitlab-server=https://gitlab.example.com + EOS + + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content(default_content) + end + + it 'skips rendering the auth settings when access control is disabled' do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_pages: { + access_control: false, + auth_secret: 'auth_secret' + } + ) + + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content| + expect(content).not_to match(%r{auth-secret=auth_secret}) + } + end + + context 'when access control is enabled' do + context 'when access control secrets are not specified' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_pages: { + access_control: true, + auth_secret: 'auth_secret' + } + ) + + allow(GitlabPages).to receive(:authorize_with_gitlab) { + Gitlab['gitlab_pages']['gitlab_secret'] = 'app_secret' + Gitlab['gitlab_pages']['gitlab_id'] = 'app_id' + } + end + + it 'authorizes Pages with GitLab' do + expect(chef_run).to run_ruby_block('authorize pages with gitlab') + .at_converge_time + expect(chef_run).to run_ruby_block('re-populate GitLab Pages configuration options') + .at_converge_time + expect(GitlabPages).to receive(:authorize_with_gitlab) + + chef_run.ruby_block('authorize pages with gitlab').block.call + chef_run.ruby_block('re-populate GitLab Pages configuration options').block.call + + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content| + expect(content).to match(%r{auth-client-id=app_id}) + expect(content).to match(%r{auth-client-secret=app_secret}) + expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com/auth}) + expect(content).to match(%r{auth-secret=auth_secret}) + } + end + end + + context 'when access control secrets are specified' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_pages: { + access_control: true, + gitlab_id: 'app_id', + gitlab_secret: 'app_secret', + auth_secret: 'auth_secret', + auth_redirect_uri: 'https://projects.pages.example.com/auth', + auth_scope: 'read_api' + } + ) + end + + it 'does not attempt to authorize with GitLab' do + expect(chef_run).not_to run_ruby_block('authorize pages with gitlab') + end + + it 'renders pages config file' do + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content| + expect(content).to match(%r{auth-client-id=app_id}) + expect(content).to match(%r{auth-client-secret=app_secret}) + expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com/auth}) + expect(content).to match(%r{auth-secret=auth_secret}) + expect(content).to match(%r{auth-scope=read_api}) + } + end + end + end + + context 'with custom port' do + before do + stub_gitlab_rb( + pages_external_url: 'https://pages.example.com:8443', + gitlab_pages: { + access_control: true + } + ) + end + + it 'sets the correct port number' do + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content| + expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com:8443/auth}) + } + end + end + + context 'with custom values' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_pages: { + external_http: ['external_pages.example.com', 'localhost:9000'], + external_https: ['external_pages.example.com', 'localhost:9001'], + external_https_proxyv2: ['external_pages.example.com', 'localhost:9002'], + metrics_address: 'localhost:1234', + redirect_http: true, + dir: '/var/opt/gitlab/pages', + cert: '/etc/gitlab/pages.crt', + artifacts_server_url: "https://gitlab.elsewhere.com/api/v5", + artifacts_server_timeout: 60, + status_uri: '/@status', + max_connections: 7500, + max_uri_length: 2048, + propagate_correlation_id: true, + log_format: 'text', + log_verbose: true, + gitlab_id: 'app_id', + gitlab_secret: 'app_secret', + auth_secret: 'auth_secret', + auth_redirect_uri: 'https://projects.pages.example.com/auth', + auth_scope: 'read_api', + access_control: true, + insecure_ciphers: true, + tls_min_version: "tls1.0", + tls_max_version: "tls1.2", + sentry_enabled: true, + sentry_dsn: 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40', + sentry_environment: 'production', + headers: ['X-XSS-Protection: 1; mode=block', 'X-Content-Type-Options: nosniff', 'Test: Header'], + server_shutdown_timeout: "30s", + gitlab_client_http_timeout: "10s", + gitlab_client_jwt_expiry: "30s", + zip_cache_expiration: "120s", + zip_cache_cleanup: "1m", + zip_cache_refresh: "60s", + zip_open_timeout: "45s", + zip_http_client_timeout: "30m", + internal_gitlab_server: "https://int.gitlab.example.com", + gitlab_cache_expiry: "1m", + gitlab_cache_refresh: "500ms", + gitlab_cache_cleanup: "100ms", + gitlab_retrieval_timeout: "3s", + gitlab_retrieval_interval: "500ms", + gitlab_retrieval_retries: 5, + rate_limit_source_ip: 100, + rate_limit_source_ip_burst: 50, + rate_limit_domain: 1000, + rate_limit_domain_burst: 10000, + rate_limit_tls_source_ip: 101, + rate_limit_tls_source_ip_burst: 51, + rate_limit_tls_domain: 1001, + rate_limit_tls_domain_burst: 10001, + enable_disk: true, + env: { + GITLAB_CONTINUOUS_PROFILING: "stackdriver?service=gitlab-pages", + }, + } + ) + end + + it 'renders pages config file in the specified directory' do + expected_content = <<~EOS + pages-domain=pages.example.com + pages-root=/var/opt/gitlab/gitlab-rails/shared/pages + api-secret-key=/var/opt/gitlab/pages/.gitlab_pages_secret + auth-client-id=app_id + auth-redirect-uri=https://projects.pages.example.com/auth + auth-client-secret=app_secret + auth-secret=auth_secret + auth-scope=read_api + zip-cache-expiration=120s + zip-cache-cleanup=1m + zip-cache-refresh=60s + zip-open-timeout=45s + zip-http-client-timeout=30m + listen-proxy=localhost:8090 + metrics-address=localhost:1234 + pages-status=/@status + max-conns=7500 + max-uri-length=2048 + propagate-correlation-id=true + log-format=text + log-verbose + sentry-dsn=https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40 + sentry-environment=production + redirect-http=true + use-http2=true + artifacts-server=https://gitlab.elsewhere.com/api/v5 + artifacts-server-timeout=60 + gitlab-server=https://gitlab.example.com + internal-gitlab-server=https://int.gitlab.example.com + insecure-ciphers + tls-min-version=tls1.0 + tls-max-version=tls1.2 + server-shutdown-timeout=30s + gitlab-client-http-timeout=10s + gitlab-client-jwt-expiry=30s + listen-http=external_pages.example.com,localhost:9000 + listen-https=external_pages.example.com,localhost:9001 + listen-https-proxyv2=external_pages.example.com,localhost:9002 + root-cert=/etc/gitlab/pages.crt + root-key=/etc/gitlab/ssl/pages.example.com.key + gitlab-cache-expiry=1m + gitlab-cache-refresh=500ms + gitlab-cache-cleanup=100ms + gitlab-retrieval-timeout=3s + gitlab-retrieval-timeout=500ms + gitlab-retrieval-retries=5 + enable-disk=true + rate-limit-source-ip=100 + rate-limit-source-ip-burst=50 + rate-limit-domain=1000 + rate-limit-domain-burst=10000 + rate-limit-tls-source-ip=101 + rate-limit-tls-source-ip-burst=51 + rate-limit-tls-domain=1001 + rate-limit-tls-domain-burst=10001 + EOS + + expect(chef_run).to render_file("/var/opt/gitlab/pages/gitlab-pages-config").with_content(expected_content) + end + + it 'specifies headers as arguments in the run file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-header="X-XSS-Protection: 1; mode=block"}) + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-header="X-Content-Type-Options: nosniff"}) + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-header="Test: Header"}) + end + + it 'renders the env dir files' do + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-pages/env/GITLAB_CONTINUOUS_PROFILING") + .with_content('stackdriver?service=gitlab-pages') + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-pages/env/SSL_CERT_DIR") + .with_content('/opt/gitlab/embedded/ssl/certs') + end + end + + describe 'logrotate settings' do + context 'default values' do + it_behaves_like 'configured logrotate service', 'gitlab-pages', 'git', 'git' + end + + context 'specified username and group' do + before do + stub_gitlab_rb( + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'configured logrotate service', 'gitlab-pages', 'foo', 'bar' + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/account_helper_spec.rb b/spec/chef/cookbooks/gitlab/libraries/account_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2FjY291bnRfaGVscGVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/account_helper_spec.rb @@ -0,0 +1,17 @@ +require 'chef_helper' + +RSpec.describe AccountHelper do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + it 'returns a list of users' do + expect(AccountHelper.new(chef_run.node).users).to eq( + %w(git gitlab-www gitlab-redis gitlab-psql mattermost registry gitlab-prometheus gitlab-consul) + ) + end + + it 'returns a list of groups' do + expect(AccountHelper.new(chef_run.node).groups).to eq( + %w(git gitlab-www gitlab-redis gitlab-psql mattermost registry gitlab-consul gitlab-prometheus) + ) + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/helpers/base_pg_helper_spec.rb b/spec/chef/cookbooks/gitlab/libraries/helpers/base_pg_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2hlbHBlcnMvYmFzZV9wZ19oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/helpers/base_pg_helper_spec.rb @@ -0,0 +1,336 @@ +require 'chef_helper' + +RSpec.describe BasePgHelper do + cached(:chef_run) { converge_config } + let(:node) { chef_run.node } + subject { described_class.new(node) } + + before do + allow(subject).to receive(:service_name) { 'postgresql' } + allow(subject).to receive(:service_cmd) { 'gitlab-psql' } + end + + context 'when handling ssl configuration' do + after do + chef_run.node.rm_normal('postgresql', 'dir') + chef_run.node.rm_normal('postgresql', 'ssl_cert_file') + end + + describe 'ssl_cert_file' do + it 'is configured with an absolute path' do + absolute_path = "/my/absolute/path" + config_path = "MrBoots" + chef_run.node.normal['postgresql']['ssl_cert_file'] = absolute_path + chef_run.node.normal['postgresql']['dir'] = config_path + expect(subject.ssl_cert_file).to eq(absolute_path) + end + + it 'is configured with a relative path' do + relative_path = "my/relative/path" + config_path = "/MrBoots" + chef_run.node.normal['postgresql']['ssl_cert_file'] = relative_path + chef_run.node.normal['postgresql']['dir'] = config_path + expect(subject.ssl_cert_file).to eq(File.join(config_path, 'data', relative_path)) + end + end + + describe 'ssl_key_file' do + it 'is configured with an absolute path' do + absolute_path = "/my/absolute/path" + config_path = "MrBoots" + chef_run.node.normal['postgresql']['ssl_key_file'] = absolute_path + chef_run.node.normal['postgresql']['dir'] = config_path + expect(subject.ssl_key_file).to eq(absolute_path) + end + + it 'is configured with a relative path' do + relative_path = "my/relative/path" + config_path = "/MrBoots" + chef_run.node.normal['postgresql']['ssl_key_file'] = relative_path + chef_run.node.normal['postgresql']['dir'] = config_path + expect(subject.ssl_key_file).to eq(File.join(config_path, 'data', relative_path)) + end + end + end + + describe '#database_exists?' do + it 'calls out to psql_cmd' do + expect(subject).to receive(:psql_cmd).with( + [ + "-d 'template1'", + "-c 'select datname from pg_database' -A", + '| grep -x example' + ]) + subject.database_exists?('example') + end + + context 'database present' do + it 'truthy' do + allow(subject).to receive(:psql_cmd) { true } + expect(subject.database_exists?('example')).to be_truthy + end + + it 'falsey' do + allow(subject).to receive(:psql_cmd) { false } + expect(subject.database_exists?('example')).to be_falsey + end + end + end + + describe '#extension_enabled?' do + it 'will check with psql_cmd when database present' do + expect(subject).to receive(:psql_cmd).with( + [ + "-d 'database'", + "-c 'select extname from pg_extension' -A", + '| grep -x extension' + ]) + subject.extension_enabled?('extension', 'database') + end + + context 'extension present' do + it 'truthy' do + allow(subject).to receive(:psql_cmd) { true } + expect(subject.extension_enabled?('extension', 'database')).to be_truthy + end + + it 'falsey' do + allow(subject).to receive(:psql_cmd) { false } + expect(subject.extension_enabled?('extension', 'database')).to be_falsey + end + end + end + + describe '#extension_can_be_enabled?' do + before do + allow(subject).to receive(:is_running?).and_return(true) + allow(subject).to receive(:is_standby?).and_return(false) + allow(subject).to receive(:extension_exists?).and_return(true) + allow(subject).to receive(:database_exists?).and_return(true) + allow(subject).to receive(:extension_enabled?).and_return(false) + end + + it 'can be enabled' do + expect(subject.extension_can_be_enabled?('extension', 'db')).to be_truthy + end + + it 'needs to be running' do + allow(subject).to receive(:is_running?).and_return(false) + expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey + end + + it 'cannot be done on a slave' do + allow(subject).to receive(:is_standby?).and_return(true) + expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey + end + + it 'needs to have the extension available' do + allow(subject).to receive(:extension_exists?).and_return(false) + expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey + end + + it 'needs the database to load the extension into' do + allow(subject).to receive(:database_exists?).and_return(false) + expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey + end + + it 'should not enable twice' do + allow(subject).to receive(:extension_enabled?).and_return(true) + expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey + end + end + + describe '#user_options' do + before do + result = spy('shellout') + allow(result).to receive(:stdout).and_return("f|f|t|f\n") + allow(subject).to receive(:do_shell_out).and_return(result) + end + + it 'returns hash from query' do + expect(subject.user_options('')).to eq( + { + 'SUPERUSER' => false, + 'CREATEDB' => false, + 'REPLICATION' => true, + 'BYPASSRLS' => false + } + ) + end + end + + describe '#user_options_set?' do + let(:default_options) do + { + 'SUPERUSER' => false, + 'CREATEDB' => false, + 'REPLICATION' => true, + 'BYPASSRLS' => false + } + end + + context 'default user options' do + before do + allow(subject).to receive(:user_options).and_return(default_options) + end + + it 'returns true when no options are asked about' do + expect(subject.user_options_set?('', [])).to be_truthy + end + + it 'returns true when options are set to their defaults' do + expect(subject.user_options_set?('', ['NOSUPERUSER'])).to be_truthy + end + + it 'returns false when options are set away from their defaults' do + expect(subject.user_options_set?('', ['SUPERUSER'])).to be_falsey + end + end + + context 'modified user' do + before do + allow(subject).to receive(:user_options).and_return(default_options.merge({ 'SUPERUSER' => true })) + end + + it 'returns false when options is not what we expect' do + expect(subject.user_options_set?('', ['NOSUPERUSER'])).to be_falsey + end + end + end + + describe '#user_password_match?' do + before do + # user: gitlab pass: test123 + allow(subject).to receive(:user_hashed_password) { 'md5b56573ef0d94cff111898c63ec259f3f' } + end + + it 'returns true when same password is in plain-text' do + expect(subject.user_password_match?('gitlab', 'test123')).to be_truthy + end + + it 'returns true when same password is in MD5 format' do + expect(subject.user_password_match?('gitlab', 'md5b56573ef0d94cff111898c63ec259f3f')).to be_truthy + end + + it 'returns false when wrong password is in plain-text' do + expect(subject.user_password_match?('gitlab', 'wrong')).to be_falsey + end + + it 'returns false when wrong password is in MD5 format' do + expect(subject.user_password_match?('gitlab', 'md5b599de4332636c03a60fca13be1edb5f')).to be_falsey + end + + it 'returns false when password is not supplied' do + expect(subject.user_password_match?('gitlab', nil)).to be_falsey + end + + context 'nil password' do + before do + # user: gitlab pass: unset + allow(subject).to receive(:user_hashed_password) { '' } + end + + it 'returns true when the password is nil' do + expect(subject.user_password_match?('gitlab', nil)).to be_truthy + end + end + end + + describe '#parse_pghash' do + let(:payload) { '{host=127.0.0.1,dbname=gitlabhq_production,port=5432,user=gitlab,"password=foo}bar\"zoo\\cat"}' } + + it 'returns a hash' do + expect(subject.parse_pghash(payload)).to be_a(Hash) + end + + it 'when content is empty still return a hash' do + expect(subject.parse_pghash('')).to be_a(Hash) + expect(subject.parse_pghash('{}')).to be_a(Hash) + end + + it 'returns hash with expected keys' do + hash = subject.parse_pghash(payload) + + expect(hash.keys).to contain_exactly(:host, :dbname, :port, :user, :password) + end + + it 'returns hash with expected values' do + hash = subject.parse_pghash(payload) + + expect(hash.values).to contain_exactly('127.0.0.1', 'gitlabhq_production', '5432', 'gitlab', 'foo}bar"zoo\cat') + end + end + + describe '#is_running?' do + it 'returns true when postgres is running' do + stub_service_success_status('postgresql', true) + + expect(subject.is_running?).to be_truthy + end + + it 'returns false when postgres is not running' do + stub_service_success_status('postgresql', false) + + expect(subject.is_running?).to be_falsey + end + end + + describe '#is_managed_and_offline?' do + it 'returns true when conditions are met' do + chef_run.node.normal['postgresql']['enable'] = true + stub_service_failure_status('postgresql', true) + + expect(subject.is_managed_and_offline?).to be_truthy + end + + it 'returns false when conditions are not met' do + chef_run.node.normal['postgresql']['enable'] = true + stub_service_failure_status('postgresql', false) + + expect(subject.is_managed_and_offline?).to be_falsey + + chef_run.node.normal['postgresql']['enable'] = false + stub_service_failure_status('postgresql', false) + + expect(subject.is_managed_and_offline?).to be_falsey + + stub_service_failure_status('postgresql', true) + expect(subject.is_managed_and_offline?).to be_falsey + end + end + + describe '#is_standby?' do + let(:recovery_files) { %w(recovery.conf recovery.signal standby.signal) } + + it 'returns true for a standby instance' do + recovery_files.each do |f| + allow(File).to receive(:exist?) + .with("/var/opt/gitlab/postgresql/data/#{f}").and_return(true) + end + + expect(subject.is_standby?).to be true + end + + it 'returns false for a primary instance' do + recovery_files.each do |f| + allow(File).to receive(:exist?) + .with("/var/opt/gitlab/postgresql/data/#{f}").and_return(false) + end + expect(subject.is_standby?).to be false + end + end + + context 'when handling connection information' do + describe '#connection_info' do + it 'raises a not implemented warning' do + expect { subject.connection_info }.to raise_error(NotImplementedError) + end + end + + describe '#build_connection_info' do + it 'returns a ConnectionInfo object' do + expect(subject.build_connection_info('dbname', 'dbhost', 'port', 'pguser')).to be_a(described_class::ConnectionInfo) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/helpers/gitlab_workhorse_helper_spec.rb b/spec/chef/cookbooks/gitlab/libraries/helpers/gitlab_workhorse_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2hlbHBlcnMvZ2l0bGFiX3dvcmtob3JzZV9oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/helpers/gitlab_workhorse_helper_spec.rb @@ -0,0 +1,47 @@ +require 'chef_helper' + +RSpec.describe GitlabWorkhorseHelper do + let(:node) { chef_run.node } + subject { described_class.new(node) } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'workhorse is listening on a tcp socket' do + cached(:chef_run) { converge_config } + let(:tcp_address) { '1.9.8.4' } + + before do + stub_gitlab_rb( + gitlab_workhorse: { + listen_network: 'http', + listen_addr: tcp_address + } + ) + end + + describe '#unix_socket?' do + it 'returns false' do + expect(subject.unix_socket?).to be false + end + end + end + + context 'workhorse is listening on a unix socket' do + cached(:chef_run) { converge_config } + before do + stub_gitlab_rb( + gitlab_workhorse: { + listen_network: 'unix' + } + ) + end + + describe '#unix_socket?' do + it 'returns true' do + expect(subject.unix_socket?).to be true + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/helpers/helper_spec.rb b/spec/chef/cookbooks/gitlab/libraries/helpers/helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2hlbHBlcnMvaGVscGVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/helpers/helper_spec.rb @@ -0,0 +1,254 @@ +require 'chef_helper' + +RSpec.shared_examples 'Postgres helpers' do |service_name, service_cmd, edition| + let(:chef_run) do + ChefSpec::SoloRunner.new do |node| + node.normal['gitlab'][service_name]['dir'] = '/fakedir' + node.normal['package']['install-dir'] = '/fake/install/dir' + end.converge("#{edition}::config") + end + + let(:node) { chef_run.node } + + let!(:helper) do + described_class.new(node) + end + + before do + allow(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/psql --version') { 'YYYYYYYY XXXXXXX' } + end + + it 'is associated with a valid service' do + # this is a validation to make sure we are passing a valid/existing service_name to the shared example + expect(node['gitlab'][service_name].to_h).not_to be_empty + end + + describe '#version' do + it 'returns a valid version' do + expect(helper.version).to eq('XXXXXXX') + end + end + + describe '#database_version' do + it 'returns a valid database_version' do + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with('/fakedir/data/PG_VERSION') { true } + allow(File).to receive(:read).and_call_original + allow(File).to receive(:read).with('/fakedir/data/PG_VERSION') { '111.222' } + allow(Dir).to receive(:glob).with('/fake/install/dir/embedded/postgresql/*') { %w(111.222.18 222.333.11) } + + # We mock this in chef_helper.rb. Override the mock to call the original + allow_any_instance_of(described_class).to receive(:database_version).and_call_original + expect(helper.database_version).to eq('111.222') + end + end + + describe '#extension_exists?' do + it 'returns whether an extension exists' do + expect(helper).to receive(:success?).with("/opt/gitlab/bin/#{service_cmd} -d 'template1' -c 'select name from pg_available_extensions' -A | grep -x myextension") + helper.extension_exists?('myextension') + end + end + + describe '#extension_enabled?' do + it 'returns whether an extension exists' do + expect(helper).to receive(:success?).with("/opt/gitlab/bin/#{service_cmd} -d 'mydatabase' -c 'select extname from pg_extension' -A | grep -x myextension") + helper.extension_enabled?('myextension', 'mydatabase') + end + end + + describe '#database_exists?' do + it 'returns whether a database exists' do + expect(helper).to receive(:success?).with("/opt/gitlab/bin/#{service_cmd} -d 'template1' -c 'select datname from pg_database' -A | grep -x mydatabase") + helper.database_exists?('mydatabase') + end + end +end + +RSpec.describe PgHelper do + include_examples 'Postgres helpers', 'postgresql', 'gitlab-psql', 'gitlab' +end + +RSpec.describe GeoPgHelper do + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb( + 'geo_postgresql' => { + 'dir' => '/fakedir' + } + ) + end + + include_examples 'Postgres helpers', 'geo-postgresql', 'gitlab-geo-psql', 'gitlab-ee' +end + +RSpec.describe OmnibusHelper do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + let(:node) { chef_run.node } + let(:services) do + %w( + puma + sidekiq + gitlab-workhorse + postgresql + redis + nginx + logrotate + prometheus + alertmanager + grafana + node-exporter + redis-exporter + postgres-exporter + gitlab-exporter + gitlab-pages + gitaly + ).freeze + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(File).to receive(:symlink?).with(any_args).and_call_original + end + + context 'service is currently enabled, bootstrapped and is running' do + before do + services.each do |service| + stub_should_notify?(service, true) + end + stub_gitlab_rb(nginx: { enable: true }) + end + + it 'notifies the service' do + expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'disabling a service that was bootstrapped and is currently running' do + before do + services.each do |service| + stub_should_notify?(service, true) + end + stub_gitlab_rb(nginx: { enable: false }) + end + + it 'does not notify the service' do + expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'enabling a service that was bootstrapped but not currently running' do + before do + services.each do |service| + allow(File).to receive(:symlink?).with("/opt/gitlab/service/#{service}").and_return(true) + allow_any_instance_of(OmnibusHelper).to receive(:success?).with("/opt/gitlab/init/#{service} status").and_return(false) + end + stub_gitlab_rb(nginx: { enable: true }) + end + + it 'does not notify the service' do + expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'disabling a service that was bootstrapped but not currently running' do + before do + services.each do |service| + allow(File).to receive(:symlink?).with("/opt/gitlab/service/#{service}").and_return(true) + allow_any_instance_of(OmnibusHelper).to receive(:success?).with("/opt/gitlab/init/#{service} status").and_return(false) + end + stub_gitlab_rb(nginx: { enable: false }) + end + + it 'does not notify the service' do + expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'enabling a service that was disabled but currently running' do + before do + services.each do |service| + allow(File).to receive(:symlink?).with("/opt/gitlab/service/#{service}").and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:success?).with("/opt/gitlab/init/#{service} status").and_return(true) + end + stub_gitlab_rb(nginx: { enable: true }) + end + + it 'does not notify the service' do + expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'disabling a service that was disabled but currently running' do + before do + services.each do |service| + allow(File).to receive(:symlink?).with("/opt/gitlab/service/#{service}").and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:success?).with("/opt/gitlab/init/#{service} status").and_return(true) + end + stub_gitlab_rb(nginx: { enable: false }) + end + + it 'does not notify the service' do + expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'enabling a service that was disabled and not currently running' do + before do + services.each do |service| + stub_should_notify?(service, false) + end + stub_gitlab_rb(nginx: { enable: true }) + end + + it 'does not notify the service' do + expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'disabling a service that was disabled and not currently running' do + before do + services.each do |service| + stub_should_notify?(service, false) + end + stub_gitlab_rb(nginx: { enable: false }) + end + + it 'does not notify the service' do + expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'expected_owner?' do + let(:oh) { OmnibusHelper.new(chef_run.node) } + before do + allow_any_instance_of(OmnibusHelper).to receive(:expected_owner?) + .and_call_original + allow_any_instance_of(OmnibusHelper).to receive( + :expected_user?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive( + :expected_user? + ).with('/tmp/fakefile', 'fakeuser').and_return(true) + allow_any_instance_of(OmnibusHelper).to receive( + :expected_group?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive( + :expected_group? + ).with('/tmp/fakefile', 'fakegroup').and_return(true) + end + + it 'should return false if the group is wrong' do + expect(oh.expected_owner?('/tmp/fakefile', 'fakeuser', 'wronggroup')) + .to be false + end + + it 'should return false if the user is wrong' do + expect(oh.expected_owner?('/tmp/fakefile', 'wronguser', 'fakegroup')) + .to be false + end + + it 'should return true if user and group is correct' do + expect(oh.expected_owner?('/tmp/fakefile', 'fakeuser', 'fakegroup')) + .to be true + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/helpers/pg_status_helper_spec.rb b/spec/chef/cookbooks/gitlab/libraries/helpers/pg_status_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2hlbHBlcnMvcGdfc3RhdHVzX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/helpers/pg_status_helper_spec.rb @@ -0,0 +1,133 @@ +require 'chef_helper' + +RSpec.describe PgStatusHelper do + cached(:chef_run) { converge_config } + let!(:node) { chef_run.node } + let!(:helper) { PgHelper.new(node) } + let(:connection_info) { helper.build_connection_info('dbname', 'dbhost', 'port', 'pguser') } + + context 'when checking status' do + subject { described_class.new(connection_info, node) } + + describe '#service_checks_exhausted?' do + it 'returns true when there are zero remaining service checks' do + allow(subject).to receive(:remaining_service_checks).and_return(0) + expect(subject.service_checks_exhausted?).to be true + end + + it 'returns false when more service checks are allowed' do + allow(subject).to receive(:remaining_service_checks).and_return(1) + expect(subject.service_checks_exhausted?).to be false + end + end + + describe '#ready?' do + it 'raises no warnings when Postgres is accepting connections' do + allow(subject).to receive(:accepting_connections?).and_return(true) + expect(subject.ready?).to be true + end + + it 'raises a warning when Postgres is not responding' do + allow(subject).to receive(:not_responding?).and_return(true) + allow(subject).to receive(:remaining_service_checks).and_return(0) + expect { subject.ready? }.to raise_error(RuntimeError, 'PostgreSQL did not respond before service checks were exhausted') + end + + it 'raises a warning when Postgres gets invalid connection parameters' do + allow(subject).to receive(:invalid_connection_parameters?).and_return(true) + expect { subject.ready? }.to raise_error(RuntimeError, 'PostgreSQL is not receiving the correct connection parameters') + end + + it 'raises a warning when all service checks are exhausted' do + allow(subject).to receive(:service_checks_exhausted?).and_return(true) + expect { subject.ready? }.to raise_error(RuntimeError, 'Exhausted service checks and database is still not available') + end + end + + context 'when invoking pg_isready' do + using RSpec::Parameterized::TableSyntax + + where(:exit_code, :accepting, :rejecting, :not_responding, :invalid_parameters) do + 0 | true | false | false | false + 1 | false | true | false | false + 2 | false | false | true | false + 3 | false | false | false | true + end + + with_them do + before do + result = spy('shellout') + allow(subject).to receive(:do_shell_out).and_return(result) + allow(result).to receive(:exitstatus).and_return(exit_code) + end + + describe '#service_state' do + it 'returns the expected exit code' do + expect(subject.service_state).to eq(exit_code) + end + end + + describe '#accepting_connections?' do + it 'returns the expected boolean' do + expect(subject.accepting_connections?).to be accepting + end + end + + describe '#rejecting_connections?' do + it 'returns the expected boolean' do + expect(subject.rejecting_connections?).to be rejecting + end + end + + describe '#not_responding?' do + it 'returns the expected boolean' do + expect(subject.not_responding?).to be not_responding + end + end + + describe '#invalid_connection_parameters?' do + it 'returns the expected boolean' do + expect(subject.invalid_connection_parameters?).to be invalid_parameters + end + end + end + end + end + + context 'when all checks use configured defaults' do + subject { described_class.new(connection_info, node) } + + describe '#maximum_service_checks' do + it 'will check 20 times' do + expect(subject.maximum_service_checks).to eq(20) + end + end + + describe '#service_check_interval' do + it 'will check every 5 seconds' do + expect(subject.service_check_interval).to eq(5) + end + end + end + + context 'when checks are are customized' do + let(:the_answer) { 424242 } + before do + chef_run.node.normal['postgresql']['max_service_checks'] = the_answer + chef_run.node.normal['postgresql']['service_check_interval'] = the_answer + @subject = described_class.new(connection_info, node) + end + + describe '#maximum_service_checks' do + it 'will check the configured number of times' do + expect(@subject.maximum_service_checks).to eq(the_answer) + end + end + + describe '#service_check_interval' do + it 'will check at a configured interval' do + expect(@subject.service_check_interval).to eq(the_answer) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/pg_version_spec.rb b/spec/chef/cookbooks/gitlab/libraries/pg_version_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3BnX3ZlcnNpb25fc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/pg_version_spec.rb @@ -0,0 +1,83 @@ +require 'chef_helper' + +RSpec.describe PGVersion do + context '.parse' do + it 'returns a PGVersion class when provided a value' do + expect(PGVersion.parse('string')).to be_a(described_class) + end + + it 'returns nil when not passed a value' do + expect(PGVersion.parse(nil)).to be_nil + end + end + + context 'postgres 9.6' do + it '.valid? validates version strings' do + expect(PGVersion.new('9.6.0').valid?).to be true + expect(PGVersion.new('9.6').valid?).to be true + expect(PGVersion.new('9').valid?).to be false + expect(PGVersion.new('9.6.0.0').valid?).to be false + expect(PGVersion.new('i9').valid?).to be false + expect(PGVersion.new('i9.6').valid?).to be false + expect(PGVersion.new('9.6.0i').valid?).to be false + expect(PGVersion.new('i9.6.0').valid?).to be false + end + + it 'parses the correct version parts from a MAJOR only version' do + version = PGVersion.new('9.6') + + expect(version.major).to eq '9.6' + expect(version.minor).to be_nil + end + + it 'parses the correct major version from a full version' do + version = PGVersion.new('9.6.10') + + expect(version.major).to eq '9.6' + expect(version.minor).to eq '10' + end + end + + context 'postgres 10+' do + it '.valid? validates version strings' do + expect(PGVersion.new('10.0.0').valid?).to be true + expect(PGVersion.new('11.0').valid?).to be true + expect(PGVersion.new('10').valid?).to be true + expect(PGVersion.new('10.0.0.0').valid?).to be false + expect(PGVersion.new('i10').valid?).to be false + expect(PGVersion.new('i11.0').valid?).to be false + expect(PGVersion.new('10.0.0i').valid?).to be false + expect(PGVersion.new('i10.0.0').valid?).to be false + end + + it 'parses the correct version parts from a MAJOR only version' do + version = PGVersion.new('10') + version_new = PGVersion.new('12') + + expect(version.major).to eq '10' + expect(version.minor).to be_nil + expect(version_new.major).to eq '12' + expect(version_new.minor).to be_nil + end + + it 'parses the correct major version from a full version' do + version = PGVersion.new('10.5') + version_new = PGVersion.new('12.122') + + expect(version.major).to eq '10' + expect(version.minor).to eq '5' + expect(version_new.major).to eq '12' + expect(version_new.minor).to eq '122' + end + + it 'ignores patch version' do + version = PGVersion.new('10.5.3') + version_new = PGVersion.new('12.122.3') + + expect(version.major).to eq '10' + expect(version.minor).to eq '5' + expect(version_new.major).to eq '12' + expect(version_new.minor).to eq '122' + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/rails_migration_helper_spec.rb b/spec/chef/cookbooks/gitlab/libraries/rails_migration_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JhaWxzX21pZ3JhdGlvbl9oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/rails_migration_helper_spec.rb @@ -0,0 +1,39 @@ +require 'chef_helper' + +RSpec.describe RailsMigrationHelper do + cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + subject(:helper) { described_class.new(chef_run.node) } + + context '#migrated?' do + it 'returns true if status file exists and content is "0"' do + allow(helper).to receive(:db_migrate_status_file) { File.join(fixture_path, 'migration/successful-migration-status-file') } + + expect(helper.migrated?).to be_truthy + end + + it 'returns false when status file doesnt exist' do + allow(helper).to receive(:db_migrate_status_file) { File.join(fixture_path, 'migration/non-existent-migration') } + + expect(helper.migrated?).to be_falsey + end + + it 'returns false when content in status file is different from "0"' do + allow(helper).to receive(:db_migrate_status_file) { File.join(fixture_path, 'migration/failed-migration-status-file') } + + expect(helper.migrated?).to be_falsey + end + end + + context '#db_migrate_status_file' do + it 'returns a path including status_file_prefix connection_digest and revision' do + fake_connection_digest = '63467bb60aa187d2a6830aa8f1b5cbe0' + fake_revision = 'f9631484e7f' + expected_status_file = "/var/opt/gitlab/gitlab-rails/upgrade-status/db-migrate-#{fake_connection_digest}-#{fake_revision}" + + allow(helper).to receive(:connection_digest) { fake_connection_digest } + allow(helper).to receive(:revision) { fake_revision } + + expect(helper.db_migrate_status_file).to eq(expected_status_file) + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/redis_helper_spec.rb b/spec/chef/cookbooks/gitlab/libraries/redis_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/redis_helper_spec.rb @@ -0,0 +1,251 @@ +require 'chef_helper' + +RSpec.describe RedisHelper do + let(:chef_run) { converge_config } + subject { described_class.new(chef_run.node) } + + context '#redis_params' do + context 'without sentinels' do + before { allow(Gitlab).to receive(:[]).and_call_original } + + it 'returns correct parameters' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_port: 8888, + redis_password: 'mypass' + } + ) + expect(subject.redis_params).to eq(['redis.example.com', 8888, 'mypass']) + expect(subject.redis_params(support_sentinel_groupname: false)).to eq(['redis.example.com', 8888, 'mypass']) + end + end + + context 'with sentinels' do + before { allow(Gitlab).to receive(:[]).and_call_original } + + it 'returns correct parameters' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_port: 8888, + redis_password: 'mypass', + redis_sentinels: [ + { 'host' => 'sentinel1.example.com', 'port' => '12345' }, + { 'host' => 'sentinel2.example.com', 'port' => '12345' } + ] + }, + redis: { + master_name: 'master_from.redis.master_name', + master_password: 'password_from.redis.master_password' + } + ) + expect(subject.redis_params).to eq(['master_from.redis.master_name', 6379, 'password_from.redis.master_password']) + expect(subject.redis_params(support_sentinel_groupname: false)).to eq(['redis.example.com', 8888, 'mypass']) + end + end + end + + context '#redis_url' do + context 'with default configuration' do + it 'returns a unix socket' do + expect(subject.redis_url.to_s).to eq('unix:/var/opt/gitlab/redis/redis.socket') + end + end + + context 'with custom configuration' do + before { allow(Gitlab).to receive(:[]).and_call_original } + + it 'returns a Redis URL when redis_host is defined' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com' + } + ) + + expect(subject.redis_url.to_s).to eq('redis://redis.example.com/') + end + + it 'returns a Redis URL with port when a non default port is defined' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_port: 8888 + } + ) + + expect(subject.redis_url.to_s).to eq('redis://redis.example.com:8888/') + end + + it 'returns a Redis URL with database when specified' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_database: 0 + } + ) + + expect(subject.redis_url.to_s).to eq('redis://redis.example.com/0') + end + + it 'returns a Redis URL with password when specified' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_password: 'mypass' + } + ) + + expect(subject.redis_url.to_s).to eq('redis://:mypass@redis.example.com/') + end + + it 'returns a Redis URL with an encoded password' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_password: '#223' + } + ) + + expect(subject.redis_url.to_s).to eq('redis://:%23223@redis.example.com/') + end + + it 'returns a Redis URL with password, port and database when all specified' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_password: 'mypass', + redis_database: 0, + redis_port: 8888 + } + ) + + expect(subject.redis_url.to_s).to eq('redis://:mypass@redis.example.com:8888/0') + end + + it 'returns an SSL Redis URL with password, port and database when all specified' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_password: 'mypass', + redis_database: 0, + redis_port: 8888, + redis_ssl: true + } + ) + + expect(subject.redis_url.to_s).to eq('rediss://:mypass@redis.example.com:8888/0') + end + end + end + + describe '#running_version' do + let(:redis_cli_output) do + <<~MSG + # Server + redis_version:3.2.12 + redis_git_sha1:00000000 + redis_git_dirty:0 + redis_build_id:e16da30f4a0a7845 + redis_mode:standalone + os:Linux 4.15.0-58-generic x86_64 + MSG + end + + before do + # Un-doing the stub added in chef_helper + allow_any_instance_of(described_class).to receive(:running_version).and_call_original + allow(Gitlab).to receive(:[]).and_call_original + allow(VersionHelper).to receive(:version).with(/redis-cli.*INFO/).and_return(redis_cli_output) + end + + context 'when redis is not running' do + it 'returns nil' do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(false) + + expect(subject.running_version).to be_nil + end + end + + context 'when redis is running' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(true) + end + context 'over socket' do + it 'calls VersionHelper.version with correct arguments' do + expect(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket INFO') + + subject.running_version + end + end + + context 'over TCP' do + before do + stub_gitlab_rb( + redis: { + bind: '0.0.0.0', + port: 6379 + } + ) + end + + it 'calls VersionHelper.version with correct arguments' do + expect(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 6379 INFO') + + subject.running_version + end + + context 'with a Redis password specified' do + before do + stub_gitlab_rb( + redis: { + bind: '0.0.0.0', + port: 6379, + password: 'toomanysecrets' + } + ) + end + + it 'it passes password to the command' do + expect(VersionHelper).to receive(:version).with("/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 6379 -a 'toomanysecrets' INFO") + + subject.running_version + end + end + end + + it 'parses version from redis-cli output properly' do + expect(subject.running_version).to eq('3.2.12') + end + end + end + + describe '#installed_version' do + let(:redis_server_output) { 'Redis server v=3.2.12 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=e16da30f4a0a7845' } + + before do + # Un-doing the stub added in chef_helper + allow_any_instance_of(described_class).to receive(:installed_version).and_call_original + allow(Gitlab).to receive(:[]).and_call_original + allow(VersionHelper).to receive(:version).with(/redis-server --version/).and_return(redis_server_output) + end + + context 'when redis is not running' do + it 'returns nil' do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(false) + + expect(subject.installed_version).to be_nil + end + end + + context 'when redis is running' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(true) + end + + it 'parses redis-server output properly' do + expect(subject.installed_version).to eq('3.2.12') + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb b/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb @@ -0,0 +1,210 @@ +# This spec is to test the Redis helper and whether the values parsed +# are the ones we expect +require 'chef_helper' + +RSpec.describe 'Redis' do + let(:chef_run) { converge_config(is_ee: true) } + let(:node) { chef_run.node } + subject { ::Redis } + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context '.parse_variables' do + it 'delegates to parse_redis_settings' do + expect(subject).to receive(:parse_redis_settings) + + subject.parse_variables + end + end + + context '.parse_redis_settings' do + context 'when no customization is made' do + it 'keeps unixsocket' do + expect(node['gitlab']['gitlab-rails']['unixsocket']).not_to eq false + + subject.parse_redis_settings + end + end + + context 'within redis host and port synchronization with gitlab_rails' do + let(:redis_host) { '1.2.3.4' } + let(:redis_port) { 6370 } + + context 'when not using sentinels' do + before do + stub_gitlab_rb( + redis: { + bind: redis_host, + port: redis_port + } + ) + end + + it 'disables unix socket when redis tcp params are defined' do + expect(node['redis']['unixsocket']).to eq false + + subject.parse_redis_settings + end + + it 'expects redis_host to match bind value from redis' do + expect(node['gitlab']['gitlab-rails']['redis_host']).to eq redis_host + + subject.parse_redis_settings + end + + it 'expects redis_port to match port value from redis' do + expect(node['gitlab']['gitlab-rails']['redis_port']).to eq redis_port + + subject.parse_redis_settings + end + end + + context 'when using sentinels' do + let(:master_name) { 'gitlabredis' } + let(:master_pass) { 'PASSWORD' } + + before do + stub_gitlab_rb( + redis: { + bind: redis_host, + port: redis_port, + master_name: master_name, + master_password: master_pass + }, + gitlab_rails: { + redis_sentinels: [ + { host: '1.2.3.4', port: '26379' } + ] + } + ) + end + + it 'disables unix socket when sentinel params are defined' do + expect(node['redis']['unixsocket']).to eq false + + subject.parse_redis_settings + end + end + + context 'when with redis_replica_role enabled' do + before do + stub_gitlab_rb( + redis_replica_role: { + enable: true + }, + redis: { + master_ip: '10.0.0.0', + master_port: 6379, + master_password: 'PASSWORD' + } + ) + end + + it 'defined redis master as false' do + expect(node['redis']['master']).to eq false + end + end + end + + context 'within redis password and master_password' do + let(:redis_password) { 'PASSWORD' } + + context 'when master_role is enabled' do + before do + stub_gitlab_rb( + redis_master_role: { + enable: true + }, + redis: { + password: redis_password, + master_ip: '10.0.0.0' + } + ) + end + + it 'master_password is autofilled based on redis current password' do + expect(node['redis']['master_password']).to eq redis_password + end + end + + context 'when redis is a replica' do + before do + stub_gitlab_rb( + redis: { + master: false, + password: redis_password, + master_ip: '10.0.0.0' + } + ) + end + + it 'master_password is autofilled based on redis current password' do + expect(node['redis']['master_password']).to eq redis_password + end + end + + context 'when sentinel is enabled' do + before do + stub_gitlab_rb( + sentinel: { + enable: true + }, + redis: { + password: redis_password, + master_ip: '10.0.0.0' + } + ) + end + + it 'master_password is autofilled based on redis current password' do + expect(node['redis']['master_password']).to eq redis_password + end + + context 'announce_ip is defined' do + let(:redis_port) { 6379 } + let(:redis_announce_ip) { '10.10.10.10' } + before do + stub_gitlab_rb( + redis_sentinel_role: { + enable: true, + }, + redis: { + master_password: redis_password, + master_ip: '10.0.0.0', + port: redis_port, + announce_ip: redis_announce_ip + } + ) + end + + it 'Redis announce_port is autofilled based on redis current port' do + expect(node['redis']['announce_port']).to eq redis_port + + subject.parse_redis_settings + end + end + end + + context 'when both password and master_password are present' do + let(:master_password) { 'anotherPASSWORD' } + before do + stub_gitlab_rb( + redis_replica_role: { + enable: true + }, + redis: { + password: redis_password, + master_ip: '10.0.0.0', + master_password: master_password + } + ) + end + + it 'keeps user specified master_password' do + expect(node['redis']['master_password']).to eq master_password + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/redis_uri_spec.rb b/spec/chef/cookbooks/gitlab/libraries/redis_uri_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX3VyaV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/redis_uri_spec.rb @@ -0,0 +1,76 @@ +# This spec is to test the Redis helper and whether the values parsed +# are the ones we expect + +require 'spec_helper' + +RSpec.describe URI::Redis do + subject { URI('redis://localhost') } + + it { is_expected.to be_a(described_class) } + + context '.parse' do + it 'delegates to URI.parse' do + expect(URI).to receive(:parse).with('redis://localhost') + + described_class.parse('redis://localhost') + end + end + + context 'port' do + it 'has a default port' do + expect(subject.default_port).to eq 6379 + end + + it 'outputs default port when none is defined' do + expect(subject.port).to eq subject.default_port + end + + it 'does not include port when it is default port' do + expect(subject.to_s).to eq 'redis://localhost' + end + + it 'includes port when it is different than default' do + subject.port = 6378 + expect(subject.to_s).to eq 'redis://localhost:6378' + end + end + + context 'with password' do + before { subject.password = 'password' } + + it 'allows password to be defined' do + expect(subject.password).to eq 'password' + end + + it 'renders url with password' do + expect(subject.to_s).to eq 'redis://:password@localhost' + end + end + + context 'with non-alphanumeric password' do + let(:password) { "&onBsidv6#XeKFd}=BDDyRrv" } + let(:escaped) { CGI.escape(password) } + + it 'rejects unencoded passwords' do + expect { subject.password = password }.to raise_error(URI::InvalidComponentError) + end + + it 'allows encoded passwords' do + subject.password = escaped + + expect(subject.password).to eq(escaped) + end + + it 'renders url with escaped password' do + subject.password = escaped + + expect(subject.to_s).to eq "redis://:#{escaped}@localhost" + end + end + + context 'without password' do + it 'renders url without authentication division characters' do + expect(subject.to_s).to eq 'redis://localhost' + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/registry_spec.rb b/spec/chef/cookbooks/gitlab/libraries/registry_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZ2lzdHJ5X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/registry_spec.rb @@ -0,0 +1,59 @@ +require 'chef_helper' + +RSpec.describe Registry do + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'registry is disabled' do + before do + stub_gitlab_rb( + registry: { + enabled: false + } + ) + end + + it 'should return false' do + expect(described_class.auto_enable).to be_falsey + end + end + + context 'registry_external_url is set' do + before do + stub_gitlab_rb( + registry_external_url: 'https://registry.example.com' + ) + end + + it 'should return false' do + expect(described_class.auto_enable).to be_falsey + end + end + + context 'lets encrypt is not enabled' do + before do + stub_gitlab_rb( + letsencrypt: { + enable: false + } + ) + end + + it 'should return false' do + expect(described_class.auto_enable).to be_falsey + end + end + + context 'external_url is a relative url' do + before do + stub_gitlab_rb( + external_url: 'https://registry.example.com/path' + ) + end + + it 'should return false' do + expect(described_class.auto_enable).to be_falsey + end + end +end diff --git a/spec/chef/cookbooks/gitlab/libraries/workhorse_spec.rb b/spec/chef/cookbooks/gitlab/libraries/workhorse_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3dvcmtob3JzZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/libraries/workhorse_spec.rb @@ -0,0 +1,125 @@ +# This spec is to test the Workhorse library and whether the values parsed +# are the ones we expect +require 'chef_helper' + +RSpec.describe 'GitlabWorkhorse' do + let(:node) { chef_run.node } + let(:user_socket) { '/where/is/my/ten/mm/socket_now' } + let(:user_sockets_directory) { '/where/is/my/ten/mm/sockets' } + let(:default_sockets_directory) { '/var/opt/gitlab/gitlab-workhorse/sockets' } + let(:default_socket) { '/var/opt/gitlab/gitlab-workhorse/sockets/socket' } + let(:tcp_listen_address) { '1.9.8.4' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context '.parse_variables' do + context 'listening on a tcp socket' do + let(:chef_run) { converge_config } + + before do + stub_gitlab_rb( + gitlab_workhorse: { + listen_network: 'http', + listen_addr: tcp_listen_address + } + ) + end + + it 'uses the user configured TCP listen address' do + expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq(tcp_listen_address) + end + + it 'keeps the sockets_directory as nil' do + expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(nil) + end + end + + context 'listening on a unix socket' do + context 'using default configuration' do + let(:chef_run) { converge_config } + + before do + stub_gitlab_rb( + gitlab_workhorse: { + listen_network: 'unix' + } + ) + end + + it 'uses the default sockets directory' do + expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(default_sockets_directory) + end + + it 'uses the default socket file path' do + expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq(default_socket) + end + end + + context 'only listen_addr is set' do + let(:chef_run) { converge_config } + + before do + stub_gitlab_rb( + gitlab_workhorse: { + listen_network: 'unix', + listen_addr: user_socket + } + ) + end + + it 'uses the user configured listen address' do + expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq(user_socket) + end + + it 'keeps the sockets_directory as nil' do + expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(nil) + end + end + + context 'only sockets_directory is set' do + let(:chef_run) { converge_config } + + before do + stub_gitlab_rb( + gitlab_workhorse: { + listen_network: 'unix', + sockets_directory: user_sockets_directory + } + ) + end + + it 'uses the user configured sockets directory' do + expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(user_sockets_directory) + end + + it 'creates a socket named socket in the user configured sockets directory' do + expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq("#{user_sockets_directory}/socket") + end + end + + context 'listen_addr and sockets_directory are both set' do + let(:chef_run) { converge_config } + + before do + stub_gitlab_rb( + gitlab_workhorse: { + listen_network: 'unix', + listen_addr: user_socket, + sockets_directory: user_sockets_directory + } + ) + end + + it 'uses the user configured sockets directory' do + expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(user_sockets_directory) + end + + it 'creates a socket matching the configured listen_addr' do + expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq(user_socket) + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/add_trusted_certs_spec.rb b/spec/chef/cookbooks/gitlab/recipes/add_trusted_certs_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9hZGRfdHJ1c3RlZF9jZXJ0c19zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/add_trusted_certs_spec.rb @@ -0,0 +1,44 @@ +require 'chef_helper' + +RSpec.describe 'add_trusted_certs recipe' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + let(:cert_helper) { CertificateHelper.new('/etc/gitlab/trusted-certs', '/opt/gitlab/embedded/ssl/certs', '/var/opt/gitlab') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + it 'creates the certificate directories' do + expect(chef_run).to create_directory('/opt/gitlab/embedded/ssl/certs').with(mode: '0755') + expect(chef_run).to create_directory('/etc/gitlab/trusted-certs').with(mode: '0755') + end + + it 'creates a readme file for the managed certificate directory' do + expect(chef_run).to create_file('/opt/gitlab/embedded/ssl/certs/README').with( + mode: '0644', + content: /This directory is managed by omnibus\-gitlab/ + ) + end + + context 'when new trusted certificates have been added' do + before do + allow(cert_helper).to receive(:new_certificate_added?).and_return(true) + allow(CertificateHelper).to receive(:new).and_return(cert_helper) + end + + it 'attempts to link trusted certificates into our certificate chain' do + expect(chef_run).to run_ruby_block('Move existing certs and link to /opt/gitlab/embedded/ssl/certs') + end + end + + context 'when there have been no changes to the trusted certificates directory' do + before do + allow(cert_helper).to receive(:new_certificate_added?).and_return(false) + allow(CertificateHelper).to receive(:new).and_return(cert_helper) + end + + it 'does not attempt to link trusted certificates into our certificate chain' do + expect(chef_run).not_to run_ruby_block('Move existing certs and link to /opt/gitlab/embedded/ssl/certs') + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/config_spec.rb b/spec/chef/cookbooks/gitlab/recipes/config_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9jb25maWdfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/config_spec.rb @@ -0,0 +1,226 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::config' do + cached(:chef_run) { converge_config } + let(:node) { chef_run.node } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + shared_examples 'regular services are disabled' do + it 'disables regular services' do + expect(node['gitlab']['puma']['enable']).to eq false + expect(node['gitlab']['sidekiq']['enable']).to eq false + expect(node['gitlab']['gitlab-workhorse']['enable']).to eq false + expect(node['gitaly']['enable']).to eq false + expect(node['gitlab']['nginx']['enable']).to eq false + expect(node['postgresql']['enable']).to eq false + expect(node['gitlab']['mailroom']['enable']).to eq false + expect(node['monitoring']['gitlab-exporter']['enable']).to eq false + expect(node['monitoring']['postgres-exporter']['enable']).to eq false + expect(node['monitoring']['prometheus']['enable']).to eq false + end + end + + it 'ignores unsupported top level variables' do + allow(IO).to receive(:read).and_call_original + allow(IO).to receive(:read).with('config-test.rb').and_return('abc="top-level"') + Gitlab.from_file('config-test.rb') + + expect(node['gitlab']['abc']).to be_nil + end + + it 'errors on unsupported top level methods' do + allow(IO).to receive(:read).and_call_original + allow(IO).to receive(:read).with('config-test.rb').and_return('abc "top-level"') + expect do + Gitlab.from_file('config-test.rb') + end.to raise_error(Mixlib::Config::UnknownConfigOptionError) + end + + it 'errors on unsupported nested variables' do + allow(IO).to receive(:read).and_call_original + allow(IO).to receive(:read).with('config-test.rb').and_return('abc["def"]["hij"] = "top-level"') + expect do + Gitlab.from_file('config-test.rb') + end.to raise_error(Mixlib::Config::UnknownConfigOptionError) + end + + context 'when gitlab-rails is disabled' do + cached(:chef_run) { converge_config } + before do + stub_gitlab_rb( + gitlab_rails: { + enable: false + } + ) + end + + it 'disables Gitlab components' do + expect(node['gitlab']['puma']['enable']).to eq false + expect(node['gitlab']['sidekiq']['enable']).to eq false + expect(node['gitlab']['gitlab-workhorse']['enable']).to eq false + expect(node['monitoring']['gitlab-exporter']['enable']).to eq false + end + + it 'still leaves other default service enabled' do + expect(node['gitlab']['nginx']['enable']).to eq true + expect(node['postgresql']['enable']).to eq true + expect(node['redis']['enable']).to eq true + expect(node['monitoring']['prometheus']['enable']).to eq true + expect(node['monitoring']['alertmanager']['enable']).to eq true + expect(node['monitoring']['node-exporter']['enable']).to eq true + expect(node['monitoring']['redis-exporter']['enable']).to eq true + expect(node['logrotate']['enable']).to eq true + expect(node['monitoring']['postgres-exporter']['enable']).to eq true + end + end + + context 'with roles' do + context 'when redis_sentinel_role is enabled' do + cached(:chef_run) { converge_config(is_ee: true) } + before do + stub_gitlab_rb( + redis_sentinel_role: { + enable: true + }, + redis: { + master_password: 'PASSWORD', + master_ip: '10.0.0.0' + } + ) + end + + it_behaves_like 'regular services are disabled' + + it 'only sentinel is enabled' do + expect(node['gitlab']['sentinel']['enable']).to eq true + expect(node['redis']['enable']).to eq false + expect(node['monitoring']['redis-exporter']['enable']).to eq false + expect(node['monitoring']['node-exporter']['enable']).to eq true + expect(node['logrotate']['enable']).to eq true + end + + context 'when redis_sentinel_role is enabled with redis_master_role' do + cached(:chef_run) { converge_config(is_ee: true) } + before do + stub_gitlab_rb( + redis_sentinel_role: { + enable: true + }, + redis_master_role: { + enable: true + } + ) + end + + it_behaves_like 'regular services are disabled' + + it 'redis and sentinel are enabled' do + expect(node['gitlab']['sentinel']['enable']).to eq true + expect(node['redis']['enable']).to eq true + expect(node['monitoring']['redis-exporter']['enable']).to eq true + expect(node['monitoring']['node-exporter']['enable']).to eq true + expect(node['logrotate']['enable']).to eq true + end + end + + context 'when redis_sentinel_role is enabled with redis_replica_role' do + cached(:chef_run) { converge_config(is_ee: true) } + before do + stub_gitlab_rb( + redis_sentinel_role: { + enable: true + }, + redis_replica_role: { + enable: true + }, + redis: { + master_ip: '10.0.0.0', + master_port: 6379, + master_password: 'PASSWORD' + } + ) + end + + it_behaves_like 'regular services are disabled' + + it 'only redis is enabled' do + expect(node['gitlab']['sentinel']['enable']).to eq true + expect(node['redis']['enable']).to eq true + expect(node['monitoring']['redis-exporter']['enable']).to eq true + expect(node['monitoring']['node-exporter']['enable']).to eq true + expect(node['logrotate']['enable']).to eq true + end + end + end + + context 'when redis_master_role is enabled' do + cached(:chef_run) { converge_config(is_ee: true) } + before do + stub_gitlab_rb( + redis_master_role: { + enable: true + } + ) + end + + it_behaves_like 'regular services are disabled' + + it 'only redis is enabled' do + expect(node['redis']['enable']).to eq true + expect(node['gitlab']['sentinel']['enable']).to eq false + expect(node['monitoring']['redis-exporter']['enable']).to eq true + expect(node['monitoring']['node-exporter']['enable']).to eq true + expect(node['logrotate']['enable']).to eq true + end + end + + context 'when redis_replica_role is enabled' do + cached(:chef_run) { converge_config(is_ee: true) } + before do + stub_gitlab_rb( + redis_replica_role: { + enable: true + }, + redis: { + master_ip: '10.0.0.0', + master_port: 6379, + master_password: 'PASSWORD' + } + ) + end + + it_behaves_like 'regular services are disabled' + + it 'only redis is enabled' do + expect(node['redis']['enable']).to eq true + expect(node['gitlab']['sentinel']['enable']).to eq false + end + end + + context 'when redis_master_role and redis_replica_role are enabled' do + cached(:chef_run) { converge_config(is_ee: true) } + before do + stub_gitlab_rb( + redis_master_role: { + enable: true + }, + redis_replica_role: { + enable: true + }, + redis: { + master_ip: '10.0.0.0', + master_port: 6379, + master_password: 'PASSWORD' + } + ) + end + + it 'fails with an error' do + expect { chef_run }.to raise_error RuntimeError + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/database_migrations_spec.rb b/spec/chef/cookbooks/gitlab/recipes/database_migrations_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9kYXRhYmFzZV9taWdyYXRpb25zX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/database_migrations_spec.rb @@ -0,0 +1,192 @@ +require 'chef_helper' + +# NOTE: These specs do not verify whether the code actually ran +# Nor whether the resource inside of the recipe was notified correctly. +# At this moment they only verify whether the expected commands are passed +# to the resource block. + +RSpec.describe 'gitlab::database-migrations' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when migration should run' do + before do + stub_default_not_listening?(false) + end + + let(:bash_block) { chef_run.bash('migrate gitlab-rails database') } + let(:migration_block) { chef_run.rails_migration('gitlab-rails') } + + it 'runs the migrations with expected attributes' do + expect(chef_run).to run_rails_migration('gitlab-rails') do |resource| + expect(resource.rake_task).to eq('gitlab:db:configure') + expect(resource.logfile_prefix).to eq('gitlab-rails-db-migrate') + expect(resource.helper).to be_a(RailsMigrationHelper) + end + end + + it 'skips outdated external databases warning by default' do + expect(migration_block).to notify('ruby_block[check remote PG version]').to(:run) + expect(chef_run.ruby_block('check remote PG version').should_skip?(:run)).to be_truthy + end + + context 'using external db' do + before { stub_gitlab_rb(postgresql: { enable: false }) } + + it 'warns about outdated databases' do + allow(GitlabRailsEnvHelper).to receive(:db_version).and_return(11) + + expect(migration_block).to notify('ruby_block[check remote PG version]').to(:run) + expect(chef_run.ruby_block('check remote PG version').should_skip?(:run)).to be_falsey + + chef_run.ruby_block('check remote PG version').block.call + + expect_logged_warning(/Support for PostgreSQL 11 has been removed/) + end + end + + context 'initial root password' do + context 'set via gitlab.rb' do + before do + stub_gitlab_rb( + gitlab_rails: { + initial_root_password: 'foobar' + } + ) + end + + it 'runs DB migration with GITLAB_ROOT_PASSWORD variable set to provided value' do + expect(chef_run).to run_rails_migration('gitlab-rails').with( + environment: { + 'GITLAB_ROOT_PASSWORD' => 'foobar', + } + ) + end + end + + context 'set via env variable' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('GITLAB_ROOT_PASSWORD').and_return('asdf1234@') + end + + it 'runs DB migration with GITLAB_ROOT_PASSWORD variable set to provided value' do + expect(chef_run).to run_rails_migration('gitlab-rails').with( + environment: { + 'GITLAB_ROOT_PASSWORD' => 'asdf1234@', + } + ) + end + end + + context 'set via both env variable and gitlab.rb' do + before do + stub_gitlab_rb( + gitlab_rails: { + initial_root_password: 'foobar' + } + ) + + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('GITLAB_ROOT_PASSWORD').and_return('asdf1234@') + end + + it 'runs DB migration with GITLAB_ROOT_PASSWORD variable set to value provided via env variable' do + expect(chef_run).to run_rails_migration('gitlab-rails').with( + environment: { + 'GITLAB_ROOT_PASSWORD' => 'asdf1234@', + } + ) + end + end + + context 'not set' do + before do + allow(SecretsHelper).to receive(:generate_base64).and_return('LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=') + end + + it 'generates a random root password and runs DB migration with GITLAB_ROOT_PASSWORD set to it' do + expect(chef_run).to run_rails_migration('gitlab-rails').with( + environment: { + 'GITLAB_ROOT_PASSWORD' => 'LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=' + } + ) + end + end + end + + it 'runs with the initial_root_password and initial_shared_runners_registration_token in the environment' do + stub_gitlab_rb( + gitlab_rails: { initial_root_password: '123456789', initial_shared_runners_registration_token: '987654321' } + ) + + expect(chef_run).to run_rails_migration('gitlab-rails').with( + environment: { + 'GITLAB_ROOT_PASSWORD' => '123456789', + 'GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN' => '987654321' + } + ) + end + + context 'initial license file' do + before do + allow(SecretsHelper).to receive(:generate_base64).and_return('LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=') + end + + it 'detects license file from /etc/gitlab' do + allow(Dir).to receive(:glob).and_call_original + allow(Dir).to receive(:glob).with('/etc/gitlab/*.gitlab-license').and_return(%w[/etc/gitlab/company.gitlab-license /etc/gitlab/company2.gitlab-license]) + + expect(chef_run).to run_rails_migration('gitlab-rails').with( + environment: { + 'GITLAB_ROOT_PASSWORD' => 'LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=', + 'GITLAB_LICENSE_FILE' => '/etc/gitlab/company.gitlab-license' + } + ) + end + + it 'license file specified in gitlab.rb gets priority' do + allow(Dir).to receive(:glob).and_call_original + allow(Dir).to receive(:glob).with('/etc/gitlab/*.gitlab-license').and_return(%w[/etc/gitlab/company.gitlab-license /etc/gitlab/company2.gitlab-license]) + + stub_gitlab_rb( + gitlab_rails: { initial_license_file: '/mnt/random.gitlab-license' } + ) + + expect(chef_run).to run_rails_migration('gitlab-rails').with( + environment: { + 'GITLAB_ROOT_PASSWORD' => 'LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=', + 'GITLAB_LICENSE_FILE' => '/mnt/random.gitlab-license' + } + ) + end + + it 'Does not fail if no license file found in /etc/gitlab' do + allow(Dir).to receive(:glob).and_call_original + allow(Dir).to receive(:glob).with('/etc/gitlab/*.gitlab-license').and_return([]) + + expect(chef_run).to run_rails_migration('gitlab-rails').with( + environment: { + 'GITLAB_ROOT_PASSWORD' => 'LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=', + } + ) + end + end + + it 'should notify rails cache clear resource' do + expect(migration_block).to notify( + 'execute[clear the gitlab-rails cache]') + end + end + + context 'with auto_migrate off' do + before { stub_gitlab_rb(gitlab_rails: { auto_migrate: false }) } + + it 'skips running the migrations' do + expect(chef_run).not_to run_rails_migration('gitlab-rails') + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/database_reindexing_disable_spec.rb b/spec/chef/cookbooks/gitlab/recipes/database_reindexing_disable_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9kYXRhYmFzZV9yZWluZGV4aW5nX2Rpc2FibGVfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/database_reindexing_disable_spec.rb @@ -0,0 +1,13 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::database-reindexing' do + let(:chef_run) { converge_config('gitlab::database_reindexing_disable') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + it 'removes the database-reindexing cronjob' do + expect(chef_run).to delete_crond_job('database-reindexing') + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/database_reindexing_enable_spec.rb b/spec/chef/cookbooks/gitlab/recipes/database_reindexing_enable_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9kYXRhYmFzZV9yZWluZGV4aW5nX2VuYWJsZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/database_reindexing_enable_spec.rb @@ -0,0 +1,53 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::database-reindexing' do + let(:chef_run) { converge_config('gitlab::database_reindexing_enable') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'with defaults' do + before do + stub_gitlab_rb(gitlab_rails: { database_reindexing: { enable: true } }) + end + + it 'adds a crond_job with default schedule' do + expect(chef_run).to create_crond_job('database-reindexing').with( + user: "root", + hour: '*', + minute: 0, + month: '*', + day_of_month: '*', + day_of_week: '0,6', + command: "/opt/gitlab/bin/gitlab-rake gitlab:db:reindex" + ) + end + end + + context 'with specific schedule' do + let(:config) do + { + enable: true, + hour: 10, + minute: 5, + month: 3, + day_of_month: 2, + day_of_week: 1 + } + end + + it 'adds a crond_job with the configured schedule' do + stub_gitlab_rb(gitlab_rails: { database_reindexing: config }) + + expect(chef_run).to create_crond_job('database-reindexing').with( + user: "root", + hour: 10, + minute: 5, + month: 3, + day_of_week: 1, + command: "/opt/gitlab/bin/gitlab-rake gitlab:db:reindex" + ) + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/default_spec.rb b/spec/chef/cookbooks/gitlab/recipes/default_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9kZWZhdWx0X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/default_spec.rb @@ -0,0 +1,193 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::default' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + it 'creates the user config directory' do + expect(chef_run).to create_directory('/etc/gitlab').with( + user: 'root', + group: 'root', + mode: '0775' + ) + end + + it 'creates the var opt data config directory' do + expect(chef_run).to create_directory('Create /var/opt/gitlab').with( + path: '/var/opt/gitlab', + user: 'root', + group: 'root', + mode: '0755' + ) + + gitconfig_hash = { + "receive" => ["fsckObjects = true", "advertisePushOptions = true"], + "pack" => ["threads = 1"], + "repack" => ["writeBitmaps = true"], + "transfer" => ["hideRefs=^refs/tmp/", "hideRefs=^refs/keep-around/", "hideRefs=^refs/remotes/"], + "core" => [ + 'alternateRefsCommand="exit 0 #"', + "fsyncObjectFiles = true" + ], + "fetch" => ["writeCommitGraph = true"] + } + + expect(chef_run).to create_template('/opt/gitlab/embedded/etc/gitconfig').with( + variables: { gitconfig: gitconfig_hash } + ) + end + + it 'creates the system gitconfig directory and file' do + stub_gitlab_rb(omnibus_gitconfig: { system: { receive: ["fsckObjects = true", "advertisePushOptions = true"], pack: ["threads = 2"] } }) + + expect(chef_run).to create_directory('/opt/gitlab/embedded/etc').with( + user: 'root', + group: 'root', + mode: '0755' + ) + + gitconfig_hash = { + "receive" => ["fsckObjects = true", "advertisePushOptions = true"], + "pack" => ["threads = 2"], + "repack" => ["writeBitmaps = true"], + "transfer" => ["hideRefs=^refs/tmp/", "hideRefs=^refs/keep-around/", "hideRefs=^refs/remotes/"], + "core" => [ + 'alternateRefsCommand="exit 0 #"', + "fsyncObjectFiles = true" + ], + "fetch" => ["writeCommitGraph = true"] + } + + expect(chef_run).to create_template('/opt/gitlab/embedded/etc/gitconfig').with( + source: 'gitconfig-system.erb', + variables: { gitconfig: gitconfig_hash }, + mode: 0755 + ) + end + + context 'with logrotate' do + it 'runs logrotate directory and configuration recipe by default' do + expect(chef_run).to include_recipe('logrotate::folders_and_configs') + end + + it 'runs logrotate directory and configuration recipe when logrotate is disabled' do + stub_gitlab_rb(logrotate: { enable: false }) + + expect(chef_run).to include_recipe('logrotate::folders_and_configs') + end + end + + context 'when manage_etc directory management is disabled' do + before { stub_gitlab_rb(manage_storage_directories: { enable: true, manage_etc: false }) } + + it 'does not create the user config directory' do + expect(chef_run).not_to create_directory('/etc/gitlab') + end + end + + context 'prometheus is enabled by default' do + it 'includes the prometheus recipe' do + expect(chef_run).to include_recipe('monitoring::prometheus') + expect(chef_run).not_to include_recipe('monitoring::prometheus_disable') + end + end + + context 'with prometheus disabled' do + before { stub_gitlab_rb(prometheus: { enable: false }) } + + it 'includes the prometheus_disable recipe' do + expect(chef_run).to include_recipe('monitoring::prometheus_disable') + expect(chef_run).not_to include_recipe('monitoring::prometheus') + end + end + + context 'with database reindexing and LetsEncrypt auto-renew disabled' do + it 'disables crond' do + expect(chef_run).to include_recipe('crond::disable') + expect(chef_run).not_to include_recipe('crond::enable') + end + end + + context 'with database reindexing enabled' do + before do + stub_gitlab_rb(gitlab_rails: { database_reindexing: { enable: true } }) + end + + it 'enables crond' do + expect(chef_run).to include_recipe('crond::enable') + expect(chef_run).not_to include_recipe('crond::disable') + end + end + + context 'with LetsEncrypt auto-renew enabled' do + before do + # Registry will be auto-enabled if LetsEncrypt is enabled + stub_gitlab_rb(external_url: 'http://gitlab.example.com', + registry: { enable: false }, + letsencrypt: { enable: true, auto_renew: true }) + end + + it 'enables crond' do + expect(chef_run).to include_recipe('crond::enable') + expect(chef_run).not_to include_recipe('crond::disable') + end + end + + context 'with sidekiq exporter settings' do + context 'when Sidekiq exporter and Sidekiq health checks port are the same' do + before do + stub_gitlab_rb( + sidekiq: + { + metrics_enabled: true, + listen_address: 'localhost', + listen_port: 3807, + health_checks_enabled: true, + health_checks_listen_address: 'localhost', + health_checks_listen_port: 3807 + } + ) + end + + it 'logs a warning' do + allow(LoggingHelper).to receive(:deprecation) + expect(LoggingHelper).to receive(:deprecation).with("Sidekiq exporter and health checks are set to the same address and port. This is deprecated and will result in an error in version 15.0. See https://docs.gitlab.com/ee/administration/sidekiq.html") + + chef_run + end + end + + context 'when Sidekiq exporter and Sidekiq health checks port are different' do + before do + stub_gitlab_rb( + sidekiq: + { + metrics_enabled: true, + listen_address: 'localhost', + listen_port: 3807, + health_checks_enabled: true, + health_checks_listen_address: 'localhost', + health_checks_listen_port: 3907 + } + ) + end + + it 'does not log a warning' do + expect(LoggingHelper).not_to receive(:deprecation).with("Sidekiq exporter and health checks are set to the same address and port. This is deprecated and will result in an error in version 15.0. See https://docs.gitlab.com/ee/administration/sidekiq.html") + + chef_run + end + end + end + + context 'with sidekiq exporter settings not set (default settings)' do + it 'does not log a warning' do + expect(LoggingHelper).not_to receive(:deprecation).with("Sidekiq exporter and health checks are set to the same address and port. This is deprecated and will result in an error in version 15.0. See https://docs.gitlab.com/ee/administration/sidekiq.html") + + chef_run + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-healthcheck_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-healthcheck_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItaGVhbHRoY2hlY2tfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-healthcheck_spec.rb @@ -0,0 +1,65 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-healthcheck' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'nginx is enabled' do + before do + stub_gitlab_rb(nginx: { enable: true }) + end + + it 'correctly renders the healthcheck-rc file' do + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") + .with_content(%r{url='http://localhost:80/help'}) + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc").with_content(%r{flags='--insecure'}) + end + + it 'correctly renders out the healthcheck-rc file when using https' do + stub_gitlab_rb(external_url: 'https://gitlab.example.com') + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") + .with_content(%r{url='https://localhost:443/help'}) + end + + it 'correctly renders out the healthcheck-rc file when using custom port' do + stub_gitlab_rb(external_url: 'http://gitlab.example.com:8080') + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") + .with_content(%r{url='http://localhost:8080/help'}) + end + + it 'correctly renders out the healthcheck-rc file when using a relative url' do + stub_gitlab_rb(external_url: 'http://gitlab.example.com/custom') + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") + .with_content(%r{url='http://localhost:80/custom/help'}) + end + end + + context 'nginx is disabled' do + before do + stub_gitlab_rb(nginx: { enable: false }) + end + + it 'correctly renders the healthcheck-rc file using workhorse' do + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") + .with_content(%r{url='http://localhost/help'}) + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") + .with_content(%r{flags='--unix-socket /var/opt/gitlab/gitlab-workhorse/sockets/socket'}) + end + + it 'correctly renders healthcheck-rc file using workhorse on a port' do + stub_gitlab_rb( + gitlab_workhorse: { listen_network: 'tcp', listen_addr: 'localhost:9191' } + ) + expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") + .with_content(%r{url='http://localhost:9191/help'}) + end + + it 'does not render the healthcheck-rc file when workhorse workhorse is disabled' do + stub_gitlab_rb(nginx: { enable: false }, gitlab_workhorse: { enable: false }) + expect(chef_run).not_to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/database_yml_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/database_yml_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZGF0YWJhc2VfeW1sX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/database_yml_spec.rb @@ -0,0 +1,277 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + describe 'Database settings' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: 'templatesymlink').converge('gitlab::default') } + let(:database_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/database.yml') } + let(:database_yml_file_content) { ChefSpec::Renderer.new(chef_run, database_yml_template).content } + let(:database_yml) { YAML.safe_load(database_yml_file_content, [], [], true, symbolize_names: true) } + let(:default_content) do + { + main: { + adapter: 'postgresql', + application_name: nil, + collation: nil, + connect_timeout: nil, + database: "gitlabhq_production", + database_tasks: true, + encoding: "unicode", + host: "/var/opt/gitlab/postgresql", + keepalives: nil, + keepalives_count: nil, + keepalives_idle: nil, + keepalives_interval: nil, + load_balancing: { + hosts: [] + }, + password: nil, + port: 5432, + prepared_statements: false, + socket: nil, + sslca: nil, + sslcompression: 0, + sslmode: nil, + sslrootcert: nil, + statement_limit: 1000, + tcp_user_timeout: nil, + username: "gitlab", + variables: { + statement_timeout: nil + } + } + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(File).to receive(:symlink?).and_call_original + end + + context 'with default settings' do + it 'renders database.yml with main database and default values' do + expect(database_yml[:production]).to eq(default_content) + end + end + + context 'with user provided settings' do + context 'via top level db_* keys' do + before do + stub_gitlab_rb( + gitlab_rails: { + db_database: 'foobar' + } + ) + end + + it 'renders database.yml with user specified values for main database' do + expect(database_yml[:production][:main][:database]).to eq('foobar') + expect(database_yml[:production][:main][:database_tasks]).to eq(true) + end + end + + context 'via top level db_* keys and overwritten main:' do + before do + stub_gitlab_rb( + gitlab_rails: { + db_database: 'foobar', + db_database_tasks: true, + databases: { + main: { + enable: true, + db_database_tasks: false + } + } + } + ) + end + + it 'renders database.yml with user specified values for main database' do + expect(database_yml[:production][:main][:database]).to eq('foobar') + expect(database_yml[:production][:main][:database_tasks]).to eq(false) + end + end + + context "for main database via gitlab_rails['databases']['main'] setting" do + before do + stub_gitlab_rb( + gitlab_rails: { + databases: { + main: { + enable: true, + db_database: 'foobar' + } + } + } + ) + end + + it 'renders database.yml with user specified values for main database' do + expect(database_yml[:production][:main][:database]).to eq('foobar') + end + end + + context 'with additional databases specified' do + context 'when using the same database as main:' do + before do + stub_gitlab_rb( + gitlab_rails: { + databases: { + ci: { + enable: true + } + } + } + ) + end + + it 'renders database.yml with main stanza first' do + expect(database_yml_file_content).to match("production:\n main:") + end + + it 'renders database.yml with both main and additional databases using default values, but disabled database_tasks' do + ci_content = default_content[:main].dup + ci_content[:database_tasks] = false + expected_output = default_content.merge(ci: ci_content) + + expect(database_yml[:production]).to eq(expected_output) + end + end + + context 'when using a different database to main:' do + before do + stub_gitlab_rb( + gitlab_rails: { + databases: { + ci: { + enable: true, + db_database: 'gitlabhq_production_ci' + } + } + } + ) + end + + it 'renders database.yml with main stanza first' do + expect(database_yml_file_content).to match("production:\n main:") + end + + it 'renders database.yml with both main and additional databases using default values, and enabled database_tasks' do + ci_content = default_content[:main].dup + ci_content[:database] = 'gitlabhq_production_ci' + ci_content[:database_tasks] = true + expected_output = default_content.merge(ci: ci_content) + + expect(database_yml[:production]).to eq(expected_output) + end + end + + context 'when db_database_tasks is explicitly enabled in main, but disabled in CI' do + before do + stub_gitlab_rb( + gitlab_rails: { + databases: { + main: { + db_connect_timeout: 30, + db_database_tasks: true + }, + ci: { + enable: true, + db_host: 'patroni-ci', + db_connect_timeout: 50, + db_database_tasks: false + } + } + } + ) + end + + it 'renders database.yml with user specified DB settings' do + expect(database_yml[:production][:main][:connect_timeout]).to eq(30) + expect(database_yml[:production][:main][:database_tasks]).to eq(true) + expect(database_yml[:production][:ci][:connect_timeout]).to eq(50) + expect(database_yml[:production][:ci][:database_tasks]).to eq(false) + end + end + + context 'with different settings for main database and additional databases' do + before do + stub_gitlab_rb( + gitlab_rails: { + databases: { + main: { + db_connect_timeout: 30, + }, + ci: { + enable: true, + db_connect_timeout: 50, + db_database_tasks: false + } + } + } + ) + end + + it 'renders database.yml with user specified DB settings' do + expect(database_yml[:production][:main][:connect_timeout]).to eq(30) + expect(database_yml[:production][:main][:database_tasks]).to eq(true) + expect(database_yml[:production][:ci][:connect_timeout]).to eq(50) + expect(database_yml[:production][:ci][:database_tasks]).to eq(false) + end + end + + context 'with additional database specified but not enabled' do + before do + stub_gitlab_rb( + gitlab_rails: { + databases: { + main: { + db_connect_timeout: 30, + }, + ci: { + db_connect_timeout: 50 + } + } + } + ) + end + + it 'renders database.yml without additional database' do + expect(database_yml[:production].keys).not_to include('ci') + end + end + + context 'with invalid additional database specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + databases: { + foobar: { + enable: true, + db_database: 'gitlabhq_foobar' + }, + johndoe: { + db_database: 'gitlabhq_johndoe' + }, + ci: { + enable: true, + db_database: 'gitlabhq_ci' + } + } + } + ) + end + + it 'raises warning about invalid database' do + chef_run + expect_logged_warning("Additional database `foobar` not supported in Rails application. It will be ignored.") + end + + it 'does not raise warning about invalid database that is not enabled' do + chef_run + expect(LoggingHelper.messages).not_to include(kind: :warning, message: /Additional database `johndoe` not supported/) + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/backup_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/backup_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9iYWNrdXBfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/backup_spec.rb @@ -0,0 +1,105 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + include_context 'object storage config' + + describe 'Backup settings' do + let(:backup_settings) { gitlab_yml[:production][:backup] } + + context 'with default values' do + it 'renders with default backup settings' do + expect(backup_settings).to eq( + archive_permissions: nil, + gitaly_backup_path: '/opt/gitlab/embedded/bin/gitaly-backup', + keep_time: nil, + path: '/var/opt/gitlab/backups', + pg_schema: nil, + upload: { + connection: nil, + encryption: nil, + encryption_key: nil, + multipart_chunk_size: nil, + remote_directory: nil, + storage_options: {}, + storage_class: nil + } + ) + end + end + + context 'with user specified gitaly-backup path set' do + before do + stub_gitlab_rb( + gitlab_rails: { + backup_gitaly_backup_path: '/some other/bin/gitaly-backup' + } + ) + end + + it 'renders with the user specified gitaly-backup path' do + expect(backup_settings).to include( + gitaly_backup_path: '/some other/bin/gitaly-backup' + ) + end + end + + context 'remote upload', :aggregate_failures do + let(:upload_settings) { backup_settings[:upload] } + + context 'with Server-Side Encryption' do + context 'with Amazon S3-Managed Keys (SSE-S3)' do + before do + stub_gitlab_rb( + gitlab_rails: { + 'backup_upload_connection' => aws_connection_hash, + 'backup_encryption' => 'AES256' + } + ) + end + + it 'renders encryption option' do + expect(upload_settings[:encryption]).to eq('AES256') + expect(upload_settings[:encryption_key]).to be_nil + expect(upload_settings[:storage_options]).to eq({}) + end + end + + context 'with Customer Master Keys (CMKs) Stored in AWS Key Management Service (SSE-KMS)' do + before do + stub_gitlab_rb( + gitlab_rails: { + 'backup_upload_connection' => aws_connection_hash, + 'backup_upload_storage_options' => aws_storage_options_hash + } + ) + end + + it 'renders storage options' do + expect(upload_settings[:encryption]).to be_nil + expect(upload_settings[:encryption_key]).to be_nil + expect(upload_settings[:storage_options]).to eq(aws_storage_options) + end + end + + context 'with Customer-Provided Keys (SSE-C)' do + before do + stub_gitlab_rb( + gitlab_rails: { + 'backup_upload_connection' => aws_connection_hash, + 'backup_encryption' => 'AES256', + 'backup_encryption_key' => 'A12345678' + } + ) + end + + it 'renders encryption and encryption key options' do + expect(upload_settings[:encryption]).to eq('AES256') + expect(upload_settings[:encryption_key]).to eq('A12345678') + expect(upload_settings[:storage_options]).to eq({}) + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/consul_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/consul_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9jb25zdWxfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/consul_spec.rb @@ -0,0 +1,123 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'Consul settings' do + context 'with default values' do + it 'renders gitlab.yml without Consul settings' do + expect(gitlab_yml[:production][:consul]).to eq( + api_url: "" + ) + end + end + + context 'with user specified values' do + context 'when bundled consul is enabled' do + before do + stub_gitlab_rb( + consul: { + enable: true + } + ) + end + + it 'renders gitlab.yml with default Consul settings' do + expect(gitlab_yml[:production][:consul]).to eq( + api_url: 'http://localhost:8500' + ) + end + end + + context 'when Consul is running on non-default location' do + context 'when setting client_addr and ports' do + before do + stub_gitlab_rb( + consul: { + enable: true, + configuration: { + client_addr: '10.0.0.1', + ports: { + http: 1234 + } + } + } + ) + end + + it 'renders gitlab.yml with specified Consul settings' do + expect(gitlab_yml[:production][:consul]).to eq( + api_url: 'http://10.0.0.1:1234' + ) + end + end + + context 'when setting addresses and ports' do + before do + stub_gitlab_rb( + consul: { + enable: true, + configuration: { + addresses: { + http: '10.0.1.2', + }, + ports: { + http: 1234 + } + } + } + ) + end + + it 'renders gitlab.yml with specified Consul settings' do + expect(gitlab_yml[:production][:consul]).to eq( + api_url: 'http://10.0.1.2:1234' + ) + end + end + + context 'when http port is disabled via negative port number' do + before do + stub_gitlab_rb( + consul: { + enable: true, + configuration: { + addresses: { + https: '10.0.1.2', + }, + ports: { + http: -1, + https: 1234 + } + } + } + ) + end + + it 'renders gitlab.yml with specified Consul settings' do + expect(gitlab_yml[:production][:consul]).to eq( + api_url: 'https://10.0.1.2:1234' + ) + end + end + end + + context 'when tls is enabled' do + before do + stub_gitlab_rb( + consul: { + enable: true, + use_tls: true + } + ) + end + + it 'renders gitlab.yml with specified Consul settings' do + expect(gitlab_yml[:production][:consul]).to eq( + api_url: 'https://localhost:8501' + ) + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/cron_jobs_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/cron_jobs_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9jcm9uX2pvYnNfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/cron_jobs_spec.rb @@ -0,0 +1,72 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'cron settings' do + context 'with default values' do + it "renders gitlab.yml without any cron settings" do + expect(gitlab_yml[:production][:cron_jobs]).to be nil + end + end + + context 'with user specified values' do + using RSpec::Parameterized::TableSyntax + + where(:gitlab_yml_setting, :gitlab_rb_setting) do + 'admin_email_worker' | 'admin_email_worker_cron' + 'analytics_devops_adoption_create_all_snapshots_worker' | 'analytics_devops_adoption_create_all_snapshots_worker' + 'analytics_usage_trends_count_job_trigger_worker' | 'analytics_usage_trends_count_job_trigger_worker_cron' + 'ci_archive_traces_cron_worker' | 'ci_archive_traces_cron_worker_cron' + 'ci_platform_metrics_update_cron_worker' | 'ci_platform_metrics_update_cron_worker' + 'elastic_index_bulk_cron_worker' | 'elastic_index_bulk_cron' + 'environments_auto_stop_cron_worker' | 'environments_auto_stop_cron_worker_cron' + 'expire_build_artifacts_worker' | 'expire_build_artifacts_worker_cron' + 'geo_file_download_dispatch_worker' | 'geo_file_download_dispatch_worker_cron' + 'geo_migrated_local_files_clean_up_worker' | 'geo_migrated_local_files_clean_up_worker_cron' + 'geo_prune_event_log_worker' | 'geo_prune_event_log_worker_cron' + 'geo_repository_sync_worker' | 'geo_repository_sync_worker_cron' + 'geo_repository_verification_primary_batch_worker' | 'geo_repository_verification_primary_batch_worker_cron' + 'geo_repository_verification_secondary_scheduler_worker' | 'geo_repository_verification_secondary_scheduler_worker_cron' + 'geo_secondary_registry_consistency_worker' | 'geo_secondary_registry_consistency_worker' + 'geo_secondary_usage_data_cron_worker' | 'geo_secondary_usage_data_cron_worker' + 'historical_data_worker' | 'historical_data_worker_cron' + 'ldap_group_sync_worker' | 'ldap_group_sync_worker_cron' + 'ldap_sync_worker' | 'ldap_sync_worker_cron' + 'member_invitation_reminder_emails_worker' | 'member_invitation_reminder_emails_worker_cron' + 'pages_domain_removal_cron_worker' | 'pages_domain_removal_cron_worker' + 'pages_domain_ssl_renewal_cron_worker' | 'pages_domain_ssl_renewal_cron_worker' + 'pages_domain_verification_cron_worker' | 'pages_domain_verification_cron_worker' + 'personal_access_tokens_expired_notification_worker' | 'personal_access_tokens_expired_notification_worker_cron' + 'personal_access_tokens_expiring_worker' | 'personal_access_tokens_expiring_worker_cron' + 'pipeline_schedule_worker' | 'pipeline_schedule_worker_cron' + 'pseudonymizer_worker' | 'pseudonymizer_worker_cron' + 'remove_unaccepted_member_invites_worker' | 'remove_unaccepted_member_invites_cron_worker' + 'repository_archive_cache_worker' | 'repository_archive_cache_worker_cron' + 'repository_check_worker' | 'repository_check_worker_cron' + 'schedule_migrate_external_diffs_worker' | 'schedule_migrate_external_diffs_worker_cron' + 'stuck_ci_jobs_worker' | 'stuck_ci_jobs_worker_cron' + 'user_status_cleanup_batch_worker' | 'user_status_cleanup_batch_worker_cron' + 'namespaces_in_product_marketing_emails_worker' | 'namespaces_in_product_marketing_emails_worker_cron' + 'ssh_keys_expired_notification_worker' | 'ssh_keys_expired_notification_worker_cron' + 'ssh_keys_expiring_soon_notification_worker' | 'ssh_keys_expiring_soon_notification_worker_cron' + 'loose_foreign_keys_cleanup_worker' | 'loose_foreign_keys_cleanup_worker_cron' + end + + with_them do + before do + stub_gitlab_rb( + gitlab_rails: { + gitlab_rb_setting => '1 2 3 4 5' + }.transform_keys(&:to_sym) + ) + end + + it "renders gitlab.yml with user specified cron settings" do + config = gitlab_yml[:production][:cron_jobs][gitlab_yml_setting.to_sym][:cron] + expect(config).to eq '1 2 3 4 5' + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/extra_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/extra_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9leHRyYV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/extra_spec.rb @@ -0,0 +1,89 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'extra settings' do + context 'with default values' do + it 'renders gitlab.yml without extra settings' do + expect(gitlab_yml[:production][:extra]).to be nil + end + end + + context 'with user specified values' do + describe 'matomo settings' do + context 'with just matomo_url specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + extra_matomo_url: 'http://foobar.com' + } + ) + end + + it 'renders gitlab.yml with default values for other matomo settings' do + expect(gitlab_yml[:production][:extra][:matomo_url]).to eq('http://foobar.com') + expect(gitlab_yml[:production][:extra][:matomo_site_id]).to be nil + expect(gitlab_yml[:production][:extra][:matomo_disable_cookies]).to be nil + end + end + + context 'with all settings specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + extra_matomo_url: 'http://foobar.com', + extra_matomo_site_id: 'foobar', + extra_matomo_disable_cookies: true + } + ) + end + + it 'renders gitlab.yml with specified matomo settings' do + expect(gitlab_yml[:production][:extra][:matomo_url]).to eq('http://foobar.com') + expect(gitlab_yml[:production][:extra][:matomo_site_id]).to eq('foobar') + expect(gitlab_yml[:production][:extra][:matomo_disable_cookies]).to be true + end + end + end + + describe 'one_trust_id setting' do + before do + stub_gitlab_rb(gitlab_rails: { extra_one_trust_id: '0000-0000-test' }) + end + + it 'renders gitlab.yml with the provided value' do + expect(gitlab_yml[:production][:extra][:one_trust_id]).to eq('0000-0000-test') + end + end + + describe 'google_tag_manager_nonce_id setting' do + before do + stub_gitlab_rb(gitlab_rails: { extra_google_tag_manager_nonce_id: '0000-0000' }) + end + + it 'renders gitlab.yml with the provided value' do + expect(gitlab_yml[:production][:extra][:google_tag_manager_nonce_id]).to eq('0000-0000') + end + end + + context 'bizible' do + context 'when true' do + before do + stub_gitlab_rb( + gitlab_rails: { bizible: true } + ) + end + + it 'should set bizible to true' do + expect(chef_run).to create_templatesymlink('Create a gitlab.yml and create a symlink to Rails root').with_variables( + hash_including( + 'bizible' => true + ) + ) + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/forti_authenticator_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/forti_authenticator_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9mb3J0aV9hdXRoZW50aWNhdG9yX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/forti_authenticator_spec.rb @@ -0,0 +1,40 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + using RSpec::Parameterized::TableSyntax + include_context 'gitlab-rails' + + describe 'FortiAuthenticator settings' do + context 'with default values' do + it 'renders gitlab.yml with FortiAuthenticator disabled' do + expect(gitlab_yml[:production][:forti_authenticator]).to eq( + enabled: false + ) + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + gitlab_rails: { + forti_authenticator_enabled: true, + forti_authenticator_host: 'forti_authenticator.example.com', + forti_authenticator_port: 444, + forti_authenticator_username: 'janedoe', + forti_authenticator_access_token: '123s3cr3t456' + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expect(gitlab_yml[:production][:forti_authenticator]).to eq( + enabled: true, + host: 'forti_authenticator.example.com', + port: 444, + username: 'janedoe', + access_token: '123s3cr3t456' + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/forti_token_cloud_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/forti_token_cloud_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9mb3J0aV90b2tlbl9jbG91ZF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/forti_token_cloud_spec.rb @@ -0,0 +1,36 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + using RSpec::Parameterized::TableSyntax + include_context 'gitlab-rails' + + describe 'FortiToken Cloud settings' do + context 'with default values' do + it 'renders gitlab.yml with FortiAuthenticator disabled' do + expect(gitlab_yml[:production][:forti_token_cloud]).to eq( + enabled: false + ) + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + gitlab_rails: { + forti_token_cloud_enabled: true, + forti_token_cloud_client_id: 'forti_token_cloud_client_id', + forti_token_cloud_client_secret: '123s3cr3t456' + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expect(gitlab_yml[:production][:forti_token_cloud]).to eq( + enabled: true, + client_id: 'forti_token_cloud_client_id', + client_secret: '123s3cr3t456' + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/geo_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/geo_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9nZW9fc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/geo_spec.rb @@ -0,0 +1,41 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'Geo settings' do + context 'with default configuration' do + it 'renders gitlab.yml with default values' do + expect(gitlab_yml[:production][:geo]).to eq( + node_name: nil, + registry_replication: { + enabled: nil, + primary_api_url: nil + } + ) + end + end + + context 'with user specified configuration' do + before do + stub_gitlab_rb( + gitlab_rails: { + geo_node_name: 'foobar', + geo_registry_replication_enabled: true, + geo_registry_replication_primary_api_url: 'https://example.com' + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expect(gitlab_yml[:production][:geo]).to eq( + node_name: 'foobar', + registry_replication: { + enabled: true, + primary_api_url: 'https://example.com' + } + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitaly_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitaly_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9naXRhbHlfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitaly_spec.rb @@ -0,0 +1,33 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'Gitaly settings' do + context 'with default values' do + it 'renders gitlab.yml without Gitaly token set' do + expect(gitlab_yml[:production][:gitaly]).to eq( + client_path: "/opt/gitlab/embedded/bin", + token: "" + ) + end + end + + context 'with user specified token' do + before do + stub_gitlab_rb( + gitlab_rails: { + gitaly_token: 'token123456' + } + ) + end + + it 'renders gitlab.yml with user specified token' do + expect(gitlab_yml[:production][:gitaly]).to eq( + client_path: "/opt/gitlab/embedded/bin", + token: 'token123456' + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitlab_shell_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitlab_shell_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9naXRsYWJfc2hlbGxfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitlab_shell_spec.rb @@ -0,0 +1,78 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'GitLab Shell settings' do + context 'with default values' do + it 'renders gitlab.yml with default values' do + expect(gitlab_yml[:production][:gitlab_shell]).to eq( + authorized_keys_file: '/var/opt/gitlab/.ssh/authorized_keys', + git_timeout: 10800, + hooks_path: '/opt/gitlab/embedded/service/gitlab-shell/hooks/', + path: '/opt/gitlab/embedded/service/gitlab-shell/', + receive_pack: nil, + ssh_port: nil, + upload_pack: nil + ) + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + gitlab_rails: { + gitlab_shell_git_timeout: 99999, + gitlab_shell_hooks_path: '/tmp/hooks', + gitlab_shell_path: '/tmp/shell', + gitlab_shell_upload_pack: true, + gitlab_shell_receive_pack: true, + gitlab_shell_ssh_port: 123, + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expect(gitlab_yml[:production][:gitlab_shell]).to eq( + authorized_keys_file: '/var/opt/gitlab/.ssh/authorized_keys', + git_timeout: 99999, + hooks_path: '/tmp/hooks', + path: '/tmp/shell', + receive_pack: true, + ssh_port: 123, + upload_pack: true + ) + end + + describe 'authorized_keys_file' do + context 'when explicitly configured' do + before do + stub_gitlab_rb( + gitlab_shell: { + auth_file: '/tmp/authorized_keys' + } + ) + end + + it 'renders gitlab.yml with user specified path' do + expect(gitlab_yml[:production][:gitlab_shell][:authorized_keys_file]).to eq('/tmp/authorized_keys') + end + end + + context 'when the user home directory is specified' do + before do + stub_gitlab_rb( + user: { + home: '/tmp/user' + } + ) + end + + it 'defaults to auth file within specified home directory' do + expect(gitlab_yml[:production][:gitlab_shell][:authorized_keys_file]).to eq('/tmp/user/.ssh/authorized_keys') + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitlab_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitlab_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9naXRsYWJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/gitlab_spec.rb @@ -0,0 +1,207 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'GitLab Application Settings' do + describe 'Content Security Policy settings' do + context 'with default configuration' do + it 'renders gitlab.yml without content security policy settings' do + expect(gitlab_yml[:production][:gitlab][:content_security_policy]).to be nil + end + end + + context 'with user specified configuration' do + context 'with content security policy disabled' do + before do + stub_gitlab_rb( + gitlab_rails: { + content_security_policy: { + enabled: false + } + } + ) + end + + it 'renders gitlab.yml with content security policy disabled' do + expect(gitlab_yml[:production][:gitlab][:content_security_policy]).to eq( + enabled: false + ) + end + end + + context 'for various content security policy related settings' do + before do + stub_gitlab_rb( + gitlab_rails: { + content_security_policy: { + enabled: true, + report_only: true, + directives: { + default_src: "'self'", + script_src: "'self' http://recaptcha.net", + worker_src: "'self'" + } + } + } + ) + end + + it 'renders gitlab.yml with user specified values for content security policy' do + expect(gitlab_yml[:production][:gitlab][:content_security_policy]).to eq( + enabled: true, + report_only: true, + directives: { + default_src: "'self'", + script_src: "'self' http://recaptcha.net", + worker_src: "'self'" + } + ) + end + end + end + end + + describe 'SMIME email settings' do + context 'with default configuration' do + it 'renders gitlab.yml with SMIME email settings disabled' do + expect(gitlab_yml[:production][:gitlab][:email_smime]).to eq( + enabled: false, + cert_file: '/etc/gitlab/ssl/gitlab_smime.crt', + key_file: '/etc/gitlab/ssl/gitlab_smime.key', + ca_certs_file: nil + ) + end + end + + context 'with user specified configuration' do + before do + stub_gitlab_rb( + gitlab_rails: { + gitlab_email_smime_enabled: true, + gitlab_email_smime_key_file: '/etc/gitlab/ssl/custom_gitlab_smime.key', + gitlab_email_smime_cert_file: '/etc/gitlab/ssl/custom_gitlab_smime.crt', + gitlab_email_smime_ca_certs_file: '/etc/gitlab/ssl/custom_gitlab_smime_cas.crt' + } + ) + end + + it 'renders gitlab.yml with user specified values for SMIME email settings' do + expect(gitlab_yml[:production][:gitlab][:email_smime]).to eq( + enabled: true, + cert_file: '/etc/gitlab/ssl/custom_gitlab_smime.crt', + key_file: '/etc/gitlab/ssl/custom_gitlab_smime.key', + ca_certs_file: '/etc/gitlab/ssl/custom_gitlab_smime_cas.crt' + ) + end + end + end + + describe 'Allowed hosts' do + context 'with default configuration' do + it 'does not render allowed_hosts in gitlab.yml' do + expect(gitlab_yml[:production][:gitlab][:allowed_hosts]).to be nil + end + end + + context 'with user specified configuration' do + before do + stub_gitlab_rb( + gitlab_rails: { + allowed_hosts: ['example.com', 'foobar.com'] + } + ) + end + + it 'renders specified allowed_hosts in gitlab.yml' do + expect(gitlab_yml[:production][:gitlab][:allowed_hosts]).to eq(['example.com', 'foobar.com']) + end + end + end + + describe 'Application settings cache expiry' do + context 'with default configuration' do + it 'renders gitlab.yml without application settings cache expiry' do + expect(gitlab_yml[:production][:gitlab][:application_settings_cache_seconds]).to be nil + end + end + + context 'with user specified configuration' do + before do + stub_gitlab_rb( + gitlab_rails: { + application_settings_cache_seconds: 30 + } + ) + end + + it 'renders gitlab.yml with user specified value for application settings cache expiry' do + expect(gitlab_yml[:production][:gitlab][:application_settings_cache_seconds]).to eq(30) + end + end + end + + describe 'Maximum Request Duration' do + context 'with default configuration' do + it 'renders gitlab.yml with default value for maximum request duration' do + expect(gitlab_yml[:production][:gitlab][:max_request_duration_seconds]).to eq(57) + end + + context 'with default configuration and Unicorn as webserver' do + before do + stub_gitlab_rb( + puma: { enable: false }, + unicorn: { enable: true } + ) + end + + it 'renders gitlab.yml with default value for maximum request duration' do + expect(gitlab_yml[:production][:gitlab][:max_request_duration_seconds]).to eq(57) + end + end + end + + context 'with user specified configuration' do + context 'for worker_timeout' do + using RSpec::Parameterized::TableSyntax + + where(:web_worker, :configured_timeout, :expected_duration) do + :unicorn | 30 | 29 + :unicorn | "30" | 29 + :puma | 120 | 114 + end + + with_them do + before do + stub_gitlab_rb( + unicorn: { enable: web_worker == :unicorn, worker_timeout: configured_timeout }, + puma: { enable: web_worker == :puma, worker_timeout: configured_timeout } + ) + end + + it 'renders gitlab.yml with maximum request duration computed from configured worker timeout' do + expect(gitlab_yml[:production][:gitlab][:max_request_duration_seconds]).to eq(expected_duration) + end + end + end + + context 'for max_request_duration_seconds' do + before do + stub_gitlab_rb( + puma: { + worker_timeout: 120 + }, + gitlab_rails: { + max_request_duration_seconds: 100 + } + ) + end + + it 'renders gitlab.yml with user specified value for maximum request duration' do + expect(gitlab_yml[:production][:gitlab][:max_request_duration_seconds]).to eq(100) + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/incoming_email_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/incoming_email_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9pbmNvbWluZ19lbWFpbF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/incoming_email_spec.rb @@ -0,0 +1,60 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'incoming email settings' do + describe 'log file location' do + context 'with default values' do + it 'renders gitlab.yml with default value for mailroom log file path' do + expect(gitlab_yml[:production][:incoming_email][:log_path]).to eq '/var/log/gitlab/mailroom/mail_room_json.log' + end + end + + context 'with custom log file path specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + incoming_email_log_file: '/my/custom/log/file' + } + ) + end + + it 'renders gitlab.yml with specified value for mailroom log file path' do + expect(gitlab_yml[:production][:incoming_email][:log_path]).to eq '/my/custom/log/file' + end + end + + context 'with custom log directory specified' do + before do + stub_gitlab_rb( + mailroom: { + log_directory: '/my/custom/directory' + } + ) + end + + it 'renders gitlab.yml with default log file name inside specified directory' do + expect(gitlab_yml[:production][:incoming_email][:log_path]).to eq '/my/custom/directory/mail_room_json.log' + end + end + + context 'with custom log file and directory specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + incoming_email_log_file: '/foobar.log' + }, + mailroom: { + log_directory: '/my/custom/directory' + } + ) + end + + it 'renders gitlab.yml with specified log file path irrespective of specified directory' do + expect(gitlab_yml[:production][:incoming_email][:log_path]).to eq '/foobar.log' + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/ldap_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/ldap_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9sZGFwX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/ldap_spec.rb @@ -0,0 +1,152 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'LDAP server configuration' do + context 'with default configuration' do + it 'renders gitlab.yml with LDAP disabled' do + expect(gitlab_yml[:production][:ldap]).to eq( + enabled: false, + active_directory: nil, + admin_group: nil, + allow_username_or_email_login: nil, + base: nil, + bind_dn: nil, + group_base: nil, + host: nil, + lowercase_usernames: nil, + method: nil, + password: nil, + port: nil, + prevent_ldap_sign_in: false, + sync_ssh_keys: nil, + sync_time: nil, + uid: nil, + user_filter: nil + ) + end + end + + context 'with user specified configuration' do + context 'when using new syntax for LDAP servers' do + before do + stub_gitlab_rb( + gitlab_rails: { + ldap_enabled: true, + prevent_ldap_sign_in: false, + ldap_sync_time: 20, + ldap_servers: { + main: { + label: 'LDAP Primary', + host: 'primary.ldap', + port: 389, + uid: 'uid', + encryption: 'plain', + password: 's3cr3t', + base: 'dc=example,dc=com', + user_filter: '' + }, + secondary: { + label: 'LDAP Secondary', + host: 'secondary.ldap', + port: 389, + uid: 'uid', + encryption: 'plain', + bind_dn: 'dc=example,dc=com', + password: 's3cr3t', + smartcard_auth: 'required', + base: '', + user_filter: '', + } + } + }) + end + + it 'renders gitlab.yml with user specified values' do + expected_output = { + enabled: true, + prevent_ldap_sign_in: false, + sync_time: 20, + servers: { + main: { + label: 'LDAP Primary', + host: 'primary.ldap', + port: 389, + uid: 'uid', + encryption: 'plain', + password: 's3cr3t', + base: 'dc=example,dc=com', + user_filter: '' + }, + secondary: { + label: 'LDAP Secondary', + host: 'secondary.ldap', + port: 389, + uid: 'uid', + encryption: 'plain', + bind_dn: 'dc=example,dc=com', + password: 's3cr3t', + smartcard_auth: 'required', + base: '', + user_filter: '', + } + } + } + + expect(gitlab_yml[:production][:ldap]).to eq(expected_output) + end + end + + context 'when using old syntax with single LDAP server' do + before do + stub_gitlab_rb( + gitlab_rails: { + ldap_enabled: true, + prevent_ldap_sign_in: false, + ldap_host: 'primary.ldap', + ldap_port: 389, + ldap_uid: 'uid', + ldap_password: 's3cr3t', + ldap_base: 'dc=example,dc=com', + ldap_user_filter: '', + ldap_method: 'plain', + ldap_bind_dn: 'foobar', + ldap_active_directory: 'asdf', + ldap_allow_username_or_email_login: false, + ldap_lowercase_usernames: true, + ldap_group_base: 'dc-example.com', + ldap_admin_group: 'foo', + ldap_sync_ssh_keys: true, + ldap_sync_time: 10 + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expected_output = { + enabled: true, + prevent_ldap_sign_in: false, + sync_time: 10, + host: 'primary.ldap', + port: 389, + uid: 'uid', + password: 's3cr3t', + base: 'dc=example,dc=com', + user_filter: '', + method: 'plain', + bind_dn: 'foobar', + active_directory: 'asdf', + allow_username_or_email_login: false, + lowercase_usernames: true, + group_base: 'dc-example.com', + admin_group: 'foo', + sync_ssh_keys: 'true', + } + + expect(gitlab_yml[:production][:ldap]).to eq(expected_output) + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/mattermost_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/mattermost_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9tYXR0ZXJtb3N0X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/mattermost_spec.rb @@ -0,0 +1,66 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + using RSpec::Parameterized::TableSyntax + include_context 'gitlab-rails' + + describe 'Mattermost settings' do + context 'with default values' do + it 'renders gitlab.yml with Mattermost disabled' do + expect(gitlab_yml[:production][:mattermost]).to eq( + enabled: false, + host: nil + ) + end + end + + context 'with user specified values' do + context 'when mattermost_external_url is specified' do + before do + stub_gitlab_rb( + mattermost_external_url: 'http://mattermost.example.com' + ) + end + + it 'renders gitlab.yml with specified Mattermost settings' do + expect(gitlab_yml[:production][:mattermost]).to eq( + enabled: true, + host: 'http://mattermost.example.com' + ) + end + end + + context 'when mattermost is running on a different machine' do + before do + stub_gitlab_rb( + gitlab_rails: { + mattermost_host: 'http://mattermost.example.com' + } + ) + end + + it 'renders gitlab.yml with specified Mattermost settings' do + expect(gitlab_yml[:production][:mattermost]).to eq( + enabled: true, + host: 'http://mattermost.example.com' + ) + end + end + + context 'when both gitlab-rails.mattermost_host and mattermost_external_url is set' do + before do + stub_gitlab_rb( + mattermost_external_url: 'http://foobar.com', + gitlab_rails: { + mattermost_host: 'http://mattermost.example.com' + } + ) + end + + it 'mattermost_external_url is used' do + expect(gitlab_yml[:production][:mattermost][:host]).to eq('http://foobar.com') + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/monitoring_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/monitoring_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9tb25pdG9yaW5nX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/monitoring_spec.rb @@ -0,0 +1,143 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'Monitoring settings' do + context 'with default configuration' do + it 'renders gitlab.yml with default values' do + expect(gitlab_yml[:production][:monitoring]).to eq( + ip_whitelist: %w[127.0.0.0/8 ::1/128], + sidekiq_exporter: { + enabled: true, + log_enabled: false, + port: 8082, + address: '127.0.0.1' + }, + sidekiq_health_checks: { + enabled: true, + log_enabled: false, + port: 8082, + address: '127.0.0.1' + }, + web_exporter: { + enabled: false, + port: 8083, + address: '127.0.0.1' + } + ) + end + end + + context 'with user specified configuration' do + before do + stub_gitlab_rb( + gitlab_rails: { + monitoring_whitelist: %w[1.0.0.0 2.0.0.0], + } + ) + end + + it 'renders gitlab.rb with user specified values' do + expect(gitlab_yml[:production][:monitoring][:ip_whitelist]).to eq(%w[1.0.0.0 2.0.0.0]) + end + + context 'when sidekiq exports logs' do + before do + stub_gitlab_rb( + sidekiq: { + metrics_enabled: false, + exporter_log_enabled: true, + listen_address: '1.2.3.4', + listen_port: 1234 + } + ) + end + + it 'renders gitlab.rb with user specified values' do + expect(gitlab_yml[:production][:monitoring][:sidekiq_exporter]).to eq( + enabled: false, + log_enabled: true, + port: 1234, + address: '1.2.3.4' + ) + end + end + + context 'when sidekiq exporter uses custom settings' do + let(:exporter_settings) do + { + sidekiq: { + metrics_enabled: false, + exporter_log_enabled: true, + listen_address: '1.2.2.2', + listen_port: 2222 + } + } + end + + before do + stub_gitlab_rb(exporter_settings.merge(health_checks_settings)) + end + + context 'when health-checks are not configured manually' do + let(:health_checks_settings) { {} } + + it 'defaults health-checks to exporter settings' do + expect(gitlab_yml[:production][:monitoring][:sidekiq_health_checks]).to eq( + enabled: false, + log_enabled: true, + port: 2222, + address: '1.2.2.2' + ) + end + end + + context 'when health-checks are configured manually' do + let(:health_checks_settings) do + { + sidekiq: { + health_checks_enabled: true, + health_checks_log_enabled: true, + health_checks_listen_address: '1.2.3.5', + health_checks_listen_port: 1235 + } + } + end + + it 'uses custom health-checks settings' do + expect(gitlab_yml[:production][:monitoring][:sidekiq_health_checks]).to eq( + enabled: true, + log_enabled: true, + port: 1235, + address: '1.2.3.5' + ) + end + end + end + + context 'when webserver exports are enabled' do + context 'with Puma' do + before do + stub_gitlab_rb( + puma: { + enable: true, + exporter_enabled: false, + exporter_address: '1.2.3.4', + exporter_port: 1234 + } + ) + end + + it 'renders gitlab.rb with user specified values' do + expect(gitlab_yml[:production][:monitoring][:web_exporter]).to eq( + enabled: false, + port: 1234, + address: '1.2.3.4' + ) + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/object_storage_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/object_storage_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9vYmplY3Rfc3RvcmFnZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/object_storage_spec.rb @@ -0,0 +1,60 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'object storage settings' do + include_context 'object storage config' + + describe 'consolidated object storage settings' do + context 'with default values' do + it 'renders gitlab.yml without consolidated object storage settings' do + expect(gitlab_yml[:production][:object_store]).to be_nil + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + gitlab_rails: { + object_store: { + enabled: true, + connection: aws_connection_hash, + storage_options: aws_storage_options_hash, + objects: object_config, + proxy_download: true, + } + } + ) + end + + it 'generates gitlab.yml properly with specified values' do + expect(gitlab_yml[:production][:object_store]).to eq( + enabled: true, + connection: aws_connection_data, + storage_options: aws_storage_options, + objects: object_config, + proxy_download: true + ) + end + end + end + + describe 'individual object storage settings' do + # Parameters are: + # 1. Component name + # 2. Default settings deviating from general pattern + # 3. Whether Workhorse acceleration is in place - decides whether to + # include background_upload, direct_upload, proxy_download etc. + include_examples 'renders object storage settings in gitlab.yml', 'artifacts' + include_examples 'renders object storage settings in gitlab.yml', 'uploads' + include_examples 'renders object storage settings in gitlab.yml', 'external_diffs', { remote_directory: 'external-diffs' } + include_examples 'renders object storage settings in gitlab.yml', 'lfs', { remote_directory: 'lfs-objects' } + include_examples 'renders object storage settings in gitlab.yml', 'packages' + include_examples 'renders object storage settings in gitlab.yml', 'dependency_proxy' + include_examples 'renders object storage settings in gitlab.yml', 'terraform_state', { remote_directory: 'terraform' }, false + include_examples 'renders object storage settings in gitlab.yml', 'ci_secure_files', { remote_directory: 'ci-secure-files' }, false + include_examples 'renders object storage settings in gitlab.yml', 'pages', {}, false + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/omniauth_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/omniauth_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9vbW5pYXV0aF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/omniauth_spec.rb @@ -0,0 +1,101 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + using RSpec::Parameterized::TableSyntax + include_context 'gitlab-rails' + + describe 'omniauth settings' do + context 'with default values' do + it 'renders gitlab.yml with omniauth allow_single_sign_on set to saml' do + expect(gitlab_yml[:production][:omniauth][:allow_single_sign_on]).to eq(['saml']) + end + + it 'renders gitlab.yml with other omniauth settings set to nil' do + expect(gitlab_yml[:production][:omniauth]).to eq( + allow_bypass_two_factor: nil, + auto_link_ldap_user: nil, + auto_link_saml_user: nil, + auto_link_user: nil, + allow_single_sign_on: ['saml'], + auto_sign_in_with_provider: nil, + block_auto_created_users: nil, + enabled: nil, + external_providers: nil, + providers: nil + ) + end + end + + context 'with user specified values' do + describe 'saml_message_max_byte_size' do + before do + stub_gitlab_rb( + gitlab_rails: { + omniauth_saml_message_max_byte_size: 100000 + } + ) + end + + it 'renders gitlab.yml with custom value' do + expect(gitlab_yml[:production][:omniauth][:saml_message_max_byte_size]).to eq(100000) + end + end + + describe 'settings that take in boolean values' do + where(:gitlab_yml_setting, :gitlab_rb_setting) do + 'enabled' | 'omniauth_enabled' + 'block_auto_created_users' | 'omniauth_block_auto_created_users' + 'auto_link_ldap_user' | 'omniauth_auto_link_ldap_user' + 'auto_link_saml_user' | 'omniauth_auto_link_saml_user' + end + + with_them do + context "with #{params[:gitlab_rb_setting]} set to true" do + before do + stub_gitlab_rb( + gitlab_rails: { + gitlab_rb_setting => true + }.transform_keys(&:to_sym) + ) + end + + it "renders gitlab.yml with #{params[:gitlab_yml_setting]} set to true" do + expect(gitlab_yml[:production][:omniauth][gitlab_yml_setting.to_sym]).to be true + end + end + end + end + + describe 'settings that take in multiple types of values' do + where(:gitlab_yml_setting, :gitlab_rb_setting) do + 'allow_single_sign_on' | 'omniauth_allow_single_sign_on' + 'sync_email_from_provider' | 'omniauth_sync_email_from_provider' + 'sync_profile_from_provider' | 'omniauth_sync_profile_from_provider' + 'allow_bypass_two_factor' | 'omniauth_allow_bypass_two_factor' + 'sync_profile_attributes' | 'omniauth_sync_profile_attributes' + 'auto_link_user' | 'omniauth_auto_link_user' + end + + with_them do + # Testing scenarios where settings are set to boolean true, string, + # and array of strings. + [true, 'foo', ['bar', 'tar']].each do |value| + context "with #{params[:gitlab_rb_setting]} set to #{value}" do + before(:each) do + stub_gitlab_rb( + gitlab_rails: { + gitlab_rb_setting => value + }.transform_keys(&:to_sym) + ) + end + + it "renders gitlab.yml with #{params[:gitlab_yml_setting]} set to #{value}" do + expect(gitlab_yml[:production][:omniauth][gitlab_yml_setting.to_sym]).to eq value + end + end + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/pages_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/pages_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9wYWdlc19zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/pages_spec.rb @@ -0,0 +1,208 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + using RSpec::Parameterized::TableSyntax + include_context 'gitlab-rails' + include_context 'object storage config' + + let(:aws_connection_data) { JSON.parse(aws_connection_hash.to_json, symbolize_names: true) } + + describe 'Pages settings' do + context 'with default values' do + it 'renders gitlab.yml with Pages disabled' do + expect(gitlab_yml[:production][:pages]).to eq( + enabled: false, + access_control: false, + artifacts_server: true, + external_http: false, + external_https: false, + host: nil, + https: false, + object_store: { + connection: {}, + enabled: false, + remote_directory: "pages" + }, + local_store: { + enabled: true, + path: "/var/opt/gitlab/gitlab-rails/shared/pages", + }, + path: "/var/opt/gitlab/gitlab-rails/shared/pages", + port: nil + ) + end + end + + context 'with user specified values' do + context 'when Pages deployed along with GitLab' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_pages: { + access_control: true, + artifacts_server: false, + external_http: ['1.2.3.4'] + }, + gitlab_rails: { + pages_path: '/random/path', + pages_object_store_enabled: true, + pages_object_store_remote_directory: 'foobar', + pages_object_store_connection: aws_connection_hash + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expect(gitlab_yml[:production][:pages]).to eq( + { + enabled: true, + access_control: true, + path: '/random/path', + host: 'pages.example.com', + port: 443, + https: true, + external_http: true, + external_https: false, + artifacts_server: false, + object_store: { + enabled: true, + remote_directory: 'foobar', + connection: aws_connection_data + }, + local_store: { + enabled: true, + path: '/random/path' + } + } + ) + end + end + + context 'when Pages deployed external to GitLab' do + before do + stub_gitlab_rb( + gitlab_rails: { + pages_enabled: true, + pages_path: '/random/path', + pages_host: 'pages.example.com', + pages_port: 443, + pages_https: true, + pages_object_store_enabled: true, + pages_object_store_remote_directory: 'foobar', + pages_object_store_connection: aws_connection_hash + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expect(gitlab_yml[:production][:pages]).to eq( + { + enabled: true, + access_control: false, + path: '/random/path', + host: 'pages.example.com', + port: 443, + https: true, + external_http: false, + external_https: false, + artifacts_server: true, + object_store: { + enabled: true, + remote_directory: 'foobar', + connection: aws_connection_data + }, + local_store: { + enabled: true, + path: '/random/path' + } + } + ) + end + end + + describe 'for external HTTPS settings' do + context 'when external_https is used' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_pages: { + external_https: ['1.2.3.4'] + } + ) + end + + it 'renders gitlab.yml with pages.external_https set to true' do + expect(gitlab_yml[:production][:pages][:external_https]).to be true + end + end + + context 'when external_https_proxyv2 is used' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_pages: { + external_https_proxyv2: ['1.2.3.4'] + } + ) + end + + it 'renders gitlab.yml with pages.external_https set to true' do + expect(gitlab_yml[:production][:pages][:external_https]).to be true + end + end + end + + describe 'Pages local store settings' do + context 'when neither Pages path nor local store path is specified' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com' + ) + end + + it 'renders gitlab.yml with Pages local store path set to default Pages path' do + expect(gitlab_yml[:production][:pages][:local_store][:path]).to eq('/var/opt/gitlab/gitlab-rails/shared/pages') + end + end + + context 'when Pages path is specified but not local store path' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_rails: { + pages_local_store_enabled: true, + pages_path: '/tmp/test' + } + ) + end + + it 'renders gitlab.yml with Pages local store path set to Pages path' do + expect(gitlab_yml[:production][:pages][:local_store][:path]).to eq('/tmp/test') + end + end + + context 'when Pages local store path is different than Pages path' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + pages_external_url: 'https://pages.example.com', + gitlab_rails: { + pages_path: '/tmp/test', + pages_local_store_path: '/another/path' + } + ) + end + + it 'renders gitlab.yml with specified Pages local store path' do + expect(gitlab_yml[:production][:pages][:local_store][:path]).to eq('/another/path') + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/prometheus_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/prometheus_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9wcm9tZXRoZXVzX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/prometheus_spec.rb @@ -0,0 +1,106 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'Prometheus settings' do + context 'with default configuration' do + it 'renders gitlab.yml with bundled Prometheus default values' do + expect(gitlab_yml[:production][:prometheus]).to eq( + enabled: true, + server_address: 'localhost:9090' + ) + end + end + + context 'with user specified configuation' do + context 'when bundled Prometheus is running on non-default address' do + before do + stub_gitlab_rb( + prometheus: { + listen_address: '0.0.0.0:9191' + } + ) + end + + it 'renders gitlab.yml with correct values' do + expect(gitlab_yml[:production][:prometheus]).to eq( + enabled: true, + server_address: '0.0.0.0:9191' + ) + end + end + + context 'when bundled Prometheus is running on non-default address with only port specified' do + before do + stub_gitlab_rb( + prometheus: { + listen_address: ':8080' + } + ) + end + + it 'renders gitlab.yml with server address rendered as a string' do + expect(gitlab_yml[:production][:prometheus]).to eq( + enabled: true, + server_address: ':8080' + ) + end + end + + context 'when configured with an external and internal Prometheus addresses' do + before do + stub_gitlab_rb( + prometheus: { + listen_address: '0.0.0.0:9191' + }, + gitlab_rails: { + prometheus_address: '1.1.1.1:2222' + } + ) + end + + it 'renders gitlab.yml with the external address' do + expect(gitlab_yml[:production][:prometheus]).to eq( + enabled: true, + server_address: '1.1.1.1:2222' + ) + end + end + + context 'when bundled Prometheus is disabled and no external Prometheus address is specified' do + before do + stub_gitlab_rb( + prometheus: { + enable: false + } + ) + end + + it 'renders gitlab.yml with Prometheus disabled' do + expect(gitlab_yml[:production][:prometheus][:enabled]).to be false + end + end + + context 'when bundled Prometheus is disabled but an external Prometheus address is specified' do + before do + stub_gitlab_rb( + prometheus: { + enable: false, + }, + gitlab_rails: { + prometheus_address: '1.2.3.4:2222' + } + ) + end + + it 'renders gitlab.yml with external Prometheus addreess' do + expect(gitlab_yml[:production][:prometheus]).to eq( + enabled: true, + server_address: '1.2.3.4:2222' + ) + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/pseudonymizer_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/pseudonymizer_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9wc2V1ZG9ueW1pemVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/pseudonymizer_spec.rb @@ -0,0 +1,47 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + include_context 'object storage config' + + describe 'pseudonymizer settings' do + context 'with default values' do + it 'renders gitlab.yml with pseudonymizer disabled' do + config = gitlab_yml[:production][:pseudonymizer] + expect(config).to eq( + { + manifest: nil, + upload: { + connection: {}, + remote_directory: nil + } + } + ) + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + gitlab_rails: { + pseudonymizer_manifest: 'another/path/manifest.yml', + pseudonymizer_upload_remote_directory: 'gitlab-pseudo', + pseudonymizer_upload_connection: aws_connection_hash, + } + ) + end + it 'renders gitlab.yml with user specified values' do + config = gitlab_yml[:production][:pseudonymizer] + expect(config).to eq( + { + manifest: 'another/path/manifest.yml', + upload: { + connection: aws_connection_data, + remote_directory: 'gitlab-pseudo' + } + } + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/registry_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/registry_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9yZWdpc3RyeV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/registry_spec.rb @@ -0,0 +1,122 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'Registry settings' do + context 'with default configuration' do + context 'when GitLab is running under http' do + it 'renders gitlab.yml without Registry settings' do + expect(gitlab_yml[:production][:registry]).to eq( + enabled: false, + api_url: nil, + host: nil, + port: nil, + issuer: 'omnibus-gitlab-issuer', + key: '/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key', + notification_secret: nil, + path: nil + ) + end + end + + context 'when GitLab is running under HTTPS using Omnibus automated TLS certificates' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com' + ) + end + + it 'automatically enables Registry and renders gitlab.yml accordingly' do + expect(gitlab_yml[:production][:registry]).to eq( + enabled: true, + host: 'gitlab.example.com', + port: 5050, + api_url: 'http://localhost:5000', + issuer: 'omnibus-gitlab-issuer', + key: '/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key', + notification_secret: nil, + path: '/var/opt/gitlab/gitlab-rails/shared/registry' + ) + end + end + + context 'with user specified configuration' do + context 'when URL for bundled Registry is specified' do + before do + stub_gitlab_rb( + registry_external_url: 'http://registry.example.com' + ) + end + + it 'renders gitlab.yml with specified Registry URL and other default Registry settings' do + expect(gitlab_yml[:production][:registry]).to eq( + enabled: true, + host: 'registry.example.com', + port: nil, + api_url: 'http://localhost:5000', + issuer: 'omnibus-gitlab-issuer', + key: '/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key', + notification_secret: nil, + path: '/var/opt/gitlab/gitlab-rails/shared/registry' + ) + end + end + + context 'when other Registry configuration are specified' do + before do + stub_gitlab_rb( + registry_external_url: 'http://registry.example.com:1234', + registry: { + registry_http_addr: 'localhost:1111' + } + ) + end + + it 'renders gitlab.yml with specified Registry settings' do + expect(gitlab_yml[:production][:registry]).to eq( + enabled: true, + host: 'registry.example.com', + port: 1234, + api_url: 'http://localhost:1111', + issuer: 'omnibus-gitlab-issuer', + key: '/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key', + notification_secret: nil, + path: '/var/opt/gitlab/gitlab-rails/shared/registry' + ) + end + end + + context 'with external Registry' do + before do + stub_gitlab_rb( + gitlab_rails: { + registry_enabled: 'true', + registry_key_path: '/fake/path', + registry_host: 'registry.example.com', + registry_api_url: 'http://registry.example.com:1234', + registry_port: 1234, + registry_issuer: 'foobar', + registry_notification_secret: 'qwerty', + registry_path: '/tmp/registry' + } + ) + end + + it 'renders gitlab.yml with correct registry settings' do + expect(gitlab_yml[:production][:registry]).to eq( + enabled: true, + host: 'registry.example.com', + port: 1234, + api_url: 'http://registry.example.com:1234', + issuer: 'foobar', + key: '/fake/path', + notification_secret: 'qwerty', + path: '/tmp/registry' + ) + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/repositories_storages_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/repositories_storages_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9yZXBvc2l0b3JpZXNfc3RvcmFnZXNfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/repositories_storages_spec.rb @@ -0,0 +1,62 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + using RSpec::Parameterized::TableSyntax + include_context 'gitlab-rails' + + describe 'repositories storages settings' do + context 'with default values' do + it 'renders gitlab.yml with default repository storage settings' do + expect(gitlab_yml[:production][:repositories][:storages]).to eq( + default: { + gitaly_address: 'unix:/var/opt/gitlab/gitaly/gitaly.socket', + path: '/var/opt/gitlab/git-data/repositories' + } + ) + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + git_data_dirs: { + second_storage: { + path: '/tmp/foobar', + gitaly_address: 'unix:/var/gitaly.socket' + } + } + ) + end + + it 'renders gitlab.yml with specified repository storage settings' do + expect(gitlab_yml[:production][:repositories][:storages]).to eq( + second_storage: { + gitaly_address: 'unix:/var/gitaly.socket', + path: '/tmp/foobar/repositories' + } + ) + end + + context 'when path is not provided for a storage' do + before do + stub_gitlab_rb( + git_data_dirs: { + default: { + gitaly_address: 'unix:/var/gitaly.socket' + } + } + ) + end + + it 'sets the default path' do + expect(gitlab_yml[:production][:repositories][:storages]).to eq( + default: { + gitaly_address: 'unix:/var/gitaly.socket', + path: '/var/opt/gitlab/git-data/repositories' + } + ) + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/sentry_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/sentry_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9zZW50cnlfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/sentry_spec.rb @@ -0,0 +1,44 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'sentry settings' do + context 'with default values' do + it 'renders gitlab.yml with sentry disabled' do + expect(gitlab_yml[:production][:sentry]).to eq( + { + enabled: false, + dsn: nil, + clientside_dsn: nil, + environment: nil + } + ) + end + end + + context 'when the user enables sentry' do + before do + stub_gitlab_rb( + gitlab_rails: { + sentry_enabled: true, + sentry_dsn: 'https://708cd0ca88972f04d5c836a395b8db63@example.com/76', + sentry_clientside_dsn: 'https://708cd0ca88972f04d5c836a395b8db63@example.com/77', + sentry_environment: 'testing' + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expect(gitlab_yml[:production][:sentry]).to eq( + { + enabled: true, + dsn: 'https://708cd0ca88972f04d5c836a395b8db63@example.com/76', + clientside_dsn: 'https://708cd0ca88972f04d5c836a395b8db63@example.com/77', + environment: 'testing' + } + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/service_desk_email_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/service_desk_email_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9zZXJ2aWNlX2Rlc2tfZW1haWxfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/service_desk_email_spec.rb @@ -0,0 +1,60 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'service desk email settings' do + describe 'log file location' do + context 'with default values' do + it 'renders gitlab.yml with default value for mailroom log file path' do + expect(gitlab_yml[:production][:service_desk_email][:log_path]).to eq '/var/log/gitlab/mailroom/mail_room_json.log' + end + end + + context 'with custom log file path specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + service_desk_email_log_file: '/my/custom/log/file' + } + ) + end + + it 'renders gitlab.yml with specified value for mailroom log file path' do + expect(gitlab_yml[:production][:service_desk_email][:log_path]).to eq '/my/custom/log/file' + end + end + + context 'with custom log directory specified' do + before do + stub_gitlab_rb( + mailroom: { + log_directory: '/my/custom/directory' + } + ) + end + + it 'renders gitlab.yml with default log file name inside specified directory' do + expect(gitlab_yml[:production][:service_desk_email][:log_path]).to eq '/my/custom/directory/mail_room_json.log' + end + end + + context 'with custom log file and directory specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + service_desk_email_log_file: '/foobar.log' + }, + mailroom: { + log_directory: '/my/custom/directory' + } + ) + end + + it 'renders gitlab.yml with specified log file path irrespective of specified directory' do + expect(gitlab_yml[:production][:service_desk_email][:log_path]).to eq '/foobar.log' + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/shutdown_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/shutdown_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9zaHV0ZG93bl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/shutdown_spec.rb @@ -0,0 +1,27 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'shutdown settings' do + context 'with default values' do + it 'renders gitlab.yml with default value for blackout seconds' do + expect(gitlab_yml[:production][:shutdown][:blackout_seconds]).to eq(10) + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + gitlab_rails: { + shutdown_blackout_seconds: 30 + } + ) + end + + it 'renders gitlab.yml with specified value for blackout seconds' do + expect(gitlab_yml[:production][:shutdown][:blackout_seconds]).to eq(30) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/sidekiq_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/sidekiq_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9zaWRla2lxX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/sidekiq_spec.rb @@ -0,0 +1,79 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'sidekiq settings' do + describe 'log_format' do + context 'with default values' do + it 'renders gitlab.yml with sidekiq log format set to json' do + expect(gitlab_yml[:production][:sidekiq][:log_format]).to eq('json') + end + end + + context 'with user specified value' do + before do + stub_gitlab_rb( + sidekiq: { + log_format: 'text' + } + ) + end + + it 'renders gitlab.yml with user specified value for sidekiq log format' do + expect(gitlab_yml[:production][:sidekiq][:log_format]).to eq('text') + end + end + end + + describe 'routing_rules' do + context 'with default values' do + it 'renders gitlab.yml without routing_rules' do + expect(gitlab_yml[:production][:sidekiq]).not_to include(:routing_rules) + end + end + + context 'with an empty array' do + before do + stub_gitlab_rb( + sidekiq: { + routing_rules: [] + } + ) + end + + it 'renders gitlab.yml without routing_rules' do + expect(gitlab_yml[:production][:sidekiq]).not_to include(:routing_rules) + end + end + + context 'with a valid routing rules list' do + before do + stub_gitlab_rb( + sidekiq: { + routing_rules: [ + ["resource_boundary=cpu", "cpu_boundary"], + ["feature_category=pages", nil], + ["feature_category=search", ''], + ["feature_category=memory|resource_boundary=memory", ''], + ["*", "default"] + ] + } + ) + end + + it 'renders gitlab.yml with user specified value for sidekiq routing rules' do + expect(gitlab_yml[:production][:sidekiq][:routing_rules]).to eq( + [ + ["resource_boundary=cpu", "cpu_boundary"], + ["feature_category=pages", nil], + ["feature_category=search", ""], + ["feature_category=memory|resource_boundary=memory", ""], + ["*", "default"] + ] + ) + end + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/smartcard_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/smartcard_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC9zbWFydGNhcmRfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/smartcard_spec.rb @@ -0,0 +1,46 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + include_context 'gitlab-rails' + + describe 'Smartcard authentication settings' do + context 'with default configuration' do + it 'renders gitlab.yml with Smartcard authentication disabled' do + expect(gitlab_yml[:production][:smartcard]).to eq( + enabled: false, + ca_file: '/etc/gitlab/ssl/CA.pem', + client_certificate_required_host: nil, + client_certificate_required_port: 3444, + required_for_git_access: false, + san_extensions: false + ) + end + end + + context 'with user specified configuration' do + before do + stub_gitlab_rb( + gitlab_rails: { + smartcard_enabled: true, + smartcard_ca_file: '/foobar/CA.pem', + smartcard_client_certificate_required_host: 'smartcard.gitlab.example.com', + smartcard_client_certificate_required_port: 123, + smartcard_required_for_git_access: true, + smartcard_san_extensions: true + } + ) + end + + it 'renders gitlab.yml with user specified values for Smartcard authentication' do + expect(gitlab_yml[:production][:smartcard]).to eq( + enabled: true, + ca_file: '/foobar/CA.pem', + client_certificate_required_host: 'smartcard.gitlab.example.com', + client_certificate_required_port: 123, + required_for_git_access: true, + san_extensions: true + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/unleash_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/unleash_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMvZ2l0bGFiX3ltbC91bmxlYXNoX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails/gitlab_yml/unleash_spec.rb @@ -0,0 +1,38 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + using RSpec::Parameterized::TableSyntax + include_context 'gitlab-rails' + + describe 'Unleash settings' do + context 'with default values' do + it 'renders gitlab.yml unleash disabled' do + expect(gitlab_yml[:production][:feature_flags][:unleash]).to eq( + enabled: false + ) + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + gitlab_rails: { + feature_flags_unleash_enabled: true, + feature_flags_unleash_url: 'foobar.com', + feature_flags_unleash_app_name: 'GitLab Production', + feature_flags_unleash_instance_id: 'foobar' + } + ) + end + + it 'renders gitlab.yml with user specified values' do + expect(gitlab_yml[:production][:feature_flags][:unleash]).to eq( + enabled: true, + url: 'foobar.com', + app_name: 'GitLab Production', + instance_id: 'foobar' + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHNfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb @@ -0,0 +1,1147 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-rails' do + using RSpec::Parameterized::TableSyntax + + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink runit_service)).converge('gitlab::default') } + let(:redis_instances) { %w(cache queues shared_state trace_chunks rate_limiting sessions) } + let(:config_dir) { '/var/opt/gitlab/gitlab-rails/etc/' } + let(:default_vars) do + { + 'HOME' => '/var/opt/gitlab', + 'RAILS_ENV' => 'production', + 'SIDEKIQ_MEMORY_KILLER_MAX_RSS' => '2000000', + 'BUNDLE_GEMFILE' => '/opt/gitlab/embedded/service/gitlab-rails/Gemfile', + 'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin', + 'ICU_DATA' => '/opt/gitlab/embedded/share/icu/current', + 'PYTHONPATH' => '/opt/gitlab/embedded/lib/python3.9/site-packages', + 'EXECJS_RUNTIME' => 'Disabled', + 'TZ' => ':/etc/localtime', + 'LD_PRELOAD' => '/opt/gitlab/embedded/lib/libjemalloc.so', + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + 'SSL_CERT_FILE' => '/opt/gitlab/embedded/ssl/cert.pem' + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(File).to receive(:symlink?).and_call_original + end + + context 'with defaults' do + it 'creates a default VERSION file and restarts services' do + expect(chef_run).to create_version_file('Create version file for Rails').with( + version_file_path: '/var/opt/gitlab/gitlab-rails/RUBY_VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/ruby --version' + ) + + dependent_services = [] + dependent_services.each do |svc| + expect(chef_run.version_file('Create version file for Rails')).to notify("runit_service[#{svc}]").to(:restart) + end + end + end + + context 'when manage-storage-directories is disabled' do + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb(gitlab_rails: { shared_path: '/tmp/shared', + uploads_directory: '/tmp/uploads', + uploads_storage_path: '/tmp/uploads_storage' }, + gitlab_ci: { builds_directory: '/tmp/builds' }, + git_data_dirs: { + "some_storage" => { + "path" => "/tmp/git-data" + } + }, + manage_storage_directories: { enable: false }) + end + + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it 'does not create the git-data directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/git-data') + end + + it 'does not create the repositories directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/git-data/repositories') + end + + it 'does not create the shared directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared') + end + + it 'does not create the artifacts directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/artifacts') + end + + it 'does not create the external-diffs directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/external-diffs') + end + + it 'does not create the lfs storage directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/lfs-objects') + end + + it 'does not create the packages storage directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/packages') + end + + it 'does not create the dependency_proxy storage directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/dependency_proxy') + end + + it 'does not create the terraform_state storage directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/terraform_state') + end + + it 'does not create the ci_secure_files storage directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/ci_secure_files') + end + + it 'does not create the GitLab pages directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/pages') + end + + it 'does not create the uploads directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/uploads') + end + + it 'does not create the ci builds directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/builds') + end + + it 'does not create the uploads storage directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/uploads_storage') + end + end + + context 'when manage-storage-directories is enabled' do + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb(gitlab_rails: { shared_path: '/tmp/shared', + uploads_directory: '/tmp/uploads', + uploads_storage_path: '/tmp/uploads_storage' }, + gitlab_ci: { builds_directory: '/tmp/builds' }, + git_data_dirs: { + "some_storage" => { + "path" => "/tmp/git-data" + } + }) + end + + ChefSpec::SoloRunner.converge('gitlab::default') + end + + it 'creates the git-data directory' do + expect(chef_run).to create_storage_directory('/tmp/git-data').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the repositories directory' do + expect(chef_run).to create_storage_directory('/tmp/git-data/repositories').with(owner: 'git', group: 'git', mode: '2770') + end + + it 'creates the shared directory' do + expect(chef_run).to create_storage_directory('/tmp/shared').with(owner: 'git', group: 'gitlab-www', mode: '0751') + end + + it 'creates the artifacts directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/artifacts').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the external-diffs directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/external-diffs').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the lfs storage directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/lfs-objects').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the packages directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/packages').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the dependency_proxy directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/dependency_proxy').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the terraform_state directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/terraform_state').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the ci_secure_files directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/ci_secure_files').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the encrypted_settings directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/encrypted_settings').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the GitLab pages directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/pages').with(owner: 'git', group: 'gitlab-www', mode: '0750') + end + + it 'creates the shared tmp directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/tmp').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the shared cache directory' do + expect(chef_run).to create_storage_directory('/tmp/shared/cache').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the uploads directory' do + expect(chef_run).to create_storage_directory('/tmp/uploads').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the ci builds directory' do + expect(chef_run).to create_storage_directory('/tmp/builds').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the uploads storage directory' do + expect(chef_run).to create_storage_directory('/tmp/uploads_storage').with(owner: 'git', group: 'git', mode: '0700') + end + end + + context 'when uploads storage directory is not specified' do + cached(:chef_run) do + ChefSpec::SoloRunner.converge('gitlab::default') + end + + it 'does not create the uploads storage directory' do + expect(chef_run).not_to create_storage_directory('/opt/gitlab/embedded/service/gitlab-rails/public') + end + end + + context 'with redis settings' do + let(:config_file) { '/var/opt/gitlab/gitlab-rails/etc/resque.yml' } + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } + + context 'and default configuration' do + it 'creates the config file with the required redis settings' do + expect(chef_run).to create_templatesymlink('Create a resque.yml and create a symlink to Rails root').with_variables( + hash_including( + redis_url: URI('unix:/var/opt/gitlab/redis/redis.socket'), + redis_sentinels: [], + redis_enable_client: true + ) + ) + + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(url: unix:/var/opt/gitlab/redis/redis.socket$)) + expect(content).not_to match(/id:/) + } + end + + it 'creates cable.yml with the same settings' do + expect(chef_run).to create_templatesymlink('Create a cable.yml and create a symlink to Rails root').with_variables( + hash_including( + redis_url: URI('unix:/var/opt/gitlab/redis/redis.socket'), + redis_sentinels: [], + redis_enable_client: true + ) + ) + + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/cable.yml').with_content { |content| + expect(content).to match(%r(url: unix:/var/opt/gitlab/redis/redis.socket$)) + } + end + + it 'does not render the separate instance configurations' do + redis_instances.each do |instance| + expect(chef_run).not_to render_file("#{config_dir}redis.#{instance}.yml") + end + end + + it 'deletes the separate instance config files' do + redis_instances.each do |instance| + expect(chef_run).to delete_file("/opt/gitlab/embedded/service/gitlab-rails/config/redis.#{instance}.yml") + expect(chef_run).to delete_file("/var/opt/gitlab/gitlab-rails/etc/redis.#{instance}.yml") + end + end + end + + context 'and custom configuration' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_port: 8888, + redis_database: 2, + redis_password: 'mypass', + redis_enable_client: false + } + ) + end + + it 'creates the config file with custom host, port, password and database' do + expect(chef_run).to create_templatesymlink('Create a resque.yml and create a symlink to Rails root').with_variables( + hash_including( + redis_url: URI('redis://:mypass@redis.example.com:8888/2'), + redis_sentinels: [], + redis_enable_client: false + ) + ) + + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(url: redis://:mypass@redis.example.com:8888/2)) + expect(content).to match(/id:$/) + } + end + + it 'creates cable.yml with custom host, port, password and database' do + expect(chef_run).to create_templatesymlink('Create a cable.yml and create a symlink to Rails root').with_variables( + hash_including( + redis_url: URI('redis://:mypass@redis.example.com:8888/2'), + redis_sentinels: [], + redis_enable_client: false + ) + ) + + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(url: redis://:mypass@redis.example.com:8888/2)) + expect(content).to match(/id:$/) + } + end + end + + context 'with multiple instances' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_enable_client: false, + redis_cache_instance: "redis://:fakepass@fake.redis.cache.com:8888/2", + redis_cache_sentinels: [ + { host: 'cache', port: '1234' }, + { host: 'cache', port: '3456' } + ], + redis_queues_instance: "redis://:fakepass@fake.redis.queues.com:8888/2", + redis_queues_sentinels: [ + { host: 'queues', port: '1234' }, + { host: 'queues', port: '3456' } + ], + redis_shared_state_instance: "redis://:fakepass@fake.redis.shared_state.com:8888/2", + redis_shared_state_sentinels: [ + { host: 'shared_state', port: '1234' }, + { host: 'shared_state', port: '3456' } + ], + redis_trace_chunks_instance: "redis://:fakepass@fake.redis.trace_chunks.com:8888/2", + redis_trace_chunks_sentinels: [ + { host: 'trace_chunks', port: '1234' }, + { host: 'trace_chunks', port: '3456' } + ], + redis_rate_limiting_instance: "redis://:fakepass@fake.redis.rate_limiting.com:8888/2", + redis_rate_limiting_sentinels: [ + { host: 'rate_limiting', port: '1234' }, + { host: 'rate_limiting', port: '3456' } + ], + redis_sessions_instance: "redis://:fakepass@fake.redis.sessions.com:8888/2", + redis_sessions_sentinels: [ + { host: 'sessions', port: '1234' }, + { host: 'sessions', port: '3456' } + ], + redis_actioncable_instance: "redis://:fakepass@fake.redis.actioncable.com:8888/2", + redis_actioncable_sentinels: [ + { host: 'actioncable', port: '1234' }, + { host: 'actioncable', port: '3456' } + ] + } + ) + end + + it 'render separate config files' do + redis_instances.each do |instance| + expect(chef_run).to create_templatesymlink("Create a redis.#{instance}.yml and create a symlink to Rails root").with_variables( + redis_url: "redis://:fakepass@fake.redis.#{instance}.com:8888/2", + redis_sentinels: [{ "host" => instance, "port" => "1234" }, { "host" => instance, "port" => "3456" }], + redis_enable_client: false + ) + expect(chef_run).not_to delete_file("/var/opt/gitlab/gitlab-rails/etc/redis.#{instance}.yml") + end + end + + it 'still renders the default configuration file' do + expect(chef_run).to create_templatesymlink('Create a resque.yml and create a symlink to Rails root') + end + + it 'creates cable.yml with custom settings' do + expect(chef_run).to create_templatesymlink('Create a cable.yml and create a symlink to Rails root').with_variables( + hash_including( + redis_url: "redis://:fakepass@fake.redis.actioncable.com:8888/2", + redis_sentinels: [{ 'host' => 'actioncable', 'port' => '1234' }, { 'host' => 'actioncable', 'port' => '3456' }], + redis_enable_client: false + ) + ) + end + end + end + + describe 'gitlab.yml' do + gitlab_yml_path = '/var/opt/gitlab/gitlab-rails/etc/gitlab.yml' + let(:gitlab_yml) { chef_run.template(gitlab_yml_path) } + let(:gitlab_yml_templatesymlink) { chef_run.templatesymlink('Create a gitlab.yml and create a symlink to Rails root') } + + # NOTE: Test if we pass proper notifications to other resources + describe 'rails cache management' do + before do + stub_default_not_listening?(false) + end + + context 'with default values' do + it 'should notify rails cache clear resource' do + expect(gitlab_yml_templatesymlink).to notify('execute[clear the gitlab-rails cache]') + end + end + + context 'with rake_cache_clear set to false' do + before do + stub_gitlab_rb(gitlab_rails: { rake_cache_clear: false }) + end + + it 'should notify rails cache clear resource' do + expect(gitlab_yml_templatesymlink).to notify( + 'execute[clear the gitlab-rails cache]') + end + + it 'should not run cache clear' do + expect(chef_run).not_to run_execute( + 'clear the gitlab-rails cache') + end + end + end + end + + context 'with environment variables' do + context 'by default' do + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables(default_vars) + end + + context 'when a custom env variable is specified' do + before do + stub_gitlab_rb(gitlab_rails: { env: { 'IAM' => 'CUSTOMVAR' } }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables( + default_vars.merge( + { + 'IAM' => 'CUSTOMVAR' + } + ) + ) + end + end + end + + context 'when relative URL is enabled' do + before do + stub_gitlab_rb(gitlab_rails: { gitlab_relative_url: '/gitlab' }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables( + default_vars.merge( + { + 'RAILS_RELATIVE_URL_ROOT' => '/gitlab' + } + ) + ) + end + end + + context 'when relative URL is specified in external_url' do + before do + stub_gitlab_rb(external_url: 'http://localhost/gitlab') + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables( + default_vars.merge( + { + 'RAILS_RELATIVE_URL_ROOT' => '/gitlab' + } + ) + ) + end + end + + context 'when jemalloc is disabled' do + before do + stub_gitlab_rb(gitlab_rails: { enable_jemalloc: false }) + end + + it 'creates necessary env variable files' do + vars = default_vars.dup + vars.delete("LD_PRELOAD") + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables(vars) + end + end + end + + describe "with symlinked templates" do + let(:chef_run) { ChefSpec::SoloRunner.new.converge('gitlab::default') } + + before do + %w( + alertmanager + gitlab-exporter + gitlab-pages + gitlab-kas + gitlab-workhorse + logrotate + nginx + node-exporter + postgres-exporter + postgresql + prometheus + redis + redis-exporter + sidekiq + puma + gitaly + ).map { |svc| stub_should_notify?(svc, true) } + end + + describe 'database.yml' do + database_yml_path = '/var/opt/gitlab/gitlab-rails/etc/database.yml' + let(:database_yml) { chef_run.template(database_yml_path) } + let(:database_yml_content) { ChefSpec::Renderer.new(chef_run, database_yml).content } + let(:generated_yml_content) { YAML.safe_load(database_yml_content) } + + let(:config_file) { database_yml_path } + let(:templatesymlink) { chef_run.templatesymlink('Create a database.yml and create a symlink to Rails root') } + + context 'by default' do + cached(:chef_run) do + ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') + end + + it 'creates the template' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_host' => '/var/opt/gitlab/postgresql', + 'db_database' => 'gitlabhq_production', + 'db_load_balancing' => { 'hosts' => [] }, + 'db_prepared_statements' => false, + 'db_sslcompression' => 0, + 'db_sslcert' => nil, + 'db_sslkey' => nil, + 'db_application_name' => nil + ) + ) + end + + it 'template triggers notifications' do + expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed + expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed + expect(templatesymlink).not_to notify('runit_service[gitlab-workhorse]').to(:restart).delayed + expect(templatesymlink).not_to notify('runit_service[nginx]').to(:restart).delayed + end + + it 'renders expected YAML' do + expect(generated_yml_content.dig('production', 'main', 'adapter')).to eq('postgresql') + expect(generated_yml_content.dig('production', 'main', 'host')).to eq('/var/opt/gitlab/postgresql') + expect(generated_yml_content.dig('production', 'main', 'port')).to eq(5432) + expect(generated_yml_content.dig('production', 'main', 'application_name')).to eq(nil) + end + end + + context 'with specific database settings' do + context 'with an application name set' do + where(:appname, :expected) do + '' | '' + 'test' | 'test' + end + + with_them do + cached(:chef_run) do + ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') + end + + before do + stub_gitlab_rb( + 'gitlab_rails' => { 'db_application_name' => appname } + ) + end + + it 'renders expected YAML' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_application_name' => appname + ) + ) + + expect(generated_yml_content.dig('production', 'main').keys).to include(*%w(adapter host port application_name)) + expect(generated_yml_content.dig('production', 'main', 'application_name')).to eq(expected) + end + end + end + + context 'when multiple postgresql listen_address is used' do + before do + stub_gitlab_rb(postgresql: { listen_address: "127.0.0.1,1.1.1.1" }) + end + + it 'creates the postgres configuration file with multi listen_address and database.yml file with one host' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_host' => '127.0.0.1' + ) + ) + end + end + + context 'when no postgresql listen_address is used' do + it 'creates the postgres configuration file with empty listen_address and database.yml file with default one' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_host' => '/var/opt/gitlab/postgresql' + ) + ) + end + end + + context 'when one postgresql listen_address is used' do + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb(postgresql: { listen_address: "127.0.0.1" }) + end + + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it 'creates the postgres configuration file with one listen_address and database.yml file with one host' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_host' => '127.0.0.1' + ) + ) + end + + it 'template triggers notifications' do + expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed + expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed + expect(templatesymlink).not_to notify('runit_service[gitlab-workhorse]').to(:restart).delayed + expect(templatesymlink).not_to notify('runit_service[nginx]').to(:restart).delayed + end + end + + context 'when load balancers are specified' do + before do + stub_gitlab_rb(gitlab_rails: { db_load_balancing: { 'hosts' => ['primary.example.com', 'secondary.example.com'] } }) + end + + it 'uses provided value in database.yml' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_load_balancing' => { 'hosts' => ['primary.example.com', 'secondary.example.com'] } + ) + ) + end + end + + context 'when prepared_statements are disabled' do + before do + stub_gitlab_rb(gitlab_rails: { db_prepared_statements: false }) + end + + it 'uses provided value in database.yml' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_prepared_statements' => false, + 'db_statements_limit' => 1000 + ) + ) + end + end + + context 'when limit for prepared_statements are specified' do + before do + stub_gitlab_rb(gitlab_rails: { db_statements_limit: 12345 }) + end + + it 'uses provided value in database.yml' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_prepared_statements' => false, + 'db_statements_limit' => 12345 + ) + ) + end + end + + context 'when SSL compression is enabled' do + before do + stub_gitlab_rb(gitlab_rails: { db_sslcompression: 1 }) + end + + it 'uses provided value in database.yml' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_sslcompression' => 1 + ) + ) + end + end + + context 'when SSL certificate and key for DB is specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + db_sslcert: '/etc/certs/db.cer', + db_sslkey: '/etc/certs/db.key' + } + ) + end + + it 'uses specified value in database.yml' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_sslcert' => '/etc/certs/db.cer', + 'db_sslkey' => '/etc/certs/db.key' + ) + ) + end + end + end + + describe 'client side statement_timeout' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } + + context 'default values' do + it 'does not set a default value' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_statement_timeout' => nil + ) + ) + + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(statement_timeout: $)) + } + end + end + + context 'custom value' do + before do + stub_gitlab_rb( + 'postgresql' => { 'statement_timeout' => '65000' }, + 'gitlab_rails' => { 'db_statement_timeout' => '70000' } + ) + end + + it 'uses specified client side statement_timeout value' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + 'db_statement_timeout' => '70000' + ) + ) + + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(statement_timeout: 70000$)) + } + end + end + end + + context 'adjusting database adapter connection parameters' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } + + using RSpec::Parameterized::TableSyntax + + where(:rb_param, :yaml_param, :default_value, :custom_value) do + 'db_connect_timeout' | 'connect_timeout' | nil | 5 + 'db_keepalives' | 'keepalives' | nil | 1 + 'db_keepalives_idle' | 'keepalives_idle' | nil | 5 + 'db_keepalives_interval' | 'keepalives_interval' | nil | 3 + 'db_keepalives_count' | 'keepalives_count' | nil | 3 + 'db_tcp_user_timeout' | 'tcp_user_timeout' | nil | 13000 + end + + with_them do + context 'default values' do + it 'does not set a default value' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + rb_param => default_value + ) + ) + + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(#{yaml_param}: $)) + } + end + end + + context 'custom connection parameter value' do + before do + stub_gitlab_rb( + 'gitlab_rails' => { rb_param => custom_value } + ) + end + + it 'uses specified connection parameter value' do + expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( + hash_including( + rb_param => custom_value + ) + ) + + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(#{yaml_param}: #{custom_value}$)) + } + end + end + end + end + end + + describe 'gitlab_workhorse_secret' do + let(:templatesymlink) { chef_run.templatesymlink('Create a gitlab_workhorse_secret and create a symlink to Rails root') } + + context 'by default' do + cached(:chef_run) do + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it 'creates the template' do + expect(chef_run).to create_templatesymlink("Create a gitlab_workhorse_secret and create a symlink to Rails root").with( + owner: 'root', + group: 'root', + mode: '0644' + ) + end + + it 'template triggers notifications' do + expect(templatesymlink).to notify('runit_service[gitlab-workhorse]').to(:restart).delayed + expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed + expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed + end + end + + context 'with specific gitlab_workhorse_secret' do + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb(gitlab_workhorse: { secret_token: 'abc123-gitlab-workhorse' }) + end + + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it 'renders the correct node attribute' do + expect(chef_run).to create_templatesymlink("Create a gitlab_workhorse_secret and create a symlink to Rails root").with_variables( + secret_token: 'abc123-gitlab-workhorse' + ) + end + + it 'uses the correct owner and permissions' do + expect(chef_run).to create_templatesymlink('Create a gitlab_workhorse_secret and create a symlink to Rails root').with( + owner: 'root', + group: 'root', + mode: '0644' + ) + end + + it 'template triggers notifications' do + expect(templatesymlink).to notify('runit_service[gitlab-workhorse]').to(:restart).delayed + expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed + expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed + end + end + end + + describe 'gitlab_pages_secret' do + let(:templatesymlink) { chef_run.templatesymlink('Create a gitlab_pages_secret and create a symlink to Rails root') } + + context 'with pages disabled' do + let(:api_secret_key) { SecureRandom.base64(32) } + + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb( + pages_enabled: false, + gitlab_pages: { api_secret_key: api_secret_key, enable: false }, + pages_external_url: 'http://pages.example.com' + ) + end + + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it 'creates the template' do + expect(chef_run).to create_templatesymlink("Create a gitlab_pages_secret and create a symlink to Rails root").with( + owner: 'root', + group: 'root', + mode: '0644' + ) + end + end + + context 'by default' do + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb( + external_url: 'http://gitlab.example.com', + pages_external_url: 'http://pages.example.com' + ) + end + + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it 'creates the template' do + expect(chef_run).to create_templatesymlink("Create a gitlab_pages_secret and create a symlink to Rails root").with( + owner: 'root', + group: 'root', + mode: '0644' + ) + end + + it 'template triggers notifications' do + expect(templatesymlink).to notify('runit_service[gitlab-pages]').to(:restart).delayed + expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed + expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed + end + end + + context 'with specific gitlab_pages_secret' do + let(:api_secret_key) { SecureRandom.base64(32) } + + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb( + gitlab_pages: { api_secret_key: api_secret_key }, + external_url: 'http://gitlab.example.com', + pages_external_url: 'http://pages.example.com' + ) + end + + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it 'renders the correct node attribute' do + expect(chef_run).to create_templatesymlink("Create a gitlab_pages_secret and create a symlink to Rails root").with_variables( + secret_token: api_secret_key + ) + end + + it 'uses the correct owner and permissions' do + expect(chef_run).to create_templatesymlink('Create a gitlab_pages_secret and create a symlink to Rails root').with( + owner: 'root', + group: 'root', + mode: '0644' + ) + end + + it 'template triggers notifications' do + expect(templatesymlink).to notify('runit_service[gitlab-pages]').to(:restart).delayed + expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed + expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed + end + end + end + + describe 'gitlab_kas_secret' do + let(:templatesymlink) { chef_run.templatesymlink('Create a gitlab_kas_secret and create a symlink to Rails root') } + + shared_examples 'creates the KAS template' do + it 'creates the template' do + expect(chef_run).to create_templatesymlink('Create a gitlab_kas_secret and create a symlink to Rails root').with( + owner: 'root', + group: 'root', + mode: '0644' + ) + end + end + + context 'with KAS disabled' do + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb( + gitlab_kas: { enable: false } + ) + end + + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it_behaves_like 'creates the KAS template' + end + + context 'with KAS enabled' do + cached(:chef_run) do + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it_behaves_like 'creates the KAS template' + + it 'template triggers notifications' do + expect(templatesymlink).to notify('runit_service[gitlab-kas]').to(:restart).delayed + expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed + expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed + end + end + + context 'with specific gitlab_kas_secret' do + let(:api_secret_key) { SecureRandom.base64(32) } + + cached(:chef_run) do + RSpec::Mocks.with_temporary_scope do + stub_gitlab_rb( + gitlab_kas: { api_secret_key: api_secret_key } + ) + end + + ChefSpec::SoloRunner.new.converge('gitlab::default') + end + + it 'renders the correct node attribute' do + expect(chef_run).to create_templatesymlink('Create a gitlab_kas_secret and create a symlink to Rails root').with_variables( + secret_token: api_secret_key + ) + end + + it_behaves_like 'creates the KAS template' + + it 'template triggers notifications' do + expect(templatesymlink).to notify('runit_service[gitlab-kas]').to(:restart).delayed + expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed + expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed + end + end + end + end + + describe 'GitLab Registry files' do + describe 'gitlab-registry.key file' do + context 'Registry is disabled' do + it 'does not generate gitlab-registry.key file' do + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key") + end + end + + context 'Registry is enabled' do + context 'with default configuration' do + before do + stub_gitlab_rb( + gitlab_rails: { + registry_enabled: true + } + ) + end + + it 'generates key file in the default location' do + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key").with_content(/\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m) + end + end + + context 'with user specified configuration' do + context 'when location of key file is specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + registry_enabled: true, + registry_key_path: '/fake/path' + } + ) + end + + it 'generates key file in the specified location' do + expect(chef_run).to render_file("/fake/path").with_content(/\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m) + end + end + + context 'when key content is specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + registry_enabled: true + }, + registry: { + internal_key: 'foobar' + } + ) + end + + it 'generates key file with specified content' do + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key").with_content('foobar') + end + end + end + end + end + end + + context 'SMTP settings' do + context 'when connection pooling is not configured' do + it 'creates smtp_settings.rb with pooling disabled' do + stub_gitlab_rb( + gitlab_rails: { + smtp_enable: true + } + ) + + expect(chef_run).to create_templatesymlink('Create a smtp_settings.rb and create a symlink to Rails root').with_variables( + hash_including( + 'smtp_pool' => false + ) + ) + end + end + + context 'when connection pooling is enabled' do + it 'creates smtp_settings.rb with pooling enabled' do + stub_gitlab_rb( + gitlab_rails: { + smtp_enable: true, + smtp_pool: true + } + ) + + expect(chef_run).to create_templatesymlink('Create a smtp_settings.rb and create a symlink to Rails root').with_variables( + hash_including( + 'smtp_pool' => true + ) + ) + + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/smtp_settings.rb').with_content { |content| + expect(content).to include('ActionMailer::Base.delivery_method = :smtp_pool') + } + end + end + end + + describe 'logrotate settings' do + context 'default values' do + it_behaves_like 'configured logrotate service', 'gitlab-pages', 'git', 'git' + end + + context 'specified username and group' do + before do + stub_gitlab_rb( + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'configured logrotate service', 'gitlab-pages', 'foo', 'bar' + end + end + + describe 'cleaning up the legacy sidekiq log symlink' do + it 'removes the link if it existed' do + allow(File).to receive(:symlink?).with('/var/log/gitlab/gitlab-rails/sidekiq.log') { true } + + expect(chef_run).to delete_link('/var/log/gitlab/gitlab-rails/sidekiq.log') + end + + it 'does nothing if it did not exist' do + allow(File).to receive(:symlink?).with('/var/log/gitlab/gitlab-rails/sidekiq.log') { false } + + expect(chef_run).not_to delete_link('/var/log/gitlab/gitlab-rails/sidekiq.log') + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-shell_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-shell_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItc2hlbGxfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-shell_spec.rb @@ -0,0 +1,265 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-shell' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'logrotate settings' do + context 'default values' do + it_behaves_like 'configured logrotate service', 'gitlab-shell', 'git', 'git' + end + + context 'specified username and group' do + before do + stub_gitlab_rb( + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'configured logrotate service', 'gitlab-shell', 'foo', 'bar' + end + end + + it 'defaults the auth_file to be within the user\'s home directory' do + stub_gitlab_rb(user: { home: '/tmp/user' }) + expect(chef_run.node['gitlab']['gitlab-shell']['auth_file']).to eq('/tmp/user/.ssh/authorized_keys') + end + + it 'uses custom auth_files set in gitlab.rb' do + stub_gitlab_rb(user: { home: '/tmp/user' }, gitlab_shell: { auth_file: '/tmp/authorized_keys' }) + expect(chef_run.node['gitlab']['gitlab-shell']['auth_file']).to eq('/tmp/authorized_keys') + end + + it 'creates authorized_keys file if missing' do + stub_gitlab_rb(gitlab_shell: { auth_file: '/tmp/authorized_keys' }) + expect(chef_run).to create_file_if_missing('/tmp/authorized_keys') + end + + context 'with default settings' do + it 'create config file in default location with default values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + log_file: '/var/log/gitlab/gitlab-shell/gitlab-shell.log', + log_format: "json", + migration: { enabled: true, features: [] }, + gitlab_url: 'http+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket', + gitlab_relative_path: '', + ssl_cert_dir: '/opt/gitlab/embedded/ssl/certs/' + ) + ) + end + end + + context 'with a non-default directory' do + before do + stub_gitlab_rb(gitlab_shell: { + dir: '/export/gitlab/gitlab-shell', + }) + end + + it 'create config file in specified location with default values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_link_to('/export/gitlab/gitlab-shell/config.yml') + end + end + + context 'with a non-default log directory' do + before do + stub_gitlab_rb(gitlab_shell: { + log_directory: '/tmp/log', + git_trace_log_file: '/tmp/log/gitlab-shell-git-trace.log' + }) + end + + it 'create config file with provided values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + log_file: '/tmp/log/gitlab-shell.log', + git_trace_log_file: '/tmp/log/gitlab-shell-git-trace.log' + ) + ) + end + end + + context 'when using the default auth_file location' do + before { stub_gitlab_rb(user: { home: '/tmp/user' }) } + + it 'creates the ssh dir in the user\'s home directory' do + expect(chef_run).to create_storage_directory('/tmp/user/.ssh').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the config file with the auth_file within user\'s ssh directory' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + authorized_keys: '/tmp/user/.ssh/authorized_keys' + ) + ) + end + end + + context 'when using a different location for auth_file' do + before { stub_gitlab_rb(user: { home: '/tmp/user' }, gitlab_shell: { auth_file: '/tmp/ssh/authorized_keys' }) } + + it 'creates the ssh dir in the user\'s home directory' do + expect(chef_run).to create_storage_directory('/tmp/user/.ssh').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the auth_file\'s parent directory' do + expect(chef_run).to create_storage_directory('/tmp/ssh').with(owner: 'git', group: 'git', mode: '0700') + end + + it 'creates the config file with the auth_file at the specified location' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + authorized_keys: '/tmp/ssh/authorized_keys' + ) + ) + end + end + + context 'when manage-storage-directories is disabled' do + before { stub_gitlab_rb(user: { home: '/tmp/user' }, manage_storage_directories: { enable: false }) } + + it 'doesn\'t create the ssh dir in the user\'s home directory' do + expect(chef_run).not_to run_ruby_block('directory resource: /tmp/user/.ssh') + end + end + + context 'with custom settings' do + before do + stub_gitlab_rb( + gitlab_shell: { + log_format: 'json', + migration: { + enabled: true, + features: ['discover'] + } + } + ) + end + + it 'creates the config file with custom values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + log_format: 'json', + migration: { + enabled: true, + features: ['discover'] + } + ) + ) + end + + context 'migration is disabled (set to false)' do + before do + stub_gitlab_rb( + gitlab_shell: { + migration: { + enabled: false, + features: [] + } + } + ) + end + + it 'creates the config file with migration disabled' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + migration: { + enabled: false, + features: [] + } + ) + ) + end + end + + context 'with a non-default workhorse unix socket' do + context 'without sockets_directory defined' do + before do + stub_gitlab_rb(gitlab_workhorse: { listen_addr: '/fake/workhorse/socket' }) + end + + it 'create config file with provided values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + gitlab_url: 'http+unix://%2Ffake%2Fworkhorse%2Fsocket', + gitlab_relative_path: '' + ) + ) + end + end + + context 'with sockets_directory defined' do + before do + stub_gitlab_rb(gitlab_workhorse: { 'sockets_directory': '/fake/workhorse/sockets/' }) + end + + it 'create config file with provided values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + gitlab_url: 'http+unix://%2Ffake%2Fworkhorse%2Fsockets%2Fsocket', + gitlab_relative_path: '' + ) + ) + end + end + end + + context 'with a tcp workhorse listener' do + before do + stub_gitlab_rb( + external_url: 'http://example.com/gitlab', + gitlab_workhorse: { + listen_network: 'tcp', + listen_addr: 'localhost:1234' + } + ) + end + + it 'create config file with provided values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + gitlab_url: 'http://localhost:1234/gitlab', + gitlab_relative_path: nil + ) + ) + end + end + + context 'with relative path in external_url' do + before do + stub_gitlab_rb(external_url: 'http://example.com/gitlab') + end + + it 'create config file with provided values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + gitlab_url: 'http+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket', + gitlab_relative_path: '/gitlab' + ) + ) + end + end + + context 'with internal_api_url specified' do + before do + stub_gitlab_rb(gitlab_rails: { internal_api_url: 'http://localhost:8080' }) + end + + it 'create config file with provided values' do + expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( + hash_including( + gitlab_url: 'http://localhost:8080', + gitlab_relative_path: '' + ) + ) + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-workhorse_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-workhorse_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItd29ya2hvcnNlX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-workhorse_spec.rb @@ -0,0 +1,580 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-workhorse' do + let(:node_cpus) { 1 } + let(:chef_run) do + ChefSpec::SoloRunner.new(step_into: %w(runit_service)) do |node| + node.automatic['cpu']['total'] = node_cpus + end.converge('gitlab::default') + end + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + 'HOME' => '/var/opt/gitlab', + 'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin' + } + end + let(:config_file) { '/var/opt/gitlab/gitlab-workhorse/config.toml' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'by default' do + it 'creates a default VERSION file and restarts service' do + expect(chef_run).to create_version_file('Create version file for Workhorse').with( + version_file_path: '/var/opt/gitlab/gitlab-workhorse/VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/gitlab-workhorse --version' + ) + + expect(chef_run.version_file('Create version file for Workhorse')).to notify('runit_service[gitlab-workhorse]').to(:restart) + end + + it 'includes both authSocket and authBackend arguments' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| + expect(content).to match(%r(-authSocket /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket)) + expect(content).to match(%r(-authBackend http://localhost:8080)) + } + end + + it 'does not include alternate document root' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).not_to match(/alt_document_root/) + } + end + + it 'does not include shutdown timeout' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).not_to match(/shutdown_timeout/) + } + end + + it 'does not include object storage configs' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).not_to match(/object_storage/) + } + end + + it 'does not propagate correlation ID' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).not_to match(/propagateCorrelationID/) + } + end + end + + context 'user and group' do + context 'default values' do + it_behaves_like "enabled runit service", "gitlab-workhorse", "root", "root" + it_behaves_like 'configured logrotate service', 'gitlab-workhorse', 'git', 'git' + end + + context 'custom values' do + before do + stub_gitlab_rb( + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like "enabled runit service", "gitlab-workhorse", "root", "root" + it_behaves_like 'configured logrotate service', 'gitlab-workhorse', 'foo', 'bar' + end + end + + context 'with environment variables' do + context 'by default' do + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-workhorse/env').with_variables(default_vars) + end + + context 'when a custom env variable is specified' do + before do + stub_gitlab_rb(gitlab_workhorse: { env: { 'IAM' => 'CUSTOMVAR' } }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-workhorse/env').with_variables( + default_vars.merge( + { + 'IAM' => 'CUSTOMVAR' + } + ) + ) + end + end + end + end + + context 'with alternate document root' do + before do + stub_gitlab_rb(gitlab_workhorse: { alt_document_root: '/tmp/test' }) + end + + it 'includes alternate document root setting' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(alt_document_root = "/tmp/test")) + } + end + end + + context 'with shutdown timeout' do + before do + stub_gitlab_rb(gitlab_workhorse: { shutdown_timeout: '60s' }) + end + + it 'includes alternate document root setting' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(%r(shutdown_timeout = "60s")) + } + end + end + + context 'auth_socket and auth_backend' do + context 'with only auth_socket specified' do + context "auth_socket set to legacy '' value" do + before do + stub_gitlab_rb(gitlab_workhorse: { auth_socket: "''" }) + end + + it 'includes both authSocket and authBackend arguments' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| + expect(content).to match(%r(-authSocket /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket)) + expect(content).to match(%r(-authBackend http://localhost:8080)) + } + end + end + end + + context 'with only auth_backend specified' do + before do + stub_gitlab_rb(gitlab_workhorse: { auth_backend: 'https://test.example.com:8080' }) + end + + it 'omits authSocket argument' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| + expect(content).not_to match(/\-authSocket/) + expect(content).to match(%r(-authBackend https://test.example.com:8080)) + } + end + end + + context "with nil auth_socket" do + before do + stub_gitlab_rb(gitlab_workhorse: { auth_socket: nil }) + end + + it 'includes both authSocket and authBackend arguments' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| + expect(content).to match(%r(-authSocket /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket)) + expect(content).to match(%r(-authBackend http://localhost:8080)) + } + end + + context 'with auth_backend specified' do + before do + stub_gitlab_rb(gitlab_workhorse: { auth_socket: nil, auth_backend: 'https://test.example.com:8080' }) + end + + it 'omits authSocket argument' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| + expect(content).not_to match(/\-authSocket/) + expect(content).to match(%r(-authBackend https://test.example.com:8080)) + } + end + end + end + + context 'with auth_backend and auth_socket set' do + before do + stub_gitlab_rb(gitlab_workhorse: { auth_socket: '/tmp/test.socket', auth_backend: 'https://test.example.com:8080' }) + end + + it 'includes both authSocket and authBackend arguments' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| + expect(content).to match(%r(-authSocket /tmp/test.socket)) + expect(content).to match(%r(-authBackend https://test.example.com:8080)) + } + end + end + end + + context 'consolidated object store settings' do + include_context 'object storage config' + + context 'with S3 config' do + before do + stub_gitlab_rb( + gitlab_rails: { + object_store: { + enabled: true, + connection: aws_connection_hash, + objects: object_config + } + } + ) + end + + it 'includes S3 credentials' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(/\[object_storage\]\n provider = "AWS"\n/m) + expect(content).to match(/\[object_storage.s3\]\n aws_access_key_id = "AKIAKIAKI"\n aws_secret_access_key = "secret123"\n/m) + } + end + end + + context 'with Azure config' do + before do + stub_gitlab_rb( + gitlab_rails: { + object_store: { + enabled: true, + connection: azure_connection_hash, + objects: object_config + } + } + ) + end + + it 'includes Azure credentials' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(/\[object_storage\]\n provider = "AzureRM"\n/m) + expect(content).to match(/\[object_storage.azurerm\]\n azure_storage_account_name = "testaccount"\n azure_storage_access_key = "1234abcd"\n/m) + } + end + end + end + + context 'without api rate limiting' do + it 'correctly renders out the workhorse service file' do + expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiLimit/) + expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiQueueDuration/) + expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiQueueLimit/) + end + end + + context 'with api rate limiting' do + before do + stub_gitlab_rb(gitlab_workhorse: { api_limit: 3, api_queue_limit: 6, api_queue_duration: '1m' }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiLimit 3 \\/) + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiQueueDuration 1m \\/) + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiQueueLimit 6 \\/) + end + end + + context 'without prometheus listen address' do + before do + stub_gitlab_rb(gitlab_workhorse: { prometheus_listen_addr: '' }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run") + expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-prometheusListenAddr/) + end + end + + context 'with prometheus listen address' do + before do + stub_gitlab_rb(gitlab_workhorse: { prometheus_listen_addr: ':9229' }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-prometheusListenAddr :9229 \\/) + end + end + + context 'without api ci long polling duration defined' do + it 'correctly renders out the workhorse service file' do + expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiCiLongPollingDuration/) + end + end + + context 'with api ci long polling duration defined' do + before do + stub_gitlab_rb(gitlab_workhorse: { api_ci_long_polling_duration: "60s" }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiCiLongPollingDuration 60s/) + end + end + + context 'with propagate_correlation_id enabled' do + before do + stub_gitlab_rb(gitlab_workhorse: { propagate_correlation_id: true }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-propagateCorrelationID/) + end + + context 'with trusted_cidrs_for_propagation defined' do + before do + stub_gitlab_rb(gitlab_workhorse: { + propagate_correlation_id: true, + trusted_cidrs_for_propagation: %w(127.0.0.1/32 192.168.0.1/8) + }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-propagateCorrelationID/) + end + + it 'should generate an array in the config file' do + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content { |content| + expect(content).to include(%(trusted_cidrs_for_propagation = ["127.0.0.1/32","192.168.0.1/8"])) + expect(content).not_to include(%(trusted_cidrs_for_x_forwarded_for)) + } + end + end + + context 'with trusted_cidrs_for_propagation and trusted_cidrs_for_x_forwarded_for defined' do + before do + stub_gitlab_rb(gitlab_workhorse: { + propagate_correlation_id: true, + trusted_cidrs_for_propagation: %w(127.0.0.1/32 192.168.0.1/8), + trusted_cidrs_for_x_forwarded_for: %w(1.2.3.4/16 5.6.7.8/24) + }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-propagateCorrelationID/) + end + + it 'should generate arrays in the config file' do + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content { |content| + expect(content).to include(%(trusted_cidrs_for_propagation = ["127.0.0.1/32","192.168.0.1/8"])) + expect(content).to include(%(trusted_cidrs_for_x_forwarded_for = ["1.2.3.4/16","5.6.7.8/24"])) + } + end + end + end + + context 'with log format defined as json' do + before do + stub_gitlab_rb(gitlab_workhorse: { log_format: "json" }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-logFormat json/) + end + + it 'renders svlogd file which will not prepend timestamp' do + expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/log/run").with_content(/\-tt/) + end + end + + context 'with log format defined as text' do + before do + stub_gitlab_rb(gitlab_workhorse: { log_format: "text" }) + end + + it 'correctly renders out the workhorse service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-logFormat text/) + end + + it 'renders svlogd file which will prepend timestamp' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/log/run").with_content(/\-tt/) + end + end + + context 'with default value for working directory' do + it 'should generate config file in the correct directory' do + expect(chef_run).to render_file(config_file) + end + end + + context 'with working directory specified' do + before do + stub_gitlab_rb(gitlab_workhorse: { dir: "/home/random/dir" }) + end + it 'should generate config file in the correct directory' do + expect(chef_run).to render_file("/home/random/dir/config.toml") + end + end + + context 'with default values for redis' do + it 'should generate config file' do + content_url = 'URL = "unix:/var/opt/gitlab/redis/redis.socket"' + expect(chef_run).to render_file(config_file).with_content(content_url) + expect(chef_run).not_to render_file(config_file).with_content(/Sentinel/) + end + + it 'should pass config file to workhorse' do + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-config config.toml/) + end + end + + context 'with host/port/password values for redis specified and socket disabled' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_host: "10.0.15.1", + redis_port: "1234", + redis_password: 'examplepassword' + } + ) + end + + it 'should generate config file with the specified values' do + content_url = 'URL = "redis://:examplepassword@10.0.15.1:1234/"' + content_password = 'Password = "examplepassword"' + content_sentinel = 'Sentinel' + content_sentinel_master = 'SentinelMaster' + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_password) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_master) + end + end + + context 'with socket for redis specified' do + before do + stub_gitlab_rb(gitlab_rails: { redis_socket: "/home/random/path.socket", redis_password: 'examplepassword' }) + end + + it 'should generate config file with the specified values' do + content_url = 'URL = "unix:/home/random/path.socket"' + content_password = 'Password = "examplepassword"' + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_password) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(/Sentinel/) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(/SentinelMaster/) + end + end + + context 'with Sentinels specified with default master' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_sentinels: [ + { 'host' => '127.0.0.1', 'port' => 2637 }, + { 'host' => '127.0.8.1', 'port' => 1234 } + ] + } + ) + end + + it 'should generate config file with the specified values' do + content = 'Sentinel = ["tcp://127.0.0.1:2637", "tcp://127.0.8.1:1234"]' + content_url = 'URL =' + content_sentinel_master = 'SentinelMaster = "gitlab-redis"' + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_master) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) + end + end + + context 'with Sentinels and master specified' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_sentinels: [ + { 'host' => '127.0.0.1', 'port' => 26379 }, + { 'host' => '127.0.8.1', 'port' => 12345 } + ] + }, + redis: { + master_name: 'examplemaster', + master_password: 'examplepassword' + } + ) + end + + it 'should generate config file with the specified values' do + content = 'Sentinel = ["tcp://127.0.0.1:26379", "tcp://127.0.8.1:12345"]' + content_sentinel_master = 'SentinelMaster = "examplemaster"' + content_sentinel_password = 'Password = "examplepassword"' + content_url = 'URL =' + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_master) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_password) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) + end + end + + context 'image scaler' do + context 'with default values' do + it 'sets the default maximum file size' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(/\[image_resizer\]\n max_scaler_procs = \d+\n max_filesize = 250000/m) + } + end + + context 'when reported CPU cores are at least 4' do + let(:node_cpus) { 4 } + + it 'sets default max procs to half the number of cores' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(/\[image_resizer\]\n max_scaler_procs = 2/m) + } + end + end + + context 'when reported CPU cores are less than 4' do + let(:node_cpus) { 3 } + + it 'pins default max procs to 2' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(/\[image_resizer\]\n max_scaler_procs = 2/m) + } + end + end + end + + context 'with custom values' do + before do + stub_gitlab_rb( + gitlab_workhorse: { + image_scaler_max_procs: 5, + image_scaler_max_filesize: 1024 + } + ) + end + + it 'should generate config file with the specified values' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(/\[image_resizer\]\n max_scaler_procs = 5\n max_filesize = 1024/m) + } + end + end + end + + context 'with workhorse keywatcher enabled' do + before do + stub_gitlab_rb( + gitlab_workhorse: { + workhorse_keywatcher: true, + } + ) + end + + it 'should generate redis block in the configuration file' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).to match(/\[redis\]/m) + } + end + end + + context 'with workhorse keywatcher disabled' do + before do + stub_gitlab_rb( + gitlab_workhorse: { + workhorse_keywatcher: false, + } + ) + end + + it 'should not generate redis block in the configuration file' do + expect(chef_run).to render_file(config_file).with_content { |content| + expect(content).not_to match(/\[redis\]/m) + } + end + end + + include_examples "consul service discovery", "gitlab_workhorse", "workhorse" +end diff --git a/spec/chef/cookbooks/gitlab/recipes/mailroom_spec.rb b/spec/chef/cookbooks/gitlab/recipes/mailroom_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9tYWlscm9vbV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/mailroom_spec.rb @@ -0,0 +1,291 @@ +# +# Copyright:: Copyright (c) 2018 GitLab Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef_helper' + +RSpec.describe 'gitlab::mailroom' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service templatesymlink)).converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + shared_examples 'renders Microsoft Graph config' do + let(:gitlab_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } + let(:gitlab_yml_file_content) { ChefSpec::Renderer.new(chef_run, gitlab_yml_template).content } + let(:gitlab_yml) { YAML.safe_load(gitlab_yml_file_content, [], [], true, symbolize_names: true) } + let(:inbox_options) do + { + tenant_id: 'MY-TENANT-ID', + client_id: '12345', + client_secret: 'MY-CLIENT-SECRET' + } + end + + before do + config_sections.each do |config_section| + stub_gitlab_rb( + gitlab_rails: { + "#{config_section}_enabled".to_sym => true, + "#{config_section}_inbox_method".to_sym => 'microsoft_graph', + "#{config_section}_inbox_options".to_sym => inbox_options + } + ) + end + end + + it 'renders gitlab.yml with the right data' do + config_sections.each do |config_section| + expect(gitlab_yml[:production][config_section][:inbox_method]).to eq('microsoft_graph') + expect(gitlab_yml[:production][config_section][:inbox_options]).to eq(inbox_options) + end + end + end + + shared_examples 'configured sidekiq delivery method' do + let(:gitlab_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } + let(:gitlab_yml_file_content) { ChefSpec::Renderer.new(chef_run, gitlab_yml_template).content } + let(:gitlab_yml) { YAML.safe_load(gitlab_yml_file_content, [], [], true, symbolize_names: true) } + + before do + stub_gitlab_rb( + gitlab_rails: config_sections.each_with_object({}) do |config_section, memo| + memo.merge!( + "#{config_section}_enabled".to_sym => true, + "#{config_section}_delivery_method".to_sym => 'sidekiq' + ) + end + ) + end + + it 'renders gitlab.yml with the right data' do + config_sections.each do |config_section| + expect(gitlab_yml[:production][config_section][:delivery_method]).to eq('sidekiq') + end + end + end + + shared_examples 'configured webhook delivery method' do + let(:gitlab_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } + let(:gitlab_yml_file_content) { ChefSpec::Renderer.new(chef_run, gitlab_yml_template).content } + let(:gitlab_yml) { YAML.safe_load(gitlab_yml_file_content, [], [], true, symbolize_names: true) } + + before do + stub_gitlab_rb( + external_url: 'http://localhost/gitlab/', # Mind the last "/" character + gitlab_rails: config_sections.each_with_object({}) do |config_section, memo| + memo.merge!( + "#{config_section}_enabled".to_sym => true, + "#{config_section}_delivery_method".to_sym => 'webhook' + ) + end + ) + end + + it 'renders gitlab.yml with the right data' do + config_sections.each do |config_section| + expect(gitlab_yml[:production][config_section][:delivery_method]).to eq('webhook') + expect(gitlab_yml[:production][config_section][:secret_file]).to eq(".gitlab_#{config_section}_secret") + expect(gitlab_yml[:production][config_section][:gitlab_url]).to eq("http://localhost/gitlab") + end + end + + it 'triggers notifications' do + config_sections.each do |config_section| + templatesymlink = chef_run.templatesymlink("Create a gitlab_#{config_section}_secret and create a symlink to Rails root") + expect(templatesymlink).to notify('runit_service[mailroom]').to(:restart).delayed + end + end + + context 'auth token is not specified' do + before do + allow(SecretsHelper).to receive(:generate_base64).with(32).and_return("a" * 32) + end + + it 'renders the correct node attribute with auto-generated auth token' do + config_sections.each do |config_section| + expect(chef_run).to create_templatesymlink("Create a gitlab_#{config_section}_secret and create a symlink to Rails root").with( + owner: 'root', + group: 'root', + mode: '0644' + ).with_variables( + a_hash_including( + secret_token: "a" * 32 + ) + ) + end + end + end + + context 'auth token is set' do + let(:auth_token) { SecureRandom.base64(32) } + + before do + stub_gitlab_rb( + external_url: 'http://localhost/gitlab/', + gitlab_rails: config_sections.each_with_object({}) do |config_section, memo| + memo.merge!( + "#{config_section}_enabled".to_sym => true, + "#{config_section}_delivery_method".to_sym => 'webhook', + "#{config_section}_auth_token".to_sym => "#{auth_token}-#{config_section}" + ) + end + ) + end + + it 'renders the correct node attribute' do + config_sections.each do |config_section| + expect(chef_run).to create_templatesymlink("Create a gitlab_#{config_section}_secret and create a symlink to Rails root").with( + owner: 'root', + group: 'root', + mode: '0644' + ).with_variables( + a_hash_including( + secret_token: "#{auth_token}-#{config_section}" + ) + ) + end + end + end + end + + describe 'when disabled' do + it_behaves_like 'disabled runit service', 'mailroom' + end + + describe 'when enabled' do + context 'when only service_desk_email enabled' do + let(:config_sections) { %i[service_desk_email] } + + before do + stub_gitlab_rb( + gitlab_rails: { + service_desk_email_enabled: true + } + ) + end + + it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' + it_behaves_like 'configured logrotate service', 'mailroom', 'git', 'git' + it_behaves_like 'configured sidekiq delivery method' + end + + context 'when both service_desk_email and incoming_email enabled' do + let(:config_sections) { %i[incoming_email service_desk_email] } + + before do + stub_gitlab_rb( + gitlab_rails: { + incoming_email_enabled: true, + service_desk_email_enabled: true + } + ) + end + + it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' + it_behaves_like 'configured logrotate service', 'mailroom', 'git', 'git' + it_behaves_like 'configured sidekiq delivery method' + end + + context 'default values' do + let(:config_sections) { %i[incoming_email] } + + before do + stub_gitlab_rb( + gitlab_rails: { + incoming_email_enabled: true + } + ) + end + + it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' + it_behaves_like 'configured logrotate service', 'mailroom', 'git', 'git' + + it 'uses --log-exit-as plain' do + expect(chef_run).to render_file("/opt/gitlab/sv/mailroom/run").with_content(/\-\-log\-exit\-as plain/) + end + end + + context 'custom values' do + let(:config_sections) { %i[incoming_email] } + + before do + stub_gitlab_rb( + gitlab_rails: { + incoming_email_enabled: true + }, + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' + it_behaves_like 'configured logrotate service', 'mailroom', 'foo', 'bar' + end + + context 'incoming email with Microsoft Graph' do + let(:config_sections) { %i[incoming_email] } + + it_behaves_like 'renders Microsoft Graph config' + end + + context 'Service Desk email with Microsoft Graph' do + let(:config_sections) { %i[service_desk_email] } + + it_behaves_like 'renders Microsoft Graph config' + end + + context 'Incoming Email webhook delivery method' do + let(:config_sections) { %i[incoming_email] } + + it_behaves_like 'configured webhook delivery method' + end + + context 'Service Desk Email webhook delivery method' do + let(:config_sections) { %i[incoming_email] } + + it_behaves_like 'configured webhook delivery method' + end + + context 'Both Incoming Email and Service Desk Email webhook delivery method' do + let(:config_sections) { %i[incoming_email service_desk_email] } + + it_behaves_like 'configured webhook delivery method' + end + end + + context 'with specified exit_log_format' do + before do + stub_gitlab_rb( + gitlab_rails: { + incoming_email_enabled: true + }, + mailroom: { + exit_log_format: "json" + } + ) + end + + it 'correctly passes the --log-exit-as ' do + expect(chef_run).to render_file("/opt/gitlab/sv/mailroom/run").with_content(/\-\-log\-exit\-as json/) + end + + it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' + it_behaves_like 'configured logrotate service', 'mailroom', 'git', 'git' + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/nginx_spec.rb b/spec/chef/cookbooks/gitlab/recipes/nginx_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9uZ2lueF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/nginx_spec.rb @@ -0,0 +1,698 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::nginx' do + let(:chef_runner) do + ChefSpec::SoloRunner.new(step_into: %w(runit_service)) do |node| + node.normal['gitlab']['nginx']['enable'] = true + node.normal['package']['install-dir'] = '/opt/gitlab' + end + end + + let(:chef_run) do + chef_runner.converge('gitlab::config', 'gitlab::nginx') + end + + let(:gitlab_http_config) { '/var/opt/gitlab/nginx/conf/gitlab-http.conf' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(Gitlab).to receive(:[]).with('node') { chef_runner.node } + + # generate a random number to use as error code + @code = rand(1000) + @nginx_errors = { + @code => { + 'title' => 'TEST TITLE', + 'header' => 'TEST HEADER', + 'message' => 'TEST MESSAGE' + } + } + end + + it_behaves_like 'enabled runit service', 'nginx', 'root', 'root' + + it 'creates a custom error_page entry when a custom error is defined' do + allow(Gitlab).to receive(:[]).with('nginx').and_return({ 'custom_error_pages' => @nginx_errors }) + + expect(chef_run).to render_file(gitlab_http_config).with_content { |content| + expect(content).to include("error_page #{@code} /#{@code}-custom.html;") + } + end + + it 'renders an error template when a custom error is defined' do + chef_runner.node.normal['gitlab']['nginx']['custom_error_pages'] = @nginx_errors + expect(chef_run).to render_file("/opt/gitlab/embedded/service/gitlab-rails/public/#{@code}-custom.html").with_content { |content| + expect(content).to include("TEST MESSAGE") + } + end + + it 'creates a standard error_page entry when no custom error is defined' do + chef_runner.node.normal['gitlab']['nginx'].delete('custom_error_pages') + expect(chef_run).to render_file(gitlab_http_config).with_content { |content| + expect(content).to include("error_page 404 /404.html;") + } + end + + it 'enables the proxy_intercept_errors option when custom_error_pages is defined' do + chef_runner.node.normal['gitlab']['nginx']['custom_error_pages'] = @nginx_errors + expect(chef_run).to render_file(gitlab_http_config).with_content { |content| + expect(content).to include("proxy_intercept_errors on") + } + end + + it 'uses the default proxy_intercept_errors option when custom_error_pages is not defined' do + chef_runner.node.normal['gitlab']['nginx'].delete('custom_error_pages') + expect(chef_run).to render_file(gitlab_http_config).with_content { |content| + expect(content).not_to include("proxy_intercept_errors") + } + end +end + +RSpec.describe 'nginx' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + subject { chef_run } + + let(:gitlab_http_config) { '/var/opt/gitlab/nginx/conf/gitlab-http.conf' } + let(:nginx_status_config) { /include \/var\/opt\/gitlab\/nginx\/conf\/nginx-status\.conf;/ } + + let(:basic_nginx_headers) do + { + "Host" => "$http_host", + "X-Real-IP" => "$remote_addr", + "X-Forwarded-Proto" => "http", + "X-Forwarded-For" => "$proxy_add_x_forwarded_for" + } + end + + let(:http_conf) do + { + "gitlab" => "/var/opt/gitlab/nginx/conf/gitlab-http.conf", + "mattermost" => "/var/opt/gitlab/nginx/conf/gitlab-mattermost-http.conf", + "registry" => "/var/opt/gitlab/nginx/conf/gitlab-registry.conf", + "pages" => "/var/opt/gitlab/nginx/conf/gitlab-pages.conf", + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when http external urls are being used' do + before do + stub_gitlab_rb( + external_url: 'http://localhost', + mattermost_external_url: 'http://mattermost.localhost', + registry_external_url: 'http://registry.localhost', + pages_external_url: 'http://pages.localhost' + ) + end + + it 'properly sets the default nginx proxy headers' do + expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to eql(nginx_headers({ + "Host" => "$http_host_with_default", + "Upgrade" => "$http_upgrade", + "Connection" => "$connection_upgrade" + })) + expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to eql(basic_nginx_headers) + expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to eql(nginx_headers({ + "X-Frame-Options" => "SAMEORIGIN", + "Upgrade" => "$http_upgrade", + "Connection" => "$connection_upgrade" + })) + expect(chef_run.node['gitlab']['pages-nginx']['proxy_set_headers']).to eql(basic_nginx_headers) + end + + it 'supports overriding default nginx headers' do + expect_headers = nginx_headers({ "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp" }) + stub_gitlab_rb( + "nginx" => { proxy_set_headers: { "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp" } }, + "mattermost_nginx" => { proxy_set_headers: { "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp" } }, + "registry_nginx" => { proxy_set_headers: { "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp" } } + ) + + expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to include(expect_headers) + expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to include(expect_headers) + expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to include(expect_headers) + end + end + + context 'when https external urls are being used' do + before do + stub_gitlab_rb( + external_url: 'https://localhost', + mattermost_external_url: 'https://mattermost.localhost', + registry_external_url: 'https://registry.localhost', + pages_external_url: 'https://pages.localhost' + ) + end + + it 'properly sets the default nginx proxy ssl forward headers' do + expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to eql(nginx_headers({ + "Host" => "$http_host_with_default", + "X-Forwarded-Proto" => "https", + "X-Forwarded-Ssl" => "on", + "Upgrade" => "$http_upgrade", + "Connection" => "$connection_upgrade" + })) + + expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to eql(nginx_headers({ + "X-Forwarded-Proto" => "https", + "X-Forwarded-Ssl" => "on" + })) + + expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to eql(nginx_headers({ + "X-Forwarded-Proto" => "https", + "X-Forwarded-Ssl" => "on", + "X-Frame-Options" => "SAMEORIGIN", + "Upgrade" => "$http_upgrade", + "Connection" => "$connection_upgrade" + })) + + expect(chef_run.node['gitlab']['pages-nginx']['proxy_set_headers']).to eql(nginx_headers({ + "X-Forwarded-Proto" => "https", + "X-Forwarded-Ssl" => "on" + })) + end + + it 'supports overriding default nginx headers' do + expect_headers = nginx_headers({ "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp", "X-Forwarded-Ssl" => "on", 'Connection' => 'close' }) + set_headers = { "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp", 'Connection' => 'close' } + stub_gitlab_rb( + "nginx" => { proxy_set_headers: set_headers }, + "mattermost_nginx" => { proxy_set_headers: set_headers }, + "registry_nginx" => { proxy_set_headers: set_headers }, + "pages_nginx" => { proxy_set_headers: set_headers } + ) + + expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to include(expect_headers) + expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to include(expect_headers) + expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to include(expect_headers) + expect(chef_run.node['gitlab']['pages-nginx']['proxy_set_headers']).to include(expect_headers) + end + + it 'disables Connection header' do + expect_headers = nginx_headers({ "Host" => "nohost.example.com", "X-Forwarded-Proto" => "https", "X-Forwarded-Ssl" => "on" }) + set_headers = { "Host" => "nohost.example.com", "Connection" => nil } + stub_gitlab_rb( + "nginx" => { proxy_set_headers: set_headers }, + "mattermost_nginx" => { proxy_set_headers: set_headers }, + "registry_nginx" => { proxy_set_headers: set_headers }, + "pages_nginx" => { proxy_set_headers: set_headers } + ) + + expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to include(expect_headers) + expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to include(expect_headers) + expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to include(expect_headers) + expect(chef_run.node['gitlab']['pages-nginx']['proxy_set_headers']).to include(expect_headers) + end + + it 'does not set ssl_client_certificate by default' do + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content { |content| + expect(content).not_to include("ssl_client_certificate") + } + end + end + + it 'does not set ssl_verify_client by default' do + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content { |content| + expect(content).not_to include("ssl_verify_client") + } + end + end + + it 'does not set ssl_verify_depth by default' do + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content { |content| + expect(content).not_to include("ssl_verify_depth") + } + end + end + + it 'sets the default ssl_verify_depth when ssl_verify_client is defined' do + verify_client = { "ssl_verify_client" => "on" } + stub_gitlab_rb( + "nginx" => verify_client, + "mattermost_nginx" => verify_client, + "registry_nginx" => verify_client, + "pages_nginx" => verify_client + ) + chef_run.converge('gitlab::default') + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content { |content| + expect(content).to include("ssl_verify_depth 1") + } + end + end + + it 'applies nginx verify client settings to gitlab-http' do + stub_gitlab_rb("nginx" => { + "ssl_client_certificate" => "/etc/gitlab/ssl/gitlab-http-ca.crt", + "ssl_verify_client" => "on", + "ssl_verify_depth" => "2", + }) + chef_run.converge('gitlab::default') + expect(chef_run).to render_file(http_conf['gitlab']).with_content { |content| + expect(content).to include("ssl_client_certificate /etc/gitlab/ssl/gitlab-http-ca.crt") + expect(content).to include("ssl_verify_client on") + expect(content).to include("ssl_verify_depth 2") + } + end + + it 'applies nginx request_buffering path regex' do + expect(chef_run).to render_file(http_conf['gitlab']).with_content { |content| + expect(content).to include("location ~ (/api/v\\d/jobs/\\d+/artifacts$|\\.git/git-receive-pack$|\\.git/gitlab-lfs/objects|\\.git/info/lfs/objects/batch$)") + } + end + + it 'applies mattermost_nginx verify client settings to gitlab-mattermost-http' do + stub_gitlab_rb("mattermost_nginx" => { + "ssl_client_certificate" => "/etc/gitlab/ssl/gitlab-mattermost-http-ca.crt", + "ssl_verify_client" => "on", + "ssl_verify_depth" => "3", + }) + chef_run.converge('gitlab::default') + expect(chef_run).to render_file(http_conf['mattermost']).with_content { |content| + expect(content).to include("ssl_client_certificate /etc/gitlab/ssl/gitlab-mattermost-http-ca.crt") + expect(content).to include("ssl_verify_client on") + expect(content).to include("ssl_verify_depth 3") + } + end + + it 'applies registry_nginx verify client settings to gitlab-registry' do + stub_gitlab_rb("registry_nginx" => { + "ssl_client_certificate" => "/etc/gitlab/ssl/gitlab-registry-ca.crt", + "ssl_verify_client" => "off", + "ssl_verify_depth" => "5", + }) + chef_run.converge('gitlab::default') + expect(chef_run).to render_file(http_conf['registry']).with_content { |content| + expect(content).to include("ssl_client_certificate /etc/gitlab/ssl/gitlab-registry-ca.crt") + expect(content).to include("ssl_verify_client off") + expect(content).to include("ssl_verify_depth 5") + } + end + + it 'applies pages_nginx verify client settings to gitlab-pages' do + stub_gitlab_rb("pages_nginx" => { + "ssl_client_certificate" => "/etc/gitlab/ssl/gitlab-pages-ca.crt", + "ssl_verify_client" => "on", + "ssl_verify_depth" => "7", + }) + chef_run.converge('gitlab::default') + expect(chef_run).to render_file(http_conf['pages']).with_content { |content| + expect(content).to include("ssl_client_certificate /etc/gitlab/ssl/gitlab-pages-ca.crt") + expect(content).to include("ssl_verify_client on") + expect(content).to include("ssl_verify_depth 7") + } + end + end + + context 'when is enabled' do + it 'enables nginx status by default' do + expect(chef_run.node['gitlab']['nginx']['status']).to eql({ + "enable" => true, + "listen_addresses" => ["*"], + "fqdn" => "localhost", + "port" => 8060, + "vts_enable" => true, + "options" => { + "server_tokens" => "off", + "access_log" => "off", + "allow" => "127.0.0.1", + "deny" => "all" + } + }) + expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/nginx.conf').with_content(nginx_status_config) + end + + it "supports overrading nginx status default configuration" do + custom_nginx_status_config = { + "enable" => true, + "listen_addresses" => ["127.0.0.1"], + "fqdn" => "dev.example.com", + "port" => 9999, + "vts_enable" => true, + "options" => { + "server_tokens" => "off", + "access_log" => "on", + "allow" => "127.0.0.1", + "deny" => "all" + } + } + + stub_gitlab_rb("nginx" => { + "status" => custom_nginx_status_config + }) + + chef_run.converge('gitlab::default') + + expect(chef_run.node['gitlab']['nginx']['status']).to eql(custom_nginx_status_config) + end + + it "will not load the nginx status config if nginx status is disabled" do + stub_gitlab_rb("nginx" => { "status" => { "enable" => false } }) + expect(chef_run).not_to render_file('/var/opt/gitlab/nginx/conf/nginx.conf').with_content(nginx_status_config) + end + + it 'defaults to redirect_http_to_https off' do + expect(chef_run.node['gitlab']['nginx']['redirect_http_to_https']).to be false + expect(chef_run).to render_file(gitlab_http_config).with_content { |content| + expect(content).not_to include('return 301 https://fauxhai.local:80$request_uri;') + } + end + + it 'enables redirect when redirect_http_to_https is true' do + stub_gitlab_rb(nginx: { listen_https: true, redirect_http_to_https: true }) + expect(chef_run.node['gitlab']['nginx']['redirect_http_to_https']).to be true + expect(chef_run).to render_file(gitlab_http_config).with_content('return 301 https://fauxhai.local:80$request_uri;') + end + + context 'when smartcard authentication is enabled' do + let(:gitlab_smartcard_http_config) { '/var/opt/gitlab/nginx/conf/gitlab-smartcard-http.conf' } + + before do + stub_gitlab_rb( + gitlab_rails: { + smartcard_enabled: true + }, + nginx: { listen_https: true } + ) + end + + it 'listens on a separate port' do + expect(chef_run).to render_file(gitlab_smartcard_http_config).with_content { |content| + expect(content).to include('server_name fauxhai.local;') + expect(content).to include('listen *:3444 ssl http2;') + } + end + + it 'requires client side certificate' do + expect(chef_run).to render_file(gitlab_smartcard_http_config).with_content { |content| + expect(content).to include('ssl_client_certificate /etc/gitlab/ssl/CA.pem') + expect(content).to include('ssl_verify_client on') + expect(content).to include('ssl_verify_depth 2') + } + end + + it 'forwards client side certificate in header' do + expect(chef_run).to render_file(gitlab_smartcard_http_config).with_content('proxy_set_header X-SSL-Client-Certificate') + end + + context 'when smartcard_client_certificate_required_host is set' do + before do + stub_gitlab_rb( + gitlab_rails: { + smartcard_enabled: true, + smartcard_client_certificate_required_host: 'smartcard.fauxhai.local' + }, + nginx: { listen_https: true } + ) + end + + it 'sets smartcard nginx server name' do + expect(chef_run).to render_file(gitlab_smartcard_http_config).with_content { |content| + expect(content).to include('server_name smartcard.fauxhai.local;') + expect(content).to include('listen *:3444 ssl http2;') + } + end + end + end + + context 'when smartcard authentication is disabled' do + let(:gitlab_smartcard_http_config) { '/var/opt/gitlab/nginx/conf/gitlab-smartcard-http.conf' } + + before do + stub_gitlab_rb(gitlab_rails: { smartcard_enabled: false }) + end + + it 'should not add the gitlab smartcard config' do + expect(chef_run).not_to render_file(gitlab_smartcard_http_config) + end + end + end + + context 'when is disabled' do + it 'should not add the nginx status config' do + stub_gitlab_rb("nginx" => { "enable" => false }) + expect(chef_run).not_to render_file('/var/opt/gitlab/nginx/conf/nginx.conf').with_content(nginx_status_config) + end + end + + context 'when grafana is enabled' do + before do + stub_gitlab_rb( + grafana: { enable: true } + ) + end + + it 'applies nginx grafana proxy' do + expect(chef_run).to render_file(http_conf['gitlab']).with_content { |content| + expect(content).to include('location /-/grafana/ {') + expect(content).to include('proxy_pass http://localhost:3000/;') + } + end + end + + context 'when KAS is enabled' do + before do + stub_gitlab_rb( + gitlab_kas: { enable: true } + ) + end + + it 'applies nginx KAS proxy' do + expect(chef_run).to render_file(http_conf['gitlab']).with_content { |content| + expect(content).to include('location = /-/kubernetes-agent/ {') + expect(content).to include('proxy_pass http://localhost:8150/;') + + expect(content).to include('location /-/kubernetes-agent/k8s-proxy/ {') + expect(content).to include('proxy_pass http://localhost:8154/;') + } + end + end + + context 'when hsts is disabled' do + before do + stub_gitlab_rb(nginx: { hsts_max_age: 0 }) + end + it { is_expected.not_to render_file(gitlab_http_config).with_content(/add_header Strict-Transport-Security/) } + end + + it { is_expected.to render_file(gitlab_http_config).with_content(/add_header Strict-Transport-Security "max-age=63072000";/) } + + context 'when referrer_policy is disabled' do + before do + stub_gitlab_rb(nginx: { referrer_policy: false }) + end + + it { is_expected.not_to render_file(gitlab_http_config).with_content(/add_header Referrer-Policy/) } + end + + context 'when referrer_policy is set to origin' do + before do + stub_gitlab_rb(nginx: { referrer_policy: 'origin' }) + end + + it { is_expected.to render_file(gitlab_http_config).with_content(/add_header Referrer-Policy origin;/) } + end + + it { is_expected.to render_file(gitlab_http_config).with_content(/add_header Referrer-Policy strict-origin-when-cross-origin;/) } + + context 'when gzip is disabled' do + before do + stub_gitlab_rb(nginx: { gzip_enabled: false }) + end + it { is_expected.to render_file(gitlab_http_config).with_content(/gzip off;/) } + end + + it { is_expected.to render_file(gitlab_http_config).with_content(/gzip on;/) } + + context 'when include_subdomains is enabled' do + before do + stub_gitlab_rb(nginx: { hsts_include_subdomains: true }) + end + + it { is_expected.to render_file(gitlab_http_config).with_content(/add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";/) } + end + + context 'when max-age is set to 10' do + before do + stub_gitlab_rb(nginx: { hsts_max_age: 10 }) + end + + it { is_expected.to render_file(gitlab_http_config).with_content(/"max-age=10[^"]*"/) } + end + + context 'when error log level is set to debug' do + before do + stub_gitlab_rb(nginx: { error_log_level: 'debug' }) + end + it { is_expected.to render_file(gitlab_http_config).with_content(/error_log \/var\/log\/gitlab\/nginx\/gitlab_error.log debug;/) } + end + + it { is_expected.to render_file(gitlab_http_config).with_content(/error_log \/var\/log\/gitlab\/nginx\/gitlab_error.log error;/) } + + context 'when NGINX RealIP module is configured' do + before do + stub_gitlab_rb( + external_url: 'https://localhost', + mattermost_external_url: 'https://mattermost.localhost', + registry_external_url: 'https://registry.localhost', + pages_external_url: 'https://pages.localhost' + ) + end + + context 'when real_ip_header is configured' do + before do + stub_gitlab_rb( + nginx: { real_ip_header: 'X-FAKE' }, + mattermost_nginx: { real_ip_header: 'X-FAKE' }, + registry_nginx: { real_ip_header: 'X-FAKE' }, + pages_nginx: { real_ip_header: 'X-FAKE' } + ) + end + + it 'populates all config with real_ip_header' do + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content(/real_ip_header X-FAKE/) + end + end + end + + context 'when real_ip_recursive is configured' do + before do + stub_gitlab_rb( + nginx: { real_ip_recursive: 'On' }, + mattermost_nginx: { real_ip_recursive: 'On' }, + registry_nginx: { real_ip_recursive: 'On' }, + pages_nginx: { real_ip_recursive: 'On' } + ) + end + + it 'populates all config with real_up_recursive' do + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content(/real_ip_recursive On/) + end + end + end + + context 'when real_ip_trusted_addresses is configured' do + before do + stub_gitlab_rb( + nginx: { real_ip_trusted_addresses: %w(one two three) }, + mattermost_nginx: { real_ip_trusted_addresses: %w(one two three) }, + registry_nginx: { real_ip_trusted_addresses: %w(one two three) }, + pages_nginx: { real_ip_trusted_addresses: %w(one two three) } + ) + end + + it 'populates all config with all items for real_ip_trusted_addresses' do + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content { |content| + expect(content).to match(/set_real_ip_from one/) + expect(content).to match(/set_real_ip_from two/) + expect(content).to match(/set_real_ip_from three/) + } + end + end + end + + context 'when proxy_protocol is enabled' do + before do + stub_gitlab_rb( + nginx: { proxy_protocol: true }, + mattermost_nginx: { proxy_protocol: true }, + registry_nginx: { proxy_protocol: true }, + pages_nginx: { proxy_protocol: true } + ) + end + + it 'applies nginx proxy_protocol settings' do + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content { |content| + expect(content).to match(/listen .*:\d+ proxy_protocol/) + expect(content).to include('real_ip_header proxy_protocol;') + expect(content).to include('proxy_set_header X-Real-IP $proxy_protocol_addr;') + expect(content).to include('proxy_set_header X-Forwarded-For $proxy_protocol_addr;') + } + end + end + end + + it 'does not set proxy_protocol settings by default' do + http_conf.each_value do |conf| + expect(chef_run).to render_file(conf).with_content { |content| + expect(content).not_to match(/listen .*:\d+ proxy_protocol/) + expect(content).not_to include('real_ip_header proxy_protocol;') + expect(content).not_to include('proxy_set_header X-Real-IP $proxy_protocol_addr;') + expect(content).not_to include('proxy_set_header X-Forwarded-For $proxy_protocol_addr;') + } + end + end + end + + context 'for proxy_custom_buffer_size' do + before do + stub_gitlab_rb( + external_url: 'https://localhost', + mattermost_external_url: 'https://mattermost.localhost', + pages_external_url: 'https://pages.localhost' + ) + end + + context 'when proxy_custom_buffer_size is set' do + before do + stub_gitlab_rb( + nginx: { proxy_custom_buffer_size: '42k' }, + mattermost_nginx: { proxy_custom_buffer_size: '42k' }, + pages_nginx: { proxy_custom_buffer_size: '42k' } + ) + end + + it 'applies nginx proxy_custom_buffer_size settings' do + ['gitlab', 'mattermost', 'pages'].each do |conf| + expect(chef_run).to render_file(http_conf[conf]).with_content { |content| + expect(content).to include('proxy_buffers 8 42k;') + expect(content).to include('proxy_buffer_size 42k;') + } + end + end + end + + it 'does not set proxy_custom_buffer_size by default' do + ['gitlab', 'mattermost', 'pages'].each do |conf| + expect(chef_run).to render_file(http_conf[conf]).with_content { |content| + expect(content).not_to include('proxy_buffers 8 42k;') + expect(content).not_to include('proxy_buffer_size 42k;') + } + end + end + end + + include_examples "consul service discovery", "nginx", "nginx" + + describe 'logrotate settings' do + context 'default values' do + it_behaves_like 'configured logrotate service', 'nginx', 'root', 'root' + end + + context 'specified username and group' do + before do + stub_gitlab_rb( + web_server: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'configured logrotate service', 'nginx', 'root', 'root' + end + end + + def nginx_headers(additional_headers) + basic_nginx_headers.merge(additional_headers) + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/puma_spec.rb b/spec/chef/cookbooks/gitlab/recipes/puma_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9wdW1hX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/puma_spec.rb @@ -0,0 +1,260 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::puma with Ubuntu 16.04' do + let(:chef_run) do + runner = ChefSpec::SoloRunner.new( + step_into: %w(runit_service puma_config), + path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04.json' + ) + runner.converge('gitlab::default') + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_default_should_notify?(true) + stub_should_notify?('puma', true) + end + + context 'when puma is enabled' do + it_behaves_like 'enabled runit service', 'puma', 'root', 'root' + + describe 'logrotate settings' do + context 'default values' do + it_behaves_like 'configured logrotate service', 'puma', 'git', 'git' + end + + context 'specified username and group' do + before do + stub_gitlab_rb( + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'configured logrotate service', 'puma', 'foo', 'bar' + end + end + + it 'creates runtime directories' do + expect(chef_run).to create_directory('/var/log/gitlab/puma').with( + owner: 'git', + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/opt/gitlab/var/puma').with( + owner: 'git', + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/var/opt/gitlab/gitlab-rails/sockets').with( + owner: 'git', + group: 'gitlab-www', + mode: '0750' + ) + end + + it 'renders the runit configuration with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') + .with_content { |content| + expect(content).not_to match(/export prometheus_run_dir=\'\'/) + expect(content).not_to match(/rm \/run\/gitlab\/puma/) + expect(content).to match(/-u git:git/) + expect(content).to match(/-U git:git/) + expect(content).to match(/mkdir -p \/run\/gitlab\/puma/) + expect(content).to match(/chmod 0700 \/run\/gitlab\/puma/) + expect(content).to match(/chown git \/run\/gitlab\/puma/) + expect(content).to match(/export prometheus_run_dir=\'\/run\/gitlab\/puma\'/) + expect(content).to match(%r(/opt/gitlab/embedded/bin/bundle exec puma -C /var/opt/gitlab/gitlab-rails/etc/puma.rb)) + } + end + + it 'renders the puma.rb file' do + expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( + tag: 'gitlab-puma-worker', + rackup: 'config.ru', + environment: 'production', + pid: '/opt/gitlab/var/puma/puma.pid', + state_path: '/opt/gitlab/var/puma/puma.state', + listen_socket: '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket', + listen_tcp: '127.0.0.1:8080', + working_directory: '/var/opt/gitlab/gitlab-rails/working', + worker_processes: 2, + min_threads: 4, + max_threads: 4 + ) + expect(chef_run).to create_template('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| + expect(content).to match(/lowlevel_error_handler/) + expect(content).to include('Raven.capture_exception') + } + end + + it 'creates sysctl files' do + expect(chef_run).to create_gitlab_sysctl('net.core.somaxconn').with_value(1024) + end + end + + context 'with custom Puma settings' do + before do + stub_gitlab_rb( + puma: { + worker_timeout: 120, + worker_processes: 4, + min_threads: 5, + max_threads: 10, + listen: '10.0.0.1', + port: 9000, + socket: '/tmp/puma.socket', + state_path: '/tmp/puma.state', + per_worker_max_memory_mb: 1000 + } + ) + end + + it 'renders the puma.rb file' do + expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( + state_path: '/tmp/puma.state', + listen_socket: '/tmp/puma.socket', + listen_tcp: '10.0.0.1:9000', + worker_processes: 4, + min_threads: 5, + max_threads: 10, + per_worker_max_memory_mb: 1000 + ) + expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| + expect(content).to match(%r(-authSocket /tmp/puma.socket)) + } + expect(Gitlab['gitlab_workhorse']['auth_socket']).to eq('/tmp/puma.socket') + end + end + + context 'with custom user and group' do + before do + stub_gitlab_rb( + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'enabled runit service', 'puma', 'root', 'root' + end + + context 'with custom runtime_dir' do + before do + stub_gitlab_rb( + runtime_dir: '/tmp/test-dir' + ) + end + + it 'uses the user-specific runtime_dir' do + expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') + .with_content { |content| + expect(content).to match(%r(export prometheus_run_dir='/tmp/test-dir/gitlab/puma')) + expect(content).to match(%r(mkdir -p /tmp/test-dir/gitlab/puma)) + } + end + end + + context 'with ActionCable enabled' do + before do + stub_gitlab_rb( + actioncable: { + worker_pool_size: 7 + } + ) + end + + it 'renders the runit configuration with ActionCable environment variables' do + expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') + .with_content { |content| + expect(content).to match(/ACTION_CABLE_WORKER_POOL_SIZE=7/) + expect(content).to match(%r(/opt/gitlab/embedded/bin/bundle exec puma -C /var/opt/gitlab/gitlab-rails/etc/puma.rb)) + } + end + end + + include_examples "consul service discovery", "puma", "rails" +end + +RSpec.describe 'gitlab::puma Ubuntu 16.04 with no tmpfs' do + let(:chef_run) do + runner = ChefSpec::SoloRunner.new( + path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json', + step_into: %w(runit_service) + ) + runner.converge('gitlab::default') + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when puma is enabled on a node with no /run or /dev/shm tmpfs' do + it_behaves_like 'enabled runit service', 'puma', 'root', 'root' + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') + .with_content { |content| + expect(content).to match(/export prometheus_run_dir=\'\'/) + expect(content).not_to match(/mkdir -p \/run\/gitlab\/puma/) + } + end + end +end + +RSpec.describe 'gitlab::puma Ubuntu 16.04 Docker' do + let(:chef_run) do + runner = ChefSpec::SoloRunner.new( + path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04-docker.json', + step_into: %w(runit_service) + ) + runner.converge('gitlab::default') + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when puma is enabled on a node with a /dev/shm tmpfs' do + it_behaves_like 'enabled runit service', 'puma', 'root', 'root' + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') + .with_content { |content| + expect(content).to match(/export prometheus_run_dir=\'\/dev\/shm\/gitlab\/puma\'/) + expect(content).to match(/mkdir -p \/dev\/shm\/gitlab\/puma/) + } + end + end +end + +RSpec.describe 'gitlab::puma with more CPUs' do + let(:chef_run) do + runner = ChefSpec::SoloRunner.new( + step_into: %w(runit_service), + path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04-more-cpus.json' + ) + runner.converge('gitlab::default') + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when puma is enabled' do + it 'renders the puma.rb file' do + expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( + environment: 'production', + pid: '/opt/gitlab/var/puma/puma.pid', + state_path: '/opt/gitlab/var/puma/puma.state', + listen_socket: '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket', + listen_tcp: '127.0.0.1:8080', + working_directory: '/var/opt/gitlab/gitlab-rails/working', + worker_processes: 16 + ) + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/rails_cache_clear_spec.rb b/spec/chef/cookbooks/gitlab/recipes/rails_cache_clear_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9yYWlsc19jYWNoZV9jbGVhcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/rails_cache_clear_spec.rb @@ -0,0 +1,45 @@ +require 'chef_helper' + +# NOTE: We do not try to verify if we pass proper notifications to +# execute['clear the gitlab-rails cache'] resource that's done in other specs +# We just test if we use proper command and that we can change default +# attribute value with gilab.rb setting. + +RSpec.describe 'gitlab::rails-cache-clear' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'test clear cache execution' do + let(:clear_cache_exec) { chef_run.execute('clear the gitlab-rails cache') } + let(:gilab_yml_temp) do + chef_run.find_resource(:templatesymlink, + 'Create a gitlab.yml and create a symlink to Rails root') + end + + it 'check rake_cache_clear default attribute value set to true' do + expect(chef_run.node['gitlab']['gitlab-rails']['rake_cache_clear']) + .to be(true) + end + + it 'check rake_cache_clear attribute value set to true' do + stub_gitlab_rb(gitlab_rails: { rake_cache_clear: true }) + expect(chef_run.node['gitlab']['gitlab-rails']['rake_cache_clear']) + .to be(true) + end + + it 'check rake_cache_clear attribute value set to false' do + stub_gitlab_rb(gitlab_rails: { rake_cache_clear: false }) + expect(chef_run.node['gitlab']['gitlab-rails']['rake_cache_clear']) + .to be(false) + end + + it 'check command used to clear cache' do + expect(clear_cache_exec.command).to match( + '/opt/gitlab/bin/gitlab-rake cache:clear') + expect(clear_cache_exec).to do_nothing + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/remote-syslog_spec.rb b/spec/chef/cookbooks/gitlab/recipes/remote-syslog_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9yZW1vdGUtc3lzbG9nX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/remote-syslog_spec.rb @@ -0,0 +1,84 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::remote-syslog' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'creates service' do + before do + stub_gitlab_rb( + remote_syslog: { + enable: true + } + ) + end + it_behaves_like "enabled runit service", "remote-syslog", "root", "root" + end + + context 'when remote logger is enabled' do + before do + stub_gitlab_rb( + remote_syslog: { + enable: true + }, + logging: { + udp_log_shipping_hostname: "example.com" + } + ) + end + + it 'creates the remote_syslog config' do + expect(chef_run.node['gitlab']['remote-syslog']['enable']).to eq true + end + + it 'creates remote-syslog config file with correct log directories' do + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml') + + # Checking if log_directory of both type of services - those accessible + # via node[service] and those via node['gitlab'][service] are populated. + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/redis\/\*.log/) + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/nginx\/\*.log/) + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/gitlab-rails\/\*.log/) + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/postgresql\/\*.log/) + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/sidekiq\/\*.log/) + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/gitlab-workhorse\/\*.log/) + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/gitlab-pages\/\*.log/) + expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/gitlab-kas\/\*.log/) + end + + it 'creates the remote-syslog sv file without setting hostname' do + stub_gitlab_rb( + remote_syslog: { + enable: true + } + ) + contents = <<~END + #!/bin/sh + exec 2>&1 + + exec /opt/gitlab/embedded/bin/remote_syslog --no-detach --debug-level DEBUG -c /var/opt/gitlab/remote-syslog/remote_syslog.yml + END + expect(chef_run).to render_file('/opt/gitlab/sv/remote-syslog/run').with_content(contents.chomp) + end + + it 'creates the remote-syslog sv file with setting hostname' do + stub_gitlab_rb( + remote_syslog: { + enable: true + }, + logging: { + udp_log_shipping_hostname: "example.com" + } + ) + contents = <<~END + #!/bin/sh + exec 2>&1 + + exec /opt/gitlab/embedded/bin/remote_syslog --no-detach --debug-level DEBUG -c /var/opt/gitlab/remote-syslog/remote_syslog.yml --hostname example.com + END + expect(chef_run).to render_file('/opt/gitlab/sv/remote-syslog/run').with_content(contents.chomp) + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/selinux_spec.rb b/spec/chef/cookbooks/gitlab/recipes/selinux_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9zZWxpbnV4X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/selinux_spec.rb @@ -0,0 +1,96 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::gitlab-selinux' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink storage_directory)).converge('gitlab::default') } + let(:templatesymlink) { chef_run.templatesymlink('Create a config.yml and create a symlink to Rails root') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_default_should_notify?(true) + end + + context 'when NOT running on selinux' do + before do + allow_any_instance_of(ShellOutHelper).to receive(:success?).with('id -Z').and_return(false) + end + + it 'should not run the semanage bash command' do + expect(templatesymlink).to_not notify('bash[Set proper security context on ssh files for selinux]').delayed + end + end + + context 'when running on selinux' do + before do + allow_any_instance_of(ShellOutHelper).to receive(:success?).with('id -Z').and_return(true) + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with('/var/opt/gitlab/.ssh').and_return(true) + allow(File).to receive(:exist?).with('/var/opt/gitlab/.ssh/authorized_keys').and_return(true) + allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret').and_return(true) + allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-shell/config.yml').and_return(true) + allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-workhorse/sockets').and_return(true) + end + + let(:bash_block) { chef_run.bash('Set proper security context on ssh files for selinux') } + + def semanage_fcontext(filename) + "semanage fcontext -a -t gitlab_shell_t '#{filename}'" + end + + it 'should run the semanage bash command' do + expect(templatesymlink).to notify('bash[Set proper security context on ssh files for selinux]').delayed + end + + it 'sets the security context of gitlab-shell files' do + lines = bash_block.code.split("\n") + files = %w(/var/opt/gitlab/.ssh(/.*)? + /var/opt/gitlab/.ssh/authorized_keys + /var/opt/gitlab/gitlab-shell/config.yml + /var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret + /var/opt/gitlab/gitlab-workhorse/sockets) + managed_files = files.map { |file| semanage_fcontext(file) } + + expect(lines).to include(*managed_files) + expect(lines).to include("restorecon -R -v '/var/opt/gitlab/.ssh'") + expect(lines).to include("restorecon -v '/var/opt/gitlab/.ssh/authorized_keys'") + expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-shell/config.yml'") + expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret'") + expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-workhorse/sockets'") + end + + context 'and the user configured a custom workhorse sockets directory' do + let(:user_sockets_directory) { '/how/do/you/do' } + before do + stub_gitlab_rb( + gitlab_workhorse: { + listen_network: 'unix', + sockets_directory: user_sockets_directory + } + ) + end + + it 'sets the security context of a custom workhorse sockets directory' do + allow(File).to receive(:exist?).with(user_sockets_directory).and_return(true) + lines = bash_block.code.split("\n") + files = [user_sockets_directory] + managed_files = files.map { |file| semanage_fcontext(file) } + + expect(lines).to include(*managed_files) + expect(lines).to include("restorecon -v '#{user_sockets_directory}'") + end + end + + context 'when gitlab-rails is disabled' do + before do + stub_gitlab_rb( + gitlab_rails: { + enable: false + } + ) + end + + it 'should not attempt to set security context' do + expect(chef_run).not_to run_bash('Set proper security context on ssh files for selinux') + end + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/show_config_spec.rb b/spec/chef/cookbooks/gitlab/recipes/show_config_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9zaG93X2NvbmZpZ19zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/show_config_spec.rb @@ -0,0 +1,20 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::show_config' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::show_config') } + + it 'outputs user-defined gitlab.rb configuration to stdout' do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb( + patroni: { scope: 'test-cluster' } + ) + + expect { chef_run }.to output(/"scope": "test-cluster"/).to_stdout + end + + it 'outputs GitlabCluster defined attributes to stdout' do + GitlabCluster.config.set('primary', true) + + expect { chef_run }.to output(/"primary": true/).to_stdout + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/sidekiq_spec.rb b/spec/chef/cookbooks/gitlab/recipes/sidekiq_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9zaWRla2lxX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/sidekiq_spec.rb @@ -0,0 +1,57 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::sidekiq' do + let(:chef_run) do + runner = ChefSpec::SoloRunner.new( + step_into: %w(sidekiq_service runit_service), + path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04.json' + ) + runner.converge('gitlab::default') + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'with default values' do + it 'correctly renders out the sidekiq service file' do + expect(chef_run).to render_file("/opt/gitlab/sv/sidekiq/run") + .with_content { |content| + expect(content).not_to match(/export prometheus_run_dir=\'\'/) + expect(content).to match(/mkdir -p \/run\/gitlab\/sidekiq/) + expect(content).to match(/rm \/run\/gitlab\/sidekiq/) + expect(content).to match(/chmod 0700 \/run\/gitlab\/sidekiq/) + expect(content).to match(/chown git \/run\/gitlab\/sidekiq/) + expect(content).to match(/export prometheus_run_dir=\'\/run\/gitlab\/sidekiq\'/) + expect(content).to match(%r{bin/sidekiq-cluster}) + expect(content).to match(/-m 50/) # max_concurrency + expect(content).to match(/--timeout 25/) # shutdown timeout + expect(content).to match(/\*/) # all queues + } + end + + it_behaves_like "enabled runit service", "sidekiq", "root", "root" + end + + describe 'log_format' do + context 'by default' do + it 'does not pass timestamp flag to svlogd' do + expect(chef_run).not_to render_file("/opt/gitlab/sv/sidekiq/log/run").with_content(/-tt/) + end + end + + context 'when user specifies text log format' do + before do + stub_gitlab_rb( + sidekiq: { log_format: 'text' } + ) + end + + it 'passes timestamp flag to svlogd' do + expect(chef_run).to render_file("/opt/gitlab/sv/sidekiq/log/run").with_content(/-tt/) + end + end + end + + include_examples "consul service discovery", "sidekiq", "sidekiq" +end diff --git a/spec/chef/cookbooks/gitlab/recipes/storage-check_spec.rb b/spec/chef/cookbooks/gitlab/recipes/storage-check_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9zdG9yYWdlLWNoZWNrX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/storage-check_spec.rb @@ -0,0 +1,57 @@ +# +# Copyright:: Copyright (c) 2017 GitLab Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef_helper' + +RSpec.describe 'gitlab::storage-check' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'when disabled' do + it_behaves_like 'disabled runit service', 'storage-check' + end + + describe 'when enabled' do + context 'default values' do + before do + stub_gitlab_rb( + storage_check: { enable: true } + ) + end + + it_behaves_like 'enabled runit service', 'storage-check', 'root', 'root' + end + + context 'custom values' do + before do + stub_gitlab_rb( + storage_check: { + enable: true + }, + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'enabled runit service', 'storage-check', 'root', 'root' + end + end +end diff --git a/spec/chef/cookbooks/gitlab/recipes/users_spec.rb b/spec/chef/cookbooks/gitlab/recipes/users_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy91c2Vyc19zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/recipes/users_spec.rb @@ -0,0 +1,47 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::users' do + let(:chef_run) { ChefSpec::SoloRunner.new.converge('gitlab::users') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'with default attributes' do + it 'creates gitlab user' do + account_params = { + username: 'git', + groupname: 'git', + home: '/var/opt/gitlab', + shell: '/bin/sh' + } + + expect(chef_run).to create_directory('/var/opt/gitlab') + expect(chef_run).to create_account('GitLab user and group').with(account_params) + end + + it 'creates .gitconfig' do + template_params = { + owner: 'git', + group: 'git', + mode: '0644' + } + + expect(chef_run).to create_template('/var/opt/gitlab/.gitconfig').with(template_params) + expect(chef_run).to render_file('/var/opt/gitlab/.gitconfig').with_content { |content| + expect(content).to match(/name = GitLab/) + expect(content).to match(/email = gitlab@fauxhai.local/) + expect(content).to match(/fsyncObjectFiles = true/) + } + end + + it 'creates .bundle directory' do + directory_params = { + owner: 'git', + group: 'git' + } + + expect(chef_run).to create_directory('/var/opt/gitlab/.bundle').with(directory_params) + end + end +end diff --git a/spec/chef/cookbooks/gitlab/resources/puma_config_spec.rb b/spec/chef/cookbooks/gitlab/resources/puma_config_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVzb3VyY2VzL3B1bWFfY29uZmlnX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/gitlab/resources/puma_config_spec.rb @@ -0,0 +1,53 @@ +require 'chef_helper' + +RSpec.describe 'puma_config' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(puma_config)) + end + + context 'create' do + let(:chef_run) { runner.converge('test_gitlab::puma_config_create') } + + it 'creates necessary directories' do + expect(chef_run).to create_directory('/var/opt/gitlab/gitlab-rails/etc') + end + + it 'renders puma.rb file' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| + expect(content).to match(%r(^environment 'production')) + expect(content).to match(%r(^rackup '/opt/gitlab/embedded/service/gitlab-rails/config.ru')) + expect(content).to match(%r(^pidfile '/opt/gitlab/var/puma/puma.pid')) + expect(content).to match(%r(^state_path '/opt/gitlab/var/puma/puma.state')) + expect(content).to match(%r(^bind 'unix:///var/opt/gitlab/gitlab-rails/sockets/gitlab.socket')) + expect(content).to match(%r(^bind 'tcp://127.0.0.1:8080')) + expect(content).to match(%r(^directory '/var/opt/gitlab/gitlab-rails/working')) + expect(content).to match(%r(^require_relative "/opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/cluster/lifecycle_events"$)) + expect(content).to match(/^options = { workers: 2 }$/) + expect(content).to match(%r(Gitlab::Cluster::PumaWorkerKillerInitializer.start\(options\))) + expect(content).to match(/^preload_app!$/) + expect(content).to match(%r(^require_relative "/opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/puma_logging/json_formatter"$)) + } + end + end + + context 'create with default puma config' do + let(:chef_run) { runner.converge('test_gitlab::puma_config_create') } + + it 'renders puma.rb file' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| + expect(content).to match(%r(rackup '/opt/gitlab/embedded/service/gitlab-rails)) + } + end + end + + context 'create with custom Puma settings' do + let(:chef_run) { runner.converge('test_gitlab::puma_config_custom') } + + it 'renders puma.rb file' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| + expect(content).to match(%r(Gitlab::Cluster::PumaWorkerKillerInitializer.start\(options, puma_per_worker_max_memory_mb: 1000\))) + expect(content).to match(%r(rackup '/opt/custom/gitlab/embedded/service/gitlab-rails)) + } + end + end +end diff --git a/spec/chef/cookbooks/gitlab/resources/rails_migration_spec.rb b/spec/chef/cookbooks/gitlab/resources/rails_migration_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVzb3VyY2VzL3JhaWxzX21pZ3JhdGlvbl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/gitlab/resources/rails_migration_spec.rb @@ -0,0 +1,67 @@ +require 'chef_helper' + +RSpec.describe 'rails_migration' do + let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(rails_migration)) } + + before do + allow_any_instance_of(PgHelper).to receive(:postgresql_user).and_return('fakeuser') + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'run' do + let(:chef_run) { runner.converge('test_gitlab::rails_migration_run') } + let(:bash_block) { chef_run.bash('migrate gitlab-test database') } + let(:migration_block) { chef_run.rails_migration('gitlab-test') } + + context 'when database has not been migrated' do + before do + allow_any_instance_of(GitlabGeoHelper).to receive(:migrated?).and_return(false) + end + + it 'executes the bash script migration' do + expect(chef_run).to run_bash('migrate gitlab-test database') + end + + it 'restarts dependent_services' do + expect(bash_block).to notify('ruby_block[test-dependent]').to(:restart) + end + end + + context 'when database has been migrated already' do + it 'doesnt execute the bash script' do + expect_any_instance_of(RailsMigrationHelper).to receive(:migrated?) { true } + + expect(chef_run).not_to run_bash('migrate gitlab-test database') + end + end + + context 'bash script' do + it 'defines log file based on logfile_prefix' do + log_file = %(log_file="/var/log/gitlab/gitlab-rails/gitlab-test-db-migrate-$(date +%Y-%m-%d-%H-%M-%S).log") + + expect(bash_block.code).to include(log_file) + end + + it 'passes appropriate environment variables through' do + expect(bash_block.environment).to match(hash_including('SOME_ENV')) + end + + it 'triggers provided rake_task' do + migrate = %(/opt/gitlab/bin/gitlab-rake gitlab:db:configure 2>& 1 | tee ${log_file}) + + expect(bash_block.code).to match(/#{migrate}/) + end + + it 'pipes exit code to db-migrate file' do + fake_connection_digest = '63467bb60aa187d2a6830aa8f1b5cbe0' + fake_revision = 'f9631484e7f' + pipe = %(echo $STATUS > /var/opt/gitlab/gitlab-rails/upgrade-status/db-migrate-#{fake_connection_digest}-#{fake_revision}) + + expect_any_instance_of(RailsMigrationHelper).to receive(:connection_digest) { fake_connection_digest } + expect_any_instance_of(RailsMigrationHelper).to receive(:revision) { fake_revision } + + expect(bash_block.code).to include(pipe) + end + end + end +end diff --git a/spec/chef/cookbooks/letsencrypt/libraries/lets_encrypt_spec.rb b/spec/chef/cookbooks/letsencrypt/libraries/lets_encrypt_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9sZXRzZW5jcnlwdC9saWJyYXJpZXMvbGV0c19lbmNyeXB0X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/letsencrypt/libraries/lets_encrypt_spec.rb @@ -0,0 +1,179 @@ +require 'chef_helper' + +RSpec.describe LetsEncrypt do + subject { ::LetsEncrypt } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context '.parse_variables' do + it 'calls to parse_enable' do + expect(subject).to receive(:parse_enable) + + subject.parse_variables + end + end + + context '.parse_enable' do + context 'when specifying letsencrypt enabled' do + context 'true' do + before { stub_gitlab_rb(letsencrypt: { enable: true }) } + + it 'should not call should_auto_enable?' do + expect(subject).not_to receive(:should_auto_enable?) + + subject.parse_enable + end + end + + context 'false' do + before { stub_gitlab_rb(letsencrypt: { enable: false }) } + + it 'should not call should_auto_enable?' do + expect(subject).not_to receive(:should_auto_enable?) + + subject.parse_enable + end + end + + context 'unspecified' do + it 'should use the value of should_auto_enable' do + allow(subject).to receive(:should_auto_enable?).and_return('bananas') + subject.parse_enable + + expect(Gitlab['letsencrypt']['enable']).to eq('bananas') + end + end + end + end + + context '.should_auto_enable?' do + let(:node) { Mash.new(gitlab: { nginx: {} }) } + + before do + stub_gitlab_rb( + gitlab_rails: { + gitlab_https: true + }, + nginx: { + ssl_certificate_key: 'example.key', + ssl_certificate: 'example.crt' + } + ) + + allow(Gitlab).to receive(:[]).with(:node).and_return(node) + allow(File).to receive(:exist?).with('example.key').and_return(false) + allow(File).to receive(:exist?).with('example.crt').and_return(false) + end + + it 'is true' do + expect(subject.should_auto_enable?).to be_truthy + end + + it 'is false when not using a https url' do + stub_gitlab_rb(gitlab_rails: { gitlab_https: false }) + + expect(subject.should_auto_enable?).to be_falsey + end + + it 'is false when nginx is not enabled' do + stub_gitlab_rb(nginx: { enable: false }) + + expect(subject.should_auto_enable?).to be_falsey + end + + it 'is false when nginx is disabled by roles' do + allow(node['gitlab']['nginx']).to receive(:[]).with('enable').and_return(false) + + expect(subject.should_auto_enable?).to be_falsey + end + + it 'is false with explicit nginx.listen_https = false' do + stub_gitlab_rb(nginx: { listen_https: false }) + + expect(subject.should_auto_enable?).to be_falsey + end + + it 'is false with the key present' do + allow(File).to receive(:exist?).with('example.key').and_return(true) + + expect(subject.should_auto_enable?).to be_falsey + end + + it 'is false with the cert present' do + mock_cert = OpenSSL::X509::Certificate.new + allow(mock_cert).to receive(:not_after).and_return(Time.now + 600) + allow(File).to receive(:exist?).with('example.crt').and_return(true) + allow(File).to receive(:read).with('example.crt').and_return(nil) + allow(OpenSSL::X509::Certificate).to receive(:new).and_return(mock_cert) + + expect(subject.should_auto_enable?).to be_falsey + end + + it 'is true when files present, but we provisioned them before' do + stub_gitlab_rb(letsencrypt: { auto_enabled: true }) + + allow(File).to receive(:exist?).with('example.key').and_return(true) + allow(File).to receive(:exist?).with('example.crt').and_return(true) + + expect(subject.should_auto_enable?).to be_truthy + end + + it 'is true when files present, but LE certificate is expired' do + mock_cert = OpenSSL::X509::Certificate.new + allow(mock_cert).to receive(:not_after).and_return(Time.now - 1) + allow(mock_cert).to receive(:issuer).and_return( + OpenSSL::X509::Name.parse(%(/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3))) + allow(File).to receive(:exist?).with('example.key').and_return(true) + allow(File).to receive(:exist?).with('example.crt').and_return(true) + allow(File).to receive(:read).with('example.crt').and_return(nil) + allow(OpenSSL::X509::Certificate).to receive(:new).and_return(mock_cert) + + expect(subject.should_auto_enable?).to be_truthy + end + + it 'is false when files present, but non-LE certificate is expired' do + mock_cert = OpenSSL::X509::Certificate.new + allow(mock_cert).to receive(:not_after).and_return(Time.now - 1) + allow(mock_cert).to receive(:issuer).and_return( + OpenSSL::X509::Name.parse('/C=US/O=Example Corporation/CN=Example')) + allow(File).to receive(:exist?).with('example.key').and_return(true) + allow(File).to receive(:exist?).with('example.crt').and_return(true) + allow(File).to receive(:read).with('example.crt').and_return(nil) + allow(OpenSSL::X509::Certificate).to receive(:new).and_return(mock_cert) + + expect(subject.should_auto_enable?).to be_falsey + end + end + + context '.save_auto_enabled' do + it 'does nothing if not auto_enabled' do + expect(SecretsHelper).not_to receive(:load_gitlab_secrets) + + subject.save_auto_enabled + end + + context 'auto_enabled' do + before do + stub_gitlab_rb(letsencrypt: { auto_enabled: true }) + allow(SecretsHelper).to receive(:load_gitlab_secrets).and_return({}) + allow(SecretsHelper).to receive(:write_to_gitlab_secrets) + end + + it 'writes when secret is absent' do + expect(SecretsHelper).to receive(:write_to_gitlab_secrets) + + subject.save_auto_enabled + end + + it 'does not write if secret is already true' do + allow(SecretsHelper).to receive(:load_gitlab_secrets) + .and_return('letsencrypt' => { 'auto_enabled' => true }) + expect(SecretsHelper).not_to receive(:write_to_gitlab_secrets) + + subject.save_auto_enabled + end + end + end +end diff --git a/spec/chef/cookbooks/letsencrypt/recipes/letsencrypt_spec.rb b/spec/chef/cookbooks/letsencrypt/recipes/letsencrypt_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9sZXRzZW5jcnlwdC9yZWNpcGVzL2xldHNlbmNyeXB0X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/letsencrypt/recipes/letsencrypt_spec.rb @@ -0,0 +1,227 @@ +require 'chef_helper' + +RSpec.describe 'enabling letsencrypt' do + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb(external_url: 'https://fakehost.example.com') + end + + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + context 'unspecified uses LetsEncrypt.should_auto_enable?' do + it 'true' do + allow(LetsEncrypt).to receive(:should_auto_enable?).and_return(true) + + expect(chef_run).to include_recipe('letsencrypt::enable') + end + + it 'false' do + allow(LetsEncrypt).to receive(:should_auto_enable?).and_return(false) + + expect(chef_run).to include_recipe('letsencrypt::disable') + expect(chef_run).not_to include_recipe('letsencrypt::enable') + end + end + + context 'specified' do + it 'true' do + stub_gitlab_rb(letsencrypt: { enable: true }) + + expect(chef_run).to include_recipe('letsencrypt::enable') + end + + it 'false' do + stub_gitlab_rb(letsencrypt: { enable: false }) + + expect(chef_run).not_to include_recipe('letsencrypt::enable') + end + end +end + +RSpec.describe 'letsencrypt::enable' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + let(:node) { chef_run.node } + + let(:https_redirect_block) { %r!server { ## HTTPS redirect server(.*)} ## end HTTPS redirect server!m } + let(:https_block) { %r!server { ## HTTPS server(.*)} ## end HTTPS server!m } + let(:acme_challenge_block) { %r!\s*location /.well-known/acme-challenge/ {! } + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(LetsEncrypt).to receive(:should_auto_enable?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('nginx').and_return(true) + end + + context 'default' do + it 'does not run' do + expect(chef_run).not_to include_recipe('letsencrypt::enable') + end + end + + context 'enabled' do + before do + stub_gitlab_rb( + external_url: 'https://fakehost.example.com', + mattermost_external_url: 'https://fakemost.example.com', + registry_external_url: 'https://fakereg.example.com', + letsencrypt: { + enable: true + } + ) + end + + it 'is included' do + expect(chef_run).to include_recipe('letsencrypt::enable') + end + + context 'when updating nginx configurations' do + using RSpec::Parameterized::TableSyntax + + where(:redirect_config_key, :nginx_config_file) do + 'nginx' | '/var/opt/gitlab/nginx/conf/gitlab-http.conf' + 'mattermost-nginx' | '/var/opt/gitlab/nginx/conf/gitlab-mattermost-http.conf' + 'registry-nginx' | '/var/opt/gitlab/nginx/conf/gitlab-registry.conf' + end + + with_them do + it 'redirects http to https' do + expect(node['gitlab'][redirect_config_key]['redirect_http_to_https']).to be_truthy + end + + it 'includes the well known acme challenge location block' do + expect(chef_run).to render_file(nginx_config_file).with_content { |content| + https_redirect_server = content.match(https_redirect_block) + https_server = content.match(https_block) + + expect(https_redirect_server).not_to be_nil + expect(https_server).not_to be_nil + + expect(https_redirect_server.to_s).to match(acme_challenge_block) + expect(https_server.to_s).to match(acme_challenge_block) + } + end + end + end + + it 'uses http authorization by default' do + expect(chef_run).to include_recipe('letsencrypt::http_authorization') + end + + it 'creates a self signed certificate' do + expect(chef_run).to create_acme_selfsigned('fakehost.example.com').with( + key: '/etc/gitlab/ssl/fakehost.example.com.key', + crt: '/etc/gitlab/ssl/fakehost.example.com.crt' + ) + end + + it 'creates a letsencrypt certificate' do + expect(chef_run).to create_letsencrypt_certificate('fakehost.example.com').with( + key: '/etc/gitlab/ssl/fakehost.example.com.key', + crt: '/etc/gitlab/ssl/fakehost.example.com.crt' + ) + end + + it 'warns the user' do + prod_cert = chef_run.letsencrypt_certificate('fakehost.example.com') + expect(prod_cert).to notify('ruby_block[display_le_message]').to(:run) + end + + context 'auto_renew' do + context 'default' do + it 'enables crond' do + expect(chef_run).to include_recipe('crond::enable') + end + + it 'adds a crond_job' do + expect(chef_run).to create_crond_job('letsencrypt-renew').with( + user: "root", + hour: 0, + minute: 31, + day_of_month: "*/4", + command: "/opt/gitlab/bin/gitlab-ctl renew-le-certs" + ) + end + + it 'does not log a warning' do + expect(LoggingHelper).not_to receive(:warning).with("Let's Encrypt is enabled, but external_url is using http") + chef_run.ruby_block('display_le_message').block.call + end + end + + context 'false' do + before do + stub_gitlab_rb(letsencrypt: { enable: true, auto_renew: false }) + allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return false + end + + it 'removes the letsencrypt-renew cronjob' do + expect(chef_run).to delete_crond_job('letsencrypt-renew') + end + + it 'warns that we do not setup automatic renewal' do + chef_run.ruby_block('display_le_message').block.call + + expect_logged_warning(/does not setup/) + end + end + end + + context 'external_url uses http' do + before do + stub_gitlab_rb( + external_url: 'http://plainhost.example.com', + letsencrypt: { + enable: true + } + ) + end + + it 'logs a warning' do + expect(chef_run).to run_ruby_block('http external-url') + end + end + end +end + +# This should work standalone for renewal purposes +RSpec.describe 'letsencrypt::renew' do + let(:chef_run) do + ChefSpec::SoloRunner.converge('gitlab::letsencrypt_renew') + end + + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('nginx').and_return(true) + end + + context 'letsencrypt enabled' do + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb( + external_url: 'https://standalone.fakehost.com', + letsencrypt: { + enable: true + } + ) + end + + it 'executes letsencrypt_certificate' do + expect(chef_run).to create_letsencrypt_certificate('standalone.fakehost.com') + end + end + + context 'letsencrypt auto-enabled' do + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(OpenSSL::X509::Certificate).to receive(:not_after).and_return(Time.now - 1) + stub_gitlab_rb( + external_url: 'https://standalone.fakehost.com' + ) + end + + it 'executes letsencrypt_certificate' do + expect(chef_run).to create_letsencrypt_certificate('standalone.fakehost.com') + end + end +end diff --git a/spec/chef/cookbooks/letsencrypt/resources/letsencrypt_certificate_spec.rb b/spec/chef/cookbooks/letsencrypt/resources/letsencrypt_certificate_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9sZXRzZW5jcnlwdC9yZXNvdXJjZXMvbGV0c2VuY3J5cHRfY2VydGlmaWNhdGVfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/letsencrypt/resources/letsencrypt_certificate_spec.rb @@ -0,0 +1,115 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::letsencrypt' do + let(:chef_run) do + ChefSpec::SoloRunner.new(step_into: %(letsencrypt_certificate)).converge('gitlab::default') + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb( + external_url: 'https://fakehost.example.com', + letsencrypt: { + enable: true, + } + ) + end + + context 'with NGINX running' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('nginx').and_return(true) + end + + it 'creates a staging certificate' do + expect(chef_run).to create_acme_certificate('staging').with( + crt: '/etc/gitlab/ssl/fakehost.example.com.crt-staging', + key: '/etc/gitlab/ssl/fakehost.example.com.key-staging', + wwwroot: '/var/opt/gitlab/nginx/www', + dir: 'https://acme-staging-v02.api.letsencrypt.org/directory', + sensitive: true + ) + end + + it "updates the node['acme']['private_key'] attribute" do + expect(chef_run).to run_ruby_block('reset private key') + end + + it 'creates a production certificate' do + expect(chef_run).to create_acme_certificate('production').with( + crt: '/etc/gitlab/ssl/fakehost.example.com.crt', + key: '/etc/gitlab/ssl/fakehost.example.com.key', + wwwroot: '/var/opt/gitlab/nginx/www', + sensitive: true + ) + end + + context 'specifying a different key_size' do + before do + allow(File).to receive(:file?).and_call_original + allow(File).to receive(:file?).with('/etc/gitlab/ssl/fakehost.example.com.key').and_return(true) + allow(File).to receive(:read).with(anything).and_call_original + allow(File).to receive(:read).with('/etc/gitlab/ssl/fakehost.example.com.key').and_return(OpenSSL::PKey::RSA.new(2048)) + + stub_gitlab_rb( + external_url: 'https://fakehost.example.com', + letsencrypt: { + enable: true, + key_size: 3072 + } + ) + end + + it 'deletes and recreates the SSL files' do + expect(chef_run).to delete_file('/etc/gitlab/ssl/fakehost.example.com.key') + expect(chef_run).to delete_file('/etc/gitlab/ssl/fakehost.example.com.crt') + + expect(chef_run).to create_acme_certificate('production').with( + crt: '/etc/gitlab/ssl/fakehost.example.com.crt', + key: '/etc/gitlab/ssl/fakehost.example.com.key', + key_size: 3072, + wwwroot: '/var/opt/gitlab/nginx/www', + sensitive: true + ) + end + end + + it 'reloads nginx' do + prod_cert = chef_run.acme_certificate('production') + expect(prod_cert).to notify('execute[reload nginx]').to(:run) + end + + context 'with extra options' do + before do + stub_gitlab_rb( + external_url: 'https://fakehost.example.com', + letsencrypt: { + enable: true, + alt_names: %w(one.example.com two.example.com), + contact_emails: %w(foo@bar.com one@two.com) + } + ) + end + + it 'adds alt_names to the certificate resource' do + expect(chef_run).to create_acme_certificate('production') + .with( + alt_names: %w(one.example.com two.example.com), + contact: %w(mailto:foo@bar.com mailto:one@two.com) + ) + end + end + end + + context 'when NGINX is not running' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('nginx').and_return(false) + end + + it 'does not attempt to create a certificate' do + expect(chef_run).not_to create_acme_certificate('staging') + expect(chef_run).not_to create_acme_certificate('production') + end + end +end diff --git a/spec/chef/cookbooks/logrotate/recipes/folders_and_configs_spec.rb b/spec/chef/cookbooks/logrotate/recipes/folders_and_configs_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9sb2dyb3RhdGUvcmVjaXBlcy9mb2xkZXJzX2FuZF9jb25maWdzX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/logrotate/recipes/folders_and_configs_spec.rb @@ -0,0 +1,143 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::logrotate_folder_and_configs_spec' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when logrotate is enabled' do + it 'creates default set of directories' do + expect(chef_run.node['logrotate']['dir']) + .to eql('/var/opt/gitlab/logrotate') + expect(chef_run.node['logrotate']['log_directory']) + .to eql('/var/log/gitlab/logrotate') + + expect(chef_run).to create_directory('/var/opt/gitlab/logrotate').with( + owner: nil, + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/var/log/gitlab/logrotate').with( + owner: nil, + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/var/opt/gitlab/logrotate/logrotate.d').with( + owner: nil, + group: nil, + mode: '0700' + ) + end + + it 'creates logrotate directories in alternative locations' do + stub_gitlab_rb(logrotate: { dir: "/tmp/logrotate", log_directory: "/tmp/logs/logrotate" }) + + expect(chef_run).to create_directory('/tmp/logrotate').with( + owner: nil, + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/tmp/logs/logrotate').with( + owner: nil, + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/tmp/logrotate/logrotate.d').with( + owner: nil, + group: nil, + mode: '0700' + ) + end + + it 'creates default set of configuration templates' do + expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/nginx') + expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-rails') + expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-shell') + expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-workhorse') + expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-pages') + expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitaly') + end + + it 'populates configuration template with default values' do + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/daily/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/rotate 30/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/compress/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/copytruncate/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/notifempty/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/postrotate/) + expect(chef_run).not_to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/size/) + expect(chef_run).not_to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/maxsize/) + expect(chef_run).not_to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/dateext/) + end + + it 'populates configuration template with custom values when logrotate is disabled' do + stub_gitlab_rb(logging: + { logrotate_frequency: "weekly", + logrotate_size: "50", + logrotate_maxsize: "50", + logrotate_rotate: "50", + logrotate_compress: "nocompress", + logrotate_method: "copy", + logrotate_postrotate: "/usr/bin/killall -HUP nginx", + logrotate_dateformat: "-%Y-%m-%d", }, logrotate: { enable: false }) + + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/weekly/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/rotate 50/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/nocompress/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/copy/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/postrotate/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/size 50/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/maxsize 50/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/dateext/) + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') + .with_content(/dateformat -%Y-%m-%d/) + end + + context 'when services not under gitlab key are specified' do + it 'populates files correctly' do + stub_gitlab_rb( + logrotate: { + services: ['gitlab-rails', 'gitaly'] + }, + gitaly: { + log_directory: '/my/log/directory' + } + ) + expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-rails') + expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/gitaly') + .with_content(/my\/log\/directory\/\*\.log/) + end + end + + context 'when services that are not supported are specified' do + it 'raises an error' do + stub_gitlab_rb( + logrotate: { + services: ['gitlab-rails', 'foo-bar'] + } + ) + + expect { chef_run }.to raise_error("Service foo-bar was specified in logrotate['services'], but is not a valid service.") + end + end + end +end diff --git a/spec/chef/cookbooks/logrotate/recipes/logrotate_spec.rb b/spec/chef/cookbooks/logrotate/recipes/logrotate_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9sb2dyb3RhdGUvcmVjaXBlcy9sb2dyb3RhdGVfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/logrotate/recipes/logrotate_spec.rb @@ -0,0 +1,54 @@ +require 'chef_helper' + +RSpec.describe 'logrotate' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when logrotate is enabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/logrotate/log/config') } + + it_behaves_like "enabled runit service", "logrotate", "root", "root" + + it 'populates the files with expected configuration' do + expect(config_template).to notify('ruby_block[reload_log_service]') + + expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/run') + .with_content(/cd \/var\/opt\/gitlab\/logrotate/) + expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/run') + .with_content(/exec \/opt\/gitlab\/embedded\/bin\/chpst -P \/usr\/bin\/env/) + expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/run') + .with_content(/\/opt\/gitlab\/embedded\/bin\/gitlab-logrotate-wrapper/) + + expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/logrotate/) + + expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/config') + .with_content(/s209715200/) + expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/config') + .with_content(/n30/) + expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/config') + .with_content(/t86400/) + expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/config') + .with_content(/!gzip/) + end + + it 'executes start command' do + expect(chef_run).to run_execute('/opt/gitlab/bin/gitlab-ctl start logrotate').with(retries: 20) + end + end + + context 'when logrotate is disabled' do + before do + stub_gitlab_rb(logrotate: { enable: false }) + end + + it_behaves_like "disabled runit service", "logrotate" + + it 'does not execute the start command' do + expect(chef_run).not_to run_execute('/opt/gitlab/bin/gitlab-ctl start logrotate').with(retries: 20) + end + end +end diff --git a/spec/chef/cookbooks/mattermost/recipes/mattermost_spec.rb b/spec/chef/cookbooks/mattermost/recipes/mattermost_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tYXR0ZXJtb3N0L3JlY2lwZXMvbWF0dGVybW9zdF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/mattermost/recipes/mattermost_spec.rb @@ -0,0 +1,322 @@ +require 'chef_helper' + +RSpec.describe 'gitlab::mattermost' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:default_vars) do + { + 'MM_FILESETTINGS_DIRECTORY' => '/var/opt/gitlab/mattermost/data', + 'MM_GITLABSETTINGS_AUTHENDPOINT' => 'http://gitlab.example.com/oauth/authorize', + 'MM_GITLABSETTINGS_ENABLE' => 'false', + 'MM_GITLABSETTINGS_ID' => '', + 'MM_GITLABSETTINGS_SCOPE' => '', + 'MM_GITLABSETTINGS_SECRET' => '', + 'MM_GITLABSETTINGS_TOKENENDPOINT' => 'http://gitlab.example.com/oauth/token', + 'MM_GITLABSETTINGS_USERAPIENDPOINT' => 'http://gitlab.example.com/api/v4/user', + 'MM_INSTALL_TYPE' => 'gitlab_omnibus', + 'MM_LOGSETTINGS_FILELOCATION' => '/var/log/gitlab/mattermost', + 'MM_PLUGINSETTINGS_CLIENTDIRECTORY' => '/var/opt/gitlab/mattermost/client-plugins', + 'MM_PLUGINSETTINGS_DIRECTORY' => '/var/opt/gitlab/mattermost/plugins', + 'MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS' => ' gitlab.example.com', + 'MM_SERVICESETTINGS_ENABLEAPITEAMDELETION' => 'true', + 'MM_SERVICESETTINGS_LISTENADDRESS' => '127.0.0.1:8065', + 'MM_SERVICESETTINGS_SITEURL' => 'http://mattermost.example.com', + 'MM_SQLSETTINGS_ATRESTENCRYPTKEY' => 'aabbccddeeff11223344556677889900', + 'MM_SQLSETTINGS_DATASOURCE' => 'user=gitlab_mattermost host=/var/opt/gitlab/postgresql port=5432 dbname=mattermost_production', + 'MM_SQLSETTINGS_DRIVERNAME' => 'postgres', + 'MM_TEAMSETTINGS_SITENAME' => 'GitLab Mattermost', + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb(external_url: 'http://gitlab.example.com', mattermost_external_url: 'http://mattermost.example.com') + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:database_exists?).and_return(true) + allow(SecretsHelper).to receive(:generate_hex).with(64).and_return('aaaabbbbccccddddeeeeffff1111222233334444555566667777888899990000') + allow(SecretsHelper).to receive(:generate_hex).with(16).and_return('aabbccddeeff11223344556677889900') + end + + context 'by default' do + it 'creates a default VERSION file and restarts service' do + expect(chef_run).to create_version_file('Create version file for Mattermost').with( + version_file_path: '/var/opt/gitlab/mattermost/VERSION', + version_check_cmd: 'cat /opt/gitlab/embedded/service/mattermost/VERSION' + ) + + expect(chef_run.version_file('Create version file for Mattermost')).to notify('runit_service[mattermost]').to(:hup) + end + end + + context 'service user and group' do + context 'default values' do + it_behaves_like "enabled runit service", "mattermost", "root", "root" + end + + context 'custom user and group' do + before do + stub_gitlab_rb( + external_url: 'http://gitlab.example.com', + mattermost_external_url: 'http://mattermost.example.com', + mattermost: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like "enabled runit service", "mattermost", "root", "root" + end + end + + context 'SiteUrl setting' do + context 'default value' do + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( + variables: default_vars + ) + end + end + + context 'when explicitly set' do + before do + stub_gitlab_rb(mattermost: { + service_site_url: 'http://mattermost.gitlab.example' + }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( + variables: default_vars.merge({ 'MM_SERVICESETTINGS_SITEURL' => 'http://mattermost.gitlab.example' }) + ) + end + end + end + + it 'authorizes mattermost with gitlab' do + allow(MattermostHelper).to receive(:authorize_with_gitlab) + + expect(chef_run).to run_ruby_block('authorize mattermost with gitlab') + .at_converge_time + expect(MattermostHelper).to receive(:authorize_with_gitlab) + .with 'http://gitlab.example.com' + + chef_run.ruby_block('authorize mattermost with gitlab').block.call + end + + it 'populates mattermost configuration options to node attributes' do + stub_gitlab_rb(mattermost: { enable: true, gitlab_id: 'old' }) + allow(MattermostHelper).to receive(:authorize_with_gitlab) do |url| + Gitlab['mattermost']['gitlab_id'] = 'new' + end + + expect(chef_run).to run_ruby_block('populate mattermost configuration options') + .at_converge_time + + chef_run.ruby_block('authorize mattermost with gitlab').block.call + chef_run.ruby_block('populate mattermost configuration options').block.call + + expect(chef_run.node['mattermost']['gitlab_id']).to eq 'new' + end + + context 'populate env variables based on provided gitlab settings' do + before do + stub_gitlab_rb(mattermost: { + enable: true, + gitlab_enable: true, + gitlab_id: 'gitlab_id', + gitlab_secret: 'gitlab_secret', + gitlab_scope: 'scope', + }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( + variables: default_vars.merge( + { + 'MM_GITLABSETTINGS_ENABLE' => 'true', + 'MM_GITLABSETTINGS_SECRET' => 'gitlab_secret', + 'MM_GITLABSETTINGS_ID' => 'gitlab_id', + 'MM_GITLABSETTINGS_SCOPE' => 'scope', + 'MM_GITLABSETTINGS_AUTHENDPOINT' => 'http://gitlab.example.com/oauth/authorize', + 'MM_GITLABSETTINGS_TOKENENDPOINT' => 'http://gitlab.example.com/oauth/token', + 'MM_GITLABSETTINGS_USERAPIENDPOINT' => 'http://gitlab.example.com/api/v4/user', + } + ) + ) + end + end + + context 'allows overrides to the mattermost settings regarding GitLab endpoints' do + before do + stub_gitlab_rb(mattermost: { + enable: true, + gitlab_enable: true, + gitlab_auth_endpoint: 'https://test-endpoint.example.com/test/auth', + gitlab_token_endpoint: 'https://test-endpoint.example.com/test/token', + gitlab_user_api_endpoint: 'https://test-endpoint.example.com/test/user/api' + }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( + variables: default_vars.merge( + { + 'MM_GITLABSETTINGS_ENABLE' => 'true', + 'MM_GITLABSETTINGS_AUTHENDPOINT' => 'https://test-endpoint.example.com/test/auth', + 'MM_GITLABSETTINGS_TOKENENDPOINT' => 'https://test-endpoint.example.com/test/token', + 'MM_GITLABSETTINGS_USERAPIENDPOINT' => 'https://test-endpoint.example.com/test/user/api' + } + ) + ) + end + end + + context 'gitlab is added to untrusted internal connections list' do + context 'when no allowed internal connections are provided by gitlab.rb' do + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with(variables: default_vars) + end + end + + context 'when some allowed internal connections are provided by gitlab.rb' do + before do + stub_gitlab_rb(mattermost: { enable: true, service_allowed_untrusted_internal_connections: 'localhost' }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( + variables: default_vars.merge( + { + 'MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS' => 'localhost gitlab.example.com' + } + ) + ) + end + end + end + + shared_examples 'no gitlab authorization performed' do + it 'does not authorize mattermost with gitlab' do + expect(chef_run).not_to run_ruby_block('authorize mattermost with gitlab') + end + end + + context 'when gitlab authentication parameters are specified explicitly' do + before { stub_gitlab_rb(mattermost: { enable: true, gitlab_enable: true }) } + + it_behaves_like 'no gitlab authorization performed' + end + + context 'when gitlab-rails is disabled' do + before { stub_gitlab_rb(gitlab_rails: { enable: false }) } + + it_behaves_like 'no gitlab authorization performed' + + it 'does not add gitlab automatically to the list of allowed internal addresses' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( + variables: default_vars.merge( + { + 'MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS' => nil + } + ) + ) + end + end + + context 'when database is not running' do + before { allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(false) } + + it_behaves_like 'no gitlab authorization performed' + end + + context 'when mattermost database does not exist' do + before { allow_any_instance_of(PgHelper).to receive(:database_exists?).and_return(false) } + + it_behaves_like 'no gitlab authorization performed' + end + + context 'when a custom env variable is specified' do + before do + stub_gitlab_rb(mattermost: { env: { 'IAM' => 'CUSTOMVAR' } }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( + variables: default_vars.merge( + { + 'IAM' => 'CUSTOMVAR' + } + ) + ) + end + end + + describe 'letsencrypt' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + mattermost_external_url: 'https://mattermost.example.com' + ) + + allow(File).to receive(:exist?).and_call_original + end + + describe 'HTTP to HTTPS redirection' do + context 'by default' do + it 'is enabled' do + expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-mattermost-http.conf').with_content("return 301 https://mattermost.example.com:443$request_uri;") + end + end + + context 'if disabled in gitlab.rb' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + mattermost_external_url: 'https://mattermost.example.com', + mattermost_nginx: { + redirect_http_to_https: false + } + ) + end + + it 'is disabled' do + expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-mattermost-http.conf') + expect(chef_run).not_to render_file('/var/opt/gitlab/nginx/conf/gitlab-mattermost.conf').with_content("return 301 https://mattermost.example.com:443$request_uri;") + end + end + end + + context 'default certificate file is missing' do + before do + allow(File).to receive(:exist?).with('/etc/gitlab/ssl/mattermost.example.com.crt').and_return(false) + end + + it 'adds itself to letsencrypt alt_names' do + expect(chef_run.node['letsencrypt']['alt_names']).to eql(['mattermost.example.com']) + end + + it 'is reflected in the acme_selfsigned' do + expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( + alt_names: ['mattermost.example.com'] + ) + end + end + + context 'default certificate file is present' do + before do + allow(File).to receive(:exist?).with('/etc/gitlab/ssl/mattermost.example.com.crt').and_return(true) + end + + it 'does not alter letsencrypt alt_names' do + expect(chef_run.node['letsencrypt']['alt_names']).to eql([]) + end + + it 'is reflected in the acme_selfsigned' do + expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( + alt_names: [] + ) + end + end + end +end diff --git a/spec/chef/cookbooks/monitoring/libraries/prometheus_helper_spec.rb b/spec/chef/cookbooks/monitoring/libraries/prometheus_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL2xpYnJhcmllcy9wcm9tZXRoZXVzX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/monitoring/libraries/prometheus_helper_spec.rb @@ -0,0 +1,44 @@ +# +# Copyright:: Copyright (c) 2017 GitLab Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef_helper' + +RSpec.describe PrometheusHelper do + let(:chef_run) { ChefSpec::SoloRunner.new } + subject { described_class.new(chef_run.node) } + + context 'flags for prometheus' do + context 'with default options' do + it 'returns the correct default config string' do + chef_run.converge('gitlab::default') + expect(subject.flags('prometheus')).to eq( + '--web.listen-address=localhost:9090 --storage.tsdb.path=/var/opt/gitlab/prometheus/data --config.file=/var/opt/gitlab/prometheus/prometheus.yml') + end + end + + context 'with custom options' do + before { allow(Gitlab).to receive(:[]).and_call_original } + + it 'does not return the correct string if any attributes have been changed' do + chef_run.node.normal['monitoring']['prometheus']['home'] = '/fake/dir' + chef_run.converge('gitlab::default') + + expect(subject.flags('prometheus')).to eq( + '--web.listen-address=localhost:9090 --storage.tsdb.path=/fake/dir/data --config.file=/fake/dir/prometheus.yml') + end + end + end +end diff --git a/spec/chef/cookbooks/monitoring/libraries/prometheus_spec.rb b/spec/chef/cookbooks/monitoring/libraries/prometheus_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL2xpYnJhcmllcy9wcm9tZXRoZXVzX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/monitoring/libraries/prometheus_spec.rb @@ -0,0 +1,17 @@ +require 'chef_helper' + +RSpec.describe Prometheus do + before { Services.add_services('gitlab', Services::BaseServices.list) } + + it 'should return a list of known services' do + expect(Prometheus.services).to match_array(%w( + prometheus + alertmanager + grafana + node-exporter + redis-exporter + postgres-exporter + gitlab-exporter + )) + end +end diff --git a/spec/chef/cookbooks/monitoring/recipes/alertmanager_spec.rb b/spec/chef/cookbooks/monitoring/recipes/alertmanager_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvYWxlcnRtYW5hZ2VyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/monitoring/recipes/alertmanager_spec.rb @@ -0,0 +1,149 @@ +require 'chef_helper' + +alertmanager_yml_output = <<-ALERTMANAGERYML + --- + global: + smtp_from: gitlab-omnibus + smtp_smarthost: testhost:25 + templates: [] + route: + receiver: default-receiver + routes: [] + receivers: + - name: default-receiver + email_configs: + - to: admin@example.com + inhibit_rules: [] +ALERTMANAGERYML + +RSpec.describe 'monitoring::alertmanager' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when alertmanager is enabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/alertmanager/log/config') } + + before do + stub_gitlab_rb( + prometheus: { enable: true }, + alertmanager: { + enable: true, + admin_email: 'admin@example.com', + }, + gitlab_rails: { + gitlab_email_from: 'gitlab-omnibus', + smtp_enable: true, + smtp_address: 'testhost', + smtp_port: 25, + } + ) + end + + it_behaves_like 'enabled runit service', 'alertmanager', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/alertmanager/env').with_variables(default_vars) + end + + it 'populates the files with expected configuration' do + expect(config_template).to notify('ruby_block[reload_log_service]') + + expect(chef_run).to render_file('/opt/gitlab/sv/alertmanager/run') + .with_content { |content| + expect(content).to match(/exec chpst -P/) + expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/alertmanager/) + expect(content).to match(/alertmanager.yml/) + } + + expect(chef_run).to render_file('/var/opt/gitlab/alertmanager/alertmanager.yml') + .with_content(alertmanager_yml_output.gsub(/^ {2}/, '')) + + expect(chef_run).to render_file('/opt/gitlab/sv/alertmanager/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/alertmanager/) + end + + it 'creates default set of directories' do + expect(chef_run).to create_directory('/var/log/gitlab/alertmanager').with( + owner: 'gitlab-prometheus', + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/var/opt/gitlab/alertmanager').with( + owner: 'gitlab-prometheus', + group: nil, + mode: '0750' + ) + end + + it 'should create a gitlab-prometheus user account' do + expect(chef_run).to create_account('Prometheus user and group').with(username: 'gitlab-prometheus') + end + + it 'sets a default listen address' do + expect(chef_run).to render_file('/opt/gitlab/sv/alertmanager/run') + .with_content(/web.listen-address=localhost:9093/) + end + end + + context 'with user provided settings' do + before do + stub_gitlab_rb( + prometheus: { enable: true }, + alertmanager: { + listen_address: ':9093', + enable: true, + env: { + 'USER_SETTING' => 'asdf1234' + }, + global: { + 'smtp_from' => 'override_value' + } + }, + gitlab_rails: { + smtp_enable: true, + smtp_address: 'other-testhost', + smtp_port: 465, + smtp_from: 'default_value' + } + ) + end + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/alertmanager/run') + .with_content(/web.listen-address=:9093/) + end + + it 'keeps the defaults that the user did not override' do + expect(chef_run).to render_file('/var/opt/gitlab/alertmanager/alertmanager.yml') + .with_content(/receiver: default-receiver/) + end + + it 'renders alertmanager.yml with the non-default value' do + expect(chef_run).to render_file('/var/opt/gitlab/alertmanager/alertmanager.yml') + .with_content(/smtp_smarthost: other-testhost:465/) + end + + it 'renders alertmanager.yml with the user override value' do + expect(chef_run).to render_file('/var/opt/gitlab/alertmanager/alertmanager.yml') + .with_content(/smtp_from: override_value/) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/alertmanager/env').with_variables( + default_vars.merge( + { + 'USER_SETTING' => 'asdf1234' + } + ) + ) + end + end +end diff --git a/spec/chef/cookbooks/monitoring/recipes/gitlab-exporter_spec.rb b/spec/chef/cookbooks/monitoring/recipes/gitlab-exporter_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvZ2l0bGFiLWV4cG9ydGVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/monitoring/recipes/gitlab-exporter_spec.rb @@ -0,0 +1,151 @@ +require 'chef_helper' + +RSpec.describe 'monitoring::gitlab-exporter' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:default_env_vars) do + { + 'LD_PRELOAD' => '/opt/gitlab/embedded/lib/libjemalloc.so', + 'MALLOC_CONF' => 'dirty_decay_ms:0,muzzy_decay_ms:0', + 'RUBY_GC_HEAP_INIT_SLOTS' => 80000, + 'RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO' => 0.055, + 'RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO' => 0.111, + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + 'SSL_CERT_FILE' => '/opt/gitlab/embedded/ssl/cert.pem' + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when gitlab-exporter is enabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/gitlab-exporter/log/config') } + + before do + stub_gitlab_rb( + gitlab_exporter: { enable: true } + ) + end + + it 'creates a default VERSION file and restarts service' do + expect(chef_run).to create_version_file('Create version file for GitLab-Exporter').with( + version_file_path: '/var/opt/gitlab/gitlab-exporter/RUBY_VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/ruby --version' + ) + + expect(chef_run.version_file('Create version file for GitLab-Exporter')).to notify('runit_service[gitlab-exporter]').to(:restart) + end + + it_behaves_like 'enabled runit service', 'gitlab-exporter', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-exporter/env').with_variables(default_env_vars) + end + + it 'populates the files with expected configuration' do + expect(config_template).to notify('ruby_block[reload_log_service]') + + expect(chef_run).to render_file('/opt/gitlab/sv/gitlab-exporter/run') + .with_content { |content| + expect(content).to match(/exec chpst -P/) + expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/gitlab-exporter/) + } + + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-exporter/gitlab-exporter.yml') + .with_content { |content| + # Not disabling this Cop fails the test with: + # Psych::BadAlias: Unknown alias: db_common + settings = YAML.load(content) # rubocop:disable Security/YAMLLoad + expect(settings.dig('server', 'name')).to eq('webrick') + expect(settings.dig('probes', 'database')).not_to be_nil + expect(settings.dig('probes', 'ruby')).not_to be_nil + expect(settings.dig('probes', 'metrics', 'rows_count')).not_to be_nil + + expect(content).to match(/host=\/var\/opt\/gitlab\/postgresql/) + expect(content).to match(/redis_enable_client: true/) + } + + expect(chef_run).to render_file('/opt/gitlab/sv/gitlab-exporter/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/gitlab-exporter/) + end + + it 'creates default set of directories' do + expect(chef_run).to create_directory('/var/log/gitlab/gitlab-exporter').with( + owner: 'git', + group: nil, + mode: '0700' + ) + end + end + + context 'with custom user and group' do + before do + stub_gitlab_rb( + gitlab_exporter: { + enable: true + }, + user: { + username: 'foo', + group: 'bar' + } + ) + end + + it_behaves_like 'enabled runit service', 'gitlab-exporter', 'root', 'root' + end + + context 'when gitlab-exporter is enabled and postgres is disabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/gitlab-exporter/log/config') } + + before do + stub_gitlab_rb( + gitlab_exporter: { enable: true }, + gitlab_rails: { db_host: 'postgres.example.com', db_port: '5432', db_password: 'secret' }, + postgresql: { enabled: false } + ) + end + + it 'populates a config with a remote host' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-exporter/gitlab-exporter.yml') + .with_content { |content| + expect(content).to match(/host=postgres\.example\.com/) + expect(content).to match(/port=5432/) + expect(content).to match(/password=secret/) + } + end + end + + context 'with custom Redis settings' do + before do + stub_gitlab_rb( + gitlab_exporter: { enable: true }, + gitlab_rails: { redis_enable_client: false } + ) + end + + it 'disables Redis CLIENT' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-exporter/gitlab-exporter.yml') + .with_content { |content| + expect(content).to match(/redis_enable_client: false/) + } + end + end + + context 'when log dir is changed' do + before do + stub_gitlab_rb( + gitlab_exporter: { + log_directory: 'foo', + enable: true + } + ) + end + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/gitlab-exporter/log/run') + .with_content(/exec svlogd -tt foo/) + end + end + + include_examples "consul service discovery", "gitlab_exporter", "gitlab-exporter" +end diff --git a/spec/chef/cookbooks/monitoring/recipes/grafana_spec.rb b/spec/chef/cookbooks/monitoring/recipes/grafana_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvZ3JhZmFuYV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/monitoring/recipes/grafana_spec.rb @@ -0,0 +1,356 @@ +require 'chef_helper' + +default_dashboards_yml_output = <<-DASHBOARDYML +--- +apiVersion: 1 +providers: +- name: GitLab Omnibus + orgId: 1 + folder: GitLab Omnibus + type: file + disableDeletion: true + updateIntervalSeconds: 600 + options: + path: "/opt/gitlab/embedded/service/grafana-dashboards" +DASHBOARDYML + +default_datasources_yml_output = <<-DATASOURCEYML +--- +apiVersion: 1 +datasources: +- name: GitLab Omnibus + type: prometheus + access: proxy + url: http://localhost:9090 +DATASOURCEYML + +RSpec.describe 'monitoring::grafana' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:database_exists?).and_return(true) + end + + context 'when grafana is enabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/grafana/log/config') } + + before do + stub_gitlab_rb( + grafana: { enable: true }, + prometheus: { enable: true } + ) + end + + it_behaves_like 'enabled runit service', 'grafana', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/grafana/env').with_variables(default_vars) + end + + it 'populates the files with expected configuration' do + expect(config_template).to notify('ruby_block[reload_log_service]') + + expect(chef_run).to render_file('/opt/gitlab/sv/grafana/run') + .with_content { |content| + expect(content).to match(/exec chpst -P/) + expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/grafana-server/) + expect(content).to match(/-config '\/var\/opt\/gitlab\/grafana\/grafana.ini'/) + } + + expect(chef_run).to render_file('/opt/gitlab/sv/grafana/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/grafana/) + end + + it 'creates default set of directories' do + expect(chef_run).to create_directory('/var/log/gitlab/grafana').with( + owner: 'gitlab-prometheus', + group: nil, + mode: '0700' + ) + end + + it 'creates the configuration file' do + expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') + .with_content { |content| + expect(content).to match(/http_addr = localhost/) + expect(content).to match(/http_port = 3000/) + expect(content).to match(/root_url = http:\/\/localhost\/-\/grafana/) + expect(content).not_to match(/\[auth\.gitlab\]/) + expect(content).to match(/reporting_enabled = true/) + } + end + + it 'creates a default dashboards file' do + expect(chef_run).to render_file('/var/opt/gitlab/grafana/provisioning/dashboards/gitlab_dashboards.yml') + .with_content(default_dashboards_yml_output) + end + + it 'creates a default datasources file' do + expect(chef_run).to render_file('/var/opt/gitlab/grafana/provisioning/datasources/gitlab_datasources.yml') + .with_content(default_datasources_yml_output) + end + end + + context 'when grafana is enabled and prometheus is disabled' do + before do + stub_gitlab_rb( + external_url: 'http://gitlab.example.com', + prometheus: { enable: false }, + grafana: { enable: true } + ) + end + + it 'should create the gitlab-prometheus account if prometheus is disabled' do + expect(chef_run).to create_account('Prometheus user and group').with_username('gitlab-prometheus') + end + end + + context 'when log dir is changed' do + before do + stub_gitlab_rb( + external_url: 'http://gitlab.example.com', + grafana: { + log_directory: 'foo', + enable: true + } + ) + end + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/grafana/log/run') + .with_content(/exec svlogd -tt foo/) + end + end + + it 'authorizes Grafana with gitlab' do + stub_gitlab_rb(external_url: 'http://gitlab.example.com') + + allow(GrafanaHelper).to receive(:authorize_with_gitlab) + + expect(chef_run).to run_ruby_block('authorize Grafana with GitLab') + .at_converge_time + expect(GrafanaHelper).to receive(:authorize_with_gitlab) + .with 'http://gitlab.example.com' + + chef_run.ruby_block('authorize Grafana with GitLab').block.call + end + + context 'with user provided settings' do + before do + stub_gitlab_rb( + external_url: 'https://trailingslash.example.com/', + grafana: { + http_addr: '0.0.0.0', + http_port: 3333, + enable: true, + gitlab_application_id: 'appid', + gitlab_secret: 'secret', + gitlab_auth_sign_up: false, + allowed_groups: %w[ + allowed + also-allowed + ], + smtp: { + enabled: true, + host: 'smtp_server:255', + user: 'testuser', + password: 'test', + cert_file: '/path/to/cert_file.crt', + key_file: '/path/to/key_file.key', + skip_verify: true, + from_address: 'smtp_admin@grafana.gitlab', + from_name: 'Grafana Gitlab SMTP', + ehlo_identity: 'dashboard.grafama.gitlab', + startTLS_policy: 'NoStartTLS' + }, + env: { + 'USER_SETTING' => 'asdf1234' + }, + dashboards: [ + { + name: 'GitLab Omnibus', + orgId: 1, + folder: 'GitLab Omnibus', + type: 'file', + disableDeletion: true, + updateIntervalSeconds: 600, + options: { + path: '/etc/grafana/dashboards', + }, + }, + ], + } + ) + end + + it 'creates a custom dashboards file' do + expect(chef_run).to render_file('/var/opt/gitlab/grafana/provisioning/dashboards/gitlab_dashboards.yml') + .with_content { |content| + expect(content).to match(/options:\n path: "\/etc\/grafana\/dashboards"\n/) + } + end + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') + .with_content { |content| + expect(content).to match(/http_addr = 0.0.0.0/) + expect(content).to match(/http_port = 3333/) + expect(content).to match(/root_url = https:\/\/trailingslash.example.com\/-\/grafana/) + expect(content).to match(/\[auth\.gitlab\]\nenabled = true\nallow_sign_up = false/) + expect(content).to match(/client_id = appid/) + expect(content).to match(/client_secret = secret/) + expect(content).to match(/auth_url = https:\/\/trailingslash.example.com\/oauth\/authorize/) + expect(content).to match(/token_url = https:\/\/trailingslash.example.com\/oauth\/token/) + expect(content).to match(/api_url = https:\/\/trailingslash.example.com\/api\/v4/) + expect(content).to match(/allowed_groups = allowed also-allowed/) + expect(content).to match(/\[metrics\]\n#.+\nenabled = false/) + expect(content).not_to match(/basic_auth_username/) + expect(content).not_to match(/basic_auth_password/) + expect(content).to match(/scopes = read_api/) + # smtp configuration tests + expect(content).to match(/\[smtp\]\nenabled = true/) + expect(content).to match(/host = smtp_server:255/) + expect(content).to match(/user = testuser/) + expect(content).to match(/password = test/) + expect(content).to match(/cert_file = \/path\/to\/cert_file.crt/) + expect(content).to match(/key_file = \/path\/to\/key_file.key/) + expect(content).to match(/skip_verify = true/) + expect(content).to match(/from_address = smtp_admin@grafana.gitlab/) + expect(content).to match(/from_name = Grafana Gitlab SMTP/) + expect(content).to match(/ehlo_identity = dashboard.grafama.gitlab/) + expect(content).to match(/startTLS_policy = NoStartTLS/) + } + end + + it 'generates grafana metrics basic auth password when enabled' do + stub_gitlab_rb( + grafana: { + metrics_enabled: true + } + ) + + expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') + .with_content { |content| + expect(content).to match(/\[metrics\]\n#.+\nenabled = true/) + expect(content).not_to match(/basic_auth_username/) + expect(content).to match(/basic_auth_password = """\S+"""/) + } + end + + it 'adds metrics basic_auth_username when set' do + stub_gitlab_rb( + grafana: { + metrics_enabled: true, + metrics_basic_auth_username: 'user' + } + ) + + expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') + .with_content { |content| + expect(content).to match(/\[metrics\]\n#.+\nenabled = true/) + expect(content).to match(/basic_auth_username = """user"""/) + expect(content).to match(/basic_auth_password = """\S+"""/) + } + end + + it 'can use special chars in metrics basic auth username and password' do + stub_gitlab_rb( + grafana: { + metrics_enabled: true, + metrics_basic_auth_username: '#user;@', + metrics_basic_auth_password: 'password_#;@' + } + ) + + expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') + .with_content { |content| + expect(content).to match(/\[metrics\]\n#.+\nenabled = true/) + expect(content).to match(/basic_auth_username = """#user;@"""/) + expect(content).to match(/basic_auth_password = """password_#;@"""/) + } + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/grafana/env').with_variables( + default_vars.merge( + { + 'USER_SETTING' => 'asdf1234' + } + ) + ) + end + + it 'does not deduplicate attributes outside its own scope' do + queue_groups = ['geo,post_receive,cronjob', 'geo,post_receive,cronjob', 'geo,post_receive,cronjob'] + + stub_gitlab_rb( + grafana: { + metrics_enabled: true, + }, + sidekiq: { + enable: true, + queue_groups: queue_groups + } + ) + + block = chef_run.ruby_block('populate Grafana configuration options') + block.block.call + + expect(chef_run.node['gitlab']['sidekiq']['queue_groups']).to eq(queue_groups) + end + + it 'disables reporting when usage_ping_enabled is disabled' do + stub_gitlab_rb( + gitlab_rails: { + usage_ping_enabled: false + } + ) + + expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') + .with_content { |content| + expect(content).to match(/reporting_enabled = false/) + } + end + + it 'disables reporting when usage_ping_enabled is enabled but is explicitly disabled' do + stub_gitlab_rb( + gitlab_rails: { + usage_ping_enabled: true + }, + grafana: { + reporting_enabled: false + } + ) + + expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') + .with_content { |content| + expect(content).to match(/reporting_enabled = false/) + } + end + + context 'without allowed_groups specified' do + before do + stub_gitlab_rb( + external_url: 'https://trailingslash.example.com/', + grafana: { + http_addr: '0.0.0.0', + http_port: 3333, + enable: true, + gitlab_application_id: 'appid', + gitlab_secret: 'secret' + } + ) + end + + it 'sets auth scope to read_user' do + expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini').with_content(/scopes = read_user/) + end + end + end +end diff --git a/spec/chef/cookbooks/monitoring/recipes/node-exporter_spec.rb b/spec/chef/cookbooks/monitoring/recipes/node-exporter_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvbm9kZS1leHBvcnRlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/monitoring/recipes/node-exporter_spec.rb @@ -0,0 +1,149 @@ +require 'chef_helper' + +RSpec.describe 'monitoring::node-exporter' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when node-exporter is enabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/node-exporter/log/config') } + + before do + stub_gitlab_rb( + node_exporter: { enable: true } + ) + end + + it_behaves_like 'enabled runit service', 'node-exporter', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/node-exporter/env').with_variables(default_vars) + end + + it 'populates the files with expected configuration' do + expect(config_template).to notify('ruby_block[reload_log_service]') + + expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/run') + .with_content { |content| + expect(content).to match(/exec chpst -P/) + expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/node_exporter/) + expect(content).to match(/--collector\.mountstats /) + expect(content).to match(/--collector\.runit /) + expect(content).to match(/--collector\.runit.servicedir=\/opt\/gitlab\/sv /) + expect(content).to match(/\/textfile_collector/) + } + + expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/node-exporter/) + end + + it 'creates default set of directories' do + expect(chef_run).to create_directory('/var/log/gitlab/node-exporter').with( + owner: 'gitlab-prometheus', + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/var/opt/gitlab/node-exporter/textfile_collector').with( + owner: 'gitlab-prometheus', + group: nil, + mode: '0755' + ) + end + + it 'sets a default listen address' do + expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/run') + .with_content(/web.listen-address=localhost:9100/) + end + end + + context 'when node-exporter is enabled and prometheus is disabled' do + before do + stub_gitlab_rb( + prometheus: { enable: false }, + node_exporter: { enable: true } + ) + end + + it 'should create the gitlab-prometheus account if prometheus is disabled' do + expect(chef_run).to create_account('Prometheus user and group').with_username('gitlab-prometheus') + end + end + + context 'when node-exporter is enabled and prometheus is enabled' do + before do + stub_gitlab_rb( + prometheus: { enable: true }, + node_exporter: { enable: true } + ) + end + + it 'creates a node.rules file' do + expect(chef_run).to render_file('/var/opt/gitlab/prometheus/rules/node.rules') + .with_content(/instance:node_cpus:count/) + end + end + + context 'when log dir is changed' do + before do + stub_gitlab_rb( + node_exporter: { + log_directory: 'foo', + enable: true + } + ) + end + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/log/run') + .with_content(/exec svlogd -tt foo/) + end + end + + context 'with user provided settings' do + before do + stub_gitlab_rb( + node_exporter: { + flags: { + 'collector.textfile.directory' => '/tmp', + 'collector.arp' => false, + 'collector.mountstats' => false + }, + listen_address: 'localhost:9899', + enable: true, + env: { + 'USER_SETTING' => 'asdf1234' + } + } + ) + end + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/run') + .with_content { |content| + expect(content).to match(/web\.listen-address=localhost:9899/) + expect(content).to match(/collector\.textfile\.directory=\/tmp/) + expect(content).to match(/--no-collector\.arp/) + expect(content).to match(/--no-collector\.mountstats/) + expect(content).to match(/--collector\.runit/) + } + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/node-exporter/env').with_variables( + default_vars.merge( + { + 'USER_SETTING' => 'asdf1234' + } + ) + ) + end + end + + include_examples "consul service discovery", "node_exporter", "node-exporter" +end diff --git a/spec/chef/cookbooks/monitoring/recipes/pgbouncer-exporter_spec.rb b/spec/chef/cookbooks/monitoring/recipes/pgbouncer-exporter_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvcGdib3VuY2VyLWV4cG9ydGVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/monitoring/recipes/pgbouncer-exporter_spec.rb @@ -0,0 +1,70 @@ +# +# Copyright:: Copyright (c) 2018 GitLab Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require 'chef_helper' + +RSpec.describe 'monitoring::pgbouncer-exporter' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + let(:config_yaml) { '/var/opt/gitlab/pgbouncer-exporter/pgbouncer-exporter.yaml' } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'when enabled' do + before do + stub_gitlab_rb( + pgbouncer_exporter: { + enable: true + }, + pgbouncer: { + enable: true, + databases: { + gitlabhq_production: { + host: '1.2.3.4' + } + } + }, + postgresql: { + pgbouncer_user: 'fakeuser', + pgbouncer_user_password: 'fakeuserpassword' + } + ) + end + + it 'includes the pgbouncer-exporter recipe' do + expect(chef_run).to include_recipe('monitoring::pgbouncer-exporter') + end + + it 'includes the postgresql user recipe' do + expect(chef_run).to include_recipe('postgresql::user') + end + + it_behaves_like 'enabled runit service', 'pgbouncer-exporter', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/pgbouncer-exporter/env').with_variables(default_vars) + end + + it 'creates the appropriate directories' do + expect(chef_run).to create_directory('/var/log/gitlab/pgbouncer-exporter') + end + end +end diff --git a/spec/chef/cookbooks/monitoring/recipes/postgres_exporter_spec.rb b/spec/chef/cookbooks/monitoring/recipes/postgres_exporter_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvcG9zdGdyZXNfZXhwb3J0ZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/monitoring/recipes/postgres_exporter_spec.rb @@ -0,0 +1,194 @@ +require 'chef_helper' + +RSpec.describe 'monitoring::postgres-exporter' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:node) { chef_run.node } + let(:default_vars) do + { + 'DATA_SOURCE_NAME' => 'host=/var/opt/gitlab/postgresql user=gitlab-psql database=gitlabhq_production', + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when postgres is disabled locally' do + before do + stub_gitlab_rb( + postgresql: { enable: false } + ) + end + + it 'defaults the postgres-exporter to being disabled' do + expect(node['monitoring']['postgres-exporter']['enable']).to eq false + end + + it 'allows postgres-exporter to be explicitly enabled' do + stub_gitlab_rb(postgres_exporter: { enable: true }) + + expect(node['monitoring']['postgres-exporter']['enable']).to eq true + end + + it 'uses gitlab-rails db_host for the database host if postgres-exporter is explicitly enabled' do + stub_gitlab_rb( + postgres_exporter: { enable: true }, + gitlab_rails: { + db_host: '10.0.0.1', + db_port: '4242', + db_username: 'foo', + db_password: 'bar', + db_database: 'baz' + } + ) + + expect(node['monitoring']['postgres-exporter']['env']['DATA_SOURCE_NAME']) + .to eq "host=10.0.0.1 port=4242 user=foo password=bar database=baz" + end + end + + context 'when postgres is enabled locally' do + before do + stub_gitlab_rb( + postgresql: { enable: true } + ) + end + + it 'uses the local postgres dir for the database host' do + stub_gitlab_rb( + postgres_exporter: { enable: true }, + gitlab_rails: { db_host: '10.0.0.1' }, + postgresql: { dir: '/dir/to/postgresql' } + ) + + expect(node['monitoring']['postgres-exporter']['env']['DATA_SOURCE_NAME']) + .to eq "host=/dir/to/postgresql user=gitlab-psql database=gitlabhq_production" + end + end + + context 'when postgres-exporter is enabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/postgres-exporter/log/config') } + + before do + stub_gitlab_rb( + postgres_exporter: { enable: true } + ) + end + + it_behaves_like 'enabled runit service', 'postgres-exporter', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/postgres-exporter/env').with_variables(default_vars) + end + + it 'populates the files with expected configuration' do + expect(config_template).to notify('ruby_block[reload_log_service]') + + expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/run') + .with_content { |content| + expect(content).to match(/exec chpst /) + expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/postgres_exporter/) + } + + expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/postgres-exporter/) + end + + it 'creates the queries.yaml file' do + expect(chef_run).to render_file('/var/opt/gitlab/postgres-exporter/queries.yaml') + .with_content { |content| + expect(content).to match(/pg_replication:/) + expect(content).not_to match(/pg_stat_user_table:/) + } + end + + it 'creates default set of directories' do + expect(chef_run).to create_directory('/var/log/gitlab/postgres-exporter').with( + owner: 'gitlab-psql', + group: nil, + mode: '0700' + ) + end + + it 'sets default flags' do + expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/run') + .with_content { |content| + expect(content).to match(/web.listen-address=localhost:9187/) + expect(content).to match(/extend.query-path=\/var\/opt\/gitlab\/postgres-exporter\/queries.yaml/) + } + end + end + + context 'when enabled and run as an isolated recipe' do + let(:chef_run) { converge_config('monitoring::postgres-exporter') } + before do + stub_gitlab_rb(postgres_exporter: { enable: true }) + end + + it 'includes the postgresql_user recipe' do + expect(chef_run).to include_recipe('postgresql::user') + end + end + + context 'when log dir is changed' do + before do + stub_gitlab_rb( + postgres_exporter: { + log_directory: 'foo', + enable: true + } + ) + end + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/log/run') + .with_content(/exec svlogd -tt foo/) + end + end + + context 'with user provided settings' do + before do + stub_gitlab_rb( + postgres_exporter: { + flags: { + 'some.flag' => 'foo' + }, + listen_address: 'localhost:9700', + enable: true, + per_table_stats: true, + sslmode: 'require', + env: { + 'USER_SETTING' => 'asdf1234' + } + } + ) + end + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/run') + .with_content(/web.listen-address=localhost:9700/) + expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/run') + .with_content(/some.flag=foo/) + end + + it 'creates the queries.yaml file' do + expect(chef_run).to render_file('/var/opt/gitlab/postgres-exporter/queries.yaml') + .with_content(/pg_stat_user_tables:/) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/postgres-exporter/env').with_variables( + default_vars.merge( + { + 'DATA_SOURCE_NAME' => 'host=/var/opt/gitlab/postgresql user=gitlab-psql '\ + 'database=gitlabhq_production sslmode=require', + 'USER_SETTING' => 'asdf1234' + } + ) + ) + end + end + + include_examples "consul service discovery", "postgres_exporter", "postgres-exporter" +end diff --git a/spec/chef/cookbooks/monitoring/recipes/prometheus_spec.rb b/spec/chef/cookbooks/monitoring/recipes/prometheus_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvcHJvbWV0aGV1c19zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/monitoring/recipes/prometheus_spec.rb @@ -0,0 +1,312 @@ +require 'chef_helper' + +prometheus_yml_output = <<-PROMYML + --- + global: + scrape_interval: 15s + scrape_timeout: 15s + external_labels: {} + remote_read: [] + remote_write: [] + rule_files: + - "/var/opt/gitlab/prometheus/rules/*.rules" + scrape_configs: + - job_name: prometheus + static_configs: + - targets: + - localhost:9090 + - job_name: nginx + static_configs: + - targets: + - localhost:8060 + - job_name: redis + static_configs: + - targets: + - localhost:9121 + - job_name: postgres + static_configs: + - targets: + - localhost:9187 + - job_name: node + static_configs: + - targets: + - localhost:9100 + - job_name: gitlab-workhorse + static_configs: + - targets: + - localhost:9229 + - job_name: gitlab-rails + metrics_path: "/-/metrics" + static_configs: + - targets: + - 127.0.0.1:8080 + relabel_configs: + - source_labels: + - __address__ + regex: 127.0.0.1:(.*) + replacement: localhost:$1 + target_label: instance + - job_name: gitlab-sidekiq + static_configs: + - targets: + - 127.0.0.1:8082 + relabel_configs: + - source_labels: + - __address__ + regex: 127.0.0.1:(.*) + replacement: localhost:$1 + target_label: instance + - job_name: registry + static_configs: + - targets: + - localhost:5001 + - job_name: gitlab_exporter_database + metrics_path: "/database" + static_configs: + - targets: + - localhost:9168 + - job_name: gitlab_exporter_sidekiq + metrics_path: "/sidekiq" + static_configs: + - targets: + - localhost:9168 + - job_name: gitlab_exporter_ruby + metrics_path: "/ruby" + static_configs: + - targets: + - localhost:9168 + - job_name: gitaly + static_configs: + - targets: + - localhost:9236 + - job_name: kubernetes-cadvisor + scheme: https + tls_config: + ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + insecure_skip_verify: true + bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" + kubernetes_sd_configs: + - role: node + api_server: https://kubernetes.default.svc:443 + tls_config: + ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: + - __meta_kubernetes_node_name + regex: "(.+)" + target_label: __metrics_path__ + replacement: "/api/v1/nodes/${1}/proxy/metrics/cadvisor" + metric_relabel_configs: + - source_labels: + - pod_name + target_label: environment + regex: "(.+)-.+-.+" + - job_name: kubernetes-nodes + scheme: https + tls_config: + ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + insecure_skip_verify: true + bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" + kubernetes_sd_configs: + - role: node + api_server: https://kubernetes.default.svc:443 + tls_config: + ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: + - __meta_kubernetes_node_name + regex: "(.+)" + target_label: __metrics_path__ + replacement: "/api/v1/nodes/${1}/proxy/metrics" + metric_relabel_configs: + - source_labels: + - pod_name + target_label: environment + regex: "(.+)-.+-.+" + - job_name: kubernetes-pods + tls_config: + ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + insecure_skip_verify: true + bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" + kubernetes_sd_configs: + - role: pod + api_server: https://kubernetes.default.svc:443 + tls_config: + ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" + relabel_configs: + - source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape + action: keep + regex: 'true' + - source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + action: replace + target_label: __metrics_path__ + regex: "(.+)" + - source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + action: replace + regex: "([^:]+)(?::[0-9]+)?;([0-9]+)" + replacement: "$1:$2" + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - source_labels: + - __meta_kubernetes_namespace + action: replace + target_label: kubernetes_namespace + - source_labels: + - __meta_kubernetes_pod_name + action: replace + target_label: kubernetes_pod_name + alerting: + alertmanagers: + - static_configs: + - targets: + - localhost:9093 +PROMYML + +RSpec.describe 'monitoring::prometheus' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when prometheus is enabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/prometheus/log/config') } + + before do + stub_gitlab_rb( + alertmanager: { + enable: true + }, + prometheus: { + enable: true + }, + gitlab_exporter: { + enable: true + }, + registry: { + enable: true, + debug_addr: 'localhost:5001' + } + ) + end + + it_behaves_like 'enabled runit service', 'prometheus', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/prometheus/env').with_variables(default_vars) + end + + it 'populates the files with expected configuration' do + expect(config_template).to notify('ruby_block[reload_log_service]') + + expect(chef_run).to render_file('/opt/gitlab/sv/prometheus/run') + .with_content { |content| + expect(content).to match(/exec chpst -P/) + expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/prometheus/) + expect(content).to match(/prometheus.yml/) + } + + expect(chef_run).to render_file('/var/opt/gitlab/prometheus/prometheus.yml') + .with_content(prometheus_yml_output.gsub(/^ {2}/, '')) + + expect(chef_run).to render_file('/opt/gitlab/sv/prometheus/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/prometheus/) + end + + it 'creates default set of directories' do + expect(chef_run).to create_directory('/var/log/gitlab/prometheus').with( + owner: 'gitlab-prometheus', + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/var/opt/gitlab/prometheus').with( + owner: 'gitlab-prometheus', + group: nil, + mode: '0750' + ) + end + + it 'should create a gitlab-prometheus user and group' do + expect(chef_run).to create_account('Prometheus user and group').with(username: 'gitlab-prometheus', groupname: 'gitlab-prometheus') + end + + it 'sets a default listen address' do + expect(chef_run).to render_file('/opt/gitlab/sv/prometheus/run') + .with_content(/web.listen-address=localhost:9090/) + end + end + + context 'by default' do + context 'with user provided settings' do + it 'configures puma job' do + expect(chef_run).to render_file('/var/opt/gitlab/prometheus/prometheus.yml') + .with_content(%r{- job_name: gitlab-rails\s+metrics_path: "/-/metrics"\s+static_configs:\s+- targets:\s+- 127.0.0.1:8080}) + end + end + end + + context 'rules directory' do + context 'default settings' do + it 'creates rules directory in correct location' do + expect(chef_run).to create_directory("/var/opt/gitlab/prometheus/rules") + expect(chef_run).to render_file("/var/opt/gitlab/prometheus/rules/node.rules") + expect(chef_run).to render_file("/var/opt/gitlab/prometheus/rules/gitlab.rules") + end + end + + context 'user specified home directory' do + before do + stub_gitlab_rb( + prometheus: { + home: "/var/opt/gitlab/prometheus-bak" + } + ) + end + + it 'creates rules directory in correct location' do + expect(chef_run).to create_directory("/var/opt/gitlab/prometheus-bak/rules") + expect(chef_run).to render_file("/var/opt/gitlab/prometheus-bak/rules/node.rules") + expect(chef_run).to render_file("/var/opt/gitlab/prometheus-bak/rules/gitlab.rules") + end + end + + context 'user specified rules directory' do + before do + stub_gitlab_rb( + prometheus: { + rules_directory: "/var/opt/gitlab/prometheus/alert-rules" + } + ) + end + + it 'creates rules directory in correct location' do + expect(chef_run).to create_directory("/var/opt/gitlab/prometheus/alert-rules") + expect(chef_run).to render_file("/var/opt/gitlab/prometheus/alert-rules/node.rules") + expect(chef_run).to render_file("/var/opt/gitlab/prometheus/alert-rules/gitlab.rules") + end + end + end + + include_examples "consul service discovery", "prometheus", "prometheus" +end diff --git a/spec/chef/cookbooks/monitoring/recipes/redis_exporter_spec.rb b/spec/chef/cookbooks/monitoring/recipes/redis_exporter_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvcmVkaXNfZXhwb3J0ZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/monitoring/recipes/redis_exporter_spec.rb @@ -0,0 +1,129 @@ +require 'chef_helper' + +RSpec.describe 'monitoring::redis-exporter' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:node) { chef_run.node } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when redis is disabled locally' do + before do + stub_gitlab_rb( + redis: { enable: false } + ) + end + + it 'defaults the redis-exporter to being disabled' do + expect(node['monitoring']['redis-exporter']['enable']).to eq false + end + + it 'allows redis-exporter to be explicitly enabled' do + stub_gitlab_rb(redis_exporter: { enable: true }) + + expect(node['monitoring']['redis-exporter']['enable']).to eq true + end + end + + context 'when redis-exporter is enabled' do + let(:config_template) { chef_run.template('/opt/gitlab/sv/redis-exporter/log/config') } + + before do + stub_gitlab_rb( + redis_exporter: { enable: true } + ) + end + + it_behaves_like 'enabled runit service', 'redis-exporter', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/redis-exporter/env').with_variables(default_vars) + end + + it 'populates the files with expected configuration' do + expect(config_template).to notify('ruby_block[reload_log_service]') + + expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') + .with_content { |content| + expect(content).to match(/exec chpst -P/) + expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/redis_exporter/) + } + + expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/redis-exporter/) + end + + it 'creates default set of directories' do + expect(chef_run).to create_directory('/var/log/gitlab/redis-exporter').with( + owner: 'gitlab-redis', + group: nil, + mode: '0700' + ) + end + + it 'sets default flags' do + expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') + .with_content(/web.listen-address=localhost:9121/) + expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') + .with_content(%r{redis.addr=unix:///var/opt/gitlab/redis/redis.socket}) + end + end + + context 'when log dir is changed' do + before do + stub_gitlab_rb( + redis_exporter: { + log_directory: 'foo', + enable: true + } + ) + end + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/log/run') + .with_content(/exec svlogd -tt foo/) + end + end + + context 'with user provided settings' do + before do + stub_gitlab_rb( + redis_exporter: { + flags: { + 'redis.addr' => '/tmp/socket' + }, + listen_address: 'localhost:9900', + enable: true, + env: { + 'USER_SETTING' => 'asdf1234' + } + } + ) + end + + it 'populates the files with expected configuration' do + expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') + .with_content(/web.listen-address=localhost:9900/) + expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') + .with_content(%r{redis.addr=/tmp/socket}) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/redis-exporter/env').with_variables( + default_vars.merge( + { + 'USER_SETTING' => 'asdf1234' + } + ) + ) + end + end + + include_examples "consul service discovery", "redis_exporter", "redis-exporter" +end diff --git a/spec/chef/cookbooks/package/libraries/config/gitlab_spec.rb b/spec/chef/cookbooks/package/libraries/config/gitlab_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9jb25maWcvZ2l0bGFiX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/config/gitlab_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +RSpec.describe Gitlab do + context 'when using an attribute_block' do + it 'sets top level attributes to the provided root' do + Gitlab.attribute_block('gitlab') do + expect(Gitlab.attribute('test_attribute')[:parent]).to eq 'gitlab' + end + expect(Gitlab['test_attribute']).not_to be_nil + expect(Gitlab.hyphenate_config_keys['gitlab']).to include('test-attribute') + end + end + + it 'sets top level attributes when no parent is provided' do + Gitlab.attribute('test_attribute') + expect(Gitlab['test_attribute']).not_to be_nil + expect(Gitlab.hyphenate_config_keys).to include('test-attribute') + end + + it 'properly defines roles' do + role = Gitlab.role('test_node') + expect(Gitlab['test_node_role']).not_to be_nil + expect(Gitlab.hyphenate_config_keys['roles']).to include('test-node') + expect(role).to include(manage_services: true) + end + + it 'supports overriding role default configuration' do + role = Gitlab.role('test_node', manage_services: false) + expect(Gitlab['test_node_role']).not_to be_nil + expect(role).to include(manage_services: false) + end + + it 'supports overriding attribute default configuration' do + attribute = Gitlab.attribute('test_attribute', parent: 'example', priority: 40, enable: false, default: '') + expect(Gitlab['test_attribute']).to eq('') + expect(attribute).to include(parent: 'example', priority: 40, enable: false) + end + + it 'disables ee attributes when EE is not enabled' do + allow(Gitlab).to receive(:[]).and_call_original + allow(Gitlab).to receive(:[]).with('edition').and_return(:ce) + expect(Gitlab.ee_attribute('test_attribute')[:ee]).to eq true + expect(Gitlab['test_attribute']).not_to be_nil + expect(Gitlab.hyphenate_config_keys).not_to include('test-attribute') + end + + it 'enables ee attributes when EE is enabled' do + allow(Gitlab).to receive(:[]).and_call_original + allow(Gitlab).to receive(:[]).with('edition').and_return(:ee) + expect(Gitlab.ee_attribute('test_attribute')[:ee]).to eq true + expect(Gitlab['test_attribute']).not_to be_nil + expect(Gitlab.hyphenate_config_keys).to include('test-attribute') + end + + it 'sorts attributes by sequence' do + Gitlab.attribute('last', priority: 99) + Gitlab.attribute('other1') + Gitlab.attribute('first', priority: -99) + Gitlab.attribute('other2') + + expect(Gitlab.send(:sorted_settings).first[0]).to eq 'first' + expect(Gitlab.send(:sorted_settings).last[0]).to eq 'last' + end + + it 'filters ee settings when sorting' do + Gitlab.attribute('test_attribute1') + Gitlab.attribute('test_attribute2', ee: true) + allow(Gitlab).to receive(:[]).and_call_original + allow(Gitlab).to receive(:[]).with('edition').and_return(:ce) + expect(Gitlab.send(:sorted_settings).map(&:first)).to include('test_attribute1') + expect(Gitlab.send(:sorted_settings).map(&:first)).not_to include('test_attribute2') + end + + it 'allows passing a block to the attribute use method' do + attribute = Gitlab.attribute('test_attribute').use { 'test' } + expect(attribute.handler).to eq('test') + end +end diff --git a/spec/chef/cookbooks/package/libraries/config/roles_spec.rb b/spec/chef/cookbooks/package/libraries/config/roles_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9jb25maWcvcm9sZXNfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/config/roles_spec.rb @@ -0,0 +1,116 @@ +require 'chef_helper' + +RSpec.describe 'GitLabRoles' do + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(Services).to receive(:enable_group).and_call_original + end + + after do + # Disable all roles after each test to clean the environment + Gitlab.available_roles.each { |name, _value| Gitlab["#{name}_role"]['enable'] = false } + end + + describe 'roles config array' do + it 'enables roles listed in the roles array' do + stub_gitlab_rb(roles: %w(application_role geo_primary_role)) + + Gitlab.load_roles + + expect(Gitlab['application_role']['enable']).to be true + expect(Gitlab['geo_primary_role']['enable']).to be true + end + + it 'supports providing a single role as a string' do + stub_gitlab_rb(roles: 'application_role') + + Gitlab.load_roles + + expect(Gitlab['application_role']['enable']).to be true + end + + it 'handles users specifying hyphens instead of underscores' do + stub_gitlab_rb(roles: ['geo-primary-role']) + + Gitlab.load_roles + + expect(Gitlab['geo_primary_role']['enable']).to be true + end + + it 'throws errors when an invalid role is used' do + stub_gitlab_rb(roles: ['some_invalid_role']) + + expect { Gitlab.load_roles }.to raise_error(RuntimeError, /invalid roles have been set/) + end + end + + describe 'DefaultRole' do + before do + allow(DefaultRole).to receive(:load_role).and_call_original + allow(GeoPrimaryRole).to receive(:load_role).and_call_original + end + + it 'enables the default services when no other roles are active' do + Gitlab.load_roles + + expect(Services).to have_received(:enable_group).with(Services::DEFAULT_GROUP, anything).once + end + + it 'enables the default services when no "service managed" roles are active' do + stub_gitlab_rb(geo_primary_role: { enable: true }) + + Gitlab.load_roles + + expect(Services).to have_received(:enable_group).with(Services::DEFAULT_GROUP, anything).once + expect(GeoPrimaryRole).to have_received(:load_role) + end + + it 'leaves the default services disabled when another role is active' do + stub_gitlab_rb(application_role: { enable: true }) + + Gitlab.load_roles + + expect(DefaultRole).to have_received(:load_role) + expect(Services).not_to have_received(:enable_group).with(Services::DEFAULT_GROUP, anything) + end + + it 'leaves rails roles disabled when requested to maintain backward compat' do + stub_gitlab_rb(gitlab_rails: { enable: false }) + + Gitlab.load_roles + + expect(Services).to have_received(:enable_group).with(Services::DEFAULT_GROUP, hash_including(except: ['rails'])).once + end + end + + describe 'ApplicationRole' do + before do + allow(ApplicationRole).to receive(:load_role).and_call_original + end + + it 'enables the rails services' do + stub_gitlab_rb(application_role: { enable: true }) + + Gitlab.load_roles + + expect(ApplicationRole).to have_received(:load_role) + expect(Gitlab['gitlab_rails']['enable']).to eq true + expect(Services).to have_received(:enable_group).with('rails').once + end + end + + describe 'MonitoringRole' do + before do + allow(MonitoringRole).to receive(:load_role).and_call_original + end + + it 'enables the monitoring services' do + stub_gitlab_rb(roles: %w(monitoring_role)) + + Gitlab.load_roles + + expect(MonitoringRole).to have_received(:load_role) + expect(Services).to have_received(:enable_group).with('monitoring_role').once + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/config/services_spec.rb b/spec/chef/cookbooks/package/libraries/config/services_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9jb25maWcvc2VydmljZXNfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/config/services_spec.rb @@ -0,0 +1,316 @@ +require 'chef_helper' + +RSpec.describe Services do + before { allow(Gitlab).to receive(:[]).and_call_original } + + describe 'when using the gitlab cookbook' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + it 'returns the gitlab service list' do + chef_run + expect(Services.service_list).to have_key('puma') + expect(Services.service_list).not_to have_key('sentinel') + end + end + + describe 'when using the gitlab-ee cookbook' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + it 'returns the gitlab service list including gitlab-ee items' do + chef_run + expect(Services.service_list).to have_key('puma') + expect(Services.service_list).to have_key('sentinel') + end + end + + it 'uses the default template when populating service information' do + expect(Services::Config.send(:service, ['test_service'])).to eq({ groups: [] }) + end + + describe 'service' do + let(:runner) { ChefSpec::SoloRunner.new } + let(:chef_run) { runner.converge('gitlab::config') } + let(:node) { runner.node } + + before { Services.add_services('gitlab', Services::BaseServices.list) } + + context 'when using user values that conflict with service settings' do + let!(:runner) { ChefSpec::SoloRunner.new { |node| Gitlab[:node] = node } } + + it 'node service settings are overridden by gitlab.rb changes' do + stub_gitlab_rb(redis: { enable: true }, mattermost: { enable: false }) + Services.disable('redis') + Services.enable('mattermost') + runner.converge('gitlab::config') + + expect(node['redis']['enable']).to be true + expect(node['mattermost']['enable']).to be false + end + end + + context 'when enable/disable is passed a single service' do + it 'sets the correct values' do + chef_run + Services.disable('redis') + expect(node['redis']['enable']).to be false + + Services.enable('mattermost') + expect(node['mattermost']['enable']).to be true + end + + it 'supports exceptions' do + chef_run + Services.disable('mattermost') + Services.enable('mattermost', except: 'mattermost') + expect(node['mattermost']['enable']).to be false + + Services.enable('redis') + Services.disable('redis', except: 'redis') + expect(node['redis']['enable']).to be true + end + end + + context 'when enable/disable is passed multiple services' do + before { chef_run } + + it 'sets the correct values' do + Services.disable('redis', 'postgresql', 'gitaly') + expect(node['redis']['enable']).to be false + expect(node['postgresql']['enable']).to be false + expect(node['gitaly']['enable']).to be false + + Services.enable('mattermost', 'registry', 'mailroom') + expect(node['mattermost']['enable']).to be true + expect(node['registry']['enable']).to be true + expect(node['gitlab']['mailroom']['enable']).to be true + end + + it 'supports single exceptions' do + Services.disable('registry') + Services.enable('mattermost', 'registry', 'mailroom', except: 'registry') + expect(node['mattermost']['enable']).to be true + expect(node['registry']['enable']).to be false + expect(node['gitlab']['mailroom']['enable']).to be true + + Services.enable('postgresql') + Services.disable('redis', 'postgresql', 'gitaly', except: 'postgresql') + expect(node['redis']['enable']).to be false + expect(node['postgresql']['enable']).to be true + expect(node['gitaly']['enable']).to be false + end + + it 'supports multiple exceptions' do + Services.disable('registry', 'mailroom') + Services.enable('mattermost', 'registry', 'mailroom', except: %w(registry mailroom)) + expect(node['mattermost']['enable']).to be true + expect(node['registry']['enable']).to be false + expect(node['gitlab']['mailroom']['enable']).to be false + + Services.enable('postgresql', 'gitaly') + Services.disable('redis', 'postgresql', 'gitaly', except: %w(postgresql gitaly)) + expect(node['redis']['enable']).to be false + expect(node['postgresql']['enable']).to be true + expect(node['gitaly']['enable']).to be true + end + + it 'ignores disable on system services' do + Services.disable('node_exporter') + expect(node['monitoring']['node-exporter']['enable']).to be true + end + + it 'allows forced disable on system services' do + Services.disable('node_exporter', include_system: true) + expect(node['monitoring']['node-exporter']['enable']).to be false + end + end + + context 'when passed single exception' do + before { chef_run } + + it 'enables all others' do + Services.disable('registry') + Services.enable(Services::ALL_SERVICES, except: 'registry') + expect(node['redis']['enable']).to be true + expect(node['postgresql']['enable']).to be true + expect(node['mattermost']['enable']).to be true + expect(node['registry']['enable']).to be false + end + + it 'disables all others' do + Services.enable('redis') + Services.disable(Services::ALL_SERVICES, except: 'redis') + expect(node['redis']['enable']).to be true + expect(node['postgresql']['enable']).to be false + expect(node['mattermost']['enable']).to be false + expect(node['registry']['enable']).to be false + end + end + + context 'when passed multiple exceptions' do + before { chef_run } + + it 'enables all others' do + Services.disable('registry', 'mailroom') + Services.enable(Services::ALL_SERVICES, except: %w(registry mailroom)) + expect(node['redis']['enable']).to be true + expect(node['postgresql']['enable']).to be true + expect(node['gitaly']['enable']).to be true + expect(node['mattermost']['enable']).to be true + expect(node['gitlab']['mailroom']['enable']).to be false + expect(node['registry']['enable']).to be false + end + + it 'disables all others' do + Services.enable('postgresql', 'gitaly') + Services.disable(Services::ALL_SERVICES, except: %w(postgresql gitaly)) + expect(node['redis']['enable']).to be false + expect(node['postgresql']['enable']).to be true + expect(node['gitaly']['enable']).to be true + expect(node['mattermost']['enable']).to be false + expect(node['gitlab']['mailroom']['enable']).to be false + expect(node['registry']['enable']).to be false + end + end + end + + describe 'group' do + let(:runner) { ChefSpec::SoloRunner.new } + let(:chef_run) { runner.converge('gitlab::config') } + let(:node) { runner.node } + + before { Services.add_services('gitlab', Services::BaseServices.list) } + + context 'when using user values that conflict with service settings' do + cached!(:runner) { ChefSpec::SoloRunner.new { |node| Gitlab[:node] = node } } + + it 'node service settings are overridden by gitlab.rb changes' do + stub_gitlab_rb(redis: { enable: true }, postgresql: { enable: false }) + Services.disable_group('redis') + Services.enable_group('postgres') + runner.converge('gitlab::config') + + expect(node['redis']['enable']).to be true + expect(node['mattermost']['enable']).to be false + end + end + + context 'when enable_group/disable_group is passed a single group' do + before { chef_run } + + it 'sets the correct values' do + Services.disable_group('redis') + expect(node['redis']['enable']).to be false + expect(node['monitoring']['redis-exporter']['enable']).to be false + + Services.enable_group('rails') + expect(node['gitlab']['puma']['enable']).to be true + expect(node['monitoring']['gitlab-exporter']['enable']).to be true + end + + it 'supports exceptions' do + Services.disable_group('monitoring') + Services.enable_group('rails', except: 'monitoring') + expect(node['monitoring']['gitlab-exporter']['enable']).to be false + expect(node['gitlab']['puma']['enable']).to be true + + Services.enable_group('monitoring') + Services.disable_group('redis', except: 'monitoring') + expect(node['redis']['enable']).to be false + expect(node['monitoring']['redis-exporter']['enable']).to be true + end + end + + context 'when enable/disable is passed multiple groups' do + before { chef_run } + it 'sets the correct values' do + Services.disable_group('redis', 'postgres') + expect(node['redis']['enable']).to be false + expect(node['postgresql']['enable']).to be false + + Services.enable_group('rails', 'monitoring') + expect(node['monitoring']['redis-exporter']['enable']).to be true + expect(node['gitlab']['puma']['enable']).to be true + end + + it 'supports single exceptions' do + Services.disable_group('monitoring') + Services.enable_group('redis', 'rails', except: 'monitoring') + expect(node['redis']['enable']).to be true + expect(node['gitlab']['puma']['enable']).to be true + expect(node['monitoring']['gitlab-exporter']['enable']).to be false + expect(node['monitoring']['redis-exporter']['enable']).to be false + + Services.enable_group('postgres') + Services.disable_group('redis', 'monitoring', except: 'postgres') + expect(node['redis']['enable']).to be false + expect(node['monitoring']['postgres-exporter']['enable']).to be true + expect(node['monitoring']['prometheus']['enable']).to be false + end + + it 'supports multiple exceptions' do + Services.disable_group('redis', Services::SYSTEM_GROUP, include_system: true) + Services.enable_group('rails', 'monitoring', except: ['redis', Services::SYSTEM_GROUP]) + expect(node['monitoring']['redis-exporter']['enable']).to be false + expect(node['monitoring']['node-exporter']['enable']).to be false + expect(node['gitlab']['puma']['enable']).to be true + + Services.enable_group('sidekiq', 'monitoring') + Services.disable_group('rails', 'postgres', except: %w(sidekiq monitoring)) + expect(node['gitlab']['gitlab-workhorse']['enable']).to be false + expect(node['gitlab']['sidekiq']['enable']).to be true + expect(node['postgresql']['enable']).to be false + expect(node['monitoring']['postgres-exporter']['enable']).to be true + end + + it 'ignores disable on system services' do + Services.enable_group(Services::SYSTEM_GROUP) + Services.disable_group(Services::SYSTEM_GROUP) + expect(node['logrotate']['enable']).to be true + end + + it 'allows forced disable on system services' do + Services.enable_group(Services::SYSTEM_GROUP) + Services.disable_group(Services::SYSTEM_GROUP, include_system: true) + expect(node['logrotate']['enable']).to be false + end + end + + context 'when passed single exception' do + before { chef_run } + + it 'enables all others' do + Services.disable_group('monitoring') + Services.enable_group(Services::ALL_GROUPS, except: 'monitoring') + expect(node['gitlab']['puma']['enable']).to be true + expect(node['monitoring']['gitlab-exporter']['enable']).to be false + end + + it 'disables all others' do + Services.enable_group('monitoring') + Services.disable_group(Services::ALL_GROUPS, except: 'monitoring') + expect(node['monitoring']['postgres-exporter']['enable']).to be true + expect(node['postgresql']['enable']).to be false + end + end + + context 'when passed multiple exceptions' do + before { chef_run } + + it 'enables all others' do + Services.disable_group('redis', 'rails') + Services.enable_group(Services::ALL_GROUPS, except: %w(redis rails)) + expect(node['gitlab']['puma']['enable']).to be false + expect(node['monitoring']['node-exporter']['enable']).to be true + expect(node['monitoring']['redis-exporter']['enable']).to be false + end + + it 'disables all others' do + Services.enable_group('redis', 'rails') + Services.disable_group(Services::ALL_GROUPS, except: %w(redis rails)) + expect(node['monitoring']['prometheus']['enable']).to be false + expect(node['redis']['enable']).to be true + expect(node['gitlab']['sidekiq']['enable']).to be true + end + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/config_mash_spec.rb b/spec/chef/cookbooks/package/libraries/config_mash_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9jb25maWdfbWFzaF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/config_mash_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +RSpec.describe Gitlab::ConfigMash do + subject(:mash) { described_class.new } + + describe '.auto_vivify?' do + subject { described_class } + + it 'returns true when inside auto_vivify block' do + subject.auto_vivify do + expect(subject.auto_vivify?).to be_truthy + end + end + + it 'returns false when not in auto_vivify block' do + expect(subject.auto_vivify?).to be_falsey + end + end + + describe '#[]' do + it 'behaves like a normal Mash by default' do + mash['param1'] = 'value1' + expect(mash['param1']).to eq 'value1' + expect(mash['param2']).to be_nil + expect { mash['param2']['nested'] }.to raise_error(NoMethodError, /nil:NilClass/) + end + + it 'allows nested undefined reads when in an auto-vivify block' do + mash['param1'] = 'value1' + expect(mash['param1']).to eq 'value1' + expect(mash['param2']).to be_nil + + Gitlab::ConfigMash.auto_vivify do + expect(mash['param2']).not_to be_nil + expect { mash['param2']['nested'] }.not_to raise_error + mash['param3']['enable'] = true + expect(mash['param3']['enable']).to eq true + end + + # confirm it is turned off after the block + expect(mash['param4']).to be_nil + end + end + + describe 'convert_value' do + it 'returns passed value when its a ConfigMash' do + another_mash = Gitlab::ConfigMash.new + + converted = mash.convert_value(another_mash) + + expect(converted).to be_a(Gitlab::ConfigMash) + expect(converted).to eq(another_mash) + end + + it 'returns a ConfigMash with the same data when passed a Hash' do + hash = { 'key' => 'value' } + + converted = mash.convert_value(hash) + + expect(converted).to be_a(Gitlab::ConfigMash) + expect(converted.keys).to match_array('key') + expect(converted.values).to match_array('value') + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/deprecations_spec.rb b/spec/chef/cookbooks/package/libraries/deprecations_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9kZXByZWNhdGlvbnNfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/deprecations_spec.rb @@ -0,0 +1,221 @@ +require 'spec_helper' + +RSpec.describe Gitlab::Deprecations do + let(:valid_config) { { gitlab: { nginx: { listen_addresses: "SomeRandomString" } } } } + let(:invalid_config) do + { + "gitlab" => { + "nginx" => { + "listen_address" => "SomeRandomString" + }, + "gitlab-rails" => { + "stuck_ci_builds_worker_cron" => "5 * * * *" + } + }, + "mattermost" => { + "system_read_timeout" => 50, + "log_file_directory" => "/my/random/path" + }, + "monitoring" => { + "gitlab-monitor" => { + "enable" => false + } + } + } + end + + let(:conf1) do + { + config_keys: %w(gitlab nginx listen_address), + deprecation: '8.10', + removal: '11.0', + note: "Use nginx['listen_addresses'] instead." + } + end + + let(:conf2) do + { + config_keys: %w(gitlab gitlab-rails stuck_ci_builds_worker_cron), + deprecation: '9.0', + removal: '12.0', + note: "Use gitlab_rails['stuck_ci_jobs_worker_cron'] instead." + } + end + + let(:conf3) do + { + config_keys: %w(gitlab gitlab-shell git_data_directories), + deprecation: '8.10', + removal: '11.0', + note: "Use git_data_dirs instead." + } + end + + let(:conf4) do + { + config_keys: %w(monitoring gitlab-monitor enable), + deprecation: '12.0', + removal: '13.0', + note: "Use gitlab_exporter['enable'] instead." + } + end + + let(:deprecation_list) do + [ + conf1, + conf2, + conf3, + conf4, + ] + end + + before(:each) do + allow(Gitlab::Deprecations).to receive(:list).and_return(deprecation_list) + end + + describe '.applicable_deprecations' do + it 'detects valid configuration' do + expect(described_class.applicable_deprecations("11.0", valid_config, :deprecation)).to eq([]) + end + + it 'distinguishes from deprecated and removed configuration' do + expect(described_class.applicable_deprecations("11.0", invalid_config, :deprecation)).to include(conf1) + expect(described_class.applicable_deprecations("11.0", invalid_config, :deprecation)).to include(conf2) + expect(described_class.applicable_deprecations("12.0", invalid_config, :deprecation)).to include(conf1) + expect(described_class.applicable_deprecations("12.0", invalid_config, :deprecation)).to include(conf2) + + expect(described_class.applicable_deprecations("11.0", invalid_config, :removal)).not_to include(conf2) + expect(described_class.applicable_deprecations("12.0", invalid_config, :removal)).to include(conf2) + end + + it 'also detects deprecated falsey values' do + expect(described_class.applicable_deprecations("12.0", invalid_config, :deprecation)).to include(conf4) + end + end + + describe '.check_config' do + it 'detects valid_config configuration' do + expect(described_class.check_config("11.0", valid_config)).to eq([]) + end + + it 'detects deprecated configuration for specified version and ignores not yet deprecated ones' do + message_1 = "* nginx['listen_address'] has been deprecated since 8.10 and was removed in 11.0. Use nginx['listen_addresses'] instead." + message_2 = "* gitlab_rails['stuck_ci_builds_worker_cron'] has been deprecated since 9.0 and was removed in 12.0. Use gitlab_rails['stuck_ci_jobs_worker_cron'] instead." + message_3 = "* gitlab_monitor['enable'] has been deprecated since 12.0 and will be removed in 13.0. Use gitlab_exporter['enable'] instead." + + expect(described_class.check_config("11.0", invalid_config)).to include(message_1) + expect(described_class.check_config("11.0", invalid_config)).not_to include(message_2) + expect(described_class.check_config("12.0", invalid_config)).to include(message_2) + expect(described_class.check_config("12.0", invalid_config, :deprecation)).to include(message_3) + end + end + + describe '.identify_deprecated_config' do + it 'detects deprecations correctly from list of supported keys' do + mattermost_supported_keys = %w(log_file_directory) + output = [ + { + config_keys: %w(mattermost system_read_timeout), + deprecation: '10.2', + removal: '11.0', + note: nil + } + ] + expect(described_class.identify_deprecated_config(invalid_config, ["mattermost"], mattermost_supported_keys, "10.2", "11.0")).to eq(output) + end + end + + describe '.deprecate_only_if_value' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'during removals' do + context 'when deprecated config is not set' do + it 'does not raise warning' do + expect(described_class.deprecate_only_if_value('14.0', chef_run.node.normal, :removal, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq([]) + end + end + + context 'when deprecated config is set to an acceptable value' do + before do + stub_gitlab_rb( + puma: { enable: false } + ) + end + it 'does not raise warning' do + expect(described_class.deprecate_only_if_value('14.0', chef_run.node.normal, :removal, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq([]) + end + end + + context 'when deprecated config is set to an unacceptable value' do + before do + stub_gitlab_rb( + puma: { enable: true } + ) + end + it 'raises warning' do + expect(described_class.deprecate_only_if_value('14.0', chef_run.node.normal, :removal, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq(["* puma[enable] has been deprecated since 13.10 and was removed in 14.0."]) + end + end + end + + context 'during deprecations' do + context 'when deprecated config is not set' do + it 'does not raise warning' do + expect(described_class.deprecate_only_if_value('13.12', chef_run.node.normal, :deprecation, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq([]) + end + end + + context 'when deprecated config is set to an acceptable value' do + before do + stub_gitlab_rb( + puma: { enable: false } + ) + end + it 'raises warning' do + expect(described_class.deprecate_only_if_value('13.12', chef_run.node.normal, :deprecation, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq(["* puma[enable] has been deprecated since 13.10 and will be removed in 14.0."]) + end + end + + context 'when deprecated config is set to an unacceptable value' do + before do + stub_gitlab_rb( + puma: { enable: true } + ) + end + it 'raises warning' do + expect(described_class.deprecate_only_if_value('13.12', chef_run.node.normal, :deprecation, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq(["* puma[enable] has been deprecated since 13.10 and will be removed in 14.0."]) + end + end + end + end + + describe 'NodeAttribute' do + before do + Gitlab::Deprecations::NodeAttribute.log_deprecations = true + end + + after do + Gitlab::Deprecations::NodeAttribute.log_deprecations = false + end + + it 'Logs deprecations for passed variables and proxies to new object' do + config = { 'monitoring' => { 'test' => 'test-value' } } + config['prometheus'] = Gitlab::Deprecations::NodeAttribute.new(config['monitoring'], "config['prometheus']", "config['monitoring']") + + expect(config['prometheus']['test']).to eq('test-value') + expect_logged_deprecation(/Accessing config\['prometheus'\] is deprecated/) + end + + it 'Logs deprecations for passed variables and proxies to new Proc if provided' do + config = { 'monitoring' => { 'test' => 'test-value' } } + config['prometheus'] = Gitlab::Deprecations::NodeAttribute.new(proc { config['monitoring'] }, "config['prometheus']", "config['monitoring']") + + expect(config['prometheus']['test']).to eq('test-value') + expect_logged_deprecation(/Accessing config\['prometheus'\] is deprecated/) + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/gitlab_cluster_spec.rb b/spec/chef/cookbooks/package/libraries/gitlab_cluster_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9naXRsYWJfY2x1c3Rlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/gitlab_cluster_spec.rb @@ -0,0 +1,163 @@ +require 'spec_helper' + +RSpec.describe GitlabCluster, :cluster_config do + let(:gitlab_cluster_config_path) { described_class::JSON_FILE } + subject { described_class.config } + + describe '#all' do + context 'when the cluster configuration file does not exist' do + it 'returns an empty hash' do + expect(subject.all).to be_empty + end + end + + context 'when the cluster configuration file exists' do + it 'parses the file content' do + stub_file_content(gitlab_cluster_config_path, foo: 'bar') + + expect(subject.all).to eq('foo' => 'bar') + end + end + end + + describe '#set' do + it 'set the value of a single config option' do + subject.set('primary', true) + + expect(subject.all).to eq('primary' => true) + end + + it 'sets the value of a nested config option' do + subject.set('patroni', 'standby_cluster', 'enable', true) + + expect(subject.all).to eq('patroni' => { 'standby_cluster' => { 'enable' => true } }) + end + + it 'overrides the key value if it already set' do + stub_file_content(gitlab_cluster_config_path, patroni: { standby_cluster: { enable: false } }) + + subject.set('patroni', 'standby_cluster', 'enable', true) + + expect(subject.all).to eq('patroni' => { 'standby_cluster' => { 'enable' => true } }) + end + end + + describe '#get' do + before do + stub_file_content(gitlab_cluster_config_path, primary: false, patroni: { standby_cluster: { enable: true } }) + end + + it 'get the value of a single config option if the key exists' do + expect(subject.get('primary')).to eq(false) + end + + it 'get the value of a nested config option if the key exists' do + expect(subject.get('patroni', 'standby_cluster', 'enable')).to eq(true) + end + + it 'returns nil if the key does not exist' do + expect(subject.get('foo', 'bar')).to be_nil + end + end + + describe '#load_roles!' do + before do + stub_gitlab_rb(application_role: { enable: true }, geo_primary_role: { enable: nil }, geo_secondary_role: { enable: true }) + end + + it 'overrides roles defined in the configuration file' do + stub_file_content(gitlab_cluster_config_path, secondary: false) + + subject.load_roles! + + expect(Gitlab['application_role']['enable']).to eq(true) + expect(Gitlab['geo_secondary_role']['enable']).to eq(false) + end + + it 'does not override roles not defined in the configuration file' do + stub_file_content(gitlab_cluster_config_path, {}) + + subject.load_roles! + + expect(Gitlab['application_role']['enable']).to eq(true) + expect(Gitlab['geo_secondary_role']['enable']).to eq(true) + end + + it 'prints a warning message for each enabled role defined in the configuration file' do + stub_file_content(gitlab_cluster_config_path, primary: true, secondary: false) + + expect(LoggingHelper) + .to receive(:warning) + .with("The 'geo_primary_role' is defined in #{gitlab_cluster_config_path} as 'true' and overrides the setting in the /etc/gitlab/gitlab.rb") + + expect(LoggingHelper) + .to receive(:warning) + .with("The 'geo_secondary_role' is defined in #{gitlab_cluster_config_path} as 'false' and overrides the setting in the /etc/gitlab/gitlab.rb") + .once + + subject.load_roles! + end + end + + describe '#write_to_file!' do + let(:config_path) { Dir.mktmpdir } + let(:gitlab_cluster_config_path) { File.join(config_path, 'gitlab-cluster.json') } + + before do + stub_const('GitlabCluster::CONFIG_PATH', config_path) + stub_const('GitlabCluster::JSON_FILE', gitlab_cluster_config_path) + end + + after do + FileUtils.rm_rf(config_path) + end + + context 'when the config directory does not exist' do + it 'does not create the configuration file' do + FileUtils.rm_rf(config_path) + + subject.save + + expect(File.exist?(gitlab_cluster_config_path)).to eq(false) + end + end + + context 'when the cluster configuration file does not exist' do + it 'creates the configuration file' do + FileUtils.rm_rf(gitlab_cluster_config_path) + + subject.save + + expect(File.exist?(gitlab_cluster_config_path)).to eq(true) + expect(read_file_content(gitlab_cluster_config_path)).to be_empty + end + end + + context 'when the cluster configuration file exists' do + it 'overrides previous settings' do + write_file_content(gitlab_cluster_config_path, foo: 'bar', zoo: true) + subject.set('zoo', false) + + subject.save + + expect(read_file_content(gitlab_cluster_config_path)).to eq("foo" => "bar", "zoo" => false) + end + end + end + + def stub_file_content(fullpath, content) + allow(File).to receive(:exist?).with(fullpath).and_return(true) + allow(IO).to receive(:read).with(fullpath).and_return(content.to_json) + end + + def read_file_content(fullpath) + JSON.parse(File.read(fullpath)) + end + + def write_file_content(fullpath, content) + File.open(fullpath, 'w') do |f| + f.write(content.to_json) + f.chmod(0600) + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/helpers/logging_helper_spec.rb b/spec/chef/cookbooks/package/libraries/helpers/logging_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL2xvZ2dpbmdfaGVscGVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/helpers/logging_helper_spec.rb @@ -0,0 +1,172 @@ +require 'chef_helper' + +RSpec.describe LoggingHelper do + let(:log) { spy('Chef::Log') } + + before do + subject.reset + stub_const('Chef::Log', log) + end + + context '.messages' do + it 'starts with no messages' do + expect(subject.messages).to eq([]) + end + end + + context '.log' do + it 'records into .messages' do + subject.log('one') + expect(subject.messages).to contain_exactly(Hash) + end + + it 'records message' do + subject.log('two') + expect(subject.messages).to contain_exactly(hash_including(message: 'two')) + end + + it 'records two messages' do + subject.log('one') + subject.log('two') + expect(subject.messages).to contain_exactly( + hash_including(message: 'one'), + hash_including(message: 'two') + ) + end + + it 'optionally records kind' do + subject.log('three', kind: :fake) + expect(subject.messages).to contain_exactly(hash_including(kind: :fake)) + end + end + + context '.deprecation' do + it 'calls Chef::Log.warn' do + subject.deprecation('hello') + expect(log).to have_received(:warn).with('hello') + end + + it 'adds the kind :deprecation' do + subject.deprecation('basic') + expect(subject.messages).to contain_exactly(hash_including(kind: :deprecation)) + end + end + + context '.warning' do + it 'calls Chef::Log.warn' do + subject.warning('hello') + expect(log).to have_received(:warn).with('hello') + end + + it 'adds the kind :warning' do + subject.warning('basic') + expect(subject.messages).to contain_exactly(hash_including(kind: :warning)) + end + end + + context '.debug' do + it 'calls Chef::Log.debug' do + subject.debug('hello') + expect(log).to have_received(:debug).with('hello') + end + + it 'adds the kind :debug' do + subject.debug('basic') + expect(subject.messages).to contain_exactly(hash_including(kind: :debug)) + end + end + + context '.note' do + it 'adds the kind :note' do + subject.note('basic') + expect(subject.messages).to contain_exactly(hash_including(kind: :note)) + end + end + + context '.report' do + it 'prints nothing if nothing happened' do + expect { subject.report }.not_to output.to_stdout + end + + it 'prints a removal header, then removal' do + subject.removal('one') + expect { subject.report }.to output(/\nRemovals:\none/).to_stdout + end + + it 'prints a removal header, then removals' do + subject.removal('one') + subject.removal('two') + expect { subject.report }.to output(/\nRemovals:\none\ntwo/).to_stdout + end + + it 'prints a deprecation header, then deprecation' do + subject.deprecation('one') + expect { subject.report }.to output(/\nDeprecations:\none/).to_stdout + end + + it 'prints a deprecation header, then deprecations' do + subject.deprecation('one') + subject.deprecation('two') + expect { subject.report }.to output(/\nDeprecations:\none\ntwo/).to_stdout + end + + it 'prints a warning header, then warning' do + subject.warning('one') + expect { subject.report }. to output(/\nWarnings:\none/).to_stdout + end + + it 'prints a warning header, then warnings' do + subject.warning('one') + subject.warning('two') + expect { subject.report }.to output(/\nWarnings:\none\ntwo/).to_stdout + end + + it 'prints a note header, then note' do + subject.note('one') + expect { subject.report }. to output(/\nNotes:\none/).to_stdout + end + + it 'prints a note header, then notes' do + subject.note('one') + subject.note('two') + expect { subject.report }.to output(/\nNotes:\none\ntwo/).to_stdout + end + + it 'prints a removal header, removals, deprecation header, deprecations, warning header, warnings, note header then notes' do + subject.removal('one') + subject.removal('two') + subject.deprecation('three') + subject.deprecation('four') + subject.warning('five') + subject.warning('six') + subject.note('seven') + subject.note('eight') + + output = <<~EOS + + Removals: + one + two + + + Deprecations: + three + four + + + Warnings: + five + six + + + Notes: + seven + eight + + EOS + + expect { subject.report } + .to output(output).to_stdout + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/helpers/node_helper_spec.rb b/spec/chef/cookbooks/package/libraries/helpers/node_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25vZGVfaGVscGVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/helpers/node_helper_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'chef_helper' + +RSpec.describe NodeHelper do + let(:chef_run) { converge_config } + subject { described_class } + + describe '.consume_cluster_attributes' do + it 'merges new attributes into node as override level' do + new_attrs = ::Gitlab::ConfigMash.new(geo_primary: true) + + subject.consume_cluster_attributes(chef_run.node, new_attrs) + + expect(chef_run.node.attributes).to include(geo_primary: true) + expect(chef_run.node.override).to include(geo_primary: true) + end + + it 'merges overwritting existing attributes into node as override level' do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb(package: { custom: true }) + + expect(chef_run.node.attributes['package']['custom']).to be true + + new_attrs = ::Gitlab::ConfigMash.new(package: { custom: false }) + subject.consume_cluster_attributes(chef_run.node, new_attrs) + + expect(chef_run.node.attributes['package']['custom']).to be false + expect(chef_run.node.override['package']['custom']).to be false + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/helpers/output_helper_spec.rb b/spec/chef/cookbooks/package/libraries/helpers/output_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL291dHB1dF9oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/helpers/output_helper_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +RSpec.describe OutputHelper do + include OutputHelper + + describe "#quote" do + context 'handling nil' do + it 'should return nil' do + expect(quote(nil)).to eq(nil) + end + end + + context 'handling numbers' do + it 'should cooerce numbers to strings' do + result = quote(42) + expect(result).to be_instance_of(String) + expect(result).to eq('"42"') + end + end + + context 'roundtripping values via YAML' do + values = [ + "foo", + # single quotes + "foo'", + "'foo", + "fo'o", + "'foo'", + # double quotes + 'foo"', + '"foo', + 'fo"o', + '"foo"', + # newlines + "foo\n", + "\nfoo", + "fo\no", + "\nfoo\n", + # tabs + "foo\t", + "\tfoo", + "fo\to", + "\tfoo\t", + # spaces + "foo ", + " foo", + "fo o", + " foo ", + # unicode snowman + "foo☃", + "☃foo", + "fo☃o", + "☃foo☃", + ] + values.each do |value| + it "should YAML roundtrip #{value.inspect}" do + # create a document with structure { 'value' : quote(value) } + yaml_document = "---\nvalue: #{quote(value)}" + document = YAML.safe_load(yaml_document) + expect(document['value']).to eq(value) + end + end + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/helpers/shell_out_helper_spec.rb b/spec/chef/cookbooks/package/libraries/helpers/shell_out_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3NoZWxsX291dF9oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/helpers/shell_out_helper_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe ShellOutHelper do + let(:dummy_class) { Class.new { extend ShellOutHelper } } + + before do + allow(Mixlib::ShellOut).to receive(:new).and_call_original + end + + describe '.do_shell_out' do + context 'without optional arguments' do + it 'creates Mixlib::ShellOut object without optional arguments' do + expect(Mixlib::ShellOut).to receive(:new).with('ls', { user: nil, cwd: nil, environment: {} }) + + dummy_class.do_shell_out('ls') + end + end + + context 'with optional arguments specified' do + it 'creates Mixlib::ShellOut object with specified optional arguments' do + expect(Mixlib::ShellOut).to receive(:new).with('ls', { user: 'foobar', cwd: '/tmp', environment: { PATH: '/tmp/bin' } }) + + dummy_class.do_shell_out('ls', 'foobar', '/tmp', env: { PATH: '/tmp/bin' }) + end + end + + context 'when command can not be executed' do + let(:shellout_object) { double('shellout', run_command: true) } + + it 'logs relevant information' do + allow(Mixlib::ShellOut).to receive(:new).and_return(shellout_object) + allow(shellout_object).to receive(:run_command).and_raise(Errno::EACCES) + + expect(Chef::Log).to receive(:info).with("Cannot execute ls.") + + dummy_class.do_shell_out('ls') + end + end + + context 'when command not found' do + let(:shellout_object) { double('shellout', run_command: true) } + + it 'logs relevant information' do + allow(Mixlib::ShellOut).to receive(:new).and_return(shellout_object) + allow(shellout_object).to receive(:run_command).and_raise(Errno::ENOENT) + + expect(Chef::Log).to receive(:info).with("ls does not exist.") + + dummy_class.do_shell_out('ls') + end + end + end + + describe '.do_shell_out_with_embedded_path' do + context 'without optional arguments' do + it 'calls do_shell_out with embedded bin path prependend to PATH' do + allow(ENV).to receive(:[]).with('PATH').and_return('/usr/local/bin') + + expect(dummy_class).to receive(:do_shell_out).with('ls', nil, nil, env: { PATH: '/opt/gitlab/embedded/bin:/usr/local/bin' }) + + dummy_class.do_shell_out_with_embedded_path('ls') + end + end + + context 'without PATH specified in optional env argument' do + it 'calls do_shell_out with embedded bin path prependend to PATH' do + allow(ENV).to receive(:[]).with('PATH').and_return('/usr/local/bin') + + expect(dummy_class).to receive(:do_shell_out).with('ls', nil, nil, env: { PATH: '/opt/gitlab/embedded/bin:/tmp/bin:/usr/local/bin' }) + + dummy_class.do_shell_out_with_embedded_path('ls', env: { 'PATH' => '/tmp/bin' }) + end + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/omnibus_helper_spec.rb b/spec/chef/cookbooks/package/libraries/omnibus_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9vbW5pYnVzX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/omnibus_helper_spec.rb @@ -0,0 +1,557 @@ +# frozen_string_literal: true + +require 'chef_helper' +require 'time' + +RSpec.describe OmnibusHelper do + cached(:chef_run) { converge_config } + let(:node) { chef_run.node } + let(:file) { double(:file, write: true) } + + subject { described_class.new(chef_run.node) } + + before do + allow(Gitlab).to receive(:[]).and_call_original + + allow(File).to receive(:open).and_call_original + allow(File).to receive(:open).with('/etc/gitlab/initial_root_password', 'w', 0600).and_yield(file).once + end + + describe '#user_exists?' do + it 'returns true when user exists' do + allow_any_instance_of(ShellOutHelper).to receive(:success?).with("id -u root").and_return(true) + expect(subject.user_exists?('root')).to be_truthy + end + + it 'returns false when user does not exist' do + allow_any_instance_of(ShellOutHelper).to receive(:success?).with("id -u nonexistentuser").and_return(false) + expect(subject.user_exists?('nonexistentuser')).to be_falsey + end + end + + describe '#group_exists?' do + it 'returns true when group exists' do + allow_any_instance_of(ShellOutHelper).to receive(:success?).with("getent group root").and_return(true) + expect(subject.group_exists?('root')).to be_truthy + end + + it 'returns false when group does not exist' do + allow_any_instance_of(ShellOutHelper).to receive(:success?).with("getent group nonexistentgroup").and_return(false) + expect(subject.group_exists?('nonexistentgroup')).to be_falsey + end + end + + describe '#not_listening?' do + let(:chef_run) { converge_config } + context 'when Redis is disabled' do + before do + stub_gitlab_rb( + redis: { enable: false } + ) + end + + it 'returns true when service is disabled' do + expect(subject.not_listening?('redis')).to be_truthy + end + end + + context 'when Redis is enabled' do + before do + stub_gitlab_rb( + redis: { enable: true } + ) + end + + it 'returns true when service is down' do + stub_service_failure_status('redis', true) + + expect(subject.not_listening?('redis')).to be_truthy + end + + it 'returns false when service is up' do + stub_service_failure_status('redis', false) + + expect(subject.not_listening?('redis')).to be_falsey + end + end + end + + describe '#service_enabled?' do + context 'services are enabled' do + before do + chef_run.node.normal['gitlab']['old_service']['enable'] = true + chef_run.node.normal['new_service']['enable'] = true + chef_run.node.normal['monitoring']['another_service']['enable'] = true + end + + it 'should return true' do + expect(subject.service_enabled?('old_service')).to be_truthy + expect(subject.service_enabled?('new_service')).to be_truthy + expect(subject.service_enabled?('another_service')).to be_truthy + end + end + + context 'services are disabled' do + before do + chef_run.node.normal['gitlab']['old_service']['enable'] = false + chef_run.node.normal['new_service']['enable'] = false + chef_run.node.normal['monitoring']['another_service']['enable'] = false + end + + it 'should return false' do + expect(subject.service_enabled?('old_service')).to be_falsey + expect(subject.service_enabled?('new_service')).to be_falsey + expect(subject.service_enabled?('another_service')).to be_falsey + end + end + end + + describe '#is_managed_and_offline?' do + context 'services are disabled' do + before do + chef_run.node.normal['gitlab']['old_service']['enable'] = false + chef_run.node.normal['new_service']['enable'] = false + end + + it 'returns false' do + expect(subject.is_managed_and_offline?('old_service')).to be_falsey + expect(subject.is_managed_and_offline?('new_service')).to be_falsey + end + end + + context 'services are enabled' do + before do + chef_run.node.normal['gitlab']['old_service']['enable'] = true + chef_run.node.normal['new_service']['enable'] = true + end + + it 'returns true when services are offline' do + stub_service_failure_status('old_service', true) + stub_service_failure_status('new_service', true) + + expect(subject.is_managed_and_offline?('old_service')).to be_truthy + expect(subject.is_managed_and_offline?('new_service')).to be_truthy + end + + it 'returns false when services are online ' do + stub_service_failure_status('old_service', false) + stub_service_failure_status('new_service', false) + + expect(subject.is_managed_and_offline?('old_service')).to be_falsey + expect(subject.is_managed_and_offline?('new_service')).to be_falsey + end + end + end + + describe '#is_deprecated_os?' do + before do + allow(OmnibusHelper).to receive(:deprecated_os_list).and_return({ "raspbian-8.0" => "GitLab 11.8" }) + end + + it 'detects deprecated OS correctly' do + allow_any_instance_of(Ohai::System).to receive(:data).and_return({ "platform" => "raspbian", "platform_version" => "8.0" }) + + OmnibusHelper.is_deprecated_os? + + expect_logged_deprecation(/Your OS, raspbian-8.0, will be deprecated soon/) + end + + it 'does not detects valid OS as deprecated' do + allow_any_instance_of(Ohai::System).to receive(:data).and_return({ "platform" => "ubuntu", "platform_version" => "16.04.3" }) + expect(LoggingHelper).not_to receive(:deprecation) + OmnibusHelper.is_deprecated_os? + end + end + + describe '#is_deprecated_praefect_config?' do + before do + chef_run.node.normal['praefect'] = config + end + + context 'deprecated config' do + let(:config) do + { + storage_nodes: [ + { storage: 'praefect1', address: 'tcp://node1.internal' }, + { storage: 'praefect2', address: 'tcp://node2.internal' } + ] + } + end + + it 'detects deprecated config correctly' do + subject.is_deprecated_praefect_config? + + expect_logged_deprecation(/Specifying Praefect storage nodes as an array is deprecated/) + end + end + + context 'valid config' do + let(:config) do + { + storage_nodes: { + 'praefect1' => { address: 'tcp://node1.internal' }, + 'praefect2' => { address: 'tcp://node2.internal' } + } + } + end + + it 'does not detect a valid config as deprecated' do + expect(LoggingHelper).not_to receive(:deprecation) + + subject.is_deprecated_praefect_config? + end + end + end + + describe '#write_root_password' do + before do + stub_gitlab_rb( + gitlab_rails: { + initial_root_password: 'foobar', + store_initial_root_password: true + } + ) + end + + it 'stores root password to /etc/gitlab/initial_root_password' do + content = <<~EOS + # WARNING: This value is valid only in the following conditions + # 1. If provided manually (either via `GITLAB_ROOT_PASSWORD` environment variable or via `gitlab_rails['initial_root_password']` setting in `gitlab.rb`, it was provided before database was seeded for the first time (usually, the first reconfigure run). + # 2. Password hasn't been changed manually, either via UI or via command line. + # + # If the password shown here doesn't work, you must reset the admin password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password. + + Password: foobar + + # NOTE: This file will be automatically deleted in the first reconfigure run after 24 hours. + EOS + + expect(file).to receive(:write).with(content) + + described_class.new(converge_config.node).write_root_password + end + end + + describe '.cleanup_root_password_file' do + context 'when /etc/gitlab/initial_root_password does not exist' do + before do + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with('/etc/gitlab/initial_root_password').and_return(false) + end + + it 'does not attempt to remove the file' do + expect(FileUtils).not_to receive(:rm_f).with('/etc/gitlab/initial_root_password') + + described_class.cleanup_root_password_file + end + end + + context 'when /etc/gitlab/initial_root_password exists' do + before do + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with('/etc/gitlab/initial_root_password').and_return(true) + allow(Time).to receive(:now).and_return(Time.parse('2021-06-07 08:45:51.377926667 +0530')) + end + + context 'when file is older than 24 hours' do + before do + allow(File).to receive(:mtime).with('/etc/gitlab/initial_root_password').and_return(Time.parse('2021-06-03 06:45:51.377926667 +0530')) + end + + it 'attempts to remove the file' do + expect(FileUtils).to receive(:rm_f).with('/etc/gitlab/initial_root_password') + + described_class.cleanup_root_password_file + + expect_logged_note('Found old initial root password file at /etc/gitlab/initial_root_password and deleted it.') + end + end + + context 'when file is younger than 24 hours' do + before do + allow(File).to receive(:mtime).with('/etc/gitlab/initial_root_password').and_return(Time.parse('2021-06-08 06:45:51.377926667 +0530')) + end + + it 'does not attempt to remove the file' do + expect(FileUtils).not_to receive(:rm_f).with('/etc/gitlab/initial_root_password') + + described_class.cleanup_root_password_file + + expect_logged_note('Found old initial root password file at /etc/gitlab/initial_root_password and deleted it.') + end + end + end + end + + describe '#print_root_account_details' do + context 'when not on first reconfigure after installation' do + before do + chef_run.node.normal['gitlab']['bootstrap']['enable'] = false + end + + it 'does not add a note or write password to file' do + expect(LoggingHelper).not_to receive(:note) + expect(subject).not_to receive(:write_root_password) + + subject.print_root_account_details + end + end + + context 'when on first reconfigure after installation' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:writ_root_password).and_return(true) + end + + context 'when display_initial_root_password is true' do + before do + stub_gitlab_rb( + gitlab_rails: { + initial_root_password: 'foobar', + display_initial_root_password: true, + store_initial_root_password: false + } + ) + end + + it 'displays root password at the end of reconfigure' do + msg = <<~EOS + Default admin account has been configured with following details: + Username: root + Password: foobar + + NOTE: Because these credentials might be present in your log files in plain text, it is highly recommended to reset the password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password. + EOS + + described_class.new(converge_config.node).print_root_account_details + + expect_logged_note(msg) + end + end + + context 'when display_initial_root_password is false' do + before do + stub_gitlab_rb( + gitlab_rails: { + initial_root_password: 'foobar', + display_initial_root_password: false, + store_initial_root_password: false + } + ) + end + + it 'does not display root credentials at the end of reconfigure' do + msg = <<~EOS + Default admin account has been configured with following details: + Username: root + Password: You didn't opt-in to print initial root password to STDOUT. + + NOTE: Because these credentials might be present in your log files in plain text, it is highly recommended to reset the password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password. + EOS + + described_class.new(converge_config.node).print_root_account_details + + expect_logged_note(msg) + end + end + + describe '#write_root_password' do + context 'when store_initial_root_password is true' do + before do + stub_gitlab_rb( + gitlab_rails: { + initial_root_password: 'foobar', + display_initial_root_password: false, + store_initial_root_password: true + } + ) + end + + it 'writes initial root password to /etc/gitlab/initial_root_password' do + subject = described_class.new(converge_config.node) + expect(subject).to receive(:write_root_password) + + subject.print_root_account_details + + expect_logged_note(%r{Password stored to /etc/gitlab/initial_root_password}) + end + end + + context 'with default value for store_initial_root_password' do + context 'when password is auto-generated' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('GITLAB_ROOT_PASSWORD').and_return(nil) + end + + it 'writes initial root password to /etc/gitlab/initial_root_password' do + chef_run = ChefSpec::SoloRunner.converge('gitlab::default') + subject = described_class.new(chef_run.node) + + expect(subject).to receive(:write_root_password) + + subject.print_root_account_details + + expect_logged_note(%r{Password stored to /etc/gitlab/initial_root_password}) + end + end + + context 'when password is specified by user' do + context 'via gitlab.rb' do + before do + stub_gitlab_rb( + gitlab_rails: { + initial_root_password: 'foobar', + } + ) + end + + it 'does not write initial root password to /etc/gitlab/initial_root_password' do + subject = described_class.new(converge_config.node) + + expect(subject).not_to receive(:write_root_password) + + subject.print_root_account_details + end + end + + context 'via env variable' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('GITLAB_ROOT_PASSWORD').and_return('foobar') + end + + it 'does not write initial root password to /etc/gitlab/initial_root_password' do + subject = described_class.new(converge_config.node) + + expect(subject).not_to receive(:write_root_password) + + subject.print_root_account_details + end + end + end + end + end + end + end + + describe '#check_locale' do + let(:error_message) { "Identified encoding is not UTF-8. GitLab requires UTF-8 encoding to function properly. Please check your locale settings." } + + describe 'using LC_ALL variable' do + it 'does not raise a warning when set to a UTF-8 locale even if others are not' do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('LC_ALL').and_return('en_US.UTF-8') + allow(ENV).to receive(:[]).with('LC_COLLATE').and_return('en_SG ISO-8859-1') + allow(ENV).to receive(:[]).with('LC_CTYPE').and_return('en_SG ISO-8859-1') + allow(ENV).to receive(:[]).with('LANG').and_return('en_SG ISO-8859-1') + + expect(LoggingHelper).not_to receive(:warning).with("Environment variable .* specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") + + described_class.check_locale + end + + it 'raises warning when LC_ALL is non-UTF-8' do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('LC_ALL').and_return('en_SG ISO-8859-1') + + described_class.check_locale + + expect_logged_warning("Environment variable LC_ALL specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") + end + end + + describe 'using LC_CTYPE variable' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('LC_ALL').and_return(nil) + end + + it 'raises warning when LC_CTYPE is non-UTF-8' do + allow(ENV).to receive(:[]).with('LC_CTYPE').and_return('en_SG ISO-8859-1') + + described_class.check_locale + + expect_logged_warning("Environment variable LC_CTYPE specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") + end + end + + describe 'using LC_COLLATE variable' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('LC_ALL').and_return(nil) + allow(ENV).to receive(:[]).with('LC_CTYPE').and_return(nil) + end + + it 'raises warning when LC_COLLATE is non-UTF-8' do + allow(ENV).to receive(:[]).with('LC_COLLATE').and_return('en_SG ISO-8859-1') + + described_class.check_locale + + expect_logged_warning("Environment variable LC_COLLATE specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") + end + end + + describe 'using LANG variable' do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('LC_ALL').and_return(nil) + end + + context 'without LC_CTYPE and LC_COLLATE' do + before do + allow(ENV).to receive(:[]).with('LC_CTYPE').and_return(nil) + allow(ENV).to receive(:[]).with('LC_COLLATE').and_return(nil) + end + + it 'raises warning when LANG is non-UTF-8' do + allow(ENV).to receive(:[]).with('LANG').and_return('en_SG ISO-8859-1') + + described_class.check_locale + + expect_logged_warning("Environment variable LANG specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") + end + end + + context 'with only LC_CTYPE set to UTF-8' do + before do + allow(ENV).to receive(:[]).with('LC_CTYPE').and_return('en_US.UTF-8') + allow(ENV).to receive(:[]).with('LC_COLLATE').and_return(nil) + end + + it 'raises warning when LANG is non-UTF-8' do + allow(ENV).to receive(:[]).with('LANG').and_return('en_SG ISO-8859-1') + + described_class.check_locale + + expect_logged_warning("Environment variable LANG specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") + end + end + + context 'with both LC_CTYPE and LC_COLLATE set to UTF-8' do + before do + allow(ENV).to receive(:[]).with('LC_CTYPE').and_return('en_US.UTF-8') + allow(ENV).to receive(:[]).with('LC_COLLATE').and_return('en_US.UTF-8') + end + + it 'does not raise a warning even if LANG is not UTF-8' do + described_class.check_locale + + expect_logged_warning("Environment variable LANG specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") + end + end + end + end + + describe '.resource_available?' do + cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + subject(:omnibus_helper) { described_class.new(chef_run.node) } + + it 'returns false for a resource that exists but has not been loaded in runtime' do + expect(omnibus_helper.resource_available?('runit_service[geo-logcursor]')).to be_falsey + end + + it 'returns true for a resource that exists and is loaded in runtime' do + expect(omnibus_helper.resource_available?('runit_service[logrotated]')) + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/secrets_helper_spec.rb b/spec/chef/cookbooks/package/libraries/secrets_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9zZWNyZXRzX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/secrets_helper_spec.rb @@ -0,0 +1,375 @@ +require 'chef_helper' +require 'base64' + +RSpec.describe 'secrets' do + let(:chef_run) { ChefSpec::SoloRunner.new.converge('gitlab::default') } + + HEX_KEY = /\h{128}/.freeze + RSA_KEY = /\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m.freeze + + def stub_gitlab_secrets_json(secrets) + allow(File).to receive(:read).with('/etc/gitlab/gitlab-secrets.json').and_return(JSON.generate(secrets)) + end + + before do + allow(File).to receive(:directory?).and_call_original + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:read).and_call_original + allow(File).to receive(:open).and_call_original + allow(File).to receive(:open).with('/etc/gitlab/initial_root_password', 'w', 0600).and_yield(double(:file, write: true)).once + end + + context 'when /etc/gitlab does not exist' do + it 'does not write secrets to the file' do + allow(File).to receive(:directory?).with('/etc/gitlab').and_return(false) + expect(File).not_to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w') + + chef_run + end + end + + context 'when /etc/gitlab exists' do + let(:file) { double(:file) } + let(:new_secrets) { @new_secrets } + let(:gitlab_rb_ci_jwt_signing_key) { SecretsHelper.generate_rsa(4096).to_pem } + + before do + allow(SecretsHelper).to receive(:system) + allow(File).to receive(:directory?).with('/etc/gitlab').and_return(true) + allow(File).to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w', 0600).and_yield(file).once + allow(file).to receive(:puts) { |json| @new_secrets = JSON.parse(json) } + allow(file).to receive(:chmod).and_return(true) + end + + context 'when there are no existing secrets' do + before do + allow(File).to receive(:exist?).with('/etc/gitlab/gitlab-secrets.json').and_return(false) + + chef_run + end + + it 'writes new secrets to the file, with different values for each' do + rails_keys = new_secrets['gitlab_rails'] + hex_keys = rails_keys.values_at('db_key_base', 'otp_key_base', 'secret_key_base', 'encrypted_settings_key_base') + rsa_keys = rails_keys.values_at('openid_connect_signing_key', 'ci_jwt_signing_key') + + expect(rails_keys.to_a.uniq).to eq(rails_keys.to_a) + expect(hex_keys).to all(match(HEX_KEY)) + expect(rsa_keys).to all(match(RSA_KEY)) + end + + it 'does not write legacy keys' do + expect(new_secrets).not_to have_key('gitlab_ci') + expect(new_secrets['gitlab_rails']).not_to have_key('jws_private_key') + end + + it 'generates an appropriate secret for gitlab-workhorse' do + workhorse_secret = new_secrets['gitlab_workhorse']['secret_token'] + expect(Base64.strict_decode64(workhorse_secret).length).to eq(32) + end + + it 'generates an appropriate shared secret for gitlab-pages' do + pages_shared_secret = new_secrets['gitlab_pages']['api_secret_key'] + expect(Base64.strict_decode64(pages_shared_secret).length).to eq(32) + end + + it 'generates an appropriate shared secret for gitlab-kas' do + kas_shared_secret = new_secrets['gitlab_kas']['api_secret_key'] + expect(Base64.strict_decode64(kas_shared_secret).length).to eq(32) + end + end + + context 'gitlab.rb provided gitlab_pages.api_secret_key' do + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + it 'fails when provided gitlab_pages.shared_secret is not 32 bytes' do + stub_gitlab_rb(gitlab_pages: { api_secret_key: SecureRandom.base64(16) }) + + expect { chef_run }.to raise_error(RuntimeError, /gitlab_pages\['api_secret_key'\] should be exactly 32 bytes/) + end + + it 'accepts provided gitlab_pages.api_secret_key when it is 32 bytes' do + api_secret_key = SecureRandom.base64(32) + stub_gitlab_rb(gitlab_pages: { api_secret_key: api_secret_key }) + + expect { chef_run }.not_to raise_error + expect(new_secrets['gitlab_pages']['api_secret_key']).to eq(api_secret_key) + end + end + + context 'gitlab.rb provided gitlab_kas.api_secret_key' do + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + it 'fails when provided gitlab_kas.shared_secret is not 32 bytes' do + stub_gitlab_rb(gitlab_kas: { api_secret_key: SecureRandom.base64(16) }) + + expect { chef_run }.to raise_error(RuntimeError, /gitlab_kas\['api_secret_key'\] should be exactly 32 bytes/) + end + + it 'accepts provided gitlab_kas.api_secret_key when it is 32 bytes' do + api_secret_key = SecureRandom.base64(32) + stub_gitlab_rb(gitlab_kas: { api_secret_key: api_secret_key }) + + expect { chef_run }.not_to raise_error + expect(new_secrets['gitlab_kas']['api_secret_key']).to eq(api_secret_key) + end + end + + context 'when there are existing secrets in /etc/gitlab/gitlab-secrets.json' do + before do + allow(SecretsHelper).to receive(:system) + allow(File).to receive(:directory?).with('/etc/gitlab').and_return(true) + allow(File).to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w').and_yield(file).once + allow(File).to receive(:exist?).with('/etc/gitlab/gitlab-secrets.json').and_return(true) + end + + context 'when secrets are only partially present' do + before do + stub_gitlab_secrets_json(gitlab_ci: { db_key_base: 'json_ci_db_key_base' }) + chef_run + end + + it 'uses secrets from /etc/gitlab/gitlab-secrets.json where available' do + expect(new_secrets['gitlab_rails']['db_key_base']).to eq('json_ci_db_key_base') + end + + it 'falls back further to generating new secrets' do + expect(new_secrets['gitlab_rails']['otp_key_base']).to match(HEX_KEY) + end + end + + context 'when secrets exist under legacy keys' do + before do + stub_gitlab_secrets_json( + gitlab_ci: { db_key_base: 'json_ci_db_key_base', secret_token: 'json_ci_secret_token' }, + gitlab_rails: { + secret_token: 'json_rails_secret_token', + jws_private_key: 'json_rails_jws_private_key' + } + ) + + chef_run + end + + it 'moves gitlab_ci.db_key_base to gitlab_rails.db_key_base' do + expect(new_secrets['gitlab_rails']['db_key_base']).to eq('json_ci_db_key_base') + end + + it 'moves gitlab_rails.secret_token to gitlab_rails.otp_key_base' do + expect(new_secrets['gitlab_rails']['otp_key_base']).to eq('json_rails_secret_token') + end + + it 'moves gitlab_ci.db_key_base to gitlab_rails.secret_key_base' do + expect(new_secrets['gitlab_rails']['secret_key_base']).to eq('json_ci_db_key_base') + end + + it 'moves gitlab_rails.jws_private_key to gitlab_rails.openid_connect_signing_key' do + expect(new_secrets['gitlab_rails']['openid_connect_signing_key']).to eq('json_rails_jws_private_key') + end + + it 'ignores other, unused, secrets' do + expect(new_secrets.inspect).not_to include('json_ci_secret_token') + end + end + end + + context 'when there are existing secrets in /etc/gitlab/gitlab.rb and /etc/gitlab/gitlab-secrets.json' do + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(File).to receive(:exist?).with('/etc/gitlab/gitlab-secrets.json').and_return(true) + end + + context 'when secrets are only partially present' do + before do + stub_gitlab_secrets_json( + gitlab_ci: { db_key_base: 'json_ci_db_key_base' }, + gitlab_rails: { + secret_token: 'json_rails_secret_token', + jws_private_key: 'json_rails_jws_private_key' + } + ) + + stub_gitlab_rb(gitlab_ci: { db_key_base: 'rb_ci_db_key_base' }) + + chef_run + end + + it 'uses secrets from /etc/gitlab/gitlab.rb when available' do + expect(new_secrets['gitlab_rails']['db_key_base']).to eq('rb_ci_db_key_base') + end + + it 'falls back to secrets from /etc/gitlab/gitlab-secrets.json' do + expect(new_secrets['gitlab_rails']['otp_key_base']).to eq('json_rails_secret_token') + end + + it 'falls back further to generating new secrets' do + expect(new_secrets['gitlab_shell']['secret_token']).to match(HEX_KEY) + end + end + + context 'when secrets exist under legacy keys' do + before do + stub_gitlab_rb(gitlab_ci: { db_key_base: 'rb_ci_db_key_base', + secret_token: 'rb_ci_secret_token' }) + + stub_gitlab_secrets_json( + gitlab_rails: { + secret_token: 'json_rails_secret_token', + jws_private_key: 'json_rails_jws_private_key', + encrypted_settings_key_base: 'encrypted_settings_key_base', + ci_jwt_signing_key: gitlab_rb_ci_jwt_signing_key + } + ) + + chef_run + end + + it 'moves gitlab_ci.db_key_base to gitlab_rails.db_key_base' do + expect(new_secrets['gitlab_rails']['db_key_base']).to eq('rb_ci_db_key_base') + end + + it 'moves gitlab_rails.secret_token to gitlab_rails.otp_key_base' do + expect(new_secrets['gitlab_rails']['otp_key_base']).to eq('json_rails_secret_token') + end + + it 'moves gitlab_ci.db_key_base to gitlab_rails.secret_key_base' do + expect(new_secrets['gitlab_rails']['secret_key_base']).to eq('rb_ci_db_key_base') + end + + it 'moves gitlab_rails.jws_private_key to gitlab_rails.openid_connect_signing_key' do + expect(new_secrets['gitlab_rails']['openid_connect_signing_key']).to eq('json_rails_jws_private_key') + end + + it 'ignores other, unused, secrets' do + expect(new_secrets.inspect).not_to include('rb_ci_secret_token') + end + + it 'writes the correct data to secrets.yml' do + expect(chef_run).to create_templatesymlink('Create a secrets.yml and create a symlink to Rails root').with_variables( + 'secrets' => { + 'production' => { + 'db_key_base' => 'rb_ci_db_key_base', + 'secret_key_base' => 'rb_ci_db_key_base', + 'otp_key_base' => 'json_rails_secret_token', + 'encrypted_settings_key_base' => 'encrypted_settings_key_base', + 'openid_connect_signing_key' => 'json_rails_jws_private_key', + 'ci_jwt_signing_key' => gitlab_rb_ci_jwt_signing_key + } + } + ) + end + + it 'deletes the secret file' do + expect(chef_run).to delete_file('/var/opt/gitlab/gitlab-rails/etc/secret') + expect(chef_run).to delete_file('/opt/gitlab/embedded/service/gitlab-rails/.secret') + end + end + + context 'when secrets are ambiguous and cannot be migrated automatically' do + before { stub_gitlab_secrets_json({}) } + + it 'fails when gitlab_ci.db_key_base and gitlab_rails.db_key_base are different' do + stub_gitlab_rb( + gitlab_rails: { db_key_base: 'rb_rails_db_key_base' }, + gitlab_ci: { db_key_base: 'rb_ci_db_key_base' } + ) + + expect(File).not_to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w') + expect { chef_run }.to raise_error(RuntimeError, /db_key_base/) + end + + it 'fails when the secret file does not match gitlab_rails.otp_key_base' do + secret_file = '/var/opt/gitlab/gitlab-rails/etc/secret' + + stub_gitlab_rb(gitlab_rails: { otp_key_base: 'rb_rails_otp_key_base', + secret_key_base: 'rb_rails_secret_key_base' }) + + allow(File).to receive(:exist?).with(secret_file).and_return(true) + allow(File).to receive(:read).with(secret_file).and_return('secret_key_base') + + expect { chef_run }.to raise_error(RuntimeError, /otp_key_base/) + end + end + + context 'ci_jwt_signing_key' do + let(:secrets_json_ci_jwt_signing_key) { SecretsHelper.generate_rsa(4096).to_pem } + + it 'uses the key from /etc/gitlab/gitlab.rb when available' do + stub_gitlab_secrets_json( + gitlab_rails: { ci_jwt_signing_key: secrets_json_ci_jwt_signing_key } + ) + + stub_gitlab_rb( + gitlab_rails: { ci_jwt_signing_key: gitlab_rb_ci_jwt_signing_key } + ) + + chef_run + + expect(new_secrets['gitlab_rails']['ci_jwt_signing_key']).to eq(gitlab_rb_ci_jwt_signing_key) + end + + it 'uses the key from /etc/gitlab/gitlab-secrets.json when available' do + stub_gitlab_secrets_json( + gitlab_rails: { ci_jwt_signing_key: secrets_json_ci_jwt_signing_key } + ) + + chef_run + + expect(new_secrets['gitlab_rails']['ci_jwt_signing_key']).to eq(secrets_json_ci_jwt_signing_key) + end + + it 'rejects invalid RSA keys' do + stub_gitlab_secrets_json({}) + stub_gitlab_rb( + gitlab_rails: { ci_jwt_signing_key: 'invalid key' } + ) + + expect { chef_run }.to raise_error('ci_jwt_signing_key: The provided key is not valid RSA key') + end + + it 'rejects RSA public keys' do + public_key = SecretsHelper.generate_rsa(4096).public_key.to_pem + + stub_gitlab_secrets_json({}) + stub_gitlab_rb( + gitlab_rails: { ci_jwt_signing_key: public_key } + ) + + expect { chef_run }.to raise_error('ci_jwt_signing_key: The provided key is not private RSA key') + end + + it 'writes the correct data to secrets.yml' do + stub_gitlab_secrets_json({}) + + stub_gitlab_rb( + gitlab_rails: { + db_key_base: 'rb_ci_db_key_base', + secret_key_base: 'rb_ci_db_key_base', + otp_key_base: 'json_rails_secret_token', + encrypted_settings_key_base: 'encrypted_settings_key_base', + openid_connect_signing_key: 'json_rails_jws_private_key', + ci_jwt_signing_key: gitlab_rb_ci_jwt_signing_key + } + ) + + expect(chef_run).to create_templatesymlink('Create a secrets.yml and create a symlink to Rails root').with_variables( + 'secrets' => { + 'production' => { + 'db_key_base' => 'rb_ci_db_key_base', + 'secret_key_base' => 'rb_ci_db_key_base', + 'otp_key_base' => 'json_rails_secret_token', + 'encrypted_settings_key_base' => 'encrypted_settings_key_base', + 'openid_connect_signing_key' => 'json_rails_jws_private_key', + 'ci_jwt_signing_key' => gitlab_rb_ci_jwt_signing_key + } + } + ) + end + end + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/services_helper_spec.rb b/spec/chef/cookbooks/package/libraries/services_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9zZXJ2aWNlc19oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/services_helper_spec.rb @@ -0,0 +1,229 @@ +require 'chef_helper' + +RSpec.describe Services do + let(:chef_run) { ChefSpec::SoloRunner.new } + + subject(:services) { described_class } + + describe '.enable_group' do + before do + chef_run.converge('gitlab::default') + end + + it 'enables services in provide groups list' do + expect do + services.enable_group('pages_role') + end.to change { services.enabled?('gitlab_pages') }.from(false).to(true) + end + + it 'skips services that dont `exist?`' do + expect do + services.enable_group('monitoring') + end.not_to change { services.enabled?('pgbouncer_exporter') } + end + end + + describe '.disable_group' do + before do + chef_run.converge('gitlab::default') + end + + it 'disables services in the provide groups list when setting to false' do + expect do + services.disable_group('redis') + end.to change { services.enabled?('redis') }.from(true).to(false) + end + + it 'it allows disabling services in system group when setting to false and include_system: true' do + expect do + services.disable_group('system', include_system: true) + end.to change { services.enabled?('logrotate') }.from(true).to(false) + end + end + + describe '.enable' do + before do + chef_run.converge('gitlab::default') + end + + it 'enables provide service' do + expect do + services.enable('gitlab_pages') + end.to change { services.enabled?('gitlab_pages') }.from(false).to(true) + end + + it 'skips services that dont `exist?`' do + expect do + services.enable('pgbouncer_exporter') + end.not_to change { services.enabled?('pgbouncer_exporter') } + end + end + + describe '.disable' do + before do + chef_run.converge('gitlab::default') + end + + it 'disables provide service' do + expect do + services.disable('puma') + end.to change { services.enabled?('puma') }.from(true).to(false) + end + + it 'allows disabling service in system group when include_system: true' do + expect do + services.disable('logrotate', include_system: true) + end.to change { services.enabled?('logrotate') }.from(true).to(false) + end + end + + describe '.set_status' do + before do + chef_run.converge('gitlab::default') + end + + it 'enables provide service when setting to true' do + expect do + services.set_status('gitlab_pages', true) + end.to change { services.enabled?('gitlab_pages') }.from(false).to(true) + end + + it 'disables provide service when setting to false' do + expect do + services.set_status('puma', false) + end.to change { services.enabled?('puma') }.from(true).to(false) + end + + it 'allows disabling service in system group when setting to false and include_system: true' do + expect do + services.set_status('logrotate', false, include_system: true) + end.to change { services.enabled?('logrotate') }.from(true).to(false) + end + end + + describe '.set_group_status' do + before do + chef_run.converge('gitlab::default') + end + + it 'enables services in provide groups list when setting to true' do + expect do + services.set_group_status('pages_role', true) + end.to change { services.enabled?('gitlab_pages') }.from(false).to(true) + end + + it 'disables services in the provide groups list when setting to false' do + expect do + services.set_group_status('redis', false) + end.to change { services.enabled?('redis') }.from(true).to(false) + end + + it 'it allows disabling services in system group when setting to false and include_system: true' do + expect do + services.set_group_status('system', false, include_system: true) + end.to change { services.enabled?('logrotate') }.from(true).to(false) + end + end + + describe '.system_services' do + it 'returns a list of services that are in the SYSTEM_GROUP' do + service_list = { + 'redis' => { groups: %w[redis] }, + 'logrotate' => { groups: %w[system] } + } + services.add_services('test', service_list) + + expect(services.system_services).to include('logrotate') + end + end + + describe '.find_by_group' do + before do + service_list = { + 'redis' => { groups: %w[redis] }, + 'redis_exporter' => { groups: %w[redis monitoring] } + } + + services.add_services('test', service_list) + end + + it 'returns a list of services that matches provided groups' do + expect(services.find_by_group('monitoring')).to eq(['redis_exporter']) + end + + it 'returns an empty list when no provide groups match' do + expect(services.find_by_group('non_existent')).to eq([]) + end + end + + describe '.service_list' do + it 'returns a list of loaded known services by existing cookbooks' do + chef_run.converge('gitlab::default') + + expect(services.service_list).to include(Services::BaseServices.list) + end + end + + describe '.add_services' do + it 'adds provided services and register associated cookbook' do + services.add_services('gitlab', Services::BaseServices.list) + + expect(services.service_list).to include(Services::BaseServices.list) + expect(services.send(:cookbook_services).keys).to include('gitlab') + end + end + + describe '.enabled?' do + before do + chef_run.converge('gitlab::default') + end + + context 'when status is set via configuration file' do + it 'returns true if set as enabled' do + Gitlab['mailroom']['enable'] = true + + expect(services.enabled?('mailroom')).to be_truthy + end + + it 'returns false if set as disabled' do + Gitlab['puma']['enable'] = false + + expect(services.enabled?('puma')).to be_falsey + end + end + + context 'when status is not set via configuration file' do + it 'returns true when its enabled via cookbooks' do + expect(services.enabled?('puma')).to be_truthy + end + + it 'returns false when disabled via cookbooks' do + expect(services.enabled?('mailroom')).to be_falsey + end + end + end + + describe '.exist?' do + it 'returns false for non existing service' do + expect(services.exist?('inexistent_service')).to be_falsey + end + + context 'with only CE services registered' do + it 'returns true for CE services and false for EE services' do + chef_run.converge('gitlab::default') + + expect(services.exist?('logrotate')).to be_truthy + expect(services.exist?('pgbouncer_exporter')).to be_falsey + end + end + + context 'with CE and EE services registered' do + it 'returns true for EE services registered via add_service' do + chef_run.converge('gitlab::default', 'gitlab-ee::default') + + expect(services.exist?('logrotate')).to be_truthy + expect(services.exist?('pgbouncer_exporter')).to be_truthy + end + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/storage_directory_helper_spec.rb b/spec/chef/cookbooks/package/libraries/storage_directory_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9zdG9yYWdlX2RpcmVjdG9yeV9oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/storage_directory_helper_spec.rb @@ -0,0 +1,109 @@ +require 'chef_helper' + +RSpec.describe StorageDirectoryHelper do + let(:success_shell) do + shell = instance_double(Mixlib::ShellOut) + allow(shell).to receive(:exitstatus).and_return(0) + shell + end + + let(:fail_shell) do + shell = instance_double(Mixlib::ShellOut) + allow(shell).to receive(:exitstatus).and_return(1) + shell + end + + before { allow(Gitlab).to receive(:[]).and_call_original } + + describe :validate do + context 'owner provided' do + subject { ::StorageDirectoryHelper.new('git', nil, nil) } + + it 'checks directory and owner and succeeds' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(success_shell) + expect(subject.validate('/tmp/validate')).to eq(true) + end + + it 'fails when path is not a directory' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(fail_shell) + expect(subject).not_to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args) + expect(subject.validate('/tmp/validate')).to eq(false) + end + + it 'fails when owner does not match' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(fail_shell) + expect(subject.validate('/tmp/validate')).to eq(false) + end + end + + context 'owner and group provided' do + subject { ::StorageDirectoryHelper.new('git', 'root', nil) } + + it 'checks directory, owner and group and succeeds' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U:%G' $(readlink -f /tmp/validate))\" = 'git:root' ]", any_args).and_return(success_shell) + expect(subject.validate('/tmp/validate')).to eq(true) + end + + it 'fails when path is not a directory' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(fail_shell) + expect(subject).not_to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U:%G' $(readlink -f /tmp/validate))\" = 'git:root' ]", any_args) + expect(subject.validate('/tmp/validate')).to eq(false) + end + + it 'fails when group does not match' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U:%G' $(readlink -f /tmp/validate))\" = 'git:root' ]", any_args).and_return(fail_shell) + expect(subject.validate('/tmp/validate')).to eq(false) + end + end + + context 'owner and permission mode provided' do + subject { ::StorageDirectoryHelper.new('git', nil, '700') } + + it 'checks directory, owner and permissions and succeeds' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%04a' $(readlink -f /tmp/validate) | grep -o '...$')\" = '700' ]", any_args).and_return(success_shell) + expect(subject.validate('/tmp/validate')).to eq(true) + end + + it 'fails when path is not a directory' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(fail_shell) + expect(subject).not_to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U:%G' $(readlink -f /tmp/validate))\" = 'git:root' ]", any_args) + expect(subject).not_to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%04a' $(readlink -f /tmp/validate) | grep -o '...$')\" = '700' ]", any_args) + expect(subject.validate('/tmp/validate')).to eq(false) + end + + it 'fails when owner does not match' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(fail_shell) + expect(subject).not_to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%04a' $(readlink -f /tmp/validate) | grep -o '...$')\" = '700' ]", any_args) + expect(subject.validate('/tmp/validate')).to eq(false) + end + + it 'fails when permissions do not match' do + expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(success_shell) + expect(subject).to receive(:run_command) + .with("set -x && [ \"$(stat --printf='%04a' $(readlink -f /tmp/validate) | grep -o '...$')\" = '700' ]", any_args).and_return(fail_shell) + expect(subject.validate('/tmp/validate')).to eq(false) + end + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/systemd_helper_spec.rb b/spec/chef/cookbooks/package/libraries/systemd_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9zeXN0ZW1kX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/systemd_helper_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +RSpec.describe SystemdHelper do + describe '#systemd_version' do + context 'when systemctl returns valid output' do + before do + valid_output = <<~OUTPUT + systemd 242 (242) + +PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT + OUTPUT + + allow(IO).to receive(:popen).with(%w[systemctl --version]).and_return(valid_output) + end + + it 'returns correct version' do + expect(described_class.systemd_version).to eq(242) + end + end + + context 'when systemctl returns invalid output' do + before do + invalid_output = <<~OUTPUT + some invalid output + OUTPUT + + allow(IO).to receive(:popen).with(%w[systemctl --version]).and_return(invalid_output) + end + + it 'returns an insanely low version' do + expect(described_class.systemd_version).to eq(-999) + end + end + end +end diff --git a/spec/chef/cookbooks/package/recipes/runit_spec.rb b/spec/chef/cookbooks/package/recipes/runit_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL3JlY2lwZXMvcnVuaXRfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/recipes/runit_spec.rb @@ -0,0 +1,85 @@ +require 'chef_helper' + +RSpec.describe 'package::runit' do + before do + allow(Chef::Log).to receive(:info).and_call_original + allow(Chef::Log).to receive(:warn).and_call_original + allow(File).to receive(:directory?).and_call_original + allow(File).to receive(:directory?).with('/run/systemd/system').and_return(false) + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with('/.dockerenv').and_return(false) + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'default values' do + let(:chef_run) { ChefSpec::SoloRunner.converge('package::runit') } + + before do + allow_any_instance_of(Chef::Recipe).to receive(:include_recipe) + end + + it 'detects systemd correctly and informs user' do + allow(File).to receive(:directory?).with('/run/systemd/system').and_return(true) + + expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('package::runit_systemd') + + chef_run + end + + it 'detects docker container correctly' do + allow(File).to receive(:exist?).with('/.dockerenv').and_return(true) + + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_systemd') + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_upstart') + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_sysvinit') + + chef_run + end + + it 'detects upstart correctly and informs user' do + allow(Open3).to receive(:capture3).with('/sbin/init --version | grep upstart').and_return(['', '', double(success?: true)]) + + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_systemd') + expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('package::runit_upstart') + expect(Chef::Log).to receive(:warn).with("Selected upstart because /sbin/init --version is showing upstart.") + + chef_run + end + + it 'fall backs to sysvinit correctly and informs user' do + allow(Open3).to receive(:capture3).with('/sbin/init --version | grep upstart').and_return(['', '', double(success?: false)]) + + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_systemd') + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_upstart') + expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('package::runit_sysvinit') + + chef_run + end + end + + context 'explicitly disabled' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + stub_gitlab_rb( + package: { + detect_init: false + } + ) + end + + it 'does not run any recipe' do + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_upstart') + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_systemd') + expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_sysvinit') + + chef_run + end + + it 'informs the user about skipping init detection' do + expect(Chef::Log).to receive(:info).with('Skipped selecting an init system because it was explicitly disabled') + + chef_run + end + end +end diff --git a/spec/chef/cookbooks/package/recipes/runit_systemd_spec.rb b/spec/chef/cookbooks/package/recipes/runit_systemd_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL3JlY2lwZXMvcnVuaXRfc3lzdGVtZF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/package/recipes/runit_systemd_spec.rb @@ -0,0 +1,92 @@ +require 'chef_helper' + +RSpec.describe 'package::runit_systemd' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config', 'package::runit_systemd') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'setting TasksMax value' do + context 'when systemd version < 227' do + before do + allow(SystemdHelper).to receive(:systemd_version).and_return(200) + end + + it 'does not include TasksMax setting in unit file' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service') + expect(chef_run).not_to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('TasksMax=4915') + end + end + + context 'when systemd version >= 227' do + before do + allow(SystemdHelper).to receive(:systemd_version).and_return(235) + end + + it 'include TasksMax setting in unit file' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('TasksMax=4915') + end + end + + context 'with user provided value for TasksMax' do + before do + stub_gitlab_rb( + package: { systemd_tasks_max: 10000 } + ) + allow(SystemdHelper).to receive(:systemd_version).and_return(235) + end + + it 'sets correct value for TasksMax in unit file' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('TasksMax=10000') + end + end + end + + describe 'setting WantedBy and After settings for unit file' do + context 'by default' do + it 'uses "multi-user.target" for WantedBy' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('WantedBy=multi-user.target') + end + + it 'uses "multi-user.target" for WantedBy' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('After=multi-user.target') + end + end + + context 'when WantedBy is specified' do + before do + stub_gitlab_rb( + package: { systemd_wanted_by: 'basic.target' } + ) + end + + it 'uses specified value for WantedBy' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('WantedBy=basic.target') + end + + it 'uses value specified for WantedBy for After also' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('After=basic.target') + end + end + + context 'when both WantedBy and After are specified' do + before do + stub_gitlab_rb( + package: { + systemd_wanted_by: 'foo', + systemd_after: 'bar' + } + ) + end + + it 'uses specified value for WantedBy' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('WantedBy=foo') + end + + it 'uses specified value for After, not value given for WantedBy' do + expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('After=bar') + end + end + end +end diff --git a/spec/chef/cookbooks/package/resources/account_spec.rb b/spec/chef/cookbooks/package/resources/account_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL3Jlc291cmNlcy9hY2NvdW50X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/package/resources/account_spec.rb @@ -0,0 +1,25 @@ +require 'chef_helper' + +RSpec.describe 'account' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(account)) + end + + context 'create' do + let(:chef_run) { runner.converge('test_package::account_create') } + + it 'creates user' do + expect(chef_run).to create_user('foobar').with_username('foo') + expect(chef_run).to create_group('foobar').with_group_name('bar') + end + end + + context 'remove' do + let(:chef_run) { runner.converge('test_package::account_remove') } + + it 'remove user' do + expect(chef_run).to remove_user('foo') + expect(chef_run).to remove_group('bar') + end + end +end diff --git a/spec/chef/cookbooks/package/resources/env_dir_spec.rb b/spec/chef/cookbooks/package/resources/env_dir_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL3Jlc291cmNlcy9lbnZfZGlyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/package/resources/env_dir_spec.rb @@ -0,0 +1,26 @@ +require 'chef_helper' + +RSpec.describe 'env_dir' do + let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(env_dir)) } + let(:chef_run) { runner.converge("test_package::env_dir_create") } + + after do + FileUtils.rm_rf('/tmp/env') + end + + it 'creates env directory' do + expect(chef_run).to create_directory('/tmp/env') + end + + it 'creates env variable' do + expect(chef_run).to create_file('/tmp/env/FOO').with_content('Lorem') + expect(chef_run).to create_file('/tmp/env/BAR').with_content('Ipsum') + end + + it 'deletes extraneous files' do + FileUtils.mkdir_p('/tmp/env') + FileUtils.touch('/tmp/env/erase-me') + + expect(chef_run).to delete_file('/tmp/env/erase-me') + end +end diff --git a/spec/chef/cookbooks/package/resources/gitlab_sysctl_spec.rb b/spec/chef/cookbooks/package/resources/gitlab_sysctl_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL3Jlc291cmNlcy9naXRsYWJfc3lzY3RsX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/package/resources/gitlab_sysctl_spec.rb @@ -0,0 +1,56 @@ +require 'chef_helper' + +RSpec.describe 'sysctl' do + let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(gitlab_sysctl)) } + let(:chef_run) { runner.converge("package::sysctl", "test_package::gitlab_sysctl_create") } + let(:conf) { '90-omnibus-gitlab-foo.conf' } + + context 'with default values' do + it 'creates sysctl.d directory for the service' do + expect(chef_run).to create_directory('/etc/sysctl.d').with(mode: '0755', recursive: true) + end + + it 'creates conf file for the service and loads it' do + expect(chef_run).to create_file("create /opt/gitlab/embedded/etc/#{conf} foo").with_content("foo = 15000\n") + + resource = chef_run.file("create /opt/gitlab/embedded/etc/#{conf} foo") + expect(resource).to notify("execute[load sysctl conf foo]").to(:run).delayed + end + + it 'links conf file to /etc/sysctl.d location' do + expect(chef_run).to create_link("/etc/sysctl.d/#{conf}") + end + + it 'loaded the settings' do + resource = chef_run.execute('load sysctl conf foo') + expect(resource.command).to eq("sysctl -e -p /opt/gitlab/embedded/etc/#{conf}") + expect(resource).to do_nothing + end + end + + context 'when modify_kernel_parameters is false' do + let(:chef_run) { runner.converge("gitlab::config", "package::sysctl", "test_package::gitlab_sysctl_create") } + + before do + allow(Gitlab).to receive(:[]).and_call_original + + stub_gitlab_rb( + package: { + modify_kernel_parameters: false + } + ) + end + + it 'do not create sysctl.d directory' do + expect(chef_run).not_to create_directory('/etc/sysctl.d') + end + + it 'does not create conf file' do + expect(chef_run).not_to create_file("create /opt/gitlab/embedded/etc/#{conf} foo") + end + + it 'do not link conf file to /etc/sysctl.d location' do + expect(chef_run).not_to create_link("/etc/sysctl.d/#{conf}") + end + end +end diff --git a/spec/chef/cookbooks/package/resources/storage_directory_spec.rb b/spec/chef/cookbooks/package/resources/storage_directory_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL3Jlc291cmNlcy9zdG9yYWdlX2RpcmVjdG9yeV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/package/resources/storage_directory_spec.rb @@ -0,0 +1,22 @@ +require 'chef_helper' + +RSpec.describe 'storage_directory' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(storage_directory)) do |node| + node.normal['gitlab']['manage-storage-directories']['enable'] = true + end + end + let(:chef_run) { runner.converge("test_package::storage_directory_create") } + + it 'executes correct ruby block' do + allow_any_instance_of(StorageDirectoryHelper).to receive(:ensure_directory_exists).and_return(true) + allow_any_instance_of(StorageDirectoryHelper).to receive(:ensure_permissions_set).and_return(true) + allow_any_instance_of(StorageDirectoryHelper).to receive(:validate!).and_return(true) + + expect(chef_run).to run_ruby_block('directory resource: /tmp/bar') + expect_any_instance_of(StorageDirectoryHelper).to receive(:ensure_directory_exists) + + ruby_block = chef_run.ruby_block('directory resource: /tmp/bar') + ruby_block.block.call + end +end diff --git a/spec/chef/cookbooks/package/resources/templatesymlink_spec.rb b/spec/chef/cookbooks/package/resources/templatesymlink_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL3Jlc291cmNlcy90ZW1wbGF0ZXN5bWxpbmtfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/resources/templatesymlink_spec.rb @@ -0,0 +1,26 @@ +require 'chef_helper' + +RSpec.describe 'templatesymlink' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)) + end + + context 'create' do + let(:chef_run) { runner.converge('test_package::templatesymlink_create') } + + it 'creates symlinks' do + expect(chef_run).to create_link('/opt/gitlab/embedded/service/gitlab-rails/config/database.yml') + end + + it 'populates conf file' do + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/database.yml').with_content('value: 500') + end + end + context 'delete' do + let(:chef_run) { runner.converge('test_package::templatesymlink_delete') } + + it 'deletes symlinks' do + expect(chef_run).to delete_link('/opt/gitlab/embedded/service/gitlab-rails/config/database.yml') + end + end +end diff --git a/spec/chef/cookbooks/package/resources/version_file_spec.rb b/spec/chef/cookbooks/package/resources/version_file_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL3Jlc291cmNlcy92ZXJzaW9uX2ZpbGVfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/package/resources/version_file_spec.rb @@ -0,0 +1,24 @@ +require 'chef_helper' + +version_file_path = '/tmp/VERSION_TEST' +version_check_cmd = 'echo 1.0.0-test' + +# Collect version as the custom resource would +version = VersionHelper.version(version_check_cmd) + +RSpec.describe 'version_file' do + let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(version_file)) } + let(:chef_run) { runner.converge("test_package::version_file_create") } + + after do + FileUtils.rm_rf(version_file_path) + end + + it 'creates version file' do + expect(chef_run).to create_file(version_file_path).with_content(version) + end + + it 'restarts service' do + expect(chef_run.version_file('Test version file creation')).to notify('runit_service[foo]').to(:hup) + end +end diff --git a/spec/chef/cookbooks/patroni/libraries/patroni_helper_spec.rb b/spec/chef/cookbooks/patroni/libraries/patroni_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYXRyb25pL2xpYnJhcmllcy9wYXRyb25pX2hlbHBlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/patroni/libraries/patroni_helper_spec.rb @@ -0,0 +1,144 @@ +require 'chef_helper' + +RSpec.describe PatroniHelper do + let(:chef_run) do + ChefSpec::SoloRunner.new(step_into: %w(patroni)).converge('gitlab-ee::default') + end + + subject(:helper) { PatroniHelper.new(chef_run.node) } + let(:pg_helper) { PgHelper.new(chef_run.node) } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe '#ctl_command' do + it 'returns a full path to the ctl_command' do + expect(helper.ctl_command).to eq('/opt/gitlab/embedded/bin/patronictl') + end + end + + describe '#bootstrapped?' do + before do + allow(File).to receive(:exist?).and_call_original + end + + it 'returns true when patroni.dynamic.json exists in postgresql data directory' do + allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/patroni.dynamic.json').and_return(true) + + expect(helper.bootstrapped?).to eq(true) + end + + it 'returns false when patroni.dynamic.json does not exist in postgresql data directory' do + allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/patroni.dynamic.json').and_return(false) + + expect(helper.bootstrapped?).to eq(false) + end + end + + describe '#dynamic_settings' do + it 'returns a hash with required keys' do + expected_root_keys = PatroniHelper::DCS_ATTRIBUTES + %w[postgresql slots] + + expect(helper.dynamic_settings(pg_helper).keys).to match_array(expected_root_keys) + end + + context 'with standby cluster enabled' do + it 'includes standby cluster attributes' do + stub_gitlab_rb( + patroni: { + enable: true, + standby_cluster: { + enable: true + } + } + ) + + expected_root_keys = PatroniHelper::DCS_ATTRIBUTES + %w[postgresql slots standby_cluster] + + expect(helper.dynamic_settings(pg_helper).keys).to match_array(expected_root_keys) + end + end + end + + describe '#node_status' do + context 'when Patroni service is down' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('patroni').and_return(false) + end + + it 'returns not running' do + expect(helper.node_status).to eq 'not running' + end + end + + context 'when Patroni service is up' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('patroni').and_return(true) + + result = spy('shellout') + allow(helper).to receive(:do_shell_out).and_return(result) + allow(result).to receive(:stdout).and_return(' running') + end + + it 'returns running current node state' do + expect(helper.node_status).to eq 'running' + end + end + end + + describe '#public_attributes' do + context 'when patroni is enabled' do + it 'returns a hash with required keys' do + stub_gitlab_rb( + patroni: { + enable: true + } + ) + + expected_patroni_keys = %w(config_dir data_dir log_dir api_address) + + expect(helper.public_attributes.keys).to match_array('patroni') + expect(helper.public_attributes['patroni'].keys).to match_array(expected_patroni_keys) + end + end + + context 'when patroni is enabled with tls api' do + it 'returns a hash with required keys' do + stub_gitlab_rb( + patroni: { + enable: true, + tls_certificate_file: '/path/to/crt.pem', + tls_key_file: '/path/to/key.pem', + tls_key_password: 'fakepassword', + tls_ca_file: '/path/to/ca.pem', + tls_ciphers: 'CIPHERS LIST', + tls_client_mode: 'optional', + tls_client_certificate_file: '/path/to/client.pem', + tls_client_key_file: '/path/to/client.key' + } + ) + + expected_tls_cfg = { + 'tls_verify' => true, + 'ca_file' => '/path/to/ca.pem', + 'verify_client' => true, + 'client_cert' => '/path/to/client.pem', + 'client_key' => '/path/to/client.key' + } + + expect(helper.public_attributes.keys).to match_array('patroni') + expect(helper.public_attributes['patroni']).to include(expected_tls_cfg) + expect(helper.public_attributes['patroni']['api_address']).to start_with('https://') + end + end + + context 'when patroni is disabled' do + it 'returns an empty hash' do + expect(helper.public_attributes).to be_empty + end + end + end +end diff --git a/spec/chef/cookbooks/patroni/recipes/patroni_spec.rb b/spec/chef/cookbooks/patroni/recipes/patroni_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wYXRyb25pL3JlY2lwZXMvcGF0cm9uaV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/patroni/recipes/patroni_spec.rb @@ -0,0 +1,734 @@ +require 'chef_helper' +require 'yaml' + +RSpec.describe 'patroni cookbook' do + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + let(:chef_run) do + ChefSpec::SoloRunner.new(step_into: %w(database_objects)).converge('gitlab-ee::default') + end + + it 'should be disabled by default' do + expect(chef_run).to include_recipe('patroni::disable') + end + + context 'when postgres_role is enabled' do + before do + stub_gitlab_rb(roles: %w(postgres_role)) + end + + it 'should be disabled' do + expect(chef_run).to include_recipe('patroni::disable') + end + end + + context 'when patroni_role is configured' do + before do + stub_gitlab_rb(roles: %w(patroni_role)) + end + + it 'should be enabled' do + expect(chef_run).to include_recipe('patroni::enable') + end + end + + context 'when patroni_role and postgres_role are configured' do + before do + stub_gitlab_rb(roles: %w(postgres_role patroni_role)) + end + + it 'should be enabled' do + expect(chef_run).to include_recipe('patroni::enable') + end + end + + context 'when enabled with default config' do + before do + stub_gitlab_rb( + roles: %w(patroni_role), + postgresql: { + pgbouncer_user_password: '' + } + ) + allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:bootstrapped?).and_return(false) + allow_any_instance_of(PgHelper).to receive(:is_replica?).and_return(false) + end + + let(:default_patroni_config_pg13) do + { + name: 'fauxhai.local', + scope: 'postgresql-ha', + log: { + level: 'INFO' + }, + consul: { + url: 'http://127.0.0.1:8500', + service_check_interval: '10s', + register_service: true, + checks: [], + }, + postgresql: { + bin_dir: '/opt/gitlab/embedded/bin', + data_dir: '/var/opt/gitlab/postgresql/data', + config_dir: '/var/opt/gitlab/postgresql/data', + listen: :'5432', + connect_address: "#{Patroni.private_ipv4}:5432", + use_unix_socket: true, + parameters: { + unix_socket_directories: '/var/opt/gitlab/postgresql' + }, + authentication: { + superuser: { + username: 'gitlab-psql' + }, + replication: { + username: 'gitlab_replicator' + }, + }, + remove_data_directory_on_diverged_timelines: false, + remove_data_directory_on_rewind_failure: false, + basebackup: [ + 'no-password' + ], + }, + bootstrap: { + dcs: { + loop_wait: 10, + ttl: 30, + retry_timeout: 10, + maximum_lag_on_failover: 1_048_576, + max_timelines_history: 0, + master_start_timeout: 300, + postgresql: { + use_pg_rewind: true, + use_slots: true, + parameters: { + wal_level: 'replica', + hot_standby: 'on', + wal_keep_size: 160, + max_replication_slots: 5, + max_connections: 200, + max_locks_per_transaction: 128, + max_worker_processes: 8, + max_wal_senders: 5, + checkpoint_timeout: 30, + max_prepared_transactions: 0, + track_commit_timestamp: 'off', + wal_log_hints: 'off' + }, + }, + slots: {}, + }, + method: 'gitlab_ctl', + gitlab_ctl: { + command: '/opt/gitlab/bin/gitlab-ctl patroni bootstrap --srcdir=/var/opt/gitlab/patroni/data' + } + }, + restapi: { + listen: :'8008', + connect_address: "#{Patroni.private_ipv4}:8008", + allowlist_include_members: false + }, + } + end + + let(:default_patroni_config) do + { + name: 'fauxhai.local', + scope: 'postgresql-ha', + log: { + level: 'INFO' + }, + consul: { + url: 'http://127.0.0.1:8500', + service_check_interval: '10s', + register_service: true, + checks: [], + }, + postgresql: { + bin_dir: '/opt/gitlab/embedded/bin', + data_dir: '/var/opt/gitlab/postgresql/data', + config_dir: '/var/opt/gitlab/postgresql/data', + listen: :'5432', + connect_address: "#{Patroni.private_ipv4}:5432", + use_unix_socket: true, + parameters: { + unix_socket_directories: '/var/opt/gitlab/postgresql' + }, + authentication: { + superuser: { + username: 'gitlab-psql' + }, + replication: { + username: 'gitlab_replicator' + }, + }, + remove_data_directory_on_diverged_timelines: false, + remove_data_directory_on_rewind_failure: false, + basebackup: [ + 'no-password' + ], + }, + bootstrap: { + dcs: { + loop_wait: 10, + ttl: 30, + retry_timeout: 10, + maximum_lag_on_failover: 1_048_576, + max_timelines_history: 0, + master_start_timeout: 300, + postgresql: { + use_pg_rewind: true, + use_slots: true, + parameters: { + wal_level: 'replica', + hot_standby: 'on', + wal_keep_segments: 10, + max_replication_slots: 5, + max_connections: 200, + max_locks_per_transaction: 128, + max_worker_processes: 8, + max_wal_senders: 5, + checkpoint_timeout: 30, + max_prepared_transactions: 0, + track_commit_timestamp: 'off', + wal_log_hints: 'off' + }, + }, + slots: {}, + }, + method: 'gitlab_ctl', + gitlab_ctl: { + command: '/opt/gitlab/bin/gitlab-ctl patroni bootstrap --srcdir=/var/opt/gitlab/patroni/data' + } + }, + restapi: { + listen: :'8008', + connect_address: "#{Patroni.private_ipv4}:8008", + allowlist_include_members: false + }, + } + end + + it 'should enable patroni service and disable postgresql runit service' do + expect(chef_run).to enable_runit_service('patroni') + expect(chef_run).to disable_runit_service('postgresql') + end + + it 'should notify patroni service to hup' do + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('patroni').and_return(true) + + expect(chef_run.template('/var/opt/gitlab/patroni/patroni.yaml')).to notify('runit_service[patroni]').to(:hup) + end + + it 'should skip standalone postgresql configuration' do + expect(chef_run).to create_postgresql_config('gitlab') + expect(chef_run.postgresql_config('gitlab')).not_to notify('execute[start postgresql]').to(:run) + expect(chef_run).not_to run_execute(/(start|reload) postgresql/) + end + + it 'should create database objects (roles, databses, extension)' do + expect(chef_run).not_to run_execute('/opt/gitlab/embedded/bin/initdb -D /var/opt/gitlab/postgresql/data -E UTF8') + expect(chef_run).to create_postgresql_user('gitlab') + expect(chef_run).to create_postgresql_user('gitlab_replicator') + expect(chef_run).to create_pgbouncer_user('rails') + expect(chef_run).to create_postgresql_database('gitlabhq_production') + expect(chef_run).to enable_postgresql_extension('pg_trgm') + expect(chef_run).to enable_postgresql_extension('btree_gist') + end + + it 'should create patroni configuration file for PostgreSQL 12' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| + expect(YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true)).to eq(default_patroni_config) + } + end + + it 'should create patroni configuration file for PostgreSQL 13' do + allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('13.0')) + allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('13.0')) + + expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| + expect(YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true)).to eq(default_patroni_config_pg13) + } + end + end + + context 'when enabled with specific config' do + before do + stub_gitlab_rb( + roles: %w(postgres_role), + postgresql: { + username: 'test_psql_user', + sql_user: 'test_sql_user', + sql_user_password: 'dbda601b8d4dc3d1697ef84dbbb8e61b', + sql_replication_user: 'test_sql_replication_user', + sql_replication_password: '48e84afb4b268128ac14f7c66fc7af42', + pgbouncer_user: 'test_pgbouncer_user', + pgbouncer_user_password: '2bc94731612abb74aea7805a41dfcb09', + connect_port: 15432, + }, + patroni: { + enable: true, + scope: 'test-scope', + name: 'test-node-name', + log_level: 'DEBUG', + loop_wait: 20, + ttl: 60, + master_start_timeout: 600, + use_slots: false, + use_pg_rewind: true, + connect_address: '1.2.3.4', + connect_port: 18008, + username: 'gitlab', + password: 'restapipassword', + replication_password: 'fakepassword', + allowlist: ['1.2.3.4/32', '127.0.0.1/32'], + allowlist_include_members: false, + remove_data_directory_on_diverged_timelines: true, + remove_data_directory_on_rewind_failure: true, + replication_slots: { + 'geo_secondary' => { 'type' => 'physical' } + }, + consul: { + service_check_interval: '20s' + }, + postgresql: { + wal_keep_segments: 16, + max_wal_senders: 4, + max_replication_slots: 4 + }, + tags: { + nofailover: true + }, + callbacks: { + on_role_change: "/patroni/scripts/post-failover-maintenance.sh" + }, + recovery_conf: { + restore_command: "/opt/wal-g/bin/wal-g wal-fetch %f %p" + } + } + ) + end + + it 'should be reflected in patroni configuration file' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg).to include( + name: 'test-node-name', + scope: 'test-scope', + log: { + level: 'DEBUG' + }, + tags: { + nofailover: true + } + ) + expect(cfg[:consul][:service_check_interval]).to eq('20s') + expect(cfg[:postgresql][:connect_address]).to eq('1.2.3.4:15432') + expect(cfg[:postgresql][:authentication]).to eq( + superuser: { + username: 'test_psql_user' + }, + replication: { + username: 'test_sql_replication_user', + password: 'fakepassword' + } + ) + expect(cfg[:postgresql][:callbacks]).to eq( + on_role_change: "/patroni/scripts/post-failover-maintenance.sh" + ) + expect(cfg[:postgresql][:recovery_conf]).to eq( + restore_command: "/opt/wal-g/bin/wal-g wal-fetch %f %p" + ) + expect(cfg[:restapi]).to include( + connect_address: '1.2.3.4:18008', + authentication: { + username: 'gitlab', + password: 'restapipassword' + }, + allowlist: ['1.2.3.4/32', '127.0.0.1/32'], + allowlist_include_members: false + ) + expect(cfg[:bootstrap][:dcs]).to include( + loop_wait: 20, + ttl: 60, + master_start_timeout: 600, + slots: { + geo_secondary: { type: 'physical' } + } + ) + expect(cfg[:bootstrap][:dcs][:postgresql]).to include( + use_slots: false, + use_pg_rewind: true + ) + expect(cfg[:bootstrap][:dcs][:postgresql][:parameters]).to include( + wal_keep_segments: 16, + max_wal_senders: 4, + max_replication_slots: 4 + ) + expect(cfg[:postgresql][:remove_data_directory_on_rewind_failure]).to be true + expect(cfg[:postgresql][:remove_data_directory_on_diverged_timelines]).to be true + } + end + + it 'should reflect into dcs config file' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg).to include( + loop_wait: 20, + ttl: 60, + master_start_timeout: 600, + slots: { + geo_secondary: { type: 'physical' } + } + ) + expect(cfg[:postgresql]).to include( + use_slots: false, + use_pg_rewind: true + ) + expect(cfg[:postgresql][:parameters]).to include( + wal_keep_segments: 16, + max_wal_senders: 4, + max_replication_slots: 4 + ) + } + end + end + + context 'when standby cluster is enabled' do + before do + stub_gitlab_rb( + roles: %w(postgres_role), + patroni: { + enable: true, + use_pg_rewind: true, + replication_password: 'fakepassword', + standby_cluster: { + enable: true, + host: '1.2.3.4', + port: 5432, + primary_slot_name: 'geo_secondary' + } + }, + postgresql: { + sql_user_password: 'a4125c87ce2572ce271cd77e0de9a0ad', + sql_replication_password: 'e64b415e9b9a34ac7ac6e53ae16ccacb', + md5_auth_cidr_addresses: '1.2.3.4/32' + } + ) + end + + it 'should be reflected in patroni configuration file' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg[:postgresql][:authentication]).to include( + replication: { + username: 'gitlab_replicator', + password: 'fakepassword' + } + ) + expect(cfg[:bootstrap][:dcs]).to include( + standby_cluster: { + host: '1.2.3.4', + port: 5432, + primary_slot_name: 'geo_secondary' + } + ) + expect(cfg[:bootstrap][:dcs][:postgresql]).to include( + use_pg_rewind: true + ) + } + end + + it 'should reflect into dcs config file' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg).to include( + standby_cluster: { + host: '1.2.3.4', + port: 5432, + primary_slot_name: 'geo_secondary' + } + ) + expect(cfg[:postgresql]).to include( + use_pg_rewind: true + ) + } + end + end + + context 'when building a cluster' do + before do + stub_gitlab_rb( + roles: %w(postgres_role), + patroni: { + enable: true + } + ) + end + + context 'from scratch' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_return(false) + end + + it 'should enable patroni service and disable postgresql runit service' do + expect(chef_run).to enable_runit_service('patroni') + expect(chef_run).to disable_runit_service('postgresql') + end + end + + context 'converting a standalone instance to a cluster member' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_return(true) + allow_any_instance_of(PatroniHelper).to receive(:node_status).and_return('running') + end + + it 'should signal to node to restart postgresql and disable its runit service' do + expect(chef_run).to enable_runit_service('patroni') + expect(chef_run).to disable_runit_service('postgresql') + expect(chef_run).to run_execute('signal to restart postgresql') + end + end + + context 'on a replica' do + before do + allow_any_instance_of(PgHelper).to receive(:replica?).and_return(true) + end + + it 'should not create database objects' do + expect(chef_run).not_to create_postgresql_user('gitlab') + expect(chef_run).not_to create_postgresql_user('gitlab_replicator') + expect(chef_run).not_to create_pgbouncer_user('patroni') + expect(chef_run).not_to run_execute('create gitlabhq_production database') + expect(chef_run).not_to enable_postgresql_extension('pg_trgm') + expect(chef_run).not_to enable_postgresql_extension('btree_gist') + end + end + end + + context 'postgresql dynamic configuration' do + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'with no explicit override' do + before do + stub_gitlab_rb( + roles: %w(postgres_role), + patroni: { + enable: true + } + ) + end + + it 'should use default values from postgresql cookbook and handle corner cases' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg[:postgresql][:use_pg_rewind]).to be(true) + expect(cfg[:postgresql][:parameters]).to include( + max_connections: 200, + max_locks_per_transaction: 128, + max_worker_processes: 8, + max_prepared_transactions: 0, + track_commit_timestamp: 'off', + wal_log_hints: 'off', + max_wal_senders: 5, + max_replication_slots: 5, + wal_keep_segments: 10, + checkpoint_timeout: 30 + ) + } + end + end + + context 'with no explicit override and non-default postgresql settings' do + before do + stub_gitlab_rb( + roles: %w(postgres_role), + patroni: { + enable: true, + }, + postgresql: { + max_connections: 123, + max_locks_per_transaction: 321, + max_worker_processes: 12, + wal_log_hints: 'foo', + max_wal_senders: 11, + max_replication_slots: 13, + } + ) + end + + it 'should use default values from postgresql cookbook' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg[:postgresql][:use_pg_rewind]).to be(true) + expect(cfg[:postgresql][:parameters]).to include( + max_connections: 123, + max_locks_per_transaction: 321, + max_worker_processes: 12, + max_prepared_transactions: 0, + track_commit_timestamp: 'off', + wal_log_hints: 'foo', + max_wal_senders: 11, + max_replication_slots: 13, + wal_keep_segments: 10, + checkpoint_timeout: 30 + ) + } + end + end + + context 'with explicit override' do + before do + stub_gitlab_rb( + roles: %w(postgres_role), + patroni: { + enable: true, + postgresql: { + max_connections: 100, + max_locks_per_transaction: 64, + max_worker_processes: 4, + wal_log_hints: 'on', + max_wal_senders: 0, + max_replication_slots: 0, + checkpoint_timeout: '5min' + } + } + ) + end + + it 'should use default values from postgresql cookbook' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg[:postgresql][:use_pg_rewind]).to be(true) + expect(cfg[:postgresql][:parameters]).to include( + max_connections: 100, + max_locks_per_transaction: 64, + max_worker_processes: 4, + max_wal_senders: 0, + max_replication_slots: 0, + wal_log_hints: 'on' + ) + } + end + end + + context 'when pg_rewind is enabled' do + before do + stub_gitlab_rb( + roles: %w(postgres_role), + patroni: { + enable: true, + use_pg_rewind: true, + remove_data_directory_on_diverged_timelines: true, + remove_data_directory_on_rewind_failure: true + } + ) + end + + it 'should use default values from postgresql cookbook' do + expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg[:postgresql][:use_pg_rewind]).to be(true) + expect(cfg[:postgresql][:parameters]).to include( + max_connections: 200, + max_locks_per_transaction: 128, + max_worker_processes: 8, + wal_log_hints: 'on' + ) + } + end + end + end + + context 'when patroni is enabled but consul is not' do + let(:chef_run) do + converge_config('gitlab-ee::default', is_ee: true) + end + + before do + stub_gitlab_rb( + patroni: { + enable: true + } + ) + end + + it 'expects a warning to be printed' do + chef_run + + expect_logged_warning(/Patroni is enabled but Consul seems to be disabled/) + end + end + + context 'when tls is enabled' do + it 'should only set the path to the tls certificate and key' do + stub_gitlab_rb( + roles: %w(postgres_role), + patroni: { + enable: true, + tls_certificate_file: '/path/to/crt.pem', + tls_key_file: '/path/to/key.pem' + } + ) + + expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg[:restapi][:certfile]).to eq('/path/to/crt.pem') + expect(cfg[:restapi][:keyfile]).to eq('/path/to/key.pem') + expect(cfg[:restapi][:keyfile_password]).to be(nil) + expect(cfg[:restapi][:cafile]).to be(nil) + expect(cfg[:restapi][:ciphers]).to be(nil) + expect(cfg[:restapi][:verify_client]).to be(nil) + expect(cfg[:ctl]).to be(nil) + } + end + + it 'should set all available tls configuration including tls certificate and key paths' do + stub_gitlab_rb( + roles: %w(postgres_role), + patroni: { + enable: true, + tls_certificate_file: '/path/to/crt.pem', + tls_key_file: '/path/to/key.pem', + tls_key_password: 'fakepassword', + tls_ca_file: '/path/to/ca.pem', + tls_ciphers: 'CIPHERS LIST', + tls_client_mode: 'optional', + tls_client_certificate_file: '/path/to/client.pem', + tls_client_key_file: '/path/to/client.key' + } + ) + + expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| + cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) + + expect(cfg[:restapi][:certfile]).to eq('/path/to/crt.pem') + expect(cfg[:restapi][:keyfile]).to eq('/path/to/key.pem') + expect(cfg[:restapi][:keyfile_password]).to eq('fakepassword') + expect(cfg[:restapi][:cafile]).to eq('/path/to/ca.pem') + expect(cfg[:restapi][:ciphers]).to eq('CIPHERS LIST') + expect(cfg[:restapi][:verify_client]).to eq('optional') + expect(cfg[:ctl][:insecure]).to eq(false) + expect(cfg[:ctl][:certfile]).to eq('/path/to/client.pem') + expect(cfg[:ctl][:keyfile]).to eq('/path/to/client.key') + } + end + end +end diff --git a/spec/chef/cookbooks/pgbouncer/libraries/pgbouncer_helper_spec.rb b/spec/chef/cookbooks/pgbouncer/libraries/pgbouncer_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wZ2JvdW5jZXIvbGlicmFyaWVzL3BnYm91bmNlcl9oZWxwZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/pgbouncer/libraries/pgbouncer_helper_spec.rb @@ -0,0 +1,165 @@ +require 'chef_helper' + +RSpec.describe PgbouncerHelper do + let(:chef_run) { converge_config(is_ee: true) } + subject { described_class.new(chef_run.node) } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe '#running?' do + cached(:chef_run) { converge_config(is_ee: true) } + + it 'returns true when pgbouncer is running' do + stub_service_success_status('pgbouncer', true) + + expect(subject.running?).to be_truthy + end + + it 'returns false when pgbouncer is not running' do + stub_service_success_status('pgbouncer', false) + + expect(subject.running?).to be_falsey + end + end + + describe '#pg_auth_type_prefix' do + using RSpec::Parameterized::TableSyntax + where(:type, :prefix) do + 'md5' | 'md5' + 'scram-sha-256' | 'SCRAM-SHA-256$' + 'MD5' | 'md5' + 'SCRAM-SHA-256' | 'SCRAM-SHA-256$' + 'plain' | nil + 'ScRaM-ShA-256' | 'SCRAM-SHA-256$' + end + + with_them do + it 'responds to default values' do + expect(subject.pg_auth_type_prefix(type)).to eq(prefix) + end + end + end + + describe '#pgbouncer_admin_config' do + context 'by default' do + it 'by default' do + expect(subject.pgbouncer_admin_config).to eq('user=pgbouncer dbname=pgbouncer sslmode=disable port=6432 host=/var/opt/gitlab/pgbouncer') + end + end + + context 'with custom sttings' do + before do + stub_gitlab_rb( + postgresql: { + pgbouncer_user: 'tester', + }, + pgbouncer: { + listen_port: 1234, + data_directory: '/tmp' + } + ) + end + + it 'uses custom settings' do + expect(subject.pgbouncer_admin_config).to eq('user=tester dbname=pgbouncer sslmode=disable port=1234 host=/tmp') + end + end + end + + describe '#pg_auth_users' do + context 'by default' do + it 'by default' do + expect(subject.pg_auth_users).to be_empty + end + end + + context 'with users' do + before do + stub_gitlab_rb( + pgbouncer: { + users: { + fakeuser: { + password: 'fakepassword' + } + } + } + ) + end + + it 'returns a hash' do + expect(subject.pg_auth_users).to eq 'fakeuser' => { 'password' => 'fakepassword' } + end + end + + context 'with databases' do + before do + stub_gitlab_rb( + pgbouncer: { + databases: { + fakedb: { + user: 'fakeuser', + password: 'fakepassword' + } + } + } + ) + end + + it 'returns a hash' do + expect(subject.pg_auth_users).to eq 'fakeuser' => { 'password' => 'fakepassword' } + end + end + + context 'with both' do + before do + stub_gitlab_rb( + pgbouncer: { + databases: { + fakedb: { + user: 'fakedbuser', + password: 'fakepassword' + } + }, + users: { + fakeusersuser: { + password: 'fakepassword' + } + } + } + ) + end + + it 'should merge both' do + expected = { + 'fakeusersuser' => { 'password' => 'fakepassword' }, 'fakedbuser' => { 'password' => 'fakepassword' } + } + expect(subject.pg_auth_users).to eq expected + end + end + + context 'with multiple databases' do + before do + stub_gitlab_rb( + pgbouncer: { + databases: { + fakedb_one: { + user: 'fakeuser', + password: 'fakepassword' + }, + fakedb_two: { + user: 'fakeuser', + password: 'fakepassword' + } + } + } + ) + end + + it 'should only have one entry' do + expect(subject.pg_auth_users).to eq 'fakeuser' => { 'password' => 'fakepassword' } + end + end + end +end diff --git a/spec/chef/cookbooks/pgbouncer/recipes/pgbouncer_spec.rb b/spec/chef/cookbooks/pgbouncer/recipes/pgbouncer_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wZ2JvdW5jZXIvcmVjaXBlcy9wZ2JvdW5jZXJfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/pgbouncer/recipes/pgbouncer_spec.rb @@ -0,0 +1,395 @@ +# +# Copyright:: Copyright (c) 2017 GitLab Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require 'chef_helper' + +RSpec.describe 'pgbouncer' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + let(:pgbouncer_ini) { '/var/opt/gitlab/pgbouncer/pgbouncer.ini' } + let(:databases_json) { '/var/opt/gitlab/pgbouncer/databases.json' } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'when enabled' do + before do + stub_gitlab_rb( + pgbouncer: { + enable: true, + databases: { + gitlabhq_production: { + host: '1.2.3.4' + } + } + }, + postgresql: { + pgbouncer_user: 'fakeuser', + pgbouncer_user_password: 'fakeuserpassword' + } + ) + end + + it 'includes the pgbouncer recipe' do + expect(chef_run).to include_recipe('pgbouncer::enable') + end + + it 'includes the postgresql user recipe' do + expect(chef_run).to include_recipe('postgresql::user') + end + + it_behaves_like 'enabled runit service', 'pgbouncer', 'root', 'root' + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/pgbouncer/env').with_variables(default_vars) + end + + it 'creates the appropriate directories' do + expect(chef_run).to create_directory('/var/log/gitlab/pgbouncer') + expect(chef_run).to create_directory('/var/opt/gitlab/pgbouncer') + end + + it 'installs pgbouncer.ini with default values' do + # Default values are pulled from: + # https://github.com/pgbouncer/pgbouncer/blob/6ef66f0139b9c8a5c0747f2a6157d008b87bf0c5/etc/pgbouncer.ini + expect(chef_run).to render_file(pgbouncer_ini).with_content { |content| + expect(content).to match(/^listen_addr = 0\.0\.0\.0$/) + expect(content).to match(/^listen_port = 6432$/) + expect(content).to match(/^pool_mode = transaction$/) + expect(content).to match(/^server_reset_query = DISCARD ALL$/) + expect(content).to match(/^application_name_add_host = 1$/) + expect(content).to match(/^max_client_conn = 2048$/) + expect(content).to match(/^default_pool_size = 100$/) + expect(content).to match(/^min_pool_size = 0$/) + expect(content).to match(/^reserve_pool_size = 5$/) + expect(content).to match(/^reserve_pool_timeout = 5.0$/) + expect(content).to match(/^server_round_robin = 0$/) + expect(content).to match(/^auth_type = md5$/) + expect(content).to match(/^log_connections = 0/) + expect(content).to match(/^server_idle_timeout = 30.0$/) + expect(content).to match(/^dns_max_ttl = 15.0$/) + expect(content).to match(/^dns_zone_check_period = 0$/) + expect(content).to match(/^dns_nxdomain_ttl = 15.0$/) + expect(content).to match(%r{^auth_file = /var/opt/gitlab/pgbouncer/pg_auth$}) + expect(content).to match(/^admin_users = gitlab-psql, postgres, pgbouncer$/) + expect(content).to match(/^stats_users = gitlab-psql, postgres, pgbouncer$/) + expect(content).to match(/^ignore_startup_parameters = extra_float_digits$/) + expect(content).to match(%r{^unix_socket_dir = /var/opt/gitlab/pgbouncer$}) + expect(content).to match(%r{^%include /var/opt/gitlab/pgbouncer/databases.ini}) + expect(content).to match(/^unix_socket_mode = 0777$/) + expect(content).to match(/^client_tls_sslmode = disable$/) + expect(content).to match(/^client_tls_protocols = all$/) + expect(content).to match(/^client_tls_dheparams = auto$/) + expect(content).to match(/^client_tls_ecdhcurve = auto$/) + expect(content).to match(/^server_tls_sslmode = disable$/) + expect(content).to match(/^server_tls_protocols = all$/) + expect(content).to match(/^server_tls_ciphers = fast$/) + expect(content).to match(/^server_reset_query_always = 0$/) + expect(content).to match(/^server_check_query = select 1$/) + expect(content).to match(/^server_check_delay = 30$/) + expect(content).to match(/^syslog = 0$/) + expect(content).to match(/^syslog_facility = daemon$/) + expect(content).to match(/^syslog_ident = pgbouncer$/) + expect(content).to match(/^log_disconnections = 1$/) + expect(content).to match(/^log_pooler_errors = 1$/) + expect(content).to match(/^stats_period = 60$/) + expect(content).to match(/^verbose = 0$/) + expect(content).to match(/^server_lifetime = 3600$/) + expect(content).to match(/^server_connect_timeout = 15$/) + expect(content).to match(/^server_login_retry = 15$/) + expect(content).to match(/^query_timeout = 0$/) + expect(content).to match(/^query_wait_timeout = 120$/) + expect(content).to match(/^client_idle_timeout = 0$/) + expect(content).to match(/^client_login_timeout = 60$/) + expect(content).to match(/^autodb_idle_timeout = 3600$/) + expect(content).to match(/^suspend_timeout = 10$/) + expect(content).to match(/^idle_transaction_timeout = 0$/) + expect(content).to match(/^pkt_buf = 4096$/) + expect(content).to match(/^listen_backlog = 128$/) + expect(content).to match(/^sbuf_loopcnt = 5$/) + expect(content).to match(/^max_packet_size = 2147483647$/) + expect(content).to match(/^tcp_defer_accept = 0$/) + expect(content).to match(/^tcp_socket_buffer = 0$/) + expect(content).to match(/^tcp_keepalive = 1$/) + expect(content).to match(/^tcp_keepcnt = 0$/) + expect(content).to match(/^tcp_keepidle = 0$/) + expect(content).to match(/^tcp_keepintvl = 0$/) + expect(content).to match(/^disable_pqexec = 0$/) + expect(content).not_to match(/^logfile =/) + expect(content).not_to match(/^pidfile =/) + expect(content).not_to match(%r{^unix_socket_group =}) + expect(content).not_to match(%r{^client_tls_ca_file =}) + expect(content).not_to match(%r{^client_tls_cert_file =}) + expect(content).not_to match(%r{^server_tls_ca_file =}) + expect(content).not_to match(%r{^server_tls_key_file =}) + expect(content).not_to match(%r{^server_tls_cert_file =}) + expect(content).not_to match(%r{^max_db_connections =}) + expect(content).not_to match(%r{^max_user_connections =}) + } + end + + context 'pgbouncer.ini template changes' do + let(:template) { chef_run.template(pgbouncer_ini) } + + it 'stores the socket directory in a different location when set' do + stub_gitlab_rb( + pgbouncer: { + enable: true, + unix_socket_dir: '/fake/dir', + unix_socket_group: 'fakegroup', + client_tls_ca_file: '/fakecafile', + client_tls_cert_file: '/fakecertfile', + server_tls_ca_file: '/fakeservercafile', + server_tls_key_file: '/fakeserverkeyfile', + server_tls_cert_file: '/fakeservercertfile', + max_db_connections: 99999, + max_user_connections: 88888 + } + ) + expect(chef_run).to render_file(pgbouncer_ini).with_content { |content| + expect(content).to match(%r{^unix_socket_dir = /fake/dir$}) + expect(content).to match(%r{^unix_socket_group = fakegroup$}) + expect(content).to match(%r{^client_tls_ca_file = /fakecafile$}) + expect(content).to match(%r{^client_tls_cert_file = /fakecertfile$}) + expect(content).to match(%r{^server_tls_ca_file = /fakeservercafile$}) + expect(content).to match(%r{^server_tls_key_file = /fakeserverkeyfile$}) + expect(content).to match(%r{^server_tls_cert_file = /fakeservercertfile$}) + expect(content).to match(%r{^max_db_connections = 99999$}) + expect(content).to match(%r{^max_user_connections = 88888$}) + } + end + + it 'reloads pgbouncer and starts pgbouncer if it is not running' do + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('pgbouncer').and_return(true) + expect(template).to notify('execute[reload pgbouncer]').to(:run).immediately + end + end + + context 'databases.json' do + it 'creates databases.json' do + expect(chef_run).to create_file(databases_json) + .with_content("{\"gitlabhq_production\":{\"host\":\"1.2.3.4\"}}") + .with(user: 'root', group: 'gitlab-psql') + end + + it 'notifies pgb-notify to generate databases.ini' do + json_resource = chef_run.file(databases_json) + expect(json_resource).to notify('execute[generate databases.ini]').to(:run).immediately + end + + it 'does not run pgb-notify when databases.ini exists' do + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with('/var/opt/gitlab/pgbouncer/databases.ini').and_return(true) + expect(chef_run).not_to run_execute('generate databases.ini') + end + + it 'stores in a different location when attribute is set' do + stub_gitlab_rb( + pgbouncer: { + enable: true, + databases_json: '/fakepath/fakedatabases.json' + } + ) + expect(chef_run).to create_file('databases.json') + .with(path: '/fakepath/fakedatabases.json') + end + + it 'changes the user when the attribute is changed' do + stub_gitlab_rb( + pgbouncer: { + enable: true, + databases_ini_user: 'fakeuser' + } + ) + expect(chef_run).to create_file('databases.json') + .with(user: 'fakeuser', group: 'gitlab-psql') + end + end + end + + context 'authentication' do + let(:pg_auth) { '/var/opt/gitlab/pgbouncer/pg_auth' } + + it 'sets up auth_hba when attributes are set' do + stub_gitlab_rb( + { + pgbouncer: { + enable: true, + auth_hba_file: '/fake/hba_file', + auth_query: 'SELECT * FROM FAKETABLE' + } + } + ) + expect(chef_run).to render_file(pgbouncer_ini).with_content { |content| + expect(content).to match(%r{^auth_hba_file = /fake/hba_file$}) + expect(content).to match(/^auth_query = SELECT \* FROM FAKETABLE$/) + } + end + + it 'does not create the user file by default' do + expect(chef_run).not_to render_file(pg_auth) + end + + it 'creates the user file when the attributes are set' do + stub_gitlab_rb( + { + pgbouncer: { + enable: true, + databases: { + gitlabhq_production: { + password: 'fakemd5password', + user: 'fakeuser', + host: '127.0.0.1', + port: 5432 + } + } + } + } + ) + expect(chef_run).to render_file(pg_auth) + .with_content(%r{^"fakeuser" "md5fakemd5password"$}) + end + + it 'creates arbitrary user' do + stub_gitlab_rb( + { + pgbouncer: { + enable: true, + users: { + 'fakeuser': { + 'password': 'fakehash' + } + } + } + } + ) + expect(chef_run).to render_file(pg_auth) + .with_content(%r{^"fakeuser" "md5fakehash"}) + end + + it 'supports SCRAM secrets' do + stub_gitlab_rb( + pgbouncer: { + enable: true, + auth_type: 'scram-sha-256', + users: { + 'fakeuser': { + 'password': 'REALLYFAKEHASH' + } + } + } + ) + expect(chef_run).to render_file(pg_auth) + .with_content(%r{^"fakeuser" "SCRAM-SHA-256\$REALLYFAKEHASH"}) + end + + it 'supports a default auth type' do + stub_gitlab_rb( + pgbouncer: { + enable: true, + auth_type: 'scram-sha-256', + users: { + 'firstfakeuser': { + 'password': 'AREALLYFAKEHASH' + }, + 'secondfakeuser': { + 'password': 'ANOTHERREALLYFAKEHASH' + } + }, + databases: { + fakedb: { + user: 'databasefakeuser', + password: 'DATABASEHASH' + } + } + } + ) + expect(chef_run).to render_file(pg_auth).with_content { |content| + expect(content).to match(%r{^"firstfakeuser" "SCRAM-SHA-256\$AREALLYFAKEHASH"}) + expect(content).to match(%r{^"secondfakeuser" "SCRAM-SHA-256\$ANOTHERREALLYFAKEHASH"}) + expect(content).to match(%r{^"databasefakeuser" "SCRAM-SHA-256\$DATABASEHASH"}) + } + end + + it 'supports per user auth types' do + stub_gitlab_rb( + pgbouncer: { + enable: true, + users: { + 'firstfakeuser': { + 'password': 'AREALLYFAKEHASH' + }, + 'secondfakeuser': { + 'password': 'ANOTHERREALLYFAKEHASH', + 'auth_type': 'scram-sha-256' + } + }, + databases: { + fakedb: { + user: 'databasefakeuser', + auth_type: 'plain', + password: 'DATABASEHASH' + } + } + } + ) + expect(chef_run).to render_file(pg_auth).with_content { |content| + expect(content).to match(%r{^"firstfakeuser" "md5AREALLYFAKEHASH"}) + expect(content).to match(%r{^"secondfakeuser" "SCRAM-SHA-256\$ANOTHERREALLYFAKEHASH"}) + expect(content).to match(%r{^"databasefakeuser" "DATABASEHASH"}) + } + end + + context 'when disabled by default' do + it_behaves_like 'disabled runit service', 'pgbouncer' + + it 'includes the pgbouncer_disable recipe' do + expect(chef_run).to include_recipe('pgbouncer::disable') + end + end + end +end + +RSpec.describe 'gitlab-ee::default' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb( + { + pgbouncer: { + db_user_password: 'fakeuserpassword' + }, + postgresql: { + pgbouncer_user: 'fakeuser', + pgbouncer_user_password: 'fakeuserpassword' + } + } + ) + end + + it 'should create the pgbouncer user on the database' do + expect(chef_run).to include_recipe('pgbouncer::user') + expect(chef_run).to create_pgbouncer_user('rails').with( + password: 'fakeuserpassword' + ) + end +end diff --git a/spec/chef/cookbooks/pgbouncer/recipes/pgbouncer_user_spec.rb b/spec/chef/cookbooks/pgbouncer/recipes/pgbouncer_user_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wZ2JvdW5jZXIvcmVjaXBlcy9wZ2JvdW5jZXJfdXNlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/pgbouncer/recipes/pgbouncer_user_spec.rb @@ -0,0 +1,118 @@ +# +# Copyright:: Copyright (c) 2017 GitLab Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef_helper' + +RSpec.describe 'gitlab-ee::pgbouncer_user' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'create the rails pgbouncer user' do + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb( + { + postgresql: { + enable: true, + pgbouncer_user: 'pgbouncer-rails', + pgbouncer_user_password: 'fakepassword' + } + } + ) + end + + it 'should call pgbouncer_user with the correct values for rails' do + expect(chef_run).to create_pgbouncer_user('rails').with( + database: 'gitlabhq_production', + password: 'fakepassword', + user: 'pgbouncer-rails', + add_auth_function: true + ) + expect(chef_run).not_to create_pgbouncer_user('geo') + end + end + + context 'auth_query is not the default value' do + before do + stub_gitlab_rb( + { + pgbouncer: { + auth_query: 'SELECT * FROM FAKETABLE' + }, + postgresql: { + enable: true, + pgbouncer_user: 'pgbouncer-rails', + pgbouncer_user_password: 'fakepassword' + } + } + ) + end + + it 'should not create the pg_shadow_lookup function' do + expect(chef_run).to create_pgbouncer_user('rails').with( + add_auth_function: false + ) + end + end + + context 'no pgbouncer user' do + before do + stub_gitlab_rb( + { + postgresql: { + enable: true, + }, + geo_postgresql: { + enable: true, + } + } + ) + end + + it 'should not create the pgbouncer user' do + expect(chef_run).not_to create_pgbouncer_user('rails') + expect(chef_run).not_to create_pgbouncer_user('geo') + end + end + + context 'create the geo pgbouncer user' do + before do + stub_gitlab_rb( + { + geo_postgresql: { + enable: true, + pgbouncer_user: 'pgbouncer-geo', + pgbouncer_user_password: 'fakepassword' + } + } + ) + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:user_exists?).with('pgbouncer-geo').and_return(false) + end + + it 'should call pgbouncer_user with the correct values for geo' do + expect(chef_run).to create_pgbouncer_user('geo').with( + database: 'gitlabhq_geo_production', + password: 'fakepassword', + user: 'pgbouncer-geo', + add_auth_function: true + ) + expect(chef_run).not_to create_pgbouncer_user('rails') + end + end +end diff --git a/spec/chef/cookbooks/postgresql/recipes/postgresql_spec.rb b/spec/chef/cookbooks/postgresql/recipes/postgresql_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/postgresql/recipes/postgresql_spec.rb @@ -0,0 +1,880 @@ +require 'chef_helper' + +RSpec.describe 'postgresql' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config database_objects)).converge('gitlab::default') } + let(:postgresql_data_dir) { '/var/opt/gitlab/postgresql/data' } + let(:postgresql_ssl_cert) { File.join(postgresql_data_dir, 'server.crt') } + let(:postgresql_ssl_key) { File.join(postgresql_data_dir, 'server.key') } + let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') } + let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' } + let(:pg_hba_conf) { '/var/opt/gitlab/postgresql/data/pg_hba.conf' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('best_version')) + allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('best_version')) + allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('best_version')) + end + + it 'includes the postgresql::bin recipe' do + expect(chef_run).to include_recipe('postgresql::bin') + end + + it 'includes the postgresql::user recipe' do + expect(chef_run).to include_recipe('postgresql::user') + end + + it 'includes postgresql::sysctl recipe' do + expect(chef_run).to include_recipe('postgresql::sysctl') + end + + it 'does not warn the user that a restart is needed by default' do + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + expect(chef_run).not_to run_ruby_block('warn pending postgresql restart') + end + + it 'includes runtime.conf in postgresql.conf' do + expect(chef_run).to render_file(postgresql_conf) + .with_content(/include 'runtime.conf'/) + end + + context 'with default settings' do + it_behaves_like 'enabled runit service', 'postgresql', 'root', 'root', 'gitlab-psql', 'gitlab-psql' + + context 'when rendering postgresql.conf' do + it 'correctly sets the shared_preload_libraries default setting' do + expect(chef_run.node['postgresql']['shared_preload_libraries']) + .to be_nil + + expect(chef_run).to render_file(postgresql_conf) + .with_content(/shared_preload_libraries = ''/) + end + + it 'disables archive mode' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/archive_mode = off/) + end + + it 'enables SSL by default' do + expect(chef_run.node['postgresql']['ssl']) + .to eq('on') + + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/ssl = on/) + end + + it 'sets the default SSL cipher list' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(%r{ssl_ciphers = 'HIGH:MEDIUM:\+3DES:!aNULL:!SSLv3:!TLSv1'}) + end + + it 'sets the default locations of SSL certificates' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/ssl_cert_file = 'server.crt'/) + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/ssl_key_file = 'server.key'/) + expect(chef_run).to render_file( + postgresql_conf + ).with_content(%r{ssl_ca_file = '/opt/gitlab/embedded/ssl/certs/cacert.pem'}) + end + + it 'leaves synchronous_standby_names empty' do + expect(chef_run.node['postgresql']['synchronous_standby_names']) + .to eq('') + + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/synchronous_standby_names = ''/) + end + + it 'disables wal_log_hints setting' do + expect(chef_run.node['postgresql']['wal_log_hints']).to eq('off') + + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/wal_log_hints = off/) + end + + it 'does not set dynamic_shared_memory_type by default' do + expect(chef_run).not_to render_file( + postgresql_conf + ).with_content(/^dynamic_shared_memory_type = /) + end + + it 'sets the max_locks_per_transaction setting' do + expect(chef_run.node['postgresql']['max_locks_per_transaction']) + .to eq(128) + + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/max_locks_per_transaction = 128/) + end + + it 'does not include gitlab-geo.conf' do + expect(chef_run).to render_file(postgresql_conf) + .with_content { |content| + expect(content).not_to match(/include_if_exists 'gitlab-geo.conf'/) + } + end + end + + it 'generates a self-signed SSL certificate and key' do + stub_gitlab_rb(postgresql: { ssl_cert_file: 'certfile', ssl_key_file: 'keyfile' }) + + absolute_cert_path = File.join(postgresql_data_dir, 'certfile') + absolute_key_path = File.join(postgresql_data_dir, 'keyfile') + + expect(chef_run).to create_file(absolute_cert_path).with( + user: 'gitlab-psql', + group: 'gitlab-psql', + mode: 0400 + ) + + expect(chef_run).to create_file(absolute_key_path).with( + user: 'gitlab-psql', + group: 'gitlab-psql', + mode: 0400 + ) + + expect(chef_run).to render_file(absolute_cert_path) + .with_content(/-----BEGIN CERTIFICATE-----/) + expect(chef_run).to render_file(absolute_key_path) + .with_content(/-----BEGIN RSA PRIVATE KEY-----/) + end + + context 'when rendering runtime.conf' do + it 'correctly sets the log_line_prefix default setting' do + expect(chef_run.node['postgresql']['log_line_prefix']) + .to be_nil + + expect(chef_run).to render_file(runtime_conf) + .with_content(/log_line_prefix = ''/) + end + + it 'does not include log_statement by default' do + expect(chef_run).not_to render_file(runtime_conf) + .with_content(/log_statement = /) + end + + it 'sets max_standby settings' do + expect(chef_run).to render_file( + runtime_conf + ).with_content(/max_standby_archive_delay = 30s/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/max_standby_streaming_delay = 30s/) + end + + it 'sets archive settings' do + expect(chef_run).to render_file( + runtime_conf + ).with_content(/archive_command = ''/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/archive_timeout = 0/) + end + + it 'sets logging directory' do + expect(chef_run.node['postgresql']['log_directory']) + .to eq('/var/log/gitlab/postgresql') + + expect(chef_run).to render_file( + runtime_conf + ).with_content(%r(^log_directory = '/var/log/gitlab/postgresql')) + end + + it 'disables hot_standby_feedback' do + expect(chef_run.node['postgresql']['hot_standby_feedback']) + .to eq('off') + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/hot_standby_feedback = off/) + end + + it 'sets the random_page_cost setting' do + expect(chef_run.node['postgresql']['random_page_cost']) + .to eq(2.0) + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/random_page_cost = 2\.0/) + end + + it 'sets the log_temp_files setting' do + expect(chef_run.node['postgresql']['log_temp_files']) + .to eq(-1) + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/log_temp_files = -1/) + end + + it 'disables the log_checkpoints setting' do + expect(chef_run.node['postgresql']['log_checkpoints']) + .to eq('off') + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/log_checkpoints = off/) + end + + it 'sets idle_in_transaction_session_timeout' do + expect(chef_run.node['postgresql']['idle_in_transaction_session_timeout']) + .to eq('60000') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/idle_in_transaction_session_timeout = 60000/) + end + + it 'sets effective_io_concurrency' do + expect(chef_run.node['postgresql']['effective_io_concurrency']) + .to eq(1) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/effective_io_concurrency = 1/) + end + + it 'sets max_worker_processes' do + expect(chef_run.node['postgresql']['max_worker_processes']) + .to eq(8) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/max_worker_processes = 8/) + end + + it 'sets max_parallel_workers_per_gather' do + expect(chef_run.node['postgresql']['max_parallel_workers_per_gather']) + .to eq(0) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/max_parallel_workers_per_gather = 0/) + end + + it 'sets log_lock_waits' do + expect(chef_run.node['postgresql']['log_lock_waits']) + .to eq(1) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/log_lock_waits = 1/) + end + + it 'sets deadlock_timeout' do + expect(chef_run.node['postgresql']['deadlock_timeout']) + .to eq('5s') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/deadlock_timeout = '5s'/) + end + + it 'disables track_io_timing' do + expect(chef_run.node['postgresql']['track_io_timing']) + .to eq('off') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/track_io_timing = 'off'/) + end + + it 'sets default_statistics_target' do + expect(chef_run.node['postgresql']['default_statistics_target']) + .to eq(1000) + + expect(chef_run).to render_file(runtime_conf) + .with_content(/default_statistics_target = 1000/) + end + + it 'enables the synchronous_commit setting' do + expect(chef_run.node['postgresql']['synchronous_commit']) + .to eq('on') + + expect(chef_run).to render_file( + runtime_conf + ).with_content(/synchronous_commit = on/) + end + end + + context 'when rendering pg_hba.conf' do + it 'creates a standard pg_hba.conf' do + expect(chef_run).to render_file(pg_hba_conf) + .with_content('local all all peer map=gitlab') + end + + it 'cert authentication is disabled by default' do + expect(chef_run).to render_file(pg_hba_conf).with_content { |content| + expect(content).to_not match(/cert$/) + } + end + end + end + + context 'with user specified settings' do + before do + stub_gitlab_rb(postgresql: { + shared_preload_libraries: 'pg_stat_statements', + archive_mode: 'on', + username: 'foo', + group: 'bar', + ssl: 'off', + ssl_crl_file: 'revoke.crl', + ssl_ciphers: 'ALL', + log_destination: 'csvlog', + logging_collector: 'on', + log_filename: 'test.log', + log_file_mode: '0600', + log_truncate_on_rotation: 'on', + log_rotation_age: '1d', + log_rotation_size: '10MB', + dynamic_shared_memory_type: 'none', + wal_log_hints: 'on' + }, + geo_secondary_role: { + enable: true + }) + end + + it_behaves_like 'enabled runit service', 'postgresql', 'root', 'root', 'foo', 'bar' + + context 'when rendering postgresql.conf' do + it 'correctly sets the shared_preload_libraries setting' do + expect(chef_run.node['postgresql']['shared_preload_libraries']) + .to eql('pg_stat_statements') + + expect(chef_run).to render_file(postgresql_conf) + .with_content(/shared_preload_libraries = 'pg_stat_statements'/) + end + + it 'enables archive mode' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/archive_mode = on/) + end + + it 'disables SSL' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/ssl = off/) + + expect(chef_run).not_to render_file(postgresql_ssl_cert) + expect(chef_run).not_to render_file(postgresql_ssl_key) + end + + it 'sets the certificate revocation list' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/ssl_crl_file = 'revoke.crl'/) + end + + it 'sets the SSL cipher list' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/ssl_ciphers = 'ALL'/) + end + + it 'includes gitlab-geo.conf in postgresql.conf' do + expect(chef_run).to render_file(postgresql_conf) + .with_content(/include_if_exists 'gitlab-geo.conf'/) + end + + it 'sets user specified logging parameters' do + expect(chef_run).to render_file(postgresql_conf).with_content { |content| + expect(content).to match(/logging_collector = on/) + } + + expect(chef_run).to render_file(runtime_conf).with_content { |content| + expect(content).to match(/log_destination = 'csvlog'/) + expect(content).to match(/log_filename = 'test.log'/) + expect(content).to match(/log_file_mode = 0600/) + expect(content).to match(/log_truncate_on_rotation = on/) + expect(content).to match(/log_rotation_age = 1d/) + expect(content).to match(/log_rotation_size = 10MB/) + } + end + + it 'sets the dynamic_shared_memory_type' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/^dynamic_shared_memory_type = none/) + end + + it 'enables wal_log_hints' do + expect(chef_run).to render_file( + postgresql_conf + ).with_content(/^wal_log_hints = on/) + end + end + + context 'when rendering runtime.conf' do + before do + stub_gitlab_rb(postgresql: { + log_line_prefix: '%a', + log_statement: 'all', + max_standby_archive_delay: '60s', + max_standby_streaming_delay: '120s', + archive_command: 'command', + archive_timeout: '120', + }) + end + + it 'correctly sets the log_line_prefix setting' do + expect(chef_run.node['postgresql']['log_line_prefix']) + .to eql('%a') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/log_line_prefix = '%a'/) + end + + it 'correctly sets the log_statement setting' do + expect(chef_run.node['postgresql']['log_statement']) + .to eql('all') + + expect(chef_run).to render_file(runtime_conf) + .with_content(/log_statement = 'all'/) + end + + it 'sets max_standby settings' do + expect(chef_run).to render_file( + runtime_conf + ).with_content(/max_standby_archive_delay = 60s/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/max_standby_streaming_delay = 120s/) + end + + it 'sets archive settings' do + expect(chef_run).to render_file( + runtime_conf + ).with_content(/archive_command = 'command'/) + expect(chef_run).to render_file( + runtime_conf + ).with_content(/archive_timeout = 120/) + end + end + + context 'when rendering pg_hba.conf' do + before do + stub_gitlab_rb( + postgresql: { + hostssl: true, + trust_auth_cidr_addresses: ['127.0.0.1/32'], + custom_pg_hba_entries: { + foo: [ + { + type: 'host', + database: 'foo', + user: 'bar', + cidr: '127.0.0.1/32', + method: 'trust' + } + ] + }, + cert_auth_addresses: { + '1.2.3.4/32' => { + database: 'fakedatabase', + user: 'fakeuser' + }, + 'fakehostname' => { + database: 'anotherfakedatabase', + user: 'anotherfakeuser' + }, + } + } + ) + end + + it 'prefers hostssl when configured in pg_hba.conf' do + expect(chef_run).to render_file(pg_hba_conf) + .with_content('hostssl all all 127.0.0.1/32 trust') + end + + it 'adds users custom entries to pg_hba.conf' do + expect(chef_run).to render_file(pg_hba_conf) + .with_content('host foo bar 127.0.0.1/32 trust') + end + + it 'allows cert authentication to be enabled' do + expect(chef_run).to render_file(pg_hba_conf).with_content('hostssl fakedatabase fakeuser 1.2.3.4/32 cert') + expect(chef_run).to render_file(pg_hba_conf).with_content('hostssl anotherfakedatabase anotherfakeuser fakehostname cert') + end + end + end + + context 'when postgresql.conf changes' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original + allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('postgresql').and_return(true) + allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_call_original + allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).with('postgresql').and_return(true) + end + + it 'notifies reload postgresql task' do + expect(chef_run).to create_postgresql_config('gitlab') + postgresql_config = chef_run.postgresql_config('gitlab') + expect(postgresql_config).to notify('execute[reload postgresql]').to(:run).immediately + expect(postgresql_config).to notify('execute[start postgresql]').to(:run).immediately + end + end + + context 'when enabling extensions' do + it 'creates the pg_trgm extension when it is possible' do + allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('pg_trgm', 'gitlabhq_production').and_return(true) + expect(chef_run).to enable_postgresql_extension('pg_trgm') + end + + it 'does not create the pg_trgm extension if it is not possible' do + allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('pg_trgm', 'gitlabhq_production').and_return(false) + expect(chef_run).not_to run_execute('enable pg_trgm extension') + end + + context 'when on a secondary database node' do + before do + allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:replica?).and_return(true) + end + + it 'should not activate pg_trgm' do + expect(chef_run).not_to run_execute('enable pg_trgm extension') + end + end + + it 'creates the btree_gist extension when it is possible' do + allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('btree_gist', 'gitlabhq_production').and_return(true) + expect(chef_run).to enable_postgresql_extension('btree_gist') + end + + it 'does not create the btree_gist extension if it is not possible' do + allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('btree_gist', 'gitlabhq_production').and_return(false) + expect(chef_run).not_to run_execute('enable btree_gist extension') + end + end + + context 'when configuring postgresql_user resources' do + before do + stub_gitlab_rb( + { + postgresql: { + sql_user_password: 'fakepassword', + sql_replication_password: 'fakepassword' + } + } + ) + end + + context 'when on the primary database node' do + before do + allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(false) + end + + it 'should set a password for sql_user when sql_user_password is set' do + expect(chef_run).to create_postgresql_user('gitlab').with(password: 'md5fakepassword') + end + + it 'should create the gitlab_replicator user with replication permissions' do + expect(chef_run).to create_postgresql_user('gitlab_replicator').with( + options: %w(replication), + password: 'md5fakepassword' + ) + end + end + + context 'when on a secondary database node' do + before do + allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:replica?).and_return(true) + end + + it 'should not create users' do + expect(chef_run).not_to create_postgresql_user('gitlab') + expect(chef_run).not_to create_postgresql_user('gitlab_replicator') + end + end + end +end + +RSpec.describe 'postgresql 13' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config)).converge('gitlab::default') } + let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') } + let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' } + + before do + allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('13.0')) + allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('13.0')) + end + + it 'configures wal_keep_size instead of wal_keep_segments' do + expect(chef_run).to render_file(runtime_conf).with_content { |content| + expect(content).to include("wal_keep_size") + expect(content).not_to include("wal_keep_segments") + } + end +end + +RSpec.describe 'postgresql 12' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config)).converge('gitlab::default') } + let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') } + let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' } + + before do + allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('12.0')) + allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('12.0')) + end + + it 'configures wal_keep_segments instead of wal_keep_size' do + expect(chef_run).to render_file(runtime_conf).with_content { |content| + expect(content).to include("wal_keep_segments") + expect(content).to_not include("wal_keep_size") + } + end +end + +RSpec.describe 'postgres when version mismatches occur' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config)).converge('gitlab::default') } + let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') } + let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' } + + context 'when data and binary versions differ' do + before do + allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('expectation')) + allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('expectation')) + allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('reality')) + allow(File).to receive(:exists?).and_call_original + allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true) + allow(Dir).to receive(:glob).and_call_original + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/reality*").and_return( + ['/opt/gitlab/embedded/postgresql/reality'] + ) + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/reality/bin/*").and_return( + %w( + /opt/gitlab/embedded/postgresql/reality/bin/foo_one + /opt/gitlab/embedded/postgresql/reality/bin/foo_two + /opt/gitlab/embedded/postgresql/reality/bin/foo_three + ) + ) + end + + it 'corrects symlinks to the correct location' do + allow(FileUtils).to receive(:ln_sf).and_return(true) + %w(foo_one foo_two foo_three).each do |pg_bin| + expect(FileUtils).to receive(:ln_sf).with( + "/opt/gitlab/embedded/postgresql/reality/bin/#{pg_bin}", + "/opt/gitlab/embedded/bin/#{pg_bin}" + ) + end + chef_run.ruby_block('Link postgresql bin files to the correct version').block.call + end + + it 'does not warn the user that a restart is needed by default' do + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + expect(chef_run).not_to run_ruby_block('warn pending postgresql restart') + end + end + + context 'when running version and installed version differ' do + before do + allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('expectation')) + allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('reality')) + end + + it 'warns the user that a restart is needed' do + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + expect(chef_run).to run_ruby_block('warn pending postgresql restart') + end + + it 'does not warns the user that a restart is needed when postgres is stopped' do + expect(chef_run).not_to run_ruby_block('warn pending postgresql restart') + end + end + + context 'when an older data version is present and no longer used' do + before do + allow(Gitlab).to receive(:[]).and_call_original + allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('new_shiny')) + allow_any_instance_of(PGVersion).to receive(:major).and_return('new_shiny') + allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('new_shiny')) + allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('ancient_history')) + allow(File).to receive(:exists?).and_call_original + allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true) + allow(Dir).to receive(:glob).and_call_original + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/ancient_history*").and_return([]) + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/new_shiny*").and_return( + ['/opt/gitlab/embedded/postgresql/new_shiny'] + ) + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/new_shiny/bin/*").and_return( + %w( + /opt/gitlab/embedded/postgresql/new_shiny/bin/foo_one + /opt/gitlab/embedded/postgresql/new_shiny/bin/foo_two + /opt/gitlab/embedded/postgresql/new_shiny/bin/foo_three + ) + ) + end + + it 'corrects symlinks to the correct location' do + allow(FileUtils).to receive(:ln_sf).and_return(true) + %w(foo_one foo_two foo_three).each do |pg_bin| + expect(FileUtils).to receive(:ln_sf).with( + "/opt/gitlab/embedded/postgresql/new_shiny/bin/#{pg_bin}", + "/opt/gitlab/embedded/bin/#{pg_bin}" + ) + end + chef_run.ruby_block('Link postgresql bin files to the correct version').block.call + end + end + + context 'when the expected postgres version is missing' do + before do + allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('how_it_started')) + allow(File).to receive(:exists?).and_call_original + allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true) + allow(Dir).to receive(:glob).and_call_original + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/how_it_started*").and_return([]) + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/how_it_is_going*").and_return([]) + end + + it 'throws an error' do + expect do + chef_run.ruby_block('Link postgresql bin files to the correct version').block.call + end.to raise_error(RuntimeError, /Could not find PostgreSQL binaries/) + end + end +end + +RSpec.describe 'postgresql::bin' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + let(:gitlab_psql_rc) do + <<-EOF +psql_user='gitlab-psql' +psql_group='gitlab-psql' +psql_host='/var/opt/gitlab/postgresql' +psql_port='5432' + EOF + end + + before do + allow(Gitlab). to receive(:[]).and_call_original + end + + context 'when bundled postgresql is disabled' do + before do + stub_gitlab_rb( + postgresql: { + enable: false + } + ) + + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/PG_VERSION').and_return(false) + + allow_any_instance_of(PgHelper).to receive(:database_version).and_return(nil) + version = double("PgHelper", major: 10, minor: 9) + allow_any_instance_of(PgHelper).to receive(:version).and_return(version) + end + + it 'still includes the postgresql::bin recipe' do + expect(chef_run).to include_recipe('postgresql::bin') + end + + it 'includes postgresql::directory_locations' do + expect(chef_run).to include_recipe('postgresql::directory_locations') + end + + it 'creates gitlab-psql-rc' do + expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-psql-rc') + .with_content(gitlab_psql_rc) + end + + # We do expect the ruby block to run, but nothing to be found + it "doesn't link any files by default" do + expect(FileUtils).to_not receive(:ln_sf) + end + + context "with postgresql['version'] set" do + before do + stub_gitlab_rb( + postgresql: { + enable: false, + version: '999' + } + ) + allow(Dir).to receive(:glob).and_call_original + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/999*").and_return( + %w( + /opt/gitlab/embedded/postgresql/999 + ) + ) + allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/999/bin/*").and_return( + %w( + /opt/gitlab/embedded/postgresql/999/bin/foo_one + /opt/gitlab/embedded/postgresql/999/bin/foo_two + /opt/gitlab/embedded/postgresql/999/bin/foo_three + ) + ) + end + + it "doesn't print a warning with a valid postgresql version" do + expect(chef_run).to_not run_ruby_block('check_postgresql_version') + end + + it 'links the specified version' do + allow(FileUtils).to receive(:ln_sf).and_return(true) + %w(foo_one foo_two foo_three).each do |pg_bin| + expect(FileUtils).to receive(:ln_sf).with( + "/opt/gitlab/embedded/postgresql/999/bin/#{pg_bin}", + "/opt/gitlab/embedded/bin/#{pg_bin}" + ) + end + chef_run.ruby_block('Link postgresql bin files to the correct version').block.call + end + end + + context "with an invalid version in postgresql['version']" do + before do + stub_gitlab_rb( + postgresql: { + enable: false, + version: '888' + } + ) + allow(Dir).to receive(:glob).and_call_original + allow(Dir).to receive(:glob).with('/opt/gitlab/embedded/postgresql/888*').and_return([]) + end + + it 'should print a warning' do + expect(chef_run).to run_ruby_block('check_postgresql_version') + end + end + end +end + +RSpec.describe 'default directories' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'postgresql directory' do + context 'with default settings' do + it 'creates postgresql directory' do + expect(chef_run).to create_directory('/var/opt/gitlab/postgresql').with(owner: 'gitlab-psql', mode: '0755', recursive: true) + end + end + + context 'with custom settings' do + before do + stub_gitlab_rb( + postgresql: { + dir: '/mypgdir', + home: '/mypghomedir' + }) + end + + it 'creates postgresql directory with custom path' do + expect(chef_run).to create_directory('/mypgdir').with(owner: 'gitlab-psql', mode: '0755', recursive: true) + end + end + end +end diff --git a/spec/chef/cookbooks/postgresql/recipes/sysctl_spec.rb b/spec/chef/cookbooks/postgresql/recipes/sysctl_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3JlY2lwZXMvc3lzY3RsX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/postgresql/recipes/sysctl_spec.rb @@ -0,0 +1,10 @@ +require 'chef_helper' + +RSpec.describe 'postgresql::user' do + cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config', 'postgresql::sysctl') } + + it 'creates sysctl files' do + expect(chef_run).to create_gitlab_sysctl('kernel.shmmax').with_value(17179869184) + expect(chef_run).to create_gitlab_sysctl('kernel.shmall').with_value(4194304) + end +end diff --git a/spec/chef/cookbooks/postgresql/recipes/user_spec.rb b/spec/chef/cookbooks/postgresql/recipes/user_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3JlY2lwZXMvdXNlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/postgresql/recipes/user_spec.rb @@ -0,0 +1,79 @@ +require 'chef_helper' + +RSpec.describe 'postgresql::user' do + cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config', 'postgresql::user') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + it 'includes postgresql::directory_locations recipe' do + expect(chef_run).to include_recipe('postgresql::directory_locations') + end + + context 'with default attributes' do + it 'creates postgresql user and groups' do + account_params = { + username: 'gitlab-psql', + groupname: 'gitlab-psql', + home: '/var/opt/gitlab/postgresql', + shell: '/bin/sh' + } + + expect(chef_run).to create_account('Postgresql user and group').with(account_params) + end + + it 'creates postgresql base directory' do + directory_params = { + owner: 'gitlab-psql', + mode: '0755', + recursive: true + } + expect(chef_run).to create_directory('/var/opt/gitlab/postgresql').with(directory_params) + end + + it 'creates a .profile with bundled PostgreSQL PATH' do + file_params = { + owner: 'gitlab-psql', + content: %r{PATH=/opt/gitlab/embedded/bin:/opt/gitlab/bin:\$PATH} + } + + expect(chef_run).to create_file('/var/opt/gitlab/postgresql/.profile').with(file_params) + end + end + + context 'when using custom values for directories' do + cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config', 'postgresql::user') } + + before do + stub_gitlab_rb( + postgresql: { + home: '/mypghomedir' + }) + end + + it 'creates postgresql user and groups' do + account_params = { + username: 'gitlab-psql', + groupname: 'gitlab-psql', + home: '/mypghomedir', + shell: '/bin/sh' + } + + expect(chef_run).to create_account('Postgresql user and group').with(account_params) + end + + it 'creates postgresql base directory' do + directory_params = { + owner: 'gitlab-psql', + mode: '0755', + recursive: true + } + expect(chef_run).to create_directory('/mypghomedir').with(directory_params) + end + + it 'creates necessary directories' do + expect(chef_run).to create_directory('/mypghomedir').with(owner: 'gitlab-psql', mode: '0755', recursive: true) + end + end +end diff --git a/spec/chef/cookbooks/postgresql/resources/config_spec.rb b/spec/chef/cookbooks/postgresql/resources/config_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3Jlc291cmNlcy9jb25maWdfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/postgresql/resources/config_spec.rb @@ -0,0 +1,45 @@ +require 'chef_helper' + +RSpec.describe 'postgresql_config' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(postgresql_config)) do |node| + node.normal['postgresql']['dir'] = '/fakedir' + node.normal['patroni']['dir'] = '/patronifakedir' + end + end + + let(:chef_run) { runner.converge('gitlab::config', 'test_postgresql::postgresql_config') } + + before do + allow_any_instance_of(PgHelper).to receive(:postgresql_user).and_return('fakeuser') + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'create' do + context 'default settings' do + it 'creates the files' do + expect(chef_run).to render_file('/fakedir/data/postgresql.conf') + expect(chef_run).to render_file('/fakedir/data/runtime.conf') + expect(chef_run).to render_file('/fakedir/data/pg_hba.conf') + expect(chef_run).to render_file('/fakedir/data/pg_ident.conf') + end + end + + context 'when patroni is enabled' do + before do + stub_gitlab_rb( + patroni: { + enable: true + } + ) + end + + it 'creates the files' do + expect(chef_run).to render_file('/patronifakedir/data/postgresql.base.conf') + expect(chef_run).to render_file('/patronifakedir/data/runtime.conf') + expect(chef_run).to render_file('/patronifakedir/data/pg_hba.conf') + expect(chef_run).to render_file('/patronifakedir/data/pg_ident.conf') + end + end + end +end diff --git a/spec/chef/cookbooks/postgresql/resources/database_spec.rb b/spec/chef/cookbooks/postgresql/resources/database_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3Jlc291cmNlcy9kYXRhYmFzZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/postgresql/resources/database_spec.rb @@ -0,0 +1,64 @@ +require 'chef_helper' + +RSpec.describe 'postgresql_database' do + let(:runner) do + ChefSpec::SoloRunner.new(step_into: %w(postgresql_database)) do |node| + # unix_socket_directory is normally conditionally set in postgresql::enable + # which is not executed as part of this spec + node.normal['postgresql']['unix_socket_directory'] = '/var/opt/gitlab/postgresql' + end + end + + context 'create' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_database_create') } + + context 'server is running' do + before do + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + end + + context 'database does not already exist' do + before do + allow_any_instance_of(PgHelper).to receive(:database_exists?).and_return(false) + end + + it 'creates a database' do + expect(chef_run).to run_execute('create database example').with( + command: %(/opt/gitlab/embedded/bin/createdb --port 5432 -h /var/opt/gitlab/postgresql -O gitlab example), + user: 'gitlab-psql' + ) + end + + context 'with non-default options' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_database_create_with_options') } + + it 'creates with the correct options set' do + expect(chef_run).to run_execute('create database fakedb').with( + command: %(/opt/gitlab/embedded/bin/createdb --port 9999 -h /fake/dir -O fakeuser fakedb) + ) + end + end + end + + context 'database already exists' do + before do + allow_any_instance_of(PgHelper).to receive(:database_exists?).with('example').and_return(true) + end + + it 'does nothing' do + expect(chef_run).not_to run_execute('create database example') + end + end + end + + context 'server is not running' do + before do + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(false) + end + + it 'does nothing' do + expect(chef_run).not_to run_execute('create database example') + end + end + end +end diff --git a/spec/chef/cookbooks/postgresql/resources/extension_spec.rb b/spec/chef/cookbooks/postgresql/resources/extension_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3Jlc291cmNlcy9leHRlbnNpb25fc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/postgresql/resources/extension_spec.rb @@ -0,0 +1,32 @@ +require 'chef_helper' + +RSpec.describe 'postgresql_query' do + let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(postgresql_extension)) } + + context 'run' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_extension_enable') } + + context 'when extension can be enabled' do + before do + allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('foobar', 'lorem').and_return(true) + end + + it 'runs the query to enable extension' do + expect(chef_run).to run_postgresql_query('enable foobar extension').with( + query: %(CREATE EXTENSION IF NOT EXISTS foobar), + db_name: 'lorem' + ) + end + end + + context 'when extension can not be enabled' do + before do + allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('foobar', 'lorem').and_return(false) + end + + it 'does not run the query to enable extension' do + expect(chef_run).not_to run_postgresql_query('enable foobar extension') + end + end + end +end diff --git a/spec/chef/cookbooks/postgresql/resources/query_spec.rb b/spec/chef/cookbooks/postgresql/resources/query_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3Jlc291cmNlcy9xdWVyeV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/postgresql/resources/query_spec.rb @@ -0,0 +1,43 @@ +require 'chef_helper' + +RSpec.describe 'postgresql_query' do + let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(postgresql_query)) } + + before do + allow_any_instance_of(PgHelper).to receive(:is_ready?).and_return(true) + allow_any_instance_of(GeoPgHelper).to receive(:is_ready?).and_return(true) + end + + context 'run' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_query_run') } + + context 'when service is up' do + before do + allow_any_instance_of(PgHelper).to receive(:is_offline_or_readonly?).and_return(false) + allow_any_instance_of(GeoPgHelper).to receive(:is_offline_or_readonly?).and_return(false) + end + + it 'runs the query with correct service' do + expect(chef_run).to run_execute('create schema (postgresql)').with( + command: %(/opt/gitlab/bin/gitlab-psql -d omnibus_test -c "CREATE SCHEMA example AUTHORIZATION foobar;") + ) + + expect(chef_run).to run_execute('create schema (geo-postgresql)').with( + command: %(/opt/gitlab/bin/gitlab-geo-psql -d omnibus_test -c "CREATE SCHEMA example AUTHORIZATION foobar;") + ) + end + end + + context 'when service is offline' do + before do + allow_any_instance_of(PgHelper).to receive(:is_offline_or_readonly?).and_return(true) + allow_any_instance_of(GeoPgHelper).to receive(:is_offline_or_readonly?).and_return(true) + end + + it 'does not run the query' do + expect(chef_run).not_to run_execute('create example schema (postgresql)') + expect(chef_run).not_to run_execute('create example schema (geo-postgresql)') + end + end + end +end diff --git a/spec/chef/cookbooks/postgresql/resources/schema_spec.rb b/spec/chef/cookbooks/postgresql/resources/schema_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3Jlc291cmNlcy9zY2hlbWFfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/postgresql/resources/schema_spec.rb @@ -0,0 +1,52 @@ +require 'chef_helper' + +RSpec.describe 'postgresql_schema' do + let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(postgresql_schema)) } + + context 'create' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_schema_create') } + + context 'server is running' do + before do + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(false) + end + + it 'creates schema' do + expect(chef_run).to run_postgresql_query('create example schema on omnibus_gitlab_test').with( + db_name: 'omnibus_gitlab_test' + ) + end + + context 'schema already exists' do + before do + allow_any_instance_of(PgHelper).to receive(:schema_exists?).and_return(true) + end + + it 'does not try to create schema again' do + expect(chef_run).not_to run_postgresql_query('create example schema on omnibus_gitlab_test') + end + + context 'when schema owner is different' do + before do + allow_any_instance_of(PgHelper).to receive(:schema_owner?).and_return(false) + end + + it 'tries to update schema' do + expect(chef_run).to run_postgresql_query('modify example schema owner on omnibus_gitlab_test') + end + end + + context 'when schema owner is the same' do + before do + allow_any_instance_of(PgHelper).to receive(:schema_owner?).and_return(true) + end + + it 'tries to update schema' do + expect(chef_run).not_to run_postgresql_query('modify example schema owner on omnibus_gitlab_test') + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/postgresql/resources/user_spec.rb b/spec/chef/cookbooks/postgresql/resources/user_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wb3N0Z3Jlc3FsL3Jlc291cmNlcy91c2VyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/postgresql/resources/user_spec.rb @@ -0,0 +1,78 @@ +require 'chef_helper' + +RSpec.describe 'postgresql_user' do + before do + allow_any_instance_of(PgHelper).to receive(:is_offline_or_readonly?).and_return(false) + allow_any_instance_of(PgHelper).to receive(:user_exists?).and_return(false, true) + allow_any_instance_of(PgHelper).to receive(:user_password_match?).and_return(false) + allow_any_instance_of(PgHelper).to receive(:user_options_set?).and_return(false) + allow_any_instance_of(PgHelper).to receive(:is_ready?).and_return(true) + allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) + end + + let(:runner) { ChefSpec::SoloRunner.new(step_into: ['postgresql_user']) } + + context 'create' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_user_create') } + + it 'creates a user' do + expect(chef_run).to run_execute('create example postgresql user') + end + end + + context 'password' do + context 'not specified' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_user_password_unspecified') } + + it 'does not set the password of the no_password user' do + expect(chef_run).not_to run_execute('set password for no_password postgresql user') + end + end + + context 'nil' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_user_password_nil') } + + it 'does set the password of the nil_password user' do + expect(chef_run).to run_execute('set password for nil_password postgresql user') + .with(command: /PASSWORD NULL/) + end + end + + context 'md5' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_user_password_md5') } + + it 'does set the password of the md5_password user' do + expect(chef_run).to run_execute('set password for md5_password postgresql user') + .with(command: /PASSWORD 'e99b79fbdf9b997e6918df2385e60f5c'/) + end + end + + context 'empty' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_user_password_empty') } + + it 'does set the password of the empty_password user' do + expect(chef_run).to run_execute('set password for empty_password postgresql user') + .with(command: /PASSWORD ''/) + end + end + end + + context 'options' do + context 'unspecified' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_user_options_unspecified') } + + it 'does not set options' do + expect(chef_run).not_to run_execute('set options for example postgresql user') + end + end + + context 'SUPERUSER' do + let(:chef_run) { runner.converge('test_postgresql::postgresql_user_options_superuser') } + + it 'does set SUPERUSER' do + expect(chef_run).to run_execute('set options for example postgresql user') + .with(command: /\bSUPERUSER\b/) + end + end + end +end diff --git a/spec/chef/cookbooks/praefect/recipes/praefect_spec.rb b/spec/chef/cookbooks/praefect/recipes/praefect_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9wcmFlZmVjdC9yZWNpcGVzL3ByYWVmZWN0X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/praefect/recipes/praefect_spec.rb @@ -0,0 +1,303 @@ +require 'chef_helper' +RSpec.describe 'praefect' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service env_dir)).converge('gitlab::default') } + let(:prometheus_grpc_latency_buckets) do + '[0.001, 0.005, 0.025, 0.1, 0.5, 1.0, 10.0, 30.0, 60.0, 300.0, 1500.0]' + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when the defaults are used' do + it_behaves_like 'disabled runit service', 'praefect' + end + + context 'when praefect is enabled' do + let(:config_path) { '/var/opt/gitlab/praefect/config.toml' } + let(:env_dir) { '/opt/gitlab/etc/praefect/env' } + let(:auth_transitioning) { false } + + before do + stub_gitlab_rb(praefect: { + enable: true, + auth_transitioning: auth_transitioning, + }) + end + + it 'creates expected directories with correct permissions' do + expect(chef_run).to create_directory('/var/opt/gitlab/praefect').with(user: 'git', mode: '0700') + end + + it 'creates a default VERSION file and sends hup to service' do + expect(chef_run).to create_version_file('Create Praefect version file').with( + version_file_path: '/var/opt/gitlab/praefect/VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/praefect --version' + ) + + expect(chef_run.version_file('Create Praefect version file')).to notify('runit_service[praefect]').to(:hup) + end + + it 'renders the config.toml' do + rendered = { + 'auth' => { 'token' => '', 'transitioning' => false }, + 'listen_addr' => 'localhost:2305', + 'logging' => { 'format' => 'json' }, + 'prometheus_listen_addr' => 'localhost:9652', + 'prometheus_exclude_database_from_default_metrics' => true, + 'sentry' => {}, + 'database' => { + 'session_pooled' => {}, + }, + 'reconciliation' => {}, + 'failover' => { 'enabled' => true } + } + + expect(chef_run).to render_file(config_path).with_content { |content| + expect(Tomlrb.parse(content)).to eq(rendered) + } + expect(chef_run).not_to render_file(config_path) + .with_content(%r{\[prometheus\]\s+grpc_latency_buckets =}) + end + + it 'renders the env dir files' do + expect(chef_run).to render_file(File.join(env_dir, "GITALY_PID_FILE")) + .with_content('/var/opt/gitlab/praefect/praefect.pid') + expect(chef_run).to render_file(File.join(env_dir, "WRAPPER_JSON_LOGGING")) + .with_content('true') + expect(chef_run).to render_file(File.join(env_dir, "SSL_CERT_DIR")) + .with_content('/opt/gitlab/embedded/ssl/certs/') + end + + it 'renders the service run file with wrapper' do + expect(chef_run).to render_file('/opt/gitlab/sv/praefect/run') + .with_content('/opt/gitlab/embedded/bin/gitaly-wrapper /opt/gitlab/embedded/bin/praefect') + .with_content('exec chpst -e /opt/gitlab/etc/praefect/env') + end + + context 'with custom settings' do + let(:dir) { nil } + let(:socket_path) { '/var/opt/gitlab/praefect/praefect.socket' } + let(:auth_token) { 'secrettoken123' } + let(:auth_transitioning) { false } + let(:sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project' } + let(:sentry_environment) { 'production' } + let(:listen_addr) { 'localhost:4444' } + let(:tls_listen_addr) { 'localhost:5555' } + let(:certificate_path) { '/path/to/cert.pem' } + let(:key_path) { '/path/to/key.pem' } + let(:prom_addr) { 'localhost:1234' } + let(:separate_database_metrics) { false } + let(:log_level) { 'debug' } + let(:log_format) { 'text' } + let(:primaries) { %w[praefect1 praefect2] } + let(:virtual_storages) do + { + 'default' => { + 'default_replication_factor' => 2, + 'nodes' => { + 'praefect1' => { address: 'tcp://node1.internal', token: "praefect1-token" }, + 'praefect2' => { address: 'tcp://node2.internal', token: "praefect2-token" }, + 'praefect3' => { address: 'tcp://node3.internal', token: "praefect3-token" }, + 'praefect4' => { address: 'tcp://node4.internal', token: "praefect4-token" } + }, + 'praefect5' => { address: 'tcp://node5.internal', token: "praefect5-token" } + } + } + end + let(:failover_enabled) { true } + let(:database_host) { 'pg.external' } + let(:database_port) { 2234 } + let(:database_user) { 'praefect-pg' } + let(:database_password) { 'praefect-pg-pass' } + let(:database_dbname) { 'praefect_production' } + let(:database_sslmode) { 'require' } + let(:database_sslcert) { '/path/to/client-cert' } + let(:database_sslkey) { '/path/to/client-key' } + let(:database_sslrootcert) { '/path/to/rootcert' } + let(:database_sslrootcert) { '/path/to/rootcert' } + let(:database_direct_host) { 'pg.internal' } + let(:database_direct_port) { 1234 } + let(:reconciliation_scheduling_interval) { '1m' } + let(:reconciliation_histogram_buckets) { '[1.0, 2.0]' } + + before do + stub_gitlab_rb(praefect: { + enable: true, + dir: dir, + socket_path: socket_path, + auth_token: auth_token, + auth_transitioning: auth_transitioning, + sentry_dsn: sentry_dsn, + sentry_environment: sentry_environment, + listen_addr: listen_addr, + tls_listen_addr: tls_listen_addr, + certificate_path: certificate_path, + key_path: key_path, + prometheus_listen_addr: prom_addr, + prometheus_grpc_latency_buckets: prometheus_grpc_latency_buckets, + separate_database_metrics: separate_database_metrics, + logging_level: log_level, + logging_format: log_format, + failover_enabled: failover_enabled, + virtual_storages: virtual_storages, + database_host: database_host, + database_port: database_port, + database_user: database_user, + database_password: database_password, + database_dbname: database_dbname, + database_sslmode: database_sslmode, + database_sslcert: database_sslcert, + database_sslkey: database_sslkey, + database_sslrootcert: database_sslrootcert, + database_direct_host: database_direct_host, + database_direct_port: database_direct_port, + reconciliation_scheduling_interval: reconciliation_scheduling_interval, + reconciliation_histogram_buckets: reconciliation_histogram_buckets, + }) + end + + it 'renders the config.toml' do + expect(chef_run).to render_file(config_path).with_content { |content| + expect(Tomlrb.parse(content)).to eq( + { + 'auth' => { + 'token' => 'secrettoken123', + 'transitioning' => false + }, + 'database' => { + 'dbname' => 'praefect_production', + 'host' => 'pg.external', + 'password' => 'praefect-pg-pass', + 'port' => 2234, + 'sslcert' => '/path/to/client-cert', + 'sslkey' => '/path/to/client-key', + 'sslmode' => 'require', + 'sslrootcert' => '/path/to/rootcert', + 'user' => 'praefect-pg', + 'session_pooled' => { + 'host' => 'pg.internal', + 'port' => 1234, + } + }, + 'failover' => { + 'enabled' => true + }, + 'logging' => { + 'format' => 'text', + 'level' => 'debug' + }, + 'listen_addr' => 'localhost:4444', + 'prometheus' => { + 'grpc_latency_buckets' => [0.001, 0.005, 0.025, 0.1, 0.5, 1.0, 10.0, 30.0, 60.0, 300.0, 1500.0] + }, + 'reconciliation' => { + 'histogram_buckets' => [1.0, 2.0], + 'scheduling_interval' => '1m' + }, + 'sentry' => { + 'sentry_dsn' => 'https://my_key:my_secret@sentry.io/test_project', + 'sentry_environment' => 'production' + }, + 'prometheus_listen_addr' => 'localhost:1234', + 'prometheus_exclude_database_from_default_metrics' => false, + 'socket_path' => '/var/opt/gitlab/praefect/praefect.socket', + 'tls' => { + 'certificate_path' => '/path/to/cert.pem', + 'key_path' => '/path/to/key.pem' + }, + 'tls_listen_addr' => 'localhost:5555', + 'virtual_storage' => [ + { + 'name' => 'default', + 'default_replication_factor' => 2, + 'node' => [ + { + 'address' => 'tcp://node1.internal', + 'storage' => 'praefect1', + 'token' => 'praefect1-token' + }, + { + 'address' => 'tcp://node2.internal', + 'storage' => 'praefect2', + 'token' => 'praefect2-token' + }, + { + 'address' => 'tcp://node3.internal', + 'storage' => 'praefect3', + 'token' => 'praefect3-token' + }, + { + 'address' => 'tcp://node4.internal', + 'storage' => 'praefect4', + 'token' => 'praefect4-token' + }, + { + 'address' => 'tcp://node5.internal', + 'storage' => 'praefect5', + 'token' => 'praefect5-token' + } + ] + } + ] + } + ) + } + end + + it 'renders the env dir files correctly' do + expect(chef_run).to render_file(File.join(env_dir, "WRAPPER_JSON_LOGGING")) + .with_content('false') + end + + context 'with virtual_storages as an array' do + let(:virtual_storages) { [{ name: 'default', 'nodes' => [{ storage: 'praefect1', address: 'tcp://node1.internal', token: "praefect1-token" }] }] } + + it 'raises an error' do + expect { chef_run }.to raise_error("Praefect virtual_storages must be a hash") + end + end + + context 'with duplicate virtual storage node configured via fallback' do + let(:virtual_storages) { { 'default' => { 'node-1' => {}, 'nodes' => { 'node-1' => {} } } } } + + it 'raises an error' do + allow(LoggingHelper).to receive(:deprecation) + expect(LoggingHelper).to receive(:deprecation).with( + <<~EOS + Configuring the Gitaly nodes directly in the virtual storage's root configuration object has + been deprecated in GitLab 13.12 and will no longer be supported in GitLab 15.0. Move the Gitaly + nodes under the 'nodes' key as described in step 6 of https://docs.gitlab.com/ee/administration/gitaly/praefect.html#praefect. + EOS + ) + + expect { chef_run }.to raise_error("Virtual storage 'default' contains duplicate configuration for node 'node-1'") + end + end + + context 'with nodes within virtual_storages as an array' do + let(:virtual_storages) { { 'default' => [{ storage: 'praefect1', address: 'tcp://node1.internal', token: "praefect1-token" }] } } + + it 'raises an error' do + expect { chef_run }.to raise_error("nodes of a Praefect virtual_storage must be a hash") + end + end + end + + describe 'database migrations' do + it 'runs the migrations' do + expect(chef_run).to run_bash('migrate praefect database') + end + + context 'with auto_migrate off' do + before { stub_gitlab_rb(praefect: { auto_migrate: false }) } + + it 'skips running the migrations' do + expect(chef_run).not_to run_bash('migrate praefect database') + end + end + end + + include_examples "consul service discovery", "praefect", "praefect" + end +end diff --git a/spec/chef/cookbooks/redis/recipes/redis_spec.rb b/spec/chef/cookbooks/redis/recipes/redis_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9yZWRpcy9yZWNpcGVzL3JlZGlzX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/redis/recipes/redis_spec.rb @@ -0,0 +1,379 @@ +require 'chef_helper' + +RSpec.describe 'redis' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(redis_service runit_service)).converge('gitlab::default') } + let(:redis_conf) { '/var/opt/gitlab/redis/redis.conf' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'by default' do + let(:gitlab_redis_cli_rc) do + <<-EOF +redis_dir='/var/opt/gitlab/redis' +redis_host='127.0.0.1' +redis_port='0' +redis_socket='/var/opt/gitlab/redis/redis.socket' + EOF + end + + it 'enables the redis service' do + expect(chef_run).to create_redis_service('redis') + end + + it 'creates redis config with default values' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content { |content| + expect(content).to match(/client-output-buffer-limit normal 0 0 0/) + expect(content).to match(/client-output-buffer-limit replica 256mb 64mb 60/) + expect(content).to match(/client-output-buffer-limit pubsub 32mb 8mb 60/) + expect(content).to match(/^hz 10/) + expect(content).to match(/^save 900 1/) + expect(content).to match(/^save 300 10/) + expect(content).to match(/^save 60 10000/) + expect(content).to match(/^maxmemory 0/) + expect(content).to match(/^maxmemory-policy noeviction/) + expect(content).to match(/^maxmemory-samples 5/) + expect(content).to match(/^tcp-backlog 511/) + expect(content).to match(/^rename-command KEYS ""$/) + expect(content).to match(/^lazyfree-lazy-eviction no$/) + expect(content).to match(/^lazyfree-lazy-expire no$/) + expect(content).to match(/^io-threads 1$/) + expect(content).to match(/^io-threads-do-reads no$/) + expect(content).to match(/^stop-writes-on-bgsave-error yes$/) + expect(content).not_to match(/^replicaof/) + expect(content).not_to match(/^tls-/) + } + end + + it 'creates redis user and group' do + expect(chef_run).to create_account('user and group for redis').with(username: 'gitlab-redis', groupname: 'gitlab-redis') + end + + it_behaves_like 'enabled runit service', 'redis', 'root', 'root' + + it 'creates gitlab-redis-cli-rc' do + expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-redis-cli-rc') + .with_content(gitlab_redis_cli_rc) + end + + describe 'pending restart check' do + context 'when running version is same as installed version' do + before do + allow_any_instance_of(RedisHelper).to receive(:running_version).and_return('3.2.12') + allow_any_instance_of(RedisHelper).to receive(:installed_version).and_return('3.2.12') + end + + it 'does not raise a warning' do + expect(chef_run).not_to run_ruby_block('warn pending redis restart') + end + end + + context 'when running version is different than installed version' do + before do + allow_any_instance_of(RedisHelper).to receive(:running_version).and_return('3.2.12') + allow_any_instance_of(RedisHelper).to receive(:installed_version).and_return('5.0.9') + end + + it 'raises a warning' do + expect(chef_run).to run_ruby_block('warn pending redis restart') + end + end + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + redis: { + client_output_buffer_limit_normal: "5 5 5", + client_output_buffer_limit_replica: "512mb 128mb 120", + client_output_buffer_limit_pubsub: "64mb 16mb 120", + save: ["10 15000"], + maxmemory: "32gb", + maxmemory_policy: "allkeys-url", + maxmemory_samples: 10, + tcp_backlog: 1024, + hz: 100, + username: 'foo', + group: 'bar', + rename_commands: { + "FAKE_COMMAND" => "RENAMED_FAKE_COMMAND", + "DISABLED_FAKE_COMMAND" => "" + } + } + ) + end + + it 'creates redis config with custom values' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/client-output-buffer-limit normal 5 5 5/) + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/client-output-buffer-limit replica 512mb 128mb 120/) + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/client-output-buffer-limit pubsub 64mb 16mb 120/) + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^save 10 15000/) + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^maxmemory 32gb/) + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^maxmemory-policy allkeys-url/) + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^maxmemory-samples 10/) + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^tcp-backlog 1024/) + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^hz 100/) + end + + it 'does not include the default renamed keys in redis.conf' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content { |content| + expect(content).not_to match(/^rename-command KEYS ""$/) + expect(content).to match(/^rename-command FAKE_COMMAND "RENAMED_FAKE_COMMAND"$/) + expect(content).to match(/^rename-command DISABLED_FAKE_COMMAND ""$/) + } + end + + it 'creates redis user and group' do + expect(chef_run).to create_account('user and group for redis').with(username: 'foo', groupname: 'bar') + end + + it_behaves_like 'enabled runit service', 'redis', 'root', 'root' + end + + context 'with snapshotting disabled' do + before do + stub_gitlab_rb( + redis: { + save: [] + } + ) + end + it 'creates redis config without save setting' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + expect(chef_run).not_to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^save/) + end + end + + context 'with snapshotting cleared' do + before do + stub_gitlab_rb( + redis: { + save: [""] + } + ) + end + it 'creates redis config without save setting' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^save ""/) + end + end + + context 'with a replica configured' do + let(:redis_host) { '1.2.3.4' } + let(:redis_port) { 6370 } + let(:master_ip) { '10.0.0.0' } + let(:master_port) { 6371 } + + let(:gitlab_redis_cli_rc) do + <<-EOF +redis_dir='/var/opt/gitlab/redis' +redis_host='1.2.3.4' +redis_port='6370' +redis_socket='' + EOF + end + + before do + stub_gitlab_rb( + redis: { + bind: redis_host, + port: redis_port, + master_ip: master_ip, + master_port: master_port, + master_password: 'password', + master: false + } + ) + end + + it 'includes replicaof' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^replicaof #{master_ip} #{master_port}/) + end + + it 'creates gitlab-redis-cli-rc' do + expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-redis-cli-rc') + .with_content(gitlab_redis_cli_rc) + end + end + + context 'in HA mode with Sentinels' do + let(:redis_host) { '1.2.3.4' } + let(:redis_port) { 6370 } + let(:master_ip) { '10.0.0.0' } + let(:master_port) { 6371 } + + let(:gitlab_redis_cli_rc) do + <<-EOF +redis_dir='/var/opt/gitlab/redis' +redis_host='1.2.3.4' +redis_port='6370' +redis_socket='' + EOF + end + + before do + stub_gitlab_rb( + redis: { + bind: redis_host, + port: redis_port, + ha: true, + master_ip: master_ip, + master_port: master_port, + master_password: 'password', + master: false + } + ) + end + + it 'omits replicaof' do + expect(chef_run).not_to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content(/^replicaof/) + end + + it 'creates gitlab-redis-cli-rc' do + expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-redis-cli-rc') + .with_content(gitlab_redis_cli_rc) + end + end + + context 'with rename_commands disabled' do + before do + stub_gitlab_rb( + redis: { + rename_commands: {} + } + ) + end + + it 'should not rename any commands' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content { |content| + expect(content).not_to match(/^rename-command/) + } + end + end + + context 'with lazy eviction enabled' do + before do + stub_gitlab_rb( + redis: { + lazyfree_lazy_eviction: true + } + ) + end + + it 'creates redis config with lazyfree-lazy-eviction yes' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content { |content| + expect(content).to match(/^lazyfree-lazy-eviction yes$/) + expect(content).to match(/^lazyfree-lazy-expire no$/) + } + end + end + + context 'with lazy eviction enabled' do + before do + stub_gitlab_rb( + redis: { + io_threads: 4, + io_threads_do_reads: true + } + ) + end + + it 'creates redis config with lazyfree-lazy-eviction yes' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content { |content| + expect(content).to match(/^io-threads 4$/) + expect(content).to match(/^io-threads-do-reads yes$/) + } + end + end + + context 'with stop writes on bgsave error disabled' do + before do + stub_gitlab_rb( + redis: { + stop_writes_on_bgsave_error: false + } + ) + end + + it 'creates redis config with stop-writes-on-bgsave-error no' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content { |content| + expect(content).to match(/^stop-writes-on-bgsave-error no$/) + } + end + end + + context 'with tls settings specified' do + before do + stub_gitlab_rb( + redis: { + tls_port: 6380, + tls_cert_file: '/etc/gitlab/ssl/redis.crt', + tls_key_file: '/etc/gitlab/ssl/redis.key', + tls_dh_params_file: '/etc/gitlab/ssl/redis-dhparams', + tls_ca_cert_file: '/etc/gitlab/ssl/redis-ca.crt', + tls_ca_cert_dir: '/opt/gitlab/embedded/ssl/certs', + tls_auth_clients: 'no', + tls_replication: 'yes', + tls_cluster: 'yes', + tls_protocols: 'TLSv1.2 TLSv1.3', + tls_ciphers: 'DEFAULT:!MEDIUM', + tls_ciphersuites: 'TLS_CHACHA20_POLY1305_SHA256', + tls_prefer_server_ciphers: 'yes', + tls_session_caching: 'no', + tls_session_cache_size: 10000, + tls_session_cache_timeout: 120 + } + ) + end + + it 'renders redis config with tls settings' do + expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') + .with_content { |content| + expect(content).to match(%r{^tls-port 6380$}) + expect(content).to match(%r{^tls-cert-file /etc/gitlab/ssl/redis.crt$}) + expect(content).to match(%r{^tls-key-file /etc/gitlab/ssl/redis.key$}) + expect(content).to match(%r{^tls-dh-params-file /etc/gitlab/ssl/redis-dhparams$}) + expect(content).to match(%r{^tls-ca-cert-file /etc/gitlab/ssl/redis-ca.crt$}) + expect(content).to match(%r{^tls-ca-cert-dir /opt/gitlab/embedded/ssl/certs$}) + expect(content).to match(%r{^tls-auth-clients no$}) + expect(content).to match(%r{^tls-replication yes$}) + expect(content).to match(%r{^tls-cluster yes$}) + expect(content).to match(%r{^tls-protocols "TLSv1.2 TLSv1.3"$}) + expect(content).to match(%r{^tls-ciphers DEFAULT:!MEDIUM$}) + expect(content).to match(%r{^tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256$}) + expect(content).to match(%r{^tls-prefer-server-ciphers yes$}) + expect(content).to match(%r{^tls-session-caching no$}) + expect(content).to match(%r{^tls-session-cache-size 10000$}) + expect(content).to match(%r{^tls-session-cache-timeout 120$}) + } + end + end + + context 'with redis disabled' do + before do + stub_gitlab_rb(redis: { enable: false }) + end + + it_behaves_like 'disabled runit service', 'redis', 'root', 'root' + end +end diff --git a/spec/chef/cookbooks/registry/recipes/registry_spec.rb b/spec/chef/cookbooks/registry/recipes/registry_spec.rb new file mode 100755 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9yZWdpc3RyeS9yZWNpcGVzL3JlZ2lzdHJ5X3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/registry/recipes/registry_spec.rb @@ -0,0 +1,510 @@ +require 'chef_helper' + +RSpec.describe 'registry recipe' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + describe 'letsencrypt' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + registry_external_url: 'https://registry.example.com' + ) + + allow(File).to receive(:exist?).and_call_original + end + + describe 'HTTP to HTTPS redirection' do + context 'by default' do + it 'is enabled' do + expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-registry.conf').with_content("return 301 https://registry.example.com$request_uri;") + end + end + + context 'if disabled in gitlab.rb' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + registry_external_url: 'https://registry.example.com', + registry_nginx: { + redirect_http_to_https: false + } + ) + end + + it 'is disabled' do + expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-registry.conf') + expect(chef_run).not_to render_file('/var/opt/gitlab/nginx/conf/gitlab-registry.conf').with_content("return 301 https://registry.example.com$request_uri;") + end + end + + context 'registry on gitlab domain with a different port ' do + before do + stub_gitlab_rb( + external_url: 'https://gitlab.example.com', + registry_external_url: 'https://gitlab.example.com:5005' + ) + end + + it 'is enabled and has correct redirect URL in nginx config' do + expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-registry.conf').with_content("return 301 https://gitlab.example.com:5005$request_uri;") + end + end + end + + context 'default certificate file is missing' do + before do + allow(File).to receive(:exist?).with('/etc/gitlab/ssl/registry.example.com.crt').and_return(false) + end + + it 'adds itself to letsencrypt alt_names' do + expect(chef_run.node['letsencrypt']['alt_names']).to eql(['registry.example.com']) + end + + it 'is reflected in the acme_selfsigned' do + expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( + alt_names: ['registry.example.com'] + ) + end + end + + context 'default certificate file is present' do + before do + allow(File).to receive(:exist?).with('/etc/gitlab/ssl/registry.example.com.crt').and_return(true) + end + + it 'does not alter letsencrypt alt_names' do + expect(chef_run.node['letsencrypt']['alt_names']).to eql([]) + end + + it 'is reflected in the acme_selfsigned' do + expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( + alt_names: [] + ) + end + end + end + + context 'when registry is enabled' do + before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com') } + + it_behaves_like 'enabled registry service' + + it_behaves_like 'renders a valid YAML file', '/var/opt/gitlab/registry/config.yml' + + it 'creates a default VERSION file and restarts service' do + expect(chef_run).to create_version_file('Create version file for Registry').with( + version_file_path: '/var/opt/gitlab/registry/VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/registry --version' + ) + + expect(chef_run.version_file('Create version file for Registry')).to notify('runit_service[registry]').to(:restart) + end + + context 'when registry storagedriver health check is disabled' do + before { stub_gitlab_rb(registry: { health_storagedriver_enabled: false }) } + + it 'creates registry config with specified value' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/health:\s*storagedriver:\s*enabled:\s*false/) + end + end + + context 'when registry validation is enabled' do + before { stub_gitlab_rb(registry: { validation_enabled: true }) } + + it 'creates registry config with specified value' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/^validation:\s*disabled: false$/) + end + end + + context 'when registry middleware is enabled' do + let(:middleware_config) do + { "storage" => [ + { "name" => "googlecdn", + "options" => { + "baseurl" => "https://example.org", + "privatekey" => "/etc/gitlab/googlecdn.key", + "keyname" => "example-key" + } } + ] } + end + + before { stub_gitlab_rb(registry: { middleware: middleware_config }) } + + it 'creates registry config with middleware' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(%r(^middleware: {"storage":)) + end + end + + context 'when a log formatter is specified' do + before { stub_gitlab_rb(registry: { log_formatter: 'json' }) } + + it 'creates the registry config with the specified value' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/log:\s*level: info\s*formatter:\s*json/) + end + + it 'does not append timestamp in logs if logging format is json' do + expect(chef_run).to render_file('/opt/gitlab/sv/registry/log/run') + .with_content(/exec svlogd \/var\/log\/gitlab\/registry/) + end + end + + context 'when schema1 compatibility is enabled' do + before { stub_gitlab_rb(registry: { compatibility_schema1_enabled: true }) } + + it 'creates registry config with specified value' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/compatibility:\s*schema1:\s*enabled:\s*true/) + end + end + end + + context 'when registry port is specified' do + before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com', registry: { registry_http_addr: 'localhost:5001' }) } + + it 'creates registry and rails configs with specified value' do + expect(chef_run).to create_templatesymlink('Create a gitlab.yml and create a symlink to Rails root').with_variables(hash_including('registry_api_url' => 'http://localhost:5001')) + + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/addr: localhost:5001/) + end + end + + context 'when a debug addr is specified' do + before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com', registry: { debug_addr: 'localhost:5005' }) } + + it 'creates the registry config with the specified debug value' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/debug:\n\s*addr: localhost:5005/) + end + end + + context 'when user and group are specified' do + before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com', registry: { username: 'registryuser', group: 'registrygroup' }) } + it 'make registry run file start registry under correct user' do + expect(chef_run).to render_file('/opt/gitlab/sv/registry/run') + .with_content(/-U registryuser:registrygroup/) + expect(chef_run).to render_file('/opt/gitlab/sv/registry/run') + .with_content(/-u registryuser:registrygroup/) + end + end +end + +RSpec.describe 'registry' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:default_vars) do + { + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' + } + end + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when registry is enabled' do + before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com') } + + it_behaves_like 'enabled registry service' + + context 'when custom storage parameters are specified' do + before do + stub_gitlab_rb( + registry: { + storage: { + s3: { accesskey: 'awsaccesskey', secretkey: 'awssecretkey', bucketname: 'bucketname' } + } + } + ) + end + + it 'uses custom storage instead of the default rootdirectory' do + expect(chef_run.node['registry']['storage']) + .to include(s3: { accesskey: 'awsaccesskey', secretkey: 'awssecretkey', bucketname: 'bucketname' }) + expect(chef_run.node['registry']['storage']) + .not_to include('rootdirectory' => '/var/opt/gitlab/gitlab-rails/shared/registry') + end + + it 'uses the default cache and delete settings if not overridden' do + expect(chef_run.node['registry']['storage']['cache']) + .to eql('blobdescriptor' => 'inmemory') + expect(chef_run.node['registry']['storage']['delete']) + .to eql('enabled' => true) + end + + it 'allows the cache and delete settings to be overridden' do + stub_gitlab_rb(registry: { storage: { cache: 'somewhere-else', delete: { enabled: false } } }) + expect(chef_run.node['registry']['storage']['cache']) + .to eql('somewhere-else') + expect(chef_run.node['registry']['storage']['delete']) + .to eql('enabled' => false) + end + end + + context 'when storage_delete_enabled is false' do + before { stub_gitlab_rb(registry: { storage_delete_enabled: false }) } + + it 'sets the delete enabled field on the storage object' do + expect(chef_run.node['registry']['storage']['delete']) + .to eql('enabled' => false) + end + end + + context 'when notification is configured for Geo replication' do + before do + stub_gitlab_rb( + registry: { + notifications: [ + { + 'name' => 'geo_event', + 'url' => 'https://registry.example.com/notify', + 'timeout' => '500ms', + 'threshold' => 5, + 'backoff' => '1s', + 'headers' => { + "Authorization" => ["mysecret"] + } + } + ] + } + ) + end + + it 'assigns registry_notification_secret variable automatically' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"Authorization":\["mysecret"\]/) + expect(chef_run.node['gitlab']['gitlab-rails']['registry_notification_secret']) + .to eql('mysecret') + end + end + + context 'when registry notification endpoint is configured with the minimum required' do + before do + stub_gitlab_rb( + registry: { + notifications: [ + name: 'test_endpoint', + url: 'https://registry.example.com/notify' + ] + } + ) + end + + it 'creates the registry config with the specified endpoint config' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"name":"test_endpoint"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"url":"https:\/\/registry.example.com\/notify"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"timeout":"500ms"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"threshold":5/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"backoff":"1s"/) + end + end + + context 'when the default values are overridden' do + before do + stub_gitlab_rb( + registry: { + notifications: [ + name: 'test_endpoint', + url: 'https://registry.example.com/notify' + ], + default_notifications_timeout: '5000ms', + default_notifications_threshold: 10, + default_notifications_backoff: '50s', + default_notifications_headers: { + "Authorization" => %w(AUTHORIZATION_EXAMPLE_TOKEN1 AUTHORIZATION_EXAMPLE_TOKEN2) + } + } + ) + end + + it 'creates the registry config overriding the values not set with the new defaults' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"name":"test_endpoint"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"url":"https:\/\/registry.example.com\/notify"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"timeout":"5000ms"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"threshold":10/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"backoff":"50s"/) + end + end + + context 'when registry notification endpoint is configured with all the available variables' do + before do + stub_gitlab_rb( + registry: { + notifications: [ + { + 'name' => 'test_endpoint', + 'url' => 'https://registry.example.com/notify', + 'timeout' => '500ms', + 'threshold' => 5, + 'backoff' => '1s', + 'headers' => { + "Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"] + } + } + ] + } + ) + end + + it 'creates the registry config with the specified endpoint config' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"name":"test_endpoint"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"url":"https:\/\/registry.example.com\/notify"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"timeout":"500ms"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"threshold":5/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"backoff":"1s"/) + end + end + + context 'when 3 registry notification endpoints are configured' do + before do + stub_gitlab_rb( + registry: { + notifications: [ + { + 'name' => 'test_endpoint', + 'url' => 'https://registry.example.com/notify' + }, + { + 'name' => 'test_endpoint2', + 'url' => 'https://registry.example.com/notify2', + 'timeout' => '100ms', + 'threshold' => 2, + 'backoff' => '4s', + 'headers' => { + "Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"] + } + }, + { + 'name' => 'test_endpoint3', + 'url' => 'https://registry.example.com/notify3' + } + ] + } + ) + end + + it 'creates the registry config with the specified endpoint config' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"name":"test_endpoint"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/\"url\":\"https:\/\/registry.example.com\/notify\"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"timeout":"500ms"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"threshold":5/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"backoff":"1s"/) + # Second endpoint + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"name":"test_endpoint2"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"url":"https:\/\/registry.example.com\/notify2"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"timeout":"100ms"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"threshold":2/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"backoff":"4s"/) + # Third endpoint + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"name":"test_endpoint3"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"url":"https:\/\/registry.example.com\/notify3"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"timeout":"500ms"/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"threshold":5/) + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + .with_content(/"backoff":"1s"/) + end + end + + context 'when registry notification endpoint is not configured' do + it 'creates the registry config without the endpoint config' do + expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') + expect(chef_run).not_to render_file('/var/opt/gitlab/registry/config.yml') + .with_content('notifications:') + end + end + + context 'when registry has custom environment variables configured' do + before do + stub_gitlab_rb(registry: { env: { 'HTTP_PROXY' => 'my-proxy' } }) + end + + it 'creates necessary env variable files' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/registry/env').with_variables( + default_vars.merge( + { + 'HTTP_PROXY' => 'my-proxy' + } + ) + ) + end + end + end +end + +RSpec.describe 'auto enabling registry' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } + let(:registry_config) { '/var/opt/gitlab/registry/config.yml' } + let(:nginx_config) { '/var/opt/gitlab/nginx/conf/gitlab-registry.conf' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + stub_gitlab_rb( + external_url: 'https://gitlab.example.com' + ) + end + + it_behaves_like 'enabled registry service' + + it 'should listen on port 5050 with nginx' do + expect(chef_run).to render_file(nginx_config) + .with_content { |content| + expect(content).to include("listen *:5050 ssl;") + expect(content).to include("server_name gitlab.example.com;") + } + end + + it "should use the default Let's Encrypt certificates" do + expect(chef_run).to render_file(nginx_config) + .with_content { |content| + expect(content).to include("ssl_certificate /etc/gitlab/ssl/gitlab.example.com.crt;") + expect(content).to include("ssl_certificate_key /etc/gitlab/ssl/gitlab.example.com.key;") + } + end + + it 'should point gitlab-rails to the registry' do + expect(chef_run).to create_templatesymlink( + 'Create a gitlab.yml and create a symlink to Rails root' + ).with_variables( + hash_including( + 'registry_host' => 'gitlab.example.com', + 'registry_port' => '5050' + ) + ) + end +end diff --git a/spec/chef/cookbooks/spamcheck/recipes/spamcheck_spec.rb b/spec/chef/cookbooks/spamcheck/recipes/spamcheck_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2Nvb2tib29rcy9zcGFtY2hlY2svcmVjaXBlcy9zcGFtY2hlY2tfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/cookbooks/spamcheck/recipes/spamcheck_spec.rb @@ -0,0 +1,274 @@ +require 'chef_helper' +require 'toml-rb' + +RSpec.describe 'spamcheck' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context 'when spamcheck is disabled (default)' do + it 'includes spamcheck::disable recipe' do + expect(chef_run).to include_recipe('spamcheck::disable') + end + end + + context 'when spamcheck is enabled' do + before do + stub_gitlab_rb( + spamcheck: { + enable: true + } + ) + end + + it 'includes spamcheck::enable recipe' do + expect(chef_run).to include_recipe('spamcheck::enable') + end + end + + describe 'spamcheck::enable' do + context 'with default values' do + before do + stub_gitlab_rb( + spamcheck: { + enable: true + } + ) + end + + it 'creates necessary directories at default locations' do + %w[ + /var/opt/gitlab/spamcheck + /var/opt/gitlab/spamcheck/sockets + /var/log/gitlab/spamcheck + /var/log/gitlab/spam-classifier + ].each do |dir| + expect(chef_run).to create_directory(dir).with( + owner: 'git', + mode: '0700', + recursive: true + ) + end + end + + it 'creates config.toml with default values' do + actual_content = get_rendered_toml(chef_run, '/var/opt/gitlab/spamcheck/config.toml') + expected_content = { + grpc: { + port: '8001' + }, + rest: { + externalPort: '' + }, + logger: { + level: 'info', + format: 'json', + output: 'stdout' + }, + monitor: { + address: ":8003" + }, + extraAttributes: { + monitorMode: 'false' + }, + filter: { + allowList: {}, + denyList: {} + }, + preprocessor: { + socketPath: '/var/opt/gitlab/spamcheck/sockets/preprocessor.sock' + }, + modelAttributes: { + modelPath: '/opt/gitlab/embedded/service/spam-classifier/model/issues/tflite/model.tflite' + } + } + expect(actual_content).to eq(expected_content) + end + + it 'creates env directory with default variables' do + expect(chef_run).to create_env_dir('/opt/gitlab/etc/spamcheck/env').with_variables( + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' + ) + end + + it_behaves_like "enabled runit service", "spamcheck", "root", "root" + it_behaves_like "enabled runit service", "spam-classifier", "root", "root" + + it 'creates runit files for spamcheck service' do + expected_content = <<~EOS + #!/bin/bash + + # Let runit capture all script error messages + exec 2>&1 + + exec chpst -e /opt/gitlab/etc/spamcheck/env -P \\ + -u git:git \\ + -U git:git \\ + /opt/gitlab/embedded/bin/spamcheck -config /var/opt/gitlab/spamcheck/config.toml + EOS + + expect(chef_run).to render_file('/opt/gitlab/sv/spamcheck/run').with_content(expected_content) + end + + it 'creates runit files for spam-classifier service' do + expected_content = <<~EOS + #!/bin/bash + + # Let runit capture all script error messages + exec 2>&1 + + exec chpst -e /opt/gitlab/etc/spamcheck/env -P \\ + -u git:git \\ + -U git:git \\ + /opt/gitlab/embedded/bin/python3 /opt/gitlab/embedded/service/spam-classifier/preprocessor/preprocess.py \\ + --tokenizer-pickle-path /opt/gitlab/embedded/service/spam-classifier/preprocessor/tokenizer.pickle \\ + --log-dir /var/log/gitlab/spam-classifier \\ + --socket-dir /var/opt/gitlab/spamcheck/sockets + EOS + + expect(chef_run).to render_file('/opt/gitlab/sv/spam-classifier/run').with_content(expected_content) + end + end + + context 'with user specified values' do + before do + stub_gitlab_rb( + user: { + username: 'randomuser', + group: 'randomgroup' + }, + spamcheck: { + enable: true, + dir: '/data/spamcheck', + port: 5001, + external_port: 5002, + monitoring_address: ':5003', + log_level: 'debug', + log_format: 'text', + log_output: 'file', + monitor_mode: true, + allowlist: { + '14' => 'spamtest/hello' + }, + denylist: { + '15' => 'foobar/random' + }, + env_directory: '/env/spamcheck', + log_directory: '/log/spamcheck', + env: { + 'FOO' => 'BAR' + }, + classifier: { + log_directory: '/log/spam-classifier' + } + } + ) + end + + it 'creates necessary directories at user specified locations' do + %w[ + /data/spamcheck + /data/spamcheck/sockets + /log/spamcheck + /log/spam-classifier + ].each do |dir| + expect(chef_run).to create_directory(dir).with( + owner: 'randomuser', + mode: '0700', + recursive: true + ) + end + end + + it 'creates config.toml with user specified values' do + actual_content = get_rendered_toml(chef_run, '/data/spamcheck/config.toml') + expected_content = { + grpc: { + port: '5001' + }, + rest: { + externalPort: '5002' + }, + logger: { + level: 'debug', + format: 'text', + output: 'file' + }, + monitor: { + address: ":5003" + }, + extraAttributes: { + monitorMode: 'true' + }, + filter: { + allowList: { + '14': 'spamtest/hello' + }, + denyList: { + '15': 'foobar/random' + } + }, + preprocessor: { + socketPath: '/data/spamcheck/sockets/preprocessor.sock' + }, + modelAttributes: { + modelPath: '/opt/gitlab/embedded/service/spam-classifier/model/issues/tflite/model.tflite' + } + } + expect(actual_content).to eq(expected_content) + end + + it 'creates env directory with user specified and default variables' do + expect(chef_run).to create_env_dir('/env/spamcheck').with_variables( + 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', + 'FOO' => 'BAR' + ) + end + + it_behaves_like "enabled runit service", "spamcheck", "root", "root" + it_behaves_like "enabled runit service", "spam-classifier", "root", "root" + + it 'creates runit files for spamcheck service' do + expected_content = <<~EOS + #!/bin/bash + + # Let runit capture all script error messages + exec 2>&1 + + exec chpst -e /env/spamcheck -P \\ + -u randomuser:randomgroup \\ + -U randomuser:randomgroup \\ + /opt/gitlab/embedded/bin/spamcheck -config /data/spamcheck/config.toml + EOS + + expect(chef_run).to render_file('/opt/gitlab/sv/spamcheck/run').with_content(expected_content) + end + + it 'creates runit files for spam-classifier service' do + expected_content = <<~EOS + #!/bin/bash + + # Let runit capture all script error messages + exec 2>&1 + + exec chpst -e /env/spamcheck -P \\ + -u randomuser:randomgroup \\ + -U randomuser:randomgroup \\ + /opt/gitlab/embedded/bin/python3 /opt/gitlab/embedded/service/spam-classifier/preprocessor/preprocess.py \\ + --tokenizer-pickle-path /opt/gitlab/embedded/service/spam-classifier/preprocessor/tokenizer.pickle \\ + --log-dir /log/spam-classifier \\ + --socket-dir /data/spamcheck/sockets + EOS + + expect(chef_run).to render_file('/opt/gitlab/sv/spam-classifier/run').with_content(expected_content) + end + end + end + + describe 'spamcheck::disable' do + it_behaves_like "disabled runit service", "spamcheck", "root", "root" + it_behaves_like "disabled runit service", "spam-classifier", "root", "root" + end +end diff --git a/spec/chef/crond/resources/crond_job_spec.rb b/spec/chef/crond/resources/crond_job_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2Nyb25kL3Jlc291cmNlcy9jcm9uZF9qb2Jfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/crond/resources/crond_job_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'crond_job' do - context 'crond enabled' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(crond_job)) do |node| - node.normal['crond']['cron_d'] = "prefix" - end - end - - context 'delete' do - let(:chef_run) { runner.converge("crond::enable", "test_crond::crond_job_delete") } - - it 'should delete the file' do - expect(chef_run).to delete_file("prefix/delete") - end - - it 'should notify the service' do - expect(chef_run.file("prefix/delete")) - .to notify("runit_service[crond]").to(:restart) - end - end - - context 'minimal' do - let(:chef_run) { runner.converge("crond::enable", "test_crond::crond_job_minimal") } - - it 'should set up the file' do - expect(chef_run).to create_file("prefix/minimal").with( - owner: "root", - content: "* * * * * rspec echo 'Hello world'\n" - ) - end - - it 'should notify the service' do - expect(chef_run.file("prefix/minimal")) - .to notify("runit_service[crond]").to(:restart) - end - end - end - - context 'crond disabled' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(crond_job)) do |node| - node.normal['crond']['cron_d'] = "prefix" - end - end - - context 'delete' do - let(:chef_run) { runner.converge("test_crond::crond_job_delete") } - - it 'should delete the file' do - expect(chef_run).to delete_file("prefix/delete") - end - - it 'should not try and notify the service' do - expect(chef_run.file("prefix/delete")) - .not_to notify("runit_service[crond]") - end - end - - context 'minimal' do - let(:chef_run) { runner.converge("test_crond::crond_job_minimal") } - - it 'should not set up the file' do - expect(chef_run).not_to create_file("prefix/minimal") - end - end - end -end diff --git a/spec/chef/fixtures/cookbooks/acme/attributes/default.rb b/spec/chef/fixtures/cookbooks/acme/attributes/default.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy9hY21lL2F0dHJpYnV0ZXMvZGVmYXVsdC5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/acme/attributes/default.rb @@ -0,0 +1,1 @@ +default['acme']['key_size'] = 2048 diff --git a/spec/chef/fixtures/cookbooks/acme/metadata.rb b/spec/chef/fixtures/cookbooks/acme/metadata.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy9hY21lL21ldGFkYXRhLnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/acme/metadata.rb @@ -0,0 +1,1 @@ +name 'acme' diff --git a/spec/chef/fixtures/cookbooks/acme/resources/certificate.rb b/spec/chef/fixtures/cookbooks/acme/resources/certificate.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy9hY21lL3Jlc291cmNlcy9jZXJ0aWZpY2F0ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/acme/resources/certificate.rb @@ -0,0 +1,15 @@ +property :cn, String, name_property: true +property :crt, String +property :dir, [String, nil] +property :key, String +property :owner, [String, nil] +property :chain, [String, nil] +property :wwwroot, String +property :alt_names, Array +property :key_size, [Integer, nil] +property :crt, [String, nil] +property :group, [String, nil] +property :contact, [String, Array, nil] + +actions :create +default_action :create diff --git a/spec/chef/fixtures/cookbooks/acme/resources/selfsigned.rb b/spec/chef/fixtures/cookbooks/acme/resources/selfsigned.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy9hY21lL3Jlc291cmNlcy9zZWxmc2lnbmVkLnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/acme/resources/selfsigned.rb @@ -0,0 +1,8 @@ +property :crt, String +property :key, String +property :key_size, Integer +property :chain, String +property :alt_names, Array + +actions :create +default_action :create diff --git a/spec/chef/fixtures/cookbooks/test_consul/metadata.rb b/spec/chef/fixtures/cookbooks/test_consul/metadata.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2NvbnN1bC9tZXRhZGF0YS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_consul/metadata.rb @@ -0,0 +1,2 @@ +name 'test_consul' +depends 'consul' diff --git a/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_address_port.rb b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_address_port.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2NvbnN1bC9yZWNpcGVzL2NvbnN1bF9zZXJ2aWNlX2FkZHJlc3NfcG9ydC5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_address_port.rb @@ -0,0 +1,6 @@ +include_recipe 'test_consul::reload_consul' + +consul_service 'node-exporter' do + ip_address '0.0.0.0' + port 1234 +end diff --git a/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_delete.rb b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_delete.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2NvbnN1bC9yZWNpcGVzL2NvbnN1bF9zZXJ2aWNlX2RlbGV0ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_delete.rb @@ -0,0 +1,5 @@ +include_recipe 'test_consul::reload_consul' + +consul_service 'delete_me' do + action :delete +end diff --git a/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_delete_no_reload.rb b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_delete_no_reload.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2NvbnN1bC9yZWNpcGVzL2NvbnN1bF9zZXJ2aWNlX2RlbGV0ZV9ub19yZWxvYWQucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_delete_no_reload.rb @@ -0,0 +1,6 @@ +include_recipe 'test_consul::reload_consul' + +consul_service 'delete_no_reload' do + action :delete + reload_service false +end diff --git a/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_meta.rb b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_meta.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2NvbnN1bC9yZWNpcGVzL2NvbnN1bF9zZXJ2aWNlX21ldGEucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_meta.rb @@ -0,0 +1,9 @@ +include_recipe 'test_consul::reload_consul' + +meta = { some_key: "value" } + +consul_service 'node-exporter' do + ip_address '0.0.0.0' + port 1234 + meta meta +end diff --git a/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_socket.rb b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_socket.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2NvbnN1bC9yZWNpcGVzL2NvbnN1bF9zZXJ2aWNlX3NvY2tldC5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_consul/recipes/consul_service_socket.rb @@ -0,0 +1,6 @@ +include_recipe 'test_consul::reload_consul' + +consul_service 'node-exporter' do + socket_address '0.0.0.0:5678' + reload_service false +end diff --git a/spec/chef/fixtures/cookbooks/test_consul/recipes/reload_consul.rb b/spec/chef/fixtures/cookbooks/test_consul/recipes/reload_consul.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2NvbnN1bC9yZWNpcGVzL3JlbG9hZF9jb25zdWwucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_consul/recipes/reload_consul.rb @@ -0,0 +1,4 @@ +# A stub to satisfy the `consul_service` +execute 'reload consul' do + action :nothing +end diff --git a/spec/chef/fixtures/cookbooks/test_crond/metadata.rb b/spec/chef/fixtures/cookbooks/test_crond/metadata.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2Nyb25kL21ldGFkYXRhLnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_crond/metadata.rb @@ -0,0 +1,2 @@ +name 'test_crond' +depends 'crond' diff --git a/spec/chef/fixtures/cookbooks/test_crond/recipes/crond_job_delete.rb b/spec/chef/fixtures/cookbooks/test_crond/recipes/crond_job_delete.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2Nyb25kL3JlY2lwZXMvY3JvbmRfam9iX2RlbGV0ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_crond/recipes/crond_job_delete.rb @@ -0,0 +1,3 @@ +crond_job 'delete' do + action :delete +end diff --git a/spec/chef/fixtures/cookbooks/test_crond/recipes/crond_job_minimal.rb b/spec/chef/fixtures/cookbooks/test_crond/recipes/crond_job_minimal.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2Nyb25kL3JlY2lwZXMvY3JvbmRfam9iX21pbmltYWwucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_crond/recipes/crond_job_minimal.rb @@ -0,0 +1,4 @@ +crond_job 'minimal' do + user "rspec" + command "echo 'Hello world'" +end diff --git a/spec/chef/fixtures/cookbooks/test_gitlab/metadata.rb b/spec/chef/fixtures/cookbooks/test_gitlab/metadata.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2dpdGxhYi9tZXRhZGF0YS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_gitlab/metadata.rb @@ -0,0 +1,2 @@ +name 'test_gitlab' +depends 'gitlab' diff --git a/spec/chef/fixtures/cookbooks/test_gitlab/recipes/puma_config_create.rb b/spec/chef/fixtures/cookbooks/test_gitlab/recipes/puma_config_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2dpdGxhYi9yZWNpcGVzL3B1bWFfY29uZmlnX2NyZWF0ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_gitlab/recipes/puma_config_create.rb @@ -0,0 +1,12 @@ +puma_config '/var/opt/gitlab/gitlab-rails/etc/puma.rb' do + environment 'production' + pid '/opt/gitlab/var/puma/puma.pid' + state_path '/opt/gitlab/var/puma/puma.state' + listen_socket '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket' + listen_tcp '127.0.0.1:8080' + working_directory '/var/opt/gitlab/gitlab-rails/working' + worker_processes 2 + cookbook 'gitlab' + + action :create +end diff --git a/spec/chef/fixtures/cookbooks/test_gitlab/recipes/puma_config_custom.rb b/spec/chef/fixtures/cookbooks/test_gitlab/recipes/puma_config_custom.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2dpdGxhYi9yZWNpcGVzL3B1bWFfY29uZmlnX2N1c3RvbS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_gitlab/recipes/puma_config_custom.rb @@ -0,0 +1,14 @@ +puma_config '/var/opt/gitlab/gitlab-rails/etc/puma.rb' do + environment 'production' + pid '/opt/gitlab/var/puma/puma.pid' + state_path '/opt/gitlab/var/puma/puma.state' + listen_socket '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket' + listen_tcp '127.0.0.1:8080' + working_directory '/var/opt/gitlab/gitlab-rails/working' + worker_processes 2 + per_worker_max_memory_mb 1000 + install_dir '/opt/custom/gitlab' + cookbook 'gitlab' + + action :create +end diff --git a/spec/chef/fixtures/cookbooks/test_gitlab/recipes/rails_migration_run.rb b/spec/chef/fixtures/cookbooks/test_gitlab/recipes/rails_migration_run.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2dpdGxhYi9yZWNpcGVzL3JhaWxzX21pZ3JhdGlvbl9ydW4ucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_gitlab/recipes/rails_migration_run.rb @@ -0,0 +1,12 @@ +ruby_block 'test-dependent' do + true +end + +rails_migration 'gitlab-test' do + rake_task 'gitlab:db:configure' + logfile_prefix 'gitlab-test-db-migrate' + helper RailsMigrationHelper.new(node) + environment Hash['SOME_ENV' => 'some-value'] + + dependent_services ["ruby_block[test-dependent]"] +end diff --git a/spec/chef/fixtures/cookbooks/test_gitlab_ee/metadata.rb b/spec/chef/fixtures/cookbooks/test_gitlab_ee/metadata.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2dpdGxhYl9lZS9tZXRhZGF0YS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_gitlab_ee/metadata.rb @@ -0,0 +1,2 @@ +name 'test_gitlab_ee' +depends 'gitlab-ee' diff --git a/spec/chef/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_geo.rb b/spec/chef/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_geo.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2dpdGxhYl9lZS9yZWNpcGVzL3BnYm91bmNlcl91c2VyX2NyZWF0ZV9nZW8ucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_geo.rb @@ -0,0 +1,8 @@ +pgbouncer_user 'geo' do + helper GeoPgHelper.new(node) + user 'pgbouncer-geo' + password 'fakepassword-geo' + database 'fakedb-geo' + add_auth_function true + action :create +end diff --git a/spec/chef/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_rails.rb b/spec/chef/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_rails.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X2dpdGxhYl9lZS9yZWNpcGVzL3BnYm91bmNlcl91c2VyX2NyZWF0ZV9yYWlscy5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_rails.rb @@ -0,0 +1,8 @@ +pgbouncer_user 'rails' do + helper PgHelper.new(node) + user 'pgbouncer-rails' + password 'fakepassword-rails' + database 'fakedb-rails' + add_auth_function true + action :create +end diff --git a/spec/chef/fixtures/cookbooks/test_package/metadata.rb b/spec/chef/fixtures/cookbooks/test_package/metadata.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvbWV0YWRhdGEucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/metadata.rb @@ -0,0 +1,3 @@ +name 'test_package' +depends 'package' +depends 'runit' diff --git a/spec/chef/fixtures/cookbooks/test_package/recipes/account_create.rb b/spec/chef/fixtures/cookbooks/test_package/recipes/account_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvcmVjaXBlcy9hY2NvdW50X2NyZWF0ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/recipes/account_create.rb @@ -0,0 +1,7 @@ +account 'foobar' do + username 'foo' + groupname 'bar' + manage true + + action :create +end diff --git a/spec/chef/fixtures/cookbooks/test_package/recipes/account_remove.rb b/spec/chef/fixtures/cookbooks/test_package/recipes/account_remove.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvcmVjaXBlcy9hY2NvdW50X3JlbW92ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/recipes/account_remove.rb @@ -0,0 +1,7 @@ +account 'foobar' do + username 'foo' + groupname 'bar' + manage true + + action :remove +end diff --git a/spec/chef/fixtures/cookbooks/test_package/recipes/env_dir_create.rb b/spec/chef/fixtures/cookbooks/test_package/recipes/env_dir_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvcmVjaXBlcy9lbnZfZGlyX2NyZWF0ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/recipes/env_dir_create.rb @@ -0,0 +1,8 @@ +vars = { + 'FOO' => 'Lorem', + 'BAR' => 'Ipsum' +} + +env_dir '/tmp/env' do + variables vars +end diff --git a/spec/chef/fixtures/cookbooks/test_package/recipes/gitlab_sysctl_create.rb b/spec/chef/fixtures/cookbooks/test_package/recipes/gitlab_sysctl_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvcmVjaXBlcy9naXRsYWJfc3lzY3RsX2NyZWF0ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/recipes/gitlab_sysctl_create.rb @@ -0,0 +1,3 @@ +gitlab_sysctl 'foo' do + value 15000 +end diff --git a/spec/chef/fixtures/cookbooks/test_package/recipes/storage_directory_create.rb b/spec/chef/fixtures/cookbooks/test_package/recipes/storage_directory_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvcmVjaXBlcy9zdG9yYWdlX2RpcmVjdG9yeV9jcmVhdGUucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/recipes/storage_directory_create.rb @@ -0,0 +1,5 @@ +storage_directory '/tmp/bar' do + owner 'gitfoo' + group 'gitbar' + mode '0700' +end diff --git a/spec/chef/fixtures/cookbooks/test_package/recipes/templatesymlink_create.rb b/spec/chef/fixtures/cookbooks/test_package/recipes/templatesymlink_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvcmVjaXBlcy90ZW1wbGF0ZXN5bWxpbmtfY3JlYXRlLnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/recipes/templatesymlink_create.rb @@ -0,0 +1,6 @@ +templatesymlink 'sample template' do + link_from '/opt/gitlab/embedded/service/gitlab-rails/config/database.yml' + link_to '/var/opt/gitlab/gitlab-rails/etc/database.yml' + source 'test-template.erb' + variables(value: 500) +end diff --git a/spec/chef/fixtures/cookbooks/test_package/recipes/templatesymlink_delete.rb b/spec/chef/fixtures/cookbooks/test_package/recipes/templatesymlink_delete.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvcmVjaXBlcy90ZW1wbGF0ZXN5bWxpbmtfZGVsZXRlLnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/recipes/templatesymlink_delete.rb @@ -0,0 +1,6 @@ +templatesymlink 'delete' do + link_from '/opt/gitlab/embedded/service/gitlab-rails/config/database.yml' + link_to '/var/opt/gitlab/gitlab-rails/etc/database.yml' + + action :delete +end diff --git a/spec/chef/fixtures/cookbooks/test_package/recipes/version_file_create.rb b/spec/chef/fixtures/cookbooks/test_package/recipes/version_file_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvcmVjaXBlcy92ZXJzaW9uX2ZpbGVfY3JlYXRlLnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/recipes/version_file_create.rb @@ -0,0 +1,8 @@ +runit_service 'foo' do +end + +version_file 'Test version file creation' do + version_file_path '/tmp/VERSION_TEST' + version_check_cmd 'echo 1.0.0-test' + notifies :hup, "runit_service[foo]" +end diff --git a/spec/chef/fixtures/cookbooks/test_package/templates/default/test-template.erb b/spec/chef/fixtures/cookbooks/test_package/templates/default/test-template.erb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3BhY2thZ2UvdGVtcGxhdGVzL2RlZmF1bHQvdGVzdC10ZW1wbGF0ZS5lcmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_package/templates/default/test-template.erb @@ -0,0 +1,1 @@ +value: <%= @value %> diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/metadata.rb b/spec/chef/fixtures/cookbooks/test_postgresql/metadata.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvbWV0YWRhdGEucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/metadata.rb @@ -0,0 +1,3 @@ +name 'test_postgresql' +depends 'postgresql' +depends 'gitlab' diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_config.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_config.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX2NvbmZpZy5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_config.rb @@ -0,0 +1,1 @@ +postgresql_config 'testconfig' diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX2RhdGFiYXNlX2NyZWF0ZS5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create.rb @@ -0,0 +1,2 @@ +postgresql_database 'example' do +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create_with_options.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create_with_options.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX2RhdGFiYXNlX2NyZWF0ZV93aXRoX29wdGlvbnMucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create_with_options.rb @@ -0,0 +1,5 @@ +postgresql_database 'fakedb' do + database_port 9999 + database_socket '/fake/dir' + owner 'fakeuser' +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_extension_enable.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_extension_enable.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX2V4dGVuc2lvbl9lbmFibGUucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_extension_enable.rb @@ -0,0 +1,3 @@ +postgresql_extension 'foobar' do + database 'lorem' +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_query_run.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_query_run.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3F1ZXJ5X3J1bi5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_query_run.rb @@ -0,0 +1,10 @@ +postgresql_query 'create schema' do + db_name 'omnibus_test' + query "CREATE SCHEMA example AUTHORIZATION foobar;" +end + +postgresql_query 'create schema' do + db_name 'omnibus_test' + query "CREATE SCHEMA example AUTHORIZATION foobar;" + helper GeoPgHelper.new(node) +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_schema_create.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_schema_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3NjaGVtYV9jcmVhdGUucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_schema_create.rb @@ -0,0 +1,3 @@ +postgresql_schema 'example' do + database 'omnibus_gitlab_test' +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_create.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_create.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3VzZXJfY3JlYXRlLnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_create.rb @@ -0,0 +1,2 @@ +postgresql_user 'example' do +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_superuser.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_superuser.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3VzZXJfb3B0aW9uc19zdXBlcnVzZXIucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_superuser.rb @@ -0,0 +1,3 @@ +postgresql_user 'example' do + options ['SUPERUSER'] +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_unspecified.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_unspecified.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3VzZXJfb3B0aW9uc191bnNwZWNpZmllZC5yYg== --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_unspecified.rb @@ -0,0 +1,2 @@ +postgresql_user 'example' do +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_empty.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_empty.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3VzZXJfcGFzc3dvcmRfZW1wdHkucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_empty.rb @@ -0,0 +1,3 @@ +postgresql_user 'empty_password' do + password '' +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_md5.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_md5.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3VzZXJfcGFzc3dvcmRfbWQ1LnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_md5.rb @@ -0,0 +1,5 @@ +postgresql_user 'md5_password' do + # This is not a secret password: + # ruby -rdigest -e 'puts Digest::MD5.hexdigest("toomanysecrets" + "md5_password")' + password 'e99b79fbdf9b997e6918df2385e60f5c' +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_nil.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_nil.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3VzZXJfcGFzc3dvcmRfbmlsLnJi --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_nil.rb @@ -0,0 +1,3 @@ +postgresql_user 'nil_password' do + password nil +end diff --git a/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_unspecified.rb b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_unspecified.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2Nvb2tib29rcy90ZXN0X3Bvc3RncmVzcWwvcmVjaXBlcy9wb3N0Z3Jlc3FsX3VzZXJfcGFzc3dvcmRfdW5zcGVjaWZpZWQucmI= --- /dev/null +++ b/spec/chef/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_unspecified.rb @@ -0,0 +1,2 @@ +postgresql_user 'no_password' do +end diff --git a/spec/chef/fixtures/fauxhai/ubuntu/16.04-docker.json b/spec/chef/fixtures/fauxhai/ubuntu/16.04-docker.json new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2ZhdXhoYWkvdWJ1bnR1LzE2LjA0LWRvY2tlci5qc29u --- /dev/null +++ b/spec/chef/fixtures/fauxhai/ubuntu/16.04-docker.json @@ -0,0 +1,2130 @@ +{ + "block_device": { + "sda": { + "size": "1677721600", + "removable": "0", + "model": "PersistentDisk", + "rev": "1", + "state": "running", + "timeout": "30", + "vendor": "Google", + "queue_depth": "256", + "rotational": "1", + "physical_block_size": "4096", + "logical_block_size": "512" + }, + "loop0": { + "size": "177368", + "removable": "0", + "rotational": "1", + "physical_block_size": "512", + "logical_block_size": "512" + }, + "loop1": { + "size": "177960", + "removable": "0", + "rotational": "1", + "physical_block_size": "512", + "logical_block_size": "512" + }, + "loop2": { + "size": "177928", + "removable": "0", + "rotational": "1", + "physical_block_size": "512", + "logical_block_size": "512" + }, + "loop3": { + "size": "0", + "removable": "0", + "rotational": "1", + "physical_block_size": "512", + "logical_block_size": "512" + }, + "loop4": { + "size": "0", + "removable": "0", + "rotational": "1", + "physical_block_size": "512", + "logical_block_size": "512" + }, + "loop5": { + "size": "0", + "removable": "0", + "rotational": "1", + "physical_block_size": "512", + "logical_block_size": "512" + }, + "loop6": { + "size": "0", + "removable": "0", + "rotational": "1", + "physical_block_size": "512", + "logical_block_size": "512" + }, + "loop7": { + "size": "0", + "removable": "0", + "rotational": "1", + "physical_block_size": "512", + "logical_block_size": "512" + } + }, + "chef_packages": { + "ohai": { + "version": "14.3.0", + "ohai_root": "/var/lib/gems/2.4.0/gems/ohai-14.3.0/lib/ohai" + } + }, + "command": { + "ps": "ps -ef" + }, + "counters": { + "network": { + "interfaces": { + "lo": { + "tx": { + "queuelen": "1", + "bytes": 0, + "packets": 0, + "errors": 0, + "drop": 0, + "carrier": 0, + "collisions": 0 + }, + "rx": { + "bytes": 0, + "packets": 0, + "errors": 0, + "drop": 0, + "overrun": 0 + } + }, + "eth0": { + "rx": { + "bytes": 0, + "packets": 0, + "errors": 0, + "drop": 0, + "overrun": 0, + "frame": 0, + "compressed": 0, + "multicast": 0 + }, + "tx": { + "bytes": 0, + "packets": 0, + "errors": 0, + "drop": 0, + "overrun": 0, + "collisions": 0, + "carrier": 0, + "compressed": 0 + } + } + } + } + }, + "cpu": { + "real": 1, + "total": 1, + "cores": 1 + }, + "current_user": "fauxhai", + "dmi": { + }, + "domain": "local", + "filesystem": { + "by_device": { + "overlay": { + "kb_size": "813139348", + "kb_used": "314504628", + "kb_available": "498618336", + "percent_used": "39%", + "total_inodes": "102400000", + "inodes_used": "7082272", + "inodes_available": "95317728", + "inodes_percent_used": "7%", + "fs_type": "overlay", + "mount_options": [ + "rw", + "relatime", + "lowerdir=/var/lib/docker/overlay2/l/UVFEI53IFKBMSYIBD6MEWPNZ6F:/var/lib/docker/overlay2/l/BBMNBZRYFC727VBBGOEXGFD3HA:/var/lib/docker/overlay2/l/3CXZ5G5TCWJEUVMPSRSMBQD6Z4:/var/lib/docker/overlay2/l/GCI6OPPHSEJXZTDES7HKM4FJOA:/var/lib/docker/overlay2/l/XQ7VNACY7RTHN2VODETXL7PG2B:/var/lib/docker/overlay2/l/O63N6IYQUXRP6ATLHTRWF76PUK", + "upperdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/diff", + "workdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/work" + ], + "mounts": [ + "/" + ] + }, + "tmpfs": { + "kb_size": "3829544", + "kb_used": "0", + "kb_available": "3829544", + "percent_used": "0%", + "total_inodes": "957386", + "inodes_used": "1", + "inodes_available": "957385", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ], + "mounts": [ + "/dev", + "/sys/fs/cgroup", + "/sys/firmware", + "/proc/kcore", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug" + ] + }, + "/dev/sda1": { + "kb_size": "813139348", + "kb_used": "314504628", + "kb_available": "498618336", + "percent_used": "39%", + "total_inodes": "102400000", + "inodes_used": "7082272", + "inodes_available": "95317728", + "inodes_percent_used": "7%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "mounts": [ + "/etc/hosts", + "/etc/resolv.conf", + "/etc/hostname" + ] + }, + "shm": { + "kb_size": "65536", + "kb_used": "0", + "kb_available": "65536", + "percent_used": "0%", + "total_inodes": "957386", + "inodes_used": "1", + "inodes_available": "957385", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=65536k" + ], + "mounts": [ + "/dev/shm" + ] + }, + "proc": { + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ], + "mounts": [ + "/proc", + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + }, + "devpts": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=666" + ], + "mounts": [ + "/dev/pts", + "/dev/console" + ] + }, + "sysfs": { + "fs_type": "sysfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys" + ] + }, + "cgroup": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ], + "mounts": [ + "/sys/fs/cgroup/systemd", + "/sys/fs/cgroup/freezer", + "/sys/fs/cgroup/hugetlb", + "/sys/fs/cgroup/cpu,cpuacct", + "/sys/fs/cgroup/net_cls,net_prio", + "/sys/fs/cgroup/pids", + "/sys/fs/cgroup/blkio", + "/sys/fs/cgroup/devices", + "/sys/fs/cgroup/cpuset", + "/sys/fs/cgroup/perf_event", + "/sys/fs/cgroup/memory" + ] + }, + "mqueue": { + "fs_type": "mqueue", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/dev/mqueue" + ] + }, + "sda": { + "mounts": [ + + ] + }, + "sda1": { + "mounts": [ + + ] + }, + "loop0": { + "mounts": [ + + ] + }, + "loop1": { + "mounts": [ + + ] + }, + "loop2": { + "mounts": [ + + ] + } + }, + "by_mountpoint": { + "/": { + "kb_size": "813139348", + "kb_used": "314504628", + "kb_available": "498618336", + "percent_used": "39%", + "total_inodes": "102400000", + "inodes_used": "7082272", + "inodes_available": "95317728", + "inodes_percent_used": "7%", + "fs_type": "overlay", + "mount_options": [ + "rw", + "relatime", + "lowerdir=/var/lib/docker/overlay2/l/UVFEI53IFKBMSYIBD6MEWPNZ6F:/var/lib/docker/overlay2/l/BBMNBZRYFC727VBBGOEXGFD3HA:/var/lib/docker/overlay2/l/3CXZ5G5TCWJEUVMPSRSMBQD6Z4:/var/lib/docker/overlay2/l/GCI6OPPHSEJXZTDES7HKM4FJOA:/var/lib/docker/overlay2/l/XQ7VNACY7RTHN2VODETXL7PG2B:/var/lib/docker/overlay2/l/O63N6IYQUXRP6ATLHTRWF76PUK", + "upperdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/diff", + "workdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/work" + ], + "devices": [ + "overlay" + ] + }, + "/dev": { + "kb_size": "65536", + "kb_used": "0", + "kb_available": "65536", + "percent_used": "0%", + "total_inodes": "957386", + "inodes_used": "17", + "inodes_available": "957369", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/sys/fs/cgroup": { + "kb_size": "3829544", + "kb_used": "0", + "kb_available": "3829544", + "percent_used": "0%", + "total_inodes": "957386", + "inodes_used": "16", + "inodes_available": "957370", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/etc/hosts": { + "kb_size": "813139348", + "kb_used": "314504628", + "kb_available": "498618336", + "percent_used": "39%", + "total_inodes": "102400000", + "inodes_used": "7082272", + "inodes_available": "95317728", + "inodes_percent_used": "7%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "devices": [ + "/dev/sda1" + ] + }, + "/dev/shm": { + "kb_size": "65536", + "kb_used": "0", + "kb_available": "65536", + "percent_used": "0%", + "total_inodes": "957386", + "inodes_used": "1", + "inodes_available": "957385", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=65536k" + ], + "devices": [ + "shm" + ] + }, + "/sys/firmware": { + "kb_size": "3829544", + "kb_used": "0", + "kb_available": "3829544", + "percent_used": "0%", + "total_inodes": "957386", + "inodes_used": "1", + "inodes_available": "957385", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "relatime" + ], + "devices": [ + "tmpfs" + ] + }, + "/proc": { + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/dev/pts": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=666" + ], + "devices": [ + "devpts" + ] + }, + "/sys": { + "fs_type": "sysfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "sysfs" + ] + }, + "/sys/fs/cgroup/systemd": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "xattr", + "release_agent=/lib/systemd/systemd-cgroups-agent", + "name=systemd" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/freezer": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "freezer" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/hugetlb": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/cpu,cpuacct": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpu", + "cpuacct" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/net_cls,net_prio": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "net_cls", + "net_prio" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/pids": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "pids" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/blkio": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "blkio" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/devices": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "devices" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/cpuset": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpuset" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/perf_event": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "perf_event" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/memory": { + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ], + "devices": [ + "cgroup" + ] + }, + "/dev/mqueue": { + "fs_type": "mqueue", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "mqueue" + ] + }, + "/etc/resolv.conf": { + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "devices": [ + "/dev/sda1" + ] + }, + "/etc/hostname": { + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "devices": [ + "/dev/sda1" + ] + }, + "/dev/console": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=666" + ], + "devices": [ + "devpts" + ] + }, + "/proc/bus": { + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/proc/fs": { + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/proc/irq": { + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/proc/sys": { + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/proc/sysrq-trigger": { + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/proc/kcore": { + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/proc/timer_list": { + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/proc/timer_stats": { + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/proc/sched_debug": { + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + } + }, + "by_pair": { + "overlay,/": { + "device": "overlay", + "kb_size": "813139348", + "kb_used": "314504628", + "kb_available": "498618336", + "percent_used": "39%", + "mount": "/", + "total_inodes": "102400000", + "inodes_used": "7082272", + "inodes_available": "95317728", + "inodes_percent_used": "7%", + "fs_type": "overlay", + "mount_options": [ + "rw", + "relatime", + "lowerdir=/var/lib/docker/overlay2/l/UVFEI53IFKBMSYIBD6MEWPNZ6F:/var/lib/docker/overlay2/l/BBMNBZRYFC727VBBGOEXGFD3HA:/var/lib/docker/overlay2/l/3CXZ5G5TCWJEUVMPSRSMBQD6Z4:/var/lib/docker/overlay2/l/GCI6OPPHSEJXZTDES7HKM4FJOA:/var/lib/docker/overlay2/l/XQ7VNACY7RTHN2VODETXL7PG2B:/var/lib/docker/overlay2/l/O63N6IYQUXRP6ATLHTRWF76PUK", + "upperdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/diff", + "workdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/work" + ] + }, + "tmpfs,/dev": { + "device": "tmpfs", + "kb_size": "65536", + "kb_used": "0", + "kb_available": "65536", + "percent_used": "0%", + "mount": "/dev", + "total_inodes": "957386", + "inodes_used": "17", + "inodes_available": "957369", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ] + }, + "tmpfs,/sys/fs/cgroup": { + "device": "tmpfs", + "kb_size": "3829544", + "kb_used": "0", + "kb_available": "3829544", + "percent_used": "0%", + "mount": "/sys/fs/cgroup", + "total_inodes": "957386", + "inodes_used": "16", + "inodes_available": "957370", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "mode=755" + ] + }, + "/dev/sda1,/etc/hosts": { + "device": "/dev/sda1", + "kb_size": "813139348", + "kb_used": "314504628", + "kb_available": "498618336", + "percent_used": "39%", + "mount": "/etc/hosts", + "total_inodes": "102400000", + "inodes_used": "7082272", + "inodes_available": "95317728", + "inodes_percent_used": "7%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ] + }, + "shm,/dev/shm": { + "device": "shm", + "kb_size": "65536", + "kb_used": "0", + "kb_available": "65536", + "percent_used": "0%", + "mount": "/dev/shm", + "total_inodes": "957386", + "inodes_used": "1", + "inodes_available": "957385", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=65536k" + ] + }, + "tmpfs,/sys/firmware": { + "device": "tmpfs", + "kb_size": "3829544", + "kb_used": "0", + "kb_available": "3829544", + "percent_used": "0%", + "mount": "/sys/firmware", + "total_inodes": "957386", + "inodes_used": "1", + "inodes_available": "957385", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "relatime" + ] + }, + "proc,/proc": { + "device": "proc", + "mount": "/proc", + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "devpts,/dev/pts": { + "device": "devpts", + "mount": "/dev/pts", + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=666" + ] + }, + "sysfs,/sys": { + "device": "sysfs", + "mount": "/sys", + "fs_type": "sysfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "cgroup,/sys/fs/cgroup/systemd": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/systemd", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "xattr", + "release_agent=/lib/systemd/systemd-cgroups-agent", + "name=systemd" + ] + }, + "cgroup,/sys/fs/cgroup/freezer": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/freezer", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "freezer" + ] + }, + "cgroup,/sys/fs/cgroup/hugetlb": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/hugetlb", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ] + }, + "cgroup,/sys/fs/cgroup/cpu,cpuacct": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpu,cpuacct", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpu", + "cpuacct" + ] + }, + "cgroup,/sys/fs/cgroup/net_cls,net_prio": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/net_cls,net_prio", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "net_cls", + "net_prio" + ] + }, + "cgroup,/sys/fs/cgroup/pids": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/pids", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "pids" + ] + }, + "cgroup,/sys/fs/cgroup/blkio": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/blkio", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "blkio" + ] + }, + "cgroup,/sys/fs/cgroup/devices": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/devices", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "devices" + ] + }, + "cgroup,/sys/fs/cgroup/cpuset": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpuset", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpuset" + ] + }, + "cgroup,/sys/fs/cgroup/perf_event": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/perf_event", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "perf_event" + ] + }, + "cgroup,/sys/fs/cgroup/memory": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/memory", + "fs_type": "cgroup", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ] + }, + "mqueue,/dev/mqueue": { + "device": "mqueue", + "mount": "/dev/mqueue", + "fs_type": "mqueue", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "/dev/sda1,/etc/resolv.conf": { + "device": "/dev/sda1", + "mount": "/etc/resolv.conf", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ] + }, + "/dev/sda1,/etc/hostname": { + "device": "/dev/sda1", + "mount": "/etc/hostname", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ] + }, + "devpts,/dev/console": { + "device": "devpts", + "mount": "/dev/console", + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=666" + ] + }, + "proc,/proc/bus": { + "device": "proc", + "mount": "/proc/bus", + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ] + }, + "proc,/proc/fs": { + "device": "proc", + "mount": "/proc/fs", + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ] + }, + "proc,/proc/irq": { + "device": "proc", + "mount": "/proc/irq", + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ] + }, + "proc,/proc/sys": { + "device": "proc", + "mount": "/proc/sys", + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ] + }, + "proc,/proc/sysrq-trigger": { + "device": "proc", + "mount": "/proc/sysrq-trigger", + "fs_type": "proc", + "mount_options": [ + "ro", + "relatime" + ] + }, + "tmpfs,/proc/kcore": { + "device": "tmpfs", + "mount": "/proc/kcore", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ] + }, + "tmpfs,/proc/timer_list": { + "device": "tmpfs", + "mount": "/proc/timer_list", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ] + }, + "tmpfs,/proc/timer_stats": { + "device": "tmpfs", + "mount": "/proc/timer_stats", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ] + }, + "tmpfs,/proc/sched_debug": { + "device": "tmpfs", + "mount": "/proc/sched_debug", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "size=65536k", + "mode=755" + ] + }, + "sda,": { + "device": "sda" + }, + "sda1,": { + "device": "sda1" + }, + "loop0,": { + "device": "loop0" + }, + "loop1,": { + "device": "loop1" + }, + "loop2,": { + "device": "loop2" + } + } + }, + "fips": { + "kernel": { + "enabled": false + } + }, + "fqdn": "fauxhai.local", + "hostname": "Fauxhai", + "idle": "30 days 15 hours 07 minutes 30 seconds", + "idletime_seconds": 2646450, + "init_package": "bash", + "ipaddress": "10.0.0.2", + "kernel": { + "name": "Linux", + "release": "4.4.0-130-generic", + "version": "#156-Ubuntu SMP Thu Jun 14 08:53:28 UTC 2018", + "machine": "x86_64", + "processor": "x86_64", + "os": "GNU/Linux", + "modules": { + } + }, + "keys": { + "ssh": { + "host_dsa_public": "ssh-dss AAAAB3NzaC1kc3MAAACBAJFo9BLAw4WKEs5hgipk5m423FzBsDXCZSMcC9ca/om/1VYzMqImixGe3uICDzNFUWxFoLJTQAOccyzo6MXZiQqwWJDLFi5qOSr6w2XcMyE+zd4wOyMoDiVM5fizmG8K3FzrqvGjwBcHcBdOQnavSijoj38DN25J9zhrid5BY4WlAAAAFQDxXrCyG52XCzn3FV4ej38wJBkomQAAAIBovGPJ4mP2P6BK8lHl0PPbktwQbWlpJ13oz6REJFDVcUi7vV26bX/BjQX+ohzZQzljdz1SpUbPc/8nuA4darYkVh91eBi307EN8IdxRHj2eBgp/ZG4yshIebG3WHrwJD/xUjjZ1MRfyDT1ermVi4LvjjPgWDxLZnPpMaR6S1nzgQAAAIEAj0Vd6DCWslvlsZ8+N53HWsqPi3gnx35JoLPz9Z2epkKIKqmEHav+93G3hdfztVa4I4t3phoPniQchYryF5+RNg8hqxKzjNtrIqUYCeuf2NJrksNsH7OZygPHZpqt4kTuwAGZxjxEGfAI0y8DhkU2ntp2LnzRnWH106BQBCmcXwo= fauxhai.local", + "host_rsa_public": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtLCeqtqr/HbnORckw1ukdLhpfGoOPFi5/esKEokzTqq1gsgQ2V8emmyjfq1i6XXfRtSBxkdlHv/GWdP5wBTuE2G85MzBkVSQPvmwQN8lX/JMPEEtKXkeOo0o92/PiSmvY4eRsdF0mw40Uvg7jtE3f3fxj497kzh5fKtkrHnF4x9gXCbVdr3FqXJfggR5IJwAxToerbK7x/uRS+7YuZI9Pip3tt14nv9ezwXcuGb/tvjWOZINiFl8izVIFKi7sxfTX09p4NgamxRS7TD2Yd0jT8nEoF9UZTsgXcJ1kDSx7N7NxFfNfP6rCdOGRRz4gUhXtsUjG/XkxPeCwZ7A9VnOD fauxhai.local" + } + }, + "languages": { + "ruby": { + "platform": "x86_64-linux-gnu", + "version": "2.4.4", + "release_date": "2018-03-28", + "target": "x86_64-pc-linux-gnu", + "target_cpu": "x86_64", + "target_vendor": "pc", + "target_os": "linux-gnu", + "host": "x86_64-pc-linux-gnu", + "host_cpu": "x86_64", + "host_os": "linux-gnu", + "host_vendor": "pc", + "bin_dir": "/usr/local/bin", + "ruby_bin": "/usr/local/bin/ruby", + "gems_dir": "/usr/local/gems", + "gem_bin": "/usr/local/bin/gem" + }, + "powershell": null + }, + "lsb": { + "id": "Ubuntu", + "release": "16.04", + "codename": "xenial", + "description": "Ubuntu 16.04.3 LTS" + }, + "macaddress": "11:11:11:11:11:11", + "machinename": "Fauxhai", + "memory": { + "total": "1048576kB" + }, + "network": { + "interfaces": { + "lo": { + "mtu": "65536", + "flags": [ + "LOOPBACK", + "UP", + "LOWER_UP" + ], + "encapsulation": "Loopback", + "addresses": { + "127.0.0.1": { + "family": "inet", + "prefixlen": "8", + "netmask": "255.0.0.0", + "scope": "Node", + "ip_scope": "LOOPBACK" + }, + "::1": { + "family": "inet6", + "prefixlen": "128", + "scope": "Node", + "tags": [ + + ], + "ip_scope": "LINK LOCAL LOOPBACK" + } + }, + "state": "unknown" + }, + "eth0": { + "type": "eth", + "number": "0", + "mtu": "1500", + "flags": [ + "BROADCAST", + "MULTICAST", + "UP", + "LOWER_UP" + ], + "encapsulation": "Ethernet", + "addresses": { + "11:11:11:11:11:11": { + "family": "lladdr" + }, + "10.0.0.2": { + "family": "inet", + "prefixlen": "24", + "netmask": "255.255.255.0", + "broadcast": "10.0.0.255", + "scope": "Global", + "ip_scope": "RFC1918 PRIVATE" + }, + "fe80::11:1111:1111:1111": { + "family": "inet6", + "prefixlen": "64", + "scope": "Link", + "tags": [ + + ], + "ip_scope": "LINK LOCAL UNICAST" + } + }, + "state": "up", + "arp": { + "10.0.0.1": "fe:ff:ff:ff:ff:ff" + }, + "routes": [ + { + "destination": "default", + "family": "inet", + "via": "10.0.0.1" + }, + { + "destination": "10.0.0.0/24", + "family": "inet", + "scope": "link", + "proto": "kernel", + "src": "10.0.0.2" + }, + { + "destination": "fe80::/64", + "family": "inet6", + "metric": "256", + "proto": "kernel" + } + ], + "ring_params": { + } + } + }, + "default_interface": "eth0", + "default_gateway": "10.0.0.1" + }, + "ohai_time": 1532332846.1504228, + "os": "linux", + "os_version": "4.4.0-130-generic", + "packages": { + "adduser": { + "version": "3.113+nmu3ubuntu4", + "arch": "all" + }, + "apt": { + "version": "1.2.27", + "arch": "amd64" + }, + "apt-utils": { + "version": "1.2.27", + "arch": "amd64" + }, + "base-files": { + "version": "9.4ubuntu4.5", + "arch": "amd64" + }, + "base-passwd": { + "version": "3.5.39", + "arch": "amd64" + }, + "bash": { + "version": "4.3-14ubuntu1.2", + "arch": "amd64" + }, + "bsdutils": { + "version": "1:2.27.1-6ubuntu3.3", + "arch": "amd64" + }, + "ca-certificates": { + "version": "20170717~16.04.1", + "arch": "all" + }, + "coreutils": { + "version": "8.25-2ubuntu3~16.04", + "arch": "amd64" + }, + "cron": { + "version": "3.0pl1-128ubuntu2", + "arch": "amd64" + }, + "curl": { + "version": "7.47.0-1ubuntu2.8", + "arch": "amd64" + }, + "dash": { + "version": "0.5.8-2.1ubuntu2", + "arch": "amd64" + }, + "dbus": { + "version": "1.10.6-1ubuntu3.3", + "arch": "amd64" + }, + "debconf": { + "version": "1.5.58ubuntu1", + "arch": "all" + }, + "debianutils": { + "version": "4.7", + "arch": "amd64" + }, + "diffutils": { + "version": "1:3.3-3", + "arch": "amd64" + }, + "dpkg": { + "version": "1.18.4ubuntu1.3", + "arch": "amd64" + }, + "dpkg-dev": { + "version": "1.18.4ubuntu1.4", + "arch": "all" + }, + "e2fslibs": { + "version": "1.42.13-1ubuntu1", + "arch": "amd64" + }, + "e2fsprogs": { + "version": "1.42.13-1ubuntu1", + "arch": "amd64" + }, + "fakeroot": { + "version": "1.20.2-1ubuntu1", + "arch": "amd64" + }, + "findutils": { + "version": "4.6.0+git+20160126-2", + "arch": "amd64" + }, + "gcc-5-base": { + "version": "5.4.0-6ubuntu1~16.04.10", + "arch": "amd64" + }, + "gcc-6-base": { + "version": "6.0.1-0ubuntu1", + "arch": "amd64" + }, + "gnupg": { + "version": "1.4.20-1ubuntu3.1", + "arch": "amd64" + }, + "gpgv": { + "version": "1.4.20-1ubuntu3.1", + "arch": "amd64" + }, + "grep": { + "version": "2.25-1~16.04.1", + "arch": "amd64" + }, + "gzip": { + "version": "1.6-4ubuntu1", + "arch": "amd64" + }, + "hostname": { + "version": "3.16ubuntu2", + "arch": "amd64" + }, + "ifupdown": { + "version": "0.8.10ubuntu1.4", + "arch": "amd64" + }, + "init": { + "version": "1.29ubuntu4", + "arch": "amd64" + }, + "init-system-helpers": { + "version": "1.29ubuntu4", + "arch": "all" + }, + "initscripts": { + "version": "2.88dsf-59.3ubuntu2", + "arch": "amd64" + }, + "insserv": { + "version": "1.14.0-5ubuntu3", + "arch": "amd64" + }, + "iproute2": { + "version": "4.3.0-1ubuntu3.16.04.3", + "arch": "amd64" + }, + "isc-dhcp-client": { + "version": "4.3.3-5ubuntu12.10", + "arch": "amd64" + }, + "javascript-common": { + "version": "11", + "arch": "all" + }, + "krb5-locales": { + "version": "1.13.2+dfsg-5ubuntu2", + "arch": "all" + }, + "libacl1": { + "version": "2.2.52-3", + "arch": "amd64" + }, + "libapparmor1": { + "version": "2.10.95-0ubuntu2.7", + "arch": "amd64" + }, + "libapt-inst2.0": { + "version": "1.2.27", + "arch": "amd64" + }, + "libapt-pkg5.0": { + "version": "1.2.27", + "arch": "amd64" + }, + "libasn1-8-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "libatm1": { + "version": "1:2.5.1-1.5", + "arch": "amd64" + }, + "libattr1": { + "version": "1:2.4.47-2", + "arch": "amd64" + }, + "libaudit-common": { + "version": "1:2.4.5-1ubuntu2", + "arch": "all" + }, + "libaudit1": { + "version": "1:2.4.5-1ubuntu2", + "arch": "amd64" + }, + "libblkid1": { + "version": "2.27.1-6ubuntu3.3", + "arch": "amd64" + }, + "libbz2-1.0": { + "version": "1.0.6-8", + "arch": "amd64" + }, + "libc-bin": { + "version": "2.23-0ubuntu9", + "arch": "amd64" + }, + "libc6": { + "version": "2.23-0ubuntu10", + "arch": "amd64" + }, + "libcap-ng0": { + "version": "0.7.7-1", + "arch": "amd64" + }, + "libcap2": { + "version": "1:2.24-12", + "arch": "amd64" + }, + "libcap2-bin": { + "version": "1:2.24-12", + "arch": "amd64" + }, + "libcomerr2": { + "version": "1.42.13-1ubuntu1", + "arch": "amd64" + }, + "libcryptsetup4": { + "version": "2:1.6.6-5ubuntu2.1", + "arch": "amd64" + }, + "libcurl3-gnutls": { + "version": "7.47.0-1ubuntu2.8", + "arch": "amd64" + }, + "libdb5.3": { + "version": "5.3.28-11ubuntu0.1", + "arch": "amd64" + }, + "libdbus-1-3": { + "version": "1.10.6-1ubuntu3.3", + "arch": "amd64" + }, + "libdebconfclient0": { + "version": "0.198ubuntu1", + "arch": "amd64" + }, + "libdevmapper1.02.1": { + "version": "2:1.02.110-1ubuntu10", + "arch": "amd64" + }, + "libexpat1": { + "version": "2.1.0-7ubuntu0.16.04.3", + "arch": "amd64" + }, + "libfakeroot": { + "version": "1.20.2-1ubuntu1", + "arch": "amd64" + }, + "libfdisk1": { + "version": "2.27.1-6ubuntu3.3", + "arch": "amd64" + }, + "libffi6": { + "version": "3.2.1-4", + "arch": "amd64" + }, + "libgcc1": { + "version": "1:6.0.1-0ubuntu1", + "arch": "amd64" + }, + "libgcrypt20": { + "version": "1.6.5-2ubuntu0.3", + "arch": "amd64" + }, + "libgdbm3": { + "version": "1.8.3-13.1", + "arch": "amd64" + }, + "libglib2.0-0": { + "version": "2.48.2-0ubuntu1", + "arch": "amd64" + }, + "libgmp-dev": { + "version": "2:6.1.0+dfsg-2", + "arch": "amd64" + }, + "libgmp10": { + "version": "2:6.1.0+dfsg-2", + "arch": "amd64" + }, + "libgmpxx4ldbl": { + "version": "2:6.1.0+dfsg-2", + "arch": "amd64" + }, + "libgnutls30": { + "version": "3.4.10-4ubuntu1.4", + "arch": "amd64" + }, + "libgpg-error0": { + "version": "1.21-2ubuntu1", + "arch": "amd64" + }, + "libgssapi-krb5-2": { + "version": "1.13.2+dfsg-5ubuntu2", + "arch": "amd64" + }, + "libgssapi3-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "libhcrypto4-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "libheimbase1-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "libheimntlm0-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "libhogweed4": { + "version": "3.2-1ubuntu0.16.04.1", + "arch": "amd64" + }, + "libhx509-5-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "libidn11": { + "version": "1.32-3ubuntu1.2", + "arch": "amd64" + }, + "libjs-jquery": { + "version": "1.11.3+dfsg-4", + "arch": "all" + }, + "libk5crypto3": { + "version": "1.13.2+dfsg-5ubuntu2", + "arch": "amd64" + }, + "libkeyutils1": { + "version": "1.5.9-8ubuntu1", + "arch": "amd64" + }, + "libkmod2": { + "version": "22-1ubuntu5", + "arch": "amd64" + }, + "libkrb5-26-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "libkrb5-3": { + "version": "1.13.2+dfsg-5ubuntu2", + "arch": "amd64" + }, + "libkrb5support0": { + "version": "1.13.2+dfsg-5ubuntu2", + "arch": "amd64" + }, + "libldap-2.4-2": { + "version": "2.4.42+dfsg-2ubuntu3.3", + "arch": "amd64" + }, + "liblz4-1": { + "version": "0.0~r131-2ubuntu2", + "arch": "amd64" + }, + "liblzma5": { + "version": "5.1.1alpha+20120614-2ubuntu2", + "arch": "amd64" + }, + "libmagic1": { + "version": "1:5.25-2ubuntu1.1", + "arch": "amd64" + }, + "libmnl0": { + "version": "1.0.3-5", + "arch": "amd64" + }, + "libmount1": { + "version": "2.27.1-6ubuntu3.3", + "arch": "amd64" + }, + "libmpc3": { + "version": "1.0.3-1", + "arch": "amd64" + }, + "libncurses5": { + "version": "6.0+20160213-1ubuntu1", + "arch": "amd64" + }, + "libncursesw5": { + "version": "6.0+20160213-1ubuntu1", + "arch": "amd64" + }, + "libnettle6": { + "version": "3.2-1ubuntu0.16.04.1", + "arch": "amd64" + }, + "libp11-kit0": { + "version": "0.23.2-5~ubuntu16.04.1", + "arch": "amd64" + }, + "libpam-modules": { + "version": "1.1.8-3.2ubuntu2", + "arch": "amd64" + }, + "libpam-modules-bin": { + "version": "1.1.8-3.2ubuntu2", + "arch": "amd64" + }, + "libpam-runtime": { + "version": "1.1.8-3.2ubuntu2", + "arch": "all" + }, + "libpam0g": { + "version": "1.1.8-3.2ubuntu2", + "arch": "amd64" + }, + "libpcre3": { + "version": "2:8.38-3.1", + "arch": "amd64" + }, + "libperl5.22": { + "version": "5.22.1-9ubuntu0.5", + "arch": "amd64" + }, + "libprocps4": { + "version": "2:3.3.10-4ubuntu2.3", + "arch": "amd64" + }, + "libpython3.5-minimal": { + "version": "3.5.2-2ubuntu0~16.04.4", + "arch": "amd64" + }, + "libreadline6": { + "version": "6.3-8ubuntu2", + "arch": "amd64" + }, + "libroken18-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "librtmp1": { + "version": "2.4+20151223.gitfa8646d-1ubuntu0.1", + "arch": "amd64" + }, + "libruby2.4": { + "version": "2.4.4-1bbox1~xenial1", + "arch": "amd64" + }, + "libsasl2-2": { + "version": "2.1.26.dfsg1-14build1", + "arch": "amd64" + }, + "libsasl2-modules": { + "version": "2.1.26.dfsg1-14build1", + "arch": "amd64" + }, + "libsasl2-modules-db": { + "version": "2.1.26.dfsg1-14build1", + "arch": "amd64" + }, + "libseccomp2": { + "version": "2.2.3-3ubuntu3", + "arch": "amd64" + }, + "libselinux1": { + "version": "2.4-3build2", + "arch": "amd64" + }, + "libsemanage-common": { + "version": "2.3-1build3", + "arch": "all" + }, + "libsemanage1": { + "version": "2.3-1build3", + "arch": "amd64" + }, + "libsepol1": { + "version": "2.4-2", + "arch": "amd64" + }, + "libsmartcols1": { + "version": "2.27.1-6ubuntu3.3", + "arch": "amd64" + }, + "libsqlite3-0": { + "version": "3.11.0-1ubuntu1", + "arch": "amd64" + }, + "libss2": { + "version": "1.42.13-1ubuntu1", + "arch": "amd64" + }, + "libssl1.0.0": { + "version": "1.0.2g-1ubuntu4.13", + "arch": "amd64" + }, + "libstdc++6": { + "version": "5.4.0-6ubuntu1~16.04.10", + "arch": "amd64" + }, + "libsystemd0": { + "version": "229-4ubuntu21", + "arch": "amd64" + }, + "libtasn1-6": { + "version": "4.7-3ubuntu0.16.04.3", + "arch": "amd64" + }, + "libtinfo5": { + "version": "6.0+20160213-1ubuntu1", + "arch": "amd64" + }, + "libudev1": { + "version": "229-4ubuntu21", + "arch": "amd64" + }, + "libusb-0.1-4": { + "version": "2:0.1.12-28", + "arch": "amd64" + }, + "libustr-1.0-1": { + "version": "1.0.4-5", + "arch": "amd64" + }, + "libuuid1": { + "version": "2.27.1-6ubuntu3.3", + "arch": "amd64" + }, + "libwind0-heimdal": { + "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", + "arch": "amd64" + }, + "libyaml-0-2": { + "version": "0.1.6-3", + "arch": "amd64" + }, + "login": { + "version": "1:4.2-3.1ubuntu5.3", + "arch": "amd64" + }, + "lsb-base": { + "version": "9.20160110ubuntu0.2", + "arch": "all" + }, + "lsb-release": { + "version": "9.20160110ubuntu0.2", + "arch": "all" + }, + "makedev": { + "version": "2.3.1-93ubuntu2~ubuntu16.04.1", + "arch": "all" + }, + "mawk": { + "version": "1.3.3-17ubuntu2", + "arch": "amd64" + }, + "mime-support": { + "version": "3.59ubuntu1", + "arch": "all" + }, + "mount": { + "version": "2.27.1-6ubuntu3.3", + "arch": "amd64" + }, + "multiarch-support": { + "version": "2.23-0ubuntu9", + "arch": "amd64" + }, + "ncurses-base": { + "version": "6.0+20160213-1ubuntu1", + "arch": "all" + }, + "ncurses-bin": { + "version": "6.0+20160213-1ubuntu1", + "arch": "amd64" + }, + "netbase": { + "version": "5.3", + "arch": "all" + }, + "openssl": { + "version": "1.0.2g-1ubuntu4.13", + "arch": "amd64" + }, + "passwd": { + "version": "1:4.2-3.1ubuntu5.3", + "arch": "amd64" + }, + "perl": { + "version": "5.22.1-9ubuntu0.5", + "arch": "amd64" + }, + "perl-base": { + "version": "5.22.1-9ubuntu0.5", + "arch": "amd64" + }, + "procps": { + "version": "2:3.3.10-4ubuntu2.3", + "arch": "amd64" + }, + "python3": { + "version": "3.5.1-3", + "arch": "amd64" + }, + "python3.5-minimal": { + "version": "3.5.2-2ubuntu0~16.04.4", + "arch": "amd64" + }, + "readline-common": { + "version": "6.3-8ubuntu2", + "arch": "all" + }, + "ruby2.4": { + "version": "2.4.4-1bbox1~xenial1", + "arch": "amd64" + }, + "ruby2.4-dev": { + "version": "2.4.4-1bbox1~xenial1", + "arch": "amd64" + }, + "rubygems-integration": { + "version": "1.10", + "arch": "all" + }, + "sed": { + "version": "4.2.2-7", + "arch": "amd64" + }, + "sensible-utils": { + "version": "0.0.9", + "arch": "all" + }, + "sgml-base": { + "version": "1.26+nmu4ubuntu1", + "arch": "all" + }, + "shared-mime-info": { + "version": "1.5-2ubuntu0.1", + "arch": "amd64" + }, + "systemd": { + "version": "229-4ubuntu21", + "arch": "amd64" + }, + "systemd-sysv": { + "version": "229-4ubuntu21", + "arch": "amd64" + }, + "sysv-rc": { + "version": "2.88dsf-59.3ubuntu2", + "arch": "all" + }, + "sysvinit-utils": { + "version": "2.88dsf-59.3ubuntu2", + "arch": "amd64" + }, + "tar": { + "version": "1.28-2.1ubuntu0.1", + "arch": "amd64" + }, + "ubuntu-keyring": { + "version": "2012.05.19", + "arch": "all" + }, + "ucf": { + "version": "3.0036", + "arch": "all" + }, + "unattended-upgrades": { + "version": "0.90ubuntu0.9", + "arch": "all" + }, + "unzip": { + "version": "6.0-20ubuntu1", + "arch": "amd64" + }, + "util-linux": { + "version": "2.27.1-6ubuntu3.3", + "arch": "amd64" + }, + "xdg-user-dirs": { + "version": "0.15-2ubuntu6.16.04.1", + "arch": "amd64" + }, + "xml-core": { + "version": "0.13+nmu2", + "arch": "all" + }, + "zlib1g": { + "version": "1:1.2.8.dfsg-2ubuntu4.1", + "arch": "amd64" + } + }, + "platform": "ubuntu", + "platform_family": "debian", + "platform_version": "16.04", + "root_group": "root", + "shard_seed": 161653957, + "shells": [ + "/bin/sh", + "/bin/dash", + "/bin/bash", + "/bin/rbash" + ], + "time": { + "timezone": "GMT" + }, + "uptime": "30 days 15 hours 07 minutes 30 seconds", + "uptime_seconds": 2646450, + "virtualization": { + "systems": { + } + } +} diff --git a/spec/chef/fixtures/fauxhai/ubuntu/16.04-more-cpus.json b/spec/chef/fixtures/fauxhai/ubuntu/16.04-more-cpus.json new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2ZhdXhoYWkvdWJ1bnR1LzE2LjA0LW1vcmUtY3B1cy5qc29u --- /dev/null +++ b/spec/chef/fixtures/fauxhai/ubuntu/16.04-more-cpus.json @@ -0,0 +1,1519 @@ +{ + "kernel": { + "name": "Linux", + "release": "4.8.0-52-generic", + "version": "#55~16.04.1-Ubuntu SMP Fri Apr 28 14:36:29 UTC 2017", + "machine": "x86_64", + "processor": "x86_64", + "os": "GNU/Linux", + "modules": { + "ip6table_filter": { + "size": "16384", + "refcount": "1" + }, + "ip6_tables": { + "size": "28672", + "refcount": "1" + }, + "iptable_filter": { + "size": "16384", + "refcount": "1" + }, + "ip_tables": { + "size": "24576", + "refcount": "1" + }, + "x_tables": { + "size": "36864", + "refcount": "4" + }, + "ppdev": { + "size": "20480", + "refcount": "0" + }, + "input_leds": { + "size": "16384", + "refcount": "0" + }, + "serio_raw": { + "size": "16384", + "refcount": "0" + }, + "parport_pc": { + "size": "32768", + "refcount": "0" + }, + "pvpanic": { + "size": "16384", + "refcount": "0" + }, + "parport": { + "size": "49152", + "refcount": "2" + }, + "ib_iser": { + "size": "49152", + "refcount": "0", + "version": "1.6" + }, + "rdma_cm": { + "size": "57344", + "refcount": "1" + }, + "iw_cm": { + "size": "49152", + "refcount": "1" + }, + "ib_cm": { + "size": "45056", + "refcount": "1" + }, + "ib_core": { + "size": "212992", + "refcount": "4" + }, + "configfs": { + "size": "40960", + "refcount": "2", + "version": "0.0.2" + }, + "iscsi_tcp": { + "size": "20480", + "refcount": "0" + }, + "libiscsi_tcp": { + "size": "24576", + "refcount": "1" + }, + "libiscsi": { + "size": "53248", + "refcount": "3" + }, + "scsi_transport_iscsi": { + "size": "98304", + "refcount": "4", + "version": "2.0-870" + }, + "autofs4": { + "size": "40960", + "refcount": "2" + }, + "btrfs": { + "size": "1060864", + "refcount": "0" + }, + "raid10": { + "size": "49152", + "refcount": "0" + }, + "raid456": { + "size": "110592", + "refcount": "0" + }, + "async_raid6_recov": { + "size": "20480", + "refcount": "1" + }, + "async_memcpy": { + "size": "16384", + "refcount": "2" + }, + "async_pq": { + "size": "16384", + "refcount": "2" + }, + "async_xor": { + "size": "16384", + "refcount": "3" + }, + "async_tx": { + "size": "16384", + "refcount": "5" + }, + "xor": { + "size": "24576", + "refcount": "2" + }, + "raid6_pq": { + "size": "102400", + "refcount": "4" + }, + "libcrc32c": { + "size": "16384", + "refcount": "1" + }, + "raid1": { + "size": "40960", + "refcount": "0" + }, + "raid0": { + "size": "20480", + "refcount": "0" + }, + "multipath": { + "size": "16384", + "refcount": "0" + }, + "linear": { + "size": "16384", + "refcount": "0" + }, + "crct10dif_pclmul": { + "size": "16384", + "refcount": "0" + }, + "crc32_pclmul": { + "size": "16384", + "refcount": "0" + }, + "ghash_clmulni_intel": { + "size": "16384", + "refcount": "0" + }, + "aesni_intel": { + "size": "167936", + "refcount": "0" + }, + "aes_x86_64": { + "size": "20480", + "refcount": "1" + }, + "lrw": { + "size": "16384", + "refcount": "1" + }, + "glue_helper": { + "size": "16384", + "refcount": "1" + }, + "ablk_helper": { + "size": "16384", + "refcount": "1" + }, + "cryptd": { + "size": "24576", + "refcount": "3" + }, + "psmouse": { + "size": "139264", + "refcount": "0" + }, + "virtio_net": { + "size": "32768", + "refcount": "0" + }, + "virtio_scsi": { + "size": "20480", + "refcount": "1" + } + } + }, + "os": "linux", + "os_version": "4.8.0-52-generic", + "lsb": { + "id": "Ubuntu", + "description": "Ubuntu 16.04.2 LTS", + "release": "16.04", + "codename": "xenial" + }, + "platform": "ubuntu", + "platform_version": "16.04", + "platform_family": "debian", + "filesystem": { + "by_device": { + "udev": { + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ], + "mounts": [ + "/dev" + ] + }, + "tmpfs": { + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ], + "mounts": [ + "/run", + "/dev/shm", + "/run/lock", + "/sys/fs/cgroup", + "/run/user/1001" + ] + }, + "/dev/sda1": { + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs", + "mounts": [ + "/" + ] + }, + "sysfs": { + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys" + ] + }, + "proc": { + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/proc" + ] + }, + "devpts": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ], + "mounts": [ + "/dev/pts" + ] + }, + "securityfs": { + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys/kernel/security" + ] + }, + "cgroup": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ], + "mounts": [ + "/sys/fs/cgroup/systemd", + "/sys/fs/cgroup/perf_event", + "/sys/fs/cgroup/blkio", + "/sys/fs/cgroup/devices", + "/sys/fs/cgroup/net_cls,net_prio", + "/sys/fs/cgroup/cpuset", + "/sys/fs/cgroup/cpu,cpuacct", + "/sys/fs/cgroup/memory", + "/sys/fs/cgroup/freezer", + "/sys/fs/cgroup/pids", + "/sys/fs/cgroup/hugetlb" + ] + }, + "pstore": { + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys/fs/pstore" + ] + }, + "hugetlbfs": { + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/dev/hugepages" + ] + }, + "mqueue": { + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/dev/mqueue" + ] + }, + "systemd-1": { + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ], + "mounts": [ + "/proc/sys/fs/binfmt_misc" + ] + }, + "debugfs": { + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/kernel/debug" + ] + }, + "fusectl": { + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/fs/fuse/connections" + ] + }, + "configfs": { + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/kernel/config" + ] + }, + "lxcfs": { + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ], + "mounts": [ + "/var/lib/lxcfs" + ] + }, + "/dev/sda": { + } + }, + "by_mountpoint": { + "/dev": { + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ], + "devices": [ + "udev" + ] + }, + "/run": { + "kb_size": "378828", + "kb_used": "5292", + "kb_available": "373536", + "percent_used": "2%", + "total_inodes": "473531", + "inodes_used": "460", + "inodes_available": "473071", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "size=378828k", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/": { + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs", + "devices": [ + "/dev/sda1" + ] + }, + "/dev/shm": { + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "1", + "inodes_available": "473530", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev" + ], + "devices": [ + "tmpfs" + ] + }, + "/run/lock": { + "kb_size": "5120", + "kb_used": "0", + "kb_available": "5120", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "8", + "inodes_available": "473523", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=5120k" + ], + "devices": [ + "tmpfs" + ] + }, + "/sys/fs/cgroup": { + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "16", + "inodes_available": "473515", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/run/user/1001": { + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ], + "devices": [ + "tmpfs" + ] + }, + "/sys": { + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "sysfs" + ] + }, + "/proc": { + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/dev/pts": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ], + "devices": [ + "devpts" + ] + }, + "/sys/kernel/security": { + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "securityfs" + ] + }, + "/sys/fs/cgroup/systemd": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "xattr", + "release_agent=/lib/systemd/systemd-cgroups-agent", + "name=systemd" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/pstore": { + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "pstore" + ] + }, + "/sys/fs/cgroup/perf_event": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "perf_event" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/blkio": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "blkio" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/devices": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "devices" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/net_cls,net_prio": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "net_cls", + "net_prio" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/cpuset": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpuset" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/cpu,cpuacct": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpu", + "cpuacct" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/memory": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/freezer": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "freezer" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/pids": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "pids" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/hugetlb": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ], + "devices": [ + "cgroup" + ] + }, + "/dev/hugepages": { + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "hugetlbfs" + ] + }, + "/dev/mqueue": { + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "mqueue" + ] + }, + "/proc/sys/fs/binfmt_misc": { + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ], + "devices": [ + "systemd-1" + ] + }, + "/sys/kernel/debug": { + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "debugfs" + ] + }, + "/sys/fs/fuse/connections": { + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "fusectl" + ] + }, + "/sys/kernel/config": { + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "configfs" + ] + }, + "/var/lib/lxcfs": { + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ], + "devices": [ + "lxcfs" + ] + } + }, + "by_pair": { + "udev,/dev": { + "device": "udev", + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "mount": "/dev", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ] + }, + "tmpfs,/run": { + "device": "tmpfs", + "kb_size": "378828", + "kb_used": "5292", + "kb_available": "373536", + "percent_used": "2%", + "mount": "/run", + "total_inodes": "473531", + "inodes_used": "460", + "inodes_available": "473071", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "size=378828k", + "mode=755" + ] + }, + "/dev/sda1,/": { + "device": "/dev/sda1", + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "mount": "/", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs" + }, + "tmpfs,/dev/shm": { + "device": "tmpfs", + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "mount": "/dev/shm", + "total_inodes": "473531", + "inodes_used": "1", + "inodes_available": "473530", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev" + ] + }, + "tmpfs,/run/lock": { + "device": "tmpfs", + "kb_size": "5120", + "kb_used": "0", + "kb_available": "5120", + "percent_used": "0%", + "mount": "/run/lock", + "total_inodes": "473531", + "inodes_used": "8", + "inodes_available": "473523", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=5120k" + ] + }, + "tmpfs,/sys/fs/cgroup": { + "device": "tmpfs", + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "mount": "/sys/fs/cgroup", + "total_inodes": "473531", + "inodes_used": "16", + "inodes_available": "473515", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "mode=755" + ] + }, + "tmpfs,/run/user/1001": { + "device": "tmpfs", + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "mount": "/run/user/1001", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ] + }, + "sysfs,/sys": { + "device": "sysfs", + "mount": "/sys", + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "proc,/proc": { + "device": "proc", + "mount": "/proc", + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "devpts,/dev/pts": { + "device": "devpts", + "mount": "/dev/pts", + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ] + }, + "securityfs,/sys/kernel/security": { + "device": "securityfs", + "mount": "/sys/kernel/security", + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "cgroup,/sys/fs/cgroup/systemd": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/systemd", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "xattr", + "release_agent=/lib/systemd/systemd-cgroups-agent", + "name=systemd" + ] + }, + "pstore,/sys/fs/pstore": { + "device": "pstore", + "mount": "/sys/fs/pstore", + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "cgroup,/sys/fs/cgroup/perf_event": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/perf_event", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "perf_event" + ] + }, + "cgroup,/sys/fs/cgroup/blkio": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/blkio", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "blkio" + ] + }, + "cgroup,/sys/fs/cgroup/devices": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/devices", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "devices" + ] + }, + "cgroup,/sys/fs/cgroup/net_cls,net_prio": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/net_cls,net_prio", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "net_cls", + "net_prio" + ] + }, + "cgroup,/sys/fs/cgroup/cpuset": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpuset", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpuset" + ] + }, + "cgroup,/sys/fs/cgroup/cpu,cpuacct": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpu,cpuacct", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpu", + "cpuacct" + ] + }, + "cgroup,/sys/fs/cgroup/memory": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/memory", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ] + }, + "cgroup,/sys/fs/cgroup/freezer": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/freezer", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "freezer" + ] + }, + "cgroup,/sys/fs/cgroup/pids": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/pids", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "pids" + ] + }, + "cgroup,/sys/fs/cgroup/hugetlb": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/hugetlb", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ] + }, + "hugetlbfs,/dev/hugepages": { + "device": "hugetlbfs", + "mount": "/dev/hugepages", + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "mqueue,/dev/mqueue": { + "device": "mqueue", + "mount": "/dev/mqueue", + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ] + }, + "systemd-1,/proc/sys/fs/binfmt_misc": { + "device": "systemd-1", + "mount": "/proc/sys/fs/binfmt_misc", + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ] + }, + "debugfs,/sys/kernel/debug": { + "device": "debugfs", + "mount": "/sys/kernel/debug", + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "fusectl,/sys/fs/fuse/connections": { + "device": "fusectl", + "mount": "/sys/fs/fuse/connections", + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ] + }, + "configfs,/sys/kernel/config": { + "device": "configfs", + "mount": "/sys/kernel/config", + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "lxcfs,/var/lib/lxcfs": { + "device": "lxcfs", + "mount": "/var/lib/lxcfs", + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ] + }, + "/dev/sda,": { + "device": "/dev/sda" + } + } + }, + "dmi": { + "dmidecode_version": "3.0" + }, + "init_package": "systemd", + "ohai_time": 1496219099.5081818, + "command": { + "ps": "ps -ef" + }, + "root_group": "root", + "languages": { + "ruby": { + "platform": "x86_64-linux-gnu", + "version": "2.3.1", + "release_date": "2016-04-26", + "target": "x86_64-pc-linux-gnu", + "target_cpu": "x86_64", + "target_vendor": "pc", + "target_os": "linux-gnu", + "host": "x86_64-pc-linux-gnu", + "host_cpu": "x86_64", + "host_os": "linux-gnu", + "host_vendor": "pc", + "bin_dir": "/usr/local/bin", + "ruby_bin": "/usr/local/bin/ruby", + "gems_dir": "/usr/local/gems", + "gem_bin": "/usr/local/bin/gem" + }, + "powershell": null + }, + "chef_packages": { + "chef": { + "version": "13.1.31", + "chef_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-13.1.31/lib" + }, + "ohai": { + "version": "13.1.0", + "ohai_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/ohai-13.1.0/lib/ohai" + } + }, + "counters": { + "network": { + "interfaces": { + "eth0": { + "rx": { + "bytes": "0", + "packets": "0", + "errors": "0", + "drop": 0, + "overrun": 0, + "frame": 0, + "compressed": 0, + "multicast": 0 + }, + "tx": { + "bytes": "342", + "packets": "0", + "errors": "0", + "drop": 0, + "overrun": 0, + "collisions": "0", + "carrier": 0, + "compressed": 0 + } + } + } + } + }, + "current_user": "fauxhai", + "domain": "local", + "etc": { + "passwd": { + "fauxhai": { + "dir": "/home/fauxhai", + "gid": 0, + "uid": 0, + "shell": "/bin/bash", + "gecos": "Fauxhai" + } + }, + "group": { + "fauxhai": { + "gid": 0, + "members": [ + "fauxhai" + ] + } + } + }, + "hostname": "Fauxhai", + "fqdn": "fauxhai.local", + "ipaddress": "10.0.0.2", + "keys": { + "ssh": { + "host_dsa_public": "ssh-dss AAAAB3NzaC1kc3MAAACBAJFo9BLAw4WKEs5hgipk5m423FzBsDXCZSMcC9ca/om/1VYzMqImixGe3uICDzNFUWxFoLJTQAOccyzo6MXZiQqwWJDLFi5qOSr6w2XcMyE+zd4wOyMoDiVM5fizmG8K3FzrqvGjwBcHcBdOQnavSijoj38DN25J9zhrid5BY4WlAAAAFQDxXrCyG52XCzn3FV4ej38wJBkomQAAAIBovGPJ4mP2P6BK8lHl0PPbktwQbWlpJ13oz6REJFDVcUi7vV26bX/BjQX+ohzZQzljdz1SpUbPc/8nuA4darYkVh91eBi307EN8IdxRHj2eBgp/ZG4yshIebG3WHrwJD/xUjjZ1MRfyDT1ermVi4LvjjPgWDxLZnPpMaR6S1nzgQAAAIEAj0Vd6DCWslvlsZ8+N53HWsqPi3gnx35JoLPz9Z2epkKIKqmEHav+93G3hdfztVa4I4t3phoPniQchYryF5+RNg8hqxKzjNtrIqUYCeuf2NJrksNsH7OZygPHZpqt4kTuwAGZxjxEGfAI0y8DhkU2ntp2LnzRnWH106BQBCmcXwo= fauxhai.local", + "host_rsa_public": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtLCeqtqr/HbnORckw1ukdLhpfGoOPFi5/esKEokzTqq1gsgQ2V8emmyjfq1i6XXfRtSBxkdlHv/GWdP5wBTuE2G85MzBkVSQPvmwQN8lX/JMPEEtKXkeOo0o92/PiSmvY4eRsdF0mw40Uvg7jtE3f3fxj497kzh5fKtkrHnF4x9gXCbVdr3FqXJfggR5IJwAxToerbK7x/uRS+7YuZI9Pip3tt14nv9ezwXcuGb/tvjWOZINiFl8izVIFKi7sxfTX09p4NgamxRS7TD2Yd0jT8nEoF9UZTsgXcJ1kDSx7N7NxFfNfP6rCdOGRRz4gUhXtsUjG/XkxPeCwZ7A9VnOD fauxhai.local" + } + }, + "macaddress": "11:11:11:11:11:11", + "network": { + "default_gateway": "10.0.0.1", + "default_interface": "eth0", + "settings": { + }, + "interfaces": { + "eth0": { + "addresses": { + "10.0.0.2": { + "broadcast": "10.0.0.255", + "family": "inet", + "netmask": "255.255.255.0", + "prefixlen": "24", + "scope": "Global" + } + }, + "arp": { + "10.0.0.1": "fe:ff:ff:ff:ff:ff" + }, + "encapsulation": "Ethernet", + "flags": [ + "BROADCAST", + "MULTICAST", + "UP", + "LOWER_UP" + ], + "mtu": "1500", + "number": "0", + "routes": [ + { + "destination": "10.0.0.0/24", + "family": "inet", + "scope": "link", + "proto": "kernel", + "src": "10.0.0.2" + } + ], + "state": "up", + "type": "eth" + } + } + }, + "uptime": "30 days 15 hours 07 minutes 30 seconds", + "uptime_seconds": 2646450, + "cpu": { + "real": 16, + "total": 16, + "cores": 16 + }, + "memory": { + "total": "33554432kB" + }, + "virtualization": { + "systems": { + } + } +} diff --git a/spec/chef/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json b/spec/chef/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2ZhdXhoYWkvdWJ1bnR1LzE2LjA0LW5vLXJ1bi10bXBmcy5qc29u --- /dev/null +++ b/spec/chef/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json @@ -0,0 +1,1438 @@ +{ + "kernel": { + "name": "Linux", + "release": "4.8.0-52-generic", + "version": "#55~16.04.1-Ubuntu SMP Fri Apr 28 14:36:29 UTC 2017", + "machine": "x86_64", + "processor": "x86_64", + "os": "GNU/Linux", + "modules": { + "ip6table_filter": { + "size": "16384", + "refcount": "1" + }, + "ip6_tables": { + "size": "28672", + "refcount": "1" + }, + "iptable_filter": { + "size": "16384", + "refcount": "1" + }, + "ip_tables": { + "size": "24576", + "refcount": "1" + }, + "x_tables": { + "size": "36864", + "refcount": "4" + }, + "ppdev": { + "size": "20480", + "refcount": "0" + }, + "input_leds": { + "size": "16384", + "refcount": "0" + }, + "serio_raw": { + "size": "16384", + "refcount": "0" + }, + "parport_pc": { + "size": "32768", + "refcount": "0" + }, + "pvpanic": { + "size": "16384", + "refcount": "0" + }, + "parport": { + "size": "49152", + "refcount": "2" + }, + "ib_iser": { + "size": "49152", + "refcount": "0", + "version": "1.6" + }, + "rdma_cm": { + "size": "57344", + "refcount": "1" + }, + "iw_cm": { + "size": "49152", + "refcount": "1" + }, + "ib_cm": { + "size": "45056", + "refcount": "1" + }, + "ib_core": { + "size": "212992", + "refcount": "4" + }, + "configfs": { + "size": "40960", + "refcount": "2", + "version": "0.0.2" + }, + "iscsi_tcp": { + "size": "20480", + "refcount": "0" + }, + "libiscsi_tcp": { + "size": "24576", + "refcount": "1" + }, + "libiscsi": { + "size": "53248", + "refcount": "3" + }, + "scsi_transport_iscsi": { + "size": "98304", + "refcount": "4", + "version": "2.0-870" + }, + "autofs4": { + "size": "40960", + "refcount": "2" + }, + "btrfs": { + "size": "1060864", + "refcount": "0" + }, + "raid10": { + "size": "49152", + "refcount": "0" + }, + "raid456": { + "size": "110592", + "refcount": "0" + }, + "async_raid6_recov": { + "size": "20480", + "refcount": "1" + }, + "async_memcpy": { + "size": "16384", + "refcount": "2" + }, + "async_pq": { + "size": "16384", + "refcount": "2" + }, + "async_xor": { + "size": "16384", + "refcount": "3" + }, + "async_tx": { + "size": "16384", + "refcount": "5" + }, + "xor": { + "size": "24576", + "refcount": "2" + }, + "raid6_pq": { + "size": "102400", + "refcount": "4" + }, + "libcrc32c": { + "size": "16384", + "refcount": "1" + }, + "raid1": { + "size": "40960", + "refcount": "0" + }, + "raid0": { + "size": "20480", + "refcount": "0" + }, + "multipath": { + "size": "16384", + "refcount": "0" + }, + "linear": { + "size": "16384", + "refcount": "0" + }, + "crct10dif_pclmul": { + "size": "16384", + "refcount": "0" + }, + "crc32_pclmul": { + "size": "16384", + "refcount": "0" + }, + "ghash_clmulni_intel": { + "size": "16384", + "refcount": "0" + }, + "aesni_intel": { + "size": "167936", + "refcount": "0" + }, + "aes_x86_64": { + "size": "20480", + "refcount": "1" + }, + "lrw": { + "size": "16384", + "refcount": "1" + }, + "glue_helper": { + "size": "16384", + "refcount": "1" + }, + "ablk_helper": { + "size": "16384", + "refcount": "1" + }, + "cryptd": { + "size": "24576", + "refcount": "3" + }, + "psmouse": { + "size": "139264", + "refcount": "0" + }, + "virtio_net": { + "size": "32768", + "refcount": "0" + }, + "virtio_scsi": { + "size": "20480", + "refcount": "1" + } + } + }, + "os": "linux", + "os_version": "4.8.0-52-generic", + "lsb": { + "id": "Ubuntu", + "description": "Ubuntu 16.04.2 LTS", + "release": "16.04", + "codename": "xenial" + }, + "platform": "ubuntu", + "platform_version": "16.04", + "platform_family": "debian", + "filesystem": { + "by_device": { + "udev": { + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ], + "mounts": [ + "/dev" + ] + }, + "tmpfs": { + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ], + "mounts": [ + "/dev/shm", + "/run/lock", + "/sys/fs/cgroup", + "/run/user/1001" + ] + }, + "/dev/sda1": { + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs", + "mounts": [ + "/" + ] + }, + "sysfs": { + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys" + ] + }, + "proc": { + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/proc" + ] + }, + "devpts": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ], + "mounts": [ + "/dev/pts" + ] + }, + "securityfs": { + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys/kernel/security" + ] + }, + "cgroup": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ], + "mounts": [ + "/sys/fs/cgroup/systemd", + "/sys/fs/cgroup/perf_event", + "/sys/fs/cgroup/blkio", + "/sys/fs/cgroup/devices", + "/sys/fs/cgroup/net_cls,net_prio", + "/sys/fs/cgroup/cpuset", + "/sys/fs/cgroup/cpu,cpuacct", + "/sys/fs/cgroup/memory", + "/sys/fs/cgroup/freezer", + "/sys/fs/cgroup/pids", + "/sys/fs/cgroup/hugetlb" + ] + }, + "pstore": { + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys/fs/pstore" + ] + }, + "hugetlbfs": { + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/dev/hugepages" + ] + }, + "mqueue": { + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/dev/mqueue" + ] + }, + "systemd-1": { + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ], + "mounts": [ + "/proc/sys/fs/binfmt_misc" + ] + }, + "debugfs": { + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/kernel/debug" + ] + }, + "fusectl": { + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/fs/fuse/connections" + ] + }, + "configfs": { + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/kernel/config" + ] + }, + "lxcfs": { + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ], + "mounts": [ + "/var/lib/lxcfs" + ] + }, + "/dev/sda": { + } + }, + "by_mountpoint": { + "/dev": { + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ], + "devices": [ + "udev" + ] + }, + "/": { + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs", + "devices": [ + "/dev/sda1" + ] + }, + "/run/lock": { + "kb_size": "5120", + "kb_used": "0", + "kb_available": "5120", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "8", + "inodes_available": "473523", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=5120k" + ], + "devices": [ + "tmpfs" + ] + }, + "/sys/fs/cgroup": { + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "16", + "inodes_available": "473515", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/run/user/1001": { + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ], + "devices": [ + "tmpfs" + ] + }, + "/sys": { + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "sysfs" + ] + }, + "/proc": { + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/dev/pts": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ], + "devices": [ + "devpts" + ] + }, + "/sys/kernel/security": { + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "securityfs" + ] + }, + "/sys/fs/cgroup/systemd": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "xattr", + "release_agent=/lib/systemd/systemd-cgroups-agent", + "name=systemd" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/pstore": { + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "pstore" + ] + }, + "/sys/fs/cgroup/perf_event": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "perf_event" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/blkio": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "blkio" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/devices": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "devices" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/net_cls,net_prio": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "net_cls", + "net_prio" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/cpuset": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpuset" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/cpu,cpuacct": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpu", + "cpuacct" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/memory": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/freezer": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "freezer" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/pids": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "pids" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/hugetlb": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ], + "devices": [ + "cgroup" + ] + }, + "/dev/hugepages": { + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "hugetlbfs" + ] + }, + "/dev/mqueue": { + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "mqueue" + ] + }, + "/proc/sys/fs/binfmt_misc": { + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ], + "devices": [ + "systemd-1" + ] + }, + "/sys/kernel/debug": { + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "debugfs" + ] + }, + "/sys/fs/fuse/connections": { + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "fusectl" + ] + }, + "/sys/kernel/config": { + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "configfs" + ] + }, + "/var/lib/lxcfs": { + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ], + "devices": [ + "lxcfs" + ] + } + }, + "by_pair": { + "udev,/dev": { + "device": "udev", + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "mount": "/dev", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ] + }, + "/dev/sda1,/": { + "device": "/dev/sda1", + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "mount": "/", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs" + }, + "tmpfs,/run/lock": { + "device": "tmpfs", + "kb_size": "5120", + "kb_used": "0", + "kb_available": "5120", + "percent_used": "0%", + "mount": "/run/lock", + "total_inodes": "473531", + "inodes_used": "8", + "inodes_available": "473523", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=5120k" + ] + }, + "tmpfs,/sys/fs/cgroup": { + "device": "tmpfs", + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "mount": "/sys/fs/cgroup", + "total_inodes": "473531", + "inodes_used": "16", + "inodes_available": "473515", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "mode=755" + ] + }, + "tmpfs,/run/user/1001": { + "device": "tmpfs", + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "mount": "/run/user/1001", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ] + }, + "sysfs,/sys": { + "device": "sysfs", + "mount": "/sys", + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "proc,/proc": { + "device": "proc", + "mount": "/proc", + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "devpts,/dev/pts": { + "device": "devpts", + "mount": "/dev/pts", + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ] + }, + "securityfs,/sys/kernel/security": { + "device": "securityfs", + "mount": "/sys/kernel/security", + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "cgroup,/sys/fs/cgroup/systemd": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/systemd", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "xattr", + "release_agent=/lib/systemd/systemd-cgroups-agent", + "name=systemd" + ] + }, + "pstore,/sys/fs/pstore": { + "device": "pstore", + "mount": "/sys/fs/pstore", + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "cgroup,/sys/fs/cgroup/perf_event": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/perf_event", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "perf_event" + ] + }, + "cgroup,/sys/fs/cgroup/blkio": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/blkio", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "blkio" + ] + }, + "cgroup,/sys/fs/cgroup/devices": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/devices", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "devices" + ] + }, + "cgroup,/sys/fs/cgroup/net_cls,net_prio": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/net_cls,net_prio", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "net_cls", + "net_prio" + ] + }, + "cgroup,/sys/fs/cgroup/cpuset": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpuset", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpuset" + ] + }, + "cgroup,/sys/fs/cgroup/cpu,cpuacct": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpu,cpuacct", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpu", + "cpuacct" + ] + }, + "cgroup,/sys/fs/cgroup/memory": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/memory", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ] + }, + "cgroup,/sys/fs/cgroup/freezer": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/freezer", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "freezer" + ] + }, + "cgroup,/sys/fs/cgroup/pids": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/pids", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "pids" + ] + }, + "cgroup,/sys/fs/cgroup/hugetlb": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/hugetlb", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ] + }, + "hugetlbfs,/dev/hugepages": { + "device": "hugetlbfs", + "mount": "/dev/hugepages", + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "mqueue,/dev/mqueue": { + "device": "mqueue", + "mount": "/dev/mqueue", + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ] + }, + "systemd-1,/proc/sys/fs/binfmt_misc": { + "device": "systemd-1", + "mount": "/proc/sys/fs/binfmt_misc", + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ] + }, + "debugfs,/sys/kernel/debug": { + "device": "debugfs", + "mount": "/sys/kernel/debug", + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "fusectl,/sys/fs/fuse/connections": { + "device": "fusectl", + "mount": "/sys/fs/fuse/connections", + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ] + }, + "configfs,/sys/kernel/config": { + "device": "configfs", + "mount": "/sys/kernel/config", + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "lxcfs,/var/lib/lxcfs": { + "device": "lxcfs", + "mount": "/var/lib/lxcfs", + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ] + }, + "/dev/sda,": { + "device": "/dev/sda" + } + } + }, + "dmi": { + "dmidecode_version": "3.0" + }, + "init_package": "systemd", + "ohai_time": 1496219099.5081818, + "command": { + "ps": "ps -ef" + }, + "root_group": "root", + "languages": { + "ruby": { + "platform": "x86_64-linux-gnu", + "version": "2.3.1", + "release_date": "2016-04-26", + "target": "x86_64-pc-linux-gnu", + "target_cpu": "x86_64", + "target_vendor": "pc", + "target_os": "linux-gnu", + "host": "x86_64-pc-linux-gnu", + "host_cpu": "x86_64", + "host_os": "linux-gnu", + "host_vendor": "pc", + "bin_dir": "/usr/local/bin", + "ruby_bin": "/usr/local/bin/ruby", + "gems_dir": "/usr/local/gems", + "gem_bin": "/usr/local/bin/gem" + }, + "powershell": null + }, + "chef_packages": { + "chef": { + "version": "13.1.31", + "chef_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-13.1.31/lib" + }, + "ohai": { + "version": "13.1.0", + "ohai_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/ohai-13.1.0/lib/ohai" + } + }, + "counters": { + "network": { + "interfaces": { + "eth0": { + "rx": { + "bytes": "0", + "packets": "0", + "errors": "0", + "drop": 0, + "overrun": 0, + "frame": 0, + "compressed": 0, + "multicast": 0 + }, + "tx": { + "bytes": "342", + "packets": "0", + "errors": "0", + "drop": 0, + "overrun": 0, + "collisions": "0", + "carrier": 0, + "compressed": 0 + } + } + } + } + }, + "current_user": "fauxhai", + "domain": "local", + "etc": { + "passwd": { + "fauxhai": { + "dir": "/home/fauxhai", + "gid": 0, + "uid": 0, + "shell": "/bin/bash", + "gecos": "Fauxhai" + } + }, + "group": { + "fauxhai": { + "gid": 0, + "members": [ + "fauxhai" + ] + } + } + }, + "hostname": "Fauxhai", + "fqdn": "fauxhai.local", + "ipaddress": "10.0.0.2", + "keys": { + "ssh": { + "host_dsa_public": "ssh-dss AAAAB3NzaC1kc3MAAACBAJFo9BLAw4WKEs5hgipk5m423FzBsDXCZSMcC9ca/om/1VYzMqImixGe3uICDzNFUWxFoLJTQAOccyzo6MXZiQqwWJDLFi5qOSr6w2XcMyE+zd4wOyMoDiVM5fizmG8K3FzrqvGjwBcHcBdOQnavSijoj38DN25J9zhrid5BY4WlAAAAFQDxXrCyG52XCzn3FV4ej38wJBkomQAAAIBovGPJ4mP2P6BK8lHl0PPbktwQbWlpJ13oz6REJFDVcUi7vV26bX/BjQX+ohzZQzljdz1SpUbPc/8nuA4darYkVh91eBi307EN8IdxRHj2eBgp/ZG4yshIebG3WHrwJD/xUjjZ1MRfyDT1ermVi4LvjjPgWDxLZnPpMaR6S1nzgQAAAIEAj0Vd6DCWslvlsZ8+N53HWsqPi3gnx35JoLPz9Z2epkKIKqmEHav+93G3hdfztVa4I4t3phoPniQchYryF5+RNg8hqxKzjNtrIqUYCeuf2NJrksNsH7OZygPHZpqt4kTuwAGZxjxEGfAI0y8DhkU2ntp2LnzRnWH106BQBCmcXwo= fauxhai.local", + "host_rsa_public": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtLCeqtqr/HbnORckw1ukdLhpfGoOPFi5/esKEokzTqq1gsgQ2V8emmyjfq1i6XXfRtSBxkdlHv/GWdP5wBTuE2G85MzBkVSQPvmwQN8lX/JMPEEtKXkeOo0o92/PiSmvY4eRsdF0mw40Uvg7jtE3f3fxj497kzh5fKtkrHnF4x9gXCbVdr3FqXJfggR5IJwAxToerbK7x/uRS+7YuZI9Pip3tt14nv9ezwXcuGb/tvjWOZINiFl8izVIFKi7sxfTX09p4NgamxRS7TD2Yd0jT8nEoF9UZTsgXcJ1kDSx7N7NxFfNfP6rCdOGRRz4gUhXtsUjG/XkxPeCwZ7A9VnOD fauxhai.local" + } + }, + "macaddress": "11:11:11:11:11:11", + "network": { + "default_gateway": "10.0.0.1", + "default_interface": "eth0", + "settings": { + }, + "interfaces": { + "eth0": { + "addresses": { + "10.0.0.2": { + "broadcast": "10.0.0.255", + "family": "inet", + "netmask": "255.255.255.0", + "prefixlen": "24", + "scope": "Global" + } + }, + "arp": { + "10.0.0.1": "fe:ff:ff:ff:ff:ff" + }, + "encapsulation": "Ethernet", + "flags": [ + "BROADCAST", + "MULTICAST", + "UP", + "LOWER_UP" + ], + "mtu": "1500", + "number": "0", + "routes": [ + { + "destination": "10.0.0.0/24", + "family": "inet", + "scope": "link", + "proto": "kernel", + "src": "10.0.0.2" + } + ], + "state": "up", + "type": "eth" + } + } + }, + "uptime": "30 days 15 hours 07 minutes 30 seconds", + "uptime_seconds": 2646450, + "cpu": { + "real": 1, + "total": 1, + "cores": 1 + }, + "memory": { + "total": "1048576kB" + }, + "virtualization": { + "systems": { + } + } +} diff --git a/spec/chef/fixtures/fauxhai/ubuntu/16.04.json b/spec/chef/fixtures/fauxhai/ubuntu/16.04.json new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL2ZhdXhoYWkvdWJ1bnR1LzE2LjA0Lmpzb24= --- /dev/null +++ b/spec/chef/fixtures/fauxhai/ubuntu/16.04.json @@ -0,0 +1,1519 @@ +{ + "kernel": { + "name": "Linux", + "release": "4.8.0-52-generic", + "version": "#55~16.04.1-Ubuntu SMP Fri Apr 28 14:36:29 UTC 2017", + "machine": "x86_64", + "processor": "x86_64", + "os": "GNU/Linux", + "modules": { + "ip6table_filter": { + "size": "16384", + "refcount": "1" + }, + "ip6_tables": { + "size": "28672", + "refcount": "1" + }, + "iptable_filter": { + "size": "16384", + "refcount": "1" + }, + "ip_tables": { + "size": "24576", + "refcount": "1" + }, + "x_tables": { + "size": "36864", + "refcount": "4" + }, + "ppdev": { + "size": "20480", + "refcount": "0" + }, + "input_leds": { + "size": "16384", + "refcount": "0" + }, + "serio_raw": { + "size": "16384", + "refcount": "0" + }, + "parport_pc": { + "size": "32768", + "refcount": "0" + }, + "pvpanic": { + "size": "16384", + "refcount": "0" + }, + "parport": { + "size": "49152", + "refcount": "2" + }, + "ib_iser": { + "size": "49152", + "refcount": "0", + "version": "1.6" + }, + "rdma_cm": { + "size": "57344", + "refcount": "1" + }, + "iw_cm": { + "size": "49152", + "refcount": "1" + }, + "ib_cm": { + "size": "45056", + "refcount": "1" + }, + "ib_core": { + "size": "212992", + "refcount": "4" + }, + "configfs": { + "size": "40960", + "refcount": "2", + "version": "0.0.2" + }, + "iscsi_tcp": { + "size": "20480", + "refcount": "0" + }, + "libiscsi_tcp": { + "size": "24576", + "refcount": "1" + }, + "libiscsi": { + "size": "53248", + "refcount": "3" + }, + "scsi_transport_iscsi": { + "size": "98304", + "refcount": "4", + "version": "2.0-870" + }, + "autofs4": { + "size": "40960", + "refcount": "2" + }, + "btrfs": { + "size": "1060864", + "refcount": "0" + }, + "raid10": { + "size": "49152", + "refcount": "0" + }, + "raid456": { + "size": "110592", + "refcount": "0" + }, + "async_raid6_recov": { + "size": "20480", + "refcount": "1" + }, + "async_memcpy": { + "size": "16384", + "refcount": "2" + }, + "async_pq": { + "size": "16384", + "refcount": "2" + }, + "async_xor": { + "size": "16384", + "refcount": "3" + }, + "async_tx": { + "size": "16384", + "refcount": "5" + }, + "xor": { + "size": "24576", + "refcount": "2" + }, + "raid6_pq": { + "size": "102400", + "refcount": "4" + }, + "libcrc32c": { + "size": "16384", + "refcount": "1" + }, + "raid1": { + "size": "40960", + "refcount": "0" + }, + "raid0": { + "size": "20480", + "refcount": "0" + }, + "multipath": { + "size": "16384", + "refcount": "0" + }, + "linear": { + "size": "16384", + "refcount": "0" + }, + "crct10dif_pclmul": { + "size": "16384", + "refcount": "0" + }, + "crc32_pclmul": { + "size": "16384", + "refcount": "0" + }, + "ghash_clmulni_intel": { + "size": "16384", + "refcount": "0" + }, + "aesni_intel": { + "size": "167936", + "refcount": "0" + }, + "aes_x86_64": { + "size": "20480", + "refcount": "1" + }, + "lrw": { + "size": "16384", + "refcount": "1" + }, + "glue_helper": { + "size": "16384", + "refcount": "1" + }, + "ablk_helper": { + "size": "16384", + "refcount": "1" + }, + "cryptd": { + "size": "24576", + "refcount": "3" + }, + "psmouse": { + "size": "139264", + "refcount": "0" + }, + "virtio_net": { + "size": "32768", + "refcount": "0" + }, + "virtio_scsi": { + "size": "20480", + "refcount": "1" + } + } + }, + "os": "linux", + "os_version": "4.8.0-52-generic", + "lsb": { + "id": "Ubuntu", + "description": "Ubuntu 16.04.2 LTS", + "release": "16.04", + "codename": "xenial" + }, + "platform": "ubuntu", + "platform_version": "16.04", + "platform_family": "debian", + "filesystem": { + "by_device": { + "udev": { + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ], + "mounts": [ + "/dev" + ] + }, + "tmpfs": { + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ], + "mounts": [ + "/run", + "/dev/shm", + "/run/lock", + "/sys/fs/cgroup", + "/run/user/1001" + ] + }, + "/dev/sda1": { + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs", + "mounts": [ + "/" + ] + }, + "sysfs": { + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys" + ] + }, + "proc": { + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/proc" + ] + }, + "devpts": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ], + "mounts": [ + "/dev/pts" + ] + }, + "securityfs": { + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys/kernel/security" + ] + }, + "cgroup": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ], + "mounts": [ + "/sys/fs/cgroup/systemd", + "/sys/fs/cgroup/perf_event", + "/sys/fs/cgroup/blkio", + "/sys/fs/cgroup/devices", + "/sys/fs/cgroup/net_cls,net_prio", + "/sys/fs/cgroup/cpuset", + "/sys/fs/cgroup/cpu,cpuacct", + "/sys/fs/cgroup/memory", + "/sys/fs/cgroup/freezer", + "/sys/fs/cgroup/pids", + "/sys/fs/cgroup/hugetlb" + ] + }, + "pstore": { + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "mounts": [ + "/sys/fs/pstore" + ] + }, + "hugetlbfs": { + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/dev/hugepages" + ] + }, + "mqueue": { + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/dev/mqueue" + ] + }, + "systemd-1": { + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ], + "mounts": [ + "/proc/sys/fs/binfmt_misc" + ] + }, + "debugfs": { + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/kernel/debug" + ] + }, + "fusectl": { + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/fs/fuse/connections" + ] + }, + "configfs": { + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ], + "mounts": [ + "/sys/kernel/config" + ] + }, + "lxcfs": { + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ], + "mounts": [ + "/var/lib/lxcfs" + ] + }, + "/dev/sda": { + } + }, + "by_mountpoint": { + "/dev": { + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ], + "devices": [ + "udev" + ] + }, + "/run": { + "kb_size": "378828", + "kb_used": "5292", + "kb_available": "373536", + "percent_used": "2%", + "total_inodes": "473531", + "inodes_used": "460", + "inodes_available": "473071", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "size=378828k", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/": { + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs", + "devices": [ + "/dev/sda1" + ] + }, + "/dev/shm": { + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "1", + "inodes_available": "473530", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev" + ], + "devices": [ + "tmpfs" + ] + }, + "/run/lock": { + "kb_size": "5120", + "kb_used": "0", + "kb_available": "5120", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "8", + "inodes_available": "473523", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=5120k" + ], + "devices": [ + "tmpfs" + ] + }, + "/sys/fs/cgroup": { + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "16", + "inodes_available": "473515", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "mode=755" + ], + "devices": [ + "tmpfs" + ] + }, + "/run/user/1001": { + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ], + "devices": [ + "tmpfs" + ] + }, + "/sys": { + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "sysfs" + ] + }, + "/proc": { + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "proc" + ] + }, + "/dev/pts": { + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ], + "devices": [ + "devpts" + ] + }, + "/sys/kernel/security": { + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "securityfs" + ] + }, + "/sys/fs/cgroup/systemd": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "xattr", + "release_agent=/lib/systemd/systemd-cgroups-agent", + "name=systemd" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/pstore": { + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ], + "devices": [ + "pstore" + ] + }, + "/sys/fs/cgroup/perf_event": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "perf_event" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/blkio": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "blkio" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/devices": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "devices" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/net_cls,net_prio": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "net_cls", + "net_prio" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/cpuset": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpuset" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/cpu,cpuacct": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpu", + "cpuacct" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/memory": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/freezer": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "freezer" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/pids": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "pids" + ], + "devices": [ + "cgroup" + ] + }, + "/sys/fs/cgroup/hugetlb": { + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ], + "devices": [ + "cgroup" + ] + }, + "/dev/hugepages": { + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "hugetlbfs" + ] + }, + "/dev/mqueue": { + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "mqueue" + ] + }, + "/proc/sys/fs/binfmt_misc": { + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ], + "devices": [ + "systemd-1" + ] + }, + "/sys/kernel/debug": { + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "debugfs" + ] + }, + "/sys/fs/fuse/connections": { + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "fusectl" + ] + }, + "/sys/kernel/config": { + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ], + "devices": [ + "configfs" + ] + }, + "/var/lib/lxcfs": { + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ], + "devices": [ + "lxcfs" + ] + } + }, + "by_pair": { + "udev,/dev": { + "device": "udev", + "kb_size": "1886700", + "kb_used": "0", + "kb_available": "1886700", + "percent_used": "0%", + "mount": "/dev", + "total_inodes": "471675", + "inodes_used": "371", + "inodes_available": "471304", + "inodes_percent_used": "1%", + "fs_type": "devtmpfs", + "mount_options": [ + "rw", + "nosuid", + "relatime", + "size=1886700k", + "nr_inodes=471675", + "mode=755" + ] + }, + "tmpfs,/run": { + "device": "tmpfs", + "kb_size": "378828", + "kb_used": "5292", + "kb_available": "373536", + "percent_used": "2%", + "mount": "/run", + "total_inodes": "473531", + "inodes_used": "460", + "inodes_available": "473071", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "size=378828k", + "mode=755" + ] + }, + "/dev/sda1,/": { + "device": "/dev/sda1", + "kb_size": "10098468", + "kb_used": "1401956", + "kb_available": "8680128", + "percent_used": "14%", + "mount": "/", + "total_inodes": "1280000", + "inodes_used": "90038", + "inodes_available": "1189962", + "inodes_percent_used": "8%", + "fs_type": "ext4", + "mount_options": [ + "rw", + "relatime", + "data=ordered" + ], + "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", + "label": "cloudimg-rootfs" + }, + "tmpfs,/dev/shm": { + "device": "tmpfs", + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "mount": "/dev/shm", + "total_inodes": "473531", + "inodes_used": "1", + "inodes_available": "473530", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev" + ] + }, + "tmpfs,/run/lock": { + "device": "tmpfs", + "kb_size": "5120", + "kb_used": "0", + "kb_available": "5120", + "percent_used": "0%", + "mount": "/run/lock", + "total_inodes": "473531", + "inodes_used": "8", + "inodes_available": "473523", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "size=5120k" + ] + }, + "tmpfs,/sys/fs/cgroup": { + "device": "tmpfs", + "kb_size": "1894124", + "kb_used": "0", + "kb_available": "1894124", + "percent_used": "0%", + "mount": "/sys/fs/cgroup", + "total_inodes": "473531", + "inodes_used": "16", + "inodes_available": "473515", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "ro", + "nosuid", + "nodev", + "noexec", + "mode=755" + ] + }, + "tmpfs,/run/user/1001": { + "device": "tmpfs", + "kb_size": "378828", + "kb_used": "0", + "kb_available": "378828", + "percent_used": "0%", + "mount": "/run/user/1001", + "total_inodes": "473531", + "inodes_used": "4", + "inodes_available": "473527", + "inodes_percent_used": "1%", + "fs_type": "tmpfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "size=378828k", + "mode=700", + "uid=1001", + "gid=1002" + ] + }, + "sysfs,/sys": { + "device": "sysfs", + "mount": "/sys", + "fs_type": "sysfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "proc,/proc": { + "device": "proc", + "mount": "/proc", + "fs_type": "proc", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "devpts,/dev/pts": { + "device": "devpts", + "mount": "/dev/pts", + "fs_type": "devpts", + "mount_options": [ + "rw", + "nosuid", + "noexec", + "relatime", + "gid=5", + "mode=620", + "ptmxmode=000" + ] + }, + "securityfs,/sys/kernel/security": { + "device": "securityfs", + "mount": "/sys/kernel/security", + "fs_type": "securityfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "cgroup,/sys/fs/cgroup/systemd": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/systemd", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "xattr", + "release_agent=/lib/systemd/systemd-cgroups-agent", + "name=systemd" + ] + }, + "pstore,/sys/fs/pstore": { + "device": "pstore", + "mount": "/sys/fs/pstore", + "fs_type": "pstore", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime" + ] + }, + "cgroup,/sys/fs/cgroup/perf_event": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/perf_event", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "perf_event" + ] + }, + "cgroup,/sys/fs/cgroup/blkio": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/blkio", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "blkio" + ] + }, + "cgroup,/sys/fs/cgroup/devices": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/devices", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "devices" + ] + }, + "cgroup,/sys/fs/cgroup/net_cls,net_prio": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/net_cls,net_prio", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "net_cls", + "net_prio" + ] + }, + "cgroup,/sys/fs/cgroup/cpuset": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpuset", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpuset" + ] + }, + "cgroup,/sys/fs/cgroup/cpu,cpuacct": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpu,cpuacct", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "cpu", + "cpuacct" + ] + }, + "cgroup,/sys/fs/cgroup/memory": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/memory", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "memory" + ] + }, + "cgroup,/sys/fs/cgroup/freezer": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/freezer", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "freezer" + ] + }, + "cgroup,/sys/fs/cgroup/pids": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/pids", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "pids" + ] + }, + "cgroup,/sys/fs/cgroup/hugetlb": { + "device": "cgroup", + "mount": "/sys/fs/cgroup/hugetlb", + "fs_type": "cgroup", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "noexec", + "relatime", + "hugetlb" + ] + }, + "hugetlbfs,/dev/hugepages": { + "device": "hugetlbfs", + "mount": "/dev/hugepages", + "fs_type": "hugetlbfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "mqueue,/dev/mqueue": { + "device": "mqueue", + "mount": "/dev/mqueue", + "fs_type": "mqueue", + "mount_options": [ + "rw", + "relatime" + ] + }, + "systemd-1,/proc/sys/fs/binfmt_misc": { + "device": "systemd-1", + "mount": "/proc/sys/fs/binfmt_misc", + "fs_type": "autofs", + "mount_options": [ + "rw", + "relatime", + "fd=33", + "pgrp=1", + "timeout=0", + "minproto=5", + "maxproto=5", + "direct", + "pipe_ino=10449" + ] + }, + "debugfs,/sys/kernel/debug": { + "device": "debugfs", + "mount": "/sys/kernel/debug", + "fs_type": "debugfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "fusectl,/sys/fs/fuse/connections": { + "device": "fusectl", + "mount": "/sys/fs/fuse/connections", + "fs_type": "fusectl", + "mount_options": [ + "rw", + "relatime" + ] + }, + "configfs,/sys/kernel/config": { + "device": "configfs", + "mount": "/sys/kernel/config", + "fs_type": "configfs", + "mount_options": [ + "rw", + "relatime" + ] + }, + "lxcfs,/var/lib/lxcfs": { + "device": "lxcfs", + "mount": "/var/lib/lxcfs", + "fs_type": "fuse.lxcfs", + "mount_options": [ + "rw", + "nosuid", + "nodev", + "relatime", + "user_id=0", + "group_id=0", + "allow_other" + ] + }, + "/dev/sda,": { + "device": "/dev/sda" + } + } + }, + "dmi": { + "dmidecode_version": "3.0" + }, + "init_package": "systemd", + "ohai_time": 1496219099.5081818, + "command": { + "ps": "ps -ef" + }, + "root_group": "root", + "languages": { + "ruby": { + "platform": "x86_64-linux-gnu", + "version": "2.3.1", + "release_date": "2016-04-26", + "target": "x86_64-pc-linux-gnu", + "target_cpu": "x86_64", + "target_vendor": "pc", + "target_os": "linux-gnu", + "host": "x86_64-pc-linux-gnu", + "host_cpu": "x86_64", + "host_os": "linux-gnu", + "host_vendor": "pc", + "bin_dir": "/usr/local/bin", + "ruby_bin": "/usr/local/bin/ruby", + "gems_dir": "/usr/local/gems", + "gem_bin": "/usr/local/bin/gem" + }, + "powershell": null + }, + "chef_packages": { + "chef": { + "version": "13.1.31", + "chef_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-13.1.31/lib" + }, + "ohai": { + "version": "13.1.0", + "ohai_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/ohai-13.1.0/lib/ohai" + } + }, + "counters": { + "network": { + "interfaces": { + "eth0": { + "rx": { + "bytes": "0", + "packets": "0", + "errors": "0", + "drop": 0, + "overrun": 0, + "frame": 0, + "compressed": 0, + "multicast": 0 + }, + "tx": { + "bytes": "342", + "packets": "0", + "errors": "0", + "drop": 0, + "overrun": 0, + "collisions": "0", + "carrier": 0, + "compressed": 0 + } + } + } + } + }, + "current_user": "fauxhai", + "domain": "local", + "etc": { + "passwd": { + "fauxhai": { + "dir": "/home/fauxhai", + "gid": 0, + "uid": 0, + "shell": "/bin/bash", + "gecos": "Fauxhai" + } + }, + "group": { + "fauxhai": { + "gid": 0, + "members": [ + "fauxhai" + ] + } + } + }, + "hostname": "Fauxhai", + "fqdn": "fauxhai.local", + "ipaddress": "10.0.0.2", + "keys": { + "ssh": { + "host_dsa_public": "ssh-dss AAAAB3NzaC1kc3MAAACBAJFo9BLAw4WKEs5hgipk5m423FzBsDXCZSMcC9ca/om/1VYzMqImixGe3uICDzNFUWxFoLJTQAOccyzo6MXZiQqwWJDLFi5qOSr6w2XcMyE+zd4wOyMoDiVM5fizmG8K3FzrqvGjwBcHcBdOQnavSijoj38DN25J9zhrid5BY4WlAAAAFQDxXrCyG52XCzn3FV4ej38wJBkomQAAAIBovGPJ4mP2P6BK8lHl0PPbktwQbWlpJ13oz6REJFDVcUi7vV26bX/BjQX+ohzZQzljdz1SpUbPc/8nuA4darYkVh91eBi307EN8IdxRHj2eBgp/ZG4yshIebG3WHrwJD/xUjjZ1MRfyDT1ermVi4LvjjPgWDxLZnPpMaR6S1nzgQAAAIEAj0Vd6DCWslvlsZ8+N53HWsqPi3gnx35JoLPz9Z2epkKIKqmEHav+93G3hdfztVa4I4t3phoPniQchYryF5+RNg8hqxKzjNtrIqUYCeuf2NJrksNsH7OZygPHZpqt4kTuwAGZxjxEGfAI0y8DhkU2ntp2LnzRnWH106BQBCmcXwo= fauxhai.local", + "host_rsa_public": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtLCeqtqr/HbnORckw1ukdLhpfGoOPFi5/esKEokzTqq1gsgQ2V8emmyjfq1i6XXfRtSBxkdlHv/GWdP5wBTuE2G85MzBkVSQPvmwQN8lX/JMPEEtKXkeOo0o92/PiSmvY4eRsdF0mw40Uvg7jtE3f3fxj497kzh5fKtkrHnF4x9gXCbVdr3FqXJfggR5IJwAxToerbK7x/uRS+7YuZI9Pip3tt14nv9ezwXcuGb/tvjWOZINiFl8izVIFKi7sxfTX09p4NgamxRS7TD2Yd0jT8nEoF9UZTsgXcJ1kDSx7N7NxFfNfP6rCdOGRRz4gUhXtsUjG/XkxPeCwZ7A9VnOD fauxhai.local" + } + }, + "macaddress": "11:11:11:11:11:11", + "network": { + "default_gateway": "10.0.0.1", + "default_interface": "eth0", + "settings": { + }, + "interfaces": { + "eth0": { + "addresses": { + "10.0.0.2": { + "broadcast": "10.0.0.255", + "family": "inet", + "netmask": "255.255.255.0", + "prefixlen": "24", + "scope": "Global" + } + }, + "arp": { + "10.0.0.1": "fe:ff:ff:ff:ff:ff" + }, + "encapsulation": "Ethernet", + "flags": [ + "BROADCAST", + "MULTICAST", + "UP", + "LOWER_UP" + ], + "mtu": "1500", + "number": "0", + "routes": [ + { + "destination": "10.0.0.0/24", + "family": "inet", + "scope": "link", + "proto": "kernel", + "src": "10.0.0.2" + } + ], + "state": "up", + "type": "eth" + } + } + }, + "uptime": "30 days 15 hours 07 minutes 30 seconds", + "uptime_seconds": 2646450, + "cpu": { + "real": 1, + "total": 1, + "cores": 1 + }, + "memory": { + "total": "1048576kB" + }, + "virtualization": { + "systems": { + } + } +} diff --git a/spec/chef/fixtures/migration/failed-migration-status-file b/spec/chef/fixtures/migration/failed-migration-status-file new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL21pZ3JhdGlvbi9mYWlsZWQtbWlncmF0aW9uLXN0YXR1cy1maWxl --- /dev/null +++ b/spec/chef/fixtures/migration/failed-migration-status-file @@ -0,0 +1,1 @@ +1 diff --git a/spec/chef/fixtures/migration/successful-migration-status-file b/spec/chef/fixtures/migration/successful-migration-status-file new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2ZpeHR1cmVzL21pZ3JhdGlvbi9zdWNjZXNzZnVsLW1pZ3JhdGlvbi1zdGF0dXMtZmlsZQ== --- /dev/null +++ b/spec/chef/fixtures/migration/successful-migration-status-file @@ -0,0 +1,1 @@ +0 diff --git a/spec/chef/gitlab-ctl-commands-ee/geo_replication_spec.rb b/spec/chef/gitlab-ctl-commands-ee/geo_replication_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvZ2VvX3JlcGxpY2F0aW9uX3NwZWMucmI= --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/geo_replication_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' +require 'omnibus-ctl' + +RSpec.describe 'gitlab-ctl geo-replication' do + let(:toggle_command) { double(Geo::ReplicationToggleCommand) } + + subject { Omnibus::Ctl.new('testing-ctl') } + + before do + allow_any_instance_of(Omnibus::Ctl).to receive(:require).and_call_original + allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( + '/opt/testing-ctl/embedded/service/omnibus-ctl-ee/lib/geo/replication_process' + ) do + require_relative('../../../files/gitlab-ctl-commands-ee/lib/geo/replication_process') + end + allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( + '/opt/testing-ctl/embedded/service/omnibus-ctl-ee/lib/geo/replication_toggle_command' + ) do + require_relative('../../../files/gitlab-ctl-commands-ee/lib/geo/replication_toggle_command') + end + + subject.load_file('files/gitlab-ctl-commands-ee/geo_replication.rb') + end + + it 'appends the geo replication pause and resume commands' do + expect(subject.get_all_commands_hash).to include('geo-replication-pause') + expect(subject.get_all_commands_hash).to include('geo-replication-resume') + end + + describe 'pause' do + it 'calls pause' do + expect(Geo::ReplicationToggleCommand).to receive(:new).with(anything, 'pause', anything).and_return(toggle_command) + expect(toggle_command).to receive(:execute!) + + subject.geo_replication_pause + end + end + + describe 'resume' do + it 'calls resume' do + expect(Geo::ReplicationToggleCommand).to receive(:new).with(anything, 'resume', anything).and_return(toggle_command) + expect(toggle_command).to receive(:execute!) + + subject.geo_replication_resume + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/geo_spec.rb b/spec/chef/gitlab-ctl-commands-ee/geo_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvZ2VvX3NwZWMucmI= --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/geo_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'omnibus-ctl' +require 'optparse' + +require_relative('../../../files/gitlab-ctl-commands/lib/gitlab_ctl') +require_relative('../../../files/gitlab-ctl-commands-ee/lib/geo') + +RSpec.describe 'gitlab-ctl geo' do + commands = %w(promote) + + command_lines = { + 'promote' => [] + } + + command_options = { + 'promote' => {} + } + + describe '.parse_options' do + before do + allow(Geo::Utils).to receive(:warn_and_exit).and_call_original + allow(Kernel).to receive(:exit) { |code| raise "Kernel.exit(#{code})" } + allow(Kernel).to receive(:warn) + end + + it 'throws an error when global options are invalid' do + expect { Geo.parse_options(%w(geo --foo)) }.to raise_error(OptionParser::ParseError) + end + + it 'throws an error when sub-command is not specified' do + expect { Geo.parse_options(%w(geo -v)) }.to raise_error(OptionParser::ParseError) + end + + it 'throws an error when sub-command is not defined' do + expect { Geo.parse_options(%w(geo -v foo)) }.to raise_error(OptionParser::ParseError) + end + + it 'recognizes global options' do + expect(Geo.parse_options(%w(geo -v -q promote))).to include(quiet: true, verbose: true) + end + + context 'when sub-command is passed' do + commands.each do |cmd| + it "parses #{cmd} options" do + cmd_line = command_lines[cmd] || [] + cmd_opts = command_options[cmd] || {} + cmd_opts[:command] = cmd + + expect(Geo.parse_options(%W(geo #{cmd}) + cmd_line)).to include(cmd_opts) + end + end + end + + context 'when help option is passed' do + it 'shows help message and exit for global help option' do + expect { Geo.parse_options(%w(geo -h)) }.to raise_error('Kernel.exit(0)') + expect(Geo::Utils).to have_received(:warn_and_exit).with(/Usage help/) + end + + commands.each do |cmd| + it "shows help message and exit for #{cmd} help option" do + expect { Geo.parse_options(%W(geo #{cmd} -h)) }.to raise_error('Kernel.exit(0)') + expect(Geo::Utils).to have_received(:warn_and_exit).with(instance_of(OptionParser)) + end + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/consul_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/consul_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2NvbnN1bF9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/consul_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands-ee/lib') + +require 'consul' + +RSpec.describe ConsulHandler do + describe '#initialize' do + it 'creates instance based on args' do + instance = ConsulHandler.new([nil, nil, 'consul', 'kv', 'set'], 'rspec') + expect(instance.command).to eq(ConsulHandler::Kv) + expect(instance.subcommand).to eq('set') + expect(instance.input).to eq('rspec') + end + end + + describe '#execute' do + it 'calls the method on command' do + instance = ConsulHandler.new([nil, nil, 'consul', 'kv', 'set'], 'rspec') + instance.command = spy + expect(instance.command).to receive(:set).with('rspec') + instance.execute + end + end + + let(:consul_cmd) { '/opt/gitlab/embedded/bin/consul' } + + describe ConsulHandler::Kv do + it 'allows nil values' do + results = double('results', run_command: [], error!: nil, stdout: '') + expect(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} kv put foo ").and_return(results) + described_class.send(:put, 'foo') + end + + describe '#get' do + context 'existing key' do + before do + results = double('results', run_command: [], error!: nil, stdout: 'bar') + allow(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} kv get foo").and_return(results) + end + + it 'returns the expected key' do + expect(described_class.get('foo')).to eq('bar') + end + end + + context 'non-existing key' do + before do + results = double('results', run_command: [], stdout: '', stderr: 'oops') + allow(results).to receive(:error!).and_raise(StandardError) + allow(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} kv get foo").and_return(results) + end + + it 'raises an error' do + expect { described_class.get('foo') }.to raise_error(ConsulHandler::ConsulError, 'StandardError: oops') + end + end + end + end + + describe ConsulHandler::Encrypt do + describe '#keygen' do + it 'returns the generated key' do + generated_key = 'BYX6EPaUiUI0TDdm6gAMmmLnpJSwePJ33Xwh6rjCYbg=' + results = double('results', run_command: [], error!: nil, stdout: generated_key) + allow(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} keygen").and_return(results) + + expect(described_class.keygen).to eq(generated_key) + end + + it 'raises an error' do + results = double('results', run_command: [], stdout: '', stderr: 'oops') + allow(results).to receive(:error!).and_raise(StandardError) + allow(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} keygen").and_return(results) + + expect { described_class.keygen }.to raise_error(ConsulHandler::ConsulError, 'StandardError: oops') + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/geo/pitr_file_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/geo/pitr_file_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2dlby9waXRyX2ZpbGVfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/geo/pitr_file_spec.rb @@ -0,0 +1,105 @@ +require 'spec_helper' +require 'geo/pitr_file' + +RSpec.describe Geo::PitrFile do + let(:lsn) { 'imafakelsn' } + let(:filepath) { '/fake/file/path' } + subject { described_class.new(filepath, consul_key: 'fake-key') } + + context 'with a local file' do + before do + allow(Dir).to receive(:exist?).with('/opt/gitlab/service/consul').and_return(false) + end + + it "doesn't use consul" do + expect(subject.use_consul?).to be(false) + end + + context '#create' do + it 'creates the pitr file' do + expect(File).to receive(:write).with(filepath, lsn) + subject.create(lsn) + end + end + + context '#delete' do + it 'removes the pitr file' do + allow(File).to receive(:exist?).with(filepath).and_return(true) + expect(File).to receive(:delete).with(filepath) + + subject.delete + end + + it 'does not raise an error if the file does not exist' do + allow(File).to receive(:exist?).with(filepath).and_return(false) + allow(File).to receive(:delete).with(filepath).and_raise(Errno::ENOENT) + + expect { subject.delete }.to_not raise_error(Geo::PitrFileError, "Unable to delete PITR") + end + end + + context '#get' do + before do + allow(File).to receive(:read).with(filepath).and_return(lsn) + end + + it 'returns the correct lsn' do + expect(subject.get).to eq(lsn) + end + + it 'raises an error if the file does not exist' do + allow(File).to receive(:read).with(filepath).and_raise(Errno::ENOENT) + + expect { subject.get }.to raise_error(Geo::PitrFileError, "Unable to fetch PITR") + end + end + end + + context 'when consul is enabled' do + before do + allow(Dir).to receive(:exist?).with('/opt/gitlab/service/consul').and_return(true) + end + + it "uses consul" do + expect(subject.use_consul?).to be(true) + end + + context '#create' do + it 'create the write key entry' do + expect(ConsulHandler::Kv).to receive(:put).with(subject.consul_key, lsn) + + subject.create(lsn) + end + end + + context '#delete' do + it 'removes the consul key' do + expect(ConsulHandler::Kv).to receive(:delete).with(subject.consul_key) + + subject.delete + end + + it 'raises an error if the key does not exist' do + expect { subject.delete }.to raise_error(Geo::PitrFileError, 'Unable to delete PITR') + end + end + + context '#get' do + context 'existing key' do + before do + allow(ConsulHandler::Kv).to receive(:get).with(subject.consul_key).and_return(lsn) + end + + it 'returns the correct lsn' do + expect(subject.get).to eq(lsn) + end + end + + context 'non-existing key' do + it 'raises an error if the key does not exist' do + expect { subject.get }.to raise_error(Geo::PitrFileError, 'Unable to fetch PITR') + end + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_db_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_db_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2dlby9wcm9tb3RlX2RiX3NwZWMucmI= --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_db_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +require 'geo/promote_db' +require 'gitlab_ctl/util' + +RSpec.describe Geo::PromoteDb, '#execute' do + let(:instance) { double(base_path: '/opt/gitlab/embedded') } + + subject(:command) { described_class.new(instance) } + + before do + allow($stdout).to receive(:puts) + allow($stdout).to receive(:print) + + allow(command).to receive(:run_command).with(any_args) + + allow(command).to receive(:run_command).and_return(double('error!' => nil)) + end + + context 'when PITR file does not exist' do + it 'does not run PITR recovery' do + expect(command).not_to receive(:write_recovery_settings) + + command.execute + end + end + + context 'postgres dir is in non-default location' do + it 'uses the specified postgres directory when writing ' do + expected_dir = '/non/default/location' + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'postgresql' => { 'dir' => expected_dir } }) + + expect(described_class.new(instance).postgresql_dir_path).to eq(expected_dir) + end + end + + context 'when PITR file exists' do + let(:lsn) { '16/B374D848' } + + before do + allow(command).to receive(:lsn_from_pitr_file).and_return(lsn) + allow(command).to receive(:write_geo_config_file).and_return(true) + end + + it 'writes out the recovery settings' do + expect(command).to receive(:write_recovery_settings).with(lsn) + + expect { command.execute }.to output( + /Recovery to point #{lsn} and promoting.../).to_stdout + end + + it 'writes out the geo configuration file' do + expect(command).to receive(:write_geo_config_file).once + + command.execute + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2dlby9wcm9tb3RlX3NwZWMucmI= --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_spec.rb @@ -0,0 +1,499 @@ +require 'spec_helper' +require 'geo/promote' +require 'geo/promote_db' +require 'gitlab_ctl/util' + +require_relative('../../../../../files/gitlab-ctl-commands-ee/lib/patroni') + +RSpec.describe Geo::Promote, '#execute' do + let(:base_path) { '/opt/gitlab/embedded' } + let(:ctl) { double(base_path: base_path, data_path: '/var/opt/gitlab/postgresql/data') } + let(:config_path) { Dir.mktmpdir } + let(:gitlab_cluster_config_path) { File.join(config_path, 'gitlab-cluster.json') } + let(:options) { { force: false } } + + subject(:command) { described_class.new(ctl, options) } + + before do + stub_const('GitlabCluster::CONFIG_PATH', config_path) + stub_const('GitlabCluster::JSON_FILE', gitlab_cluster_config_path) + + allow($stdin).to receive(:gets).and_return('y') + allow($stdout).to receive(:puts) + allow($stdout).to receive(:print) + + allow(ctl).to receive(:log).with(any_args) + allow(ctl).to receive(:service_enabled?).and_return(false) + allow(command).to receive(:run_command).with(any_args) + end + + around do |example| + example.run + rescue SystemExit + end + + after do + FileUtils.rm_rf(config_path) + end + + describe '#execute' do + context 'when not running in force mode' do + it 'asks for confirmation' do + expect { command.execute }.to output(/Are you sure you want to proceed\?/).to_stdout + end + end + + context 'when running in force mode' do + let(:options) { { force: true } } + + it 'does not ask for final confirmation' do + expect { command.execute }.to_not output(/Are you sure you want to proceed\?/).to_stdout + end + end + + context 'when there are no services to promote' do + before do + allow(ctl).to receive(:service_enabled?).and_return(false) + end + + it 'prints a message' do + expect { command.execute }.to output( + /No actions are required to promote this node./).to_stdout + end + + it 'returns 0 as exit code' do + expect { command.execute }.to raise_error(SystemExit) do |error| + expect(error.status).to eq(0) + end + end + end + + context 'when puma is enabled' do + before do + stub_service_enabled('puma') + end + + shared_examples 'promote secondary site to primary site' do + context 'on a primary node' do + it 'does not try to promote the secondary site to primary site' do + stub_primary_node + + allow(command).to receive(:run_reconfigure) + allow(command).to receive(:restart_services) + + expect(command).not_to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:set_secondary_as_primary", live: true) + + command.execute + end + end + + context 'on a secondary node' do + before do + stub_secondary_node + allow(command).to receive(:run_reconfigure) + end + + it 'promotes the secondary site to primary site' do + allow(command).to receive(:restart_services) + + expect(command).to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:set_secondary_as_primary", live: true).once.and_return(double(error?: false)) + + command.execute + end + + it 'restarts the puma service' do + allow(command).to receive(:promote_to_primary) + + expect(ctl).to receive(:run_sv_command_for_service).with('restart', 'puma').once.and_return(double(zero?: true)) + + command.execute + end + end + + context 'on a misconfigured node' do + it 'aborts promotion with an error message' do + stub_misconfigured_node + + expect { command.execute }.to output(/Unable to detect the role of this Geo node/).to_stdout + end + end + end + + context 'on a single-server secondary site' do + before do + stub_single_server_secondary_site + end + + it 'toggles the Geo primary/secondary roles' do + allow(command).to receive(:run_reconfigure) + allow(command).to receive(:promote_to_primary) + allow(command).to receive(:restart_services) + + command.execute + + expect(read_file_content(gitlab_cluster_config_path)).to eq("primary" => true, "secondary" => false) + end + + it 'runs reconfigure' do + allow(command).to receive(:toggle_geo_services) + allow(command).to receive(:promote_to_primary) + allow(command).to receive(:restart_services) + + expect(ctl).to receive(:run_chef).with(reconfigure_cmd).once.and_return(double(success?: true)) + + command.execute + end + + include_examples 'promote secondary site to primary site' + end + + context 'on a multiple-server secondary site' do + before do + stub_multiple_server_secondary_site + end + + include_examples 'promote secondary site to primary site' + + it 'disables Geo secondary settings' do + allow(command).to receive(:run_reconfigure) + allow(command).to receive(:promote_to_primary) + allow(command).to receive(:restart_services) + + command.execute + + expect(read_file_content(gitlab_cluster_config_path)).to eq("geo_secondary" => { "enable" => false }) + end + + it 'runs reconfigure' do + allow(command).to receive(:promote_to_primary) + allow(command).to receive(:restart_services) + + expect(ctl).to receive(:run_chef).with(reconfigure_cmd).once.and_return(double(success?: true)) + + command.execute + end + end + end + + context 'when gitaly is enabled' do + before do + stub_service_enabled('gitaly') + end + + it 'restarts the gitaly service' do + allow(command).to receive(:toggle_geo_services) + allow(command).to receive(:promote_to_primary) + allow(command).to receive(:run_reconfigure) + + expect(ctl).to receive(:run_sv_command_for_service).with('restart', 'gitaly').once.and_return(double(zero?: true)) + + command.execute + end + end + + context 'when praefect is enabled' do + before do + stub_service_enabled('praefect') + end + + it 'restarts the praefect service' do + allow(command).to receive(:toggle_geo_services) + allow(command).to receive(:promote_to_primary) + allow(command).to receive(:run_reconfigure) + + expect(ctl).to receive(:run_sv_command_for_service).with('restart', 'praefect').once.and_return(double(zero?: true)) + + command.execute + end + end + + context 'when workhorse is enabled' do + before do + stub_service_enabled('gitlab-workhorse') + end + + it 'restarts the workhorse service' do + allow(command).to receive(:toggle_geo_services) + allow(command).to receive(:promote_to_primary) + allow(command).to receive(:run_reconfigure) + + expect(ctl).to receive(:run_sv_command_for_service).with('restart', 'gitlab-workhorse').once.and_return(double(zero?: true)) + + command.execute + end + end + + shared_examples 'single-server secondary site' do + context 'on a single-server secondary site' do + before do + stub_single_server_secondary_site + end + + it 'toggles the Geo primary/secondary roles' do + allow(command).to receive(:run_reconfigure) + + command.execute + + expect(read_file_content(gitlab_cluster_config_path)).to eq("primary" => true, "secondary" => false) + end + + it 'runs reconfigure' do + expect(ctl).to receive(:run_chef).with(reconfigure_cmd).once.and_return(double(success?: true)) + + command.execute + end + end + end + + shared_examples 'multiple-server secondary site' do |settings| + context 'on a multiple-server secondary site' do + before do + stub_multiple_server_secondary_site + end + + it 'disables Geo secondary settings' do + allow(command).to receive(:run_reconfigure) + + command.execute + + expect(read_file_content(gitlab_cluster_config_path)).to eq(settings) + end + end + end + + context 'when sidekiq is enabled' do + before do + stub_service_enabled('sidekiq') + end + + include_examples 'single-server secondary site' + include_examples 'multiple-server secondary site', "geo_secondary" => { "enable" => false } + end + + context 'when geo-logcursor is enabled' do + before do + allow(ctl).to receive(:service_enabled?).with('geo-logcursor').and_return(true) + end + + include_examples 'single-server secondary site' + include_examples 'multiple-server secondary site', "geo_logcursor" => { "enable" => false } + end + + context 'when geo-postgresql is enabled' do + before do + allow(ctl).to receive(:service_enabled?).with('geo-postgresql').and_return(true) + end + + include_examples 'single-server secondary site' + include_examples 'multiple-server secondary site', "geo_postgresql" => { "enable" => false } + end + + context 'when postgresql is enabled' do + before do + stub_service_enabled('postgresql') + end + + context 'when PostgreSQL is in recovery mode' do + before do + stub_pg_is_in_recovery + end + + it 'promotes database to begin read-write operations' do + allow(command).to receive(:run_reconfigure) + + expect_any_instance_of(Geo::PromoteDb).to receive(:execute).once.and_return(true) + + command.execute + end + end + + context 'when PostgreSQL is not in recovery mode' do + before do + stub_pg_is_not_in_recovery + end + + it 'does not promote the PostgreSQL database' do + allow(command).to receive(:run_reconfigure) + + expect_any_instance_of(Geo::PromoteDb).not_to receive(:execute) + + command.execute + end + end + + it 'runs reconfigure' do + allow(command).to receive(:promote_database) + + expect(ctl).to receive(:run_chef).with(reconfigure_cmd).once.and_return(double(success?: true)) + + command.execute + end + end + + context 'when patroni is enabled' do + before do + stub_service_enabled('patroni') + end + + shared_examples 'promotes a Patroni leader' do + context 'when PostgreSQL is in recovery mode' do + before do + stub_pg_is_in_recovery + end + + it 'promotes database to begin read-write operations' do + allow(command).to receive(:disable_patroni_standby_cluster) + allow(command).to receive(:run_reconfigure) + + expect_any_instance_of(Geo::PromoteDb).to receive(:execute).once.and_return(true) + + command.execute + end + end + + context 'when PostgreSQL is not in recovery mode' do + before do + stub_pg_is_not_in_recovery + end + + it 'does not promote the PostgreSQL database' do + allow(command).to receive(:disable_patroni_standby_cluster) + allow(command).to receive(:run_reconfigure) + + expect_any_instance_of(Geo::PromoteDb).not_to receive(:execute) + + command.execute + end + end + + it 'disables Patroni Standby cluster settings' do + allow(command).to receive(:promote_postgresql_read_write) + allow(command).to receive(:run_reconfigure) + + command.execute + + expect(read_file_content(gitlab_cluster_config_path)).to eq("patroni" => { "standby_cluster" => { "enable" => false } }) + end + + it 'pauses Patroni, runs reconfigure and resume Patroni' do + allow(command).to receive(:promote_postgresql_read_write) + allow(command).to receive(:disable_patroni_standby_cluster) + + expect(command).to receive(:run_command).with(patroni_pause_cmd).twice.and_return(double(success?: true)) + expect(ctl).to receive(:run_chef).with(reconfigure_cmd).twice.and_return(double(success?: true)) + expect(command).to receive(:run_command).with(patroni_resume_cmd).twice.and_return(double(success?: true)) + + command.execute + end + end + + context 'on a Patroni standby leader' do + before do + allow(Patroni::Client).to receive(:new).and_return(double(standby_leader?: true, leader?: false, replica?: false)) + end + + include_examples 'promotes a Patroni leader' + end + + context 'on a Patroni leader' do + before do + allow(Patroni::Client).to receive(:new).and_return(double(standby_leader?: false, leader?: true, replica?: false)) + end + + include_examples 'promotes a Patroni leader' + end + + context 'on a Patroni replica' do + before do + allow(Patroni::Client).to receive(:new).and_return(double(standby_leader?: false, leader?: false, replica?: true)) + end + + it 'does not promote the PostgreSQL database' do + allow(command).to receive(:run_reconfigure) + + expect_any_instance_of(Geo::PromoteDb).not_to receive(:execute) + + command.execute + end + + it 'disables Patroni Standby cluster settings' do + allow(command).to receive(:promote_postgresql_read_write) + allow(command).to receive(:run_reconfigure) + + command.execute + + expect(read_file_content(gitlab_cluster_config_path)).to eq("patroni" => { "standby_cluster" => { "enable" => false } }) + end + + it 'runs reconfigure' do + allow(command).to receive(:promote_postgresql_read_write) + allow(command).to receive(:disable_patroni_standby_cluster) + + expect(command).not_to receive(:run_command).with(patroni_pause_cmd) + expect(ctl).to receive(:run_chef).with(reconfigure_cmd).twice.and_return(double(success?: true)) + expect(command).not_to receive(:run_command).with(patroni_resume_cmd) + + command.execute + end + end + end + + context 'when promotion succeeds' do + before do + allow(command).to receive(:ask_for_confirmation) + allow(command).to receive(:check_running_services) + allow(command).to receive(:promote_database) + allow(command).to receive(:toggle_geo_services) + allow(command).to receive(:promote_to_primary) + allow(command).to receive(:run_reconfigure) + allow(command).to receive(:restart_services) + end + + it 'prints a success message' do + expect { command.execute }.to output( + /You successfully promoted the current node! It might take some time to reload the services, and for the changes to take effect./).to_stdout + end + end + + let(:patroni_pause_cmd) { "#{base_path}/bin/gitlab-ctl patroni pause" } + let(:patroni_resume_cmd) { "#{base_path}/bin/gitlab-ctl patroni resume" } + let(:pg_is_in_recovery_cmd) { "#{base_path}/bin/gitlab-psql -c \"SELECT pg_is_in_recovery();\" -q -t" } + let(:reconfigure_cmd) { "#{base_path}/embedded/cookbooks/dna.json" } + + def stub_service_enabled(service) + allow(ctl).to receive(:service_enabled?).with(service).and_return(true) + end + + def stub_primary_node + allow(command).to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:site:role", live: true).and_return(double(error?: false, stdout: 'primary')) + end + + def stub_secondary_node + allow(command).to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:site:role", live: true).and_return(double(error?: false, stdout: 'secondary')) + end + + def stub_misconfigured_node + allow(command).to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:site:role", live: true).and_return(double(error?: true, stdout: 'misconfigured')) + end + + def stub_single_server_secondary_site + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return('roles' => { 'geo-secondary' => { 'enable' => true } }) + end + + def stub_multiple_server_secondary_site + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return('roles' => {}) + end + + def stub_pg_is_in_recovery + allow(command).to receive(:run_command).with(pg_is_in_recovery_cmd, anything).and_return(double(error?: false, stdout: 't')) + end + + def stub_pg_is_not_in_recovery + allow(command).to receive(:run_command).with(pg_is_in_recovery_cmd, anything).and_return(double(error?: false, stdout: 'f')) + end + + def read_file_content(fullpath) + JSON.parse(File.read(fullpath)) + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2dlby9wcm9tb3RlX3RvX3ByaW1hcnlfbm9kZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb @@ -0,0 +1,275 @@ +require 'spec_helper' +require 'fileutils' +require 'geo/promote_to_primary_node' +require 'geo/promote_db' +require 'geo/promotion_preflight_checks' +require 'gitlab_ctl/util' + +RSpec.describe Geo::PromoteToPrimaryNode, '#execute' do + let(:options) { { skip_preflight_checks: true } } + + let(:instance) { double(base_path: '/opt/gitlab/embedded', data_path: '/var/opt/gitlab/postgresql/data') } + + subject(:command) { described_class.new(instance, options) } + + let(:config_path) { Dir.mktmpdir } + + before do + allow($stdout).to receive(:puts) + allow($stdout).to receive(:print) + + allow(command).to receive(:run_command).with(any_args) + end + + after do + FileUtils.rm_rf(config_path) + end + + describe '#promote_postgresql_to_primary' do + before do + allow(STDIN).to receive(:gets).and_return('y') + + allow(command).to receive(:toggle_geo_roles).and_return(true) + allow(command).to receive(:reconfigure).and_return(true) + allow(command).to receive(:promote_to_primary).and_return(true) + allow(command).to receive(:success_message).and_return(true) + end + + it 'promotes the database' do + expect_any_instance_of(Geo::PromoteDb).to receive(:execute) + + command.execute + end + end + + describe '#run_preflight_checks' do + before do + allow(STDIN).to receive(:gets).and_return('y') + + allow(command).to receive(:toggle_geo_roles).and_return(true) + allow(command).to receive(:promote_postgresql_to_primary).and_return(true) + allow(command).to receive(:reconfigure).and_return(true) + allow(command).to receive(:promote_to_primary).and_return(true) + allow(command).to receive(:success_message).and_return(true) + end + + context 'when `--skip-preflight-checks` is passed' do + it 'does not run execute promotion preflight checks' do + expect_any_instance_of(Geo::PromotionPreflightChecks).not_to receive(:execute) + + command.execute + end + end + + context 'when `--skip-preflight-checks` is not passed' do + let(:options) { { confirm_primary_is_down: true } } + + before do + allow_any_instance_of(Geo::PromotionPreflightChecks).to receive( + :execute).and_return(true) + end + + it 'runs preflight checks' do + expect_any_instance_of(Geo::PromotionPreflightChecks).to receive(:execute) + + command.execute + end + + it 'passes given options to preflight checks command' do + expect(Geo::PromotionPreflightChecks).to receive(:new).with( + '/opt/gitlab/embedded', options).and_call_original + + command.execute + end + end + end + + describe '#toggle_geo_roles' do + let(:gitlab_cluster_config_path) { File.join(config_path, 'gitlab-cluster.json') } + + before do + stub_const('GitlabCluster::CONFIG_PATH', config_path) + stub_const('GitlabCluster::JSON_FILE', gitlab_cluster_config_path) + + allow(STDIN).to receive(:gets).and_return('y') + + allow(command).to receive(:run_preflight_checks).and_return(true) + allow(command).to receive(:promote_postgresql_to_primary).and_return(true) + allow(command).to receive(:reconfigure).and_return(true) + allow(command).to receive(:promote_to_primary).and_return(true) + allow(command).to receive(:success_message).and_return(true) + end + + context 'when the cluster configuration file does not exist' do + it 'creates the file with the Geo primary role enabled and secondary role disabled' do + command.execute + + expect(File.exist?(gitlab_cluster_config_path)).to eq(true) + expect(read_file_content(gitlab_cluster_config_path)).to eq("primary" => true, "secondary" => false) + end + end + + context 'when the cluster configuration file exists' do + it 'disables the Geo secondary role' do + write_file_content(gitlab_cluster_config_path, primary: false, secondary: true) + + command.execute + + expect(read_file_content(gitlab_cluster_config_path)).to eq("primary" => true, "secondary" => false) + end + end + + def read_file_content(fullpath) + JSON.parse(File.read(fullpath)) + end + + def write_file_content(fullpath, content) + File.open(fullpath, 'w') do |f| + f.write(content.to_json) + f.chmod(0600) + end + end + end + + context 'when preflight checks pass' do + before do + allow(STDIN).to receive(:gets).and_return('y') + + allow_any_instance_of(Geo::PromotionPreflightChecks).to receive( + :execute).and_return(true) + + allow(command).to receive(:toggle_geo_roles).and_return(true) + allow(command).to receive(:promote_postgresql_to_primary).and_return(true) + allow(command).to receive(:reconfigure).and_return(true) + allow(command).to receive(:promote_to_primary).and_return(true) + allow(command).to receive(:success_message).and_return(true) + end + + context 'when running in force mode' do + let(:options) { { force: true } } + + it 'does not ask for final confirmation' do + expect { command.execute }.not_to output( + /WARNING\: Secondary will now be promoted to primary./).to_stdout + end + end + + context 'when not running in force mode' do + let(:options) { { force: false } } + + it 'asks for confirmation' do + expect { command.execute }.to output( + /WARNING\: Secondary will now be promoted to primary./).to_stdout + end + + context 'when final confirmation is given' do + it 'calls all the subcommands' do + expect(command).to receive(:toggle_geo_roles) + expect(command).to receive(:promote_postgresql_to_primary) + expect(command).to receive(:reconfigure) + expect(command).to receive(:promote_to_primary) + expect(command).to receive(:success_message) + + command.execute + end + end + end + end + + context 'when preflight checks fail' do + around do |example| + example.run + rescue SystemExit + end + + before do + allow(STDIN).to receive(:gets).and_return('n') + + allow(command).to receive(:promote_postgresql_to_primary).and_return(true) + allow(command).to receive(:reconfigure).and_return(true) + allow(command).to receive(:promote_to_primary).and_return(true) + + allow_any_instance_of(Geo::PromotionPreflightChecks).to receive( + :execute).and_raise(SystemExit) + end + + context 'when running in force mode' do + let(:options) { { force: true } } + + it 'asks for confirmation' do + expect { command.execute }.to output( + /Are you sure you want to proceed?/ + ).to_stdout + end + + it 'exits with 1 if user denies' do + allow(STDIN).to receive(:gets).and_return('n') + + expect { command.execute }.to raise_error(SystemExit) do |error| + expect(error.status).to eq(1) + end + end + + it 'calls all the subcommands if user affirms' do + allow(STDIN).to receive(:gets).and_return('y') + + is_expected.to receive(:toggle_geo_roles) + is_expected.to receive(:promote_postgresql_to_primary) + is_expected.to receive(:reconfigure) + is_expected.to receive(:promote_to_primary) + is_expected.to receive(:success_message) + + command.execute + end + end + + context 'when not running in force mode' do + let(:options) { { force: false } } + + it 'exits with 1' do + expect { command.execute }.to raise_error(SystemExit) + end + end + end + + context 'when writing to the cluster configuration file fail' do + around do |example| + example.run + rescue SystemExit + end + + before do + allow(STDIN).to receive(:gets).and_return('y') + + allow(command).to receive(:run_preflight_checks).and_return(true) + + allow(GitlabCluster.config).to receive(:save).and_return(false) + end + + it 'exits with 1' do + expect { command.execute }.to raise_error(SystemExit) + end + end + + context 'when writing to the cluster configuration file succeed' do + before do + allow(STDIN).to receive(:gets).and_return('y') + + allow(command).to receive(:promote_postgresql_to_primary).and_return(true) + allow(command).to receive(:reconfigure).and_return(true) + allow(command).to receive(:promote_to_primary).and_return(true) + allow(command).to receive(:success_message).and_return(true) + + allow(GitlabCluster.config).to receive(:save).and_return(true) + end + + it 'calls all the subcommands' do + expect(command).to receive(:promote_postgresql_to_primary) + expect(command).to receive(:reconfigure) + expect(command).to receive(:promote_to_primary) + expect(command).to receive(:success_message) + + command.execute + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/geo/promotion_preflight_checks_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/geo/promotion_preflight_checks_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2dlby9wcm9tb3Rpb25fcHJlZmxpZ2h0X2NoZWNrc19zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/geo/promotion_preflight_checks_spec.rb @@ -0,0 +1,145 @@ +require 'spec_helper' +require 'geo/promotion_preflight_checks' +require 'gitlab_ctl/util' + +RSpec.describe Geo::PromotionPreflightChecks, '#execute' do + let(:confirmation) { 'y' } + let(:options) { { confirm_primary_is_down: true } } + + subject(:command) { described_class.new(nil, options) } + + before do + allow(STDIN).to receive(:gets).and_return(confirmation) + allow(command).to receive(:run_command).with(any_args) + + shell_out_object = double.tap do |shell_out_object| + allow(shell_out_object).to receive(:stdout).and_return('SUCCESS') + allow(shell_out_object).to receive(:error?).and_return(false) + end + + allow(command).to receive(:run_command).with( + 'gitlab-rake gitlab:geo:check_replication_verification_status') + .and_return(shell_out_object) + end + + it 'prints preflight check instructions' do + expect { command.execute }.to output( + /Ensure you have completed the following manual preflight checks/) + .to_stdout + end + + context 'when manual checks are confirmed' do + it 'does not raise an error' do + expect { command.execute }.to_not raise_error + end + + it 'runs `check_replication_verification_status` task' do + is_expected.to receive(:run_command).with( + 'gitlab-rake gitlab:geo:check_replication_verification_status').once + + command.execute + end + + context 'when gitlab:geo:check_replication_verification_status fails' do + around do |example| + example.run + rescue SystemExit + end + + before do + shell_out_object = double.tap do |shell_out_object| + allow(shell_out_object).to receive(:stdout).and_return('error message') + allow(shell_out_object).to receive(:error?).and_return(true) + end + + allow(command).to receive(:run_command).with( + 'gitlab-rake gitlab:geo:check_replication_verification_status') + .once + .and_return(shell_out_object) + end + + it 'prints error message when rake task errors' do + expect { command.execute }.to output( + /error message\nERROR: Replication\/verification is incomplete./).to_stdout + end + + it 'exits with 1 when rake task errors' do + expect { command.execute }.to raise_error(SystemExit) do |error| + expect(error.status).to eq(1) + end + end + end + + context 'when gitlab:geo:check_replication_verification_status passes' do + it 'prints a success message' do + expect { command.execute }.to output( + /All preflight checks have passed. This node can now be promoted./) + .to_stdout + end + end + end + + context 'when manual checks are not confirmed' do + let(:confirmation) { 'n' } + + around do |example| + example.run + rescue SystemExit + end + + it 'print error message' do + expect { command.execute }.to output( + /ERROR: Manual preflight checks were not performed/ + ).to_stdout + end + end + + describe 'option --confirm-primary-is-down' do + before do + allow(command).to receive(:confirm_manual_checks).and_return(true) + end + + context 'when the option is not passed' do + let(:options) { {} } + let(:confirmation) { 'n' } + + around do |example| + example.run + rescue SystemExit + end + + it 'asks user for confirmation' do + expect { command.execute }.to output( + /Is primary down? (N\/y)/) + .to_stdout + end + + it 'prints an error message when user doesn not select y/Y' do + expect { command.execute }.to output( + /ERROR: Primary node must be down./) + .to_stdout + end + end + + context 'when the option is passed' do + it 'does not ask user for confirmation' do + expect { command.execute }.not_to output( + /Is primary down? (N\/y)/) + .to_stdout + end + end + end + + context 'when all checks pass' do + before do + allow(command).to receive(:confirm_manual_checks).and_return(true) + allow(command).to receive(:confirm_primary_is_down).and_return(true) + end + + it 'prints a success message' do + expect { command.execute }.to output( + /All preflight checks have passed. This node can now be promoted./) + .to_stdout + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_process_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_process_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2dlby9yZXBsaWNhdGlvbl9wcm9jZXNzX3NwZWMucmI= --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_process_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' + +$LOAD_PATH << './files/gitlab-ctl-commands-ee/lib' +$LOAD_PATH << './files/gitlab-ctl-commands/lib' + +require 'geo/replication_process' +require 'gitlab_ctl/util' + +RSpec.describe Geo::ReplicationProcess do + let(:error_text) { 'AN ERROR' } + let(:good_status) { double('Command status', error?: false) } + let(:bad_status) { double('Command status', error?: true, stdout: error_text) } + let(:pause_cmd) { %r{gitlab-psql .* -c 'SELECT pg_wal_replay_pause\(\);'} } + let(:resume_cmd) { %r{gitlab-psql .* -c 'SELECT pg_wal_replay_resume\(\);'} } + let(:instance) { double(base_path: '/opt/gitlab/embedded', data_path: '/var/opt/gitlab/postgresql/data') } + let(:db_name) { 'gitlab_db_name' } + let(:options) do + { + db_name: db_name + } + end + + subject { described_class.new(instance, options) } + + describe '#pause' do + it 'logs a message if the rake task throws an error' do + expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:pause/).and_return(bad_status) + expect(GitlabCtl::Util).to receive(:run_command).with(pause_cmd).and_return(good_status) + + expect do + subject.pause + end.to output(/#{error_text}/).to_stdout + end + + it 'raises an exception if unable to pause replication' do + expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:pause/).and_return(good_status) + expect(GitlabCtl::Util).to receive(:run_command).with(pause_cmd).and_return(bad_status) + + expect do + subject.pause + end.to raise_error(/Unable to pause postgres replication/) + end + + it 'provides the requested database from the options' do + expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:pause/).and_return(good_status) + expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-psql -d #{db_name}/).and_return(good_status) + + subject.pause + end + end + + describe '#resume' do + it 'raises an exception if unable to resume pg replication' do + expect(GitlabCtl::Util).to receive(:run_command).with(resume_cmd).and_return(bad_status) + + expect do + subject.resume + end.to raise_error(/Unable to resume postgres replication/) + end + + it 'raises an error if rake task to resume fails' do + expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:resume/).and_return(bad_status) + expect(GitlabCtl::Util).to receive(:run_command).with(resume_cmd).and_return(good_status) + + expect do + subject.resume + end.to raise_error(/Unable to resume replication from primary/) + end + + it 'provides the requested database from the options' do + expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:resume/).and_return(good_status) + expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-psql -d #{db_name}/).and_return(good_status) + + subject.resume + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2dlby9yZXBsaWNhdGlvbl9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_spec.rb @@ -0,0 +1,143 @@ +require 'spec_helper' + +$LOAD_PATH << './files/gitlab-ctl-commands-ee/lib' +$LOAD_PATH << './files/gitlab-ctl-commands/lib' + +require 'geo/replication' +require 'gitlab_ctl/util' + +RSpec.describe Geo::Replication, '#execute' do + let(:command) { spy('command spy', error?: false) } + let(:instance) { double(base_path: '/opt/gitlab/embedded', data_path: '/var/opt/gitlab') } + let(:file) { spy('file spy') } + let(:options) do + { + now: true, + host: 'localhost', + port: 9999, + user: 'my-user', + sslmode: 'disable', + sslcompression: 1, + recovery_target_timeline: 'latest', + db_name: 'gitlab_db_name' + } + end + + subject { described_class.new(instance, options) } + + before do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'postgresql' => { 'dir' => '/var/opt/gitlab/postgresql' } }) + + allow(instance).to receive(:service_enabled?).and_return(true) + allow(STDOUT).to receive(:puts) + allow(subject).to receive(:print) + allow(File).to receive(:open).and_yield(file) + allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/standby.signal').and_return(true) + allow(File).to receive(:exist?).with(anything).and_call_original + allow(File).to receive(:write).with('/var/opt/gitlab/postgresql/data/standby.signal', "") + + allow(GitlabCtl::Util).to receive(:run_command).and_return(command) + allow(subject).to receive(:postgresql_user).and_return('gitlab-psql') + allow(subject).to receive(:postgresql_group).and_return('gitlab-psql') + allow(subject).to receive(:postgresql_version).and_return(12) + end + + it 'replicates geo database' do + expect(subject).to receive(:ask_pass).and_return('password') + expect(GitlabCtl::Util).to receive(:run_command) + .with(%r{/embedded/bin/pg_basebackup}, anything) + + subject.execute + end + + it 'uses the db_name option' do + expect(subject).to receive(:ask_pass).and_return('password') + expect(GitlabCtl::Util).to receive(:run_command) + .with(%r{gitlab_db_name}, anything) + + subject.execute + end + + it 'uses the supplied postgres directory' do + expected_dir = '/non/default/location' + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'postgresql' => { 'dir' => expected_dir } }) + + expect(described_class.new(instance, options).postgresql_dir_path).to eq(expected_dir) + end + + it 'writes recovery settings to postgresql.conf and creates a standby file' do + allow(File).to receive(:write) + allow(STDIN).to receive(:gets).and_return("pass\n") + allow(subject).to receive(:ask_pass).and_return('password') + + expect(subject).to receive(:write_recovery_settings!) + expect(subject).to receive(:create_standby_file!) + + subject.execute + end + + context 'when there is TTY available' do + before do + allow(STDIN).to receive(:tty?).and_return(true) + end + + it 'asks for database password in an interactive mode' do + expect(STDIN).to receive(:getpass).and_return('password') + + subject.execute + end + end + + context 'when there is no TTY available' do + before do + allow(STDIN).to receive(:tty?).and_return(false) + end + + it 'asks for a database password in a non-interactive mode' do + expect(STDIN).to receive(:gets).and_return('pass') + + subject.execute + + expect(file).to have_received(:write).with(/recovery_target_timeline = 'latest'/) + end + + it 'strips the password' do + allow(STDIN).to receive(:gets).and_return("pass\n") + + subject.execute + + expect(file).to have_received(:write).with("localhost:9999:*:my-user:pass\n") + expect(file).to have_received(:write).with(/password=pass sslmode=disable sslcompression=1'\n/) + end + end + + context 'with a custom port' do + let(:options) { { now: true, skip_backup: true, host: 'localhost', port: 9999, user: 'my-user', slot_name: 'foo', db_name: 'gitlab_db_name' } } + let(:cmd) { %q(PGPASSFILE=/var/opt/gitlab/postgresql/.pgpass /opt/gitlab/embedded/bin/gitlab-psql -h localhost -p 9999 -U my-user -d gitlab_db_name -t -c "SELECT slot_name FROM pg_create_physical_replication_slot('foo');") } + let(:status) { double(error?: false, stdout: '') } + + it 'executes a gitlab-psql call to check replication slots' do + expect(subject).to receive(:check_gitlab_active?).and_return(true) + expect(subject).to receive(:ask_pass).and_return('mypassword') + + allow(GitlabCtl::Util).to receive(:run_command).and_return(status) + expect(GitlabCtl::Util).to receive(:run_command).with(cmd, anything).and_return(status) + + subject.execute + end + end + + context 'when user has to provide a confirmation text' do + let(:options) { { now: false, host: 'localhost', port: 9999, user: 'my-user', slot_name: 'foo' } } + + it 'asks for confirmation string' do + allow(subject).to receive(:ask_pass).and_return('mypass') + expect(STDIN).to receive(:gets).and_return('replicate') + expect(GitlabCtl::Util).to receive(:run_command) + .with("PGPASSFILE=/var/opt/gitlab/postgresql/.pgpass /opt/gitlab/embedded/embedded/bin/pg_basebackup -h localhost -p 9999 -D /var/opt/gitlab/postgresql/data -U my-user -v -P -X stream -S foo", + anything) + + subject.execute + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_toggle_command_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_toggle_command_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL2dlby9yZXBsaWNhdGlvbl90b2dnbGVfY29tbWFuZF9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/geo/replication_toggle_command_spec.rb @@ -0,0 +1,90 @@ +require 'spec_helper' + +$LOAD_PATH << './files/gitlab-ctl-commands-ee/lib' +$LOAD_PATH << './files/gitlab-ctl-commands/lib' + +require 'geo/replication_toggle_command' +require 'geo/promote_to_primary_node' +require 'gitlab_ctl/util' + +RSpec.describe Geo::ReplicationToggleCommand do + let(:status) { double('Command status', error?: false) } + let(:arguments) { [] } + let(:ctl_instance) { double('gitlab-ctl instance', base_path: '', data_path: 'data_path') } + + before do + allow_any_instance_of(Geo::ReplicationToggleCommand).to receive(:current_lsn).and_return('16/B374D848') + end + + describe 'pause' do + subject { described_class.new(ctl_instance, 'pause', arguments) } + + it 'calls pause' do + expect_any_instance_of(Geo::ReplicationProcess).to receive(:pause) + expect(File).to receive(:write).with('data_path/postgresql/data/geo-pitr-file', '16/B374D848') + + expect { subject.execute! }.to output(/Create Geo point-in-time recovery file/).to_stdout + end + + it 'rescues and exits if postgres has an error' do + expect_any_instance_of(Geo::ReplicationProcess).to receive(:pause).and_raise(Geo::PsqlError, "Oh nose!") + + expect do + expect { subject.execute! }.to raise_error(SystemExit) + end.to output(/Postgres encountered an error: Oh nose!/).to_stdout + end + + context 'database specified' do + let(:arguments) { %w(--db_name=database_i_want) } + + it 'uses the specified database' do + expect(Geo::ReplicationProcess).to receive(:new).with(any_args, { db_name: 'database_i_want' }).and_call_original + expect_any_instance_of(Geo::ReplicationProcess).to receive(:pause) + expect(File).to receive(:write).with('data_path/postgresql/data/geo-pitr-file', '16/B374D848') + + expect { subject.execute! }.to output(/Create Geo point-in-time recovery file/).to_stdout + end + end + end + + describe 'resume' do + subject { described_class.new(ctl_instance, 'resume', arguments) } + + before do + allow(File).to receive(:delete).with('data_path/postgresql/data/geo-pitr-file').and_return(1) + end + + it 'calls resume' do + expect_any_instance_of(Geo::ReplicationProcess).to receive(:resume) + + expect { subject.execute! }.to output(/Remove Geo point-in-time recovery file/).to_stdout + end + + it 'rescues and exits if postgres has an error' do + expect_any_instance_of(Geo::ReplicationProcess).to receive(:resume).and_raise(Geo::PsqlError, "Oh nose!") + + expect do + expect { subject.execute! }.to raise_error(SystemExit) + end.to output(/Postgres encountered an error: Oh nose!/).to_stdout + end + + it 'rescues and exits if rake has an error' do + expect_any_instance_of(Geo::ReplicationProcess).to receive(:resume).and_raise(Geo::RakeError, "Oh nose!") + + expect do + expect { subject.execute! }.to raise_error(SystemExit) + end.to output(/Rake encountered an error: Oh nose!/).to_stdout + end + + context 'database specified' do + let(:arguments) { %w(--db_name=database_i_want) } + + it 'uses the specified database' do + expect(Geo::ReplicationProcess).to receive(:new).with(any_args, { db_name: 'database_i_want' }).and_call_original + expect_any_instance_of(Geo::ReplicationProcess).to receive(:resume) + + expect { subject.execute! }.to output(/Remove Geo point-in-time recovery file/).to_stdout + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/patroni_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/patroni_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL3BhdHJvbmlfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/patroni_spec.rb @@ -0,0 +1,225 @@ +require 'spec_helper' +require 'omnibus-ctl' +require 'optparse' + +require_relative('../../../../files/gitlab-ctl-commands/lib/gitlab_ctl') +require_relative('../../../../files/gitlab-ctl-commands-ee/lib/patroni') + +RSpec.describe 'Patroni' do + core_commands = %w(bootstrap check-leader check-replica check-standby-leader reinitialize-replica) + additional_commands = %w(members pause resume failover switchover restart reload) + all_commands = core_commands + additional_commands + + command_lines = { + 'bootstrap' => %w(--srcdir=SRCDIR --scope=SCOPE --datadir=DATADIR), + 'pause' => %w(-w), + 'resume' => %w(--wait), + 'failover' => %w(--master MASTER --candidate CANDIDATE), + 'switchover' => %w(--master MASTER --candidate CANDIDATE --scheduled SCHEDULED), + 'reinitialize-replica' => %w(--wait --member MEMBER), + 'restart' => [], + 'reload' => [] + } + + command_options = { + 'bootstrap' => { srcdir: 'SRCDIR', scope: 'SCOPE', datadir: 'DATADIR' }, + 'pause' => { wait: true }, + 'resume' => { wait: true }, + 'failover' => { master: 'MASTER', candidate: 'CANDIDATE' }, + 'switchover' => { master: 'MASTER', candidate: 'CANDIDATE', scheduled: 'SCHEDULED' }, + 'reinitialize-replica' => { wait: true, member: 'MEMBER' }, + 'restart' => {}, + 'reload' => {} + + } + + patronictl_command = { + 'members' => 'list', + 'pause' => 'pause -w', + 'resume' => 'resume -w', + 'failover' => 'failover --force --master MASTER --candidate CANDIDATE', + 'switchover' => 'switchover --force --master MASTER --candidate CANDIDATE --scheduled SCHEDULED', + 'restart' => 'restart --force fake-scope fake-node', + 'reload' => 'reload --force fake-scope fake-node' + } + + describe '.parse_options' do + before do + allow(Patroni::Utils).to receive(:warn_and_exit).and_call_original + allow(Kernel).to receive(:exit) { |code| raise "Kernel.exit(#{code})" } + allow(Kernel).to receive(:warn) + end + + it 'should throw error when global options are invalid' do + expect { Patroni.parse_options(%w(patroni --foo)) }.to raise_error(OptionParser::ParseError) + end + + it 'should throw error when sub-command is not specified' do + expect { Patroni.parse_options(%w(patroni -v)) }.to raise_error(OptionParser::ParseError) + end + + it 'should throw error when sub-command is not defined' do + expect { Patroni.parse_options(%w(patroni -v foo)) }.to raise_error(OptionParser::ParseError) + end + + it 'should recognize global options' do + expect(Patroni.parse_options(%w(patroni -v -q members))).to include(quiet: true, verbose: true) + end + + context 'when sub-command is passed' do + all_commands.each do |cmd| + it "should parse #{cmd} options" do + cmd_line = command_lines[cmd] || [] + cmd_opts = command_options[cmd] || {} + cmd_opts[:command] = cmd + + expect(Patroni.parse_options(%W(patroni #{cmd}) + cmd_line)).to include(cmd_opts) + end + end + end + + context 'when help option is passed' do + it 'should show help message and exit for global help option' do + expect { Patroni.parse_options(%w(patroni -h)) }.to raise_error('Kernel.exit(0)') + expect(Patroni::Utils).to have_received(:warn_and_exit).with(/Usage help/) + end + + all_commands.each do |cmd| + it "should show help message and exit for #{cmd} help option" do + expect { Patroni.parse_options(%W(patroni #{cmd} -h)) }.to raise_error('Kernel.exit(0)') + expect(Patroni::Utils).to have_received(:warn_and_exit).with(instance_of(OptionParser)) + end + end + end + end + + describe 'additional commands' do + before do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'patroni' => { 'config_dir' => '/fake' } }) + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({ 'patroni' => { 'scope' => 'fake-scope', 'name' => 'fake-node' } }) + allow(GitlabCtl::Util).to receive(:run_command) + end + + additional_commands.each do |cmd| + it "should run the relevant patronictl command for #{cmd}" do + Patroni.send(cmd.to_sym, command_options[cmd] || {}) + + expect(GitlabCtl::Util).to have_received(:run_command).with( + "/opt/gitlab/embedded/bin/patronictl -c /fake/patroni.yaml #{patronictl_command[cmd]}", + user: 'root', live: false) + end + end + end + + describe 'command output on non-Patroni node' do + before do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'patroni' => nil }) + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({ 'patroni' => nil }) + end + + additional_commands.each do |cmd| + it "should raise errors when running command #{cmd}" do + expect { Patroni.send(cmd.to_sym, command_options[cmd]) }.to( + raise_error(RuntimeError, /no Patroni configuration/) + ) + end + end + end + + describe '.init_db' do + before do + allow(GitlabCtl::Util).to receive(:run_command) + end + + it 'should call initdb command with the specified options' do + Patroni.init_db(command_options['bootstrap']) + + expect(GitlabCtl::Util).to have_received(:run_command).with('/opt/gitlab/embedded/bin/initdb -D DATADIR -E UTF8') + end + end + + describe '.copy_config' do + before do + allow(FileUtils).to receive(:cp_r) + end + + it 'should call initdb command with the specified options' do + Patroni.copy_config(command_options['bootstrap']) + + expect(FileUtils).to have_received(:cp_r).with('SRCDIR/.', 'DATADIR') + end + end + + describe '.leader?, .replica?, and .standyLeader?' do + before do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'patroni' => { 'api_address' => 'http://localhost:8009' } }) + allow_any_instance_of(Patroni::Client).to receive(:get).with('/leader').and_yield(Struct.new(:code).new(leader_status)) + allow_any_instance_of(Patroni::Client).to receive(:get).with('/replica').and_yield(Struct.new(:code).new(replica_status)) + allow_any_instance_of(Patroni::Client).to receive(:get).with('/standby-leader').and_yield(Struct.new(:code).new(standby_leader_status)) + end + + context 'when node is leader' do + let(:leader_status) { '200' } + let(:replica_status) { '503' } + let(:standby_leader_status) { '503' } + + it 'should identify node role' do + expect(Patroni.leader?({})).to be(true) + expect(Patroni.replica?({})).to be(false) + expect(Patroni.standby_leader?({})).to be(false) + end + end + + context 'when node is replica' do + let(:leader_status) { '503' } + let(:replica_status) { '200' } + let(:standby_leader_status) { '503' } + + it 'should identify node role' do + expect(Patroni.leader?({})).to be(false) + expect(Patroni.replica?({})).to be(true) + expect(Patroni.standby_leader?({})).to be(false) + end + end + + context 'when node is standby leader' do + let(:leader_status) { '503' } + let(:replica_status) { '503' } + let(:standby_leader_status) { '200' } + + it 'should identify node role' do + expect(Patroni.leader?({})).to be(false) + expect(Patroni.replica?({})).to be(false) + expect(Patroni.standby_leader?({})).to be(true) + end + end + end + + describe '.reinitialize_replica' do + before do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'patroni' => { 'config_dir' => '/fake' } }) + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({ 'patroni' => { 'scope' => 'fake-scope', 'name' => 'fake-node' } }) + allow(GitlabCtl::Util).to receive(:run_command) + end + + context 'when member option is set' do + it 'calls reinit command with the specified options to reinitialize the cluster member' do + Patroni.reinitialize_replica(command_options['reinitialize-replica']) + + expect(GitlabCtl::Util).to have_received(:run_command).with( + '/opt/gitlab/embedded/bin/patronictl -c /fake/patroni.yaml reinit --force --wait fake-scope MEMBER', + user: 'root', live: true) + end + end + + context 'when member option is not set' do + it 'calls reinit command with the specified options to reinitialize the current cluster member' do + Patroni.reinitialize_replica(command_options['reinitialize-replica'].slice(:wait)) + + expect(GitlabCtl::Util).to have_received(:run_command).with( + '/opt/gitlab/embedded/bin/patronictl -c /fake/patroni.yaml reinit --force --wait fake-scope fake-node', + user: 'root', live: true) + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/pgbouncer_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/pgbouncer_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL3BnYm91bmNlcl9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/pgbouncer_spec.rb @@ -0,0 +1,120 @@ +require 'spec_helper' + +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands-ee/lib') + +require 'pgbouncer' + +RSpec.describe Pgbouncer::Databases do + let(:fake_ohai) do + { + 'gitlab' => { + 'gitlab-rails' => { + 'db_database' => 'fake_database' + }, + }, + 'pgbouncer' => { + 'databases_ini' => '/fakedata/pgbouncer/databases.ini', + 'databases_json' => '/fakedata/pgbouncer/databases.json' + } + } + end + + let(:fake_databases_json) do + { + fakedb: { + host: 'fakehost', + user: 'fakeuser', + port: 9999, + password: 'dslgkjdfgklfsd' + } + }.to_json.to_s + end + + before do + allow(GitlabCtl::Util).to receive(:get_fqdn).and_return('fakehost') + allow(Dir).to receive(:exist?).and_call_original + allow(Dir).to receive(:exist?).with('/fakedata/pgbouncer').and_return(true) + allow(File).to receive(:exist?).with('/fakedata/pgbouncer/databases.json').and_return(true) + allow(File).to receive(:read).and_call_original + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return(fake_ohai) + allow(File).to receive(:read).with('/fakedata/pgbouncer/databases.json').and_return(fake_databases_json) + @obj = Pgbouncer::Databases.new({}, '/fakeinstall', '/fakedata') + end + + context 'default behavior' do + it 'allows creating a new object' do + expect(@obj.class).to be(Pgbouncer::Databases) + end + + it 'creates the database object' do + results = { + "fakedb" => "host=fakehost port=9999 auth_user=fakeuser" + } + expect(@obj.databases).to eq(results) + end + + it 'renders the template' do + expect(@obj.render).to eq( + "[databases]\n\nfakedb = host=fakehost port=9999 auth_user=fakeuser\n\n" + ) + end + end + + context 'with options' do + before do + options = { + 'databases_ini' => '/another/databases.ini', + 'databases_json' => '/another/databases.json', + 'port' => 8888, + 'user' => 'fakeuser' + } + allow(File).to receive(:exist?).with('/another/databases.json').and_return(true) + allow(File).to receive(:read).with('/another/databases.json').and_return(fake_databases_json) + @obj = Pgbouncer::Databases.new(options, '/fakeinstall', '/fakedata') + end + + it 'sets the custom options' do + expect(@obj.ini_file).to eq('/another/databases.ini') + expect(@obj.json_file).to eq('/another/databases.json') + end + + it 'renders the template' do + expect(@obj.render).to eq( + "[databases]\n\nfakedb = host=fakehost port=8888 auth_user=fakeuser\n\n" + ) + end + end + + context 'with empty databases.json' do + before do + allow(File).to receive(:read).with('/fakedata/pgbouncer/databases.json').and_return({}.to_s) + @obj = Pgbouncer::Databases.new({}, '/fakeinstall', '/fakedata') + end + + it 'should generate an databases.ini with sane defaults' do + expect(@obj.render).to eq( + "[databases]\n\nfake_database = \n\n" + ) + end + end + + context 'with pgbouncer listening' do + before do + allow(@obj).to receive(:show_databases).and_return("nyan") + end + + it 'should be running' do + expect(@obj.running?).to be true + end + end + + context 'with pgbouncer not listening' do + before do + allow(@obj).to receive(:show_databases).and_raise(GitlabCtl::Errors::ExecutionError.new("nya", "nya", "neko")) + end + + it 'should not be running' do + expect(@obj.running?).to be false + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/lib/postgresql/ee_spec.rb b/spec/chef/gitlab-ctl-commands-ee/lib/postgresql/ee_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvbGliL3Bvc3RncmVzcWwvZWVfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/lib/postgresql/ee_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands-ee/lib') + +require 'gitlab_ctl/util' +require 'postgresql/ee' + +RSpec.describe GitlabCtl::PostgreSQL::EE do + describe "#get_primary" do + context 'when Consul disabled' do + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes) + .and_return({}) + end + + it 'should raise an error' do + expect { described_class.get_primary } .to raise_error(/Consul agent is not enabled/) + end + end + + context 'when PostgreSQL service name is not defined' do + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes) + .and_return( + { + 'consul' => { + 'enable' => true, + } + } + ) + end + + it 'should raise an error' do + expect { described_class.get_primary } .to raise_error(/PostgreSQL service name is not defined/) + end + end + + context 'when required settings are available' do + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes) + .and_return( + { + 'consul' => { + 'enable' => true, + }, + 'patroni' => { + 'scope' => 'fake' + } + } + ) + allow_any_instance_of(Resolv::DNS).to receive(:getresources) + .with('master.fake.service.consul', Resolv::DNS::Resource::IN::SRV) + .and_return([Struct.new(:target, :port).new('fake.address', 6432)]) + allow_any_instance_of(Resolv::DNS).to receive(:getaddress) + .with('fake.address') + .and_return('1.2.3.4') + end + + it 'should get the list of PostgreSQL endpoints' do + expect(described_class.get_primary).to eq ['1.2.3.4:6432'] + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands-ee/promote_db_spec.rb b/spec/chef/gitlab-ctl-commands-ee/promote_db_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvcHJvbW90ZV9kYl9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/promote_db_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'chef_helper' +require 'geo/promote_db' + +RSpec.describe 'gitlab-ctl promote-db' do + let(:klass) { Geo::PromoteDb } + let(:command_name) { 'promote-db' } + let(:command_script) { 'promote_db' } + + include_context 'ctl' + + it_behaves_like 'gitlab geo promotion commands', 'promote-db' + + # rubocop:disable Style/MutableConstant, Lint/ConstantDefinitionInBlock + it 'prints the deprecation message' do + # ARGV contains the commands that were passed to rspec, which are + # invalid for the omnibus-ctl commands + oldargv = ARGV + ARGV = [] + + expect_any_instance_of(klass).to receive(:execute) + + expect { ctl.send(command_script) }.to output( + /WARNING: As of GitLab 14.5, this command is deprecated/).to_stdout + + ARGV = oldargv + end + # rubocop:enable Style/MutableConstant, Lint/ConstantDefinitionInBlock +end diff --git a/spec/chef/gitlab-ctl-commands-ee/promote_to_primary_node_spec.rb b/spec/chef/gitlab-ctl-commands-ee/promote_to_primary_node_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvcHJvbW90ZV90b19wcmltYXJ5X25vZGVfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/promote_to_primary_node_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'geo/promote_to_primary_node' + +RSpec.describe 'gitlab-ctl promote-to-primary-node' do + let(:klass) { Geo::PromoteToPrimaryNode } + let(:command_name) { 'promote-to-primary-node' } + let(:command_script) { 'promote_to_primary_node' } + + include_context 'ctl' + + it_behaves_like 'gitlab geo promotion commands', 'promote-to-primary-node' + + it_behaves_like 'geo promotion command accepts option', + '--confirm-primary-is-down', + { confirm_primary_is_down: true } + + it_behaves_like 'geo promotion command accepts option', + '--skip-preflight-checks', + { skip_preflight_checks: true } + + it_behaves_like 'geo promotion command accepts option', + '--force', + { force: true } + + # rubocop:disable Style/MutableConstant, Lint/ConstantDefinitionInBlock + it 'prints the deprecation message' do + # ARGV contains the commands that were passed to rspec, which are + # invalid for the omnibus-ctl commands + oldargv = ARGV + ARGV = [] # rubocop:disable Style/MutableConstant + + expect_any_instance_of(klass).to receive(:execute) + + expect { ctl.send(command_script) }.to output( + /WARNING: As of GitLab 14.5, this command is deprecated/).to_stdout + + ARGV = oldargv + end + # rubocop:enable Style/MutableConstant, Lint/ConstantDefinitionInBlock +end diff --git a/spec/chef/gitlab-ctl-commands-ee/promotion_preflight_checks_spec.rb b/spec/chef/gitlab-ctl-commands-ee/promotion_preflight_checks_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvcHJvbW90aW9uX3ByZWZsaWdodF9jaGVja3Nfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/promotion_preflight_checks_spec.rb @@ -0,0 +1,19 @@ +require 'chef_helper' +require 'geo/promotion_preflight_checks' + +RSpec.shared_context 'promotion-preflight-checks' do + let(:command_name) { 'promotion-preflight-checks' } + let(:klass) { Geo::PromotionPreflightChecks } + let(:command_script) { 'promotion_preflight_checks' } +end + +RSpec.describe 'gitlab-ctl promotion-preflight-checks' do + include_context 'promotion-preflight-checks' + include_context 'ctl' + + it_behaves_like 'gitlab geo promotion commands' + + it_behaves_like 'geo promotion command accepts option', + '--confirm-primary-is-down', + { confirm_primary_is_down: true } +end diff --git a/spec/chef/gitlab-ctl-commands-ee/replicate_geo_database_spec.rb b/spec/chef/gitlab-ctl-commands-ee/replicate_geo_database_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMtZWUvcmVwbGljYXRlX2dlb19kYXRhYmFzZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands-ee/replicate_geo_database_spec.rb @@ -0,0 +1,143 @@ +require 'spec_helper' +require 'omnibus-ctl' + +RSpec.describe 'gitlab-ctl replicate-geo-database' do + subject { Omnibus::Ctl.new('testing-ctl') } + + let(:required_arguments) do + %w(--host=gitlab-primary.geo + --slot-name=gitlab_primary_geo) + end + + before do + allow_any_instance_of(Omnibus::Ctl).to receive(:require).and_call_original + allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( + '/opt/testing-ctl/embedded/service/omnibus-ctl-ee/lib/geo/replication' + ) do + require_relative('../../../files/gitlab-ctl-commands-ee/lib/geo/replication') + end + + subject.load_file('files/gitlab-ctl-commands-ee/replicate_geo_database.rb') + end + + it 'appends a geo replication command' do + expect(subject.get_all_commands_hash).to include('replicate-geo-database') + end + + describe "#replicate_geo_database" do + it 'executes the geo replication command' do + stub_command_arguments(required_arguments) + + expect(Geo::Replication).to receive(:new).and_call_original + .with(subject, hash_including(host: 'gitlab-primary.geo', + slot_name: 'gitlab_primary_geo')) + + expect_any_instance_of(Geo::Replication).to receive(:execute) + + replicate_geo_database + end + + it 'applies defaults to optional arguments' do + stub_command_arguments(required_arguments) + + expect(Geo::Replication).to receive(:new).and_call_original + .with(subject, hash_including(host: 'gitlab-primary.geo', + slot_name: 'gitlab_primary_geo', + user: 'gitlab_replicator', + port: 5432, + password: nil, + now: false, + force: false, + skip_backup: false, + skip_replication_slot: false, + backup_timeout: 1800, + sslmode: 'verify-full', + sslcompression: 0, + recovery_target_timeline: 'latest', + db_name: 'gitlabhq_production')) + + expect_any_instance_of(Geo::Replication).to receive(:execute) + + replicate_geo_database + end + + it 'requires the host argument' do + stub_command_arguments(%w(--slot-name=gitlab_primary_geo)) + + expect(Geo::Replication).not_to receive(:new) + + # Important to catch this SystemExit or else RSpec exits + expect { subject.replicate_geo_database }.to raise_error(SystemExit).and( + output(/missing argument: host/).to_stdout + ) + end + + it 'requires the slot-name argument' do + stub_command_arguments(%w(--host=gitlab-primary.geo)) + + expect(Geo::Replication).not_to receive(:new) + + # Important to catch this SystemExit or else RSpec exits + expect { subject.replicate_geo_database }.to raise_error(SystemExit).and( + output(/missing argument: --slot-name/).to_stdout + ) + end + + context 'with SSL compression enabled' do + it 'enables SSL compression' do + stub_command_arguments( + required_arguments + + %w(--sslmode=disable + --sslcompression=1 + --no-wait)) + + expect(Geo::Replication).to receive(:new).and_call_original + .with(subject, hash_including(host: 'gitlab-primary.geo', + slot_name: 'gitlab_primary_geo', + sslmode: 'disable', + sslcompression: 1, + now: true)) + + expect_any_instance_of(Geo::Replication).to receive(:execute) + + replicate_geo_database + end + end + + context 'with db_name specified' do + it 'sets the db_name option' do + stub_command_arguments( + required_arguments + + %w(--db-name=custom_db_name)) + + expect(Geo::Replication).to receive(:new).and_call_original + .with(subject, hash_including(host: 'gitlab-primary.geo', + slot_name: 'gitlab_primary_geo', + db_name: 'custom_db_name')) + + expect_any_instance_of(Geo::Replication).to receive(:execute) + + replicate_geo_database + end + end + end + + def stub_command_arguments(arguments) + expect_any_instance_of(Omnibus::Ctl::GeoReplicationCommand) + .to receive(:arguments).and_return(arguments) + end + + def replicate_geo_database + # GeoReplicationCommand is designed to `exit 1` when there is a problem + # during option parsing. Tests that trigger this exit will also silently + # exit RSpec immediately, *and* they will *not* fail. + # + # So if a test expects a successful call, we *must* catch at least + # SystemExit for the test to be valid. And since other errors are always + # possible, and should also fail the test, we don't need to specify + # SystemExit here. + # + # In short, this is necessary for a failable test. + expect { subject.replicate_geo_database }.not_to raise_error + end +end diff --git a/spec/chef/gitlab-ctl-commands/gitlab_ctl_spec.rb b/spec/chef/gitlab-ctl-commands/gitlab_ctl_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMvZ2l0bGFiX2N0bF9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands/gitlab_ctl_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' +require 'omnibus-ctl' + +RSpec.describe 'gitlab-ctl' do + before do + allow_any_instance_of(Omnibus::Ctl).to receive(:require).and_call_original + allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( + '/opt/testing-ctl/embedded/service/omnibus-ctl/lib/gitlab_ctl' + ) do + require_relative('../../../files/gitlab-ctl-commands/lib/gitlab_ctl') + end + end + + let(:ctl_dir) { "#{File.dirname(__FILE__)}/../../../files/gitlab-ctl-commands" } + + it 'should be able to load all files' do + # ARGV contains the commands that were passed to rspec, which are + # invalid for the omnibus-ctl commands + oldargv = ARGV + ARGV = [] # rubocop:disable Style/MutableConstant + expect do + ctl = Omnibus::Ctl.new('testing-ctl') + ctl.load_files(@ctl_dir) + end.not_to raise_error + ARGV = oldargv + end +end diff --git a/spec/chef/gitlab-ctl-commands/lib/gitlab_ctl_backup_spec.rb b/spec/chef/gitlab-ctl-commands/lib/gitlab_ctl_backup_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMvbGliL2dpdGxhYl9jdGxfYmFja3VwX3NwZWMucmI= --- /dev/null +++ b/spec/chef/gitlab-ctl-commands/lib/gitlab_ctl_backup_spec.rb @@ -0,0 +1,318 @@ +require 'chef_helper' + +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') + +require 'gitlab_ctl' + +RSpec.describe GitlabCtl::Backup do + let(:backup_dir_path) { '/etc/gitlab/config_backup' } + + # Valid backup files match the regular expression + let(:future_valid_backup_files) do + ['gitlab_config_2302886428_2042_12_22.tar'] + end + + let(:past_valid_backup_files) do + [ + 'gitlab_config_1603388428_2020_10_22.tar', + 'gitlab_config_1606070428_2020_11_22.tar', + 'gitlab_config_1608662428_2020_12_22.tar', + ] + end + + let(:valid_backup_files) do + future_valid_backup_files + past_valid_backup_files + end + + let(:invalid_backup_files) do + [ + 'lab_config_1600793789_2020_09_22.tar', + 'gitlab_config_manual.tar', + 'my_cool_backup.tar' + ] + end + + let(:all_backup_files) do + valid_backup_files + invalid_backup_files + end + + let(:warning_message) { "WARNING: In GitLab 14.0 we will begin removing all configuration backups older than" } + + before do + allow(GitlabCtl::Backup).to receive(:print_warning?).and_return(false) + allow(File).to receive(:exist?).and_return(true) + allow(FileUtils).to receive(:chmod) + allow(FileUtils).to receive(:chown) + allow(FileUtils).to receive(:mkdir) + allow_any_instance_of(Kernel).to receive(:system).and_return(true) + allow_any_instance_of(Kernel).to receive(:exit!) + allow(Dir).to receive(:chdir).and_yield + allow(FileUtils).to receive(:rm) + allow(Time).to receive(:now).and_return(Time.utc(2021)) + # Don't let messages output during test + allow(STDOUT).to receive(:write) + end + + context 'with default settings' do + let(:options) { { delete_old_backups: nil } } + + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({}) + end + + it 'should default to deleting old backups' do + backup = GitlabCtl::Backup.new + expect(backup.wants_pruned).to eq(true) + end + + context 'when the backup path is readable by non-root' do + before do + allow_any_instance_of(GitlabCtl::Backup).to receive(:secure?).and_return(false) + end + + it 'should warn the administrator' do + expect { GitlabCtl::Backup.perform }.to output(/WARNING: #{backup_dir_path} may be read by non-root users/).to_stderr + end + end + + context 'when the backup path is readable by only root' do + let(:archive_name) { 'gitlab_config_8675309_1981_11_16.tar' } + + before do + allow_any_instance_of(GitlabCtl::Backup).to receive(:secure?).and_return(true) + allow_any_instance_of(GitlabCtl::Backup).to receive(:archive_name).and_return(archive_name) + end + + it 'should use proper tar command' do + expect_any_instance_of(Kernel).to receive(:system).with( + *%W(tar --absolute-names --dereference --verbose --create --file /etc/gitlab/config_backup/#{archive_name} + --exclude /etc/gitlab/config_backup -- /etc/gitlab) + ) + GitlabCtl::Backup.perform + end + + it 'should set proper file mode on archive file' do + expect(FileUtils).to receive(:chmod).with(0600, %r{#{backup_dir_path}/#{archive_name}}) + GitlabCtl::Backup.perform + end + + it 'should notify about archive creation starting' do + expect { GitlabCtl::Backup.perform }.to output( + %r{Creating configuration backup archive: #{archive_name}} + ).to_stdout + end + + it 'should put notify about archive creation completion' do + expect { GitlabCtl::Backup.perform }.to output( + %r{Configuration backup archive complete: #{backup_dir_path}/#{archive_name}} + ).to_stdout + end + + it 'should be able to accept a backup directory path argument' do + custom_dir = "/TanukisOfUnusualSize" + options = { backup_path: custom_dir } + expect { GitlabCtl::Backup.perform(options) }.to output( + %r{Configuration backup archive complete: #{custom_dir}/#{archive_name}} + ).to_stdout + end + + context 'when etc backup path does not exist' do + before do + allow(File).to receive(:exist?).with(backup_dir_path).and_return(false) + end + + it 'should log proper message' do + expect { GitlabCtl::Backup.perform }.to output( + %r{Could not find '#{backup_dir_path}' directory\. Creating\.}).to_stdout + end + + it 'should create directory' do + expect(FileUtils).to receive(:mkdir).with(backup_dir_path, mode: 0700) + GitlabCtl::Backup.perform + end + + it 'should set proper owner and group' do + expect(FileUtils).to receive(:chown).with('root', 'root', backup_dir_path) + GitlabCtl::Backup.perform + end + + context 'when /etc/gitlab is NFS share' do + before do + allow(STDERR).to receive(:write) + allow(FileUtils).to receive(:chown).with('root', 'root', backup_dir_path).and_raise(Errno::EPERM) + end + + it 'should put proper output to STDERR' do + expect { GitlabCtl::Backup.perform }.to output( + /Warning: Could not change owner of #{backup_dir_path} to 'root:root'. As a result your backups may be accessible to some non-root users./).to_stderr + end + end + end + end + + context 'when etc path does not exist' do + let(:etc_path) { '/etc/gitlab' } + + before do + allow(File).to receive(:exist?).with(etc_path).and_return(false) + end + + it "should abort with proper message" do + expect { GitlabCtl::Backup.perform }.to output(/Could not find '#{etc_path}' directory. Is your package installed correctly?/).to_stderr.and raise_error + end + end + end + + context 'when backup_keep_time is non-zero' do + let(:options) { { delete_old_backups: true } } + let(:node_config) do + { + 'gitlab' => { + 'gitlab-rails' => { + 'backup_keep_time' => 1 + } + } + } + end + + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return(node_config) + allow(GitlabCtl::Backup).to receive(:secure?).and_return(true) + @backup = GitlabCtl::Backup.new(options) + end + + context 'when no valid files exist' do + before do + allow(Dir).to receive(:glob).and_return(invalid_backup_files) + allow_any_instance_of(Kernel).to receive(:warn) + end + + it 'should find no files to remove' do + expect(@backup.removable_archives.length).to equal(0) + expect(FileUtils).not_to have_received(:rm) + expect { @backup.prune }.to output(/done. Removed 0 older configuration backups./).to_stdout + end + end + + context 'when files exist to remove' do + before do + allow(Dir).to receive(:glob).and_return(all_backup_files) + end + + it 'should identify the correct files to remove' do + removed = past_valid_backup_files.map do |x| + File.join(backup_dir_path, x) + end + + expect(@backup.removable_archives).to match_array(removed) + @backup.remove_backups + removed.each do |f| + expect(FileUtils).to have_received(:rm).with(f) + end + end + + context 'when file removal fails' do + let(:failed_file) { File.join(backup_dir_path, past_valid_backup_files[1]) } + let(:message) { "Permission denied @ unlink_internal - #{failed_file}" } + let(:removed_files) do + files = past_valid_backup_files.map do |f| + File.join(backup_dir_path, f) + end + + files.select! { |f| f != failed_file } + end + + before do + allow(FileUtils).to receive(:rm).with(failed_file).and_raise(Errno::EACCES, message) + end + + it 'removes the remaining expected files' do + allow_any_instance_of(Kernel).to receive(:warn) + @backup.remove_backups + removed_files.each do |filename| + expect(FileUtils).to have_received(:rm).with(filename) + end + end + + it 'sets the proper file removal count' do + allow_any_instance_of(Kernel).to receive(:warn) + expect { @backup.remove_backups }.to output(/done. Removed #{removed_files.length} older configuration backups./).to_stdout + end + + it 'prints the error from file that could not be removed' do + expect { @backup.remove_backups }.to output(a_string_matching(message)).to_stderr + end + end + end + + context 'when the only valid files are after backup_keep_time' do + before do + allow(Dir).to receive(:glob).and_return(future_valid_backup_files) + end + + it 'should find no files to remove' do + allow_any_instance_of(Kernel).to receive(:warn) + expect(@backup.removable_archives.length).to equal(0) + expect(FileUtils).not_to have_received(:rm) + expect { @backup.prune }.to output(/done. Removed 0 older configuration backups./).to_stdout + end + end + end + + context 'when backup_keep_time is zero' do + let(:options) { { delete_old_backups: nil } } + let(:node_config) do + { + 'gitlab' => { + 'gitlab-rails' => { + 'backup_keep_time' => 0 + } + } + } + end + + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return(node_config) + allow(GitlabCtl::Backup).to receive(:secure?).and_return(true) + allow(Dir).to receive(:glob).and_return(all_backup_files) + @backup = GitlabCtl::Backup.new(options) + end + + it 'should skip performing a backup' do + expect { @backup.prune }.to output(/Keeping all older configuration backups/).to_stdout + expect(FileUtils).not_to have_received(:rm) + end + end + + context 'when node attributes invocation fails' do + let(:options) { { delete_old_backups: nil } } + + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_raise(GitlabCtl::Errors::NodeError, "Oh no, node failed") + allow_any_instance_of(Kernel).to receive(:warn) + allow(Dir).to receive(:glob).and_return(all_backup_files) + end + + it 'should not remove old archives' do + backup = GitlabCtl::Backup.new(options) + expect { backup.prune }.to output(/Keeping all older configuration backups/).to_stdout + expect(FileUtils).not_to have_received(:rm) + end + end + + context 'when node attributes returns an empty hash' do + let(:options) { { delete_old_backups: nil } } + + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({}) + allow_any_instance_of(Kernel).to receive(:warn) + allow(Dir).to receive(:glob).and_return(all_backup_files) + end + + it 'should not remove old archives' do + backup = GitlabCtl::Backup.new(options) + expect { backup.prune }.to output(/Keeping all older configuration backups/).to_stdout + expect(FileUtils).not_to have_received(:rm) + end + end +end diff --git a/spec/chef/gitlab-ctl-commands/lib/gitlab_ctl_pg_upgrade_spec.rb b/spec/chef/gitlab-ctl-commands/lib/gitlab_ctl_pg_upgrade_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMvbGliL2dpdGxhYl9jdGxfcGdfdXBncmFkZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/gitlab-ctl-commands/lib/gitlab_ctl_pg_upgrade_spec.rb @@ -0,0 +1,145 @@ +require 'spec_helper' + +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') + +require 'gitlab_ctl' + +def stub_postgresql_json_attributes(config = nil) + options = config || {} + options['port'] ||= '5432' + node_data = { postgresql: options.transform_keys(&:to_sym) } + public_data = { 'postgresql' => options } + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return(node_data) + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return(public_data) +end + +RSpec.describe GitlabCtl::PgUpgrade do + before do + @fake_default_dir = '/fake/data/postgresql/data' + allow(GitlabCtl::Util).to receive(:get_command_output).with( + "/fakebasedir/embedded/bin/pg_ctl --version" + ).and_return('fakeoldverision') + end + + context 'with a default configuration' do + before do + stub_postgresql_json_attributes + @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) + end + + it 'should create a new object' do + expect(@dbw).to be_instance_of(GitlabCtl::PgUpgrade) + end + + it 'should allow for a custom base directory' do + expect(@dbw.base_path).to eq('/fakebasedir') + end + + it 'should use the specified timeout' do + expect(@dbw.timeout).to eq(123) + end + + it 'should set tmp_data_dir to data_dir if tmp_dir is nil on initialization' do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({}) + + expect(@dbw.tmp_data_dir).to eq(@dbw.data_dir) + end + + it 'should return the appropriate data version' do + stub_postgresql_json_attributes + allow(File).to receive(:read).with( + File.join(@fake_default_dir, 'PG_VERSION') + ).and_return("99.99\n") + + expect(@dbw.fetch_data_version).to eq('99.99') + end + + context 'when determining if there is enough free space to perform an upgrade' do + before do + stub_postgresql_json_attributes + end + + it 'detects when there is not enough available disk space for upgrade' do + allow(GitlabCtl::Util).to receive(:get_command_output).with( + "du -s --block-size=1m #{@dbw.data_dir}", nil, 123 + ).and_return("200000\n#{@dbw.data_dir}") + + allow(GitlabCtl::Util).to receive(:get_command_output).with( + "df -P --block-size=1m #{@dbw.data_dir} | awk '{print $4}'", nil, 123 + ).and_return("Available\n220000") + + expect(@dbw.enough_free_space?(@dbw.data_dir)).to eq(false) + end + + it 'detects when there is enough available disk space for upgrade' do + allow(GitlabCtl::Util).to receive(:get_command_output).with( + "du -s --block-size=1m #{@dbw.data_dir}", nil, 123 + ).and_return("200000\n#{@dbw.data_dir}") + + allow(GitlabCtl::Util).to receive(:get_command_output).with( + "df -P --block-size=1m #{@dbw.data_dir} | awk '{print $4}'", nil, 123 + ).and_return("Available\n250000") + + expect(@dbw.enough_free_space?(@dbw.data_dir)).to eq(true) + end + end + + context 'when a failed upgrade attempt happened' do + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({}) + end + + let(:target_data_dir) { "future_data_dir" } + describe '#upgrade_artifact_exists?' do + it 'returns false when the directory does not exist' do + expect(@dbw.upgrade_artifact_exists?(target_data_dir)).to be false + end + + it 'returns false when the directory exists with no data' do + allow(File).to receive(:exist?).with(target_data_dir).and_return(true) + allow(Dir).to receive(:empty?).with(target_data_dir).and_return(true) + expect(@dbw.upgrade_artifact_exists?(target_data_dir)).to be false + end + + it 'returns true when the directory exists with data' do + allow(File).to receive(:exist?).with(target_data_dir).and_return(true) + allow(Dir).to receive(:empty?).with(target_data_dir).and_return(false) + expect(@dbw.upgrade_artifact_exists?(target_data_dir)).to be true + end + end + end + end + + it 'should use the configured port when running pg_upgrade' do + stub_postgresql_json_attributes({ 'port' => '1959' }) + @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) + expect(@dbw.port).to eq('1959') + end + + it 'should call pg_command with the appropriate command' do + stub_postgresql_json_attributes({ 'username' => 'arbitrary-user-name' }) + @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) + expect(GitlabCtl::Util).to receive( + :get_command_output + ).with('su - arbitrary-user-name -c "fake command"', nil, 123) + @dbw.run_pg_command('fake command') + end + + context 'when an explicit data directory is specified' do + it 'should use it as data_dir' do + stub_postgresql_json_attributes({ 'dir' => 'randomdir' }) + @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) + + expect(@dbw.data_dir).to eq('randomdir/data') + end + end + + context 'when an explicit data directory is not specified' do + it 'should use find data_dir using dir/data' do + stub_postgresql_json_attributes({ 'dir' => 'parentdir' }) + @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) + + expect(@dbw.data_dir).to eq('parentdir/data') + end + end +end diff --git a/spec/chef/gitlab-ctl-commands/lib/postgresql/replication_spec.rb b/spec/chef/gitlab-ctl-commands/lib/postgresql/replication_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMvbGliL3Bvc3RncmVzcWwvcmVwbGljYXRpb25fc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands/lib/postgresql/replication_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' +$LOAD_PATH << File.join(__dir__, '../../../../files/gitlab-ctl-commands/lib') + +require 'postgresql/replication' + +RSpec.describe PostgreSQL::Replication do + let(:ctl) { spy('gitlab ctl') } + let(:util) { spy('gitlab ctl util', error?: false) } + let(:attributes) { spy('node attributes spy') } + + subject { described_class.new(ctl) } + + before do + stub_const('GitlabCtl::Util', util) + + allow(util).to receive(:get_node_attributes) + .and_return(attributes) + end + + it 'asks for the password' do + expect(subject).to receive(:ask_password).and_return('pass').once + + subject.set_password! + end + + it 'sets a password' do + allow(subject).to receive(:ask_password).and_return('mypass') + + subject.set_password! + + expect(util).to have_received(:run_command) + .with(%r{WITH ENCRYPTED PASSWORD 'mypass'}) + end + + context 'when postgresql is enabled and patroni disabled' do + before do + allow(ctl).to receive(:service_enabled?).with('postgresql').and_return(true) + allow(ctl).to receive(:service_enabled?).with('patroni').and_return(false) + end + + it 'sets a password for user defined in `sql_replication_user` attribute' do + allow(subject).to receive(:ask_password).and_return('mypass') + allow(attributes).to receive(:to_s).and_return('myuser') + + subject.set_password! + + expect(util).to have_received(:run_command) + .with(%r{ALTER USER myuser WITH ENCRYPTED PASSWORD 'mypass'}) + end + end + + it 'raises an error if replication user is not configured' do + allow(subject).to receive(:ask_password).and_return('mypass') + allow(attributes).to receive(:dig).and_return(nil) + + expect { subject.set_password! }.to raise_error(ArgumentError) + end + + context 'when postgresql is disabled and patroni enabled' do + before do + allow(ctl).to receive(:service_enabled?).with('postgresql').and_return(false) + allow(ctl).to receive(:service_enabled?).with('patroni').and_return(true) + end + + it 'sets a password for user defined in `sql_replication_user` attribute' do + allow(subject).to receive(:ask_password).and_return('mypass') + allow(attributes).to receive(:to_s).and_return('myuser') + + subject.set_password! + + expect(util).to have_received(:run_command) + .with(%r{ALTER USER myuser WITH ENCRYPTED PASSWORD 'mypass'}) + end + end + + context 'when postgresql and patroni services are disabled' do + around do |example| + example.run + rescue SystemExit + end + + before do + allow(ctl).to receive(:service_enabled?).with('postgresql').and_return(false) + allow(ctl).to receive(:service_enabled?).with('patroni').and_return(false) + end + + it 'prints an error message' do + expect { subject.set_password! }.to output( + /There is no PostgreSQL instance enabled in Omnibus/).to_stdout + end + + it 'exits with 1' do + expect { subject.set_password! }.to raise_error(SystemExit) do |error| + expect(error.status).to eq(1) + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands/lib/postgresql_spec.rb b/spec/chef/gitlab-ctl-commands/lib/postgresql_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMvbGliL3Bvc3RncmVzcWxfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands/lib/postgresql_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') + +require 'postgresql' + +RSpec.describe GitlabCtl::PostgreSQL do + describe "#postgresql_username" do + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return( + { + 'postgresql' => { + 'username' => 'bar' + } + } + ) + end + it 'detects username correctly' do + expect(described_class.postgresql_username).to eq('bar') + end + end + + describe '#postgresql_group' do + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return( + { + 'postgresql' => { + 'group' => 'foo' + } + } + ) + end + + it 'returns the correct group' do + expect(described_class.postgresql_group).to eq('foo') + end + end + + describe '#postgresql_version' do + before do + allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return( + { + 'postgresql' => { + 'dir' => '/var/opt/gitlab' + } + } + ) + end + + context 'when PG_VERSION file exists' do + before do + allow(File).to receive(:exist?).and_return(true) + allow(File).to receive(:read).and_return('12\n') + end + + it 'returns the version' do + expect(described_class.postgresql_version).to eq(12) + end + end + + context 'when PG_VERSION file exists' do + before do + allow(File).to receive(:exist?).and_return(false) + end + + it 'returns nil' do + expect(described_class.postgresql_version).to be_nil + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands/lib/praefect_spec.rb b/spec/chef/gitlab-ctl-commands/lib/praefect_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMvbGliL3ByYWVmZWN0X3NwZWMucmI= --- /dev/null +++ b/spec/chef/gitlab-ctl-commands/lib/praefect_spec.rb @@ -0,0 +1,207 @@ +require 'spec_helper' +require 'optparse' + +require_relative('../../../../files/gitlab-ctl-commands/lib/praefect') + +RSpec.describe Praefect do + describe '.parse_options!' do + before do + allow(Kernel).to receive(:exit) { |code| raise "Kernel.exit(#{code})" } + end + + shared_examples 'unknown option is specified' do + it 'throws an error' do + expect { Praefect.parse_options!(%W(praefect #{command} --unknown)) }.to raise_error(OptionParser::InvalidOption, /unknown/) + end + end + + it 'throws an error when command is not specified' do + expect { Praefect.parse_options!(%w(praefect)) }.to raise_error(OptionParser::ParseError, /Praefect command is not specified/) + end + + it 'throws an error when unknown command is specified' do + expect { Praefect.parse_options!(%w(praefect unknown-command)) }.to raise_error(OptionParser::ParseError, /Unknown Praefect command: unknown-command/) + end + + shared_examples 'parses repository options' do + it 'throws an error when an argument for --virtual-storage-name is not specified' do + expect { Praefect.parse_options!(%W(praefect #{command} --dir dir --virtual-storage-name)) }.to raise_error(OptionParser::MissingArgument, /virtual-storage-name/) + end + + it 'throws an error when --virtual-storage-name is not specified' do + expect { Praefect.parse_options!(%W(praefect #{command} --dir dir)) }.to raise_error(OptionParser::ParseError, /Option --virtual-storage-name must be specified/) + end + + it 'throws an error when an argument for --repository-relative-path is not specified' do + expect { Praefect.parse_options!(%W(praefect #{command} --dir dir --repository-relative-path)) }.to raise_error(OptionParser::MissingArgument, /repository-relative-path/) + end + + it 'throws an error when --repository-relative-path is not specified' do + expect { Praefect.parse_options!(%W(praefect #{command} --dir dir --virtual-storage-name name)) }.to raise_error(OptionParser::ParseError, /Option --repository-relative-path must be specified/) + end + + it 'successfully parses correct params' do + expected_options = { command: command, dir: 'dir', virtual_storage_name: 'name', repository_relative_path: 'path' } + + expect(Praefect.parse_options!(%W(praefect #{command} --dir dir --virtual-storage-name name --repository-relative-path path))).to eq(expected_options) + end + end + + context 'when command is remove-repository' do + let(:command) { 'remove-repository' } + + it_behaves_like 'parses repository options' + it_behaves_like 'unknown option is specified' + + it 'successfully parses apply' do + expect(Praefect.parse_options!(%W(praefect #{command} + --dir dir + --virtual-storage-name name + --repository-relative-path path + --apply))).to include(apply: true) + + expect(Praefect.parse_options!(%W(praefect #{command} + --dir dir + --virtual-storage-name name + --repository-relative-path path + --apply something))).to include(apply: true) + + expect(Praefect.parse_options!(%W(praefect #{command} + --dir dir + --virtual-storage-name name + --repository-relative-path path))).not_to include(:apply) + end + end + + context 'when command is check' do + let(:command) { 'check' } + + it_behaves_like 'unknown option is specified' + end + + context 'when command is track-repository' do + let(:command) { 'track-repository' } + + it_behaves_like 'parses repository options' + it_behaves_like 'unknown option is specified' + + it 'successfully parses authoritative-storage' do + expected_options = { command: command, + dir: 'dir', + virtual_storage_name: 'name', + repository_relative_path: 'path', + authoritative_storage: 'storage-1', + replicate_immediately: true } + + expect(Praefect.parse_options!(%W(praefect #{command} + --dir dir + --virtual-storage-name name + --repository-relative-path path + --authoritative-storage storage-1 + --replicate-immediately))).to eq(expected_options) + end + end + + context 'when help option is passed' do + it 'shows help message and exit for global help option' do + expect(Kernel).to receive(:puts) do |msg| + expect(msg.to_s).to match(/gitlab-ctl praefect command/) + end + + expect { Praefect.parse_options!(%w(praefect -h)) }.to raise_error('Kernel.exit(0)') + end + + ['remove-repository', 'track-repository'].each do |cmd| + it "shows help message and exit for #{cmd} help option" do + expect(Kernel).to receive(:puts) do |msg| + expect(msg.to_s).to match(/gitlab-ctl praefect #{cmd}/) + end + + expect { Praefect.parse_options!(%W(praefect #{cmd} -h)) }.to raise_error('Kernel.exit(0)') + end + end + end + end + + describe '#execute' do + let(:repository_options) { { virtual_storage_name: 'storage-name', repository_relative_path: 'repository-path' } } + + context 'when package is not installed correctly' do + it 'aborts the execution if a path does not exit' do + allow(File).to receive(:exists?).twice.and_return(false) + expect(Kernel).to receive(:abort).and_raise('aborted') + + expect { Praefect.execute({}) }.to raise_error('aborted') + end + end + + shared_examples 'executes the command' do + it 'successfully executes' do + allow(File).to receive(:exist?).and_return(true) + expect(Kernel).not_to receive(:abort) + args = [ + Praefect::EXEC_PATH, '-config', 'dir/config.toml', command, + ] + + args += command_args + + expect(Kernel).to receive(:system).with(*args).and_return('result!') + expect(Kernel).not_to receive(:exit!) + + common_options = { + command: command, + dir: 'dir' + } + + Praefect.execute(common_options.merge(command_options)) + end + end + + context 'check command' do + let(:command) { 'check' } + let(:command_args) { [] } + let(:command_options) { {} } + + it_behaves_like 'executes the command' + end + + context 'remove-repository command' do + let(:command) { 'list-untracked-repositories' } + let(:command_args) { [] } + let(:command_options) { {} } + + it_behaves_like 'executes the command' + end + + context 'repository commands' do + let(:command_args) { ['-virtual-storage', 'storage-name', '-repository', 'repository-path'] } + let(:command_options) { repository_options } + + context 'remove-repository command' do + let(:command) { 'remove-repository' } + + it_behaves_like 'executes the command' + end + + context 'track-repository command' do + let(:command) { 'track-repository' } + let(:command_args) { ['-virtual-storage', 'storage-name', '-repository', 'repository-path', '-authoritative-storage', 'storage-1'] } + let(:command_options) { repository_options.merge(authoritative_storage: 'storage-1') } + + it_behaves_like 'executes the command' + + context 'with replicate-immediately' do + let(:command_args) do + ['-virtual-storage', 'storage-name', + '-repository', 'repository-path', + '-authoritative-storage', 'storage-1', + '-replicate-immediately'] + end + let(:command_options) { repository_options.merge(authoritative_storage: 'storage-1', replicate_immediately: true) } + + it_behaves_like 'executes the command' + end + end + end + end +end diff --git a/spec/chef/gitlab-ctl-commands/lib/upgrade_check_spec.rb b/spec/chef/gitlab-ctl-commands/lib/upgrade_check_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMvbGliL3VwZ3JhZGVfY2hlY2tfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands/lib/upgrade_check_spec.rb @@ -0,0 +1,39 @@ +require 'chef_helper' + +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') + +require 'gitlab_ctl/upgrade_check' + +RSpec.describe GitlabCtl::UpgradeCheck do + let(:latest) { '13.1.3' } + let(:previous_major) { '12.5.10' } + let(:previous_major_latest) { '12.10.13' } + let(:current_major) { '13.0.8' } + let(:old) { '11.5.11' } + + context 'not an upgrade' do + it 'returns true' do + expect(described_class.valid?(nil, latest)).to be true + end + end + + context 'valid upgrade paths' do + it 'returns true for an upgrade from the current major version' do + expect(described_class.valid?(current_major, latest)).to be true + end + end + + context 'invalid upgrade paths' do + it 'returns false for an upgrade from 11.5 to the latest' do + expect(described_class.valid?(old, latest)).to be false + end + + it 'returns false for an upgrade from 12.5 to the latest' do + expect(described_class.valid?(previous_major, latest)).to be false + end + + it 'returns false upgrading from previous_major_latest to the latest' do + expect(described_class.valid?(previous_major_latest, latest)).to be false + end + end +end diff --git a/spec/chef/gitlab-ctl-commands/lib/util_spec.rb b/spec/chef/gitlab-ctl-commands/lib/util_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL2dpdGxhYi1jdGwtY29tbWFuZHMvbGliL3V0aWxfc3BlYy5yYg== --- /dev/null +++ b/spec/chef/gitlab-ctl-commands/lib/util_spec.rb @@ -0,0 +1,262 @@ +require 'spec_helper' + +$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') + +require 'gitlab_ctl' + +RSpec.describe GitlabCtl::Util do + context 'when there is no TTY available' do + before do + allow(STDIN).to receive(:tty?).and_return(false) + end + + it 'asks for a database password in a non-interactive mode' do + allow(STDIN).to receive(:gets).and_return('pass') + + expect(described_class.get_password).to eq('pass') + end + + it 'strips a new line character from the password' do + allow(STDIN).to receive(:tty?).and_return(false) + allow(STDIN).to receive(:gets).and_return("mypass\n") + + expect(described_class.get_password).to eq('mypass') + end + end + + context 'when there is TTY available' do + before do + allow(STDIN).to receive(:tty?).and_return(true) + end + + it 'asks for confirmation' do + expect(STDIN).to receive(:getpass).twice.and_return("mypass") + + expect(described_class.get_password(do_confirm: true)).to eq('mypass') + end + + it 'skips confirmation' do + expect(STDIN).to receive(:getpass).and_return("mypass") + + expect(described_class.get_password(do_confirm: false)).to eq('mypass') + end + end + + describe '#roles' do + let(:fake_base_path) { '/foo' } + let(:fake_hostname) { 'fakehost.fakedomain' } + let(:fake_node_file) { "#{fake_base_path}/embedded/nodes/#{fake_hostname}.json" } + + before do + hostname = double('hostname') + allow(hostname).to receive(:stdout).and_return(fake_hostname) + allow(GitlabCtl::Util).to receive(:run_command).with('hostname -f').and_return(hostname) + allow(File).to receive(:exist?).with(fake_node_file).and_return(true) + end + + it 'returns an empty list when no roles are defined' do + empty_node = { + normal: { + roles: {} + } + } + allow(File).to receive(:read).with(fake_node_file).and_return(empty_node.to_json) + expect(described_class.roles(fake_base_path)).to eq([]) + end + + it 'returns a list of roles that are defined' do + roled_node = { + normal: { + roles: { + role_one: { + enable: true + }, + role_two: { + enable: false + }, + role_three: { + enable: true + } + } + } + } + allow(File).to receive(:read).with(fake_node_file).and_return(roled_node.to_json) + expect(described_class.roles(fake_base_path)).to eq(%w[role_one role_three]) + end + + it 'when no roles are defined' do + roled_node = {} + allow(File).to receive(:read).with(fake_node_file).and_return(roled_node.to_json) + + expect(described_class.roles(fake_base_path)).to eq([]) + end + + it 'when roles is not a Hash' do + roled_node = { roles: "test" } + allow(File).to receive(:read).with(fake_node_file).and_return(roled_node.to_json) + + expect(described_class.roles(fake_base_path)).to eq([]) + end + end + + describe '#parse_json_file' do + it 'fails on malformed JSON file' do + malformed_json = <<~MSG + { + 'foo': 'bar' + MSG + allow(File).to receive(:read).with('/tmp/foo').and_return(malformed_json) + + expect { GitlabCtl::Util.parse_json_file('/tmp/foo') }.to raise_error(GitlabCtl::Errors::NodeError, "Error reading /tmp/foo, has reconfigure been run yet?") + end + + it 'do not fail on empty json file' do + allow(File).to receive(:read).with('/tmp/foo').and_return('{}') + + expect(GitlabCtl::Util.parse_json_file('/tmp/foo')).to eq({}) + end + + it 'fails on incomplete but valid node attribute file' do + incomplete_node_attributes = <<~MSG + { + "name": "a-random-server" + } + MSG + allow(File).to receive(:read).with('/opt/gitlab/embedded/nodes/12345.json').and_return(incomplete_node_attributes) + + expect { GitlabCtl::Util.parse_json_file('/opt/gitlab/embedded/nodes/12345.json') }.to raise_error(GitlabCtl::Errors::NodeError, "Attributes not found in /opt/gitlab/embedded/nodes/12345.json, has reconfigure been run yet?") + end + end + + describe '#parse_duration' do + it 'should raise error for nil, empty, or malformed inputs' do + expect { GitlabCtl::Util.parse_duration(nil) }.to raise_error ArgumentError, 'invalid value for duration: ``' + expect { GitlabCtl::Util.parse_duration('') }.to raise_error ArgumentError, 'invalid value for duration: ``' + expect { GitlabCtl::Util.parse_duration('foo') }.to raise_error ArgumentError, 'invalid value for duration: `foo`' + expect { GitlabCtl::Util.parse_duration('123foo') }.to raise_error ArgumentError, 'invalid value for duration: `123foo`' + expect { GitlabCtl::Util.parse_duration('foo123') }.to raise_error ArgumentError, 'invalid value for duration: `foo123`' + end + + it 'should parse unformatted inputs into milliseconds' do + expect(GitlabCtl::Util.parse_duration('123')).to eq(123) + expect(GitlabCtl::Util.parse_duration('123.456')).to eq(123) + end + + it 'should recognize and parse different duration units' do + expect(GitlabCtl::Util.parse_duration('123.456ms')).to eq(123) + expect(GitlabCtl::Util.parse_duration('123.456s')).to eq(123.456 * 1000) + expect(GitlabCtl::Util.parse_duration('123.456m')).to eq(123.456 * 1000 * 60) + expect(GitlabCtl::Util.parse_duration('123.456h')).to eq(123.456 * 1000 * 60 * 60) + expect(GitlabCtl::Util.parse_duration('123.456d')).to eq(123.456 * 1000 * 60 * 60 * 24) + end + + it 'should parse mixed unit inputs in any order' do + expect(GitlabCtl::Util.parse_duration('1.1d2.2h3.3m4.4s5.5ms')).to eq( + 1.1 * 1000 * 60 * 60 * 24 + + 2.2 * 1000 * 60 * 60 + + 3.3 * 1000 * 60 + + 4.4 * 1000 + + 5 + ) + expect(GitlabCtl::Util.parse_duration('5.5ms4.4s3.3m2.2h1.1d')).to eq( + 1.1 * 1000 * 60 * 60 * 24 + + 2.2 * 1000 * 60 * 60 + + 3.3 * 1000 * 60 + + 4.4 * 1000 + + 5 + ) + end + + it 'should break and return when input is partially valid' do + expect(GitlabCtl::Util.parse_duration('1h2m3foo')).to eq( + 1 * 1000 * 60 * 60 + + 2 * 1000 * 60 + ) + expect(GitlabCtl::Util.parse_duration('1h2m3')).to eq( + 1 * 1000 * 60 * 60 + + 2 * 1000 * 60 + ) + end + end + + describe '#chef_run' do + it 'should accept and use custom log files' do + allow(described_class).to receive(:run_command).and_return('success') + expect(described_class).to receive(:run_command).with(/-L log_ago_and_far_away/) + described_class.chef_run('config', 'attributes', 'log_ago_and_far_away') + end + + it 'should not pass the log file path unless set' do + allow(described_class).to receive(:run_command).and_return('success') + expect(described_class).not_to receive(:run_command).with(/ -L /) + described_class.chef_run('config', 'attributes') + end + end + + describe '#get_node_attributes' do + context 'when node file is missing' do + before do + allow(File).to receive(:exist?).with(%r{/opt/gitlab/embedded/nodes.*json}).and_return(false) + allow(Dir).to receive(:glob?).with(%r{/opt/gitlab/embedded/}).and_return([]) + end + + it 'raises an error' do + expect { GitlabCtl::Util.get_node_attributes }.to raise_error(GitlabCtl::Errors::NodeError, "Node attributes JSON file not found in /opt/gitlab/embedded/nodes, has reconfigure been run yet?") + end + end + end + + describe "#public_attributes_missing?" do + context 'when the public attributes file is missing' do + before do + allow(File).to receive(:exist?).with(%r{/var/opt/gitlab/public_attributes.json}).and_return(false) + end + + it 'returns true' do + expect(GitlabCtl::Util.public_attributes_missing?).to be true + end + end + end + + describe "#public_attributes_broken?" do + context 'when the public attributes file is broken' do + before do + allow(File).to receive(:exist?).with(%r{/var/opt/gitlab/public_attributes.json}).and_return(true) + end + + it 'returns true when the attribute is missing' do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({}) + expect(GitlabCtl::Util.public_attributes_broken?).to be true + end + + it 'returns false by default when the gitlab attribute is found' do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ "gitlab" => "hithere" }) + expect(GitlabCtl::Util.public_attributes_broken?).to be false + end + + context 'when using an alternate key to check for broken' do + it 'returns true when the key is missing' do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({}) + expect(GitlabCtl::Util.public_attributes_broken?('funky')).to be true + end + + it 'returns false when the key is present' do + allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ "funky" => "llama" }) + expect(GitlabCtl::Util.public_attributes_broken?('funky')).to be false + end + end + end + end + + describe '#get_public_node_attributes' do + context 'when node file is missing' do + before do + allow(GitlabCtl::Util).to receive(:public_attributes_missing?).and_return(true) + end + + it 'returns empty hash' do + expect(GitlabCtl::Util.get_public_node_attributes).to eq({}) + end + end + end +end diff --git a/spec/chef/gitlab-ee/libraries/gitlab_geo_helper_spec.rb b/spec/chef/gitlab-ee/libraries/gitlab_geo_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9saWJyYXJpZXMvZ2l0bGFiX2dlb19oZWxwZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/libraries/gitlab_geo_helper_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -require_relative '../../../../files/gitlab-cookbooks/gitlab-ee/libraries/gitlab_geo_helper.rb' -require 'chef_helper' - -RSpec.describe GitlabGeoHelper do - cached(:chef_run) { converge_config(is_ee: true) } - subject { described_class.new(chef_run.node) } - - describe '#migrated?' do - let(:state_file) { '/tmp/geo-db-migrate-xyz-123' } - - before do - allow(subject).to receive(:db_migrate_status_file) { state_file } - end - - it 'returns true when migration file exists and content is zero' do - stub_file_content(state_file, '0') - - expect(subject.migrated?).to eq true - end - - it 'returns false when migration file doesnt exist' do - allow(File).to receive(:exist?).with(state_file) { false } - - expect(subject.migrated?).to eq false - end - - it 'returns false when content is not zero' do - stub_file_content(state_file, '1') - - expect(subject.migrated?).to eq false - end - end - - describe '#db_migrate_status_file' do - let(:connection_digest) { '32596e8376077c3ef8d5cf52f15279ba' } - let(:revision) { '8b21d70' } - - before do - allow(subject).to receive(:connection_digest) { connection_digest } - allow(subject).to receive(:revision) { revision } - end - - it 'contains connection digest' do - expect(subject.db_migrate_status_file).to include(connection_digest) - end - - it 'contains revision number' do - expect(subject.db_migrate_status_file).to include(revision) - end - - it 'follows specific pattern' do - expect(subject.db_migrate_status_file).to include("geo-db-migrate-#{connection_digest}-#{revision}") - end - end - - def stub_file_content(fullpath, content) - allow(File).to receive(:exist?).with(fullpath) { true } - allow(IO).to receive(:read).with(fullpath) { content } - end -end diff --git a/spec/chef/gitlab-ee/recipes/default_spec.rb b/spec/chef/gitlab-ee/recipes/default_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZWNpcGVzL2RlZmF1bHRfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/recipes/default_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab-ee::default' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'postgresql is enabled' do - context 'pgbouncer will not connect to postgresql' do - it 'should always include the pgbouncer_user recipe' do - expect(chef_run).to include_recipe('pgbouncer::user') - end - end - - context 'pgbouncer will connect to postgresql' do - before do - stub_gitlab_rb( - { - postgresql: { - pgbouncer_user_password: 'fakepassword' - } - } - ) - end - - it 'should include the pgbouncer_user recipe' do - expect(chef_run).to include_recipe('pgbouncer::user') - end - end - end -end diff --git a/spec/chef/gitlab-ee/recipes/geo-logcursor_spec.rb b/spec/chef/gitlab-ee/recipes/geo-logcursor_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZWNpcGVzL2dlby1sb2djdXJzb3Jfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/recipes/geo-logcursor_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright:: Copyright (c) 2017 GitLab Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'chef_helper' - -RSpec.describe 'gitlab-ee::geo-logcursor' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'when disabled' do - it_behaves_like 'disabled runit service', 'geo-logcursor' - end - - describe 'when enabled' do - before do - stub_gitlab_rb( - geo_logcursor: { - enable: true, - } - ) - end - - it_behaves_like 'enabled runit service', 'geo-logcursor', 'root', 'root' - end -end diff --git a/spec/chef/gitlab-ee/recipes/geo-postgresql_spec.rb b/spec/chef/gitlab-ee/recipes/geo-postgresql_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZWNpcGVzL2dlby1wb3N0Z3Jlc3FsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/recipes/geo-postgresql_spec.rb +++ /dev/null @@ -1,388 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'geo postgresql' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - let(:postgresql_conf) { '/var/opt/gitlab/geo-postgresql/data/postgresql.conf' } - let(:runtime_conf) { '/var/opt/gitlab/geo-postgresql/data/runtime.conf' } - let(:pg_hba_conf) { '/var/opt/gitlab/geo-postgresql/data/pg_hba.conf' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('best_version')) - allow_any_instance_of(GeoPgHelper).to receive(:running_version).and_return(PGVersion.new('best_version')) - allow_any_instance_of(GeoPgHelper).to receive(:database_version).and_return(PGVersion.new('best_version')) - - # Workaround for Chef reloading instances across different examples - allow_any_instance_of(GeoPgHelper).to receive(:bootstrapped?).and_return(true) - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('geo-postgresql').and_return(true) - end - - context 'when geo postgres is enabled' do - before do - stub_gitlab_rb(geo_postgresql: { enable: true }) - end - - it 'includes the postgresql::bin recipe' do - expect(chef_run).to include_recipe('postgresql::bin') - end - - it 'includes the postgresql_user recipe' do - expect(chef_run).to include_recipe('postgresql::user') - end - - it 'includes the postgresql_sysctl recipe' do - expect(chef_run).to include_recipe('postgresql::sysctl') - end - - it 'does not warn the user that a restart is needed by default' do - allow_any_instance_of(GeoPgHelper).to receive(:is_running?).and_return(true) - expect(chef_run).not_to run_ruby_block('warn pending geo-postgresql restart') - end - - it 'notifies restarts postgresql when the postgresql runit run file changes' do - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('geo-postgresql').and_return(true) - - psql_service = chef_run.service('geo-postgresql') - expect(psql_service).not_to subscribe_to('template[/opt/gitlab/sv/geo-postgresql/run]').on(:restart).delayed - end - - it 'includes runtime.conf in postgresql.conf' do - expect(chef_run).to render_file(postgresql_conf) - .with_content(/include 'runtime.conf'/) - end - - it 'creates the gitlab_geo role in the geo-postgresql database, without a password' do - expect(chef_run).to create_postgresql_user('gitlab_geo').with(password: nil) - end - - it 'creates gitlabhq_geo_production database' do - params = { - owner: 'gitlab_geo' - } - expect(chef_run).to create_postgresql_database('gitlabhq_geo_production').with(params) - end - - context 'with default settings' do - it_behaves_like 'enabled runit service', 'geo-postgresql', 'root', 'root' - - context 'when rendering postgresql.conf' do - it 'correctly sets the shared_preload_libraries default setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['shared_preload_libraries']).to be_nil - - expect(chef_run).to render_file(postgresql_conf) - .with_content(/shared_preload_libraries = ''/) - end - - it 'sets archive settings' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/archive_mode = off/) - end - - it 'sets the max_replication_slots setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['max_replication_slots']).to eq(0) - - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/max_replication_slots = 0/) - end - - it 'sets the synchronous_commit setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['synchronous_standby_names']).to eq('') - - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/synchronous_standby_names = ''/) - end - - it 'does not set dynamic_shared_memory_type by default' do - expect(chef_run).not_to render_file( - postgresql_conf - ).with_content(/^dynamic_shared_memory_type = /) - end - - it 'sets the max_locks_per_transaction setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['max_locks_per_transaction']) - .to eq(128) - - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/max_locks_per_transaction = 128/) - end - end - context 'when rendering runtime.conf' do - it 'correctly sets the log_line_prefix default setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['log_line_prefix']).to be_nil - - expect(chef_run).to render_file(runtime_conf) - .with_content(/log_line_prefix = ''/) - end - - it 'sets max_standby settings' do - expect(chef_run).to render_file( - runtime_conf - ).with_content(/max_standby_archive_delay = 30s/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/max_standby_streaming_delay = 30s/) - end - - it 'sets archive settings' do - expect(chef_run).to render_file( - runtime_conf - ).with_content(/archive_command = ''/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/archive_timeout = 0/) - end - - it 'sets the synchronous_commit setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['synchronous_commit']).to eq('on') - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/synchronous_commit = on/) - end - - it 'sets the hot_standby_feedback setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['hot_standby_feedback']) - .to eq('off') - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/hot_standby_feedback = off/) - end - - it 'sets the random_page_cost setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['random_page_cost']) - .to eq(2.0) - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/random_page_cost = 2\.0/) - end - - it 'sets the log_temp_files setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['log_temp_files']) - .to eq(-1) - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/log_temp_files = -1/) - end - - it 'sets the log_checkpoints setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['log_checkpoints']) - .to eq('off') - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/log_checkpoints = off/) - end - - it 'sets idle_in_transaction_session_timeout' do - expect(chef_run.node['gitlab']['geo-postgresql']['idle_in_transaction_session_timeout']) - .to eq('60000') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/idle_in_transaction_session_timeout = 60000/) - end - - it 'sets effective_io_concurrency' do - expect(chef_run.node['gitlab']['geo-postgresql']['effective_io_concurrency']) - .to eq(1) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/effective_io_concurrency = 1/) - end - - it 'sets max_worker_processes' do - expect(chef_run.node['gitlab']['geo-postgresql']['max_worker_processes']) - .to eq(8) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/max_worker_processes = 8/) - end - - it 'sets max_parallel_workers_per_gather' do - expect(chef_run.node['gitlab']['geo-postgresql']['max_parallel_workers_per_gather']) - .to eq(0) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/max_parallel_workers_per_gather = 0/) - end - - it 'sets log_lock_waits' do - expect(chef_run.node['gitlab']['geo-postgresql']['log_lock_waits']) - .to eq(1) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/log_lock_waits = 1/) - end - - it 'sets deadlock_timeout' do - expect(chef_run.node['gitlab']['geo-postgresql']['deadlock_timeout']) - .to eq('5s') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/deadlock_timeout = '5s'/) - end - - it 'sets track_io_timing' do - expect(chef_run.node['gitlab']['geo-postgresql']['track_io_timing']) - .to eq('off') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/track_io_timing = 'off'/) - end - end - end - - context 'with user specified settings' do - before do - stub_gitlab_rb(geo_postgresql: { - enable: true, - dynamic_shared_memory_type: 'none', - custom_pg_hba_entries: { - foo: [ - type: 'host', - database: 'foo', - user: 'bar', - cidr: '127.0.0.1/32', - method: 'trust' - ] - }, - sql_user_password: 'fakepasswordhash', - shared_preload_libraries: 'pg_stat_statements', - log_line_prefix: '%a', - max_standby_archive_delay: '60s', - max_standby_streaming_delay: '120s', - archive_mode: 'on', - archive_command: 'command', - archive_timeout: '120', - }, - postgresql: { - username: 'foo', - group: 'bar' - } - ) - end - - it 'notifies geo-postgresql reload' do - runtime_resource = chef_run.template(runtime_conf) - expect(runtime_resource).to notify('execute[reload geo-postgresql]').to(:run).immediately - end - - it 'creates the gitlab_geo role in the geo-postgresql database with the specified password' do - expect(chef_run).to create_postgresql_user('gitlab_geo').with(password: 'md5fakepasswordhash') - end - - context 'when rendering postgresql.conf' do - it 'sets the dynamic_shared_memory_type' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/^dynamic_shared_memory_type = none/) - end - - it 'correctly sets the shared_preload_libraries setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['shared_preload_libraries']).to eql('pg_stat_statements') - - expect(chef_run).to render_file(postgresql_conf) - .with_content(/shared_preload_libraries = 'pg_stat_statements'/) - end - - it 'sets archive settings' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/archive_mode = on/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/archive_command = 'command'/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/archive_timeout = 120/) - end - end - - context 'when rendering runtime.conf' do - it 'correctly sets the log_line_prefix setting' do - expect(chef_run.node['gitlab']['geo-postgresql']['log_line_prefix']).to eql('%a') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/log_line_prefix = '%a'/) - end - - it 'sets max_standby settings' do - expect(chef_run).to render_file( - runtime_conf - ).with_content(/max_standby_archive_delay = 60s/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/max_standby_streaming_delay = 120s/) - end - - context 'when rendering pg_hba.conf' do - it 'creates a standard pg_hba.conf' do - expect(chef_run).to render_file(pg_hba_conf) - .with_content('local all all peer map=gitlab') - end - - it 'adds users custom entries to pg_hba.conf' do - expect(chef_run).to render_file(pg_hba_conf) - .with_content('host foo bar 127.0.0.1/32 trust') - end - end - end - end - - context 'when geo postgres is disabled' do - before do - stub_gitlab_rb(geo_postgresql: { enable: false }) - end - - it_behaves_like 'disabled runit service', 'geo-postgresql' - end - end -end - -RSpec.describe 'geo postgresql when version mismatches occur' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - let(:postgresql_conf) { '/var/opt/gitlab/geo-postgresql/data/postgresql.conf' } - let(:runtime_conf) { '/var/opt/gitlab/geo-postgresql/data/runtime.conf' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb(geo_postgresql: { enable: true }) - end - - context 'running version differs from installed version' do - before do - allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('expectation')) - allow_any_instance_of(GeoPgHelper).to receive(:running_version).and_return(PGVersion.new('reality')) - end - - it 'warns the user that a restart is needed' do - allow_any_instance_of(GeoPgHelper).to receive(:is_running?).and_return(true) - expect(chef_run).to run_ruby_block('warn pending geo-postgresql restart') - end - - it 'does not warns the user that a restart is needed when geo-postgres is stopped' do - expect(chef_run).not_to run_ruby_block('warn pending geo-postgresql restart') - end - end - - context 'running version differs from data version' do - before do - allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('expectation')) - allow_any_instance_of(GeoPgHelper).to receive(:running_version).and_return(PGVersion.new('expectation')) - allow_any_instance_of(GeoPgHelper).to receive(:database_version).and_return(PGVersion.new('reality')) - end - - it 'does not warn the user that a restart is needed' do - allow_any_instance_of(GeoPgHelper).to receive(:is_running?).and_return(true) - expect(chef_run).not_to run_ruby_block('warn pending geo-postgresql restart') - end - end -end diff --git a/spec/chef/gitlab-ee/recipes/geo-secondary_disable_spec.rb b/spec/chef/gitlab-ee/recipes/geo-secondary_disable_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZWNpcGVzL2dlby1zZWNvbmRhcnlfZGlzYWJsZV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/recipes/geo-secondary_disable_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab-ee::geo-secondary_disable' do - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'when geo_secondary_role is disabled' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before { stub_gitlab_rb(geo_secondary_role: { enable: false }) } - - it 'removes database_geo.yml symlink' do - expect(chef_run).to delete_templatesymlink('Removes database_geo.yml symlink') - .with(link_to: '/var/opt/gitlab/gitlab-rails/etc/database_geo.yml', - link_from: '/opt/gitlab/embedded/service/gitlab-rails/config/database_geo.yml') - end - end -end diff --git a/spec/chef/gitlab-ee/recipes/geo-secondary_spec.rb b/spec/chef/gitlab-ee/recipes/geo-secondary_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZWNpcGVzL2dlby1zZWNvbmRhcnlfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/recipes/geo-secondary_spec.rb +++ /dev/null @@ -1,388 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab-ee::geo-secondary' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab-ee::default') } - let(:database_geo_yml) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/database_geo.yml') } - let(:database_geo_yml_content) { ChefSpec::Renderer.new(chef_run, database_geo_yml).content } - let(:generated_yml_content) { YAML.safe_load(database_geo_yml_content, [], [], true, symbolize_names: true) } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'when geo_secondary_role is disabled' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before { stub_gitlab_rb(geo_secondary_role: { enable: false }) } - - it 'does not render the geo-secondary files' do - expect(chef_run).not_to render_file('/opt/gitlab/embedded/service/gitlab-rails/config/database_geo.yml') - expect(chef_run).not_to render_file('/var/opt/gitlab/gitlab-rails/etc/database_geo.yml') - end - end - - describe 'when geo_secondary_role is disabled but geo-postgresql enabled' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - stub_gitlab_rb(geo_secondary_role: { enable: false }, - geo_postgresql: { enable: true }) - end - - it 'does not render the geo-secondary files' do - expect(chef_run).not_to render_file('/opt/gitlab/embedded/service/gitlab-rails/config/database_geo.yml') - expect(chef_run).not_to render_file('/var/opt/gitlab/gitlab-rails/etc/database_geo.yml') - end - end - - describe 'when gitlab_rails is disabled, but geo_secondary_role is enabled' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - stub_gitlab_rb(geo_secondary_role: { enable: true }, - gitlab_rails: { enable: false }) - end - - it 'does not render the geo-secondary files' do - expect(chef_run).not_to create_templatesymlink('Create a database_geo.yml and create a symlink to Rails root') - end - end - - describe 'when gitlab_rails is enabled' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - stub_gitlab_rb(geo_secondary_role: { enable: true }, - geo_postgresql: { enable: true }, - puma: { enable: false }, - sidekiq: { enable: false }, - geo_logcursor: { enable: false }, - gitlab_rails: { enable: true }) - end - - it 'allows gitlab_rails to be overriden' do - expect(chef_run.node['gitlab']['gitlab-rails']['enable']).to be true - end - end - - context 'when gitaly is enabled' do - describe 'when gitlab_rails enable is not set' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - stub_gitlab_rb(geo_secondary_role: { enable: true }, - geo_secondary: { enable: true }, - geo_postgresql: { enable: false }, - puma: { enable: false }, - sidekiq: { enable: false }, - geo_logcursor: { enable: false }, - gitaly: { enable: true }) - end - - it 'ensures gitlab_rails is enabled' do - chef_run - expect(Gitlab['gitlab_rails']['enable']).to be true - end - end - - describe 'when gitlab_rails enable is provided' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - stub_gitlab_rb(geo_secondary_role: { enable: true }, - geo_secondary: { enable: true }, - geo_postgresql: { enable: false }, - puma: { enable: false }, - sidekiq: { enable: false }, - geo_logcursor: { enable: false }, - gitaly: { enable: true }, - gitlab_rails: { enable: false }) - end - - it 'does not override gitlab_rails enable' do - chef_run - expect(Gitlab['gitlab_rails']['enable']).to be false - end - end - end - - context 'when geo_secondary_role is enabled but geo-postgresql is disabled' do - before do - stub_gitlab_rb(geo_secondary_role: { enable: true }, - geo_postgresql: { enable: false }, - geo_secondary: { - db_host: '1.1.1.1', - db_password: 'password', - db_port: '5431' - }) - end - - describe 'migrations' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - it 'includes the database migration recipe' do - expect(chef_run).to include_recipe('gitlab-ee::geo_database_migrations') - end - end - - describe 'database.yml' do - it 'creates the database_geo.yml template not using many database structure' do - expect(generated_yml_content).to eq( - production: { - adapter: 'postgresql', - application_name: nil, - collation: nil, - connect_timeout: nil, - database: 'gitlabhq_geo_production', - encoding: 'unicode', - host: '1.1.1.1', - keepalives: nil, - keepalives_count: nil, - keepalives_idle: nil, - keepalives_interval: nil, - load_balancing: { - hosts: [] - }, - password: 'password', - port: 5431, - prepared_statements: false, - socket: nil, - sslca: nil, - sslcompression: 0, - sslmode: nil, - sslrootcert: nil, - statement_limit: nil, - tcp_user_timeout: nil, - username: 'gitlab_geo', - variables: { - statement_timeout: nil, - } - } - ) - end - - context 'when SSL compression is enabled' do - before do - stub_gitlab_rb(geo_secondary: { db_sslcompression: 1 }) - end - - it 'uses provided value in database.yml' do - expect(generated_yml_content[:production][:sslcompression]).to eq(1) - end - end - end - end - - context 'when geo_secondary_role is enabled' do - before do - stub_gitlab_rb(geo_secondary_role: { enable: true }) - - # Make sure other calls to `File.symlink?` are allowed. - allow(File).to receive(:symlink?).and_call_original - %w( - alertmanager - gitlab-exporter - gitlab-workhorse - logrotate - nginx - node-exporter - postgres-exporter - postgresql - prometheus - redis - redis-exporter - sidekiq - puma - gitaly - geo-postgresql - gitlab-pages - geo-logcursor - patroni - ).map { |svc| stub_should_notify?(svc, true) } - end - - describe 'database.yml' do - let(:templatesymlink) { chef_run.templatesymlink('Create a database_geo.yml and create a symlink to Rails root') } - - it 'creates the database_geo.yml template not using many database structure' do - expect(generated_yml_content).to eq( - production: { - adapter: 'postgresql', - application_name: nil, - collation: nil, - connect_timeout: nil, - database: 'gitlabhq_geo_production', - encoding: 'unicode', - host: '/var/opt/gitlab/geo-postgresql', - keepalives: nil, - keepalives_count: nil, - keepalives_idle: nil, - keepalives_interval: nil, - load_balancing: { - hosts: [] - }, - password: nil, - port: 5431, - prepared_statements: false, - socket: nil, - sslca: nil, - sslcompression: 0, - sslmode: nil, - sslrootcert: nil, - statement_limit: nil, - tcp_user_timeout: nil, - username: 'gitlab_geo', - variables: { - statement_timeout: nil, - } - } - ) - end - - it 'template triggers dependent services notifications' do - expect(templatesymlink).to notify('ruby_block[Restart geo-secondary dependent services]').to(:run).delayed - end - end - - describe 'PostgreSQL gitlab-geo.conf' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - let(:geo_conf) { '/var/opt/gitlab/postgresql/data/gitlab-geo.conf' } - let(:postgresql_conf) { '/var/opt/gitlab/postgresql/data/postgresql.conf' } - - context 'when postgresql enabled on the node' do - it 'renders gitlab-geo.conf' do - expect(chef_run).to render_file(geo_conf) - end - end - - context 'when postgresql disabled on the node' do - before { stub_gitlab_rb(postgresql: { enable: false }) } - - it 'does not render gitlab-geo.conf' do - expect(chef_run).not_to render_file(geo_conf) - end - end - end - - describe 'Restart geo-secondary dependent services' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - let(:ruby_block) { chef_run.ruby_block('Restart geo-secondary dependent services') } - - it 'does not run' do - expect(chef_run).not_to run_ruby_block('Restart geo-secondary dependent services') - expect(ruby_block).to do_nothing - end - - it 'ruby_block triggers dependent services notifications' do - allow(ruby_block).to receive(:notifies) - ruby_block.block.call - - %w( - puma - geo-logcursor - ).each do |svc| - expect(ruby_block).to have_received(:notifies).with(:restart, "runit_service[#{svc}]") - end - expect(ruby_block).to have_received(:notifies).with(:restart, "sidekiq_service[sidekiq]") - end - end - - describe 'include_recipe' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - it 'includes the Geo tracking DB recipe' do - expect(chef_run).to include_recipe('gitlab-ee::geo-postgresql') - end - - it 'includes the Geo secondary recipes for Rails' do - expect(chef_run).to include_recipe('gitlab-ee::geo-secondary') - expect(chef_run).to include_recipe('gitlab-ee::geo_database_migrations') - end - - it 'does not include the Geo database migrations recipe if Rails not needed' do - stub_gitlab_rb(geo_secondary_role: { enable: true }, - nginx: { enable: false }, - puma: { enable: false }, - sidekiq: { enable: false }, - gitaly: { enable: false }, - postgresql: { enable: false }, - geo_logcursor: { enable: false }, - redis: { enable: true }) - - expect(chef_run).not_to include_recipe('gitlab-ee::geo_database_migrations') - end - end - - describe 'migrations' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - it 'runs the migrations' do - expect(chef_run).to run_rails_migration('gitlab-geo tracking') - end - end - - describe 'rails_needed?' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab-ee::default') } - - context 'manually enabled services' do - before do - stub_gitlab_rb( - # Everything but puma is disabled - puma: { enable: true }, - sidekiq: { enable: false }, - geo_logcursor: { enable: false }, - gitaly: { enable: false } - ) - end - - it 'should need rails' do - expect(chef_run).to include_recipe('gitlab::gitlab-rails') - end - end - - context 'manually disabled services' do - before do - stub_gitlab_rb( - gitaly: { enable: false }, - puma: { enable: false }, - sidekiq: { enable: false }, - geo_logcursor: { enable: false } - ) - end - - it 'should not need rails' do - expect(chef_run).not_to include_recipe('gitlab::gitlab-rails') - end - end - end - end - - context 'puma worker_processes' do - let(:chef_run) do - ChefSpec::SoloRunner.new do |node| - node.automatic['cpu']['total'] = 16 - node.automatic['memory']['total'] = '8388608KB' # 8GB - end.converge('gitlab-ee::default') - end - - it 'reduces the number of puma workers on secondary node' do - stub_gitlab_rb(geo_secondary_role: { enable: true }) - - expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 5 - end - - it 'uses the specified number of puma workers' do - stub_gitlab_rb(geo_secondary_role: { enable: true }, - puma: { worker_processes: 1 }) - - expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 1 - end - - it 'does not reduce the number of puma workers on primary node' do - stub_gitlab_rb(geo_primary_role: { enable: true }) - - expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 6 - end - end -end diff --git a/spec/chef/gitlab-ee/recipes/geo_database_migrations_spec.rb b/spec/chef/gitlab-ee/recipes/geo_database_migrations_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZWNpcGVzL2dlb19kYXRhYmFzZV9taWdyYXRpb25zX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/recipes/geo_database_migrations_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -require 'chef_helper' - -# NOTE: These specs do not verify whether the code actually ran -# Nor whether the resource inside of the recipe was notified correctly. -# At this moment they only verify whether the expected commands are passed -# to the bash block. -# - -RSpec.describe 'gitlab-ee::geo-database-migrations' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when migration should run' do - before do - stub_default_not_listening?(false) - stub_gitlab_rb(geo_secondary_role: { enable: true }) - - # Make sure other calls to `File.symlink?` are allowed. - allow(File).to receive(:symlink?).and_call_original - %w( - alertmanager - gitlab-exporter - gitlab-workhorse - logrotate - nginx - node-exporter - postgres-exporter - postgresql - prometheus - redis - redis-exporter - sidekiq - puma - gitaly - geo-postgresql - gitlab-pages - geo-logcursor - ).map { |svc| stub_should_notify?(svc, true) } - end - - it 'runs the migrations with expected attributes' do - expect(chef_run).to run_rails_migration('gitlab-geo tracking') do |resource| - expect(resource.dependent_services).to include_array(%w(runit_service[puma] sidekiq_service[sidekiq])) - expect(resource.rake_task).to eq('geo:db:migrate') - expect(resource.logfile_prefix).to eq('gitlab-geo-db-migrate') - expect(resource.helper).to be_a(GitlabGeoHelper) - end - end - - it 'starts geo-postgresql if its not running' do - stub_not_listening?('geo-postgresql', true) - - expect(chef_run.rails_migration('gitlab-geo tracking')).to notify('execute[start geo-postgresql]').to(:run) - end - end - - context 'with auto_migrate off' do - it 'skips running the migrations' do - stub_gitlab_rb({ - geo_secondary_role: { enable: true }, - geo_secondary: { auto_migrate: false } - }) - - expect(chef_run).to include_recipe('gitlab-ee::geo_database_migrations') - expect(chef_run).not_to run_rails_migration('gitlab-geo tracking') - end - end -end diff --git a/spec/chef/gitlab-ee/recipes/geo_spec.rb b/spec/chef/gitlab-ee/recipes/geo_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZWNpcGVzL2dlb19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/recipes/geo_spec.rb +++ /dev/null @@ -1,166 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab-ee::geo' do - let(:node) { chef_run.node } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - shared_examples 'default services' do - it 'remain enabled', :aggregate_failures do - chef_run - - default_services = Services.find_by_group(Services::DEFAULT_GROUP) - omnibus_helper = OmnibusHelper.new(node) - - expect(default_services.count).to be > 0 - default_services.each do |service| - expect(omnibus_helper.service_enabled?(service.tr('_', '-'))).to be(true), "#{service} was not enabled" - end - end - end - - context 'when geo_primary_role enabled' do - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb(geo_primary_role: { enable: true }) - end - ChefSpec::SoloRunner.converge('gitlab-ee::default') - end - - it_behaves_like 'default services' - - context 'in geo_logcursor settings' do - it 'is not enabled' do - expect(node['gitlab']['geo-logcursor']['enable']).to eq(nil) - end - end - - context 'in postgres settings' do - let(:config_attrs) { node['postgresql'] } - - it 'defines sql_replication_user' do - expect(config_attrs['sql_replication_user']).to eq('gitlab_replicator') - end - - it 'defines wal_level' do - expect(config_attrs['wal_level']).to eq('hot_standby') - end - - it 'defines wal_log_hints' do - expect(config_attrs['wal_log_hints']).to eq('off') - end - - it 'defines max_wal_senders' do - expect(config_attrs['max_wal_senders']).to eq(10) - end - - it 'defines wal_keep_segments' do - expect(config_attrs['wal_keep_segments']).to eq(50) - end - - it 'defines max_replication_slots' do - expect(config_attrs['max_replication_slots']).to eq(1) - end - - it 'defines hot_standby' do - expect(config_attrs['hot_standby']).to eq('on') - end - end - end - - context 'geo_secondary_role enabled' do - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb(geo_secondary_role: { enable: true }) - end - ChefSpec::SoloRunner.converge('gitlab-ee::default') - end - - it_behaves_like 'default services' - - context 'in geo_postgres settings' do - it 'is enabled' do - expect(node['gitlab']['geo-postgresql']['enable']).to eq(true) - end - end - - context 'in geo_logcursor settings' do - it 'is enabled' do - expect(node['gitlab']['geo-logcursor']['enable']).to eq(true) - end - end - - context 'in postgres settings' do - let(:config_attrs) { node['postgresql'] } - - it 'defines wal_level' do - expect(config_attrs['wal_level']).to eq('hot_standby') - end - - it 'defines wal_log_hints' do - expect(config_attrs['wal_log_hints']).to eq('off') - end - - it 'defines max_wal_senders' do - expect(config_attrs['max_wal_senders']).to eq(10) - end - - it 'defines wal_keep_segments' do - expect(config_attrs['wal_keep_segments']).to eq(10) - end - - it 'defines max_replication_slots' do - expect(config_attrs['max_replication_slots']).to eq(0) - end - - it 'defines hot_standby' do - expect(config_attrs['hot_standby']).to eq('on') - end - - it 'defines standby settings' do - expect(config_attrs['max_standby_archive_delay']).to eq('60s') - expect(config_attrs['max_standby_streaming_delay']).to eq('60s') - end - end - - context 'postgresql 13' do - let(:runtime_conf) { '/var/opt/gitlab/geo-postgresql/data/runtime.conf' } - - before do - allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('13.0')) - allow_any_instance_of(GeoPgHelper).to receive(:database_version).and_return(PGVersion.new('13.0')) - end - - it 'configures wal_keep_size instead of wal_keep_segments' do - expect(chef_run).to render_file(runtime_conf).with_content { |content| - expect(content).to include("wal_keep_size") - expect(content).not_to include("wal_keep_segments") - } - end - end - - context 'postgresql 12' do - let(:runtime_conf) { '/var/opt/gitlab/geo-postgresql/data/runtime.conf' } - - before do - allow_any_instance_of(GeoPgHelper).to receive(:version).and_return(PGVersion.new('12.0')) - allow_any_instance_of(GeoPgHelper).to receive(:database_version).and_return(PGVersion.new('12.0')) - end - - it 'configures wal_keep_segments instead of wal_keep_size' do - expect(chef_run).to render_file(runtime_conf).with_content { |content| - expect(content).to include("wal_keep_segments") - expect(content).to_not include("wal_keep_size") - } - end - end - - context 'in gitlab-rails' do - it 'disables auto_migrate' do - expect(node['gitlab']['gitlab-rails']['auto_migrate']).to eq(false) - end - end - end -end diff --git a/spec/chef/gitlab-ee/recipes/sentinel_spec.rb b/spec/chef/gitlab-ee/recipes/sentinel_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZWNpcGVzL3NlbnRpbmVsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/recipes/sentinel_spec.rb +++ /dev/null @@ -1,186 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::redis' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(sentinel_service runit_service)).converge('gitlab-ee::default') } - let(:redis_master_ip) { '1.1.1.1' } - let(:redis_announce_ip) { '10.10.10.10' } - let(:redis_master_password) { 'blahblahblah' } - let(:sentinel_conf) { '/var/opt/gitlab/sentinel/sentinel.conf' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'When sentinel is disabled' do - before do - stub_gitlab_rb( - redis: { - master_ip: redis_master_ip, - announce_ip: redis_announce_ip, - master_password: redis_master_password - }, - redis_sentinel_role: { - enable: false, - } - ) - end - - it_behaves_like 'disabled runit service', 'sentinel', 'root', 'root' - end - - describe 'When sentinel is enabled' do - context 'default values' do - before do - stub_gitlab_rb( - redis: { - master_ip: redis_master_ip, - announce_ip: redis_announce_ip, - master_password: redis_master_password - }, - redis_sentinel_role: { - enable: true, - } - ) - end - it 'creates redis user and group' do - expect(chef_run).to create_account('user and group for sentinel').with(username: 'gitlab-redis', groupname: 'gitlab-redis') - end - - it 'renders sentinel config file with default values' do - expect(chef_run).to render_file('/var/opt/gitlab/sentinel/sentinel.conf') - .with_content { |content| - expect(content).to match(%r{bind 0.0.0.0}) - expect(content).to match(%r{port 26379}) - expect(content).to match(%r{sentinel announce-ip 10.10.10.10}) - expect(content).to match(%r{sentinel monitor gitlab-redis 1.1.1.1 6379 1}) - expect(content).to match(%r{sentinel down-after-milliseconds gitlab-redis 10000}) - expect(content).to match(%r{sentinel failover-timeout gitlab-redis 60000}) - expect(content).to match(%r{sentinel auth-pass gitlab-redis blahblahblah}) - expect(content).not_to match(%r{^tls}) - expect(content).to match(%r{SENTINEL resolve-hostnames no}) - expect(content).to match(%r{SENTINEL announce-hostnames no}) - } - end - - it_behaves_like 'enabled runit service', 'sentinel', 'root', 'root' - - context 'user overrides sentinel_use_hostnames' do - before do - stub_gitlab_rb( - sentinel: { - use_hostnames: true - } - ) - end - - it 'uses hostnames' do - expect(chef_run).to render_file(sentinel_conf).with_content { |content| - expect(content).to match(%r{SENTINEL resolve-hostnames yes}) - expect(content).to match(%r{SENTINEL announce-hostnames yes}) - } - end - end - end - - context 'user specified values' do - before do - stub_gitlab_rb( - redis_sentinel_role: { - enable: true, - }, - redis: { - username: 'foo', - group: 'bar', - master_ip: redis_master_ip, - announce_ip: 'fake.hostname.local', - master_password: redis_master_password - } - ) - end - it 'creates redis user and group' do - expect(chef_run).to create_account('user and group for sentinel').with(username: 'foo', groupname: 'bar') - end - - it_behaves_like 'enabled runit service', 'sentinel', 'root', 'root' - - it 'uses hostnames' do - expect(chef_run).to render_file(sentinel_conf).with_content { |content| - expect(content).to match(%r{SENTINEL resolve-hostnames yes}) - expect(content).to match(%r{SENTINEL announce-hostnames yes}) - } - end - - context 'user overrides sentinel_use_hostnames' do - before do - stub_gitlab_rb( - sentinel: { - use_hostnames: false - } - ) - end - - it 'does not use hostnames' do - expect(chef_run).to render_file(sentinel_conf).with_content { |content| - expect(content).to match(%r{SENTINEL resolve-hostnames no}) - expect(content).to match(%r{SENTINEL announce-hostnames no}) - } - end - end - end - - context 'with tls settings specified' do - before do - stub_gitlab_rb( - redis: { - master_ip: redis_master_ip, - announce_ip: redis_announce_ip, - master_password: redis_master_password, - }, - redis_sentinel_role: { - enable: true - }, - sentinel: { - tls_port: 6380, - tls_cert_file: '/etc/gitlab/ssl/redis.crt', - tls_key_file: '/etc/gitlab/ssl/redis.key', - tls_dh_params_file: '/etc/gitlab/ssl/redis-dhparams', - tls_ca_cert_file: '/etc/gitlab/ssl/redis-ca.crt', - tls_ca_cert_dir: '/opt/gitlab/embedded/ssl/certs', - tls_auth_clients: 'no', - tls_replication: 'yes', - tls_cluster: 'yes', - tls_protocols: 'TLSv1.2 TLSv1.3', - tls_ciphers: 'DEFAULT:!MEDIUM', - tls_ciphersuites: 'TLS_CHACHA20_POLY1305_SHA256', - tls_prefer_server_ciphers: 'yes', - tls_session_caching: 'no', - tls_session_cache_size: 10000, - tls_session_cache_timeout: 120 - } - ) - end - - it 'renders sentinel config file with specified tls values' do - expect(chef_run).to render_file('/var/opt/gitlab/sentinel/sentinel.conf') - .with_content { |content| - expect(content).to match(%r{^tls-port 6380$}) - expect(content).to match(%r{^tls-cert-file /etc/gitlab/ssl/redis.crt$}) - expect(content).to match(%r{^tls-key-file /etc/gitlab/ssl/redis.key$}) - expect(content).to match(%r{^tls-dh-params-file /etc/gitlab/ssl/redis-dhparams$}) - expect(content).to match(%r{^tls-ca-cert-file /etc/gitlab/ssl/redis-ca.crt$}) - expect(content).to match(%r{^tls-ca-cert-dir /opt/gitlab/embedded/ssl/certs$}) - expect(content).to match(%r{^tls-auth-clients no$}) - expect(content).to match(%r{^tls-replication yes$}) - expect(content).to match(%r{^tls-cluster yes$}) - expect(content).to match(%r{^tls-protocols "TLSv1.2 TLSv1.3"$}) - expect(content).to match(%r{^tls-ciphers DEFAULT:!MEDIUM$}) - expect(content).to match(%r{^tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256$}) - expect(content).to match(%r{^tls-prefer-server-ciphers yes$}) - expect(content).to match(%r{^tls-session-caching no$}) - expect(content).to match(%r{^tls-session-cache-size 10000$}) - expect(content).to match(%r{^tls-session-cache-timeout 120$}) - } - end - end - end -end diff --git a/spec/chef/gitlab-ee/resources/pgbouncer_user_spec.rb b/spec/chef/gitlab-ee/resources/pgbouncer_user_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1lZS9yZXNvdXJjZXMvcGdib3VuY2VyX3VzZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-ee/resources/pgbouncer_user_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'chef_helper' -RSpec.describe 'pgbouncer_user' do - let(:runner) { ChefSpec::SoloRunner.new(step_into: ['pgbouncer_user']) } - - context 'create geo' do - let(:chef_run) { runner.converge('test_gitlab_ee::pgbouncer_user_create_geo') } - it 'should create the pgbouncer user' do - expect(chef_run).to create_postgresql_user('pgbouncer-geo').with( - helper: an_instance_of(GeoPgHelper), - username: 'pgbouncer-geo', - password: 'md5fakepassword-geo' - ) - end - - it 'should create the pg_shadow_lookup function' do - postgresql_user = chef_run.postgresql_user('pgbouncer-geo') - expect(postgresql_user).to notify('execute[Add pgbouncer auth function]') - resource = chef_run.execute('Add pgbouncer auth function') - expect(resource.command).to match(%r{^/opt/gitlab/bin/gitlab-geo-psql -d fakedb-geo}) - end - - it 'should not try and recreate the function' do - expect(chef_run).not_to run_execute('Add pgbouncer auth function') - end - end - - context 'create rails' do - let(:chef_run) { runner.converge('test_gitlab_ee::pgbouncer_user_create_rails') } - it 'should create the pgbouncer user' do - expect(chef_run).to create_postgresql_user('pgbouncer-rails').with( - helper: an_instance_of(PgHelper), - username: 'pgbouncer-rails', - password: 'md5fakepassword-rails' - ) - end - - it 'should create the pg_shadow_lookup function' do - postgresql_user = chef_run.postgresql_user('pgbouncer-rails') - expect(postgresql_user).to notify('execute[Add pgbouncer auth function]') - resource = chef_run.execute('Add pgbouncer auth function') - expect(resource.command).to match(%r{^/opt/gitlab/bin/gitlab-psql -d fakedb-rails}) - end - - it 'should not try and recreate the function' do - expect(chef_run).not_to run_execute('Add pgbouncer auth function') - end - end -end diff --git a/spec/chef/gitlab-pages/recipes/gitlab-pages_spec.rb b/spec/chef/gitlab-pages/recipes/gitlab-pages_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi1wYWdlcy9yZWNpcGVzL2dpdGxhYi1wYWdlc19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab-pages/recipes/gitlab-pages_spec.rb +++ /dev/null @@ -1,333 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-pages' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service env_dir)).converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(Etc).to receive(:getpwnam).with('git').and_return(spy('getpwnam spy', uid: 1000, gid: 1000)) - end - - context 'with default values' do - it 'does not include Pages recipe' do - expect(chef_run).not_to include_recipe('gitlab-pages::enable') - expect(chef_run).to include_recipe('gitlab-pages::disable') - end - end - - context 'with Pages enabled' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com' - ) - end - - it 'includes Pages recipe' do - expect(chef_run).to include_recipe('gitlab-pages::enable') - end - - it 'creates a VERSION file and restarts the service' do - expect(chef_run).to create_version_file('Create version file for Gitlab Pages').with( - version_file_path: '/var/opt/gitlab/gitlab-pages/VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/gitlab-pages --version' - ) - - expect(chef_run.version_file('Create version file for Gitlab Pages')).to notify('runit_service[gitlab-pages]').to(:restart) - end - - it 'renders the env dir files' do - expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-pages/env/SSL_CERT_DIR') - .with_content('/opt/gitlab/embedded/ssl/certs') - end - - it 'renders the pages service run file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-config="/var/opt/gitlab/gitlab-pages/gitlab-pages-config"}) - end - - it 'renders the pages log run file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/log/run").with_content(%r{exec svlogd /var/log/gitlab/gitlab-pages}) - end - - it 'deletes old admin.secret file' do - expect(chef_run).to delete_file("/var/opt/gitlab/gitlab-pages/admin.secret") - end - - it 'renders pages config file' do - default_content = <<~EOS - pages-domain=pages.example.com - pages-root=/var/opt/gitlab/gitlab-rails/shared/pages - api-secret-key=/var/opt/gitlab/gitlab-pages/.gitlab_pages_secret - listen-proxy=localhost:8090 - log-format=json - use-http2=true - artifacts-server=https://gitlab.example.com/api/v4 - artifacts-server-timeout=10 - gitlab-server=https://gitlab.example.com - EOS - - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content(default_content) - end - - it 'skips rendering the auth settings when access control is disabled' do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_pages: { - access_control: false, - auth_secret: 'auth_secret' - } - ) - - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content| - expect(content).not_to match(%r{auth-secret=auth_secret}) - } - end - - context 'when access control is enabled' do - context 'when access control secrets are not specified' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_pages: { - access_control: true, - auth_secret: 'auth_secret' - } - ) - - allow(GitlabPages).to receive(:authorize_with_gitlab) { - Gitlab['gitlab_pages']['gitlab_secret'] = 'app_secret' - Gitlab['gitlab_pages']['gitlab_id'] = 'app_id' - } - end - - it 'authorizes Pages with GitLab' do - expect(chef_run).to run_ruby_block('authorize pages with gitlab') - .at_converge_time - expect(chef_run).to run_ruby_block('re-populate GitLab Pages configuration options') - .at_converge_time - expect(GitlabPages).to receive(:authorize_with_gitlab) - - chef_run.ruby_block('authorize pages with gitlab').block.call - chef_run.ruby_block('re-populate GitLab Pages configuration options').block.call - - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content| - expect(content).to match(%r{auth-client-id=app_id}) - expect(content).to match(%r{auth-client-secret=app_secret}) - expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com/auth}) - expect(content).to match(%r{auth-secret=auth_secret}) - } - end - end - - context 'when access control secrets are specified' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_pages: { - access_control: true, - gitlab_id: 'app_id', - gitlab_secret: 'app_secret', - auth_secret: 'auth_secret', - auth_redirect_uri: 'https://projects.pages.example.com/auth', - auth_scope: 'read_api' - } - ) - end - - it 'does not attempt to authorize with GitLab' do - expect(chef_run).not_to run_ruby_block('authorize pages with gitlab') - end - - it 'renders pages config file' do - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content| - expect(content).to match(%r{auth-client-id=app_id}) - expect(content).to match(%r{auth-client-secret=app_secret}) - expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com/auth}) - expect(content).to match(%r{auth-secret=auth_secret}) - expect(content).to match(%r{auth-scope=read_api}) - } - end - end - end - - context 'with custom port' do - before do - stub_gitlab_rb( - pages_external_url: 'https://pages.example.com:8443', - gitlab_pages: { - access_control: true - } - ) - end - - it 'sets the correct port number' do - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content| - expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com:8443/auth}) - } - end - end - - context 'with custom values' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_pages: { - external_http: ['external_pages.example.com', 'localhost:9000'], - external_https: ['external_pages.example.com', 'localhost:9001'], - external_https_proxyv2: ['external_pages.example.com', 'localhost:9002'], - metrics_address: 'localhost:1234', - redirect_http: true, - dir: '/var/opt/gitlab/pages', - cert: '/etc/gitlab/pages.crt', - artifacts_server_url: "https://gitlab.elsewhere.com/api/v5", - artifacts_server_timeout: 60, - status_uri: '/@status', - max_connections: 7500, - max_uri_length: 2048, - propagate_correlation_id: true, - log_format: 'text', - log_verbose: true, - gitlab_id: 'app_id', - gitlab_secret: 'app_secret', - auth_secret: 'auth_secret', - auth_redirect_uri: 'https://projects.pages.example.com/auth', - auth_scope: 'read_api', - access_control: true, - insecure_ciphers: true, - tls_min_version: "tls1.0", - tls_max_version: "tls1.2", - sentry_enabled: true, - sentry_dsn: 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40', - sentry_environment: 'production', - headers: ['X-XSS-Protection: 1; mode=block', 'X-Content-Type-Options: nosniff', 'Test: Header'], - server_shutdown_timeout: "30s", - gitlab_client_http_timeout: "10s", - gitlab_client_jwt_expiry: "30s", - zip_cache_expiration: "120s", - zip_cache_cleanup: "1m", - zip_cache_refresh: "60s", - zip_open_timeout: "45s", - zip_http_client_timeout: "30m", - internal_gitlab_server: "https://int.gitlab.example.com", - gitlab_cache_expiry: "1m", - gitlab_cache_refresh: "500ms", - gitlab_cache_cleanup: "100ms", - gitlab_retrieval_timeout: "3s", - gitlab_retrieval_interval: "500ms", - gitlab_retrieval_retries: 5, - rate_limit_source_ip: 100, - rate_limit_source_ip_burst: 50, - rate_limit_domain: 1000, - rate_limit_domain_burst: 10000, - rate_limit_tls_source_ip: 101, - rate_limit_tls_source_ip_burst: 51, - rate_limit_tls_domain: 1001, - rate_limit_tls_domain_burst: 10001, - enable_disk: true, - env: { - GITLAB_CONTINUOUS_PROFILING: "stackdriver?service=gitlab-pages", - }, - } - ) - end - - it 'renders pages config file in the specified directory' do - expected_content = <<~EOS - pages-domain=pages.example.com - pages-root=/var/opt/gitlab/gitlab-rails/shared/pages - api-secret-key=/var/opt/gitlab/pages/.gitlab_pages_secret - auth-client-id=app_id - auth-redirect-uri=https://projects.pages.example.com/auth - auth-client-secret=app_secret - auth-secret=auth_secret - auth-scope=read_api - zip-cache-expiration=120s - zip-cache-cleanup=1m - zip-cache-refresh=60s - zip-open-timeout=45s - zip-http-client-timeout=30m - listen-proxy=localhost:8090 - metrics-address=localhost:1234 - pages-status=/@status - max-conns=7500 - max-uri-length=2048 - propagate-correlation-id=true - log-format=text - log-verbose - sentry-dsn=https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40 - sentry-environment=production - redirect-http=true - use-http2=true - artifacts-server=https://gitlab.elsewhere.com/api/v5 - artifacts-server-timeout=60 - gitlab-server=https://gitlab.example.com - internal-gitlab-server=https://int.gitlab.example.com - insecure-ciphers - tls-min-version=tls1.0 - tls-max-version=tls1.2 - server-shutdown-timeout=30s - gitlab-client-http-timeout=10s - gitlab-client-jwt-expiry=30s - listen-http=external_pages.example.com,localhost:9000 - listen-https=external_pages.example.com,localhost:9001 - listen-https-proxyv2=external_pages.example.com,localhost:9002 - root-cert=/etc/gitlab/pages.crt - root-key=/etc/gitlab/ssl/pages.example.com.key - gitlab-cache-expiry=1m - gitlab-cache-refresh=500ms - gitlab-cache-cleanup=100ms - gitlab-retrieval-timeout=3s - gitlab-retrieval-timeout=500ms - gitlab-retrieval-retries=5 - enable-disk=true - rate-limit-source-ip=100 - rate-limit-source-ip-burst=50 - rate-limit-domain=1000 - rate-limit-domain-burst=10000 - rate-limit-tls-source-ip=101 - rate-limit-tls-source-ip-burst=51 - rate-limit-tls-domain=1001 - rate-limit-tls-domain-burst=10001 - EOS - - expect(chef_run).to render_file("/var/opt/gitlab/pages/gitlab-pages-config").with_content(expected_content) - end - - it 'specifies headers as arguments in the run file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-header="X-XSS-Protection: 1; mode=block"}) - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-header="X-Content-Type-Options: nosniff"}) - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-header="Test: Header"}) - end - - it 'renders the env dir files' do - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-pages/env/GITLAB_CONTINUOUS_PROFILING") - .with_content('stackdriver?service=gitlab-pages') - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-pages/env/SSL_CERT_DIR") - .with_content('/opt/gitlab/embedded/ssl/certs') - end - end - - describe 'logrotate settings' do - context 'default values' do - it_behaves_like 'configured logrotate service', 'gitlab-pages', 'git', 'git' - end - - context 'specified username and group' do - before do - stub_gitlab_rb( - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'configured logrotate service', 'gitlab-pages', 'foo', 'bar' - end - end - end -end diff --git a/spec/chef/gitlab/libraries/account_helper_spec.rb b/spec/chef/gitlab/libraries/account_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi9saWJyYXJpZXMvYWNjb3VudF9oZWxwZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab/libraries/account_helper_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'chef_helper' - -RSpec.describe AccountHelper do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - it 'returns a list of users' do - expect(AccountHelper.new(chef_run.node).users).to eq( - %w(git gitlab-www gitlab-redis gitlab-psql mattermost registry gitlab-prometheus gitlab-consul) - ) - end - - it 'returns a list of groups' do - expect(AccountHelper.new(chef_run.node).groups).to eq( - %w(git gitlab-www gitlab-redis gitlab-psql mattermost registry gitlab-consul gitlab-prometheus) - ) - end -end diff --git a/spec/chef/gitlab/libraries/rails_migration_helper_spec.rb b/spec/chef/gitlab/libraries/rails_migration_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi9saWJyYXJpZXMvcmFpbHNfbWlncmF0aW9uX2hlbHBlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab/libraries/rails_migration_helper_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'chef_helper' - -RSpec.describe RailsMigrationHelper do - cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - subject(:helper) { described_class.new(chef_run.node) } - - context '#migrated?' do - it 'returns true if status file exists and content is "0"' do - allow(helper).to receive(:db_migrate_status_file) { File.join(fixture_path, 'migration/successful-migration-status-file') } - - expect(helper.migrated?).to be_truthy - end - - it 'returns false when status file doesnt exist' do - allow(helper).to receive(:db_migrate_status_file) { File.join(fixture_path, 'migration/non-existent-migration') } - - expect(helper.migrated?).to be_falsey - end - - it 'returns false when content in status file is different from "0"' do - allow(helper).to receive(:db_migrate_status_file) { File.join(fixture_path, 'migration/failed-migration-status-file') } - - expect(helper.migrated?).to be_falsey - end - end - - context '#db_migrate_status_file' do - it 'returns a path including status_file_prefix connection_digest and revision' do - fake_connection_digest = '63467bb60aa187d2a6830aa8f1b5cbe0' - fake_revision = 'f9631484e7f' - expected_status_file = "/var/opt/gitlab/gitlab-rails/upgrade-status/db-migrate-#{fake_connection_digest}-#{fake_revision}" - - allow(helper).to receive(:connection_digest) { fake_connection_digest } - allow(helper).to receive(:revision) { fake_revision } - - expect(helper.db_migrate_status_file).to eq(expected_status_file) - end - end -end diff --git a/spec/chef/gitlab/recipes/show_config_spec.rb b/spec/chef/gitlab/recipes/show_config_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi9yZWNpcGVzL3Nob3dfY29uZmlnX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab/recipes/show_config_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::show_config' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::show_config') } - - it 'outputs user-defined gitlab.rb configuration to stdout' do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb( - patroni: { scope: 'test-cluster' } - ) - - expect { chef_run }.to output(/"scope": "test-cluster"/).to_stdout - end - - it 'outputs GitlabCluster defined attributes to stdout' do - GitlabCluster.config.set('primary', true) - - expect { chef_run }.to output(/"primary": true/).to_stdout - end -end diff --git a/spec/chef/gitlab/resources/puma_config_spec.rb b/spec/chef/gitlab/resources/puma_config_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi9yZXNvdXJjZXMvcHVtYV9jb25maWdfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab/resources/puma_config_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'puma_config' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(puma_config)) - end - - context 'create' do - let(:chef_run) { runner.converge('test_gitlab::puma_config_create') } - - it 'creates necessary directories' do - expect(chef_run).to create_directory('/var/opt/gitlab/gitlab-rails/etc') - end - - it 'renders puma.rb file' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| - expect(content).to match(%r(^environment 'production')) - expect(content).to match(%r(^rackup '/opt/gitlab/embedded/service/gitlab-rails/config.ru')) - expect(content).to match(%r(^pidfile '/opt/gitlab/var/puma/puma.pid')) - expect(content).to match(%r(^state_path '/opt/gitlab/var/puma/puma.state')) - expect(content).to match(%r(^bind 'unix:///var/opt/gitlab/gitlab-rails/sockets/gitlab.socket')) - expect(content).to match(%r(^bind 'tcp://127.0.0.1:8080')) - expect(content).to match(%r(^directory '/var/opt/gitlab/gitlab-rails/working')) - expect(content).to match(%r(^require_relative "/opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/cluster/lifecycle_events"$)) - expect(content).to match(/^options = { workers: 2 }$/) - expect(content).to match(%r(Gitlab::Cluster::PumaWorkerKillerInitializer.start\(options\))) - expect(content).to match(/^preload_app!$/) - expect(content).to match(%r(^require_relative "/opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/puma_logging/json_formatter"$)) - } - end - end - - context 'create with default puma config' do - let(:chef_run) { runner.converge('test_gitlab::puma_config_create') } - - it 'renders puma.rb file' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| - expect(content).to match(%r(rackup '/opt/gitlab/embedded/service/gitlab-rails)) - } - end - end - - context 'create with custom Puma settings' do - let(:chef_run) { runner.converge('test_gitlab::puma_config_custom') } - - it 'renders puma.rb file' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| - expect(content).to match(%r(Gitlab::Cluster::PumaWorkerKillerInitializer.start\(options, puma_per_worker_max_memory_mb: 1000\))) - expect(content).to match(%r(rackup '/opt/custom/gitlab/embedded/service/gitlab-rails)) - } - end - end -end diff --git a/spec/chef/gitlab/resources/rails_migration_spec.rb b/spec/chef/gitlab/resources/rails_migration_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2dpdGxhYi9yZXNvdXJjZXMvcmFpbHNfbWlncmF0aW9uX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/gitlab/resources/rails_migration_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'rails_migration' do - let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(rails_migration)) } - - before do - allow_any_instance_of(PgHelper).to receive(:postgresql_user).and_return('fakeuser') - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'run' do - let(:chef_run) { runner.converge('test_gitlab::rails_migration_run') } - let(:bash_block) { chef_run.bash('migrate gitlab-test database') } - let(:migration_block) { chef_run.rails_migration('gitlab-test') } - - context 'when database has not been migrated' do - before do - allow_any_instance_of(GitlabGeoHelper).to receive(:migrated?).and_return(false) - end - - it 'executes the bash script migration' do - expect(chef_run).to run_bash('migrate gitlab-test database') - end - - it 'restarts dependent_services' do - expect(bash_block).to notify('ruby_block[test-dependent]').to(:restart) - end - end - - context 'when database has been migrated already' do - it 'doesnt execute the bash script' do - expect_any_instance_of(RailsMigrationHelper).to receive(:migrated?) { true } - - expect(chef_run).not_to run_bash('migrate gitlab-test database') - end - end - - context 'bash script' do - it 'defines log file based on logfile_prefix' do - log_file = %(log_file="/var/log/gitlab/gitlab-rails/gitlab-test-db-migrate-$(date +%Y-%m-%d-%H-%M-%S).log") - - expect(bash_block.code).to include(log_file) - end - - it 'passes appropriate environment variables through' do - expect(bash_block.environment).to match(hash_including('SOME_ENV')) - end - - it 'triggers provided rake_task' do - migrate = %(/opt/gitlab/bin/gitlab-rake gitlab:db:configure 2>& 1 | tee ${log_file}) - - expect(bash_block.code).to match(/#{migrate}/) - end - - it 'pipes exit code to db-migrate file' do - fake_connection_digest = '63467bb60aa187d2a6830aa8f1b5cbe0' - fake_revision = 'f9631484e7f' - pipe = %(echo $STATUS > /var/opt/gitlab/gitlab-rails/upgrade-status/db-migrate-#{fake_connection_digest}-#{fake_revision}) - - expect_any_instance_of(RailsMigrationHelper).to receive(:connection_digest) { fake_connection_digest } - expect_any_instance_of(RailsMigrationHelper).to receive(:revision) { fake_revision } - - expect(bash_block.code).to include(pipe) - end - end - end -end diff --git a/spec/chef/letsencrypt/resources/letsencrypt_certificate_spec.rb b/spec/chef/letsencrypt/resources/letsencrypt_certificate_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2xldHNlbmNyeXB0L3Jlc291cmNlcy9sZXRzZW5jcnlwdF9jZXJ0aWZpY2F0ZV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/letsencrypt/resources/letsencrypt_certificate_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::letsencrypt' do - let(:chef_run) do - ChefSpec::SoloRunner.new(step_into: %(letsencrypt_certificate)).converge('gitlab::default') - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb( - external_url: 'https://fakehost.example.com', - letsencrypt: { - enable: true, - } - ) - end - - context 'with NGINX running' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('nginx').and_return(true) - end - - it 'creates a staging certificate' do - expect(chef_run).to create_acme_certificate('staging').with( - crt: '/etc/gitlab/ssl/fakehost.example.com.crt-staging', - key: '/etc/gitlab/ssl/fakehost.example.com.key-staging', - wwwroot: '/var/opt/gitlab/nginx/www', - dir: 'https://acme-staging-v02.api.letsencrypt.org/directory', - sensitive: true - ) - end - - it "updates the node['acme']['private_key'] attribute" do - expect(chef_run).to run_ruby_block('reset private key') - end - - it 'creates a production certificate' do - expect(chef_run).to create_acme_certificate('production').with( - crt: '/etc/gitlab/ssl/fakehost.example.com.crt', - key: '/etc/gitlab/ssl/fakehost.example.com.key', - wwwroot: '/var/opt/gitlab/nginx/www', - sensitive: true - ) - end - - context 'specifying a different key_size' do - before do - allow(File).to receive(:file?).and_call_original - allow(File).to receive(:file?).with('/etc/gitlab/ssl/fakehost.example.com.key').and_return(true) - allow(File).to receive(:read).with(anything).and_call_original - allow(File).to receive(:read).with('/etc/gitlab/ssl/fakehost.example.com.key').and_return(OpenSSL::PKey::RSA.new(2048)) - - stub_gitlab_rb( - external_url: 'https://fakehost.example.com', - letsencrypt: { - enable: true, - key_size: 3072 - } - ) - end - - it 'deletes and recreates the SSL files' do - expect(chef_run).to delete_file('/etc/gitlab/ssl/fakehost.example.com.key') - expect(chef_run).to delete_file('/etc/gitlab/ssl/fakehost.example.com.crt') - - expect(chef_run).to create_acme_certificate('production').with( - crt: '/etc/gitlab/ssl/fakehost.example.com.crt', - key: '/etc/gitlab/ssl/fakehost.example.com.key', - key_size: 3072, - wwwroot: '/var/opt/gitlab/nginx/www', - sensitive: true - ) - end - end - - it 'reloads nginx' do - prod_cert = chef_run.acme_certificate('production') - expect(prod_cert).to notify('execute[reload nginx]').to(:run) - end - - context 'with extra options' do - before do - stub_gitlab_rb( - external_url: 'https://fakehost.example.com', - letsencrypt: { - enable: true, - alt_names: %w(one.example.com two.example.com), - contact_emails: %w(foo@bar.com one@two.com) - } - ) - end - - it 'adds alt_names to the certificate resource' do - expect(chef_run).to create_acme_certificate('production') - .with( - alt_names: %w(one.example.com two.example.com), - contact: %w(mailto:foo@bar.com mailto:one@two.com) - ) - end - end - end - - context 'when NGINX is not running' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('nginx').and_return(false) - end - - it 'does not attempt to create a certificate' do - expect(chef_run).not_to create_acme_certificate('staging') - expect(chef_run).not_to create_acme_certificate('production') - end - end -end diff --git a/spec/chef/logrotate/recipes/folders_and_configs_spec.rb b/spec/chef/logrotate/recipes/folders_and_configs_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2xvZ3JvdGF0ZS9yZWNpcGVzL2ZvbGRlcnNfYW5kX2NvbmZpZ3Nfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/logrotate/recipes/folders_and_configs_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::logrotate_folder_and_configs_spec' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when logrotate is enabled' do - it 'creates default set of directories' do - expect(chef_run.node['logrotate']['dir']) - .to eql('/var/opt/gitlab/logrotate') - expect(chef_run.node['logrotate']['log_directory']) - .to eql('/var/log/gitlab/logrotate') - - expect(chef_run).to create_directory('/var/opt/gitlab/logrotate').with( - owner: nil, - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/var/log/gitlab/logrotate').with( - owner: nil, - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/var/opt/gitlab/logrotate/logrotate.d').with( - owner: nil, - group: nil, - mode: '0700' - ) - end - - it 'creates logrotate directories in alternative locations' do - stub_gitlab_rb(logrotate: { dir: "/tmp/logrotate", log_directory: "/tmp/logs/logrotate" }) - - expect(chef_run).to create_directory('/tmp/logrotate').with( - owner: nil, - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/tmp/logs/logrotate').with( - owner: nil, - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/tmp/logrotate/logrotate.d').with( - owner: nil, - group: nil, - mode: '0700' - ) - end - - it 'creates default set of configuration templates' do - expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/nginx') - expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-rails') - expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-shell') - expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-workhorse') - expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-pages') - expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitaly') - end - - it 'populates configuration template with default values' do - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/daily/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/rotate 30/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/compress/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/copytruncate/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/notifempty/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/postrotate/) - expect(chef_run).not_to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/size/) - expect(chef_run).not_to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/maxsize/) - expect(chef_run).not_to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/dateext/) - end - - it 'populates configuration template with custom values when logrotate is disabled' do - stub_gitlab_rb(logging: - { logrotate_frequency: "weekly", - logrotate_size: "50", - logrotate_maxsize: "50", - logrotate_rotate: "50", - logrotate_compress: "nocompress", - logrotate_method: "copy", - logrotate_postrotate: "/usr/bin/killall -HUP nginx", - logrotate_dateformat: "-%Y-%m-%d", }, logrotate: { enable: false }) - - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/weekly/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/rotate 50/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/nocompress/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/copy/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/postrotate/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/size 50/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/maxsize 50/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/dateext/) - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/nginx') - .with_content(/dateformat -%Y-%m-%d/) - end - - context 'when services not under gitlab key are specified' do - it 'populates files correctly' do - stub_gitlab_rb( - logrotate: { - services: ['gitlab-rails', 'gitaly'] - }, - gitaly: { - log_directory: '/my/log/directory' - } - ) - expect(chef_run).to create_template('/var/opt/gitlab/logrotate/logrotate.d/gitlab-rails') - expect(chef_run).to render_file('/var/opt/gitlab/logrotate/logrotate.d/gitaly') - .with_content(/my\/log\/directory\/\*\.log/) - end - end - - context 'when services that are not supported are specified' do - it 'raises an error' do - stub_gitlab_rb( - logrotate: { - services: ['gitlab-rails', 'foo-bar'] - } - ) - - expect { chef_run }.to raise_error("Service foo-bar was specified in logrotate['services'], but is not a valid service.") - end - end - end -end diff --git a/spec/chef/logrotate/recipes/logrotate_spec.rb b/spec/chef/logrotate/recipes/logrotate_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL2xvZ3JvdGF0ZS9yZWNpcGVzL2xvZ3JvdGF0ZV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/logrotate/recipes/logrotate_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'logrotate' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when logrotate is enabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/logrotate/log/config') } - - it_behaves_like "enabled runit service", "logrotate", "root", "root" - - it 'populates the files with expected configuration' do - expect(config_template).to notify('ruby_block[reload_log_service]') - - expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/run') - .with_content(/cd \/var\/opt\/gitlab\/logrotate/) - expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/run') - .with_content(/exec \/opt\/gitlab\/embedded\/bin\/chpst -P \/usr\/bin\/env/) - expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/run') - .with_content(/\/opt\/gitlab\/embedded\/bin\/gitlab-logrotate-wrapper/) - - expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/logrotate/) - - expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/config') - .with_content(/s209715200/) - expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/config') - .with_content(/n30/) - expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/config') - .with_content(/t86400/) - expect(chef_run).to render_file('/opt/gitlab/sv/logrotate/log/config') - .with_content(/!gzip/) - end - - it 'executes start command' do - expect(chef_run).to run_execute('/opt/gitlab/bin/gitlab-ctl start logrotate').with(retries: 20) - end - end - - context 'when logrotate is disabled' do - before do - stub_gitlab_rb(logrotate: { enable: false }) - end - - it_behaves_like "disabled runit service", "logrotate" - - it 'does not execute the start command' do - expect(chef_run).not_to run_execute('/opt/gitlab/bin/gitlab-ctl start logrotate').with(retries: 20) - end - end -end diff --git a/spec/chef/monitoring/libraries/prometheus_helper_spec.rb b/spec/chef/monitoring/libraries/prometheus_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvbGlicmFyaWVzL3Byb21ldGhldXNfaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/libraries/prometheus_helper_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright:: Copyright (c) 2017 GitLab Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'chef_helper' - -RSpec.describe PrometheusHelper do - let(:chef_run) { ChefSpec::SoloRunner.new } - subject { described_class.new(chef_run.node) } - - context 'flags for prometheus' do - context 'with default options' do - it 'returns the correct default config string' do - chef_run.converge('gitlab::default') - expect(subject.flags('prometheus')).to eq( - '--web.listen-address=localhost:9090 --storage.tsdb.path=/var/opt/gitlab/prometheus/data --config.file=/var/opt/gitlab/prometheus/prometheus.yml') - end - end - - context 'with custom options' do - before { allow(Gitlab).to receive(:[]).and_call_original } - - it 'does not return the correct string if any attributes have been changed' do - chef_run.node.normal['monitoring']['prometheus']['home'] = '/fake/dir' - chef_run.converge('gitlab::default') - - expect(subject.flags('prometheus')).to eq( - '--web.listen-address=localhost:9090 --storage.tsdb.path=/fake/dir/data --config.file=/fake/dir/prometheus.yml') - end - end - end -end diff --git a/spec/chef/monitoring/libraries/prometheus_spec.rb b/spec/chef/monitoring/libraries/prometheus_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvbGlicmFyaWVzL3Byb21ldGhldXNfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/libraries/prometheus_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'chef_helper' - -RSpec.describe Prometheus do - before { Services.add_services('gitlab', Services::BaseServices.list) } - - it 'should return a list of known services' do - expect(Prometheus.services).to match_array(%w( - prometheus - alertmanager - grafana - node-exporter - redis-exporter - postgres-exporter - gitlab-exporter - )) - end -end diff --git a/spec/chef/monitoring/recipes/alertmanager_spec.rb b/spec/chef/monitoring/recipes/alertmanager_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvcmVjaXBlcy9hbGVydG1hbmFnZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/recipes/alertmanager_spec.rb +++ /dev/null @@ -1,149 +0,0 @@ -require 'chef_helper' - -alertmanager_yml_output = <<-ALERTMANAGERYML - --- - global: - smtp_from: gitlab-omnibus - smtp_smarthost: testhost:25 - templates: [] - route: - receiver: default-receiver - routes: [] - receivers: - - name: default-receiver - email_configs: - - to: admin@example.com - inhibit_rules: [] -ALERTMANAGERYML - -RSpec.describe 'monitoring::alertmanager' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when alertmanager is enabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/alertmanager/log/config') } - - before do - stub_gitlab_rb( - prometheus: { enable: true }, - alertmanager: { - enable: true, - admin_email: 'admin@example.com', - }, - gitlab_rails: { - gitlab_email_from: 'gitlab-omnibus', - smtp_enable: true, - smtp_address: 'testhost', - smtp_port: 25, - } - ) - end - - it_behaves_like 'enabled runit service', 'alertmanager', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/alertmanager/env').with_variables(default_vars) - end - - it 'populates the files with expected configuration' do - expect(config_template).to notify('ruby_block[reload_log_service]') - - expect(chef_run).to render_file('/opt/gitlab/sv/alertmanager/run') - .with_content { |content| - expect(content).to match(/exec chpst -P/) - expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/alertmanager/) - expect(content).to match(/alertmanager.yml/) - } - - expect(chef_run).to render_file('/var/opt/gitlab/alertmanager/alertmanager.yml') - .with_content(alertmanager_yml_output.gsub(/^ {2}/, '')) - - expect(chef_run).to render_file('/opt/gitlab/sv/alertmanager/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/alertmanager/) - end - - it 'creates default set of directories' do - expect(chef_run).to create_directory('/var/log/gitlab/alertmanager').with( - owner: 'gitlab-prometheus', - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/var/opt/gitlab/alertmanager').with( - owner: 'gitlab-prometheus', - group: nil, - mode: '0750' - ) - end - - it 'should create a gitlab-prometheus user account' do - expect(chef_run).to create_account('Prometheus user and group').with(username: 'gitlab-prometheus') - end - - it 'sets a default listen address' do - expect(chef_run).to render_file('/opt/gitlab/sv/alertmanager/run') - .with_content(/web.listen-address=localhost:9093/) - end - end - - context 'with user provided settings' do - before do - stub_gitlab_rb( - prometheus: { enable: true }, - alertmanager: { - listen_address: ':9093', - enable: true, - env: { - 'USER_SETTING' => 'asdf1234' - }, - global: { - 'smtp_from' => 'override_value' - } - }, - gitlab_rails: { - smtp_enable: true, - smtp_address: 'other-testhost', - smtp_port: 465, - smtp_from: 'default_value' - } - ) - end - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/alertmanager/run') - .with_content(/web.listen-address=:9093/) - end - - it 'keeps the defaults that the user did not override' do - expect(chef_run).to render_file('/var/opt/gitlab/alertmanager/alertmanager.yml') - .with_content(/receiver: default-receiver/) - end - - it 'renders alertmanager.yml with the non-default value' do - expect(chef_run).to render_file('/var/opt/gitlab/alertmanager/alertmanager.yml') - .with_content(/smtp_smarthost: other-testhost:465/) - end - - it 'renders alertmanager.yml with the user override value' do - expect(chef_run).to render_file('/var/opt/gitlab/alertmanager/alertmanager.yml') - .with_content(/smtp_from: override_value/) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/alertmanager/env').with_variables( - default_vars.merge( - { - 'USER_SETTING' => 'asdf1234' - } - ) - ) - end - end -end diff --git a/spec/chef/monitoring/recipes/gitlab-exporter_spec.rb b/spec/chef/monitoring/recipes/gitlab-exporter_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvcmVjaXBlcy9naXRsYWItZXhwb3J0ZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/recipes/gitlab-exporter_spec.rb +++ /dev/null @@ -1,151 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'monitoring::gitlab-exporter' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:default_env_vars) do - { - 'LD_PRELOAD' => '/opt/gitlab/embedded/lib/libjemalloc.so', - 'MALLOC_CONF' => 'dirty_decay_ms:0,muzzy_decay_ms:0', - 'RUBY_GC_HEAP_INIT_SLOTS' => 80000, - 'RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO' => 0.055, - 'RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO' => 0.111, - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - 'SSL_CERT_FILE' => '/opt/gitlab/embedded/ssl/cert.pem' - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when gitlab-exporter is enabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/gitlab-exporter/log/config') } - - before do - stub_gitlab_rb( - gitlab_exporter: { enable: true } - ) - end - - it 'creates a default VERSION file and restarts service' do - expect(chef_run).to create_version_file('Create version file for GitLab-Exporter').with( - version_file_path: '/var/opt/gitlab/gitlab-exporter/RUBY_VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/ruby --version' - ) - - expect(chef_run.version_file('Create version file for GitLab-Exporter')).to notify('runit_service[gitlab-exporter]').to(:restart) - end - - it_behaves_like 'enabled runit service', 'gitlab-exporter', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-exporter/env').with_variables(default_env_vars) - end - - it 'populates the files with expected configuration' do - expect(config_template).to notify('ruby_block[reload_log_service]') - - expect(chef_run).to render_file('/opt/gitlab/sv/gitlab-exporter/run') - .with_content { |content| - expect(content).to match(/exec chpst -P/) - expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/gitlab-exporter/) - } - - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-exporter/gitlab-exporter.yml') - .with_content { |content| - # Not disabling this Cop fails the test with: - # Psych::BadAlias: Unknown alias: db_common - settings = YAML.load(content) # rubocop:disable Security/YAMLLoad - expect(settings.dig('server', 'name')).to eq('webrick') - expect(settings.dig('probes', 'database')).not_to be_nil - expect(settings.dig('probes', 'ruby')).not_to be_nil - expect(settings.dig('probes', 'metrics', 'rows_count')).not_to be_nil - - expect(content).to match(/host=\/var\/opt\/gitlab\/postgresql/) - expect(content).to match(/redis_enable_client: true/) - } - - expect(chef_run).to render_file('/opt/gitlab/sv/gitlab-exporter/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/gitlab-exporter/) - end - - it 'creates default set of directories' do - expect(chef_run).to create_directory('/var/log/gitlab/gitlab-exporter').with( - owner: 'git', - group: nil, - mode: '0700' - ) - end - end - - context 'with custom user and group' do - before do - stub_gitlab_rb( - gitlab_exporter: { - enable: true - }, - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'enabled runit service', 'gitlab-exporter', 'root', 'root' - end - - context 'when gitlab-exporter is enabled and postgres is disabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/gitlab-exporter/log/config') } - - before do - stub_gitlab_rb( - gitlab_exporter: { enable: true }, - gitlab_rails: { db_host: 'postgres.example.com', db_port: '5432', db_password: 'secret' }, - postgresql: { enabled: false } - ) - end - - it 'populates a config with a remote host' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-exporter/gitlab-exporter.yml') - .with_content { |content| - expect(content).to match(/host=postgres\.example\.com/) - expect(content).to match(/port=5432/) - expect(content).to match(/password=secret/) - } - end - end - - context 'with custom Redis settings' do - before do - stub_gitlab_rb( - gitlab_exporter: { enable: true }, - gitlab_rails: { redis_enable_client: false } - ) - end - - it 'disables Redis CLIENT' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-exporter/gitlab-exporter.yml') - .with_content { |content| - expect(content).to match(/redis_enable_client: false/) - } - end - end - - context 'when log dir is changed' do - before do - stub_gitlab_rb( - gitlab_exporter: { - log_directory: 'foo', - enable: true - } - ) - end - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/gitlab-exporter/log/run') - .with_content(/exec svlogd -tt foo/) - end - end - - include_examples "consul service discovery", "gitlab_exporter", "gitlab-exporter" -end diff --git a/spec/chef/monitoring/recipes/grafana_spec.rb b/spec/chef/monitoring/recipes/grafana_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvcmVjaXBlcy9ncmFmYW5hX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/recipes/grafana_spec.rb +++ /dev/null @@ -1,356 +0,0 @@ -require 'chef_helper' - -default_dashboards_yml_output = <<-DASHBOARDYML ---- -apiVersion: 1 -providers: -- name: GitLab Omnibus - orgId: 1 - folder: GitLab Omnibus - type: file - disableDeletion: true - updateIntervalSeconds: 600 - options: - path: "/opt/gitlab/embedded/service/grafana-dashboards" -DASHBOARDYML - -default_datasources_yml_output = <<-DATASOURCEYML ---- -apiVersion: 1 -datasources: -- name: GitLab Omnibus - type: prometheus - access: proxy - url: http://localhost:9090 -DATASOURCEYML - -RSpec.describe 'monitoring::grafana' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:database_exists?).and_return(true) - end - - context 'when grafana is enabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/grafana/log/config') } - - before do - stub_gitlab_rb( - grafana: { enable: true }, - prometheus: { enable: true } - ) - end - - it_behaves_like 'enabled runit service', 'grafana', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/grafana/env').with_variables(default_vars) - end - - it 'populates the files with expected configuration' do - expect(config_template).to notify('ruby_block[reload_log_service]') - - expect(chef_run).to render_file('/opt/gitlab/sv/grafana/run') - .with_content { |content| - expect(content).to match(/exec chpst -P/) - expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/grafana-server/) - expect(content).to match(/-config '\/var\/opt\/gitlab\/grafana\/grafana.ini'/) - } - - expect(chef_run).to render_file('/opt/gitlab/sv/grafana/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/grafana/) - end - - it 'creates default set of directories' do - expect(chef_run).to create_directory('/var/log/gitlab/grafana').with( - owner: 'gitlab-prometheus', - group: nil, - mode: '0700' - ) - end - - it 'creates the configuration file' do - expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') - .with_content { |content| - expect(content).to match(/http_addr = localhost/) - expect(content).to match(/http_port = 3000/) - expect(content).to match(/root_url = http:\/\/localhost\/-\/grafana/) - expect(content).not_to match(/\[auth\.gitlab\]/) - expect(content).to match(/reporting_enabled = true/) - } - end - - it 'creates a default dashboards file' do - expect(chef_run).to render_file('/var/opt/gitlab/grafana/provisioning/dashboards/gitlab_dashboards.yml') - .with_content(default_dashboards_yml_output) - end - - it 'creates a default datasources file' do - expect(chef_run).to render_file('/var/opt/gitlab/grafana/provisioning/datasources/gitlab_datasources.yml') - .with_content(default_datasources_yml_output) - end - end - - context 'when grafana is enabled and prometheus is disabled' do - before do - stub_gitlab_rb( - external_url: 'http://gitlab.example.com', - prometheus: { enable: false }, - grafana: { enable: true } - ) - end - - it 'should create the gitlab-prometheus account if prometheus is disabled' do - expect(chef_run).to create_account('Prometheus user and group').with_username('gitlab-prometheus') - end - end - - context 'when log dir is changed' do - before do - stub_gitlab_rb( - external_url: 'http://gitlab.example.com', - grafana: { - log_directory: 'foo', - enable: true - } - ) - end - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/grafana/log/run') - .with_content(/exec svlogd -tt foo/) - end - end - - it 'authorizes Grafana with gitlab' do - stub_gitlab_rb(external_url: 'http://gitlab.example.com') - - allow(GrafanaHelper).to receive(:authorize_with_gitlab) - - expect(chef_run).to run_ruby_block('authorize Grafana with GitLab') - .at_converge_time - expect(GrafanaHelper).to receive(:authorize_with_gitlab) - .with 'http://gitlab.example.com' - - chef_run.ruby_block('authorize Grafana with GitLab').block.call - end - - context 'with user provided settings' do - before do - stub_gitlab_rb( - external_url: 'https://trailingslash.example.com/', - grafana: { - http_addr: '0.0.0.0', - http_port: 3333, - enable: true, - gitlab_application_id: 'appid', - gitlab_secret: 'secret', - gitlab_auth_sign_up: false, - allowed_groups: %w[ - allowed - also-allowed - ], - smtp: { - enabled: true, - host: 'smtp_server:255', - user: 'testuser', - password: 'test', - cert_file: '/path/to/cert_file.crt', - key_file: '/path/to/key_file.key', - skip_verify: true, - from_address: 'smtp_admin@grafana.gitlab', - from_name: 'Grafana Gitlab SMTP', - ehlo_identity: 'dashboard.grafama.gitlab', - startTLS_policy: 'NoStartTLS' - }, - env: { - 'USER_SETTING' => 'asdf1234' - }, - dashboards: [ - { - name: 'GitLab Omnibus', - orgId: 1, - folder: 'GitLab Omnibus', - type: 'file', - disableDeletion: true, - updateIntervalSeconds: 600, - options: { - path: '/etc/grafana/dashboards', - }, - }, - ], - } - ) - end - - it 'creates a custom dashboards file' do - expect(chef_run).to render_file('/var/opt/gitlab/grafana/provisioning/dashboards/gitlab_dashboards.yml') - .with_content { |content| - expect(content).to match(/options:\n path: "\/etc\/grafana\/dashboards"\n/) - } - end - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') - .with_content { |content| - expect(content).to match(/http_addr = 0.0.0.0/) - expect(content).to match(/http_port = 3333/) - expect(content).to match(/root_url = https:\/\/trailingslash.example.com\/-\/grafana/) - expect(content).to match(/\[auth\.gitlab\]\nenabled = true\nallow_sign_up = false/) - expect(content).to match(/client_id = appid/) - expect(content).to match(/client_secret = secret/) - expect(content).to match(/auth_url = https:\/\/trailingslash.example.com\/oauth\/authorize/) - expect(content).to match(/token_url = https:\/\/trailingslash.example.com\/oauth\/token/) - expect(content).to match(/api_url = https:\/\/trailingslash.example.com\/api\/v4/) - expect(content).to match(/allowed_groups = allowed also-allowed/) - expect(content).to match(/\[metrics\]\n#.+\nenabled = false/) - expect(content).not_to match(/basic_auth_username/) - expect(content).not_to match(/basic_auth_password/) - expect(content).to match(/scopes = read_api/) - # smtp configuration tests - expect(content).to match(/\[smtp\]\nenabled = true/) - expect(content).to match(/host = smtp_server:255/) - expect(content).to match(/user = testuser/) - expect(content).to match(/password = test/) - expect(content).to match(/cert_file = \/path\/to\/cert_file.crt/) - expect(content).to match(/key_file = \/path\/to\/key_file.key/) - expect(content).to match(/skip_verify = true/) - expect(content).to match(/from_address = smtp_admin@grafana.gitlab/) - expect(content).to match(/from_name = Grafana Gitlab SMTP/) - expect(content).to match(/ehlo_identity = dashboard.grafama.gitlab/) - expect(content).to match(/startTLS_policy = NoStartTLS/) - } - end - - it 'generates grafana metrics basic auth password when enabled' do - stub_gitlab_rb( - grafana: { - metrics_enabled: true - } - ) - - expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') - .with_content { |content| - expect(content).to match(/\[metrics\]\n#.+\nenabled = true/) - expect(content).not_to match(/basic_auth_username/) - expect(content).to match(/basic_auth_password = """\S+"""/) - } - end - - it 'adds metrics basic_auth_username when set' do - stub_gitlab_rb( - grafana: { - metrics_enabled: true, - metrics_basic_auth_username: 'user' - } - ) - - expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') - .with_content { |content| - expect(content).to match(/\[metrics\]\n#.+\nenabled = true/) - expect(content).to match(/basic_auth_username = """user"""/) - expect(content).to match(/basic_auth_password = """\S+"""/) - } - end - - it 'can use special chars in metrics basic auth username and password' do - stub_gitlab_rb( - grafana: { - metrics_enabled: true, - metrics_basic_auth_username: '#user;@', - metrics_basic_auth_password: 'password_#;@' - } - ) - - expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') - .with_content { |content| - expect(content).to match(/\[metrics\]\n#.+\nenabled = true/) - expect(content).to match(/basic_auth_username = """#user;@"""/) - expect(content).to match(/basic_auth_password = """password_#;@"""/) - } - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/grafana/env').with_variables( - default_vars.merge( - { - 'USER_SETTING' => 'asdf1234' - } - ) - ) - end - - it 'does not deduplicate attributes outside its own scope' do - queue_groups = ['geo,post_receive,cronjob', 'geo,post_receive,cronjob', 'geo,post_receive,cronjob'] - - stub_gitlab_rb( - grafana: { - metrics_enabled: true, - }, - sidekiq: { - enable: true, - queue_groups: queue_groups - } - ) - - block = chef_run.ruby_block('populate Grafana configuration options') - block.block.call - - expect(chef_run.node['gitlab']['sidekiq']['queue_groups']).to eq(queue_groups) - end - - it 'disables reporting when usage_ping_enabled is disabled' do - stub_gitlab_rb( - gitlab_rails: { - usage_ping_enabled: false - } - ) - - expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') - .with_content { |content| - expect(content).to match(/reporting_enabled = false/) - } - end - - it 'disables reporting when usage_ping_enabled is enabled but is explicitly disabled' do - stub_gitlab_rb( - gitlab_rails: { - usage_ping_enabled: true - }, - grafana: { - reporting_enabled: false - } - ) - - expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini') - .with_content { |content| - expect(content).to match(/reporting_enabled = false/) - } - end - - context 'without allowed_groups specified' do - before do - stub_gitlab_rb( - external_url: 'https://trailingslash.example.com/', - grafana: { - http_addr: '0.0.0.0', - http_port: 3333, - enable: true, - gitlab_application_id: 'appid', - gitlab_secret: 'secret' - } - ) - end - - it 'sets auth scope to read_user' do - expect(chef_run).to render_file('/var/opt/gitlab/grafana/grafana.ini').with_content(/scopes = read_user/) - end - end - end -end diff --git a/spec/chef/monitoring/recipes/node-exporter_spec.rb b/spec/chef/monitoring/recipes/node-exporter_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvcmVjaXBlcy9ub2RlLWV4cG9ydGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/recipes/node-exporter_spec.rb +++ /dev/null @@ -1,149 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'monitoring::node-exporter' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when node-exporter is enabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/node-exporter/log/config') } - - before do - stub_gitlab_rb( - node_exporter: { enable: true } - ) - end - - it_behaves_like 'enabled runit service', 'node-exporter', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/node-exporter/env').with_variables(default_vars) - end - - it 'populates the files with expected configuration' do - expect(config_template).to notify('ruby_block[reload_log_service]') - - expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/run') - .with_content { |content| - expect(content).to match(/exec chpst -P/) - expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/node_exporter/) - expect(content).to match(/--collector\.mountstats /) - expect(content).to match(/--collector\.runit /) - expect(content).to match(/--collector\.runit.servicedir=\/opt\/gitlab\/sv /) - expect(content).to match(/\/textfile_collector/) - } - - expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/node-exporter/) - end - - it 'creates default set of directories' do - expect(chef_run).to create_directory('/var/log/gitlab/node-exporter').with( - owner: 'gitlab-prometheus', - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/var/opt/gitlab/node-exporter/textfile_collector').with( - owner: 'gitlab-prometheus', - group: nil, - mode: '0755' - ) - end - - it 'sets a default listen address' do - expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/run') - .with_content(/web.listen-address=localhost:9100/) - end - end - - context 'when node-exporter is enabled and prometheus is disabled' do - before do - stub_gitlab_rb( - prometheus: { enable: false }, - node_exporter: { enable: true } - ) - end - - it 'should create the gitlab-prometheus account if prometheus is disabled' do - expect(chef_run).to create_account('Prometheus user and group').with_username('gitlab-prometheus') - end - end - - context 'when node-exporter is enabled and prometheus is enabled' do - before do - stub_gitlab_rb( - prometheus: { enable: true }, - node_exporter: { enable: true } - ) - end - - it 'creates a node.rules file' do - expect(chef_run).to render_file('/var/opt/gitlab/prometheus/rules/node.rules') - .with_content(/instance:node_cpus:count/) - end - end - - context 'when log dir is changed' do - before do - stub_gitlab_rb( - node_exporter: { - log_directory: 'foo', - enable: true - } - ) - end - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/log/run') - .with_content(/exec svlogd -tt foo/) - end - end - - context 'with user provided settings' do - before do - stub_gitlab_rb( - node_exporter: { - flags: { - 'collector.textfile.directory' => '/tmp', - 'collector.arp' => false, - 'collector.mountstats' => false - }, - listen_address: 'localhost:9899', - enable: true, - env: { - 'USER_SETTING' => 'asdf1234' - } - } - ) - end - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/node-exporter/run') - .with_content { |content| - expect(content).to match(/web\.listen-address=localhost:9899/) - expect(content).to match(/collector\.textfile\.directory=\/tmp/) - expect(content).to match(/--no-collector\.arp/) - expect(content).to match(/--no-collector\.mountstats/) - expect(content).to match(/--collector\.runit/) - } - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/node-exporter/env').with_variables( - default_vars.merge( - { - 'USER_SETTING' => 'asdf1234' - } - ) - ) - end - end - - include_examples "consul service discovery", "node_exporter", "node-exporter" -end diff --git a/spec/chef/monitoring/recipes/pgbouncer-exporter_spec.rb b/spec/chef/monitoring/recipes/pgbouncer-exporter_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvcmVjaXBlcy9wZ2JvdW5jZXItZXhwb3J0ZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/recipes/pgbouncer-exporter_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -# -# Copyright:: Copyright (c) 2018 GitLab Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -require 'chef_helper' - -RSpec.describe 'monitoring::pgbouncer-exporter' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - let(:config_yaml) { '/var/opt/gitlab/pgbouncer-exporter/pgbouncer-exporter.yaml' } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'when enabled' do - before do - stub_gitlab_rb( - pgbouncer_exporter: { - enable: true - }, - pgbouncer: { - enable: true, - databases: { - gitlabhq_production: { - host: '1.2.3.4' - } - } - }, - postgresql: { - pgbouncer_user: 'fakeuser', - pgbouncer_user_password: 'fakeuserpassword' - } - ) - end - - it 'includes the pgbouncer-exporter recipe' do - expect(chef_run).to include_recipe('monitoring::pgbouncer-exporter') - end - - it 'includes the postgresql user recipe' do - expect(chef_run).to include_recipe('postgresql::user') - end - - it_behaves_like 'enabled runit service', 'pgbouncer-exporter', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/pgbouncer-exporter/env').with_variables(default_vars) - end - - it 'creates the appropriate directories' do - expect(chef_run).to create_directory('/var/log/gitlab/pgbouncer-exporter') - end - end -end diff --git a/spec/chef/monitoring/recipes/postgres_exporter_spec.rb b/spec/chef/monitoring/recipes/postgres_exporter_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvcmVjaXBlcy9wb3N0Z3Jlc19leHBvcnRlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/recipes/postgres_exporter_spec.rb +++ /dev/null @@ -1,194 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'monitoring::postgres-exporter' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:node) { chef_run.node } - let(:default_vars) do - { - 'DATA_SOURCE_NAME' => 'host=/var/opt/gitlab/postgresql user=gitlab-psql database=gitlabhq_production', - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when postgres is disabled locally' do - before do - stub_gitlab_rb( - postgresql: { enable: false } - ) - end - - it 'defaults the postgres-exporter to being disabled' do - expect(node['monitoring']['postgres-exporter']['enable']).to eq false - end - - it 'allows postgres-exporter to be explicitly enabled' do - stub_gitlab_rb(postgres_exporter: { enable: true }) - - expect(node['monitoring']['postgres-exporter']['enable']).to eq true - end - - it 'uses gitlab-rails db_host for the database host if postgres-exporter is explicitly enabled' do - stub_gitlab_rb( - postgres_exporter: { enable: true }, - gitlab_rails: { - db_host: '10.0.0.1', - db_port: '4242', - db_username: 'foo', - db_password: 'bar', - db_database: 'baz' - } - ) - - expect(node['monitoring']['postgres-exporter']['env']['DATA_SOURCE_NAME']) - .to eq "host=10.0.0.1 port=4242 user=foo password=bar database=baz" - end - end - - context 'when postgres is enabled locally' do - before do - stub_gitlab_rb( - postgresql: { enable: true } - ) - end - - it 'uses the local postgres dir for the database host' do - stub_gitlab_rb( - postgres_exporter: { enable: true }, - gitlab_rails: { db_host: '10.0.0.1' }, - postgresql: { dir: '/dir/to/postgresql' } - ) - - expect(node['monitoring']['postgres-exporter']['env']['DATA_SOURCE_NAME']) - .to eq "host=/dir/to/postgresql user=gitlab-psql database=gitlabhq_production" - end - end - - context 'when postgres-exporter is enabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/postgres-exporter/log/config') } - - before do - stub_gitlab_rb( - postgres_exporter: { enable: true } - ) - end - - it_behaves_like 'enabled runit service', 'postgres-exporter', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/postgres-exporter/env').with_variables(default_vars) - end - - it 'populates the files with expected configuration' do - expect(config_template).to notify('ruby_block[reload_log_service]') - - expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/run') - .with_content { |content| - expect(content).to match(/exec chpst /) - expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/postgres_exporter/) - } - - expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/postgres-exporter/) - end - - it 'creates the queries.yaml file' do - expect(chef_run).to render_file('/var/opt/gitlab/postgres-exporter/queries.yaml') - .with_content { |content| - expect(content).to match(/pg_replication:/) - expect(content).not_to match(/pg_stat_user_table:/) - } - end - - it 'creates default set of directories' do - expect(chef_run).to create_directory('/var/log/gitlab/postgres-exporter').with( - owner: 'gitlab-psql', - group: nil, - mode: '0700' - ) - end - - it 'sets default flags' do - expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/run') - .with_content { |content| - expect(content).to match(/web.listen-address=localhost:9187/) - expect(content).to match(/extend.query-path=\/var\/opt\/gitlab\/postgres-exporter\/queries.yaml/) - } - end - end - - context 'when enabled and run as an isolated recipe' do - let(:chef_run) { converge_config('monitoring::postgres-exporter') } - before do - stub_gitlab_rb(postgres_exporter: { enable: true }) - end - - it 'includes the postgresql_user recipe' do - expect(chef_run).to include_recipe('postgresql::user') - end - end - - context 'when log dir is changed' do - before do - stub_gitlab_rb( - postgres_exporter: { - log_directory: 'foo', - enable: true - } - ) - end - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/log/run') - .with_content(/exec svlogd -tt foo/) - end - end - - context 'with user provided settings' do - before do - stub_gitlab_rb( - postgres_exporter: { - flags: { - 'some.flag' => 'foo' - }, - listen_address: 'localhost:9700', - enable: true, - per_table_stats: true, - sslmode: 'require', - env: { - 'USER_SETTING' => 'asdf1234' - } - } - ) - end - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/run') - .with_content(/web.listen-address=localhost:9700/) - expect(chef_run).to render_file('/opt/gitlab/sv/postgres-exporter/run') - .with_content(/some.flag=foo/) - end - - it 'creates the queries.yaml file' do - expect(chef_run).to render_file('/var/opt/gitlab/postgres-exporter/queries.yaml') - .with_content(/pg_stat_user_tables:/) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/postgres-exporter/env').with_variables( - default_vars.merge( - { - 'DATA_SOURCE_NAME' => 'host=/var/opt/gitlab/postgresql user=gitlab-psql '\ - 'database=gitlabhq_production sslmode=require', - 'USER_SETTING' => 'asdf1234' - } - ) - ) - end - end - - include_examples "consul service discovery", "postgres_exporter", "postgres-exporter" -end diff --git a/spec/chef/monitoring/recipes/prometheus_spec.rb b/spec/chef/monitoring/recipes/prometheus_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvcmVjaXBlcy9wcm9tZXRoZXVzX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/recipes/prometheus_spec.rb +++ /dev/null @@ -1,312 +0,0 @@ -require 'chef_helper' - -prometheus_yml_output = <<-PROMYML - --- - global: - scrape_interval: 15s - scrape_timeout: 15s - external_labels: {} - remote_read: [] - remote_write: [] - rule_files: - - "/var/opt/gitlab/prometheus/rules/*.rules" - scrape_configs: - - job_name: prometheus - static_configs: - - targets: - - localhost:9090 - - job_name: nginx - static_configs: - - targets: - - localhost:8060 - - job_name: redis - static_configs: - - targets: - - localhost:9121 - - job_name: postgres - static_configs: - - targets: - - localhost:9187 - - job_name: node - static_configs: - - targets: - - localhost:9100 - - job_name: gitlab-workhorse - static_configs: - - targets: - - localhost:9229 - - job_name: gitlab-rails - metrics_path: "/-/metrics" - static_configs: - - targets: - - 127.0.0.1:8080 - relabel_configs: - - source_labels: - - __address__ - regex: 127.0.0.1:(.*) - replacement: localhost:$1 - target_label: instance - - job_name: gitlab-sidekiq - static_configs: - - targets: - - 127.0.0.1:8082 - relabel_configs: - - source_labels: - - __address__ - regex: 127.0.0.1:(.*) - replacement: localhost:$1 - target_label: instance - - job_name: registry - static_configs: - - targets: - - localhost:5001 - - job_name: gitlab_exporter_database - metrics_path: "/database" - static_configs: - - targets: - - localhost:9168 - - job_name: gitlab_exporter_sidekiq - metrics_path: "/sidekiq" - static_configs: - - targets: - - localhost:9168 - - job_name: gitlab_exporter_ruby - metrics_path: "/ruby" - static_configs: - - targets: - - localhost:9168 - - job_name: gitaly - static_configs: - - targets: - - localhost:9236 - - job_name: kubernetes-cadvisor - scheme: https - tls_config: - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - insecure_skip_verify: true - bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" - kubernetes_sd_configs: - - role: node - api_server: https://kubernetes.default.svc:443 - tls_config: - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - target_label: __address__ - replacement: kubernetes.default.svc:443 - - source_labels: - - __meta_kubernetes_node_name - regex: "(.+)" - target_label: __metrics_path__ - replacement: "/api/v1/nodes/${1}/proxy/metrics/cadvisor" - metric_relabel_configs: - - source_labels: - - pod_name - target_label: environment - regex: "(.+)-.+-.+" - - job_name: kubernetes-nodes - scheme: https - tls_config: - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - insecure_skip_verify: true - bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" - kubernetes_sd_configs: - - role: node - api_server: https://kubernetes.default.svc:443 - tls_config: - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - target_label: __address__ - replacement: kubernetes.default.svc:443 - - source_labels: - - __meta_kubernetes_node_name - regex: "(.+)" - target_label: __metrics_path__ - replacement: "/api/v1/nodes/${1}/proxy/metrics" - metric_relabel_configs: - - source_labels: - - pod_name - target_label: environment - regex: "(.+)-.+-.+" - - job_name: kubernetes-pods - tls_config: - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - insecure_skip_verify: true - bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" - kubernetes_sd_configs: - - role: pod - api_server: https://kubernetes.default.svc:443 - tls_config: - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" - relabel_configs: - - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape - action: keep - regex: 'true' - - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - action: replace - target_label: __metrics_path__ - regex: "(.+)" - - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - action: replace - regex: "([^:]+)(?::[0-9]+)?;([0-9]+)" - replacement: "$1:$2" - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - source_labels: - - __meta_kubernetes_namespace - action: replace - target_label: kubernetes_namespace - - source_labels: - - __meta_kubernetes_pod_name - action: replace - target_label: kubernetes_pod_name - alerting: - alertmanagers: - - static_configs: - - targets: - - localhost:9093 -PROMYML - -RSpec.describe 'monitoring::prometheus' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when prometheus is enabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/prometheus/log/config') } - - before do - stub_gitlab_rb( - alertmanager: { - enable: true - }, - prometheus: { - enable: true - }, - gitlab_exporter: { - enable: true - }, - registry: { - enable: true, - debug_addr: 'localhost:5001' - } - ) - end - - it_behaves_like 'enabled runit service', 'prometheus', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/prometheus/env').with_variables(default_vars) - end - - it 'populates the files with expected configuration' do - expect(config_template).to notify('ruby_block[reload_log_service]') - - expect(chef_run).to render_file('/opt/gitlab/sv/prometheus/run') - .with_content { |content| - expect(content).to match(/exec chpst -P/) - expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/prometheus/) - expect(content).to match(/prometheus.yml/) - } - - expect(chef_run).to render_file('/var/opt/gitlab/prometheus/prometheus.yml') - .with_content(prometheus_yml_output.gsub(/^ {2}/, '')) - - expect(chef_run).to render_file('/opt/gitlab/sv/prometheus/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/prometheus/) - end - - it 'creates default set of directories' do - expect(chef_run).to create_directory('/var/log/gitlab/prometheus').with( - owner: 'gitlab-prometheus', - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/var/opt/gitlab/prometheus').with( - owner: 'gitlab-prometheus', - group: nil, - mode: '0750' - ) - end - - it 'should create a gitlab-prometheus user and group' do - expect(chef_run).to create_account('Prometheus user and group').with(username: 'gitlab-prometheus', groupname: 'gitlab-prometheus') - end - - it 'sets a default listen address' do - expect(chef_run).to render_file('/opt/gitlab/sv/prometheus/run') - .with_content(/web.listen-address=localhost:9090/) - end - end - - context 'by default' do - context 'with user provided settings' do - it 'configures puma job' do - expect(chef_run).to render_file('/var/opt/gitlab/prometheus/prometheus.yml') - .with_content(%r{- job_name: gitlab-rails\s+metrics_path: "/-/metrics"\s+static_configs:\s+- targets:\s+- 127.0.0.1:8080}) - end - end - end - - context 'rules directory' do - context 'default settings' do - it 'creates rules directory in correct location' do - expect(chef_run).to create_directory("/var/opt/gitlab/prometheus/rules") - expect(chef_run).to render_file("/var/opt/gitlab/prometheus/rules/node.rules") - expect(chef_run).to render_file("/var/opt/gitlab/prometheus/rules/gitlab.rules") - end - end - - context 'user specified home directory' do - before do - stub_gitlab_rb( - prometheus: { - home: "/var/opt/gitlab/prometheus-bak" - } - ) - end - - it 'creates rules directory in correct location' do - expect(chef_run).to create_directory("/var/opt/gitlab/prometheus-bak/rules") - expect(chef_run).to render_file("/var/opt/gitlab/prometheus-bak/rules/node.rules") - expect(chef_run).to render_file("/var/opt/gitlab/prometheus-bak/rules/gitlab.rules") - end - end - - context 'user specified rules directory' do - before do - stub_gitlab_rb( - prometheus: { - rules_directory: "/var/opt/gitlab/prometheus/alert-rules" - } - ) - end - - it 'creates rules directory in correct location' do - expect(chef_run).to create_directory("/var/opt/gitlab/prometheus/alert-rules") - expect(chef_run).to render_file("/var/opt/gitlab/prometheus/alert-rules/node.rules") - expect(chef_run).to render_file("/var/opt/gitlab/prometheus/alert-rules/gitlab.rules") - end - end - end - - include_examples "consul service discovery", "prometheus", "prometheus" -end diff --git a/spec/chef/monitoring/recipes/redis_exporter_spec.rb b/spec/chef/monitoring/recipes/redis_exporter_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL21vbml0b3JpbmcvcmVjaXBlcy9yZWRpc19leHBvcnRlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/monitoring/recipes/redis_exporter_spec.rb +++ /dev/null @@ -1,129 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'monitoring::redis-exporter' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:node) { chef_run.node } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when redis is disabled locally' do - before do - stub_gitlab_rb( - redis: { enable: false } - ) - end - - it 'defaults the redis-exporter to being disabled' do - expect(node['monitoring']['redis-exporter']['enable']).to eq false - end - - it 'allows redis-exporter to be explicitly enabled' do - stub_gitlab_rb(redis_exporter: { enable: true }) - - expect(node['monitoring']['redis-exporter']['enable']).to eq true - end - end - - context 'when redis-exporter is enabled' do - let(:config_template) { chef_run.template('/opt/gitlab/sv/redis-exporter/log/config') } - - before do - stub_gitlab_rb( - redis_exporter: { enable: true } - ) - end - - it_behaves_like 'enabled runit service', 'redis-exporter', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/redis-exporter/env').with_variables(default_vars) - end - - it 'populates the files with expected configuration' do - expect(config_template).to notify('ruby_block[reload_log_service]') - - expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') - .with_content { |content| - expect(content).to match(/exec chpst -P/) - expect(content).to match(/\/opt\/gitlab\/embedded\/bin\/redis_exporter/) - } - - expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/redis-exporter/) - end - - it 'creates default set of directories' do - expect(chef_run).to create_directory('/var/log/gitlab/redis-exporter').with( - owner: 'gitlab-redis', - group: nil, - mode: '0700' - ) - end - - it 'sets default flags' do - expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') - .with_content(/web.listen-address=localhost:9121/) - expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') - .with_content(%r{redis.addr=unix:///var/opt/gitlab/redis/redis.socket}) - end - end - - context 'when log dir is changed' do - before do - stub_gitlab_rb( - redis_exporter: { - log_directory: 'foo', - enable: true - } - ) - end - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/log/run') - .with_content(/exec svlogd -tt foo/) - end - end - - context 'with user provided settings' do - before do - stub_gitlab_rb( - redis_exporter: { - flags: { - 'redis.addr' => '/tmp/socket' - }, - listen_address: 'localhost:9900', - enable: true, - env: { - 'USER_SETTING' => 'asdf1234' - } - } - ) - end - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') - .with_content(/web.listen-address=localhost:9900/) - expect(chef_run).to render_file('/opt/gitlab/sv/redis-exporter/run') - .with_content(%r{redis.addr=/tmp/socket}) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/redis-exporter/env').with_variables( - default_vars.merge( - { - 'USER_SETTING' => 'asdf1234' - } - ) - ) - end - end - - include_examples "consul service discovery", "redis_exporter", "redis-exporter" -end diff --git a/spec/chef/package/libraries/helpers/node_helper_spec.rb b/spec/chef/package/libraries/helpers/node_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvbGlicmFyaWVzL2hlbHBlcnMvbm9kZV9oZWxwZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/package/libraries/helpers/node_helper_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -require 'chef_helper' - -RSpec.describe NodeHelper do - let(:chef_run) { converge_config } - subject { described_class } - - describe '.consume_cluster_attributes' do - it 'merges new attributes into node as override level' do - new_attrs = ::Gitlab::ConfigMash.new(geo_primary: true) - - subject.consume_cluster_attributes(chef_run.node, new_attrs) - - expect(chef_run.node.attributes).to include(geo_primary: true) - expect(chef_run.node.override).to include(geo_primary: true) - end - - it 'merges overwritting existing attributes into node as override level' do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb(package: { custom: true }) - - expect(chef_run.node.attributes['package']['custom']).to be true - - new_attrs = ::Gitlab::ConfigMash.new(package: { custom: false }) - subject.consume_cluster_attributes(chef_run.node, new_attrs) - - expect(chef_run.node.attributes['package']['custom']).to be false - expect(chef_run.node.override['package']['custom']).to be false - end - end -end diff --git a/spec/chef/package/libraries/helpers/shell_out_helper_spec.rb b/spec/chef/package/libraries/helpers/shell_out_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvbGlicmFyaWVzL2hlbHBlcnMvc2hlbGxfb3V0X2hlbHBlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/package/libraries/helpers/shell_out_helper_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true -require 'spec_helper' - -RSpec.describe ShellOutHelper do - let(:dummy_class) { Class.new { extend ShellOutHelper } } - - before do - allow(Mixlib::ShellOut).to receive(:new).and_call_original - end - - describe '.do_shell_out' do - context 'without optional arguments' do - it 'creates Mixlib::ShellOut object without optional arguments' do - expect(Mixlib::ShellOut).to receive(:new).with('ls', { user: nil, cwd: nil, environment: {} }) - - dummy_class.do_shell_out('ls') - end - end - - context 'with optional arguments specified' do - it 'creates Mixlib::ShellOut object with specified optional arguments' do - expect(Mixlib::ShellOut).to receive(:new).with('ls', { user: 'foobar', cwd: '/tmp', environment: { PATH: '/tmp/bin' } }) - - dummy_class.do_shell_out('ls', 'foobar', '/tmp', env: { PATH: '/tmp/bin' }) - end - end - - context 'when command can not be executed' do - let(:shellout_object) { double('shellout', run_command: true) } - - it 'logs relevant information' do - allow(Mixlib::ShellOut).to receive(:new).and_return(shellout_object) - allow(shellout_object).to receive(:run_command).and_raise(Errno::EACCES) - - expect(Chef::Log).to receive(:info).with("Cannot execute ls.") - - dummy_class.do_shell_out('ls') - end - end - - context 'when command not found' do - let(:shellout_object) { double('shellout', run_command: true) } - - it 'logs relevant information' do - allow(Mixlib::ShellOut).to receive(:new).and_return(shellout_object) - allow(shellout_object).to receive(:run_command).and_raise(Errno::ENOENT) - - expect(Chef::Log).to receive(:info).with("ls does not exist.") - - dummy_class.do_shell_out('ls') - end - end - end - - describe '.do_shell_out_with_embedded_path' do - context 'without optional arguments' do - it 'calls do_shell_out with embedded bin path prependend to PATH' do - allow(ENV).to receive(:[]).with('PATH').and_return('/usr/local/bin') - - expect(dummy_class).to receive(:do_shell_out).with('ls', nil, nil, env: { PATH: '/opt/gitlab/embedded/bin:/usr/local/bin' }) - - dummy_class.do_shell_out_with_embedded_path('ls') - end - end - - context 'without PATH specified in optional env argument' do - it 'calls do_shell_out with embedded bin path prependend to PATH' do - allow(ENV).to receive(:[]).with('PATH').and_return('/usr/local/bin') - - expect(dummy_class).to receive(:do_shell_out).with('ls', nil, nil, env: { PATH: '/opt/gitlab/embedded/bin:/tmp/bin:/usr/local/bin' }) - - dummy_class.do_shell_out_with_embedded_path('ls', env: { 'PATH' => '/tmp/bin' }) - end - end - end -end diff --git a/spec/chef/package/libraries/omnibus_helper_spec.rb b/spec/chef/package/libraries/omnibus_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvbGlicmFyaWVzL29tbmlidXNfaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/package/libraries/omnibus_helper_spec.rb +++ /dev/null @@ -1,557 +0,0 @@ -# frozen_string_literal: true - -require 'chef_helper' -require 'time' - -RSpec.describe OmnibusHelper do - cached(:chef_run) { converge_config } - let(:node) { chef_run.node } - let(:file) { double(:file, write: true) } - - subject { described_class.new(chef_run.node) } - - before do - allow(Gitlab).to receive(:[]).and_call_original - - allow(File).to receive(:open).and_call_original - allow(File).to receive(:open).with('/etc/gitlab/initial_root_password', 'w', 0600).and_yield(file).once - end - - describe '#user_exists?' do - it 'returns true when user exists' do - allow_any_instance_of(ShellOutHelper).to receive(:success?).with("id -u root").and_return(true) - expect(subject.user_exists?('root')).to be_truthy - end - - it 'returns false when user does not exist' do - allow_any_instance_of(ShellOutHelper).to receive(:success?).with("id -u nonexistentuser").and_return(false) - expect(subject.user_exists?('nonexistentuser')).to be_falsey - end - end - - describe '#group_exists?' do - it 'returns true when group exists' do - allow_any_instance_of(ShellOutHelper).to receive(:success?).with("getent group root").and_return(true) - expect(subject.group_exists?('root')).to be_truthy - end - - it 'returns false when group does not exist' do - allow_any_instance_of(ShellOutHelper).to receive(:success?).with("getent group nonexistentgroup").and_return(false) - expect(subject.group_exists?('nonexistentgroup')).to be_falsey - end - end - - describe '#not_listening?' do - let(:chef_run) { converge_config } - context 'when Redis is disabled' do - before do - stub_gitlab_rb( - redis: { enable: false } - ) - end - - it 'returns true when service is disabled' do - expect(subject.not_listening?('redis')).to be_truthy - end - end - - context 'when Redis is enabled' do - before do - stub_gitlab_rb( - redis: { enable: true } - ) - end - - it 'returns true when service is down' do - stub_service_failure_status('redis', true) - - expect(subject.not_listening?('redis')).to be_truthy - end - - it 'returns false when service is up' do - stub_service_failure_status('redis', false) - - expect(subject.not_listening?('redis')).to be_falsey - end - end - end - - describe '#service_enabled?' do - context 'services are enabled' do - before do - chef_run.node.normal['gitlab']['old_service']['enable'] = true - chef_run.node.normal['new_service']['enable'] = true - chef_run.node.normal['monitoring']['another_service']['enable'] = true - end - - it 'should return true' do - expect(subject.service_enabled?('old_service')).to be_truthy - expect(subject.service_enabled?('new_service')).to be_truthy - expect(subject.service_enabled?('another_service')).to be_truthy - end - end - - context 'services are disabled' do - before do - chef_run.node.normal['gitlab']['old_service']['enable'] = false - chef_run.node.normal['new_service']['enable'] = false - chef_run.node.normal['monitoring']['another_service']['enable'] = false - end - - it 'should return false' do - expect(subject.service_enabled?('old_service')).to be_falsey - expect(subject.service_enabled?('new_service')).to be_falsey - expect(subject.service_enabled?('another_service')).to be_falsey - end - end - end - - describe '#is_managed_and_offline?' do - context 'services are disabled' do - before do - chef_run.node.normal['gitlab']['old_service']['enable'] = false - chef_run.node.normal['new_service']['enable'] = false - end - - it 'returns false' do - expect(subject.is_managed_and_offline?('old_service')).to be_falsey - expect(subject.is_managed_and_offline?('new_service')).to be_falsey - end - end - - context 'services are enabled' do - before do - chef_run.node.normal['gitlab']['old_service']['enable'] = true - chef_run.node.normal['new_service']['enable'] = true - end - - it 'returns true when services are offline' do - stub_service_failure_status('old_service', true) - stub_service_failure_status('new_service', true) - - expect(subject.is_managed_and_offline?('old_service')).to be_truthy - expect(subject.is_managed_and_offline?('new_service')).to be_truthy - end - - it 'returns false when services are online ' do - stub_service_failure_status('old_service', false) - stub_service_failure_status('new_service', false) - - expect(subject.is_managed_and_offline?('old_service')).to be_falsey - expect(subject.is_managed_and_offline?('new_service')).to be_falsey - end - end - end - - describe '#is_deprecated_os?' do - before do - allow(OmnibusHelper).to receive(:deprecated_os_list).and_return({ "raspbian-8.0" => "GitLab 11.8" }) - end - - it 'detects deprecated OS correctly' do - allow_any_instance_of(Ohai::System).to receive(:data).and_return({ "platform" => "raspbian", "platform_version" => "8.0" }) - - OmnibusHelper.is_deprecated_os? - - expect_logged_deprecation(/Your OS, raspbian-8.0, will be deprecated soon/) - end - - it 'does not detects valid OS as deprecated' do - allow_any_instance_of(Ohai::System).to receive(:data).and_return({ "platform" => "ubuntu", "platform_version" => "16.04.3" }) - expect(LoggingHelper).not_to receive(:deprecation) - OmnibusHelper.is_deprecated_os? - end - end - - describe '#is_deprecated_praefect_config?' do - before do - chef_run.node.normal['praefect'] = config - end - - context 'deprecated config' do - let(:config) do - { - storage_nodes: [ - { storage: 'praefect1', address: 'tcp://node1.internal' }, - { storage: 'praefect2', address: 'tcp://node2.internal' } - ] - } - end - - it 'detects deprecated config correctly' do - subject.is_deprecated_praefect_config? - - expect_logged_deprecation(/Specifying Praefect storage nodes as an array is deprecated/) - end - end - - context 'valid config' do - let(:config) do - { - storage_nodes: { - 'praefect1' => { address: 'tcp://node1.internal' }, - 'praefect2' => { address: 'tcp://node2.internal' } - } - } - end - - it 'does not detect a valid config as deprecated' do - expect(LoggingHelper).not_to receive(:deprecation) - - subject.is_deprecated_praefect_config? - end - end - end - - describe '#write_root_password' do - before do - stub_gitlab_rb( - gitlab_rails: { - initial_root_password: 'foobar', - store_initial_root_password: true - } - ) - end - - it 'stores root password to /etc/gitlab/initial_root_password' do - content = <<~EOS - # WARNING: This value is valid only in the following conditions - # 1. If provided manually (either via `GITLAB_ROOT_PASSWORD` environment variable or via `gitlab_rails['initial_root_password']` setting in `gitlab.rb`, it was provided before database was seeded for the first time (usually, the first reconfigure run). - # 2. Password hasn't been changed manually, either via UI or via command line. - # - # If the password shown here doesn't work, you must reset the admin password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password. - - Password: foobar - - # NOTE: This file will be automatically deleted in the first reconfigure run after 24 hours. - EOS - - expect(file).to receive(:write).with(content) - - described_class.new(converge_config.node).write_root_password - end - end - - describe '.cleanup_root_password_file' do - context 'when /etc/gitlab/initial_root_password does not exist' do - before do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with('/etc/gitlab/initial_root_password').and_return(false) - end - - it 'does not attempt to remove the file' do - expect(FileUtils).not_to receive(:rm_f).with('/etc/gitlab/initial_root_password') - - described_class.cleanup_root_password_file - end - end - - context 'when /etc/gitlab/initial_root_password exists' do - before do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with('/etc/gitlab/initial_root_password').and_return(true) - allow(Time).to receive(:now).and_return(Time.parse('2021-06-07 08:45:51.377926667 +0530')) - end - - context 'when file is older than 24 hours' do - before do - allow(File).to receive(:mtime).with('/etc/gitlab/initial_root_password').and_return(Time.parse('2021-06-03 06:45:51.377926667 +0530')) - end - - it 'attempts to remove the file' do - expect(FileUtils).to receive(:rm_f).with('/etc/gitlab/initial_root_password') - - described_class.cleanup_root_password_file - - expect_logged_note('Found old initial root password file at /etc/gitlab/initial_root_password and deleted it.') - end - end - - context 'when file is younger than 24 hours' do - before do - allow(File).to receive(:mtime).with('/etc/gitlab/initial_root_password').and_return(Time.parse('2021-06-08 06:45:51.377926667 +0530')) - end - - it 'does not attempt to remove the file' do - expect(FileUtils).not_to receive(:rm_f).with('/etc/gitlab/initial_root_password') - - described_class.cleanup_root_password_file - - expect_logged_note('Found old initial root password file at /etc/gitlab/initial_root_password and deleted it.') - end - end - end - end - - describe '#print_root_account_details' do - context 'when not on first reconfigure after installation' do - before do - chef_run.node.normal['gitlab']['bootstrap']['enable'] = false - end - - it 'does not add a note or write password to file' do - expect(LoggingHelper).not_to receive(:note) - expect(subject).not_to receive(:write_root_password) - - subject.print_root_account_details - end - end - - context 'when on first reconfigure after installation' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:writ_root_password).and_return(true) - end - - context 'when display_initial_root_password is true' do - before do - stub_gitlab_rb( - gitlab_rails: { - initial_root_password: 'foobar', - display_initial_root_password: true, - store_initial_root_password: false - } - ) - end - - it 'displays root password at the end of reconfigure' do - msg = <<~EOS - Default admin account has been configured with following details: - Username: root - Password: foobar - - NOTE: Because these credentials might be present in your log files in plain text, it is highly recommended to reset the password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password. - EOS - - described_class.new(converge_config.node).print_root_account_details - - expect_logged_note(msg) - end - end - - context 'when display_initial_root_password is false' do - before do - stub_gitlab_rb( - gitlab_rails: { - initial_root_password: 'foobar', - display_initial_root_password: false, - store_initial_root_password: false - } - ) - end - - it 'does not display root credentials at the end of reconfigure' do - msg = <<~EOS - Default admin account has been configured with following details: - Username: root - Password: You didn't opt-in to print initial root password to STDOUT. - - NOTE: Because these credentials might be present in your log files in plain text, it is highly recommended to reset the password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password. - EOS - - described_class.new(converge_config.node).print_root_account_details - - expect_logged_note(msg) - end - end - - describe '#write_root_password' do - context 'when store_initial_root_password is true' do - before do - stub_gitlab_rb( - gitlab_rails: { - initial_root_password: 'foobar', - display_initial_root_password: false, - store_initial_root_password: true - } - ) - end - - it 'writes initial root password to /etc/gitlab/initial_root_password' do - subject = described_class.new(converge_config.node) - expect(subject).to receive(:write_root_password) - - subject.print_root_account_details - - expect_logged_note(%r{Password stored to /etc/gitlab/initial_root_password}) - end - end - - context 'with default value for store_initial_root_password' do - context 'when password is auto-generated' do - before do - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('GITLAB_ROOT_PASSWORD').and_return(nil) - end - - it 'writes initial root password to /etc/gitlab/initial_root_password' do - chef_run = ChefSpec::SoloRunner.converge('gitlab::default') - subject = described_class.new(chef_run.node) - - expect(subject).to receive(:write_root_password) - - subject.print_root_account_details - - expect_logged_note(%r{Password stored to /etc/gitlab/initial_root_password}) - end - end - - context 'when password is specified by user' do - context 'via gitlab.rb' do - before do - stub_gitlab_rb( - gitlab_rails: { - initial_root_password: 'foobar', - } - ) - end - - it 'does not write initial root password to /etc/gitlab/initial_root_password' do - subject = described_class.new(converge_config.node) - - expect(subject).not_to receive(:write_root_password) - - subject.print_root_account_details - end - end - - context 'via env variable' do - before do - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('GITLAB_ROOT_PASSWORD').and_return('foobar') - end - - it 'does not write initial root password to /etc/gitlab/initial_root_password' do - subject = described_class.new(converge_config.node) - - expect(subject).not_to receive(:write_root_password) - - subject.print_root_account_details - end - end - end - end - end - end - end - - describe '#check_locale' do - let(:error_message) { "Identified encoding is not UTF-8. GitLab requires UTF-8 encoding to function properly. Please check your locale settings." } - - describe 'using LC_ALL variable' do - it 'does not raise a warning when set to a UTF-8 locale even if others are not' do - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('LC_ALL').and_return('en_US.UTF-8') - allow(ENV).to receive(:[]).with('LC_COLLATE').and_return('en_SG ISO-8859-1') - allow(ENV).to receive(:[]).with('LC_CTYPE').and_return('en_SG ISO-8859-1') - allow(ENV).to receive(:[]).with('LANG').and_return('en_SG ISO-8859-1') - - expect(LoggingHelper).not_to receive(:warning).with("Environment variable .* specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") - - described_class.check_locale - end - - it 'raises warning when LC_ALL is non-UTF-8' do - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('LC_ALL').and_return('en_SG ISO-8859-1') - - described_class.check_locale - - expect_logged_warning("Environment variable LC_ALL specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") - end - end - - describe 'using LC_CTYPE variable' do - before do - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('LC_ALL').and_return(nil) - end - - it 'raises warning when LC_CTYPE is non-UTF-8' do - allow(ENV).to receive(:[]).with('LC_CTYPE').and_return('en_SG ISO-8859-1') - - described_class.check_locale - - expect_logged_warning("Environment variable LC_CTYPE specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") - end - end - - describe 'using LC_COLLATE variable' do - before do - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('LC_ALL').and_return(nil) - allow(ENV).to receive(:[]).with('LC_CTYPE').and_return(nil) - end - - it 'raises warning when LC_COLLATE is non-UTF-8' do - allow(ENV).to receive(:[]).with('LC_COLLATE').and_return('en_SG ISO-8859-1') - - described_class.check_locale - - expect_logged_warning("Environment variable LC_COLLATE specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") - end - end - - describe 'using LANG variable' do - before do - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('LC_ALL').and_return(nil) - end - - context 'without LC_CTYPE and LC_COLLATE' do - before do - allow(ENV).to receive(:[]).with('LC_CTYPE').and_return(nil) - allow(ENV).to receive(:[]).with('LC_COLLATE').and_return(nil) - end - - it 'raises warning when LANG is non-UTF-8' do - allow(ENV).to receive(:[]).with('LANG').and_return('en_SG ISO-8859-1') - - described_class.check_locale - - expect_logged_warning("Environment variable LANG specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") - end - end - - context 'with only LC_CTYPE set to UTF-8' do - before do - allow(ENV).to receive(:[]).with('LC_CTYPE').and_return('en_US.UTF-8') - allow(ENV).to receive(:[]).with('LC_COLLATE').and_return(nil) - end - - it 'raises warning when LANG is non-UTF-8' do - allow(ENV).to receive(:[]).with('LANG').and_return('en_SG ISO-8859-1') - - described_class.check_locale - - expect_logged_warning("Environment variable LANG specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") - end - end - - context 'with both LC_CTYPE and LC_COLLATE set to UTF-8' do - before do - allow(ENV).to receive(:[]).with('LC_CTYPE').and_return('en_US.UTF-8') - allow(ENV).to receive(:[]).with('LC_COLLATE').and_return('en_US.UTF-8') - end - - it 'does not raise a warning even if LANG is not UTF-8' do - described_class.check_locale - - expect_logged_warning("Environment variable LANG specifies a non-UTF-8 locale. GitLab requires UTF-8 encoding to function properly. Please check your locale settings.") - end - end - end - end - - describe '.resource_available?' do - cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - subject(:omnibus_helper) { described_class.new(chef_run.node) } - - it 'returns false for a resource that exists but has not been loaded in runtime' do - expect(omnibus_helper.resource_available?('runit_service[geo-logcursor]')).to be_falsey - end - - it 'returns true for a resource that exists and is loaded in runtime' do - expect(omnibus_helper.resource_available?('runit_service[logrotated]')) - end - end -end diff --git a/spec/chef/package/libraries/services_helper_spec.rb b/spec/chef/package/libraries/services_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvbGlicmFyaWVzL3NlcnZpY2VzX2hlbHBlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/package/libraries/services_helper_spec.rb +++ /dev/null @@ -1,229 +0,0 @@ -require 'chef_helper' - -RSpec.describe Services do - let(:chef_run) { ChefSpec::SoloRunner.new } - - subject(:services) { described_class } - - describe '.enable_group' do - before do - chef_run.converge('gitlab::default') - end - - it 'enables services in provide groups list' do - expect do - services.enable_group('pages_role') - end.to change { services.enabled?('gitlab_pages') }.from(false).to(true) - end - - it 'skips services that dont `exist?`' do - expect do - services.enable_group('monitoring') - end.not_to change { services.enabled?('pgbouncer_exporter') } - end - end - - describe '.disable_group' do - before do - chef_run.converge('gitlab::default') - end - - it 'disables services in the provide groups list when setting to false' do - expect do - services.disable_group('redis') - end.to change { services.enabled?('redis') }.from(true).to(false) - end - - it 'it allows disabling services in system group when setting to false and include_system: true' do - expect do - services.disable_group('system', include_system: true) - end.to change { services.enabled?('logrotate') }.from(true).to(false) - end - end - - describe '.enable' do - before do - chef_run.converge('gitlab::default') - end - - it 'enables provide service' do - expect do - services.enable('gitlab_pages') - end.to change { services.enabled?('gitlab_pages') }.from(false).to(true) - end - - it 'skips services that dont `exist?`' do - expect do - services.enable('pgbouncer_exporter') - end.not_to change { services.enabled?('pgbouncer_exporter') } - end - end - - describe '.disable' do - before do - chef_run.converge('gitlab::default') - end - - it 'disables provide service' do - expect do - services.disable('puma') - end.to change { services.enabled?('puma') }.from(true).to(false) - end - - it 'allows disabling service in system group when include_system: true' do - expect do - services.disable('logrotate', include_system: true) - end.to change { services.enabled?('logrotate') }.from(true).to(false) - end - end - - describe '.set_status' do - before do - chef_run.converge('gitlab::default') - end - - it 'enables provide service when setting to true' do - expect do - services.set_status('gitlab_pages', true) - end.to change { services.enabled?('gitlab_pages') }.from(false).to(true) - end - - it 'disables provide service when setting to false' do - expect do - services.set_status('puma', false) - end.to change { services.enabled?('puma') }.from(true).to(false) - end - - it 'allows disabling service in system group when setting to false and include_system: true' do - expect do - services.set_status('logrotate', false, include_system: true) - end.to change { services.enabled?('logrotate') }.from(true).to(false) - end - end - - describe '.set_group_status' do - before do - chef_run.converge('gitlab::default') - end - - it 'enables services in provide groups list when setting to true' do - expect do - services.set_group_status('pages_role', true) - end.to change { services.enabled?('gitlab_pages') }.from(false).to(true) - end - - it 'disables services in the provide groups list when setting to false' do - expect do - services.set_group_status('redis', false) - end.to change { services.enabled?('redis') }.from(true).to(false) - end - - it 'it allows disabling services in system group when setting to false and include_system: true' do - expect do - services.set_group_status('system', false, include_system: true) - end.to change { services.enabled?('logrotate') }.from(true).to(false) - end - end - - describe '.system_services' do - it 'returns a list of services that are in the SYSTEM_GROUP' do - service_list = { - 'redis' => { groups: %w[redis] }, - 'logrotate' => { groups: %w[system] } - } - services.add_services('test', service_list) - - expect(services.system_services).to include('logrotate') - end - end - - describe '.find_by_group' do - before do - service_list = { - 'redis' => { groups: %w[redis] }, - 'redis_exporter' => { groups: %w[redis monitoring] } - } - - services.add_services('test', service_list) - end - - it 'returns a list of services that matches provided groups' do - expect(services.find_by_group('monitoring')).to eq(['redis_exporter']) - end - - it 'returns an empty list when no provide groups match' do - expect(services.find_by_group('non_existent')).to eq([]) - end - end - - describe '.service_list' do - it 'returns a list of loaded known services by existing cookbooks' do - chef_run.converge('gitlab::default') - - expect(services.service_list).to include(Services::BaseServices.list) - end - end - - describe '.add_services' do - it 'adds provided services and register associated cookbook' do - services.add_services('gitlab', Services::BaseServices.list) - - expect(services.service_list).to include(Services::BaseServices.list) - expect(services.send(:cookbook_services).keys).to include('gitlab') - end - end - - describe '.enabled?' do - before do - chef_run.converge('gitlab::default') - end - - context 'when status is set via configuration file' do - it 'returns true if set as enabled' do - Gitlab['mailroom']['enable'] = true - - expect(services.enabled?('mailroom')).to be_truthy - end - - it 'returns false if set as disabled' do - Gitlab['puma']['enable'] = false - - expect(services.enabled?('puma')).to be_falsey - end - end - - context 'when status is not set via configuration file' do - it 'returns true when its enabled via cookbooks' do - expect(services.enabled?('puma')).to be_truthy - end - - it 'returns false when disabled via cookbooks' do - expect(services.enabled?('mailroom')).to be_falsey - end - end - end - - describe '.exist?' do - it 'returns false for non existing service' do - expect(services.exist?('inexistent_service')).to be_falsey - end - - context 'with only CE services registered' do - it 'returns true for CE services and false for EE services' do - chef_run.converge('gitlab::default') - - expect(services.exist?('logrotate')).to be_truthy - expect(services.exist?('pgbouncer_exporter')).to be_falsey - end - end - - context 'with CE and EE services registered' do - it 'returns true for EE services registered via add_service' do - chef_run.converge('gitlab::default', 'gitlab-ee::default') - - expect(services.exist?('logrotate')).to be_truthy - expect(services.exist?('pgbouncer_exporter')).to be_truthy - end - end - end -end diff --git a/spec/chef/package/recipes/runit_spec.rb b/spec/chef/package/recipes/runit_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvcmVjaXBlcy9ydW5pdF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/package/recipes/runit_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'package::runit' do - before do - allow(Chef::Log).to receive(:info).and_call_original - allow(Chef::Log).to receive(:warn).and_call_original - allow(File).to receive(:directory?).and_call_original - allow(File).to receive(:directory?).with('/run/systemd/system').and_return(false) - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with('/.dockerenv').and_return(false) - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'default values' do - let(:chef_run) { ChefSpec::SoloRunner.converge('package::runit') } - - before do - allow_any_instance_of(Chef::Recipe).to receive(:include_recipe) - end - - it 'detects systemd correctly and informs user' do - allow(File).to receive(:directory?).with('/run/systemd/system').and_return(true) - - expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('package::runit_systemd') - - chef_run - end - - it 'detects docker container correctly' do - allow(File).to receive(:exist?).with('/.dockerenv').and_return(true) - - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_systemd') - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_upstart') - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_sysvinit') - - chef_run - end - - it 'detects upstart correctly and informs user' do - allow(Open3).to receive(:capture3).with('/sbin/init --version | grep upstart').and_return(['', '', double(success?: true)]) - - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_systemd') - expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('package::runit_upstart') - expect(Chef::Log).to receive(:warn).with("Selected upstart because /sbin/init --version is showing upstart.") - - chef_run - end - - it 'fall backs to sysvinit correctly and informs user' do - allow(Open3).to receive(:capture3).with('/sbin/init --version | grep upstart').and_return(['', '', double(success?: false)]) - - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_systemd') - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_upstart') - expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('package::runit_sysvinit') - - chef_run - end - end - - context 'explicitly disabled' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - stub_gitlab_rb( - package: { - detect_init: false - } - ) - end - - it 'does not run any recipe' do - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_upstart') - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_systemd') - expect_any_instance_of(Chef::Recipe).not_to receive(:include_recipe).with('package::runit_sysvinit') - - chef_run - end - - it 'informs the user about skipping init detection' do - expect(Chef::Log).to receive(:info).with('Skipped selecting an init system because it was explicitly disabled') - - chef_run - end - end -end diff --git a/spec/chef/package/recipes/runit_systemd_spec.rb b/spec/chef/package/recipes/runit_systemd_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvcmVjaXBlcy9ydW5pdF9zeXN0ZW1kX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/package/recipes/runit_systemd_spec.rb +++ /dev/null @@ -1,92 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'package::runit_systemd' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config', 'package::runit_systemd') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'setting TasksMax value' do - context 'when systemd version < 227' do - before do - allow(SystemdHelper).to receive(:systemd_version).and_return(200) - end - - it 'does not include TasksMax setting in unit file' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service') - expect(chef_run).not_to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('TasksMax=4915') - end - end - - context 'when systemd version >= 227' do - before do - allow(SystemdHelper).to receive(:systemd_version).and_return(235) - end - - it 'include TasksMax setting in unit file' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('TasksMax=4915') - end - end - - context 'with user provided value for TasksMax' do - before do - stub_gitlab_rb( - package: { systemd_tasks_max: 10000 } - ) - allow(SystemdHelper).to receive(:systemd_version).and_return(235) - end - - it 'sets correct value for TasksMax in unit file' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('TasksMax=10000') - end - end - end - - describe 'setting WantedBy and After settings for unit file' do - context 'by default' do - it 'uses "multi-user.target" for WantedBy' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('WantedBy=multi-user.target') - end - - it 'uses "multi-user.target" for WantedBy' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('After=multi-user.target') - end - end - - context 'when WantedBy is specified' do - before do - stub_gitlab_rb( - package: { systemd_wanted_by: 'basic.target' } - ) - end - - it 'uses specified value for WantedBy' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('WantedBy=basic.target') - end - - it 'uses value specified for WantedBy for After also' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('After=basic.target') - end - end - - context 'when both WantedBy and After are specified' do - before do - stub_gitlab_rb( - package: { - systemd_wanted_by: 'foo', - systemd_after: 'bar' - } - ) - end - - it 'uses specified value for WantedBy' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('WantedBy=foo') - end - - it 'uses specified value for After, not value given for WantedBy' do - expect(chef_run).to render_file('/usr/lib/systemd/system/gitlab-runsvdir.service').with_content('After=bar') - end - end - end -end diff --git a/spec/chef/package/resources/account_spec.rb b/spec/chef/package/resources/account_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvcmVzb3VyY2VzL2FjY291bnRfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/package/resources/account_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'account' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(account)) - end - - context 'create' do - let(:chef_run) { runner.converge('test_package::account_create') } - - it 'creates user' do - expect(chef_run).to create_user('foobar').with_username('foo') - expect(chef_run).to create_group('foobar').with_group_name('bar') - end - end - - context 'remove' do - let(:chef_run) { runner.converge('test_package::account_remove') } - - it 'remove user' do - expect(chef_run).to remove_user('foo') - expect(chef_run).to remove_group('bar') - end - end -end diff --git a/spec/chef/package/resources/env_dir_spec.rb b/spec/chef/package/resources/env_dir_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvcmVzb3VyY2VzL2Vudl9kaXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/package/resources/env_dir_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'env_dir' do - let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(env_dir)) } - let(:chef_run) { runner.converge("test_package::env_dir_create") } - - after do - FileUtils.rm_rf('/tmp/env') - end - - it 'creates env directory' do - expect(chef_run).to create_directory('/tmp/env') - end - - it 'creates env variable' do - expect(chef_run).to create_file('/tmp/env/FOO').with_content('Lorem') - expect(chef_run).to create_file('/tmp/env/BAR').with_content('Ipsum') - end - - it 'deletes extraneous files' do - FileUtils.mkdir_p('/tmp/env') - FileUtils.touch('/tmp/env/erase-me') - - expect(chef_run).to delete_file('/tmp/env/erase-me') - end -end diff --git a/spec/chef/package/resources/gitlab_sysctl_spec.rb b/spec/chef/package/resources/gitlab_sysctl_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvcmVzb3VyY2VzL2dpdGxhYl9zeXNjdGxfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/package/resources/gitlab_sysctl_spec.rb +++ /dev/null @@ -1,56 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'sysctl' do - let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(gitlab_sysctl)) } - let(:chef_run) { runner.converge("package::sysctl", "test_package::gitlab_sysctl_create") } - let(:conf) { '90-omnibus-gitlab-foo.conf' } - - context 'with default values' do - it 'creates sysctl.d directory for the service' do - expect(chef_run).to create_directory('/etc/sysctl.d').with(mode: '0755', recursive: true) - end - - it 'creates conf file for the service and loads it' do - expect(chef_run).to create_file("create /opt/gitlab/embedded/etc/#{conf} foo").with_content("foo = 15000\n") - - resource = chef_run.file("create /opt/gitlab/embedded/etc/#{conf} foo") - expect(resource).to notify("execute[load sysctl conf foo]").to(:run).delayed - end - - it 'links conf file to /etc/sysctl.d location' do - expect(chef_run).to create_link("/etc/sysctl.d/#{conf}") - end - - it 'loaded the settings' do - resource = chef_run.execute('load sysctl conf foo') - expect(resource.command).to eq("sysctl -e -p /opt/gitlab/embedded/etc/#{conf}") - expect(resource).to do_nothing - end - end - - context 'when modify_kernel_parameters is false' do - let(:chef_run) { runner.converge("gitlab::config", "package::sysctl", "test_package::gitlab_sysctl_create") } - - before do - allow(Gitlab).to receive(:[]).and_call_original - - stub_gitlab_rb( - package: { - modify_kernel_parameters: false - } - ) - end - - it 'do not create sysctl.d directory' do - expect(chef_run).not_to create_directory('/etc/sysctl.d') - end - - it 'does not create conf file' do - expect(chef_run).not_to create_file("create /opt/gitlab/embedded/etc/#{conf} foo") - end - - it 'do not link conf file to /etc/sysctl.d location' do - expect(chef_run).not_to create_link("/etc/sysctl.d/#{conf}") - end - end -end diff --git a/spec/chef/package/resources/storage_directory_spec.rb b/spec/chef/package/resources/storage_directory_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvcmVzb3VyY2VzL3N0b3JhZ2VfZGlyZWN0b3J5X3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/package/resources/storage_directory_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'storage_directory' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(storage_directory)) do |node| - node.normal['gitlab']['manage-storage-directories']['enable'] = true - end - end - let(:chef_run) { runner.converge("test_package::storage_directory_create") } - - it 'executes correct ruby block' do - allow_any_instance_of(StorageDirectoryHelper).to receive(:ensure_directory_exists).and_return(true) - allow_any_instance_of(StorageDirectoryHelper).to receive(:ensure_permissions_set).and_return(true) - allow_any_instance_of(StorageDirectoryHelper).to receive(:validate!).and_return(true) - - expect(chef_run).to run_ruby_block('directory resource: /tmp/bar') - expect_any_instance_of(StorageDirectoryHelper).to receive(:ensure_directory_exists) - - ruby_block = chef_run.ruby_block('directory resource: /tmp/bar') - ruby_block.block.call - end -end diff --git a/spec/chef/package/resources/templatesymlink_spec.rb b/spec/chef/package/resources/templatesymlink_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvcmVzb3VyY2VzL3RlbXBsYXRlc3ltbGlua19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/package/resources/templatesymlink_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'templatesymlink' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)) - end - - context 'create' do - let(:chef_run) { runner.converge('test_package::templatesymlink_create') } - - it 'creates symlinks' do - expect(chef_run).to create_link('/opt/gitlab/embedded/service/gitlab-rails/config/database.yml') - end - - it 'populates conf file' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/database.yml').with_content('value: 500') - end - end - context 'delete' do - let(:chef_run) { runner.converge('test_package::templatesymlink_delete') } - - it 'deletes symlinks' do - expect(chef_run).to delete_link('/opt/gitlab/embedded/service/gitlab-rails/config/database.yml') - end - end -end diff --git a/spec/chef/package/resources/version_file_spec.rb b/spec/chef/package/resources/version_file_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhY2thZ2UvcmVzb3VyY2VzL3ZlcnNpb25fZmlsZV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/package/resources/version_file_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'chef_helper' - -version_file_path = '/tmp/VERSION_TEST' -version_check_cmd = 'echo 1.0.0-test' - -# Collect version as the custom resource would -version = VersionHelper.version(version_check_cmd) - -RSpec.describe 'version_file' do - let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(version_file)) } - let(:chef_run) { runner.converge("test_package::version_file_create") } - - after do - FileUtils.rm_rf(version_file_path) - end - - it 'creates version file' do - expect(chef_run).to create_file(version_file_path).with_content(version) - end - - it 'restarts service' do - expect(chef_run.version_file('Test version file creation')).to notify('runit_service[foo]').to(:hup) - end -end diff --git a/spec/chef/patroni/libraries/patroni_helper_spec.rb b/spec/chef/patroni/libraries/patroni_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhdHJvbmkvbGlicmFyaWVzL3BhdHJvbmlfaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/patroni/libraries/patroni_helper_spec.rb +++ /dev/null @@ -1,144 +0,0 @@ -require 'chef_helper' - -RSpec.describe PatroniHelper do - let(:chef_run) do - ChefSpec::SoloRunner.new(step_into: %w(patroni)).converge('gitlab-ee::default') - end - - subject(:helper) { PatroniHelper.new(chef_run.node) } - let(:pg_helper) { PgHelper.new(chef_run.node) } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe '#ctl_command' do - it 'returns a full path to the ctl_command' do - expect(helper.ctl_command).to eq('/opt/gitlab/embedded/bin/patronictl') - end - end - - describe '#bootstrapped?' do - before do - allow(File).to receive(:exist?).and_call_original - end - - it 'returns true when patroni.dynamic.json exists in postgresql data directory' do - allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/patroni.dynamic.json').and_return(true) - - expect(helper.bootstrapped?).to eq(true) - end - - it 'returns false when patroni.dynamic.json does not exist in postgresql data directory' do - allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/patroni.dynamic.json').and_return(false) - - expect(helper.bootstrapped?).to eq(false) - end - end - - describe '#dynamic_settings' do - it 'returns a hash with required keys' do - expected_root_keys = PatroniHelper::DCS_ATTRIBUTES + %w[postgresql slots] - - expect(helper.dynamic_settings(pg_helper).keys).to match_array(expected_root_keys) - end - - context 'with standby cluster enabled' do - it 'includes standby cluster attributes' do - stub_gitlab_rb( - patroni: { - enable: true, - standby_cluster: { - enable: true - } - } - ) - - expected_root_keys = PatroniHelper::DCS_ATTRIBUTES + %w[postgresql slots standby_cluster] - - expect(helper.dynamic_settings(pg_helper).keys).to match_array(expected_root_keys) - end - end - end - - describe '#node_status' do - context 'when Patroni service is down' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('patroni').and_return(false) - end - - it 'returns not running' do - expect(helper.node_status).to eq 'not running' - end - end - - context 'when Patroni service is up' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('patroni').and_return(true) - - result = spy('shellout') - allow(helper).to receive(:do_shell_out).and_return(result) - allow(result).to receive(:stdout).and_return(' running') - end - - it 'returns running current node state' do - expect(helper.node_status).to eq 'running' - end - end - end - - describe '#public_attributes' do - context 'when patroni is enabled' do - it 'returns a hash with required keys' do - stub_gitlab_rb( - patroni: { - enable: true - } - ) - - expected_patroni_keys = %w(config_dir data_dir log_dir api_address) - - expect(helper.public_attributes.keys).to match_array('patroni') - expect(helper.public_attributes['patroni'].keys).to match_array(expected_patroni_keys) - end - end - - context 'when patroni is enabled with tls api' do - it 'returns a hash with required keys' do - stub_gitlab_rb( - patroni: { - enable: true, - tls_certificate_file: '/path/to/crt.pem', - tls_key_file: '/path/to/key.pem', - tls_key_password: 'fakepassword', - tls_ca_file: '/path/to/ca.pem', - tls_ciphers: 'CIPHERS LIST', - tls_client_mode: 'optional', - tls_client_certificate_file: '/path/to/client.pem', - tls_client_key_file: '/path/to/client.key' - } - ) - - expected_tls_cfg = { - 'tls_verify' => true, - 'ca_file' => '/path/to/ca.pem', - 'verify_client' => true, - 'client_cert' => '/path/to/client.pem', - 'client_key' => '/path/to/client.key' - } - - expect(helper.public_attributes.keys).to match_array('patroni') - expect(helper.public_attributes['patroni']).to include(expected_tls_cfg) - expect(helper.public_attributes['patroni']['api_address']).to start_with('https://') - end - end - - context 'when patroni is disabled' do - it 'returns an empty hash' do - expect(helper.public_attributes).to be_empty - end - end - end -end diff --git a/spec/chef/patroni/recipes/patroni_spec.rb b/spec/chef/patroni/recipes/patroni_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BhdHJvbmkvcmVjaXBlcy9wYXRyb25pX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/patroni/recipes/patroni_spec.rb +++ /dev/null @@ -1,734 +0,0 @@ -require 'chef_helper' -require 'yaml' - -RSpec.describe 'patroni cookbook' do - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - let(:chef_run) do - ChefSpec::SoloRunner.new(step_into: %w(database_objects)).converge('gitlab-ee::default') - end - - it 'should be disabled by default' do - expect(chef_run).to include_recipe('patroni::disable') - end - - context 'when postgres_role is enabled' do - before do - stub_gitlab_rb(roles: %w(postgres_role)) - end - - it 'should be disabled' do - expect(chef_run).to include_recipe('patroni::disable') - end - end - - context 'when patroni_role is configured' do - before do - stub_gitlab_rb(roles: %w(patroni_role)) - end - - it 'should be enabled' do - expect(chef_run).to include_recipe('patroni::enable') - end - end - - context 'when patroni_role and postgres_role are configured' do - before do - stub_gitlab_rb(roles: %w(postgres_role patroni_role)) - end - - it 'should be enabled' do - expect(chef_run).to include_recipe('patroni::enable') - end - end - - context 'when enabled with default config' do - before do - stub_gitlab_rb( - roles: %w(patroni_role), - postgresql: { - pgbouncer_user_password: '' - } - ) - allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:bootstrapped?).and_return(false) - allow_any_instance_of(PgHelper).to receive(:is_replica?).and_return(false) - end - - let(:default_patroni_config_pg13) do - { - name: 'fauxhai.local', - scope: 'postgresql-ha', - log: { - level: 'INFO' - }, - consul: { - url: 'http://127.0.0.1:8500', - service_check_interval: '10s', - register_service: true, - checks: [], - }, - postgresql: { - bin_dir: '/opt/gitlab/embedded/bin', - data_dir: '/var/opt/gitlab/postgresql/data', - config_dir: '/var/opt/gitlab/postgresql/data', - listen: :'5432', - connect_address: "#{Patroni.private_ipv4}:5432", - use_unix_socket: true, - parameters: { - unix_socket_directories: '/var/opt/gitlab/postgresql' - }, - authentication: { - superuser: { - username: 'gitlab-psql' - }, - replication: { - username: 'gitlab_replicator' - }, - }, - remove_data_directory_on_diverged_timelines: false, - remove_data_directory_on_rewind_failure: false, - basebackup: [ - 'no-password' - ], - }, - bootstrap: { - dcs: { - loop_wait: 10, - ttl: 30, - retry_timeout: 10, - maximum_lag_on_failover: 1_048_576, - max_timelines_history: 0, - master_start_timeout: 300, - postgresql: { - use_pg_rewind: true, - use_slots: true, - parameters: { - wal_level: 'replica', - hot_standby: 'on', - wal_keep_size: 160, - max_replication_slots: 5, - max_connections: 200, - max_locks_per_transaction: 128, - max_worker_processes: 8, - max_wal_senders: 5, - checkpoint_timeout: 30, - max_prepared_transactions: 0, - track_commit_timestamp: 'off', - wal_log_hints: 'off' - }, - }, - slots: {}, - }, - method: 'gitlab_ctl', - gitlab_ctl: { - command: '/opt/gitlab/bin/gitlab-ctl patroni bootstrap --srcdir=/var/opt/gitlab/patroni/data' - } - }, - restapi: { - listen: :'8008', - connect_address: "#{Patroni.private_ipv4}:8008", - allowlist_include_members: false - }, - } - end - - let(:default_patroni_config) do - { - name: 'fauxhai.local', - scope: 'postgresql-ha', - log: { - level: 'INFO' - }, - consul: { - url: 'http://127.0.0.1:8500', - service_check_interval: '10s', - register_service: true, - checks: [], - }, - postgresql: { - bin_dir: '/opt/gitlab/embedded/bin', - data_dir: '/var/opt/gitlab/postgresql/data', - config_dir: '/var/opt/gitlab/postgresql/data', - listen: :'5432', - connect_address: "#{Patroni.private_ipv4}:5432", - use_unix_socket: true, - parameters: { - unix_socket_directories: '/var/opt/gitlab/postgresql' - }, - authentication: { - superuser: { - username: 'gitlab-psql' - }, - replication: { - username: 'gitlab_replicator' - }, - }, - remove_data_directory_on_diverged_timelines: false, - remove_data_directory_on_rewind_failure: false, - basebackup: [ - 'no-password' - ], - }, - bootstrap: { - dcs: { - loop_wait: 10, - ttl: 30, - retry_timeout: 10, - maximum_lag_on_failover: 1_048_576, - max_timelines_history: 0, - master_start_timeout: 300, - postgresql: { - use_pg_rewind: true, - use_slots: true, - parameters: { - wal_level: 'replica', - hot_standby: 'on', - wal_keep_segments: 10, - max_replication_slots: 5, - max_connections: 200, - max_locks_per_transaction: 128, - max_worker_processes: 8, - max_wal_senders: 5, - checkpoint_timeout: 30, - max_prepared_transactions: 0, - track_commit_timestamp: 'off', - wal_log_hints: 'off' - }, - }, - slots: {}, - }, - method: 'gitlab_ctl', - gitlab_ctl: { - command: '/opt/gitlab/bin/gitlab-ctl patroni bootstrap --srcdir=/var/opt/gitlab/patroni/data' - } - }, - restapi: { - listen: :'8008', - connect_address: "#{Patroni.private_ipv4}:8008", - allowlist_include_members: false - }, - } - end - - it 'should enable patroni service and disable postgresql runit service' do - expect(chef_run).to enable_runit_service('patroni') - expect(chef_run).to disable_runit_service('postgresql') - end - - it 'should notify patroni service to hup' do - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('patroni').and_return(true) - - expect(chef_run.template('/var/opt/gitlab/patroni/patroni.yaml')).to notify('runit_service[patroni]').to(:hup) - end - - it 'should skip standalone postgresql configuration' do - expect(chef_run).to create_postgresql_config('gitlab') - expect(chef_run.postgresql_config('gitlab')).not_to notify('execute[start postgresql]').to(:run) - expect(chef_run).not_to run_execute(/(start|reload) postgresql/) - end - - it 'should create database objects (roles, databses, extension)' do - expect(chef_run).not_to run_execute('/opt/gitlab/embedded/bin/initdb -D /var/opt/gitlab/postgresql/data -E UTF8') - expect(chef_run).to create_postgresql_user('gitlab') - expect(chef_run).to create_postgresql_user('gitlab_replicator') - expect(chef_run).to create_pgbouncer_user('rails') - expect(chef_run).to create_postgresql_database('gitlabhq_production') - expect(chef_run).to enable_postgresql_extension('pg_trgm') - expect(chef_run).to enable_postgresql_extension('btree_gist') - end - - it 'should create patroni configuration file for PostgreSQL 12' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| - expect(YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true)).to eq(default_patroni_config) - } - end - - it 'should create patroni configuration file for PostgreSQL 13' do - allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('13.0')) - allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('13.0')) - - expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| - expect(YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true)).to eq(default_patroni_config_pg13) - } - end - end - - context 'when enabled with specific config' do - before do - stub_gitlab_rb( - roles: %w(postgres_role), - postgresql: { - username: 'test_psql_user', - sql_user: 'test_sql_user', - sql_user_password: 'dbda601b8d4dc3d1697ef84dbbb8e61b', - sql_replication_user: 'test_sql_replication_user', - sql_replication_password: '48e84afb4b268128ac14f7c66fc7af42', - pgbouncer_user: 'test_pgbouncer_user', - pgbouncer_user_password: '2bc94731612abb74aea7805a41dfcb09', - connect_port: 15432, - }, - patroni: { - enable: true, - scope: 'test-scope', - name: 'test-node-name', - log_level: 'DEBUG', - loop_wait: 20, - ttl: 60, - master_start_timeout: 600, - use_slots: false, - use_pg_rewind: true, - connect_address: '1.2.3.4', - connect_port: 18008, - username: 'gitlab', - password: 'restapipassword', - replication_password: 'fakepassword', - allowlist: ['1.2.3.4/32', '127.0.0.1/32'], - allowlist_include_members: false, - remove_data_directory_on_diverged_timelines: true, - remove_data_directory_on_rewind_failure: true, - replication_slots: { - 'geo_secondary' => { 'type' => 'physical' } - }, - consul: { - service_check_interval: '20s' - }, - postgresql: { - wal_keep_segments: 16, - max_wal_senders: 4, - max_replication_slots: 4 - }, - tags: { - nofailover: true - }, - callbacks: { - on_role_change: "/patroni/scripts/post-failover-maintenance.sh" - }, - recovery_conf: { - restore_command: "/opt/wal-g/bin/wal-g wal-fetch %f %p" - } - } - ) - end - - it 'should be reflected in patroni configuration file' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg).to include( - name: 'test-node-name', - scope: 'test-scope', - log: { - level: 'DEBUG' - }, - tags: { - nofailover: true - } - ) - expect(cfg[:consul][:service_check_interval]).to eq('20s') - expect(cfg[:postgresql][:connect_address]).to eq('1.2.3.4:15432') - expect(cfg[:postgresql][:authentication]).to eq( - superuser: { - username: 'test_psql_user' - }, - replication: { - username: 'test_sql_replication_user', - password: 'fakepassword' - } - ) - expect(cfg[:postgresql][:callbacks]).to eq( - on_role_change: "/patroni/scripts/post-failover-maintenance.sh" - ) - expect(cfg[:postgresql][:recovery_conf]).to eq( - restore_command: "/opt/wal-g/bin/wal-g wal-fetch %f %p" - ) - expect(cfg[:restapi]).to include( - connect_address: '1.2.3.4:18008', - authentication: { - username: 'gitlab', - password: 'restapipassword' - }, - allowlist: ['1.2.3.4/32', '127.0.0.1/32'], - allowlist_include_members: false - ) - expect(cfg[:bootstrap][:dcs]).to include( - loop_wait: 20, - ttl: 60, - master_start_timeout: 600, - slots: { - geo_secondary: { type: 'physical' } - } - ) - expect(cfg[:bootstrap][:dcs][:postgresql]).to include( - use_slots: false, - use_pg_rewind: true - ) - expect(cfg[:bootstrap][:dcs][:postgresql][:parameters]).to include( - wal_keep_segments: 16, - max_wal_senders: 4, - max_replication_slots: 4 - ) - expect(cfg[:postgresql][:remove_data_directory_on_rewind_failure]).to be true - expect(cfg[:postgresql][:remove_data_directory_on_diverged_timelines]).to be true - } - end - - it 'should reflect into dcs config file' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg).to include( - loop_wait: 20, - ttl: 60, - master_start_timeout: 600, - slots: { - geo_secondary: { type: 'physical' } - } - ) - expect(cfg[:postgresql]).to include( - use_slots: false, - use_pg_rewind: true - ) - expect(cfg[:postgresql][:parameters]).to include( - wal_keep_segments: 16, - max_wal_senders: 4, - max_replication_slots: 4 - ) - } - end - end - - context 'when standby cluster is enabled' do - before do - stub_gitlab_rb( - roles: %w(postgres_role), - patroni: { - enable: true, - use_pg_rewind: true, - replication_password: 'fakepassword', - standby_cluster: { - enable: true, - host: '1.2.3.4', - port: 5432, - primary_slot_name: 'geo_secondary' - } - }, - postgresql: { - sql_user_password: 'a4125c87ce2572ce271cd77e0de9a0ad', - sql_replication_password: 'e64b415e9b9a34ac7ac6e53ae16ccacb', - md5_auth_cidr_addresses: '1.2.3.4/32' - } - ) - end - - it 'should be reflected in patroni configuration file' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg[:postgresql][:authentication]).to include( - replication: { - username: 'gitlab_replicator', - password: 'fakepassword' - } - ) - expect(cfg[:bootstrap][:dcs]).to include( - standby_cluster: { - host: '1.2.3.4', - port: 5432, - primary_slot_name: 'geo_secondary' - } - ) - expect(cfg[:bootstrap][:dcs][:postgresql]).to include( - use_pg_rewind: true - ) - } - end - - it 'should reflect into dcs config file' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg).to include( - standby_cluster: { - host: '1.2.3.4', - port: 5432, - primary_slot_name: 'geo_secondary' - } - ) - expect(cfg[:postgresql]).to include( - use_pg_rewind: true - ) - } - end - end - - context 'when building a cluster' do - before do - stub_gitlab_rb( - roles: %w(postgres_role), - patroni: { - enable: true - } - ) - end - - context 'from scratch' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_return(false) - end - - it 'should enable patroni service and disable postgresql runit service' do - expect(chef_run).to enable_runit_service('patroni') - expect(chef_run).to disable_runit_service('postgresql') - end - end - - context 'converting a standalone instance to a cluster member' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_return(true) - allow_any_instance_of(PatroniHelper).to receive(:node_status).and_return('running') - end - - it 'should signal to node to restart postgresql and disable its runit service' do - expect(chef_run).to enable_runit_service('patroni') - expect(chef_run).to disable_runit_service('postgresql') - expect(chef_run).to run_execute('signal to restart postgresql') - end - end - - context 'on a replica' do - before do - allow_any_instance_of(PgHelper).to receive(:replica?).and_return(true) - end - - it 'should not create database objects' do - expect(chef_run).not_to create_postgresql_user('gitlab') - expect(chef_run).not_to create_postgresql_user('gitlab_replicator') - expect(chef_run).not_to create_pgbouncer_user('patroni') - expect(chef_run).not_to run_execute('create gitlabhq_production database') - expect(chef_run).not_to enable_postgresql_extension('pg_trgm') - expect(chef_run).not_to enable_postgresql_extension('btree_gist') - end - end - end - - context 'postgresql dynamic configuration' do - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'with no explicit override' do - before do - stub_gitlab_rb( - roles: %w(postgres_role), - patroni: { - enable: true - } - ) - end - - it 'should use default values from postgresql cookbook and handle corner cases' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg[:postgresql][:use_pg_rewind]).to be(true) - expect(cfg[:postgresql][:parameters]).to include( - max_connections: 200, - max_locks_per_transaction: 128, - max_worker_processes: 8, - max_prepared_transactions: 0, - track_commit_timestamp: 'off', - wal_log_hints: 'off', - max_wal_senders: 5, - max_replication_slots: 5, - wal_keep_segments: 10, - checkpoint_timeout: 30 - ) - } - end - end - - context 'with no explicit override and non-default postgresql settings' do - before do - stub_gitlab_rb( - roles: %w(postgres_role), - patroni: { - enable: true, - }, - postgresql: { - max_connections: 123, - max_locks_per_transaction: 321, - max_worker_processes: 12, - wal_log_hints: 'foo', - max_wal_senders: 11, - max_replication_slots: 13, - } - ) - end - - it 'should use default values from postgresql cookbook' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg[:postgresql][:use_pg_rewind]).to be(true) - expect(cfg[:postgresql][:parameters]).to include( - max_connections: 123, - max_locks_per_transaction: 321, - max_worker_processes: 12, - max_prepared_transactions: 0, - track_commit_timestamp: 'off', - wal_log_hints: 'foo', - max_wal_senders: 11, - max_replication_slots: 13, - wal_keep_segments: 10, - checkpoint_timeout: 30 - ) - } - end - end - - context 'with explicit override' do - before do - stub_gitlab_rb( - roles: %w(postgres_role), - patroni: { - enable: true, - postgresql: { - max_connections: 100, - max_locks_per_transaction: 64, - max_worker_processes: 4, - wal_log_hints: 'on', - max_wal_senders: 0, - max_replication_slots: 0, - checkpoint_timeout: '5min' - } - } - ) - end - - it 'should use default values from postgresql cookbook' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg[:postgresql][:use_pg_rewind]).to be(true) - expect(cfg[:postgresql][:parameters]).to include( - max_connections: 100, - max_locks_per_transaction: 64, - max_worker_processes: 4, - max_wal_senders: 0, - max_replication_slots: 0, - wal_log_hints: 'on' - ) - } - end - end - - context 'when pg_rewind is enabled' do - before do - stub_gitlab_rb( - roles: %w(postgres_role), - patroni: { - enable: true, - use_pg_rewind: true, - remove_data_directory_on_diverged_timelines: true, - remove_data_directory_on_rewind_failure: true - } - ) - end - - it 'should use default values from postgresql cookbook' do - expect(chef_run).to render_file('/var/opt/gitlab/patroni/dcs.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg[:postgresql][:use_pg_rewind]).to be(true) - expect(cfg[:postgresql][:parameters]).to include( - max_connections: 200, - max_locks_per_transaction: 128, - max_worker_processes: 8, - wal_log_hints: 'on' - ) - } - end - end - end - - context 'when patroni is enabled but consul is not' do - let(:chef_run) do - converge_config('gitlab-ee::default', is_ee: true) - end - - before do - stub_gitlab_rb( - patroni: { - enable: true - } - ) - end - - it 'expects a warning to be printed' do - chef_run - - expect_logged_warning(/Patroni is enabled but Consul seems to be disabled/) - end - end - - context 'when tls is enabled' do - it 'should only set the path to the tls certificate and key' do - stub_gitlab_rb( - roles: %w(postgres_role), - patroni: { - enable: true, - tls_certificate_file: '/path/to/crt.pem', - tls_key_file: '/path/to/key.pem' - } - ) - - expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg[:restapi][:certfile]).to eq('/path/to/crt.pem') - expect(cfg[:restapi][:keyfile]).to eq('/path/to/key.pem') - expect(cfg[:restapi][:keyfile_password]).to be(nil) - expect(cfg[:restapi][:cafile]).to be(nil) - expect(cfg[:restapi][:ciphers]).to be(nil) - expect(cfg[:restapi][:verify_client]).to be(nil) - expect(cfg[:ctl]).to be(nil) - } - end - - it 'should set all available tls configuration including tls certificate and key paths' do - stub_gitlab_rb( - roles: %w(postgres_role), - patroni: { - enable: true, - tls_certificate_file: '/path/to/crt.pem', - tls_key_file: '/path/to/key.pem', - tls_key_password: 'fakepassword', - tls_ca_file: '/path/to/ca.pem', - tls_ciphers: 'CIPHERS LIST', - tls_client_mode: 'optional', - tls_client_certificate_file: '/path/to/client.pem', - tls_client_key_file: '/path/to/client.key' - } - ) - - expect(chef_run).to render_file('/var/opt/gitlab/patroni/patroni.yaml').with_content { |content| - cfg = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) - - expect(cfg[:restapi][:certfile]).to eq('/path/to/crt.pem') - expect(cfg[:restapi][:keyfile]).to eq('/path/to/key.pem') - expect(cfg[:restapi][:keyfile_password]).to eq('fakepassword') - expect(cfg[:restapi][:cafile]).to eq('/path/to/ca.pem') - expect(cfg[:restapi][:ciphers]).to eq('CIPHERS LIST') - expect(cfg[:restapi][:verify_client]).to eq('optional') - expect(cfg[:ctl][:insecure]).to eq(false) - expect(cfg[:ctl][:certfile]).to eq('/path/to/client.pem') - expect(cfg[:ctl][:keyfile]).to eq('/path/to/client.key') - } - end - end -end diff --git a/spec/chef/pgbouncer/libraries/pgbouncer_helper_spec.rb b/spec/chef/pgbouncer/libraries/pgbouncer_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BnYm91bmNlci9saWJyYXJpZXMvcGdib3VuY2VyX2hlbHBlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/pgbouncer/libraries/pgbouncer_helper_spec.rb +++ /dev/null @@ -1,165 +0,0 @@ -require 'chef_helper' - -RSpec.describe PgbouncerHelper do - let(:chef_run) { converge_config(is_ee: true) } - subject { described_class.new(chef_run.node) } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe '#running?' do - cached(:chef_run) { converge_config(is_ee: true) } - - it 'returns true when pgbouncer is running' do - stub_service_success_status('pgbouncer', true) - - expect(subject.running?).to be_truthy - end - - it 'returns false when pgbouncer is not running' do - stub_service_success_status('pgbouncer', false) - - expect(subject.running?).to be_falsey - end - end - - describe '#pg_auth_type_prefix' do - using RSpec::Parameterized::TableSyntax - where(:type, :prefix) do - 'md5' | 'md5' - 'scram-sha-256' | 'SCRAM-SHA-256$' - 'MD5' | 'md5' - 'SCRAM-SHA-256' | 'SCRAM-SHA-256$' - 'plain' | nil - 'ScRaM-ShA-256' | 'SCRAM-SHA-256$' - end - - with_them do - it 'responds to default values' do - expect(subject.pg_auth_type_prefix(type)).to eq(prefix) - end - end - end - - describe '#pgbouncer_admin_config' do - context 'by default' do - it 'by default' do - expect(subject.pgbouncer_admin_config).to eq('user=pgbouncer dbname=pgbouncer sslmode=disable port=6432 host=/var/opt/gitlab/pgbouncer') - end - end - - context 'with custom sttings' do - before do - stub_gitlab_rb( - postgresql: { - pgbouncer_user: 'tester', - }, - pgbouncer: { - listen_port: 1234, - data_directory: '/tmp' - } - ) - end - - it 'uses custom settings' do - expect(subject.pgbouncer_admin_config).to eq('user=tester dbname=pgbouncer sslmode=disable port=1234 host=/tmp') - end - end - end - - describe '#pg_auth_users' do - context 'by default' do - it 'by default' do - expect(subject.pg_auth_users).to be_empty - end - end - - context 'with users' do - before do - stub_gitlab_rb( - pgbouncer: { - users: { - fakeuser: { - password: 'fakepassword' - } - } - } - ) - end - - it 'returns a hash' do - expect(subject.pg_auth_users).to eq 'fakeuser' => { 'password' => 'fakepassword' } - end - end - - context 'with databases' do - before do - stub_gitlab_rb( - pgbouncer: { - databases: { - fakedb: { - user: 'fakeuser', - password: 'fakepassword' - } - } - } - ) - end - - it 'returns a hash' do - expect(subject.pg_auth_users).to eq 'fakeuser' => { 'password' => 'fakepassword' } - end - end - - context 'with both' do - before do - stub_gitlab_rb( - pgbouncer: { - databases: { - fakedb: { - user: 'fakedbuser', - password: 'fakepassword' - } - }, - users: { - fakeusersuser: { - password: 'fakepassword' - } - } - } - ) - end - - it 'should merge both' do - expected = { - 'fakeusersuser' => { 'password' => 'fakepassword' }, 'fakedbuser' => { 'password' => 'fakepassword' } - } - expect(subject.pg_auth_users).to eq expected - end - end - - context 'with multiple databases' do - before do - stub_gitlab_rb( - pgbouncer: { - databases: { - fakedb_one: { - user: 'fakeuser', - password: 'fakepassword' - }, - fakedb_two: { - user: 'fakeuser', - password: 'fakepassword' - } - } - } - ) - end - - it 'should only have one entry' do - expect(subject.pg_auth_users).to eq 'fakeuser' => { 'password' => 'fakepassword' } - end - end - end -end diff --git a/spec/chef/pgbouncer/recipes/pgbouncer_spec.rb b/spec/chef/pgbouncer/recipes/pgbouncer_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BnYm91bmNlci9yZWNpcGVzL3BnYm91bmNlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/pgbouncer/recipes/pgbouncer_spec.rb +++ /dev/null @@ -1,395 +0,0 @@ -# -# Copyright:: Copyright (c) 2017 GitLab Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -require 'chef_helper' - -RSpec.describe 'pgbouncer' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - let(:pgbouncer_ini) { '/var/opt/gitlab/pgbouncer/pgbouncer.ini' } - let(:databases_json) { '/var/opt/gitlab/pgbouncer/databases.json' } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'when enabled' do - before do - stub_gitlab_rb( - pgbouncer: { - enable: true, - databases: { - gitlabhq_production: { - host: '1.2.3.4' - } - } - }, - postgresql: { - pgbouncer_user: 'fakeuser', - pgbouncer_user_password: 'fakeuserpassword' - } - ) - end - - it 'includes the pgbouncer recipe' do - expect(chef_run).to include_recipe('pgbouncer::enable') - end - - it 'includes the postgresql user recipe' do - expect(chef_run).to include_recipe('postgresql::user') - end - - it_behaves_like 'enabled runit service', 'pgbouncer', 'root', 'root' - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/pgbouncer/env').with_variables(default_vars) - end - - it 'creates the appropriate directories' do - expect(chef_run).to create_directory('/var/log/gitlab/pgbouncer') - expect(chef_run).to create_directory('/var/opt/gitlab/pgbouncer') - end - - it 'installs pgbouncer.ini with default values' do - # Default values are pulled from: - # https://github.com/pgbouncer/pgbouncer/blob/6ef66f0139b9c8a5c0747f2a6157d008b87bf0c5/etc/pgbouncer.ini - expect(chef_run).to render_file(pgbouncer_ini).with_content { |content| - expect(content).to match(/^listen_addr = 0\.0\.0\.0$/) - expect(content).to match(/^listen_port = 6432$/) - expect(content).to match(/^pool_mode = transaction$/) - expect(content).to match(/^server_reset_query = DISCARD ALL$/) - expect(content).to match(/^application_name_add_host = 1$/) - expect(content).to match(/^max_client_conn = 2048$/) - expect(content).to match(/^default_pool_size = 100$/) - expect(content).to match(/^min_pool_size = 0$/) - expect(content).to match(/^reserve_pool_size = 5$/) - expect(content).to match(/^reserve_pool_timeout = 5.0$/) - expect(content).to match(/^server_round_robin = 0$/) - expect(content).to match(/^auth_type = md5$/) - expect(content).to match(/^log_connections = 0/) - expect(content).to match(/^server_idle_timeout = 30.0$/) - expect(content).to match(/^dns_max_ttl = 15.0$/) - expect(content).to match(/^dns_zone_check_period = 0$/) - expect(content).to match(/^dns_nxdomain_ttl = 15.0$/) - expect(content).to match(%r{^auth_file = /var/opt/gitlab/pgbouncer/pg_auth$}) - expect(content).to match(/^admin_users = gitlab-psql, postgres, pgbouncer$/) - expect(content).to match(/^stats_users = gitlab-psql, postgres, pgbouncer$/) - expect(content).to match(/^ignore_startup_parameters = extra_float_digits$/) - expect(content).to match(%r{^unix_socket_dir = /var/opt/gitlab/pgbouncer$}) - expect(content).to match(%r{^%include /var/opt/gitlab/pgbouncer/databases.ini}) - expect(content).to match(/^unix_socket_mode = 0777$/) - expect(content).to match(/^client_tls_sslmode = disable$/) - expect(content).to match(/^client_tls_protocols = all$/) - expect(content).to match(/^client_tls_dheparams = auto$/) - expect(content).to match(/^client_tls_ecdhcurve = auto$/) - expect(content).to match(/^server_tls_sslmode = disable$/) - expect(content).to match(/^server_tls_protocols = all$/) - expect(content).to match(/^server_tls_ciphers = fast$/) - expect(content).to match(/^server_reset_query_always = 0$/) - expect(content).to match(/^server_check_query = select 1$/) - expect(content).to match(/^server_check_delay = 30$/) - expect(content).to match(/^syslog = 0$/) - expect(content).to match(/^syslog_facility = daemon$/) - expect(content).to match(/^syslog_ident = pgbouncer$/) - expect(content).to match(/^log_disconnections = 1$/) - expect(content).to match(/^log_pooler_errors = 1$/) - expect(content).to match(/^stats_period = 60$/) - expect(content).to match(/^verbose = 0$/) - expect(content).to match(/^server_lifetime = 3600$/) - expect(content).to match(/^server_connect_timeout = 15$/) - expect(content).to match(/^server_login_retry = 15$/) - expect(content).to match(/^query_timeout = 0$/) - expect(content).to match(/^query_wait_timeout = 120$/) - expect(content).to match(/^client_idle_timeout = 0$/) - expect(content).to match(/^client_login_timeout = 60$/) - expect(content).to match(/^autodb_idle_timeout = 3600$/) - expect(content).to match(/^suspend_timeout = 10$/) - expect(content).to match(/^idle_transaction_timeout = 0$/) - expect(content).to match(/^pkt_buf = 4096$/) - expect(content).to match(/^listen_backlog = 128$/) - expect(content).to match(/^sbuf_loopcnt = 5$/) - expect(content).to match(/^max_packet_size = 2147483647$/) - expect(content).to match(/^tcp_defer_accept = 0$/) - expect(content).to match(/^tcp_socket_buffer = 0$/) - expect(content).to match(/^tcp_keepalive = 1$/) - expect(content).to match(/^tcp_keepcnt = 0$/) - expect(content).to match(/^tcp_keepidle = 0$/) - expect(content).to match(/^tcp_keepintvl = 0$/) - expect(content).to match(/^disable_pqexec = 0$/) - expect(content).not_to match(/^logfile =/) - expect(content).not_to match(/^pidfile =/) - expect(content).not_to match(%r{^unix_socket_group =}) - expect(content).not_to match(%r{^client_tls_ca_file =}) - expect(content).not_to match(%r{^client_tls_cert_file =}) - expect(content).not_to match(%r{^server_tls_ca_file =}) - expect(content).not_to match(%r{^server_tls_key_file =}) - expect(content).not_to match(%r{^server_tls_cert_file =}) - expect(content).not_to match(%r{^max_db_connections =}) - expect(content).not_to match(%r{^max_user_connections =}) - } - end - - context 'pgbouncer.ini template changes' do - let(:template) { chef_run.template(pgbouncer_ini) } - - it 'stores the socket directory in a different location when set' do - stub_gitlab_rb( - pgbouncer: { - enable: true, - unix_socket_dir: '/fake/dir', - unix_socket_group: 'fakegroup', - client_tls_ca_file: '/fakecafile', - client_tls_cert_file: '/fakecertfile', - server_tls_ca_file: '/fakeservercafile', - server_tls_key_file: '/fakeserverkeyfile', - server_tls_cert_file: '/fakeservercertfile', - max_db_connections: 99999, - max_user_connections: 88888 - } - ) - expect(chef_run).to render_file(pgbouncer_ini).with_content { |content| - expect(content).to match(%r{^unix_socket_dir = /fake/dir$}) - expect(content).to match(%r{^unix_socket_group = fakegroup$}) - expect(content).to match(%r{^client_tls_ca_file = /fakecafile$}) - expect(content).to match(%r{^client_tls_cert_file = /fakecertfile$}) - expect(content).to match(%r{^server_tls_ca_file = /fakeservercafile$}) - expect(content).to match(%r{^server_tls_key_file = /fakeserverkeyfile$}) - expect(content).to match(%r{^server_tls_cert_file = /fakeservercertfile$}) - expect(content).to match(%r{^max_db_connections = 99999$}) - expect(content).to match(%r{^max_user_connections = 88888$}) - } - end - - it 'reloads pgbouncer and starts pgbouncer if it is not running' do - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('pgbouncer').and_return(true) - expect(template).to notify('execute[reload pgbouncer]').to(:run).immediately - end - end - - context 'databases.json' do - it 'creates databases.json' do - expect(chef_run).to create_file(databases_json) - .with_content("{\"gitlabhq_production\":{\"host\":\"1.2.3.4\"}}") - .with(user: 'root', group: 'gitlab-psql') - end - - it 'notifies pgb-notify to generate databases.ini' do - json_resource = chef_run.file(databases_json) - expect(json_resource).to notify('execute[generate databases.ini]').to(:run).immediately - end - - it 'does not run pgb-notify when databases.ini exists' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with('/var/opt/gitlab/pgbouncer/databases.ini').and_return(true) - expect(chef_run).not_to run_execute('generate databases.ini') - end - - it 'stores in a different location when attribute is set' do - stub_gitlab_rb( - pgbouncer: { - enable: true, - databases_json: '/fakepath/fakedatabases.json' - } - ) - expect(chef_run).to create_file('databases.json') - .with(path: '/fakepath/fakedatabases.json') - end - - it 'changes the user when the attribute is changed' do - stub_gitlab_rb( - pgbouncer: { - enable: true, - databases_ini_user: 'fakeuser' - } - ) - expect(chef_run).to create_file('databases.json') - .with(user: 'fakeuser', group: 'gitlab-psql') - end - end - end - - context 'authentication' do - let(:pg_auth) { '/var/opt/gitlab/pgbouncer/pg_auth' } - - it 'sets up auth_hba when attributes are set' do - stub_gitlab_rb( - { - pgbouncer: { - enable: true, - auth_hba_file: '/fake/hba_file', - auth_query: 'SELECT * FROM FAKETABLE' - } - } - ) - expect(chef_run).to render_file(pgbouncer_ini).with_content { |content| - expect(content).to match(%r{^auth_hba_file = /fake/hba_file$}) - expect(content).to match(/^auth_query = SELECT \* FROM FAKETABLE$/) - } - end - - it 'does not create the user file by default' do - expect(chef_run).not_to render_file(pg_auth) - end - - it 'creates the user file when the attributes are set' do - stub_gitlab_rb( - { - pgbouncer: { - enable: true, - databases: { - gitlabhq_production: { - password: 'fakemd5password', - user: 'fakeuser', - host: '127.0.0.1', - port: 5432 - } - } - } - } - ) - expect(chef_run).to render_file(pg_auth) - .with_content(%r{^"fakeuser" "md5fakemd5password"$}) - end - - it 'creates arbitrary user' do - stub_gitlab_rb( - { - pgbouncer: { - enable: true, - users: { - 'fakeuser': { - 'password': 'fakehash' - } - } - } - } - ) - expect(chef_run).to render_file(pg_auth) - .with_content(%r{^"fakeuser" "md5fakehash"}) - end - - it 'supports SCRAM secrets' do - stub_gitlab_rb( - pgbouncer: { - enable: true, - auth_type: 'scram-sha-256', - users: { - 'fakeuser': { - 'password': 'REALLYFAKEHASH' - } - } - } - ) - expect(chef_run).to render_file(pg_auth) - .with_content(%r{^"fakeuser" "SCRAM-SHA-256\$REALLYFAKEHASH"}) - end - - it 'supports a default auth type' do - stub_gitlab_rb( - pgbouncer: { - enable: true, - auth_type: 'scram-sha-256', - users: { - 'firstfakeuser': { - 'password': 'AREALLYFAKEHASH' - }, - 'secondfakeuser': { - 'password': 'ANOTHERREALLYFAKEHASH' - } - }, - databases: { - fakedb: { - user: 'databasefakeuser', - password: 'DATABASEHASH' - } - } - } - ) - expect(chef_run).to render_file(pg_auth).with_content { |content| - expect(content).to match(%r{^"firstfakeuser" "SCRAM-SHA-256\$AREALLYFAKEHASH"}) - expect(content).to match(%r{^"secondfakeuser" "SCRAM-SHA-256\$ANOTHERREALLYFAKEHASH"}) - expect(content).to match(%r{^"databasefakeuser" "SCRAM-SHA-256\$DATABASEHASH"}) - } - end - - it 'supports per user auth types' do - stub_gitlab_rb( - pgbouncer: { - enable: true, - users: { - 'firstfakeuser': { - 'password': 'AREALLYFAKEHASH' - }, - 'secondfakeuser': { - 'password': 'ANOTHERREALLYFAKEHASH', - 'auth_type': 'scram-sha-256' - } - }, - databases: { - fakedb: { - user: 'databasefakeuser', - auth_type: 'plain', - password: 'DATABASEHASH' - } - } - } - ) - expect(chef_run).to render_file(pg_auth).with_content { |content| - expect(content).to match(%r{^"firstfakeuser" "md5AREALLYFAKEHASH"}) - expect(content).to match(%r{^"secondfakeuser" "SCRAM-SHA-256\$ANOTHERREALLYFAKEHASH"}) - expect(content).to match(%r{^"databasefakeuser" "DATABASEHASH"}) - } - end - - context 'when disabled by default' do - it_behaves_like 'disabled runit service', 'pgbouncer' - - it 'includes the pgbouncer_disable recipe' do - expect(chef_run).to include_recipe('pgbouncer::disable') - end - end - end -end - -RSpec.describe 'gitlab-ee::default' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb( - { - pgbouncer: { - db_user_password: 'fakeuserpassword' - }, - postgresql: { - pgbouncer_user: 'fakeuser', - pgbouncer_user_password: 'fakeuserpassword' - } - } - ) - end - - it 'should create the pgbouncer user on the database' do - expect(chef_run).to include_recipe('pgbouncer::user') - expect(chef_run).to create_pgbouncer_user('rails').with( - password: 'fakeuserpassword' - ) - end -end diff --git a/spec/chef/pgbouncer/recipes/pgbouncer_user_spec.rb b/spec/chef/pgbouncer/recipes/pgbouncer_user_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3BnYm91bmNlci9yZWNpcGVzL3BnYm91bmNlcl91c2VyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/pgbouncer/recipes/pgbouncer_user_spec.rb +++ /dev/null @@ -1,118 +0,0 @@ -# -# Copyright:: Copyright (c) 2017 GitLab Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'chef_helper' - -RSpec.describe 'gitlab-ee::pgbouncer_user' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'create the rails pgbouncer user' do - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb( - { - postgresql: { - enable: true, - pgbouncer_user: 'pgbouncer-rails', - pgbouncer_user_password: 'fakepassword' - } - } - ) - end - - it 'should call pgbouncer_user with the correct values for rails' do - expect(chef_run).to create_pgbouncer_user('rails').with( - database: 'gitlabhq_production', - password: 'fakepassword', - user: 'pgbouncer-rails', - add_auth_function: true - ) - expect(chef_run).not_to create_pgbouncer_user('geo') - end - end - - context 'auth_query is not the default value' do - before do - stub_gitlab_rb( - { - pgbouncer: { - auth_query: 'SELECT * FROM FAKETABLE' - }, - postgresql: { - enable: true, - pgbouncer_user: 'pgbouncer-rails', - pgbouncer_user_password: 'fakepassword' - } - } - ) - end - - it 'should not create the pg_shadow_lookup function' do - expect(chef_run).to create_pgbouncer_user('rails').with( - add_auth_function: false - ) - end - end - - context 'no pgbouncer user' do - before do - stub_gitlab_rb( - { - postgresql: { - enable: true, - }, - geo_postgresql: { - enable: true, - } - } - ) - end - - it 'should not create the pgbouncer user' do - expect(chef_run).not_to create_pgbouncer_user('rails') - expect(chef_run).not_to create_pgbouncer_user('geo') - end - end - - context 'create the geo pgbouncer user' do - before do - stub_gitlab_rb( - { - geo_postgresql: { - enable: true, - pgbouncer_user: 'pgbouncer-geo', - pgbouncer_user_password: 'fakepassword' - } - } - ) - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:user_exists?).with('pgbouncer-geo').and_return(false) - end - - it 'should call pgbouncer_user with the correct values for geo' do - expect(chef_run).to create_pgbouncer_user('geo').with( - database: 'gitlabhq_geo_production', - password: 'fakepassword', - user: 'pgbouncer-geo', - add_auth_function: true - ) - expect(chef_run).not_to create_pgbouncer_user('rails') - end - end -end diff --git a/spec/chef/postgresql/recipes/sysctl_spec.rb b/spec/chef/postgresql/recipes/sysctl_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3Bvc3RncmVzcWwvcmVjaXBlcy9zeXNjdGxfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/postgresql/recipes/sysctl_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql::user' do - cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config', 'postgresql::sysctl') } - - it 'creates sysctl files' do - expect(chef_run).to create_gitlab_sysctl('kernel.shmmax').with_value(17179869184) - expect(chef_run).to create_gitlab_sysctl('kernel.shmall').with_value(4194304) - end -end diff --git a/spec/chef/postgresql/recipes/user_spec.rb b/spec/chef/postgresql/recipes/user_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3Bvc3RncmVzcWwvcmVjaXBlcy91c2VyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/postgresql/recipes/user_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql::user' do - cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config', 'postgresql::user') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - it 'includes postgresql::directory_locations recipe' do - expect(chef_run).to include_recipe('postgresql::directory_locations') - end - - context 'with default attributes' do - it 'creates postgresql user and groups' do - account_params = { - username: 'gitlab-psql', - groupname: 'gitlab-psql', - home: '/var/opt/gitlab/postgresql', - shell: '/bin/sh' - } - - expect(chef_run).to create_account('Postgresql user and group').with(account_params) - end - - it 'creates postgresql base directory' do - directory_params = { - owner: 'gitlab-psql', - mode: '0755', - recursive: true - } - expect(chef_run).to create_directory('/var/opt/gitlab/postgresql').with(directory_params) - end - - it 'creates a .profile with bundled PostgreSQL PATH' do - file_params = { - owner: 'gitlab-psql', - content: %r{PATH=/opt/gitlab/embedded/bin:/opt/gitlab/bin:\$PATH} - } - - expect(chef_run).to create_file('/var/opt/gitlab/postgresql/.profile').with(file_params) - end - end - - context 'when using custom values for directories' do - cached(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config', 'postgresql::user') } - - before do - stub_gitlab_rb( - postgresql: { - home: '/mypghomedir' - }) - end - - it 'creates postgresql user and groups' do - account_params = { - username: 'gitlab-psql', - groupname: 'gitlab-psql', - home: '/mypghomedir', - shell: '/bin/sh' - } - - expect(chef_run).to create_account('Postgresql user and group').with(account_params) - end - - it 'creates postgresql base directory' do - directory_params = { - owner: 'gitlab-psql', - mode: '0755', - recursive: true - } - expect(chef_run).to create_directory('/mypghomedir').with(directory_params) - end - - it 'creates necessary directories' do - expect(chef_run).to create_directory('/mypghomedir').with(owner: 'gitlab-psql', mode: '0755', recursive: true) - end - end -end diff --git a/spec/chef/postgresql/resources/config_spec.rb b/spec/chef/postgresql/resources/config_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3Bvc3RncmVzcWwvcmVzb3VyY2VzL2NvbmZpZ19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/postgresql/resources/config_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql_config' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(postgresql_config)) do |node| - node.normal['postgresql']['dir'] = '/fakedir' - node.normal['patroni']['dir'] = '/patronifakedir' - end - end - - let(:chef_run) { runner.converge('gitlab::config', 'test_postgresql::postgresql_config') } - - before do - allow_any_instance_of(PgHelper).to receive(:postgresql_user).and_return('fakeuser') - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'create' do - context 'default settings' do - it 'creates the files' do - expect(chef_run).to render_file('/fakedir/data/postgresql.conf') - expect(chef_run).to render_file('/fakedir/data/runtime.conf') - expect(chef_run).to render_file('/fakedir/data/pg_hba.conf') - expect(chef_run).to render_file('/fakedir/data/pg_ident.conf') - end - end - - context 'when patroni is enabled' do - before do - stub_gitlab_rb( - patroni: { - enable: true - } - ) - end - - it 'creates the files' do - expect(chef_run).to render_file('/patronifakedir/data/postgresql.base.conf') - expect(chef_run).to render_file('/patronifakedir/data/runtime.conf') - expect(chef_run).to render_file('/patronifakedir/data/pg_hba.conf') - expect(chef_run).to render_file('/patronifakedir/data/pg_ident.conf') - end - end - end -end diff --git a/spec/chef/postgresql/resources/database_spec.rb b/spec/chef/postgresql/resources/database_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3Bvc3RncmVzcWwvcmVzb3VyY2VzL2RhdGFiYXNlX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/postgresql/resources/database_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql_database' do - let(:runner) do - ChefSpec::SoloRunner.new(step_into: %w(postgresql_database)) do |node| - # unix_socket_directory is normally conditionally set in postgresql::enable - # which is not executed as part of this spec - node.normal['postgresql']['unix_socket_directory'] = '/var/opt/gitlab/postgresql' - end - end - - context 'create' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_database_create') } - - context 'server is running' do - before do - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - end - - context 'database does not already exist' do - before do - allow_any_instance_of(PgHelper).to receive(:database_exists?).and_return(false) - end - - it 'creates a database' do - expect(chef_run).to run_execute('create database example').with( - command: %(/opt/gitlab/embedded/bin/createdb --port 5432 -h /var/opt/gitlab/postgresql -O gitlab example), - user: 'gitlab-psql' - ) - end - - context 'with non-default options' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_database_create_with_options') } - - it 'creates with the correct options set' do - expect(chef_run).to run_execute('create database fakedb').with( - command: %(/opt/gitlab/embedded/bin/createdb --port 9999 -h /fake/dir -O fakeuser fakedb) - ) - end - end - end - - context 'database already exists' do - before do - allow_any_instance_of(PgHelper).to receive(:database_exists?).with('example').and_return(true) - end - - it 'does nothing' do - expect(chef_run).not_to run_execute('create database example') - end - end - end - - context 'server is not running' do - before do - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(false) - end - - it 'does nothing' do - expect(chef_run).not_to run_execute('create database example') - end - end - end -end diff --git a/spec/chef/postgresql/resources/extension_spec.rb b/spec/chef/postgresql/resources/extension_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3Bvc3RncmVzcWwvcmVzb3VyY2VzL2V4dGVuc2lvbl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/postgresql/resources/extension_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql_query' do - let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(postgresql_extension)) } - - context 'run' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_extension_enable') } - - context 'when extension can be enabled' do - before do - allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('foobar', 'lorem').and_return(true) - end - - it 'runs the query to enable extension' do - expect(chef_run).to run_postgresql_query('enable foobar extension').with( - query: %(CREATE EXTENSION IF NOT EXISTS foobar), - db_name: 'lorem' - ) - end - end - - context 'when extension can not be enabled' do - before do - allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('foobar', 'lorem').and_return(false) - end - - it 'does not run the query to enable extension' do - expect(chef_run).not_to run_postgresql_query('enable foobar extension') - end - end - end -end diff --git a/spec/chef/postgresql/resources/query_spec.rb b/spec/chef/postgresql/resources/query_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3Bvc3RncmVzcWwvcmVzb3VyY2VzL3F1ZXJ5X3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/postgresql/resources/query_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql_query' do - let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(postgresql_query)) } - - before do - allow_any_instance_of(PgHelper).to receive(:is_ready?).and_return(true) - allow_any_instance_of(GeoPgHelper).to receive(:is_ready?).and_return(true) - end - - context 'run' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_query_run') } - - context 'when service is up' do - before do - allow_any_instance_of(PgHelper).to receive(:is_offline_or_readonly?).and_return(false) - allow_any_instance_of(GeoPgHelper).to receive(:is_offline_or_readonly?).and_return(false) - end - - it 'runs the query with correct service' do - expect(chef_run).to run_execute('create schema (postgresql)').with( - command: %(/opt/gitlab/bin/gitlab-psql -d omnibus_test -c "CREATE SCHEMA example AUTHORIZATION foobar;") - ) - - expect(chef_run).to run_execute('create schema (geo-postgresql)').with( - command: %(/opt/gitlab/bin/gitlab-geo-psql -d omnibus_test -c "CREATE SCHEMA example AUTHORIZATION foobar;") - ) - end - end - - context 'when service is offline' do - before do - allow_any_instance_of(PgHelper).to receive(:is_offline_or_readonly?).and_return(true) - allow_any_instance_of(GeoPgHelper).to receive(:is_offline_or_readonly?).and_return(true) - end - - it 'does not run the query' do - expect(chef_run).not_to run_execute('create example schema (postgresql)') - expect(chef_run).not_to run_execute('create example schema (geo-postgresql)') - end - end - end -end diff --git a/spec/chef/postgresql/resources/schema_spec.rb b/spec/chef/postgresql/resources/schema_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3Bvc3RncmVzcWwvcmVzb3VyY2VzL3NjaGVtYV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/postgresql/resources/schema_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql_schema' do - let(:runner) { ChefSpec::SoloRunner.new(step_into: %w(postgresql_schema)) } - - context 'create' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_schema_create') } - - context 'server is running' do - before do - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(false) - end - - it 'creates schema' do - expect(chef_run).to run_postgresql_query('create example schema on omnibus_gitlab_test').with( - db_name: 'omnibus_gitlab_test' - ) - end - - context 'schema already exists' do - before do - allow_any_instance_of(PgHelper).to receive(:schema_exists?).and_return(true) - end - - it 'does not try to create schema again' do - expect(chef_run).not_to run_postgresql_query('create example schema on omnibus_gitlab_test') - end - - context 'when schema owner is different' do - before do - allow_any_instance_of(PgHelper).to receive(:schema_owner?).and_return(false) - end - - it 'tries to update schema' do - expect(chef_run).to run_postgresql_query('modify example schema owner on omnibus_gitlab_test') - end - end - - context 'when schema owner is the same' do - before do - allow_any_instance_of(PgHelper).to receive(:schema_owner?).and_return(true) - end - - it 'tries to update schema' do - expect(chef_run).not_to run_postgresql_query('modify example schema owner on omnibus_gitlab_test') - end - end - end - end - end -end diff --git a/spec/chef/postgresql/resources/user_spec.rb b/spec/chef/postgresql/resources/user_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3Bvc3RncmVzcWwvcmVzb3VyY2VzL3VzZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/postgresql/resources/user_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql_user' do - before do - allow_any_instance_of(PgHelper).to receive(:is_offline_or_readonly?).and_return(false) - allow_any_instance_of(PgHelper).to receive(:user_exists?).and_return(false, true) - allow_any_instance_of(PgHelper).to receive(:user_password_match?).and_return(false) - allow_any_instance_of(PgHelper).to receive(:user_options_set?).and_return(false) - allow_any_instance_of(PgHelper).to receive(:is_ready?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - end - - let(:runner) { ChefSpec::SoloRunner.new(step_into: ['postgresql_user']) } - - context 'create' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_user_create') } - - it 'creates a user' do - expect(chef_run).to run_execute('create example postgresql user') - end - end - - context 'password' do - context 'not specified' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_user_password_unspecified') } - - it 'does not set the password of the no_password user' do - expect(chef_run).not_to run_execute('set password for no_password postgresql user') - end - end - - context 'nil' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_user_password_nil') } - - it 'does set the password of the nil_password user' do - expect(chef_run).to run_execute('set password for nil_password postgresql user') - .with(command: /PASSWORD NULL/) - end - end - - context 'md5' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_user_password_md5') } - - it 'does set the password of the md5_password user' do - expect(chef_run).to run_execute('set password for md5_password postgresql user') - .with(command: /PASSWORD 'e99b79fbdf9b997e6918df2385e60f5c'/) - end - end - - context 'empty' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_user_password_empty') } - - it 'does set the password of the empty_password user' do - expect(chef_run).to run_execute('set password for empty_password postgresql user') - .with(command: /PASSWORD ''/) - end - end - end - - context 'options' do - context 'unspecified' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_user_options_unspecified') } - - it 'does not set options' do - expect(chef_run).not_to run_execute('set options for example postgresql user') - end - end - - context 'SUPERUSER' do - let(:chef_run) { runner.converge('test_postgresql::postgresql_user_options_superuser') } - - it 'does set SUPERUSER' do - expect(chef_run).to run_execute('set options for example postgresql user') - .with(command: /\bSUPERUSER\b/) - end - end - end -end diff --git a/spec/chef/recipes/add_trusted_certs_spec.rb b/spec/chef/recipes/add_trusted_certs_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvYWRkX3RydXN0ZWRfY2VydHNfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/add_trusted_certs_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'add_trusted_certs recipe' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - let(:cert_helper) { CertificateHelper.new('/etc/gitlab/trusted-certs', '/opt/gitlab/embedded/ssl/certs', '/var/opt/gitlab') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - it 'creates the certificate directories' do - expect(chef_run).to create_directory('/opt/gitlab/embedded/ssl/certs').with(mode: '0755') - expect(chef_run).to create_directory('/etc/gitlab/trusted-certs').with(mode: '0755') - end - - it 'creates a readme file for the managed certificate directory' do - expect(chef_run).to create_file('/opt/gitlab/embedded/ssl/certs/README').with( - mode: '0644', - content: /This directory is managed by omnibus\-gitlab/ - ) - end - - context 'when new trusted certificates have been added' do - before do - allow(cert_helper).to receive(:new_certificate_added?).and_return(true) - allow(CertificateHelper).to receive(:new).and_return(cert_helper) - end - - it 'attempts to link trusted certificates into our certificate chain' do - expect(chef_run).to run_ruby_block('Move existing certs and link to /opt/gitlab/embedded/ssl/certs') - end - end - - context 'when there have been no changes to the trusted certificates directory' do - before do - allow(cert_helper).to receive(:new_certificate_added?).and_return(false) - allow(CertificateHelper).to receive(:new).and_return(cert_helper) - end - - it 'does not attempt to link trusted certificates into our certificate chain' do - expect(chef_run).not_to run_ruby_block('Move existing certs and link to /opt/gitlab/embedded/ssl/certs') - end - end -end diff --git a/spec/chef/recipes/config_spec.rb b/spec/chef/recipes/config_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvY29uZmlnX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/config_spec.rb +++ /dev/null @@ -1,226 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::config' do - cached(:chef_run) { converge_config } - let(:node) { chef_run.node } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - shared_examples 'regular services are disabled' do - it 'disables regular services' do - expect(node['gitlab']['puma']['enable']).to eq false - expect(node['gitlab']['sidekiq']['enable']).to eq false - expect(node['gitlab']['gitlab-workhorse']['enable']).to eq false - expect(node['gitaly']['enable']).to eq false - expect(node['gitlab']['nginx']['enable']).to eq false - expect(node['postgresql']['enable']).to eq false - expect(node['gitlab']['mailroom']['enable']).to eq false - expect(node['monitoring']['gitlab-exporter']['enable']).to eq false - expect(node['monitoring']['postgres-exporter']['enable']).to eq false - expect(node['monitoring']['prometheus']['enable']).to eq false - end - end - - it 'ignores unsupported top level variables' do - allow(IO).to receive(:read).and_call_original - allow(IO).to receive(:read).with('config-test.rb').and_return('abc="top-level"') - Gitlab.from_file('config-test.rb') - - expect(node['gitlab']['abc']).to be_nil - end - - it 'errors on unsupported top level methods' do - allow(IO).to receive(:read).and_call_original - allow(IO).to receive(:read).with('config-test.rb').and_return('abc "top-level"') - expect do - Gitlab.from_file('config-test.rb') - end.to raise_error(Mixlib::Config::UnknownConfigOptionError) - end - - it 'errors on unsupported nested variables' do - allow(IO).to receive(:read).and_call_original - allow(IO).to receive(:read).with('config-test.rb').and_return('abc["def"]["hij"] = "top-level"') - expect do - Gitlab.from_file('config-test.rb') - end.to raise_error(Mixlib::Config::UnknownConfigOptionError) - end - - context 'when gitlab-rails is disabled' do - cached(:chef_run) { converge_config } - before do - stub_gitlab_rb( - gitlab_rails: { - enable: false - } - ) - end - - it 'disables Gitlab components' do - expect(node['gitlab']['puma']['enable']).to eq false - expect(node['gitlab']['sidekiq']['enable']).to eq false - expect(node['gitlab']['gitlab-workhorse']['enable']).to eq false - expect(node['monitoring']['gitlab-exporter']['enable']).to eq false - end - - it 'still leaves other default service enabled' do - expect(node['gitlab']['nginx']['enable']).to eq true - expect(node['postgresql']['enable']).to eq true - expect(node['redis']['enable']).to eq true - expect(node['monitoring']['prometheus']['enable']).to eq true - expect(node['monitoring']['alertmanager']['enable']).to eq true - expect(node['monitoring']['node-exporter']['enable']).to eq true - expect(node['monitoring']['redis-exporter']['enable']).to eq true - expect(node['logrotate']['enable']).to eq true - expect(node['monitoring']['postgres-exporter']['enable']).to eq true - end - end - - context 'with roles' do - context 'when redis_sentinel_role is enabled' do - cached(:chef_run) { converge_config(is_ee: true) } - before do - stub_gitlab_rb( - redis_sentinel_role: { - enable: true - }, - redis: { - master_password: 'PASSWORD', - master_ip: '10.0.0.0' - } - ) - end - - it_behaves_like 'regular services are disabled' - - it 'only sentinel is enabled' do - expect(node['gitlab']['sentinel']['enable']).to eq true - expect(node['redis']['enable']).to eq false - expect(node['monitoring']['redis-exporter']['enable']).to eq false - expect(node['monitoring']['node-exporter']['enable']).to eq true - expect(node['logrotate']['enable']).to eq true - end - - context 'when redis_sentinel_role is enabled with redis_master_role' do - cached(:chef_run) { converge_config(is_ee: true) } - before do - stub_gitlab_rb( - redis_sentinel_role: { - enable: true - }, - redis_master_role: { - enable: true - } - ) - end - - it_behaves_like 'regular services are disabled' - - it 'redis and sentinel are enabled' do - expect(node['gitlab']['sentinel']['enable']).to eq true - expect(node['redis']['enable']).to eq true - expect(node['monitoring']['redis-exporter']['enable']).to eq true - expect(node['monitoring']['node-exporter']['enable']).to eq true - expect(node['logrotate']['enable']).to eq true - end - end - - context 'when redis_sentinel_role is enabled with redis_replica_role' do - cached(:chef_run) { converge_config(is_ee: true) } - before do - stub_gitlab_rb( - redis_sentinel_role: { - enable: true - }, - redis_replica_role: { - enable: true - }, - redis: { - master_ip: '10.0.0.0', - master_port: 6379, - master_password: 'PASSWORD' - } - ) - end - - it_behaves_like 'regular services are disabled' - - it 'only redis is enabled' do - expect(node['gitlab']['sentinel']['enable']).to eq true - expect(node['redis']['enable']).to eq true - expect(node['monitoring']['redis-exporter']['enable']).to eq true - expect(node['monitoring']['node-exporter']['enable']).to eq true - expect(node['logrotate']['enable']).to eq true - end - end - end - - context 'when redis_master_role is enabled' do - cached(:chef_run) { converge_config(is_ee: true) } - before do - stub_gitlab_rb( - redis_master_role: { - enable: true - } - ) - end - - it_behaves_like 'regular services are disabled' - - it 'only redis is enabled' do - expect(node['redis']['enable']).to eq true - expect(node['gitlab']['sentinel']['enable']).to eq false - expect(node['monitoring']['redis-exporter']['enable']).to eq true - expect(node['monitoring']['node-exporter']['enable']).to eq true - expect(node['logrotate']['enable']).to eq true - end - end - - context 'when redis_replica_role is enabled' do - cached(:chef_run) { converge_config(is_ee: true) } - before do - stub_gitlab_rb( - redis_replica_role: { - enable: true - }, - redis: { - master_ip: '10.0.0.0', - master_port: 6379, - master_password: 'PASSWORD' - } - ) - end - - it_behaves_like 'regular services are disabled' - - it 'only redis is enabled' do - expect(node['redis']['enable']).to eq true - expect(node['gitlab']['sentinel']['enable']).to eq false - end - end - - context 'when redis_master_role and redis_replica_role are enabled' do - cached(:chef_run) { converge_config(is_ee: true) } - before do - stub_gitlab_rb( - redis_master_role: { - enable: true - }, - redis_replica_role: { - enable: true - }, - redis: { - master_ip: '10.0.0.0', - master_port: 6379, - master_password: 'PASSWORD' - } - ) - end - - it 'fails with an error' do - expect { chef_run }.to raise_error RuntimeError - end - end - end -end diff --git a/spec/chef/recipes/crond_enable_spec.rb b/spec/chef/recipes/crond_enable_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvY3JvbmRfZW5hYmxlX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/crond_enable_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'crond::enable' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('crond::enable') } - - it "should create a log directory" do - expect(chef_run).to create_directory("/var/log/gitlab/crond").with( - owner: "root" - ) - end - - it "should create a cron.d directory" do - expect(chef_run).to create_directory("/var/opt/gitlab/crond").with( - recursive: true, - owner: "root" - ) - end - - it "should pass correct options to runit_service" do - expect(chef_run).to render_file("/opt/gitlab/sv/crond/run").with_content(/--include=\/var\/opt\/gitlab\/crond/) - end - - it_behaves_like "enabled runit service", "crond", "root", "root" -end diff --git a/spec/chef/recipes/database_migrations_spec.rb b/spec/chef/recipes/database_migrations_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZGF0YWJhc2VfbWlncmF0aW9uc19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/database_migrations_spec.rb +++ /dev/null @@ -1,192 +0,0 @@ -require 'chef_helper' - -# NOTE: These specs do not verify whether the code actually ran -# Nor whether the resource inside of the recipe was notified correctly. -# At this moment they only verify whether the expected commands are passed -# to the resource block. - -RSpec.describe 'gitlab::database-migrations' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when migration should run' do - before do - stub_default_not_listening?(false) - end - - let(:bash_block) { chef_run.bash('migrate gitlab-rails database') } - let(:migration_block) { chef_run.rails_migration('gitlab-rails') } - - it 'runs the migrations with expected attributes' do - expect(chef_run).to run_rails_migration('gitlab-rails') do |resource| - expect(resource.rake_task).to eq('gitlab:db:configure') - expect(resource.logfile_prefix).to eq('gitlab-rails-db-migrate') - expect(resource.helper).to be_a(RailsMigrationHelper) - end - end - - it 'skips outdated external databases warning by default' do - expect(migration_block).to notify('ruby_block[check remote PG version]').to(:run) - expect(chef_run.ruby_block('check remote PG version').should_skip?(:run)).to be_truthy - end - - context 'using external db' do - before { stub_gitlab_rb(postgresql: { enable: false }) } - - it 'warns about outdated databases' do - allow(GitlabRailsEnvHelper).to receive(:db_version).and_return(11) - - expect(migration_block).to notify('ruby_block[check remote PG version]').to(:run) - expect(chef_run.ruby_block('check remote PG version').should_skip?(:run)).to be_falsey - - chef_run.ruby_block('check remote PG version').block.call - - expect_logged_warning(/Support for PostgreSQL 11 has been removed/) - end - end - - context 'initial root password' do - context 'set via gitlab.rb' do - before do - stub_gitlab_rb( - gitlab_rails: { - initial_root_password: 'foobar' - } - ) - end - - it 'runs DB migration with GITLAB_ROOT_PASSWORD variable set to provided value' do - expect(chef_run).to run_rails_migration('gitlab-rails').with( - environment: { - 'GITLAB_ROOT_PASSWORD' => 'foobar', - } - ) - end - end - - context 'set via env variable' do - before do - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('GITLAB_ROOT_PASSWORD').and_return('asdf1234@') - end - - it 'runs DB migration with GITLAB_ROOT_PASSWORD variable set to provided value' do - expect(chef_run).to run_rails_migration('gitlab-rails').with( - environment: { - 'GITLAB_ROOT_PASSWORD' => 'asdf1234@', - } - ) - end - end - - context 'set via both env variable and gitlab.rb' do - before do - stub_gitlab_rb( - gitlab_rails: { - initial_root_password: 'foobar' - } - ) - - allow(ENV).to receive(:[]).and_call_original - allow(ENV).to receive(:[]).with('GITLAB_ROOT_PASSWORD').and_return('asdf1234@') - end - - it 'runs DB migration with GITLAB_ROOT_PASSWORD variable set to value provided via env variable' do - expect(chef_run).to run_rails_migration('gitlab-rails').with( - environment: { - 'GITLAB_ROOT_PASSWORD' => 'asdf1234@', - } - ) - end - end - - context 'not set' do - before do - allow(SecretsHelper).to receive(:generate_base64).and_return('LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=') - end - - it 'generates a random root password and runs DB migration with GITLAB_ROOT_PASSWORD set to it' do - expect(chef_run).to run_rails_migration('gitlab-rails').with( - environment: { - 'GITLAB_ROOT_PASSWORD' => 'LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=' - } - ) - end - end - end - - it 'runs with the initial_root_password and initial_shared_runners_registration_token in the environment' do - stub_gitlab_rb( - gitlab_rails: { initial_root_password: '123456789', initial_shared_runners_registration_token: '987654321' } - ) - - expect(chef_run).to run_rails_migration('gitlab-rails').with( - environment: { - 'GITLAB_ROOT_PASSWORD' => '123456789', - 'GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN' => '987654321' - } - ) - end - - context 'initial license file' do - before do - allow(SecretsHelper).to receive(:generate_base64).and_return('LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=') - end - - it 'detects license file from /etc/gitlab' do - allow(Dir).to receive(:glob).and_call_original - allow(Dir).to receive(:glob).with('/etc/gitlab/*.gitlab-license').and_return(%w[/etc/gitlab/company.gitlab-license /etc/gitlab/company2.gitlab-license]) - - expect(chef_run).to run_rails_migration('gitlab-rails').with( - environment: { - 'GITLAB_ROOT_PASSWORD' => 'LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=', - 'GITLAB_LICENSE_FILE' => '/etc/gitlab/company.gitlab-license' - } - ) - end - - it 'license file specified in gitlab.rb gets priority' do - allow(Dir).to receive(:glob).and_call_original - allow(Dir).to receive(:glob).with('/etc/gitlab/*.gitlab-license').and_return(%w[/etc/gitlab/company.gitlab-license /etc/gitlab/company2.gitlab-license]) - - stub_gitlab_rb( - gitlab_rails: { initial_license_file: '/mnt/random.gitlab-license' } - ) - - expect(chef_run).to run_rails_migration('gitlab-rails').with( - environment: { - 'GITLAB_ROOT_PASSWORD' => 'LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=', - 'GITLAB_LICENSE_FILE' => '/mnt/random.gitlab-license' - } - ) - end - - it 'Does not fail if no license file found in /etc/gitlab' do - allow(Dir).to receive(:glob).and_call_original - allow(Dir).to receive(:glob).with('/etc/gitlab/*.gitlab-license').and_return([]) - - expect(chef_run).to run_rails_migration('gitlab-rails').with( - environment: { - 'GITLAB_ROOT_PASSWORD' => 'LFQLd2ayKNpthh+Ehxqy7ROxsmpzACy55EcOYoMfRlk=', - } - ) - end - end - - it 'should notify rails cache clear resource' do - expect(migration_block).to notify( - 'execute[clear the gitlab-rails cache]') - end - end - - context 'with auto_migrate off' do - before { stub_gitlab_rb(gitlab_rails: { auto_migrate: false }) } - - it 'skips running the migrations' do - expect(chef_run).not_to run_rails_migration('gitlab-rails') - end - end -end diff --git a/spec/chef/recipes/database_reindexing_disable_spec.rb b/spec/chef/recipes/database_reindexing_disable_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZGF0YWJhc2VfcmVpbmRleGluZ19kaXNhYmxlX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/database_reindexing_disable_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::database-reindexing' do - let(:chef_run) { converge_config('gitlab::database_reindexing_disable') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - it 'removes the database-reindexing cronjob' do - expect(chef_run).to delete_crond_job('database-reindexing') - end -end diff --git a/spec/chef/recipes/database_reindexing_enable_spec.rb b/spec/chef/recipes/database_reindexing_enable_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZGF0YWJhc2VfcmVpbmRleGluZ19lbmFibGVfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/database_reindexing_enable_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::database-reindexing' do - let(:chef_run) { converge_config('gitlab::database_reindexing_enable') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'with defaults' do - before do - stub_gitlab_rb(gitlab_rails: { database_reindexing: { enable: true } }) - end - - it 'adds a crond_job with default schedule' do - expect(chef_run).to create_crond_job('database-reindexing').with( - user: "root", - hour: '*', - minute: 0, - month: '*', - day_of_month: '*', - day_of_week: '0,6', - command: "/opt/gitlab/bin/gitlab-rake gitlab:db:reindex" - ) - end - end - - context 'with specific schedule' do - let(:config) do - { - enable: true, - hour: 10, - minute: 5, - month: 3, - day_of_month: 2, - day_of_week: 1 - } - end - - it 'adds a crond_job with the configured schedule' do - stub_gitlab_rb(gitlab_rails: { database_reindexing: config }) - - expect(chef_run).to create_crond_job('database-reindexing').with( - user: "root", - hour: 10, - minute: 5, - month: 3, - day_of_week: 1, - command: "/opt/gitlab/bin/gitlab-rake gitlab:db:reindex" - ) - end - end -end diff --git a/spec/chef/recipes/default_spec.rb b/spec/chef/recipes/default_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZGVmYXVsdF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/default_spec.rb +++ /dev/null @@ -1,193 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::default' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - it 'creates the user config directory' do - expect(chef_run).to create_directory('/etc/gitlab').with( - user: 'root', - group: 'root', - mode: '0775' - ) - end - - it 'creates the var opt data config directory' do - expect(chef_run).to create_directory('Create /var/opt/gitlab').with( - path: '/var/opt/gitlab', - user: 'root', - group: 'root', - mode: '0755' - ) - - gitconfig_hash = { - "receive" => ["fsckObjects = true", "advertisePushOptions = true"], - "pack" => ["threads = 1"], - "repack" => ["writeBitmaps = true"], - "transfer" => ["hideRefs=^refs/tmp/", "hideRefs=^refs/keep-around/", "hideRefs=^refs/remotes/"], - "core" => [ - 'alternateRefsCommand="exit 0 #"', - "fsyncObjectFiles = true" - ], - "fetch" => ["writeCommitGraph = true"] - } - - expect(chef_run).to create_template('/opt/gitlab/embedded/etc/gitconfig').with( - variables: { gitconfig: gitconfig_hash } - ) - end - - it 'creates the system gitconfig directory and file' do - stub_gitlab_rb(omnibus_gitconfig: { system: { receive: ["fsckObjects = true", "advertisePushOptions = true"], pack: ["threads = 2"] } }) - - expect(chef_run).to create_directory('/opt/gitlab/embedded/etc').with( - user: 'root', - group: 'root', - mode: '0755' - ) - - gitconfig_hash = { - "receive" => ["fsckObjects = true", "advertisePushOptions = true"], - "pack" => ["threads = 2"], - "repack" => ["writeBitmaps = true"], - "transfer" => ["hideRefs=^refs/tmp/", "hideRefs=^refs/keep-around/", "hideRefs=^refs/remotes/"], - "core" => [ - 'alternateRefsCommand="exit 0 #"', - "fsyncObjectFiles = true" - ], - "fetch" => ["writeCommitGraph = true"] - } - - expect(chef_run).to create_template('/opt/gitlab/embedded/etc/gitconfig').with( - source: 'gitconfig-system.erb', - variables: { gitconfig: gitconfig_hash }, - mode: 0755 - ) - end - - context 'with logrotate' do - it 'runs logrotate directory and configuration recipe by default' do - expect(chef_run).to include_recipe('logrotate::folders_and_configs') - end - - it 'runs logrotate directory and configuration recipe when logrotate is disabled' do - stub_gitlab_rb(logrotate: { enable: false }) - - expect(chef_run).to include_recipe('logrotate::folders_and_configs') - end - end - - context 'when manage_etc directory management is disabled' do - before { stub_gitlab_rb(manage_storage_directories: { enable: true, manage_etc: false }) } - - it 'does not create the user config directory' do - expect(chef_run).not_to create_directory('/etc/gitlab') - end - end - - context 'prometheus is enabled by default' do - it 'includes the prometheus recipe' do - expect(chef_run).to include_recipe('monitoring::prometheus') - expect(chef_run).not_to include_recipe('monitoring::prometheus_disable') - end - end - - context 'with prometheus disabled' do - before { stub_gitlab_rb(prometheus: { enable: false }) } - - it 'includes the prometheus_disable recipe' do - expect(chef_run).to include_recipe('monitoring::prometheus_disable') - expect(chef_run).not_to include_recipe('monitoring::prometheus') - end - end - - context 'with database reindexing and LetsEncrypt auto-renew disabled' do - it 'disables crond' do - expect(chef_run).to include_recipe('crond::disable') - expect(chef_run).not_to include_recipe('crond::enable') - end - end - - context 'with database reindexing enabled' do - before do - stub_gitlab_rb(gitlab_rails: { database_reindexing: { enable: true } }) - end - - it 'enables crond' do - expect(chef_run).to include_recipe('crond::enable') - expect(chef_run).not_to include_recipe('crond::disable') - end - end - - context 'with LetsEncrypt auto-renew enabled' do - before do - # Registry will be auto-enabled if LetsEncrypt is enabled - stub_gitlab_rb(external_url: 'http://gitlab.example.com', - registry: { enable: false }, - letsencrypt: { enable: true, auto_renew: true }) - end - - it 'enables crond' do - expect(chef_run).to include_recipe('crond::enable') - expect(chef_run).not_to include_recipe('crond::disable') - end - end - - context 'with sidekiq exporter settings' do - context 'when Sidekiq exporter and Sidekiq health checks port are the same' do - before do - stub_gitlab_rb( - sidekiq: - { - metrics_enabled: true, - listen_address: 'localhost', - listen_port: 3807, - health_checks_enabled: true, - health_checks_listen_address: 'localhost', - health_checks_listen_port: 3807 - } - ) - end - - it 'logs a warning' do - allow(LoggingHelper).to receive(:deprecation) - expect(LoggingHelper).to receive(:deprecation).with("Sidekiq exporter and health checks are set to the same address and port. This is deprecated and will result in an error in version 15.0. See https://docs.gitlab.com/ee/administration/sidekiq.html") - - chef_run - end - end - - context 'when Sidekiq exporter and Sidekiq health checks port are different' do - before do - stub_gitlab_rb( - sidekiq: - { - metrics_enabled: true, - listen_address: 'localhost', - listen_port: 3807, - health_checks_enabled: true, - health_checks_listen_address: 'localhost', - health_checks_listen_port: 3907 - } - ) - end - - it 'does not log a warning' do - expect(LoggingHelper).not_to receive(:deprecation).with("Sidekiq exporter and health checks are set to the same address and port. This is deprecated and will result in an error in version 15.0. See https://docs.gitlab.com/ee/administration/sidekiq.html") - - chef_run - end - end - end - - context 'with sidekiq exporter settings not set (default settings)' do - it 'does not log a warning' do - expect(LoggingHelper).not_to receive(:deprecation).with("Sidekiq exporter and health checks are set to the same address and port. This is deprecated and will result in an error in version 15.0. See https://docs.gitlab.com/ee/administration/sidekiq.html") - - chef_run - end - end -end diff --git a/spec/chef/recipes/gitaly_spec.rb b/spec/chef/recipes/gitaly_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0YWx5X3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitaly_spec.rb +++ /dev/null @@ -1,890 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitaly' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:config_path) { '/var/opt/gitlab/gitaly/config.toml' } - let(:gitaly_config) { chef_run.template(config_path) } - let(:internal_socket_dir) { '/var/opt/gitlab/gitaly/user_defined/internal_sockets' } - let(:socket_path) { '/tmp/gitaly.socket' } - let(:listen_addr) { 'localhost:7777' } - let(:tls_listen_addr) { 'localhost:8888' } - let(:certificate_path) { '/path/to/cert.pem' } - let(:key_path) { '/path/to/key.pem' } - let(:prometheus_listen_addr) { 'localhost:9000' } - let(:logging_level) { 'warn' } - let(:logging_format) { 'default' } - let(:logging_sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project' } - let(:logging_ruby_sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project-ruby' } - let(:logging_sentry_environment) { 'production' } - let(:prometheus_grpc_latency_buckets) do - '[0.001, 0.005, 0.025, 0.1, 0.5, 1.0, 10.0, 30.0, 60.0, 300.0, 1500.0]' - end - let(:auth_token) { '123secret456' } - let(:auth_transitioning) { true } - let(:ruby_max_rss) { 1000000 } - let(:graceful_restart_timeout) { '20m' } - let(:ruby_graceful_restart_timeout) { '30m' } - let(:ruby_restart_delay) { '10m' } - let(:ruby_num_workers) { 5 } - let(:ruby_rugged_git_config_search_path) { '/path/to/opt/gitlab/embedded/etc' } - let(:git_catfile_cache_size) { 50 } - let(:git_bin_path) { '/path/to/usr/bin/git' } - let(:use_bundled_git) { true } - let(:open_files_ulimit) { 10000 } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - 'TZ' => ':/etc/localtime', - 'HOME' => '/var/opt/gitlab', - 'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin', - 'ICU_DATA' => '/opt/gitlab/embedded/share/icu/current', - 'PYTHONPATH' => '/opt/gitlab/embedded/lib/python3.9/site-packages', - 'WRAPPER_JSON_LOGGING' => 'true', - "GITALY_PID_FILE" => '/var/opt/gitlab/gitaly/gitaly.pid', - } - end - - let(:gitlab_url) { 'http://localhost:3000' } - let(:workhorse_addr) { 'localhost:4000' } - let(:gitaly_custom_hooks_dir) { '/path/to/gitaly/custom/hooks' } - let(:gitlab_shell_custom_hooks_dir) { '/path/to/gitlab-shell/custom/hooks' } - let(:user) { 'user123' } - let(:password) { 'password321' } - let(:ca_file) { '/path/to/ca_file' } - let(:ca_path) { '/path/to/ca_path' } - let(:self_signed_cert) { true } - let(:read_timeout) { 123 } - let(:daily_maintenance_start_hour) { 21 } - let(:daily_maintenance_start_minute) { 9 } - let(:daily_maintenance_duration) { '45m' } - let(:daily_maintenance_storages) { ["default"] } - let(:daily_maintenance_disabled) { false } - let(:cgroups_count) { 10 } - let(:cgroups_mountpoint) { '/sys/fs/cgroup' } - let(:cgroups_hierarchy_root) { 'gitaly' } - let(:cgroups_memory_enabled) { true } - let(:cgroups_memory_limit) { 1048576 } - let(:cgroups_cpu_enabled) { true } - let(:cgroups_cpu_shares) { 512 } - let(:pack_objects_cache_enabled) { true } - let(:pack_objects_cache_dir) { '/pack-objects-cache' } - let(:pack_objects_cache_max_age) { '10m' } - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'by default' do - it_behaves_like "enabled runit service", "gitaly", "root", "root" - - it 'creates expected directories with correct permissions' do - expect(chef_run).to create_directory('/var/opt/gitlab/gitaly').with(user: 'git', mode: '0700') - expect(chef_run).to create_directory('/var/log/gitlab/gitaly').with(user: 'git', mode: '0700') - end - - it 'creates a default VERSION file and restarts service' do - expect(chef_run).to create_version_file('Create version file for Gitaly').with( - version_file_path: '/var/opt/gitlab/gitaly/VERSION', - version_check_cmd: "/opt/gitlab/embedded/bin/ruby -rdigest/sha2 -e 'puts %(sha256:) + Digest::SHA256.file(%(/opt/gitlab/embedded/bin/gitaly)).hexdigest'" - ) - - expect(chef_run.version_file('Create version file for Gitaly')).to notify('runit_service[gitaly]').to(:hup) - end - - it 'creates a default RUBY_VERSION file and restarts service' do - expect(chef_run).to create_version_file('Create Ruby version file for Gitaly').with( - version_file_path: '/var/opt/gitlab/gitaly/RUBY_VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/ruby --version' - ) - - expect(chef_run.version_file('Create Ruby version file for Gitaly')).to notify('runit_service[gitaly]').to(:hup) - end - - it 'populates gitaly config.toml with defaults' do - expect(chef_run).to render_file(config_path).with_content { |content| - expect(content).to include("socket_path = '/var/opt/gitlab/gitaly/gitaly.socket'") - expect(content).to include("internal_socket_dir = '/var/opt/gitlab/gitaly/internal_sockets'") - expect(content).to include("bin_dir = '/opt/gitlab/embedded/bin'") - expect(content).to include(%(rugged_git_config_search_path = "/opt/gitlab/embedded/etc")) - } - - expect(chef_run).not_to render_file(config_path) - .with_content("listen_addr = '#{listen_addr}'") - expect(chef_run).not_to render_file(config_path) - .with_content("tls_listen_addr =") - expect(chef_run).not_to render_file(config_path) - .with_content("certificate_path =") - expect(chef_run).not_to render_file(config_path) - .with_content("key_path =") - expect(chef_run).not_to render_file(config_path) - .with_content("prometheus_listen_addr = '#{prometheus_listen_addr}'") - expect(chef_run).not_to render_file(config_path) - .with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+sentry_dsn = '#{logging_sentry_dsn}'}) - expect(chef_run).not_to render_file(config_path) - .with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+ruby_sentry_dsn = '#{logging_ruby_sentry_dsn}'}) - expect(chef_run).not_to render_file(config_path) - .with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+sentry_environment = '#{logging_sentry_environment}'}) - expect(chef_run).not_to render_file(config_path) - .with_content(%r{\[prometheus\]\s+grpc_latency_buckets = #{Regexp.escape(prometheus_grpc_latency_buckets)}}) - expect(chef_run).not_to render_file(config_path) - .with_content(%r{\[auth\]\s+token = }) - expect(chef_run).not_to render_file(config_path) - .with_content('transitioning =') - expect(chef_run).not_to render_file(config_path) - .with_content('max_rss =') - expect(chef_run).not_to render_file(config_path) - .with_content('graceful_restart_timeout =') - expect(chef_run).not_to render_file(config_path) - .with_content('restart_delay =') - expect(chef_run).not_to render_file(config_path) - .with_content('num_workers =') - expect(chef_run).not_to render_file(config_path) - .with_content(%r{\[logging\]\s+level}) - expect(chef_run).not_to render_file(config_path) - .with_content(%r{catfile_cache_size}) - expect(chef_run).not_to render_file(config_path) - .with_content('[pack_objects_cache]') - end - - it 'populates gitaly config.toml with default git binary path' do - expect(chef_run).to render_file(config_path) - .with_content("bin_path = '/opt/gitlab/embedded/bin/git'") - end - - it 'populates gitaly config.toml with default storages' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/var/opt/gitlab/git-data/repositories'}) - end - - it 'renders the runit run script with defaults' do - expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run') - .with_content(%r{ulimit -n 15000}) - end - - it 'does not append timestamp in logs if logging format is json' do - expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/log/run') - .with_content(/exec svlogd \/var\/log\/gitlab\/gitaly/) - end - - it 'populates gitaly config.toml with gitlab-shell values' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[gitlab-shell\]\s+dir = "/opt/gitlab/embedded/service/gitlab-shell"}) - end - - it 'populates gitaly config.toml with gitlab-workhorse socket' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket'\s+relative_url_root = ''}) - end - end - - context 'with user settings' do - before do - stub_gitlab_rb( - gitaly: { - socket_path: socket_path, - internal_socket_dir: internal_socket_dir, - listen_addr: listen_addr, - tls_listen_addr: tls_listen_addr, - certificate_path: certificate_path, - key_path: key_path, - prometheus_listen_addr: prometheus_listen_addr, - logging_level: logging_level, - logging_format: logging_format, - logging_sentry_dsn: logging_sentry_dsn, - logging_ruby_sentry_dsn: logging_ruby_sentry_dsn, - logging_sentry_environment: logging_sentry_environment, - prometheus_grpc_latency_buckets: prometheus_grpc_latency_buckets, - auth_token: auth_token, - auth_transitioning: auth_transitioning, - graceful_restart_timeout: graceful_restart_timeout, - ruby_max_rss: ruby_max_rss, - ruby_graceful_restart_timeout: ruby_graceful_restart_timeout, - ruby_restart_delay: ruby_restart_delay, - ruby_num_workers: ruby_num_workers, - git_catfile_cache_size: git_catfile_cache_size, - git_bin_path: git_bin_path, - use_bundled_git: true, - open_files_ulimit: open_files_ulimit, - ruby_rugged_git_config_search_path: ruby_rugged_git_config_search_path, - daily_maintenance_start_hour: daily_maintenance_start_hour, - daily_maintenance_start_minute: daily_maintenance_start_minute, - daily_maintenance_duration: daily_maintenance_duration, - daily_maintenance_storages: daily_maintenance_storages, - daily_maintenance_disabled: daily_maintenance_disabled, - cgroups_count: cgroups_count, - cgroups_mountpoint: cgroups_mountpoint, - cgroups_hierarchy_root: cgroups_hierarchy_root, - cgroups_memory_enabled: cgroups_memory_enabled, - cgroups_memory_limit: cgroups_memory_limit, - cgroups_cpu_enabled: cgroups_cpu_enabled, - cgroups_cpu_shares: cgroups_cpu_shares, - pack_objects_cache_enabled: pack_objects_cache_enabled, - pack_objects_cache_dir: pack_objects_cache_dir, - pack_objects_cache_max_age: pack_objects_cache_max_age, - custom_hooks_dir: gitaly_custom_hooks_dir - }, - gitlab_rails: { - internal_api_url: gitlab_url - }, - gitlab_shell: { - http_settings: { - read_timeout: read_timeout, - user: user, - password: password, - ca_file: ca_file, - ca_path: ca_path, - self_signed_cert: self_signed_cert - } - }, - gitlab_workhorse: { - listen_network: 'tcp', - listen_addr: workhorse_addr, - }, - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like "enabled runit service", "gitaly", "root", "root" - - it 'creates expected directories with correct permissions' do - expect(chef_run).to create_directory(internal_socket_dir).with(user: 'foo', mode: '0700') - end - - it 'populates gitaly config.toml with custom values' do - expect(chef_run).to render_file(config_path) - .with_content("socket_path = '#{socket_path}'") - expect(chef_run).to render_file(config_path) - .with_content("internal_socket_dir = '#{internal_socket_dir}'") - expect(chef_run).to render_file(config_path) - .with_content("bin_dir = '/opt/gitlab/embedded/bin'") - expect(chef_run).to render_file(config_path) - .with_content("listen_addr = 'localhost:7777'") - expect(chef_run).to render_file(config_path) - .with_content("graceful_restart_timeout = '#{graceful_restart_timeout}'") - expect(chef_run).to render_file(config_path) - .with_content("bin_path = '#{git_bin_path}'") - expect(chef_run).to render_file(config_path) - .with_content("use_bundled_binaries = true") - - gitaly_logging_section = Regexp.new([ - %r{\[logging\]}, - %r{level = '#{logging_level}'}, - %r{format = '#{logging_format}'}, - %r{sentry_dsn = '#{logging_sentry_dsn}'}, - %r{ruby_sentry_dsn = '#{logging_ruby_sentry_dsn}'}, - %r{sentry_environment = '#{logging_sentry_environment}'}, - ].map(&:to_s).join('\s+')) - - gitaly_ruby_section = Regexp.new([ - %r{\[gitaly-ruby\]}, - %r{dir = "/opt/gitlab/embedded/service/gitaly-ruby"}, - %r{max_rss = #{ruby_max_rss}}, - %r{graceful_restart_timeout = '#{Regexp.escape(ruby_graceful_restart_timeout)}'}, - %r{restart_delay = '#{Regexp.escape(ruby_restart_delay)}'}, - %r{num_workers = #{ruby_num_workers}}, - ].map(&:to_s).join('\s+')) - - gitlab_shell_section = Regexp.new([ - %r{\[gitlab-shell\]}, - %r{dir = "/opt/gitlab/embedded/service/gitlab-shell"}, - ].map(&:to_s).join('\s+')) - - gitlab_section = Regexp.new([ - %r{\[gitlab\]}, - %r{url = '#{Regexp.escape(gitlab_url)}'}, - %r{\[gitlab.http-settings\]}, - %r{read_timeout = #{read_timeout}}, - %r{user = '#{Regexp.escape(user)}'}, - %r{password = '#{Regexp.escape(password)}'}, - %r{ca_file = '#{Regexp.escape(ca_file)}'}, - %r{ca_path = '#{Regexp.escape(ca_path)}'}, - %r{self_signed_cert = #{self_signed_cert}}, - ].map(&:to_s).join('\s+')) - - hooks_section = Regexp.new([ - %r{\[hooks\]}, - %r{custom_hooks_dir = '#{Regexp.escape(gitaly_custom_hooks_dir)}'}, - ].map(&:to_s).join('\s+')) - - maintenance_section = Regexp.new([ - %r{\[daily_maintenance\]}, - %r{start_hour = #{daily_maintenance_start_hour}}, - %r{start_minute = #{daily_maintenance_start_minute}}, - %r{duration = '#{daily_maintenance_duration}'}, - %r{storages = #{Regexp.escape(daily_maintenance_storages.to_s)}}, - ].map(&:to_s).join('\s+')) - - cgroups_section = Regexp.new([ - %r{\[cgroups\]}, - %r{count = #{cgroups_count}}, - %r{mountpoint = '#{cgroups_mountpoint}'}, - %r{hierarchy_root = '#{cgroups_hierarchy_root}'}, - ].map(&:to_s).join('\s+')) - - cgroups_memory_section = Regexp.new([ - %r{\[cgroups\.memory\]}, - %r{enabled = #{cgroups_memory_enabled}}, - %r{limit = #{cgroups_memory_limit}}, - ].map(&:to_s).join('\s+')) - - cgroups_cpu_section = Regexp.new([ - %r{\[cgroups\.cpu\]}, - %r{enabled = #{cgroups_cpu_enabled}}, - %r{shares = #{cgroups_cpu_shares}}, - ].map(&:to_s).join('\s+')) - - pack_objects_cache_section = Regexp.new([ - %r{\[pack_objects_cache\]\nenabled = true}, - %r{dir = '#{pack_objects_cache_dir}'}, - %r{max_age = '#{pack_objects_cache_max_age}'}, - ].map(&:to_s).join('\s+')) - - expect(chef_run).to render_file(config_path).with_content { |content| - expect(content).to include("tls_listen_addr = 'localhost:8888'") - expect(content).to include("certificate_path = '/path/to/cert.pem'") - expect(content).to include("key_path = '/path/to/key.pem'") - expect(content).to include(%(rugged_git_config_search_path = "#{ruby_rugged_git_config_search_path}")) - expect(content).to include("prometheus_listen_addr = 'localhost:9000'") - expect(content).to match(gitaly_logging_section) - expect(content).to match(%r{\[prometheus\]\s+grpc_latency_buckets = #{Regexp.escape(prometheus_grpc_latency_buckets)}}) - expect(content).to match(%r{\[auth\]\s+token = '#{Regexp.escape(auth_token)}'\s+transitioning = true}) - expect(content).to match(gitaly_ruby_section) - expect(content).to match(%r{\[git\]\s+catfile_cache_size = 50}) - expect(content).to match(gitlab_shell_section) - expect(content).to match(gitlab_section) - expect(content).to match(hooks_section) - expect(content).to match(maintenance_section) - expect(content).to match(cgroups_section) - expect(content).to match(cgroups_memory_section) - expect(content).to match(cgroups_cpu_section) - expect(content).to match(pack_objects_cache_section) - } - end - - it 'populates gitaly config.toml with custom deprecated values' do - stub_gitlab_rb( - gitlab_shell: { - custom_hooks_dir: gitlab_shell_custom_hooks_dir - } - ) - - hooks_section = Regexp.new([ - %r{\[hooks\]}, - %r{custom_hooks_dir = '#{Regexp.escape(gitlab_shell_custom_hooks_dir)}'}, - ].map(&:to_s).join('\s+')) - - expect(chef_run).to render_file(config_path).with_content { |content| - expect(content).to match(hooks_section) - } - end - - it 'renders the runit run script with custom values' do - expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run') - .with_content(%r{ulimit -n #{open_files_ulimit}}) - end - - it 'renders the runit run script with cgroup root creation' do - expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run').with_content { |content| - expect(content).to match(%r{mkdir -m 0700 -p #{cgroups_mountpoint}/memory/#{cgroups_hierarchy_root}}) - expect(content).to match(%r{mkdir -m 0700 -p #{cgroups_mountpoint}/cpu/#{cgroups_hierarchy_root}}) - expect(content).to match(%r{chown foo:bar #{cgroups_mountpoint}/memory/#{cgroups_hierarchy_root}}) - expect(content).to match(%r{chown foo:bar #{cgroups_mountpoint}/cpu/#{cgroups_hierarchy_root}}) - } - end - - it 'populates sv related log files' do - expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/gitaly/) - end - - context 'when using multiple maintenance storage entries' do - let(:daily_maintenance_storages) { %w(storage0 storage1 storage2) } - - it 'renders daily_maintenance with multiple storage entries' do - expect(chef_run).to render_file(config_path).with_content { |content| - expect(content).to include("storages = #{daily_maintenance_storages}") - } - end - end - - context 'when maintenance is disabled' do - let(:daily_maintenance_disabled) { true } - - it 'renders daily_maintenance with disabled set to true' do - expect(chef_run).to render_file(config_path).with_content { |content| - expect(content).to include("[daily_maintenance]\ndisabled = true\n\n") - } - end - end - - context 'when using gitaly storage configuration' do - before do - stub_gitlab_rb( - gitaly: { - storage: [ - { - 'name' => 'default', - 'path' => '/tmp/path-1' - }, - { - 'name' => 'nfs1', - 'path' => '/mnt/nfs1' - } - ] - } - ) - end - - it 'populates gitaly config.toml with custom storages' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/tmp/path-1'}) - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[storage\]\]\s+name = 'nfs1'\s+path = '/mnt/nfs1'}) - end - end - - context 'when using git_data_dirs storage configuration' do - context 'using local gitaly' do - before do - stub_gitlab_rb( - { - git_data_dirs: - { - 'default' => { 'path' => '/tmp/default/git-data' }, - 'nfs1' => { 'path' => '/mnt/nfs1' } - } - } - ) - end - - it 'populates gitaly config.toml with custom storages' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/tmp/default/git-data/repositories'}) - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[storage\]\]\s+name = 'nfs1'\s+path = '/mnt/nfs1/repositories'}) - expect(chef_run).not_to render_file(config_path) - .with_content('gitaly_address: "/var/opt/gitlab/gitaly/gitaly.socket"') - end - end - - context 'using external gitaly' do - before do - stub_gitlab_rb( - { - git_data_dirs: - { - 'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' }, - } - } - ) - end - - it 'populates gitaly config.toml with custom storages' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/var/opt/gitlab/git-data/repositories'}) - expect(chef_run).not_to render_file(config_path) - .with_content('gitaly_address: "tcp://gitaly.internal:8075"') - end - end - end - end - - context 'when gitaly is disabled' do - before do - stub_gitlab_rb(gitaly: { enable: false }) - end - - it_behaves_like "disabled runit service", "gitaly" - - it 'does not create the gitaly directories' do - expect(chef_run).not_to create_directory('/var/opt/gitlab/gitaly') - expect(chef_run).not_to create_directory('/var/log/gitlab/gitaly') - expect(chef_run).not_to create_directory('/opt/gitlab/etc/gitaly') - expect(chef_run).not_to create_file('/var/opt/gitlab/gitaly/config.toml') - end - end - - context 'when using concurrency configuration' do - before do - stub_gitlab_rb( - { - gitaly: { - concurrency: [ - { - 'rpc' => "/gitaly.SmartHTTPService/PostReceivePack", - 'max_per_repo' => 20 - }, { - 'rpc' => "/gitaly.SSHService/SSHUploadPack", - 'max_per_repo' => 5 - } - ] - } - } - ) - end - - it 'populates gitaly config.toml with custom concurrency configurations' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SmartHTTPService/PostReceivePack"\s+max_per_repo = 20}) - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SSHService/SSHUploadPack"\s+max_per_repo = 5}) - end - end - - shared_examples 'empty concurrency configuration' do - it 'does not generate a gitaly concurrency configuration' do - expect(chef_run).not_to render_file(config_path) - .with_content(%r{\[\[concurrency\]\]}) - end - end - - context 'when not using concurrency configuration' do - context 'when concurrency configuration is not set' do - before do - stub_gitlab_rb( - { - gitaly: { - } - } - ) - end - - it_behaves_like 'empty concurrency configuration' - end - - context 'when concurrency configuration is empty' do - before do - stub_gitlab_rb( - { - gitaly: { - concurrency: [] - } - } - ) - end - - it_behaves_like 'empty concurrency configuration' - end - - context 'when max_queue_size and max_queue_wait are empty' do - before do - stub_gitlab_rb( - { - gitaly: { - concurrency: [ - { - 'rpc' => "/gitaly.SmartHTTPService/PostReceivePack", - 'max_per_repo' => 20, - }, { - 'rpc' => "/gitaly.SSHService/SSHUploadPack", - 'max_per_repo' => 5, - } - ] - } - } - ) - end - - it 'populates gitaly config.toml without max_queue_size and max_queue_wait' do - expect(chef_run).to render_file(config_path).with_content { |content| - expect(content).not_to include("max_queue_size") - expect(content).not_to include("max_queue_wait") - } - end - end - - context 'when max_per_repo is empty' do - before do - stub_gitlab_rb( - { - gitaly: { - concurrency: [ - { - 'rpc' => "/gitaly.SmartHTTPService/PostReceivePack", - 'max_queue_size' => '10s' - }, { - 'rpc' => "/gitaly.SSHService/SSHUploadPack", - 'max_queue_size' => '10s' - } - ] - } - } - ) - end - - it 'populates gitaly config.toml without max_per_repo' do - expect(chef_run).to render_file(config_path).with_content { |content| - expect(content).not_to include("max_per_repo") - } - end - end - - context 'when max_queue_wait is set' do - before do - stub_gitlab_rb( - { - gitaly: { - concurrency: [ - { - 'rpc' => "/gitaly.SmartHTTPService/PostReceivePack", - 'max_queue_wait' => "10s", - } - ] - } - } - ) - end - - it 'populates gitaly config.toml with quoted max_queue_wait' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SmartHTTPService/PostReceivePack"\s+max_queue_wait = "10s"}) - end - end - end - - context 'populates default env variables' do - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitaly/env').with_variables(default_vars) - end - end - - context 'computes env variables based on other values' do - before do - stub_gitlab_rb( - { - user: { - home: "/my/random/path" - } - } - ) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitaly/env').with_variables( - default_vars.merge( - { - 'HOME' => '/my/random/path', - } - ) - ) - end - end - - context 'with a non-default workhorse unix socket' do - context 'with only a listen address set' do - before do - stub_gitlab_rb(gitlab_workhorse: { listen_addr: '/fake/workhorse/socket' }) - end - - it 'create config file with provided values' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Ffake%2Fworkhorse%2Fsocket'\s+relative_url_root = ''}) - end - end - - context 'with only a socket directory set' do - before do - stub_gitlab_rb(gitlab_workhorse: { sockets_directory: '/fake/workhorse/sockets' }) - end - - it 'create config file with provided values' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Ffake%2Fworkhorse%2Fsockets%2Fsocket'\s+relative_url_root = ''}) - end - end - - context 'with a listen_address and a sockets_directory set' do - before do - stub_gitlab_rb(gitlab_workhorse: { listen_addr: '/sockets/in/the/wind', sockets_directory: '/sockets/in/the' }) - end - - it 'create config file with provided values' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fsockets%2Fin%2Fthe%2Fwind'\s+relative_url_root = ''}) - end - end - end - - context 'with a tcp workhorse listener' do - before do - stub_gitlab_rb( - external_url: 'http://example.com/gitlab', - gitlab_workhorse: { - listen_network: 'tcp', - listen_addr: 'localhost:1234' - } - ) - end - - it 'create config file with only the URL set' do - expect(chef_run).to render_file(config_path).with_content { |content| - expect(content).to match(%r{\[gitlab\]\s+url = 'http://localhost:1234/gitlab'}) - expect(content).not_to match(/relative_url_root/) - } - end - end - - context 'with relative path in external_url' do - before do - stub_gitlab_rb(external_url: 'http://example.com/gitlab') - end - - it 'create config file with the relative_url_root set' do - expect(chef_run).to render_file(config_path) - .with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket'\s+relative_url_root = '/gitlab'}) - end - end - - context 'with cgroups mountpoint and hierarchy_root' do - before do - stub_gitlab_rb( - gitaly: { - cgroups_mountpoint: '/sys/fs/cgroup', - cgroups_hierarchy_root: 'gitaly' - } - ) - end - end - - include_examples "consul service discovery", "gitaly", "gitaly" -end - -RSpec.describe 'gitaly::git_data_dirs' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when user has not specified git_data_dir' do - it 'defaults to correct path' do - expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']) - .to eql('default' => { 'path' => '/var/opt/gitlab/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }) - end - end - - context 'when gitaly is set to use a listen_addr' do - before do - stub_gitlab_rb(git_data_dirs: { - 'default' => { - 'path' => '/tmp/user/git-data' - } - }, gitaly: { - socket_path: '', - listen_addr: 'localhost:8123' - }) - end - - it 'correctly sets the repository storage directories' do - expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']) - .to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tcp://localhost:8123' }) - end - end - - context 'when gitaly is set to use a tls_listen_addr' do - before do - stub_gitlab_rb(git_data_dirs: { - 'default' => { - 'path' => '/tmp/user/git-data' - } - }, gitaly: { - socket_path: '', tls_listen_addr: 'localhost:8123' - }) - end - - it 'correctly sets the repository storage directories' do - expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']) - .to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tls://localhost:8123' }) - end - end - - context 'when both tls and socket' do - before do - stub_gitlab_rb(git_data_dirs: { - 'default' => { - 'path' => '/tmp/user/git-data' - } - }, gitaly: { - socket_path: '/some/socket/path.socket', tls_listen_addr: 'localhost:8123' - }) - end - - it 'TlS should take precedence' do - expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']) - .to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tls://localhost:8123' }) - end - end - - context 'when git_data_dirs is set to multiple directories' do - before do - stub_gitlab_rb({ - git_data_dirs: { - 'default' => { 'path' => '/tmp/default/git-data' }, - 'overflow' => { 'path' => '/tmp/other/git-overflow-data' } - } - }) - end - - it 'correctly sets the repository storage directories' do - expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ - 'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }, - 'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' } - }) - end - end - - context 'when git_data_dirs is set to multiple directories with different gitaly addresses' do - before do - stub_gitlab_rb({ - git_data_dirs: { - 'default' => { 'path' => '/tmp/default/git-data' }, - 'overflow' => { 'path' => '/tmp/other/git-overflow-data', 'gitaly_address' => 'tcp://localhost:8123', 'gitaly_token' => '123secret456gitaly' } - } - }) - end - - it 'correctly sets the repository storage directories' do - expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ - 'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }, - 'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'tcp://localhost:8123', 'gitaly_token' => '123secret456gitaly' } - }) - end - end - - context 'when path not defined in git_data_dirs' do - before do - stub_gitlab_rb( - { - git_data_dirs: - { - 'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' }, - } - } - ) - end - - it 'correctly sets the repository storage directories' do - expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ 'default' => { 'path' => '/var/opt/gitlab/git-data/repositories', 'gitaly_address' => 'tcp://gitaly.internal:8075' } }) - end - end - - context 'when git_data_dirs is set with symbol keys rather than string keys' do - before do - with_symbol_keys = { - default: { path: '/tmp/default/git-data' }, - overflow: { path: '/tmp/other/git-overflow-data' } - } - - allow(Gitlab).to receive(:[]).with('git_data_dirs').and_return(with_symbol_keys) - end - - it 'correctly sets the repository storage directories' do - expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ - 'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }, - 'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' } - }) - end - end -end diff --git a/spec/chef/recipes/gitlab-healthcheck_spec.rb b/spec/chef/recipes/gitlab-healthcheck_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLWhlYWx0aGNoZWNrX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-healthcheck_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-healthcheck' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'nginx is enabled' do - before do - stub_gitlab_rb(nginx: { enable: true }) - end - - it 'correctly renders the healthcheck-rc file' do - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") - .with_content(%r{url='http://localhost:80/help'}) - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc").with_content(%r{flags='--insecure'}) - end - - it 'correctly renders out the healthcheck-rc file when using https' do - stub_gitlab_rb(external_url: 'https://gitlab.example.com') - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") - .with_content(%r{url='https://localhost:443/help'}) - end - - it 'correctly renders out the healthcheck-rc file when using custom port' do - stub_gitlab_rb(external_url: 'http://gitlab.example.com:8080') - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") - .with_content(%r{url='http://localhost:8080/help'}) - end - - it 'correctly renders out the healthcheck-rc file when using a relative url' do - stub_gitlab_rb(external_url: 'http://gitlab.example.com/custom') - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") - .with_content(%r{url='http://localhost:80/custom/help'}) - end - end - - context 'nginx is disabled' do - before do - stub_gitlab_rb(nginx: { enable: false }) - end - - it 'correctly renders the healthcheck-rc file using workhorse' do - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") - .with_content(%r{url='http://localhost/help'}) - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") - .with_content(%r{flags='--unix-socket /var/opt/gitlab/gitlab-workhorse/sockets/socket'}) - end - - it 'correctly renders healthcheck-rc file using workhorse on a port' do - stub_gitlab_rb( - gitlab_workhorse: { listen_network: 'tcp', listen_addr: 'localhost:9191' } - ) - expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") - .with_content(%r{url='http://localhost:9191/help'}) - end - - it 'does not render the healthcheck-rc file when workhorse workhorse is disabled' do - stub_gitlab_rb(nginx: { enable: false }, gitlab_workhorse: { enable: false }) - expect(chef_run).not_to render_file("/opt/gitlab/etc/gitlab-healthcheck-rc") - end - end -end diff --git a/spec/chef/recipes/gitlab-kas_spec.rb b/spec/chef/recipes/gitlab-kas_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLWthc19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-kas_spec.rb +++ /dev/null @@ -1,508 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab-kas' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service env_dir templatesymlink)).converge('gitlab::default') } - let(:gitlab_kas_config_yml) { chef_run_load_yaml_template(chef_run, '/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when application_role is configured' do - before do - stub_gitlab_rb(roles: %w(application_role)) - end - - it 'should be enabled' do - expect(chef_run).to include_recipe('gitlab-kas::enable') - end - end - - context 'with defaults' do - before do - stub_gitlab_rb(external_url: 'https://gitlab.example.com') - end - - it 'creates a default VERSION file and restarts service' do - expect(chef_run).to create_version_file('Create version file for Gitlab KAS').with( - version_file_path: '/var/opt/gitlab/gitlab-kas/VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/gitlab-kas --version' - ) - - expect(chef_run.version_file('Create version file for Gitlab KAS')).to notify('runit_service[gitlab-kas]').to(:restart) - end - - it 'correctly renders the KAS service run file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-kas/run").with_content(%r{--configuration-file /var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml}) - end - - it 'correctly renders the KAS log run file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-kas/log/run").with_content(%r{exec svlogd -tt /var/log/gitlab/gitlab-kas}) - end - - it 'correctly renders the KAS config file' do - expect(gitlab_kas_config_yml).to( - include( - agent: hash_including( - listen: { - network: 'tcp', - address: 'localhost:8150', - websocket: true, - }, - kubernetes_api: { - listen: { - address: 'localhost:8154', - }, - url_path_prefix: '/' - } - ), - observability: { - logging: { - level: 'info', - }, - usage_reporting_period: '60s' - }, - private_api: { - listen: { - address: "localhost:8155", - authentication_secret_file: "/var/opt/gitlab/gitlab-kas/private_api_authentication_secret_file", - network: "tcp" - }, - } - ) - ) - end - - it 'correctly renders the KAS authentication secret files' do - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-kas/authentication_secret_file").with_content { |content| Base64.strict_decode64(content).size == 32 } - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-kas/private_api_authentication_secret_file").with_content { |content| Base64.strict_decode64(content).size == 32 } - end - - it 'sets OWN_PRIVATE_API_URL and SSL_CERT_DIR' do - expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-kas/env/OWN_PRIVATE_API_URL').with_content('grpc://localhost:8155') - expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-kas/env/SSL_CERT_DIR').with_content('/opt/gitlab/embedded/ssl/certs/') - end - end - - context 'with user settings' do - let(:api_secret_key) { Base64.strict_encode64('1' * 32) } - let(:private_api_secret_key) { Base64.strict_encode64('2' * 32) } - - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_kas: { - api_secret_key: api_secret_key, - private_api_secret_key: private_api_secret_key, - listen_address: 'localhost:5006', - listen_websocket: false, - metrics_usage_reporting_period: '120', - sentry_dsn: 'https://my_key:my_secret@sentry.io/test_project', - sentry_environment: 'production', - log_level: 'debug', - } - ) - end - - it 'correctly renders the KAS service run file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-kas/run").with_content(%r{--configuration-file /var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml}) - end - - it 'correctly renders the KAS log run file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-kas/log/run").with_content(%r{exec svlogd -tt /var/log/gitlab/gitlab-kas}) - end - - it 'correctly renders the KAS config file' do - expect(gitlab_kas_config_yml).to( - include( - agent: hash_including( - listen: { - network: 'tcp', - address: 'localhost:5006', - websocket: false - } - ), - observability: { - logging: { - level: 'debug' - }, - usage_reporting_period: '120s', - sentry: { - dsn: 'https://my_key:my_secret@sentry.io/test_project', - environment: 'production' - } - } - ) - ) - end - - it 'correctly renders the KAS authentication secret files' do - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-kas/authentication_secret_file").with_content(api_secret_key) - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-kas/private_api_authentication_secret_file").with_content(private_api_secret_key) - end - end - - describe 'gitlab.yml configuration' do - let(:gitlab_yml) { chef_run_load_yaml_template(chef_run, '/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } - - context 'with defaults' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com' - ) - end - - it 'renders gitlab_kas enabled with default URLs in config/gitlab.yml' do - expect(gitlab_yml[:production][:gitlab_kas]).to include( - enabled: true, - external_url: 'wss://gitlab.example.com/-/kubernetes-agent/', - internal_url: 'grpc://localhost:8153', - external_k8s_proxy_url: 'https://gitlab.example.com/-/kubernetes-agent/k8s-proxy/' - ) - end - end - - context 'when not https' do - before do - stub_gitlab_rb( - external_url: 'http://gitlab.example.com' - ) - end - - it 'has exernal URL with scheme `ws` instead of `wss`' do - expect(gitlab_yml[:production][:gitlab_kas]).to include( - external_url: 'ws://gitlab.example.com/-/kubernetes-agent/' - ) - end - end - - context 'with custom listen addresses' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_kas: { - enable: true, - listen_address: 'custom-address:1234', - internal_api_listen_address: 'custom-api-address:9999' - } - ) - end - - it 'derives the external URLs from the top level external URL, and the internal URL from the listen address' do - expect(gitlab_yml[:production][:gitlab_kas]).to include( - enabled: true, - external_url: 'wss://gitlab.example.com/-/kubernetes-agent/', - internal_url: 'grpc://custom-api-address:9999', - external_k8s_proxy_url: 'https://gitlab.example.com/-/kubernetes-agent/k8s-proxy/' - ) - end - end - - context 'with explicitly configured URLs' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_rails: { - gitlab_kas_external_url: 'wss://kas.example.com', - gitlab_kas_internal_url: 'grpc://kas.internal', - gitlab_kas_external_k8s_proxy_url: 'https://kas.example.com/k8s-proxy' - } - ) - end - - it 'uses the explicitly configured URL' do - expect(gitlab_yml[:production][:gitlab_kas]).to include( - external_url: 'wss://kas.example.com', - internal_url: 'grpc://kas.internal', - external_k8s_proxy_url: 'https://kas.example.com/k8s-proxy' - ) - end - end - end - - describe 'logrotate settings' do - context 'default values' do - it_behaves_like 'configured logrotate service', 'gitlab-kas', 'git', 'git' - end - - context 'specified username and group' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'configured logrotate service', 'gitlab-kas', 'foo', 'bar' - end - end - - describe 'redis config' do - context 'when there is a password' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_kas: { enable: true }, - gitlab_rails: { redis_password: 'the-password' } - ) - end - - it 'writes password_file into the kas config' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| - kas_redis_cfg = YAML.safe_load(content)['redis'] - expect(kas_redis_cfg).to( - include( - 'password_file' => '/var/opt/gitlab/gitlab-kas/redis_password_file' - ) - ) - } - end - it 'renders the password file' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/redis_password_file').with_content('the-password') - end - end - - context 'when there is no password' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_kas: { enable: true } - ) - end - - it 'does not write password_file into the config' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| - kas_cfg = YAML.safe_load(content) - expect(kas_cfg['redis']).not_to include('password_file') - } - end - - it 'renders no password file' do - expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-kas/redis_password_file") - end - end - - context 'without sentinel host only' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_kas: { - enable: true - }, - gitlab_rails: { - redis_host: 'the-host' - } - ) - end - - it 'renders a single server configuration in to the kas config' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| - kas_redis_cfg = YAML.safe_load(content)['redis'] - expect(kas_redis_cfg).to( - include( - 'network' => 'tcp', - 'server' => { - 'address' => 'the-host:6379' - } - ) - ) - expect(kas_redis_cfg).not_to(include('sentinel')) - } - end - end - - context 'without sentinel host and port' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_rails: { - redis_host: 'the-host', - redis_port: 12345, - } - ) - end - - it 'renders a single server configuration in to the kas config' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| - kas_redis_cfg = YAML.safe_load(content)['redis'] - expect(kas_redis_cfg).to( - include( - 'network' => 'tcp', - 'server' => { - 'address' => 'the-host:12345' - } - ) - ) - expect(kas_redis_cfg).not_to(include('sentinel')) - } - end - end - - context 'without sentinel but with tls enabled' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_rails: { - redis_host: 'the-host', - redis_port: 12345, - redis_ssl: true, - } - ) - end - - it 'renders a configuration with tls enabled in to the kas config' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| - kas_redis_cfg = YAML.safe_load(content)['redis'] - expect(kas_redis_cfg).to( - include( - 'network' => 'tcp', - 'tls' => { - 'enabled' => true - }, - 'server' => { - 'address' => 'the-host:12345' - } - ) - ) - } - end - end - - context 'with sentinel' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_rails: { - redis_sentinels: [ - { host: 'a', port: 1 }, - { host: 'b', port: 2 }, - { host: 'c' } - ] - }, - redis: { - master_name: 'example-redis' - } - ) - end - - it 'renders a single server configuration in to the kas config' do - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-kas/gitlab-kas-config.yml').with_content { |content| - kas_redis_cfg = YAML.safe_load(content)['redis'] - expect(kas_redis_cfg).to( - include( - 'network' => 'tcp', - 'sentinel' => { - 'master_name' => 'example-redis', - 'addresses' => [ - 'a:1', - 'b:2', - 'c:6379' - ] - } - ) - ) - expect(kas_redis_cfg).not_to(include('server')) - } - end - end - end - - describe 'TLS listen config' do - context 'when all certificates and keys are defined' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_kas: { - enable: true, - listen_websocket: false, - certificate_file: '/path/to/cert.pem', - key_file: '/path/to/key.pem', - internal_api_certificate_file: '/path/to/internal-api-cert.pem', - internal_api_key_file: '/path/to/internal-api-key.pem', - kubernetes_api_certificate_file: '/path/to/kubernetes-api-cert.pem', - kubernetes_api_key_file: '/path/to/kubernetes-api-key.pem', - private_api_certificate_file: '/path/to/private-api-cert.pem', - private_api_key_file: '/path/to/private-api-key.pem' - } - ) - end - - it 'correctly renders the KAS config file' do - expect(gitlab_kas_config_yml).to( - include( - agent: hash_including( - listen: hash_including( - certificate_file: '/path/to/cert.pem', - key_file: '/path/to/key.pem' - ), - kubernetes_api: hash_including( - listen: hash_including( - certificate_file: '/path/to/kubernetes-api-cert.pem', - key_file: '/path/to/kubernetes-api-key.pem' - ) - ) - ), - api: { - listen: hash_including( - certificate_file: '/path/to/internal-api-cert.pem', - key_file: '/path/to/internal-api-key.pem' - ), - }, - private_api: { - listen: hash_including( - certificate_file: '/path/to/private-api-cert.pem', - key_file: '/path/to/private-api-key.pem' - ) - } - ) - ) - end - end - - context 'when certificate/key bundles are not correctly defined' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_kas: { - enable: true, - certificate_file: '/path/to/file.pem', - internal_api_certificate_file: '/path/to/file.pem', - kubernetes_api_key_file: '/path/to/file.pem', - private_api_key_file: '/path/to/file.pem' - } - ) - end - - it 'renders no certificate or key configuration' do - expect(gitlab_kas_config_yml).not_to(include('/path/to/file.pem')) - end - end - - context 'when the certificate/key bundle is defined and websocket tunneling is enabled' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - gitlab_kas: { - enable: true, - listen_websocket: true, - certificate_file: '/path/to/cert.pem', - key_file: '/path/to/key.pem', - } - ) - end - - it 'logs a warning' do - expect(chef_run).to run_ruby_block('websocket TLS termination') - end - end - end - - def chef_run_load_yaml_template(chef_run, path) - template = chef_run.template(path) - file_content = ChefSpec::Renderer.new(chef_run, template).content - YAML.safe_load(file_content, [], [], true, symbolize_names: true) - end -end diff --git a/spec/chef/recipes/gitlab-rails/database_yml_spec.rb b/spec/chef/recipes/gitlab-rails/database_yml_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2RhdGFiYXNlX3ltbF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/database_yml_spec.rb +++ /dev/null @@ -1,277 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - describe 'Database settings' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: 'templatesymlink').converge('gitlab::default') } - let(:database_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/database.yml') } - let(:database_yml_file_content) { ChefSpec::Renderer.new(chef_run, database_yml_template).content } - let(:database_yml) { YAML.safe_load(database_yml_file_content, [], [], true, symbolize_names: true) } - let(:default_content) do - { - main: { - adapter: 'postgresql', - application_name: nil, - collation: nil, - connect_timeout: nil, - database: "gitlabhq_production", - database_tasks: true, - encoding: "unicode", - host: "/var/opt/gitlab/postgresql", - keepalives: nil, - keepalives_count: nil, - keepalives_idle: nil, - keepalives_interval: nil, - load_balancing: { - hosts: [] - }, - password: nil, - port: 5432, - prepared_statements: false, - socket: nil, - sslca: nil, - sslcompression: 0, - sslmode: nil, - sslrootcert: nil, - statement_limit: 1000, - tcp_user_timeout: nil, - username: "gitlab", - variables: { - statement_timeout: nil - } - } - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(File).to receive(:symlink?).and_call_original - end - - context 'with default settings' do - it 'renders database.yml with main database and default values' do - expect(database_yml[:production]).to eq(default_content) - end - end - - context 'with user provided settings' do - context 'via top level db_* keys' do - before do - stub_gitlab_rb( - gitlab_rails: { - db_database: 'foobar' - } - ) - end - - it 'renders database.yml with user specified values for main database' do - expect(database_yml[:production][:main][:database]).to eq('foobar') - expect(database_yml[:production][:main][:database_tasks]).to eq(true) - end - end - - context 'via top level db_* keys and overwritten main:' do - before do - stub_gitlab_rb( - gitlab_rails: { - db_database: 'foobar', - db_database_tasks: true, - databases: { - main: { - enable: true, - db_database_tasks: false - } - } - } - ) - end - - it 'renders database.yml with user specified values for main database' do - expect(database_yml[:production][:main][:database]).to eq('foobar') - expect(database_yml[:production][:main][:database_tasks]).to eq(false) - end - end - - context "for main database via gitlab_rails['databases']['main'] setting" do - before do - stub_gitlab_rb( - gitlab_rails: { - databases: { - main: { - enable: true, - db_database: 'foobar' - } - } - } - ) - end - - it 'renders database.yml with user specified values for main database' do - expect(database_yml[:production][:main][:database]).to eq('foobar') - end - end - - context 'with additional databases specified' do - context 'when using the same database as main:' do - before do - stub_gitlab_rb( - gitlab_rails: { - databases: { - ci: { - enable: true - } - } - } - ) - end - - it 'renders database.yml with main stanza first' do - expect(database_yml_file_content).to match("production:\n main:") - end - - it 'renders database.yml with both main and additional databases using default values, but disabled database_tasks' do - ci_content = default_content[:main].dup - ci_content[:database_tasks] = false - expected_output = default_content.merge(ci: ci_content) - - expect(database_yml[:production]).to eq(expected_output) - end - end - - context 'when using a different database to main:' do - before do - stub_gitlab_rb( - gitlab_rails: { - databases: { - ci: { - enable: true, - db_database: 'gitlabhq_production_ci' - } - } - } - ) - end - - it 'renders database.yml with main stanza first' do - expect(database_yml_file_content).to match("production:\n main:") - end - - it 'renders database.yml with both main and additional databases using default values, and enabled database_tasks' do - ci_content = default_content[:main].dup - ci_content[:database] = 'gitlabhq_production_ci' - ci_content[:database_tasks] = true - expected_output = default_content.merge(ci: ci_content) - - expect(database_yml[:production]).to eq(expected_output) - end - end - - context 'when db_database_tasks is explicitly enabled in main, but disabled in CI' do - before do - stub_gitlab_rb( - gitlab_rails: { - databases: { - main: { - db_connect_timeout: 30, - db_database_tasks: true - }, - ci: { - enable: true, - db_host: 'patroni-ci', - db_connect_timeout: 50, - db_database_tasks: false - } - } - } - ) - end - - it 'renders database.yml with user specified DB settings' do - expect(database_yml[:production][:main][:connect_timeout]).to eq(30) - expect(database_yml[:production][:main][:database_tasks]).to eq(true) - expect(database_yml[:production][:ci][:connect_timeout]).to eq(50) - expect(database_yml[:production][:ci][:database_tasks]).to eq(false) - end - end - - context 'with different settings for main database and additional databases' do - before do - stub_gitlab_rb( - gitlab_rails: { - databases: { - main: { - db_connect_timeout: 30, - }, - ci: { - enable: true, - db_connect_timeout: 50, - db_database_tasks: false - } - } - } - ) - end - - it 'renders database.yml with user specified DB settings' do - expect(database_yml[:production][:main][:connect_timeout]).to eq(30) - expect(database_yml[:production][:main][:database_tasks]).to eq(true) - expect(database_yml[:production][:ci][:connect_timeout]).to eq(50) - expect(database_yml[:production][:ci][:database_tasks]).to eq(false) - end - end - - context 'with additional database specified but not enabled' do - before do - stub_gitlab_rb( - gitlab_rails: { - databases: { - main: { - db_connect_timeout: 30, - }, - ci: { - db_connect_timeout: 50 - } - } - } - ) - end - - it 'renders database.yml without additional database' do - expect(database_yml[:production].keys).not_to include('ci') - end - end - - context 'with invalid additional database specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - databases: { - foobar: { - enable: true, - db_database: 'gitlabhq_foobar' - }, - johndoe: { - db_database: 'gitlabhq_johndoe' - }, - ci: { - enable: true, - db_database: 'gitlabhq_ci' - } - } - } - ) - end - - it 'raises warning about invalid database' do - chef_run - expect_logged_warning("Additional database `foobar` not supported in Rails application. It will be ignored.") - end - - it 'does not raise warning about invalid database that is not enabled' do - chef_run - expect(LoggingHelper.messages).not_to include(kind: :warning, message: /Additional database `johndoe` not supported/) - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/backup_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/backup_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvYmFja3VwX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/backup_spec.rb +++ /dev/null @@ -1,105 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - include_context 'object storage config' - - describe 'Backup settings' do - let(:backup_settings) { gitlab_yml[:production][:backup] } - - context 'with default values' do - it 'renders with default backup settings' do - expect(backup_settings).to eq( - archive_permissions: nil, - gitaly_backup_path: '/opt/gitlab/embedded/bin/gitaly-backup', - keep_time: nil, - path: '/var/opt/gitlab/backups', - pg_schema: nil, - upload: { - connection: nil, - encryption: nil, - encryption_key: nil, - multipart_chunk_size: nil, - remote_directory: nil, - storage_options: {}, - storage_class: nil - } - ) - end - end - - context 'with user specified gitaly-backup path set' do - before do - stub_gitlab_rb( - gitlab_rails: { - backup_gitaly_backup_path: '/some other/bin/gitaly-backup' - } - ) - end - - it 'renders with the user specified gitaly-backup path' do - expect(backup_settings).to include( - gitaly_backup_path: '/some other/bin/gitaly-backup' - ) - end - end - - context 'remote upload', :aggregate_failures do - let(:upload_settings) { backup_settings[:upload] } - - context 'with Server-Side Encryption' do - context 'with Amazon S3-Managed Keys (SSE-S3)' do - before do - stub_gitlab_rb( - gitlab_rails: { - 'backup_upload_connection' => aws_connection_hash, - 'backup_encryption' => 'AES256' - } - ) - end - - it 'renders encryption option' do - expect(upload_settings[:encryption]).to eq('AES256') - expect(upload_settings[:encryption_key]).to be_nil - expect(upload_settings[:storage_options]).to eq({}) - end - end - - context 'with Customer Master Keys (CMKs) Stored in AWS Key Management Service (SSE-KMS)' do - before do - stub_gitlab_rb( - gitlab_rails: { - 'backup_upload_connection' => aws_connection_hash, - 'backup_upload_storage_options' => aws_storage_options_hash - } - ) - end - - it 'renders storage options' do - expect(upload_settings[:encryption]).to be_nil - expect(upload_settings[:encryption_key]).to be_nil - expect(upload_settings[:storage_options]).to eq(aws_storage_options) - end - end - - context 'with Customer-Provided Keys (SSE-C)' do - before do - stub_gitlab_rb( - gitlab_rails: { - 'backup_upload_connection' => aws_connection_hash, - 'backup_encryption' => 'AES256', - 'backup_encryption_key' => 'A12345678' - } - ) - end - - it 'renders encryption and encryption key options' do - expect(upload_settings[:encryption]).to eq('AES256') - expect(upload_settings[:encryption_key]).to eq('A12345678') - expect(upload_settings[:storage_options]).to eq({}) - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/consul_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/consul_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvY29uc3VsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/consul_spec.rb +++ /dev/null @@ -1,123 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'Consul settings' do - context 'with default values' do - it 'renders gitlab.yml without Consul settings' do - expect(gitlab_yml[:production][:consul]).to eq( - api_url: "" - ) - end - end - - context 'with user specified values' do - context 'when bundled consul is enabled' do - before do - stub_gitlab_rb( - consul: { - enable: true - } - ) - end - - it 'renders gitlab.yml with default Consul settings' do - expect(gitlab_yml[:production][:consul]).to eq( - api_url: 'http://localhost:8500' - ) - end - end - - context 'when Consul is running on non-default location' do - context 'when setting client_addr and ports' do - before do - stub_gitlab_rb( - consul: { - enable: true, - configuration: { - client_addr: '10.0.0.1', - ports: { - http: 1234 - } - } - } - ) - end - - it 'renders gitlab.yml with specified Consul settings' do - expect(gitlab_yml[:production][:consul]).to eq( - api_url: 'http://10.0.0.1:1234' - ) - end - end - - context 'when setting addresses and ports' do - before do - stub_gitlab_rb( - consul: { - enable: true, - configuration: { - addresses: { - http: '10.0.1.2', - }, - ports: { - http: 1234 - } - } - } - ) - end - - it 'renders gitlab.yml with specified Consul settings' do - expect(gitlab_yml[:production][:consul]).to eq( - api_url: 'http://10.0.1.2:1234' - ) - end - end - - context 'when http port is disabled via negative port number' do - before do - stub_gitlab_rb( - consul: { - enable: true, - configuration: { - addresses: { - https: '10.0.1.2', - }, - ports: { - http: -1, - https: 1234 - } - } - } - ) - end - - it 'renders gitlab.yml with specified Consul settings' do - expect(gitlab_yml[:production][:consul]).to eq( - api_url: 'https://10.0.1.2:1234' - ) - end - end - end - - context 'when tls is enabled' do - before do - stub_gitlab_rb( - consul: { - enable: true, - use_tls: true - } - ) - end - - it 'renders gitlab.yml with specified Consul settings' do - expect(gitlab_yml[:production][:consul]).to eq( - api_url: 'https://localhost:8501' - ) - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/cron_jobs_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/cron_jobs_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvY3Jvbl9qb2JzX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/cron_jobs_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'cron settings' do - context 'with default values' do - it "renders gitlab.yml without any cron settings" do - expect(gitlab_yml[:production][:cron_jobs]).to be nil - end - end - - context 'with user specified values' do - using RSpec::Parameterized::TableSyntax - - where(:gitlab_yml_setting, :gitlab_rb_setting) do - 'admin_email_worker' | 'admin_email_worker_cron' - 'analytics_devops_adoption_create_all_snapshots_worker' | 'analytics_devops_adoption_create_all_snapshots_worker' - 'analytics_usage_trends_count_job_trigger_worker' | 'analytics_usage_trends_count_job_trigger_worker_cron' - 'ci_archive_traces_cron_worker' | 'ci_archive_traces_cron_worker_cron' - 'ci_platform_metrics_update_cron_worker' | 'ci_platform_metrics_update_cron_worker' - 'elastic_index_bulk_cron_worker' | 'elastic_index_bulk_cron' - 'environments_auto_stop_cron_worker' | 'environments_auto_stop_cron_worker_cron' - 'expire_build_artifacts_worker' | 'expire_build_artifacts_worker_cron' - 'geo_file_download_dispatch_worker' | 'geo_file_download_dispatch_worker_cron' - 'geo_migrated_local_files_clean_up_worker' | 'geo_migrated_local_files_clean_up_worker_cron' - 'geo_prune_event_log_worker' | 'geo_prune_event_log_worker_cron' - 'geo_repository_sync_worker' | 'geo_repository_sync_worker_cron' - 'geo_repository_verification_primary_batch_worker' | 'geo_repository_verification_primary_batch_worker_cron' - 'geo_repository_verification_secondary_scheduler_worker' | 'geo_repository_verification_secondary_scheduler_worker_cron' - 'geo_secondary_registry_consistency_worker' | 'geo_secondary_registry_consistency_worker' - 'geo_secondary_usage_data_cron_worker' | 'geo_secondary_usage_data_cron_worker' - 'historical_data_worker' | 'historical_data_worker_cron' - 'ldap_group_sync_worker' | 'ldap_group_sync_worker_cron' - 'ldap_sync_worker' | 'ldap_sync_worker_cron' - 'member_invitation_reminder_emails_worker' | 'member_invitation_reminder_emails_worker_cron' - 'pages_domain_removal_cron_worker' | 'pages_domain_removal_cron_worker' - 'pages_domain_ssl_renewal_cron_worker' | 'pages_domain_ssl_renewal_cron_worker' - 'pages_domain_verification_cron_worker' | 'pages_domain_verification_cron_worker' - 'personal_access_tokens_expired_notification_worker' | 'personal_access_tokens_expired_notification_worker_cron' - 'personal_access_tokens_expiring_worker' | 'personal_access_tokens_expiring_worker_cron' - 'pipeline_schedule_worker' | 'pipeline_schedule_worker_cron' - 'pseudonymizer_worker' | 'pseudonymizer_worker_cron' - 'remove_unaccepted_member_invites_worker' | 'remove_unaccepted_member_invites_cron_worker' - 'repository_archive_cache_worker' | 'repository_archive_cache_worker_cron' - 'repository_check_worker' | 'repository_check_worker_cron' - 'schedule_migrate_external_diffs_worker' | 'schedule_migrate_external_diffs_worker_cron' - 'stuck_ci_jobs_worker' | 'stuck_ci_jobs_worker_cron' - 'user_status_cleanup_batch_worker' | 'user_status_cleanup_batch_worker_cron' - 'namespaces_in_product_marketing_emails_worker' | 'namespaces_in_product_marketing_emails_worker_cron' - 'ssh_keys_expired_notification_worker' | 'ssh_keys_expired_notification_worker_cron' - 'ssh_keys_expiring_soon_notification_worker' | 'ssh_keys_expiring_soon_notification_worker_cron' - 'loose_foreign_keys_cleanup_worker' | 'loose_foreign_keys_cleanup_worker_cron' - end - - with_them do - before do - stub_gitlab_rb( - gitlab_rails: { - gitlab_rb_setting => '1 2 3 4 5' - }.transform_keys(&:to_sym) - ) - end - - it "renders gitlab.yml with user specified cron settings" do - config = gitlab_yml[:production][:cron_jobs][gitlab_yml_setting.to_sym][:cron] - expect(config).to eq '1 2 3 4 5' - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/extra_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/extra_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvZXh0cmFfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/extra_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'extra settings' do - context 'with default values' do - it 'renders gitlab.yml without extra settings' do - expect(gitlab_yml[:production][:extra]).to be nil - end - end - - context 'with user specified values' do - describe 'matomo settings' do - context 'with just matomo_url specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - extra_matomo_url: 'http://foobar.com' - } - ) - end - - it 'renders gitlab.yml with default values for other matomo settings' do - expect(gitlab_yml[:production][:extra][:matomo_url]).to eq('http://foobar.com') - expect(gitlab_yml[:production][:extra][:matomo_site_id]).to be nil - expect(gitlab_yml[:production][:extra][:matomo_disable_cookies]).to be nil - end - end - - context 'with all settings specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - extra_matomo_url: 'http://foobar.com', - extra_matomo_site_id: 'foobar', - extra_matomo_disable_cookies: true - } - ) - end - - it 'renders gitlab.yml with specified matomo settings' do - expect(gitlab_yml[:production][:extra][:matomo_url]).to eq('http://foobar.com') - expect(gitlab_yml[:production][:extra][:matomo_site_id]).to eq('foobar') - expect(gitlab_yml[:production][:extra][:matomo_disable_cookies]).to be true - end - end - end - - describe 'one_trust_id setting' do - before do - stub_gitlab_rb(gitlab_rails: { extra_one_trust_id: '0000-0000-test' }) - end - - it 'renders gitlab.yml with the provided value' do - expect(gitlab_yml[:production][:extra][:one_trust_id]).to eq('0000-0000-test') - end - end - - describe 'google_tag_manager_nonce_id setting' do - before do - stub_gitlab_rb(gitlab_rails: { extra_google_tag_manager_nonce_id: '0000-0000' }) - end - - it 'renders gitlab.yml with the provided value' do - expect(gitlab_yml[:production][:extra][:google_tag_manager_nonce_id]).to eq('0000-0000') - end - end - - context 'bizible' do - context 'when true' do - before do - stub_gitlab_rb( - gitlab_rails: { bizible: true } - ) - end - - it 'should set bizible to true' do - expect(chef_run).to create_templatesymlink('Create a gitlab.yml and create a symlink to Rails root').with_variables( - hash_including( - 'bizible' => true - ) - ) - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/forti_authenticator_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/forti_authenticator_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvZm9ydGlfYXV0aGVudGljYXRvcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/forti_authenticator_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - using RSpec::Parameterized::TableSyntax - include_context 'gitlab-rails' - - describe 'FortiAuthenticator settings' do - context 'with default values' do - it 'renders gitlab.yml with FortiAuthenticator disabled' do - expect(gitlab_yml[:production][:forti_authenticator]).to eq( - enabled: false - ) - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - gitlab_rails: { - forti_authenticator_enabled: true, - forti_authenticator_host: 'forti_authenticator.example.com', - forti_authenticator_port: 444, - forti_authenticator_username: 'janedoe', - forti_authenticator_access_token: '123s3cr3t456' - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expect(gitlab_yml[:production][:forti_authenticator]).to eq( - enabled: true, - host: 'forti_authenticator.example.com', - port: 444, - username: 'janedoe', - access_token: '123s3cr3t456' - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/forti_token_cloud_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/forti_token_cloud_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvZm9ydGlfdG9rZW5fY2xvdWRfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/forti_token_cloud_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - using RSpec::Parameterized::TableSyntax - include_context 'gitlab-rails' - - describe 'FortiToken Cloud settings' do - context 'with default values' do - it 'renders gitlab.yml with FortiAuthenticator disabled' do - expect(gitlab_yml[:production][:forti_token_cloud]).to eq( - enabled: false - ) - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - gitlab_rails: { - forti_token_cloud_enabled: true, - forti_token_cloud_client_id: 'forti_token_cloud_client_id', - forti_token_cloud_client_secret: '123s3cr3t456' - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expect(gitlab_yml[:production][:forti_token_cloud]).to eq( - enabled: true, - client_id: 'forti_token_cloud_client_id', - client_secret: '123s3cr3t456' - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/geo_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/geo_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvZ2VvX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/geo_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'Geo settings' do - context 'with default configuration' do - it 'renders gitlab.yml with default values' do - expect(gitlab_yml[:production][:geo]).to eq( - node_name: nil, - registry_replication: { - enabled: nil, - primary_api_url: nil - } - ) - end - end - - context 'with user specified configuration' do - before do - stub_gitlab_rb( - gitlab_rails: { - geo_node_name: 'foobar', - geo_registry_replication_enabled: true, - geo_registry_replication_primary_api_url: 'https://example.com' - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expect(gitlab_yml[:production][:geo]).to eq( - node_name: 'foobar', - registry_replication: { - enabled: true, - primary_api_url: 'https://example.com' - } - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/gitaly_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/gitaly_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvZ2l0YWx5X3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/gitaly_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'Gitaly settings' do - context 'with default values' do - it 'renders gitlab.yml without Gitaly token set' do - expect(gitlab_yml[:production][:gitaly]).to eq( - client_path: "/opt/gitlab/embedded/bin", - token: "" - ) - end - end - - context 'with user specified token' do - before do - stub_gitlab_rb( - gitlab_rails: { - gitaly_token: 'token123456' - } - ) - end - - it 'renders gitlab.yml with user specified token' do - expect(gitlab_yml[:production][:gitaly]).to eq( - client_path: "/opt/gitlab/embedded/bin", - token: 'token123456' - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/gitlab_shell_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/gitlab_shell_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvZ2l0bGFiX3NoZWxsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/gitlab_shell_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'GitLab Shell settings' do - context 'with default values' do - it 'renders gitlab.yml with default values' do - expect(gitlab_yml[:production][:gitlab_shell]).to eq( - authorized_keys_file: '/var/opt/gitlab/.ssh/authorized_keys', - git_timeout: 10800, - hooks_path: '/opt/gitlab/embedded/service/gitlab-shell/hooks/', - path: '/opt/gitlab/embedded/service/gitlab-shell/', - receive_pack: nil, - ssh_port: nil, - upload_pack: nil - ) - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - gitlab_rails: { - gitlab_shell_git_timeout: 99999, - gitlab_shell_hooks_path: '/tmp/hooks', - gitlab_shell_path: '/tmp/shell', - gitlab_shell_upload_pack: true, - gitlab_shell_receive_pack: true, - gitlab_shell_ssh_port: 123, - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expect(gitlab_yml[:production][:gitlab_shell]).to eq( - authorized_keys_file: '/var/opt/gitlab/.ssh/authorized_keys', - git_timeout: 99999, - hooks_path: '/tmp/hooks', - path: '/tmp/shell', - receive_pack: true, - ssh_port: 123, - upload_pack: true - ) - end - - describe 'authorized_keys_file' do - context 'when explicitly configured' do - before do - stub_gitlab_rb( - gitlab_shell: { - auth_file: '/tmp/authorized_keys' - } - ) - end - - it 'renders gitlab.yml with user specified path' do - expect(gitlab_yml[:production][:gitlab_shell][:authorized_keys_file]).to eq('/tmp/authorized_keys') - end - end - - context 'when the user home directory is specified' do - before do - stub_gitlab_rb( - user: { - home: '/tmp/user' - } - ) - end - - it 'defaults to auth file within specified home directory' do - expect(gitlab_yml[:production][:gitlab_shell][:authorized_keys_file]).to eq('/tmp/user/.ssh/authorized_keys') - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/gitlab_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/gitlab_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvZ2l0bGFiX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/gitlab_spec.rb +++ /dev/null @@ -1,207 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'GitLab Application Settings' do - describe 'Content Security Policy settings' do - context 'with default configuration' do - it 'renders gitlab.yml without content security policy settings' do - expect(gitlab_yml[:production][:gitlab][:content_security_policy]).to be nil - end - end - - context 'with user specified configuration' do - context 'with content security policy disabled' do - before do - stub_gitlab_rb( - gitlab_rails: { - content_security_policy: { - enabled: false - } - } - ) - end - - it 'renders gitlab.yml with content security policy disabled' do - expect(gitlab_yml[:production][:gitlab][:content_security_policy]).to eq( - enabled: false - ) - end - end - - context 'for various content security policy related settings' do - before do - stub_gitlab_rb( - gitlab_rails: { - content_security_policy: { - enabled: true, - report_only: true, - directives: { - default_src: "'self'", - script_src: "'self' http://recaptcha.net", - worker_src: "'self'" - } - } - } - ) - end - - it 'renders gitlab.yml with user specified values for content security policy' do - expect(gitlab_yml[:production][:gitlab][:content_security_policy]).to eq( - enabled: true, - report_only: true, - directives: { - default_src: "'self'", - script_src: "'self' http://recaptcha.net", - worker_src: "'self'" - } - ) - end - end - end - end - - describe 'SMIME email settings' do - context 'with default configuration' do - it 'renders gitlab.yml with SMIME email settings disabled' do - expect(gitlab_yml[:production][:gitlab][:email_smime]).to eq( - enabled: false, - cert_file: '/etc/gitlab/ssl/gitlab_smime.crt', - key_file: '/etc/gitlab/ssl/gitlab_smime.key', - ca_certs_file: nil - ) - end - end - - context 'with user specified configuration' do - before do - stub_gitlab_rb( - gitlab_rails: { - gitlab_email_smime_enabled: true, - gitlab_email_smime_key_file: '/etc/gitlab/ssl/custom_gitlab_smime.key', - gitlab_email_smime_cert_file: '/etc/gitlab/ssl/custom_gitlab_smime.crt', - gitlab_email_smime_ca_certs_file: '/etc/gitlab/ssl/custom_gitlab_smime_cas.crt' - } - ) - end - - it 'renders gitlab.yml with user specified values for SMIME email settings' do - expect(gitlab_yml[:production][:gitlab][:email_smime]).to eq( - enabled: true, - cert_file: '/etc/gitlab/ssl/custom_gitlab_smime.crt', - key_file: '/etc/gitlab/ssl/custom_gitlab_smime.key', - ca_certs_file: '/etc/gitlab/ssl/custom_gitlab_smime_cas.crt' - ) - end - end - end - - describe 'Allowed hosts' do - context 'with default configuration' do - it 'does not render allowed_hosts in gitlab.yml' do - expect(gitlab_yml[:production][:gitlab][:allowed_hosts]).to be nil - end - end - - context 'with user specified configuration' do - before do - stub_gitlab_rb( - gitlab_rails: { - allowed_hosts: ['example.com', 'foobar.com'] - } - ) - end - - it 'renders specified allowed_hosts in gitlab.yml' do - expect(gitlab_yml[:production][:gitlab][:allowed_hosts]).to eq(['example.com', 'foobar.com']) - end - end - end - - describe 'Application settings cache expiry' do - context 'with default configuration' do - it 'renders gitlab.yml without application settings cache expiry' do - expect(gitlab_yml[:production][:gitlab][:application_settings_cache_seconds]).to be nil - end - end - - context 'with user specified configuration' do - before do - stub_gitlab_rb( - gitlab_rails: { - application_settings_cache_seconds: 30 - } - ) - end - - it 'renders gitlab.yml with user specified value for application settings cache expiry' do - expect(gitlab_yml[:production][:gitlab][:application_settings_cache_seconds]).to eq(30) - end - end - end - - describe 'Maximum Request Duration' do - context 'with default configuration' do - it 'renders gitlab.yml with default value for maximum request duration' do - expect(gitlab_yml[:production][:gitlab][:max_request_duration_seconds]).to eq(57) - end - - context 'with default configuration and Unicorn as webserver' do - before do - stub_gitlab_rb( - puma: { enable: false }, - unicorn: { enable: true } - ) - end - - it 'renders gitlab.yml with default value for maximum request duration' do - expect(gitlab_yml[:production][:gitlab][:max_request_duration_seconds]).to eq(57) - end - end - end - - context 'with user specified configuration' do - context 'for worker_timeout' do - using RSpec::Parameterized::TableSyntax - - where(:web_worker, :configured_timeout, :expected_duration) do - :unicorn | 30 | 29 - :unicorn | "30" | 29 - :puma | 120 | 114 - end - - with_them do - before do - stub_gitlab_rb( - unicorn: { enable: web_worker == :unicorn, worker_timeout: configured_timeout }, - puma: { enable: web_worker == :puma, worker_timeout: configured_timeout } - ) - end - - it 'renders gitlab.yml with maximum request duration computed from configured worker timeout' do - expect(gitlab_yml[:production][:gitlab][:max_request_duration_seconds]).to eq(expected_duration) - end - end - end - - context 'for max_request_duration_seconds' do - before do - stub_gitlab_rb( - puma: { - worker_timeout: 120 - }, - gitlab_rails: { - max_request_duration_seconds: 100 - } - ) - end - - it 'renders gitlab.yml with user specified value for maximum request duration' do - expect(gitlab_yml[:production][:gitlab][:max_request_duration_seconds]).to eq(100) - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/incoming_email_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/incoming_email_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvaW5jb21pbmdfZW1haWxfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/incoming_email_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'incoming email settings' do - describe 'log file location' do - context 'with default values' do - it 'renders gitlab.yml with default value for mailroom log file path' do - expect(gitlab_yml[:production][:incoming_email][:log_path]).to eq '/var/log/gitlab/mailroom/mail_room_json.log' - end - end - - context 'with custom log file path specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - incoming_email_log_file: '/my/custom/log/file' - } - ) - end - - it 'renders gitlab.yml with specified value for mailroom log file path' do - expect(gitlab_yml[:production][:incoming_email][:log_path]).to eq '/my/custom/log/file' - end - end - - context 'with custom log directory specified' do - before do - stub_gitlab_rb( - mailroom: { - log_directory: '/my/custom/directory' - } - ) - end - - it 'renders gitlab.yml with default log file name inside specified directory' do - expect(gitlab_yml[:production][:incoming_email][:log_path]).to eq '/my/custom/directory/mail_room_json.log' - end - end - - context 'with custom log file and directory specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - incoming_email_log_file: '/foobar.log' - }, - mailroom: { - log_directory: '/my/custom/directory' - } - ) - end - - it 'renders gitlab.yml with specified log file path irrespective of specified directory' do - expect(gitlab_yml[:production][:incoming_email][:log_path]).to eq '/foobar.log' - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/ldap_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/ldap_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvbGRhcF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/ldap_spec.rb +++ /dev/null @@ -1,152 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'LDAP server configuration' do - context 'with default configuration' do - it 'renders gitlab.yml with LDAP disabled' do - expect(gitlab_yml[:production][:ldap]).to eq( - enabled: false, - active_directory: nil, - admin_group: nil, - allow_username_or_email_login: nil, - base: nil, - bind_dn: nil, - group_base: nil, - host: nil, - lowercase_usernames: nil, - method: nil, - password: nil, - port: nil, - prevent_ldap_sign_in: false, - sync_ssh_keys: nil, - sync_time: nil, - uid: nil, - user_filter: nil - ) - end - end - - context 'with user specified configuration' do - context 'when using new syntax for LDAP servers' do - before do - stub_gitlab_rb( - gitlab_rails: { - ldap_enabled: true, - prevent_ldap_sign_in: false, - ldap_sync_time: 20, - ldap_servers: { - main: { - label: 'LDAP Primary', - host: 'primary.ldap', - port: 389, - uid: 'uid', - encryption: 'plain', - password: 's3cr3t', - base: 'dc=example,dc=com', - user_filter: '' - }, - secondary: { - label: 'LDAP Secondary', - host: 'secondary.ldap', - port: 389, - uid: 'uid', - encryption: 'plain', - bind_dn: 'dc=example,dc=com', - password: 's3cr3t', - smartcard_auth: 'required', - base: '', - user_filter: '', - } - } - }) - end - - it 'renders gitlab.yml with user specified values' do - expected_output = { - enabled: true, - prevent_ldap_sign_in: false, - sync_time: 20, - servers: { - main: { - label: 'LDAP Primary', - host: 'primary.ldap', - port: 389, - uid: 'uid', - encryption: 'plain', - password: 's3cr3t', - base: 'dc=example,dc=com', - user_filter: '' - }, - secondary: { - label: 'LDAP Secondary', - host: 'secondary.ldap', - port: 389, - uid: 'uid', - encryption: 'plain', - bind_dn: 'dc=example,dc=com', - password: 's3cr3t', - smartcard_auth: 'required', - base: '', - user_filter: '', - } - } - } - - expect(gitlab_yml[:production][:ldap]).to eq(expected_output) - end - end - - context 'when using old syntax with single LDAP server' do - before do - stub_gitlab_rb( - gitlab_rails: { - ldap_enabled: true, - prevent_ldap_sign_in: false, - ldap_host: 'primary.ldap', - ldap_port: 389, - ldap_uid: 'uid', - ldap_password: 's3cr3t', - ldap_base: 'dc=example,dc=com', - ldap_user_filter: '', - ldap_method: 'plain', - ldap_bind_dn: 'foobar', - ldap_active_directory: 'asdf', - ldap_allow_username_or_email_login: false, - ldap_lowercase_usernames: true, - ldap_group_base: 'dc-example.com', - ldap_admin_group: 'foo', - ldap_sync_ssh_keys: true, - ldap_sync_time: 10 - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expected_output = { - enabled: true, - prevent_ldap_sign_in: false, - sync_time: 10, - host: 'primary.ldap', - port: 389, - uid: 'uid', - password: 's3cr3t', - base: 'dc=example,dc=com', - user_filter: '', - method: 'plain', - bind_dn: 'foobar', - active_directory: 'asdf', - allow_username_or_email_login: false, - lowercase_usernames: true, - group_base: 'dc-example.com', - admin_group: 'foo', - sync_ssh_keys: 'true', - } - - expect(gitlab_yml[:production][:ldap]).to eq(expected_output) - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/mattermost_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/mattermost_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvbWF0dGVybW9zdF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/mattermost_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - using RSpec::Parameterized::TableSyntax - include_context 'gitlab-rails' - - describe 'Mattermost settings' do - context 'with default values' do - it 'renders gitlab.yml with Mattermost disabled' do - expect(gitlab_yml[:production][:mattermost]).to eq( - enabled: false, - host: nil - ) - end - end - - context 'with user specified values' do - context 'when mattermost_external_url is specified' do - before do - stub_gitlab_rb( - mattermost_external_url: 'http://mattermost.example.com' - ) - end - - it 'renders gitlab.yml with specified Mattermost settings' do - expect(gitlab_yml[:production][:mattermost]).to eq( - enabled: true, - host: 'http://mattermost.example.com' - ) - end - end - - context 'when mattermost is running on a different machine' do - before do - stub_gitlab_rb( - gitlab_rails: { - mattermost_host: 'http://mattermost.example.com' - } - ) - end - - it 'renders gitlab.yml with specified Mattermost settings' do - expect(gitlab_yml[:production][:mattermost]).to eq( - enabled: true, - host: 'http://mattermost.example.com' - ) - end - end - - context 'when both gitlab-rails.mattermost_host and mattermost_external_url is set' do - before do - stub_gitlab_rb( - mattermost_external_url: 'http://foobar.com', - gitlab_rails: { - mattermost_host: 'http://mattermost.example.com' - } - ) - end - - it 'mattermost_external_url is used' do - expect(gitlab_yml[:production][:mattermost][:host]).to eq('http://foobar.com') - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/monitoring_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/monitoring_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvbW9uaXRvcmluZ19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/monitoring_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'Monitoring settings' do - context 'with default configuration' do - it 'renders gitlab.yml with default values' do - expect(gitlab_yml[:production][:monitoring]).to eq( - ip_whitelist: %w[127.0.0.0/8 ::1/128], - sidekiq_exporter: { - enabled: true, - log_enabled: false, - port: 8082, - address: '127.0.0.1' - }, - sidekiq_health_checks: { - enabled: true, - log_enabled: false, - port: 8082, - address: '127.0.0.1' - }, - web_exporter: { - enabled: false, - port: 8083, - address: '127.0.0.1' - } - ) - end - end - - context 'with user specified configuration' do - before do - stub_gitlab_rb( - gitlab_rails: { - monitoring_whitelist: %w[1.0.0.0 2.0.0.0], - } - ) - end - - it 'renders gitlab.rb with user specified values' do - expect(gitlab_yml[:production][:monitoring][:ip_whitelist]).to eq(%w[1.0.0.0 2.0.0.0]) - end - - context 'when sidekiq exports logs' do - before do - stub_gitlab_rb( - sidekiq: { - metrics_enabled: false, - exporter_log_enabled: true, - listen_address: '1.2.3.4', - listen_port: 1234 - } - ) - end - - it 'renders gitlab.rb with user specified values' do - expect(gitlab_yml[:production][:monitoring][:sidekiq_exporter]).to eq( - enabled: false, - log_enabled: true, - port: 1234, - address: '1.2.3.4' - ) - end - end - - context 'when sidekiq exporter uses custom settings' do - let(:exporter_settings) do - { - sidekiq: { - metrics_enabled: false, - exporter_log_enabled: true, - listen_address: '1.2.2.2', - listen_port: 2222 - } - } - end - - before do - stub_gitlab_rb(exporter_settings.merge(health_checks_settings)) - end - - context 'when health-checks are not configured manually' do - let(:health_checks_settings) { {} } - - it 'defaults health-checks to exporter settings' do - expect(gitlab_yml[:production][:monitoring][:sidekiq_health_checks]).to eq( - enabled: false, - log_enabled: true, - port: 2222, - address: '1.2.2.2' - ) - end - end - - context 'when health-checks are configured manually' do - let(:health_checks_settings) do - { - sidekiq: { - health_checks_enabled: true, - health_checks_log_enabled: true, - health_checks_listen_address: '1.2.3.5', - health_checks_listen_port: 1235 - } - } - end - - it 'uses custom health-checks settings' do - expect(gitlab_yml[:production][:monitoring][:sidekiq_health_checks]).to eq( - enabled: true, - log_enabled: true, - port: 1235, - address: '1.2.3.5' - ) - end - end - end - - context 'when webserver exports are enabled' do - context 'with Puma' do - before do - stub_gitlab_rb( - puma: { - enable: true, - exporter_enabled: false, - exporter_address: '1.2.3.4', - exporter_port: 1234 - } - ) - end - - it 'renders gitlab.rb with user specified values' do - expect(gitlab_yml[:production][:monitoring][:web_exporter]).to eq( - enabled: false, - port: 1234, - address: '1.2.3.4' - ) - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/object_storage_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/object_storage_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvb2JqZWN0X3N0b3JhZ2Vfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/object_storage_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'object storage settings' do - include_context 'object storage config' - - describe 'consolidated object storage settings' do - context 'with default values' do - it 'renders gitlab.yml without consolidated object storage settings' do - expect(gitlab_yml[:production][:object_store]).to be_nil - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - gitlab_rails: { - object_store: { - enabled: true, - connection: aws_connection_hash, - storage_options: aws_storage_options_hash, - objects: object_config, - proxy_download: true, - } - } - ) - end - - it 'generates gitlab.yml properly with specified values' do - expect(gitlab_yml[:production][:object_store]).to eq( - enabled: true, - connection: aws_connection_data, - storage_options: aws_storage_options, - objects: object_config, - proxy_download: true - ) - end - end - end - - describe 'individual object storage settings' do - # Parameters are: - # 1. Component name - # 2. Default settings deviating from general pattern - # 3. Whether Workhorse acceleration is in place - decides whether to - # include background_upload, direct_upload, proxy_download etc. - include_examples 'renders object storage settings in gitlab.yml', 'artifacts' - include_examples 'renders object storage settings in gitlab.yml', 'uploads' - include_examples 'renders object storage settings in gitlab.yml', 'external_diffs', { remote_directory: 'external-diffs' } - include_examples 'renders object storage settings in gitlab.yml', 'lfs', { remote_directory: 'lfs-objects' } - include_examples 'renders object storage settings in gitlab.yml', 'packages' - include_examples 'renders object storage settings in gitlab.yml', 'dependency_proxy' - include_examples 'renders object storage settings in gitlab.yml', 'terraform_state', { remote_directory: 'terraform' }, false - include_examples 'renders object storage settings in gitlab.yml', 'ci_secure_files', { remote_directory: 'ci-secure-files' }, false - include_examples 'renders object storage settings in gitlab.yml', 'pages', {}, false - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/omniauth_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/omniauth_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvb21uaWF1dGhfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/omniauth_spec.rb +++ /dev/null @@ -1,101 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - using RSpec::Parameterized::TableSyntax - include_context 'gitlab-rails' - - describe 'omniauth settings' do - context 'with default values' do - it 'renders gitlab.yml with omniauth allow_single_sign_on set to saml' do - expect(gitlab_yml[:production][:omniauth][:allow_single_sign_on]).to eq(['saml']) - end - - it 'renders gitlab.yml with other omniauth settings set to nil' do - expect(gitlab_yml[:production][:omniauth]).to eq( - allow_bypass_two_factor: nil, - auto_link_ldap_user: nil, - auto_link_saml_user: nil, - auto_link_user: nil, - allow_single_sign_on: ['saml'], - auto_sign_in_with_provider: nil, - block_auto_created_users: nil, - enabled: nil, - external_providers: nil, - providers: nil - ) - end - end - - context 'with user specified values' do - describe 'saml_message_max_byte_size' do - before do - stub_gitlab_rb( - gitlab_rails: { - omniauth_saml_message_max_byte_size: 100000 - } - ) - end - - it 'renders gitlab.yml with custom value' do - expect(gitlab_yml[:production][:omniauth][:saml_message_max_byte_size]).to eq(100000) - end - end - - describe 'settings that take in boolean values' do - where(:gitlab_yml_setting, :gitlab_rb_setting) do - 'enabled' | 'omniauth_enabled' - 'block_auto_created_users' | 'omniauth_block_auto_created_users' - 'auto_link_ldap_user' | 'omniauth_auto_link_ldap_user' - 'auto_link_saml_user' | 'omniauth_auto_link_saml_user' - end - - with_them do - context "with #{params[:gitlab_rb_setting]} set to true" do - before do - stub_gitlab_rb( - gitlab_rails: { - gitlab_rb_setting => true - }.transform_keys(&:to_sym) - ) - end - - it "renders gitlab.yml with #{params[:gitlab_yml_setting]} set to true" do - expect(gitlab_yml[:production][:omniauth][gitlab_yml_setting.to_sym]).to be true - end - end - end - end - - describe 'settings that take in multiple types of values' do - where(:gitlab_yml_setting, :gitlab_rb_setting) do - 'allow_single_sign_on' | 'omniauth_allow_single_sign_on' - 'sync_email_from_provider' | 'omniauth_sync_email_from_provider' - 'sync_profile_from_provider' | 'omniauth_sync_profile_from_provider' - 'allow_bypass_two_factor' | 'omniauth_allow_bypass_two_factor' - 'sync_profile_attributes' | 'omniauth_sync_profile_attributes' - 'auto_link_user' | 'omniauth_auto_link_user' - end - - with_them do - # Testing scenarios where settings are set to boolean true, string, - # and array of strings. - [true, 'foo', ['bar', 'tar']].each do |value| - context "with #{params[:gitlab_rb_setting]} set to #{value}" do - before(:each) do - stub_gitlab_rb( - gitlab_rails: { - gitlab_rb_setting => value - }.transform_keys(&:to_sym) - ) - end - - it "renders gitlab.yml with #{params[:gitlab_yml_setting]} set to #{value}" do - expect(gitlab_yml[:production][:omniauth][gitlab_yml_setting.to_sym]).to eq value - end - end - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/pages_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/pages_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvcGFnZXNfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/pages_spec.rb +++ /dev/null @@ -1,208 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - using RSpec::Parameterized::TableSyntax - include_context 'gitlab-rails' - include_context 'object storage config' - - let(:aws_connection_data) { JSON.parse(aws_connection_hash.to_json, symbolize_names: true) } - - describe 'Pages settings' do - context 'with default values' do - it 'renders gitlab.yml with Pages disabled' do - expect(gitlab_yml[:production][:pages]).to eq( - enabled: false, - access_control: false, - artifacts_server: true, - external_http: false, - external_https: false, - host: nil, - https: false, - object_store: { - connection: {}, - enabled: false, - remote_directory: "pages" - }, - local_store: { - enabled: true, - path: "/var/opt/gitlab/gitlab-rails/shared/pages", - }, - path: "/var/opt/gitlab/gitlab-rails/shared/pages", - port: nil - ) - end - end - - context 'with user specified values' do - context 'when Pages deployed along with GitLab' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_pages: { - access_control: true, - artifacts_server: false, - external_http: ['1.2.3.4'] - }, - gitlab_rails: { - pages_path: '/random/path', - pages_object_store_enabled: true, - pages_object_store_remote_directory: 'foobar', - pages_object_store_connection: aws_connection_hash - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expect(gitlab_yml[:production][:pages]).to eq( - { - enabled: true, - access_control: true, - path: '/random/path', - host: 'pages.example.com', - port: 443, - https: true, - external_http: true, - external_https: false, - artifacts_server: false, - object_store: { - enabled: true, - remote_directory: 'foobar', - connection: aws_connection_data - }, - local_store: { - enabled: true, - path: '/random/path' - } - } - ) - end - end - - context 'when Pages deployed external to GitLab' do - before do - stub_gitlab_rb( - gitlab_rails: { - pages_enabled: true, - pages_path: '/random/path', - pages_host: 'pages.example.com', - pages_port: 443, - pages_https: true, - pages_object_store_enabled: true, - pages_object_store_remote_directory: 'foobar', - pages_object_store_connection: aws_connection_hash - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expect(gitlab_yml[:production][:pages]).to eq( - { - enabled: true, - access_control: false, - path: '/random/path', - host: 'pages.example.com', - port: 443, - https: true, - external_http: false, - external_https: false, - artifacts_server: true, - object_store: { - enabled: true, - remote_directory: 'foobar', - connection: aws_connection_data - }, - local_store: { - enabled: true, - path: '/random/path' - } - } - ) - end - end - - describe 'for external HTTPS settings' do - context 'when external_https is used' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_pages: { - external_https: ['1.2.3.4'] - } - ) - end - - it 'renders gitlab.yml with pages.external_https set to true' do - expect(gitlab_yml[:production][:pages][:external_https]).to be true - end - end - - context 'when external_https_proxyv2 is used' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_pages: { - external_https_proxyv2: ['1.2.3.4'] - } - ) - end - - it 'renders gitlab.yml with pages.external_https set to true' do - expect(gitlab_yml[:production][:pages][:external_https]).to be true - end - end - end - - describe 'Pages local store settings' do - context 'when neither Pages path nor local store path is specified' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com' - ) - end - - it 'renders gitlab.yml with Pages local store path set to default Pages path' do - expect(gitlab_yml[:production][:pages][:local_store][:path]).to eq('/var/opt/gitlab/gitlab-rails/shared/pages') - end - end - - context 'when Pages path is specified but not local store path' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_rails: { - pages_local_store_enabled: true, - pages_path: '/tmp/test' - } - ) - end - - it 'renders gitlab.yml with Pages local store path set to Pages path' do - expect(gitlab_yml[:production][:pages][:local_store][:path]).to eq('/tmp/test') - end - end - - context 'when Pages local store path is different than Pages path' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - pages_external_url: 'https://pages.example.com', - gitlab_rails: { - pages_path: '/tmp/test', - pages_local_store_path: '/another/path' - } - ) - end - - it 'renders gitlab.yml with specified Pages local store path' do - expect(gitlab_yml[:production][:pages][:local_store][:path]).to eq('/another/path') - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/prometheus_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/prometheus_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvcHJvbWV0aGV1c19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/prometheus_spec.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'Prometheus settings' do - context 'with default configuration' do - it 'renders gitlab.yml with bundled Prometheus default values' do - expect(gitlab_yml[:production][:prometheus]).to eq( - enabled: true, - server_address: 'localhost:9090' - ) - end - end - - context 'with user specified configuation' do - context 'when bundled Prometheus is running on non-default address' do - before do - stub_gitlab_rb( - prometheus: { - listen_address: '0.0.0.0:9191' - } - ) - end - - it 'renders gitlab.yml with correct values' do - expect(gitlab_yml[:production][:prometheus]).to eq( - enabled: true, - server_address: '0.0.0.0:9191' - ) - end - end - - context 'when bundled Prometheus is running on non-default address with only port specified' do - before do - stub_gitlab_rb( - prometheus: { - listen_address: ':8080' - } - ) - end - - it 'renders gitlab.yml with server address rendered as a string' do - expect(gitlab_yml[:production][:prometheus]).to eq( - enabled: true, - server_address: ':8080' - ) - end - end - - context 'when configured with an external and internal Prometheus addresses' do - before do - stub_gitlab_rb( - prometheus: { - listen_address: '0.0.0.0:9191' - }, - gitlab_rails: { - prometheus_address: '1.1.1.1:2222' - } - ) - end - - it 'renders gitlab.yml with the external address' do - expect(gitlab_yml[:production][:prometheus]).to eq( - enabled: true, - server_address: '1.1.1.1:2222' - ) - end - end - - context 'when bundled Prometheus is disabled and no external Prometheus address is specified' do - before do - stub_gitlab_rb( - prometheus: { - enable: false - } - ) - end - - it 'renders gitlab.yml with Prometheus disabled' do - expect(gitlab_yml[:production][:prometheus][:enabled]).to be false - end - end - - context 'when bundled Prometheus is disabled but an external Prometheus address is specified' do - before do - stub_gitlab_rb( - prometheus: { - enable: false, - }, - gitlab_rails: { - prometheus_address: '1.2.3.4:2222' - } - ) - end - - it 'renders gitlab.yml with external Prometheus addreess' do - expect(gitlab_yml[:production][:prometheus]).to eq( - enabled: true, - server_address: '1.2.3.4:2222' - ) - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/pseudonymizer_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/pseudonymizer_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvcHNldWRvbnltaXplcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/pseudonymizer_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - include_context 'object storage config' - - describe 'pseudonymizer settings' do - context 'with default values' do - it 'renders gitlab.yml with pseudonymizer disabled' do - config = gitlab_yml[:production][:pseudonymizer] - expect(config).to eq( - { - manifest: nil, - upload: { - connection: {}, - remote_directory: nil - } - } - ) - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - gitlab_rails: { - pseudonymizer_manifest: 'another/path/manifest.yml', - pseudonymizer_upload_remote_directory: 'gitlab-pseudo', - pseudonymizer_upload_connection: aws_connection_hash, - } - ) - end - it 'renders gitlab.yml with user specified values' do - config = gitlab_yml[:production][:pseudonymizer] - expect(config).to eq( - { - manifest: 'another/path/manifest.yml', - upload: { - connection: aws_connection_data, - remote_directory: 'gitlab-pseudo' - } - } - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/registry_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/registry_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvcmVnaXN0cnlfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/registry_spec.rb +++ /dev/null @@ -1,122 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'Registry settings' do - context 'with default configuration' do - context 'when GitLab is running under http' do - it 'renders gitlab.yml without Registry settings' do - expect(gitlab_yml[:production][:registry]).to eq( - enabled: false, - api_url: nil, - host: nil, - port: nil, - issuer: 'omnibus-gitlab-issuer', - key: '/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key', - notification_secret: nil, - path: nil - ) - end - end - - context 'when GitLab is running under HTTPS using Omnibus automated TLS certificates' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com' - ) - end - - it 'automatically enables Registry and renders gitlab.yml accordingly' do - expect(gitlab_yml[:production][:registry]).to eq( - enabled: true, - host: 'gitlab.example.com', - port: 5050, - api_url: 'http://localhost:5000', - issuer: 'omnibus-gitlab-issuer', - key: '/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key', - notification_secret: nil, - path: '/var/opt/gitlab/gitlab-rails/shared/registry' - ) - end - end - - context 'with user specified configuration' do - context 'when URL for bundled Registry is specified' do - before do - stub_gitlab_rb( - registry_external_url: 'http://registry.example.com' - ) - end - - it 'renders gitlab.yml with specified Registry URL and other default Registry settings' do - expect(gitlab_yml[:production][:registry]).to eq( - enabled: true, - host: 'registry.example.com', - port: nil, - api_url: 'http://localhost:5000', - issuer: 'omnibus-gitlab-issuer', - key: '/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key', - notification_secret: nil, - path: '/var/opt/gitlab/gitlab-rails/shared/registry' - ) - end - end - - context 'when other Registry configuration are specified' do - before do - stub_gitlab_rb( - registry_external_url: 'http://registry.example.com:1234', - registry: { - registry_http_addr: 'localhost:1111' - } - ) - end - - it 'renders gitlab.yml with specified Registry settings' do - expect(gitlab_yml[:production][:registry]).to eq( - enabled: true, - host: 'registry.example.com', - port: 1234, - api_url: 'http://localhost:1111', - issuer: 'omnibus-gitlab-issuer', - key: '/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key', - notification_secret: nil, - path: '/var/opt/gitlab/gitlab-rails/shared/registry' - ) - end - end - - context 'with external Registry' do - before do - stub_gitlab_rb( - gitlab_rails: { - registry_enabled: 'true', - registry_key_path: '/fake/path', - registry_host: 'registry.example.com', - registry_api_url: 'http://registry.example.com:1234', - registry_port: 1234, - registry_issuer: 'foobar', - registry_notification_secret: 'qwerty', - registry_path: '/tmp/registry' - } - ) - end - - it 'renders gitlab.yml with correct registry settings' do - expect(gitlab_yml[:production][:registry]).to eq( - enabled: true, - host: 'registry.example.com', - port: 1234, - api_url: 'http://registry.example.com:1234', - issuer: 'foobar', - key: '/fake/path', - notification_secret: 'qwerty', - path: '/tmp/registry' - ) - end - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/repositories_storages_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/repositories_storages_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvcmVwb3NpdG9yaWVzX3N0b3JhZ2VzX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/repositories_storages_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - using RSpec::Parameterized::TableSyntax - include_context 'gitlab-rails' - - describe 'repositories storages settings' do - context 'with default values' do - it 'renders gitlab.yml with default repository storage settings' do - expect(gitlab_yml[:production][:repositories][:storages]).to eq( - default: { - gitaly_address: 'unix:/var/opt/gitlab/gitaly/gitaly.socket', - path: '/var/opt/gitlab/git-data/repositories' - } - ) - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - git_data_dirs: { - second_storage: { - path: '/tmp/foobar', - gitaly_address: 'unix:/var/gitaly.socket' - } - } - ) - end - - it 'renders gitlab.yml with specified repository storage settings' do - expect(gitlab_yml[:production][:repositories][:storages]).to eq( - second_storage: { - gitaly_address: 'unix:/var/gitaly.socket', - path: '/tmp/foobar/repositories' - } - ) - end - - context 'when path is not provided for a storage' do - before do - stub_gitlab_rb( - git_data_dirs: { - default: { - gitaly_address: 'unix:/var/gitaly.socket' - } - } - ) - end - - it 'sets the default path' do - expect(gitlab_yml[:production][:repositories][:storages]).to eq( - default: { - gitaly_address: 'unix:/var/gitaly.socket', - path: '/var/opt/gitlab/git-data/repositories' - } - ) - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/sentry_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/sentry_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvc2VudHJ5X3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/sentry_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'sentry settings' do - context 'with default values' do - it 'renders gitlab.yml with sentry disabled' do - expect(gitlab_yml[:production][:sentry]).to eq( - { - enabled: false, - dsn: nil, - clientside_dsn: nil, - environment: nil - } - ) - end - end - - context 'when the user enables sentry' do - before do - stub_gitlab_rb( - gitlab_rails: { - sentry_enabled: true, - sentry_dsn: 'https://708cd0ca88972f04d5c836a395b8db63@example.com/76', - sentry_clientside_dsn: 'https://708cd0ca88972f04d5c836a395b8db63@example.com/77', - sentry_environment: 'testing' - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expect(gitlab_yml[:production][:sentry]).to eq( - { - enabled: true, - dsn: 'https://708cd0ca88972f04d5c836a395b8db63@example.com/76', - clientside_dsn: 'https://708cd0ca88972f04d5c836a395b8db63@example.com/77', - environment: 'testing' - } - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/service_desk_email_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/service_desk_email_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvc2VydmljZV9kZXNrX2VtYWlsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/service_desk_email_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'service desk email settings' do - describe 'log file location' do - context 'with default values' do - it 'renders gitlab.yml with default value for mailroom log file path' do - expect(gitlab_yml[:production][:service_desk_email][:log_path]).to eq '/var/log/gitlab/mailroom/mail_room_json.log' - end - end - - context 'with custom log file path specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - service_desk_email_log_file: '/my/custom/log/file' - } - ) - end - - it 'renders gitlab.yml with specified value for mailroom log file path' do - expect(gitlab_yml[:production][:service_desk_email][:log_path]).to eq '/my/custom/log/file' - end - end - - context 'with custom log directory specified' do - before do - stub_gitlab_rb( - mailroom: { - log_directory: '/my/custom/directory' - } - ) - end - - it 'renders gitlab.yml with default log file name inside specified directory' do - expect(gitlab_yml[:production][:service_desk_email][:log_path]).to eq '/my/custom/directory/mail_room_json.log' - end - end - - context 'with custom log file and directory specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - service_desk_email_log_file: '/foobar.log' - }, - mailroom: { - log_directory: '/my/custom/directory' - } - ) - end - - it 'renders gitlab.yml with specified log file path irrespective of specified directory' do - expect(gitlab_yml[:production][:service_desk_email][:log_path]).to eq '/foobar.log' - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/shutdown_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/shutdown_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvc2h1dGRvd25fc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/shutdown_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'shutdown settings' do - context 'with default values' do - it 'renders gitlab.yml with default value for blackout seconds' do - expect(gitlab_yml[:production][:shutdown][:blackout_seconds]).to eq(10) - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - gitlab_rails: { - shutdown_blackout_seconds: 30 - } - ) - end - - it 'renders gitlab.yml with specified value for blackout seconds' do - expect(gitlab_yml[:production][:shutdown][:blackout_seconds]).to eq(30) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/sidekiq_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/sidekiq_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvc2lkZWtpcV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/sidekiq_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'sidekiq settings' do - describe 'log_format' do - context 'with default values' do - it 'renders gitlab.yml with sidekiq log format set to json' do - expect(gitlab_yml[:production][:sidekiq][:log_format]).to eq('json') - end - end - - context 'with user specified value' do - before do - stub_gitlab_rb( - sidekiq: { - log_format: 'text' - } - ) - end - - it 'renders gitlab.yml with user specified value for sidekiq log format' do - expect(gitlab_yml[:production][:sidekiq][:log_format]).to eq('text') - end - end - end - - describe 'routing_rules' do - context 'with default values' do - it 'renders gitlab.yml without routing_rules' do - expect(gitlab_yml[:production][:sidekiq]).not_to include(:routing_rules) - end - end - - context 'with an empty array' do - before do - stub_gitlab_rb( - sidekiq: { - routing_rules: [] - } - ) - end - - it 'renders gitlab.yml without routing_rules' do - expect(gitlab_yml[:production][:sidekiq]).not_to include(:routing_rules) - end - end - - context 'with a valid routing rules list' do - before do - stub_gitlab_rb( - sidekiq: { - routing_rules: [ - ["resource_boundary=cpu", "cpu_boundary"], - ["feature_category=pages", nil], - ["feature_category=search", ''], - ["feature_category=memory|resource_boundary=memory", ''], - ["*", "default"] - ] - } - ) - end - - it 'renders gitlab.yml with user specified value for sidekiq routing rules' do - expect(gitlab_yml[:production][:sidekiq][:routing_rules]).to eq( - [ - ["resource_boundary=cpu", "cpu_boundary"], - ["feature_category=pages", nil], - ["feature_category=search", ""], - ["feature_category=memory|resource_boundary=memory", ""], - ["*", "default"] - ] - ) - end - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/smartcard_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/smartcard_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvc21hcnRjYXJkX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/smartcard_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - include_context 'gitlab-rails' - - describe 'Smartcard authentication settings' do - context 'with default configuration' do - it 'renders gitlab.yml with Smartcard authentication disabled' do - expect(gitlab_yml[:production][:smartcard]).to eq( - enabled: false, - ca_file: '/etc/gitlab/ssl/CA.pem', - client_certificate_required_host: nil, - client_certificate_required_port: 3444, - required_for_git_access: false, - san_extensions: false - ) - end - end - - context 'with user specified configuration' do - before do - stub_gitlab_rb( - gitlab_rails: { - smartcard_enabled: true, - smartcard_ca_file: '/foobar/CA.pem', - smartcard_client_certificate_required_host: 'smartcard.gitlab.example.com', - smartcard_client_certificate_required_port: 123, - smartcard_required_for_git_access: true, - smartcard_san_extensions: true - } - ) - end - - it 'renders gitlab.yml with user specified values for Smartcard authentication' do - expect(gitlab_yml[:production][:smartcard]).to eq( - enabled: true, - ca_file: '/foobar/CA.pem', - client_certificate_required_host: 'smartcard.gitlab.example.com', - client_certificate_required_port: 123, - required_for_git_access: true, - san_extensions: true - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails/gitlab_yml/unleash_spec.rb b/spec/chef/recipes/gitlab-rails/gitlab_yml/unleash_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzL2dpdGxhYl95bWwvdW5sZWFzaF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails/gitlab_yml/unleash_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - using RSpec::Parameterized::TableSyntax - include_context 'gitlab-rails' - - describe 'Unleash settings' do - context 'with default values' do - it 'renders gitlab.yml unleash disabled' do - expect(gitlab_yml[:production][:feature_flags][:unleash]).to eq( - enabled: false - ) - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - gitlab_rails: { - feature_flags_unleash_enabled: true, - feature_flags_unleash_url: 'foobar.com', - feature_flags_unleash_app_name: 'GitLab Production', - feature_flags_unleash_instance_id: 'foobar' - } - ) - end - - it 'renders gitlab.yml with user specified values' do - expect(gitlab_yml[:production][:feature_flags][:unleash]).to eq( - enabled: true, - url: 'foobar.com', - app_name: 'GitLab Production', - instance_id: 'foobar' - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-rails_spec.rb b/spec/chef/recipes/gitlab-rails_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXJhaWxzX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-rails_spec.rb +++ /dev/null @@ -1,1147 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-rails' do - using RSpec::Parameterized::TableSyntax - - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink runit_service)).converge('gitlab::default') } - let(:redis_instances) { %w(cache queues shared_state trace_chunks rate_limiting sessions) } - let(:config_dir) { '/var/opt/gitlab/gitlab-rails/etc/' } - let(:default_vars) do - { - 'HOME' => '/var/opt/gitlab', - 'RAILS_ENV' => 'production', - 'SIDEKIQ_MEMORY_KILLER_MAX_RSS' => '2000000', - 'BUNDLE_GEMFILE' => '/opt/gitlab/embedded/service/gitlab-rails/Gemfile', - 'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin', - 'ICU_DATA' => '/opt/gitlab/embedded/share/icu/current', - 'PYTHONPATH' => '/opt/gitlab/embedded/lib/python3.9/site-packages', - 'EXECJS_RUNTIME' => 'Disabled', - 'TZ' => ':/etc/localtime', - 'LD_PRELOAD' => '/opt/gitlab/embedded/lib/libjemalloc.so', - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - 'SSL_CERT_FILE' => '/opt/gitlab/embedded/ssl/cert.pem' - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(File).to receive(:symlink?).and_call_original - end - - context 'with defaults' do - it 'creates a default VERSION file and restarts services' do - expect(chef_run).to create_version_file('Create version file for Rails').with( - version_file_path: '/var/opt/gitlab/gitlab-rails/RUBY_VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/ruby --version' - ) - - dependent_services = [] - dependent_services.each do |svc| - expect(chef_run.version_file('Create version file for Rails')).to notify("runit_service[#{svc}]").to(:restart) - end - end - end - - context 'when manage-storage-directories is disabled' do - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb(gitlab_rails: { shared_path: '/tmp/shared', - uploads_directory: '/tmp/uploads', - uploads_storage_path: '/tmp/uploads_storage' }, - gitlab_ci: { builds_directory: '/tmp/builds' }, - git_data_dirs: { - "some_storage" => { - "path" => "/tmp/git-data" - } - }, - manage_storage_directories: { enable: false }) - end - - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it 'does not create the git-data directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/git-data') - end - - it 'does not create the repositories directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/git-data/repositories') - end - - it 'does not create the shared directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared') - end - - it 'does not create the artifacts directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/artifacts') - end - - it 'does not create the external-diffs directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/external-diffs') - end - - it 'does not create the lfs storage directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/lfs-objects') - end - - it 'does not create the packages storage directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/packages') - end - - it 'does not create the dependency_proxy storage directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/dependency_proxy') - end - - it 'does not create the terraform_state storage directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/terraform_state') - end - - it 'does not create the ci_secure_files storage directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/ci_secure_files') - end - - it 'does not create the GitLab pages directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/shared/pages') - end - - it 'does not create the uploads directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/uploads') - end - - it 'does not create the ci builds directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/builds') - end - - it 'does not create the uploads storage directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/uploads_storage') - end - end - - context 'when manage-storage-directories is enabled' do - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb(gitlab_rails: { shared_path: '/tmp/shared', - uploads_directory: '/tmp/uploads', - uploads_storage_path: '/tmp/uploads_storage' }, - gitlab_ci: { builds_directory: '/tmp/builds' }, - git_data_dirs: { - "some_storage" => { - "path" => "/tmp/git-data" - } - }) - end - - ChefSpec::SoloRunner.converge('gitlab::default') - end - - it 'creates the git-data directory' do - expect(chef_run).to create_storage_directory('/tmp/git-data').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the repositories directory' do - expect(chef_run).to create_storage_directory('/tmp/git-data/repositories').with(owner: 'git', group: 'git', mode: '2770') - end - - it 'creates the shared directory' do - expect(chef_run).to create_storage_directory('/tmp/shared').with(owner: 'git', group: 'gitlab-www', mode: '0751') - end - - it 'creates the artifacts directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/artifacts').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the external-diffs directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/external-diffs').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the lfs storage directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/lfs-objects').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the packages directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/packages').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the dependency_proxy directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/dependency_proxy').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the terraform_state directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/terraform_state').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the ci_secure_files directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/ci_secure_files').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the encrypted_settings directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/encrypted_settings').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the GitLab pages directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/pages').with(owner: 'git', group: 'gitlab-www', mode: '0750') - end - - it 'creates the shared tmp directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/tmp').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the shared cache directory' do - expect(chef_run).to create_storage_directory('/tmp/shared/cache').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the uploads directory' do - expect(chef_run).to create_storage_directory('/tmp/uploads').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the ci builds directory' do - expect(chef_run).to create_storage_directory('/tmp/builds').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the uploads storage directory' do - expect(chef_run).to create_storage_directory('/tmp/uploads_storage').with(owner: 'git', group: 'git', mode: '0700') - end - end - - context 'when uploads storage directory is not specified' do - cached(:chef_run) do - ChefSpec::SoloRunner.converge('gitlab::default') - end - - it 'does not create the uploads storage directory' do - expect(chef_run).not_to create_storage_directory('/opt/gitlab/embedded/service/gitlab-rails/public') - end - end - - context 'with redis settings' do - let(:config_file) { '/var/opt/gitlab/gitlab-rails/etc/resque.yml' } - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } - - context 'and default configuration' do - it 'creates the config file with the required redis settings' do - expect(chef_run).to create_templatesymlink('Create a resque.yml and create a symlink to Rails root').with_variables( - hash_including( - redis_url: URI('unix:/var/opt/gitlab/redis/redis.socket'), - redis_sentinels: [], - redis_enable_client: true - ) - ) - - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(url: unix:/var/opt/gitlab/redis/redis.socket$)) - expect(content).not_to match(/id:/) - } - end - - it 'creates cable.yml with the same settings' do - expect(chef_run).to create_templatesymlink('Create a cable.yml and create a symlink to Rails root').with_variables( - hash_including( - redis_url: URI('unix:/var/opt/gitlab/redis/redis.socket'), - redis_sentinels: [], - redis_enable_client: true - ) - ) - - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/cable.yml').with_content { |content| - expect(content).to match(%r(url: unix:/var/opt/gitlab/redis/redis.socket$)) - } - end - - it 'does not render the separate instance configurations' do - redis_instances.each do |instance| - expect(chef_run).not_to render_file("#{config_dir}redis.#{instance}.yml") - end - end - - it 'deletes the separate instance config files' do - redis_instances.each do |instance| - expect(chef_run).to delete_file("/opt/gitlab/embedded/service/gitlab-rails/config/redis.#{instance}.yml") - expect(chef_run).to delete_file("/var/opt/gitlab/gitlab-rails/etc/redis.#{instance}.yml") - end - end - end - - context 'and custom configuration' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_port: 8888, - redis_database: 2, - redis_password: 'mypass', - redis_enable_client: false - } - ) - end - - it 'creates the config file with custom host, port, password and database' do - expect(chef_run).to create_templatesymlink('Create a resque.yml and create a symlink to Rails root').with_variables( - hash_including( - redis_url: URI('redis://:mypass@redis.example.com:8888/2'), - redis_sentinels: [], - redis_enable_client: false - ) - ) - - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(url: redis://:mypass@redis.example.com:8888/2)) - expect(content).to match(/id:$/) - } - end - - it 'creates cable.yml with custom host, port, password and database' do - expect(chef_run).to create_templatesymlink('Create a cable.yml and create a symlink to Rails root').with_variables( - hash_including( - redis_url: URI('redis://:mypass@redis.example.com:8888/2'), - redis_sentinels: [], - redis_enable_client: false - ) - ) - - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(url: redis://:mypass@redis.example.com:8888/2)) - expect(content).to match(/id:$/) - } - end - end - - context 'with multiple instances' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_enable_client: false, - redis_cache_instance: "redis://:fakepass@fake.redis.cache.com:8888/2", - redis_cache_sentinels: [ - { host: 'cache', port: '1234' }, - { host: 'cache', port: '3456' } - ], - redis_queues_instance: "redis://:fakepass@fake.redis.queues.com:8888/2", - redis_queues_sentinels: [ - { host: 'queues', port: '1234' }, - { host: 'queues', port: '3456' } - ], - redis_shared_state_instance: "redis://:fakepass@fake.redis.shared_state.com:8888/2", - redis_shared_state_sentinels: [ - { host: 'shared_state', port: '1234' }, - { host: 'shared_state', port: '3456' } - ], - redis_trace_chunks_instance: "redis://:fakepass@fake.redis.trace_chunks.com:8888/2", - redis_trace_chunks_sentinels: [ - { host: 'trace_chunks', port: '1234' }, - { host: 'trace_chunks', port: '3456' } - ], - redis_rate_limiting_instance: "redis://:fakepass@fake.redis.rate_limiting.com:8888/2", - redis_rate_limiting_sentinels: [ - { host: 'rate_limiting', port: '1234' }, - { host: 'rate_limiting', port: '3456' } - ], - redis_sessions_instance: "redis://:fakepass@fake.redis.sessions.com:8888/2", - redis_sessions_sentinels: [ - { host: 'sessions', port: '1234' }, - { host: 'sessions', port: '3456' } - ], - redis_actioncable_instance: "redis://:fakepass@fake.redis.actioncable.com:8888/2", - redis_actioncable_sentinels: [ - { host: 'actioncable', port: '1234' }, - { host: 'actioncable', port: '3456' } - ] - } - ) - end - - it 'render separate config files' do - redis_instances.each do |instance| - expect(chef_run).to create_templatesymlink("Create a redis.#{instance}.yml and create a symlink to Rails root").with_variables( - redis_url: "redis://:fakepass@fake.redis.#{instance}.com:8888/2", - redis_sentinels: [{ "host" => instance, "port" => "1234" }, { "host" => instance, "port" => "3456" }], - redis_enable_client: false - ) - expect(chef_run).not_to delete_file("/var/opt/gitlab/gitlab-rails/etc/redis.#{instance}.yml") - end - end - - it 'still renders the default configuration file' do - expect(chef_run).to create_templatesymlink('Create a resque.yml and create a symlink to Rails root') - end - - it 'creates cable.yml with custom settings' do - expect(chef_run).to create_templatesymlink('Create a cable.yml and create a symlink to Rails root').with_variables( - hash_including( - redis_url: "redis://:fakepass@fake.redis.actioncable.com:8888/2", - redis_sentinels: [{ 'host' => 'actioncable', 'port' => '1234' }, { 'host' => 'actioncable', 'port' => '3456' }], - redis_enable_client: false - ) - ) - end - end - end - - describe 'gitlab.yml' do - gitlab_yml_path = '/var/opt/gitlab/gitlab-rails/etc/gitlab.yml' - let(:gitlab_yml) { chef_run.template(gitlab_yml_path) } - let(:gitlab_yml_templatesymlink) { chef_run.templatesymlink('Create a gitlab.yml and create a symlink to Rails root') } - - # NOTE: Test if we pass proper notifications to other resources - describe 'rails cache management' do - before do - stub_default_not_listening?(false) - end - - context 'with default values' do - it 'should notify rails cache clear resource' do - expect(gitlab_yml_templatesymlink).to notify('execute[clear the gitlab-rails cache]') - end - end - - context 'with rake_cache_clear set to false' do - before do - stub_gitlab_rb(gitlab_rails: { rake_cache_clear: false }) - end - - it 'should notify rails cache clear resource' do - expect(gitlab_yml_templatesymlink).to notify( - 'execute[clear the gitlab-rails cache]') - end - - it 'should not run cache clear' do - expect(chef_run).not_to run_execute( - 'clear the gitlab-rails cache') - end - end - end - end - - context 'with environment variables' do - context 'by default' do - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables(default_vars) - end - - context 'when a custom env variable is specified' do - before do - stub_gitlab_rb(gitlab_rails: { env: { 'IAM' => 'CUSTOMVAR' } }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables( - default_vars.merge( - { - 'IAM' => 'CUSTOMVAR' - } - ) - ) - end - end - end - - context 'when relative URL is enabled' do - before do - stub_gitlab_rb(gitlab_rails: { gitlab_relative_url: '/gitlab' }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables( - default_vars.merge( - { - 'RAILS_RELATIVE_URL_ROOT' => '/gitlab' - } - ) - ) - end - end - - context 'when relative URL is specified in external_url' do - before do - stub_gitlab_rb(external_url: 'http://localhost/gitlab') - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables( - default_vars.merge( - { - 'RAILS_RELATIVE_URL_ROOT' => '/gitlab' - } - ) - ) - end - end - - context 'when jemalloc is disabled' do - before do - stub_gitlab_rb(gitlab_rails: { enable_jemalloc: false }) - end - - it 'creates necessary env variable files' do - vars = default_vars.dup - vars.delete("LD_PRELOAD") - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-rails/env').with_variables(vars) - end - end - end - - describe "with symlinked templates" do - let(:chef_run) { ChefSpec::SoloRunner.new.converge('gitlab::default') } - - before do - %w( - alertmanager - gitlab-exporter - gitlab-pages - gitlab-kas - gitlab-workhorse - logrotate - nginx - node-exporter - postgres-exporter - postgresql - prometheus - redis - redis-exporter - sidekiq - puma - gitaly - ).map { |svc| stub_should_notify?(svc, true) } - end - - describe 'database.yml' do - database_yml_path = '/var/opt/gitlab/gitlab-rails/etc/database.yml' - let(:database_yml) { chef_run.template(database_yml_path) } - let(:database_yml_content) { ChefSpec::Renderer.new(chef_run, database_yml).content } - let(:generated_yml_content) { YAML.safe_load(database_yml_content) } - - let(:config_file) { database_yml_path } - let(:templatesymlink) { chef_run.templatesymlink('Create a database.yml and create a symlink to Rails root') } - - context 'by default' do - cached(:chef_run) do - ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') - end - - it 'creates the template' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_host' => '/var/opt/gitlab/postgresql', - 'db_database' => 'gitlabhq_production', - 'db_load_balancing' => { 'hosts' => [] }, - 'db_prepared_statements' => false, - 'db_sslcompression' => 0, - 'db_sslcert' => nil, - 'db_sslkey' => nil, - 'db_application_name' => nil - ) - ) - end - - it 'template triggers notifications' do - expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed - expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed - expect(templatesymlink).not_to notify('runit_service[gitlab-workhorse]').to(:restart).delayed - expect(templatesymlink).not_to notify('runit_service[nginx]').to(:restart).delayed - end - - it 'renders expected YAML' do - expect(generated_yml_content.dig('production', 'main', 'adapter')).to eq('postgresql') - expect(generated_yml_content.dig('production', 'main', 'host')).to eq('/var/opt/gitlab/postgresql') - expect(generated_yml_content.dig('production', 'main', 'port')).to eq(5432) - expect(generated_yml_content.dig('production', 'main', 'application_name')).to eq(nil) - end - end - - context 'with specific database settings' do - context 'with an application name set' do - where(:appname, :expected) do - '' | '' - 'test' | 'test' - end - - with_them do - cached(:chef_run) do - ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') - end - - before do - stub_gitlab_rb( - 'gitlab_rails' => { 'db_application_name' => appname } - ) - end - - it 'renders expected YAML' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_application_name' => appname - ) - ) - - expect(generated_yml_content.dig('production', 'main').keys).to include(*%w(adapter host port application_name)) - expect(generated_yml_content.dig('production', 'main', 'application_name')).to eq(expected) - end - end - end - - context 'when multiple postgresql listen_address is used' do - before do - stub_gitlab_rb(postgresql: { listen_address: "127.0.0.1,1.1.1.1" }) - end - - it 'creates the postgres configuration file with multi listen_address and database.yml file with one host' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_host' => '127.0.0.1' - ) - ) - end - end - - context 'when no postgresql listen_address is used' do - it 'creates the postgres configuration file with empty listen_address and database.yml file with default one' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_host' => '/var/opt/gitlab/postgresql' - ) - ) - end - end - - context 'when one postgresql listen_address is used' do - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb(postgresql: { listen_address: "127.0.0.1" }) - end - - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it 'creates the postgres configuration file with one listen_address and database.yml file with one host' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_host' => '127.0.0.1' - ) - ) - end - - it 'template triggers notifications' do - expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed - expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed - expect(templatesymlink).not_to notify('runit_service[gitlab-workhorse]').to(:restart).delayed - expect(templatesymlink).not_to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'when load balancers are specified' do - before do - stub_gitlab_rb(gitlab_rails: { db_load_balancing: { 'hosts' => ['primary.example.com', 'secondary.example.com'] } }) - end - - it 'uses provided value in database.yml' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_load_balancing' => { 'hosts' => ['primary.example.com', 'secondary.example.com'] } - ) - ) - end - end - - context 'when prepared_statements are disabled' do - before do - stub_gitlab_rb(gitlab_rails: { db_prepared_statements: false }) - end - - it 'uses provided value in database.yml' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_prepared_statements' => false, - 'db_statements_limit' => 1000 - ) - ) - end - end - - context 'when limit for prepared_statements are specified' do - before do - stub_gitlab_rb(gitlab_rails: { db_statements_limit: 12345 }) - end - - it 'uses provided value in database.yml' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_prepared_statements' => false, - 'db_statements_limit' => 12345 - ) - ) - end - end - - context 'when SSL compression is enabled' do - before do - stub_gitlab_rb(gitlab_rails: { db_sslcompression: 1 }) - end - - it 'uses provided value in database.yml' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_sslcompression' => 1 - ) - ) - end - end - - context 'when SSL certificate and key for DB is specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - db_sslcert: '/etc/certs/db.cer', - db_sslkey: '/etc/certs/db.key' - } - ) - end - - it 'uses specified value in database.yml' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_sslcert' => '/etc/certs/db.cer', - 'db_sslkey' => '/etc/certs/db.key' - ) - ) - end - end - end - - describe 'client side statement_timeout' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } - - context 'default values' do - it 'does not set a default value' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_statement_timeout' => nil - ) - ) - - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(statement_timeout: $)) - } - end - end - - context 'custom value' do - before do - stub_gitlab_rb( - 'postgresql' => { 'statement_timeout' => '65000' }, - 'gitlab_rails' => { 'db_statement_timeout' => '70000' } - ) - end - - it 'uses specified client side statement_timeout value' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - 'db_statement_timeout' => '70000' - ) - ) - - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(statement_timeout: 70000$)) - } - end - end - end - - context 'adjusting database adapter connection parameters' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } - - using RSpec::Parameterized::TableSyntax - - where(:rb_param, :yaml_param, :default_value, :custom_value) do - 'db_connect_timeout' | 'connect_timeout' | nil | 5 - 'db_keepalives' | 'keepalives' | nil | 1 - 'db_keepalives_idle' | 'keepalives_idle' | nil | 5 - 'db_keepalives_interval' | 'keepalives_interval' | nil | 3 - 'db_keepalives_count' | 'keepalives_count' | nil | 3 - 'db_tcp_user_timeout' | 'tcp_user_timeout' | nil | 13000 - end - - with_them do - context 'default values' do - it 'does not set a default value' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - rb_param => default_value - ) - ) - - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(#{yaml_param}: $)) - } - end - end - - context 'custom connection parameter value' do - before do - stub_gitlab_rb( - 'gitlab_rails' => { rb_param => custom_value } - ) - end - - it 'uses specified connection parameter value' do - expect(chef_run).to create_templatesymlink('Create a database.yml and create a symlink to Rails root').with_variables( - hash_including( - rb_param => custom_value - ) - ) - - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(#{yaml_param}: #{custom_value}$)) - } - end - end - end - end - end - - describe 'gitlab_workhorse_secret' do - let(:templatesymlink) { chef_run.templatesymlink('Create a gitlab_workhorse_secret and create a symlink to Rails root') } - - context 'by default' do - cached(:chef_run) do - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it 'creates the template' do - expect(chef_run).to create_templatesymlink("Create a gitlab_workhorse_secret and create a symlink to Rails root").with( - owner: 'root', - group: 'root', - mode: '0644' - ) - end - - it 'template triggers notifications' do - expect(templatesymlink).to notify('runit_service[gitlab-workhorse]').to(:restart).delayed - expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed - expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed - end - end - - context 'with specific gitlab_workhorse_secret' do - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb(gitlab_workhorse: { secret_token: 'abc123-gitlab-workhorse' }) - end - - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it 'renders the correct node attribute' do - expect(chef_run).to create_templatesymlink("Create a gitlab_workhorse_secret and create a symlink to Rails root").with_variables( - secret_token: 'abc123-gitlab-workhorse' - ) - end - - it 'uses the correct owner and permissions' do - expect(chef_run).to create_templatesymlink('Create a gitlab_workhorse_secret and create a symlink to Rails root').with( - owner: 'root', - group: 'root', - mode: '0644' - ) - end - - it 'template triggers notifications' do - expect(templatesymlink).to notify('runit_service[gitlab-workhorse]').to(:restart).delayed - expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed - expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed - end - end - end - - describe 'gitlab_pages_secret' do - let(:templatesymlink) { chef_run.templatesymlink('Create a gitlab_pages_secret and create a symlink to Rails root') } - - context 'with pages disabled' do - let(:api_secret_key) { SecureRandom.base64(32) } - - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb( - pages_enabled: false, - gitlab_pages: { api_secret_key: api_secret_key, enable: false }, - pages_external_url: 'http://pages.example.com' - ) - end - - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it 'creates the template' do - expect(chef_run).to create_templatesymlink("Create a gitlab_pages_secret and create a symlink to Rails root").with( - owner: 'root', - group: 'root', - mode: '0644' - ) - end - end - - context 'by default' do - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb( - external_url: 'http://gitlab.example.com', - pages_external_url: 'http://pages.example.com' - ) - end - - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it 'creates the template' do - expect(chef_run).to create_templatesymlink("Create a gitlab_pages_secret and create a symlink to Rails root").with( - owner: 'root', - group: 'root', - mode: '0644' - ) - end - - it 'template triggers notifications' do - expect(templatesymlink).to notify('runit_service[gitlab-pages]').to(:restart).delayed - expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed - expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed - end - end - - context 'with specific gitlab_pages_secret' do - let(:api_secret_key) { SecureRandom.base64(32) } - - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb( - gitlab_pages: { api_secret_key: api_secret_key }, - external_url: 'http://gitlab.example.com', - pages_external_url: 'http://pages.example.com' - ) - end - - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it 'renders the correct node attribute' do - expect(chef_run).to create_templatesymlink("Create a gitlab_pages_secret and create a symlink to Rails root").with_variables( - secret_token: api_secret_key - ) - end - - it 'uses the correct owner and permissions' do - expect(chef_run).to create_templatesymlink('Create a gitlab_pages_secret and create a symlink to Rails root').with( - owner: 'root', - group: 'root', - mode: '0644' - ) - end - - it 'template triggers notifications' do - expect(templatesymlink).to notify('runit_service[gitlab-pages]').to(:restart).delayed - expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed - expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed - end - end - end - - describe 'gitlab_kas_secret' do - let(:templatesymlink) { chef_run.templatesymlink('Create a gitlab_kas_secret and create a symlink to Rails root') } - - shared_examples 'creates the KAS template' do - it 'creates the template' do - expect(chef_run).to create_templatesymlink('Create a gitlab_kas_secret and create a symlink to Rails root').with( - owner: 'root', - group: 'root', - mode: '0644' - ) - end - end - - context 'with KAS disabled' do - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb( - gitlab_kas: { enable: false } - ) - end - - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it_behaves_like 'creates the KAS template' - end - - context 'with KAS enabled' do - cached(:chef_run) do - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it_behaves_like 'creates the KAS template' - - it 'template triggers notifications' do - expect(templatesymlink).to notify('runit_service[gitlab-kas]').to(:restart).delayed - expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed - expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed - end - end - - context 'with specific gitlab_kas_secret' do - let(:api_secret_key) { SecureRandom.base64(32) } - - cached(:chef_run) do - RSpec::Mocks.with_temporary_scope do - stub_gitlab_rb( - gitlab_kas: { api_secret_key: api_secret_key } - ) - end - - ChefSpec::SoloRunner.new.converge('gitlab::default') - end - - it 'renders the correct node attribute' do - expect(chef_run).to create_templatesymlink('Create a gitlab_kas_secret and create a symlink to Rails root').with_variables( - secret_token: api_secret_key - ) - end - - it_behaves_like 'creates the KAS template' - - it 'template triggers notifications' do - expect(templatesymlink).to notify('runit_service[gitlab-kas]').to(:restart).delayed - expect(templatesymlink).to notify('runit_service[puma]').to(:restart).delayed - expect(templatesymlink).to notify('sidekiq_service[sidekiq]').to(:restart).delayed - end - end - end - end - - describe 'GitLab Registry files' do - describe 'gitlab-registry.key file' do - context 'Registry is disabled' do - it 'does not generate gitlab-registry.key file' do - expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key") - end - end - - context 'Registry is enabled' do - context 'with default configuration' do - before do - stub_gitlab_rb( - gitlab_rails: { - registry_enabled: true - } - ) - end - - it 'generates key file in the default location' do - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key").with_content(/\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m) - end - end - - context 'with user specified configuration' do - context 'when location of key file is specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - registry_enabled: true, - registry_key_path: '/fake/path' - } - ) - end - - it 'generates key file in the specified location' do - expect(chef_run).to render_file("/fake/path").with_content(/\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m) - end - end - - context 'when key content is specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - registry_enabled: true - }, - registry: { - internal_key: 'foobar' - } - ) - end - - it 'generates key file with specified content' do - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key").with_content('foobar') - end - end - end - end - end - end - - context 'SMTP settings' do - context 'when connection pooling is not configured' do - it 'creates smtp_settings.rb with pooling disabled' do - stub_gitlab_rb( - gitlab_rails: { - smtp_enable: true - } - ) - - expect(chef_run).to create_templatesymlink('Create a smtp_settings.rb and create a symlink to Rails root').with_variables( - hash_including( - 'smtp_pool' => false - ) - ) - end - end - - context 'when connection pooling is enabled' do - it 'creates smtp_settings.rb with pooling enabled' do - stub_gitlab_rb( - gitlab_rails: { - smtp_enable: true, - smtp_pool: true - } - ) - - expect(chef_run).to create_templatesymlink('Create a smtp_settings.rb and create a symlink to Rails root').with_variables( - hash_including( - 'smtp_pool' => true - ) - ) - - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/smtp_settings.rb').with_content { |content| - expect(content).to include('ActionMailer::Base.delivery_method = :smtp_pool') - } - end - end - end - - describe 'logrotate settings' do - context 'default values' do - it_behaves_like 'configured logrotate service', 'gitlab-pages', 'git', 'git' - end - - context 'specified username and group' do - before do - stub_gitlab_rb( - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'configured logrotate service', 'gitlab-pages', 'foo', 'bar' - end - end - - describe 'cleaning up the legacy sidekiq log symlink' do - it 'removes the link if it existed' do - allow(File).to receive(:symlink?).with('/var/log/gitlab/gitlab-rails/sidekiq.log') { true } - - expect(chef_run).to delete_link('/var/log/gitlab/gitlab-rails/sidekiq.log') - end - - it 'does nothing if it did not exist' do - allow(File).to receive(:symlink?).with('/var/log/gitlab/gitlab-rails/sidekiq.log') { false } - - expect(chef_run).not_to delete_link('/var/log/gitlab/gitlab-rails/sidekiq.log') - end - end -end diff --git a/spec/chef/recipes/gitlab-shell_spec.rb b/spec/chef/recipes/gitlab-shell_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXNoZWxsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-shell_spec.rb +++ /dev/null @@ -1,265 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-shell' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'logrotate settings' do - context 'default values' do - it_behaves_like 'configured logrotate service', 'gitlab-shell', 'git', 'git' - end - - context 'specified username and group' do - before do - stub_gitlab_rb( - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'configured logrotate service', 'gitlab-shell', 'foo', 'bar' - end - end - - it 'defaults the auth_file to be within the user\'s home directory' do - stub_gitlab_rb(user: { home: '/tmp/user' }) - expect(chef_run.node['gitlab']['gitlab-shell']['auth_file']).to eq('/tmp/user/.ssh/authorized_keys') - end - - it 'uses custom auth_files set in gitlab.rb' do - stub_gitlab_rb(user: { home: '/tmp/user' }, gitlab_shell: { auth_file: '/tmp/authorized_keys' }) - expect(chef_run.node['gitlab']['gitlab-shell']['auth_file']).to eq('/tmp/authorized_keys') - end - - it 'creates authorized_keys file if missing' do - stub_gitlab_rb(gitlab_shell: { auth_file: '/tmp/authorized_keys' }) - expect(chef_run).to create_file_if_missing('/tmp/authorized_keys') - end - - context 'with default settings' do - it 'create config file in default location with default values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - log_file: '/var/log/gitlab/gitlab-shell/gitlab-shell.log', - log_format: "json", - migration: { enabled: true, features: [] }, - gitlab_url: 'http+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket', - gitlab_relative_path: '', - ssl_cert_dir: '/opt/gitlab/embedded/ssl/certs/' - ) - ) - end - end - - context 'with a non-default directory' do - before do - stub_gitlab_rb(gitlab_shell: { - dir: '/export/gitlab/gitlab-shell', - }) - end - - it 'create config file in specified location with default values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_link_to('/export/gitlab/gitlab-shell/config.yml') - end - end - - context 'with a non-default log directory' do - before do - stub_gitlab_rb(gitlab_shell: { - log_directory: '/tmp/log', - git_trace_log_file: '/tmp/log/gitlab-shell-git-trace.log' - }) - end - - it 'create config file with provided values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - log_file: '/tmp/log/gitlab-shell.log', - git_trace_log_file: '/tmp/log/gitlab-shell-git-trace.log' - ) - ) - end - end - - context 'when using the default auth_file location' do - before { stub_gitlab_rb(user: { home: '/tmp/user' }) } - - it 'creates the ssh dir in the user\'s home directory' do - expect(chef_run).to create_storage_directory('/tmp/user/.ssh').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the config file with the auth_file within user\'s ssh directory' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - authorized_keys: '/tmp/user/.ssh/authorized_keys' - ) - ) - end - end - - context 'when using a different location for auth_file' do - before { stub_gitlab_rb(user: { home: '/tmp/user' }, gitlab_shell: { auth_file: '/tmp/ssh/authorized_keys' }) } - - it 'creates the ssh dir in the user\'s home directory' do - expect(chef_run).to create_storage_directory('/tmp/user/.ssh').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the auth_file\'s parent directory' do - expect(chef_run).to create_storage_directory('/tmp/ssh').with(owner: 'git', group: 'git', mode: '0700') - end - - it 'creates the config file with the auth_file at the specified location' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - authorized_keys: '/tmp/ssh/authorized_keys' - ) - ) - end - end - - context 'when manage-storage-directories is disabled' do - before { stub_gitlab_rb(user: { home: '/tmp/user' }, manage_storage_directories: { enable: false }) } - - it 'doesn\'t create the ssh dir in the user\'s home directory' do - expect(chef_run).not_to run_ruby_block('directory resource: /tmp/user/.ssh') - end - end - - context 'with custom settings' do - before do - stub_gitlab_rb( - gitlab_shell: { - log_format: 'json', - migration: { - enabled: true, - features: ['discover'] - } - } - ) - end - - it 'creates the config file with custom values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - log_format: 'json', - migration: { - enabled: true, - features: ['discover'] - } - ) - ) - end - - context 'migration is disabled (set to false)' do - before do - stub_gitlab_rb( - gitlab_shell: { - migration: { - enabled: false, - features: [] - } - } - ) - end - - it 'creates the config file with migration disabled' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - migration: { - enabled: false, - features: [] - } - ) - ) - end - end - - context 'with a non-default workhorse unix socket' do - context 'without sockets_directory defined' do - before do - stub_gitlab_rb(gitlab_workhorse: { listen_addr: '/fake/workhorse/socket' }) - end - - it 'create config file with provided values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - gitlab_url: 'http+unix://%2Ffake%2Fworkhorse%2Fsocket', - gitlab_relative_path: '' - ) - ) - end - end - - context 'with sockets_directory defined' do - before do - stub_gitlab_rb(gitlab_workhorse: { 'sockets_directory': '/fake/workhorse/sockets/' }) - end - - it 'create config file with provided values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - gitlab_url: 'http+unix://%2Ffake%2Fworkhorse%2Fsockets%2Fsocket', - gitlab_relative_path: '' - ) - ) - end - end - end - - context 'with a tcp workhorse listener' do - before do - stub_gitlab_rb( - external_url: 'http://example.com/gitlab', - gitlab_workhorse: { - listen_network: 'tcp', - listen_addr: 'localhost:1234' - } - ) - end - - it 'create config file with provided values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - gitlab_url: 'http://localhost:1234/gitlab', - gitlab_relative_path: nil - ) - ) - end - end - - context 'with relative path in external_url' do - before do - stub_gitlab_rb(external_url: 'http://example.com/gitlab') - end - - it 'create config file with provided values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - gitlab_url: 'http+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket', - gitlab_relative_path: '/gitlab' - ) - ) - end - end - - context 'with internal_api_url specified' do - before do - stub_gitlab_rb(gitlab_rails: { internal_api_url: 'http://localhost:8080' }) - end - - it 'create config file with provided values' do - expect(chef_run).to create_templatesymlink('Create a config.yml and create a symlink to Rails root').with_variables( - hash_including( - gitlab_url: 'http://localhost:8080', - gitlab_relative_path: '' - ) - ) - end - end - end -end diff --git a/spec/chef/recipes/gitlab-workhorse_spec.rb b/spec/chef/recipes/gitlab-workhorse_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvZ2l0bGFiLXdvcmtob3JzZV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/gitlab-workhorse_spec.rb +++ /dev/null @@ -1,580 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-workhorse' do - let(:node_cpus) { 1 } - let(:chef_run) do - ChefSpec::SoloRunner.new(step_into: %w(runit_service)) do |node| - node.automatic['cpu']['total'] = node_cpus - end.converge('gitlab::default') - end - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - 'HOME' => '/var/opt/gitlab', - 'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin' - } - end - let(:config_file) { '/var/opt/gitlab/gitlab-workhorse/config.toml' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'by default' do - it 'creates a default VERSION file and restarts service' do - expect(chef_run).to create_version_file('Create version file for Workhorse').with( - version_file_path: '/var/opt/gitlab/gitlab-workhorse/VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/gitlab-workhorse --version' - ) - - expect(chef_run.version_file('Create version file for Workhorse')).to notify('runit_service[gitlab-workhorse]').to(:restart) - end - - it 'includes both authSocket and authBackend arguments' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| - expect(content).to match(%r(-authSocket /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket)) - expect(content).to match(%r(-authBackend http://localhost:8080)) - } - end - - it 'does not include alternate document root' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).not_to match(/alt_document_root/) - } - end - - it 'does not include shutdown timeout' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).not_to match(/shutdown_timeout/) - } - end - - it 'does not include object storage configs' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).not_to match(/object_storage/) - } - end - - it 'does not propagate correlation ID' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).not_to match(/propagateCorrelationID/) - } - end - end - - context 'user and group' do - context 'default values' do - it_behaves_like "enabled runit service", "gitlab-workhorse", "root", "root" - it_behaves_like 'configured logrotate service', 'gitlab-workhorse', 'git', 'git' - end - - context 'custom values' do - before do - stub_gitlab_rb( - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like "enabled runit service", "gitlab-workhorse", "root", "root" - it_behaves_like 'configured logrotate service', 'gitlab-workhorse', 'foo', 'bar' - end - end - - context 'with environment variables' do - context 'by default' do - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-workhorse/env').with_variables(default_vars) - end - - context 'when a custom env variable is specified' do - before do - stub_gitlab_rb(gitlab_workhorse: { env: { 'IAM' => 'CUSTOMVAR' } }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitlab-workhorse/env').with_variables( - default_vars.merge( - { - 'IAM' => 'CUSTOMVAR' - } - ) - ) - end - end - end - end - - context 'with alternate document root' do - before do - stub_gitlab_rb(gitlab_workhorse: { alt_document_root: '/tmp/test' }) - end - - it 'includes alternate document root setting' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(alt_document_root = "/tmp/test")) - } - end - end - - context 'with shutdown timeout' do - before do - stub_gitlab_rb(gitlab_workhorse: { shutdown_timeout: '60s' }) - end - - it 'includes alternate document root setting' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(%r(shutdown_timeout = "60s")) - } - end - end - - context 'auth_socket and auth_backend' do - context 'with only auth_socket specified' do - context "auth_socket set to legacy '' value" do - before do - stub_gitlab_rb(gitlab_workhorse: { auth_socket: "''" }) - end - - it 'includes both authSocket and authBackend arguments' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| - expect(content).to match(%r(-authSocket /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket)) - expect(content).to match(%r(-authBackend http://localhost:8080)) - } - end - end - end - - context 'with only auth_backend specified' do - before do - stub_gitlab_rb(gitlab_workhorse: { auth_backend: 'https://test.example.com:8080' }) - end - - it 'omits authSocket argument' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| - expect(content).not_to match(/\-authSocket/) - expect(content).to match(%r(-authBackend https://test.example.com:8080)) - } - end - end - - context "with nil auth_socket" do - before do - stub_gitlab_rb(gitlab_workhorse: { auth_socket: nil }) - end - - it 'includes both authSocket and authBackend arguments' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| - expect(content).to match(%r(-authSocket /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket)) - expect(content).to match(%r(-authBackend http://localhost:8080)) - } - end - - context 'with auth_backend specified' do - before do - stub_gitlab_rb(gitlab_workhorse: { auth_socket: nil, auth_backend: 'https://test.example.com:8080' }) - end - - it 'omits authSocket argument' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| - expect(content).not_to match(/\-authSocket/) - expect(content).to match(%r(-authBackend https://test.example.com:8080)) - } - end - end - end - - context 'with auth_backend and auth_socket set' do - before do - stub_gitlab_rb(gitlab_workhorse: { auth_socket: '/tmp/test.socket', auth_backend: 'https://test.example.com:8080' }) - end - - it 'includes both authSocket and authBackend arguments' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| - expect(content).to match(%r(-authSocket /tmp/test.socket)) - expect(content).to match(%r(-authBackend https://test.example.com:8080)) - } - end - end - end - - context 'consolidated object store settings' do - include_context 'object storage config' - - context 'with S3 config' do - before do - stub_gitlab_rb( - gitlab_rails: { - object_store: { - enabled: true, - connection: aws_connection_hash, - objects: object_config - } - } - ) - end - - it 'includes S3 credentials' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(/\[object_storage\]\n provider = "AWS"\n/m) - expect(content).to match(/\[object_storage.s3\]\n aws_access_key_id = "AKIAKIAKI"\n aws_secret_access_key = "secret123"\n/m) - } - end - end - - context 'with Azure config' do - before do - stub_gitlab_rb( - gitlab_rails: { - object_store: { - enabled: true, - connection: azure_connection_hash, - objects: object_config - } - } - ) - end - - it 'includes Azure credentials' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(/\[object_storage\]\n provider = "AzureRM"\n/m) - expect(content).to match(/\[object_storage.azurerm\]\n azure_storage_account_name = "testaccount"\n azure_storage_access_key = "1234abcd"\n/m) - } - end - end - end - - context 'without api rate limiting' do - it 'correctly renders out the workhorse service file' do - expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiLimit/) - expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiQueueDuration/) - expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiQueueLimit/) - end - end - - context 'with api rate limiting' do - before do - stub_gitlab_rb(gitlab_workhorse: { api_limit: 3, api_queue_limit: 6, api_queue_duration: '1m' }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiLimit 3 \\/) - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiQueueDuration 1m \\/) - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiQueueLimit 6 \\/) - end - end - - context 'without prometheus listen address' do - before do - stub_gitlab_rb(gitlab_workhorse: { prometheus_listen_addr: '' }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run") - expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-prometheusListenAddr/) - end - end - - context 'with prometheus listen address' do - before do - stub_gitlab_rb(gitlab_workhorse: { prometheus_listen_addr: ':9229' }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-prometheusListenAddr :9229 \\/) - end - end - - context 'without api ci long polling duration defined' do - it 'correctly renders out the workhorse service file' do - expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiCiLongPollingDuration/) - end - end - - context 'with api ci long polling duration defined' do - before do - stub_gitlab_rb(gitlab_workhorse: { api_ci_long_polling_duration: "60s" }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-apiCiLongPollingDuration 60s/) - end - end - - context 'with propagate_correlation_id enabled' do - before do - stub_gitlab_rb(gitlab_workhorse: { propagate_correlation_id: true }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-propagateCorrelationID/) - end - - context 'with trusted_cidrs_for_propagation defined' do - before do - stub_gitlab_rb(gitlab_workhorse: { - propagate_correlation_id: true, - trusted_cidrs_for_propagation: %w(127.0.0.1/32 192.168.0.1/8) - }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-propagateCorrelationID/) - end - - it 'should generate an array in the config file' do - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content { |content| - expect(content).to include(%(trusted_cidrs_for_propagation = ["127.0.0.1/32","192.168.0.1/8"])) - expect(content).not_to include(%(trusted_cidrs_for_x_forwarded_for)) - } - end - end - - context 'with trusted_cidrs_for_propagation and trusted_cidrs_for_x_forwarded_for defined' do - before do - stub_gitlab_rb(gitlab_workhorse: { - propagate_correlation_id: true, - trusted_cidrs_for_propagation: %w(127.0.0.1/32 192.168.0.1/8), - trusted_cidrs_for_x_forwarded_for: %w(1.2.3.4/16 5.6.7.8/24) - }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-propagateCorrelationID/) - end - - it 'should generate arrays in the config file' do - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content { |content| - expect(content).to include(%(trusted_cidrs_for_propagation = ["127.0.0.1/32","192.168.0.1/8"])) - expect(content).to include(%(trusted_cidrs_for_x_forwarded_for = ["1.2.3.4/16","5.6.7.8/24"])) - } - end - end - end - - context 'with log format defined as json' do - before do - stub_gitlab_rb(gitlab_workhorse: { log_format: "json" }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-logFormat json/) - end - - it 'renders svlogd file which will not prepend timestamp' do - expect(chef_run).not_to render_file("/opt/gitlab/sv/gitlab-workhorse/log/run").with_content(/\-tt/) - end - end - - context 'with log format defined as text' do - before do - stub_gitlab_rb(gitlab_workhorse: { log_format: "text" }) - end - - it 'correctly renders out the workhorse service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-logFormat text/) - end - - it 'renders svlogd file which will prepend timestamp' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/log/run").with_content(/\-tt/) - end - end - - context 'with default value for working directory' do - it 'should generate config file in the correct directory' do - expect(chef_run).to render_file(config_file) - end - end - - context 'with working directory specified' do - before do - stub_gitlab_rb(gitlab_workhorse: { dir: "/home/random/dir" }) - end - it 'should generate config file in the correct directory' do - expect(chef_run).to render_file("/home/random/dir/config.toml") - end - end - - context 'with default values for redis' do - it 'should generate config file' do - content_url = 'URL = "unix:/var/opt/gitlab/redis/redis.socket"' - expect(chef_run).to render_file(config_file).with_content(content_url) - expect(chef_run).not_to render_file(config_file).with_content(/Sentinel/) - end - - it 'should pass config file to workhorse' do - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content(/\-config config.toml/) - end - end - - context 'with host/port/password values for redis specified and socket disabled' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_host: "10.0.15.1", - redis_port: "1234", - redis_password: 'examplepassword' - } - ) - end - - it 'should generate config file with the specified values' do - content_url = 'URL = "redis://:examplepassword@10.0.15.1:1234/"' - content_password = 'Password = "examplepassword"' - content_sentinel = 'Sentinel' - content_sentinel_master = 'SentinelMaster' - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_password) - expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel) - expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_master) - end - end - - context 'with socket for redis specified' do - before do - stub_gitlab_rb(gitlab_rails: { redis_socket: "/home/random/path.socket", redis_password: 'examplepassword' }) - end - - it 'should generate config file with the specified values' do - content_url = 'URL = "unix:/home/random/path.socket"' - content_password = 'Password = "examplepassword"' - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_password) - expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(/Sentinel/) - expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(/SentinelMaster/) - end - end - - context 'with Sentinels specified with default master' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_sentinels: [ - { 'host' => '127.0.0.1', 'port' => 2637 }, - { 'host' => '127.0.8.1', 'port' => 1234 } - ] - } - ) - end - - it 'should generate config file with the specified values' do - content = 'Sentinel = ["tcp://127.0.0.1:2637", "tcp://127.0.8.1:1234"]' - content_url = 'URL =' - content_sentinel_master = 'SentinelMaster = "gitlab-redis"' - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content) - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_master) - expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) - end - end - - context 'with Sentinels and master specified' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_sentinels: [ - { 'host' => '127.0.0.1', 'port' => 26379 }, - { 'host' => '127.0.8.1', 'port' => 12345 } - ] - }, - redis: { - master_name: 'examplemaster', - master_password: 'examplepassword' - } - ) - end - - it 'should generate config file with the specified values' do - content = 'Sentinel = ["tcp://127.0.0.1:26379", "tcp://127.0.8.1:12345"]' - content_sentinel_master = 'SentinelMaster = "examplemaster"' - content_sentinel_password = 'Password = "examplepassword"' - content_url = 'URL =' - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content) - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_master) - expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_password) - expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) - end - end - - context 'image scaler' do - context 'with default values' do - it 'sets the default maximum file size' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(/\[image_resizer\]\n max_scaler_procs = \d+\n max_filesize = 250000/m) - } - end - - context 'when reported CPU cores are at least 4' do - let(:node_cpus) { 4 } - - it 'sets default max procs to half the number of cores' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(/\[image_resizer\]\n max_scaler_procs = 2/m) - } - end - end - - context 'when reported CPU cores are less than 4' do - let(:node_cpus) { 3 } - - it 'pins default max procs to 2' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(/\[image_resizer\]\n max_scaler_procs = 2/m) - } - end - end - end - - context 'with custom values' do - before do - stub_gitlab_rb( - gitlab_workhorse: { - image_scaler_max_procs: 5, - image_scaler_max_filesize: 1024 - } - ) - end - - it 'should generate config file with the specified values' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(/\[image_resizer\]\n max_scaler_procs = 5\n max_filesize = 1024/m) - } - end - end - end - - context 'with workhorse keywatcher enabled' do - before do - stub_gitlab_rb( - gitlab_workhorse: { - workhorse_keywatcher: true, - } - ) - end - - it 'should generate redis block in the configuration file' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).to match(/\[redis\]/m) - } - end - end - - context 'with workhorse keywatcher disabled' do - before do - stub_gitlab_rb( - gitlab_workhorse: { - workhorse_keywatcher: false, - } - ) - end - - it 'should not generate redis block in the configuration file' do - expect(chef_run).to render_file(config_file).with_content { |content| - expect(content).not_to match(/\[redis\]/m) - } - end - end - - include_examples "consul service discovery", "gitlab_workhorse", "workhorse" -end diff --git a/spec/chef/recipes/letsencrypt_spec.rb b/spec/chef/recipes/letsencrypt_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvbGV0c2VuY3J5cHRfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/letsencrypt_spec.rb +++ /dev/null @@ -1,227 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'enabling letsencrypt' do - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb(external_url: 'https://fakehost.example.com') - end - - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - context 'unspecified uses LetsEncrypt.should_auto_enable?' do - it 'true' do - allow(LetsEncrypt).to receive(:should_auto_enable?).and_return(true) - - expect(chef_run).to include_recipe('letsencrypt::enable') - end - - it 'false' do - allow(LetsEncrypt).to receive(:should_auto_enable?).and_return(false) - - expect(chef_run).to include_recipe('letsencrypt::disable') - expect(chef_run).not_to include_recipe('letsencrypt::enable') - end - end - - context 'specified' do - it 'true' do - stub_gitlab_rb(letsencrypt: { enable: true }) - - expect(chef_run).to include_recipe('letsencrypt::enable') - end - - it 'false' do - stub_gitlab_rb(letsencrypt: { enable: false }) - - expect(chef_run).not_to include_recipe('letsencrypt::enable') - end - end -end - -RSpec.describe 'letsencrypt::enable' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - let(:node) { chef_run.node } - - let(:https_redirect_block) { %r!server { ## HTTPS redirect server(.*)} ## end HTTPS redirect server!m } - let(:https_block) { %r!server { ## HTTPS server(.*)} ## end HTTPS server!m } - let(:acme_challenge_block) { %r!\s*location /.well-known/acme-challenge/ {! } - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(LetsEncrypt).to receive(:should_auto_enable?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('nginx').and_return(true) - end - - context 'default' do - it 'does not run' do - expect(chef_run).not_to include_recipe('letsencrypt::enable') - end - end - - context 'enabled' do - before do - stub_gitlab_rb( - external_url: 'https://fakehost.example.com', - mattermost_external_url: 'https://fakemost.example.com', - registry_external_url: 'https://fakereg.example.com', - letsencrypt: { - enable: true - } - ) - end - - it 'is included' do - expect(chef_run).to include_recipe('letsencrypt::enable') - end - - context 'when updating nginx configurations' do - using RSpec::Parameterized::TableSyntax - - where(:redirect_config_key, :nginx_config_file) do - 'nginx' | '/var/opt/gitlab/nginx/conf/gitlab-http.conf' - 'mattermost-nginx' | '/var/opt/gitlab/nginx/conf/gitlab-mattermost-http.conf' - 'registry-nginx' | '/var/opt/gitlab/nginx/conf/gitlab-registry.conf' - end - - with_them do - it 'redirects http to https' do - expect(node['gitlab'][redirect_config_key]['redirect_http_to_https']).to be_truthy - end - - it 'includes the well known acme challenge location block' do - expect(chef_run).to render_file(nginx_config_file).with_content { |content| - https_redirect_server = content.match(https_redirect_block) - https_server = content.match(https_block) - - expect(https_redirect_server).not_to be_nil - expect(https_server).not_to be_nil - - expect(https_redirect_server.to_s).to match(acme_challenge_block) - expect(https_server.to_s).to match(acme_challenge_block) - } - end - end - end - - it 'uses http authorization by default' do - expect(chef_run).to include_recipe('letsencrypt::http_authorization') - end - - it 'creates a self signed certificate' do - expect(chef_run).to create_acme_selfsigned('fakehost.example.com').with( - key: '/etc/gitlab/ssl/fakehost.example.com.key', - crt: '/etc/gitlab/ssl/fakehost.example.com.crt' - ) - end - - it 'creates a letsencrypt certificate' do - expect(chef_run).to create_letsencrypt_certificate('fakehost.example.com').with( - key: '/etc/gitlab/ssl/fakehost.example.com.key', - crt: '/etc/gitlab/ssl/fakehost.example.com.crt' - ) - end - - it 'warns the user' do - prod_cert = chef_run.letsencrypt_certificate('fakehost.example.com') - expect(prod_cert).to notify('ruby_block[display_le_message]').to(:run) - end - - context 'auto_renew' do - context 'default' do - it 'enables crond' do - expect(chef_run).to include_recipe('crond::enable') - end - - it 'adds a crond_job' do - expect(chef_run).to create_crond_job('letsencrypt-renew').with( - user: "root", - hour: 0, - minute: 31, - day_of_month: "*/4", - command: "/opt/gitlab/bin/gitlab-ctl renew-le-certs" - ) - end - - it 'does not log a warning' do - expect(LoggingHelper).not_to receive(:warning).with("Let's Encrypt is enabled, but external_url is using http") - chef_run.ruby_block('display_le_message').block.call - end - end - - context 'false' do - before do - stub_gitlab_rb(letsencrypt: { enable: true, auto_renew: false }) - allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return false - end - - it 'removes the letsencrypt-renew cronjob' do - expect(chef_run).to delete_crond_job('letsencrypt-renew') - end - - it 'warns that we do not setup automatic renewal' do - chef_run.ruby_block('display_le_message').block.call - - expect_logged_warning(/does not setup/) - end - end - end - - context 'external_url uses http' do - before do - stub_gitlab_rb( - external_url: 'http://plainhost.example.com', - letsencrypt: { - enable: true - } - ) - end - - it 'logs a warning' do - expect(chef_run).to run_ruby_block('http external-url') - end - end - end -end - -# This should work standalone for renewal purposes -RSpec.describe 'letsencrypt::renew' do - let(:chef_run) do - ChefSpec::SoloRunner.converge('gitlab::letsencrypt_renew') - end - - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('nginx').and_return(true) - end - - context 'letsencrypt enabled' do - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb( - external_url: 'https://standalone.fakehost.com', - letsencrypt: { - enable: true - } - ) - end - - it 'executes letsencrypt_certificate' do - expect(chef_run).to create_letsencrypt_certificate('standalone.fakehost.com') - end - end - - context 'letsencrypt auto-enabled' do - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(OpenSSL::X509::Certificate).to receive(:not_after).and_return(Time.now - 1) - stub_gitlab_rb( - external_url: 'https://standalone.fakehost.com' - ) - end - - it 'executes letsencrypt_certificate' do - expect(chef_run).to create_letsencrypt_certificate('standalone.fakehost.com') - end - end -end diff --git a/spec/chef/recipes/mailroom_spec.rb b/spec/chef/recipes/mailroom_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvbWFpbHJvb21fc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/mailroom_spec.rb +++ /dev/null @@ -1,291 +0,0 @@ -# -# Copyright:: Copyright (c) 2018 GitLab Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'chef_helper' - -RSpec.describe 'gitlab::mailroom' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service templatesymlink)).converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - shared_examples 'renders Microsoft Graph config' do - let(:gitlab_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } - let(:gitlab_yml_file_content) { ChefSpec::Renderer.new(chef_run, gitlab_yml_template).content } - let(:gitlab_yml) { YAML.safe_load(gitlab_yml_file_content, [], [], true, symbolize_names: true) } - let(:inbox_options) do - { - tenant_id: 'MY-TENANT-ID', - client_id: '12345', - client_secret: 'MY-CLIENT-SECRET' - } - end - - before do - config_sections.each do |config_section| - stub_gitlab_rb( - gitlab_rails: { - "#{config_section}_enabled".to_sym => true, - "#{config_section}_inbox_method".to_sym => 'microsoft_graph', - "#{config_section}_inbox_options".to_sym => inbox_options - } - ) - end - end - - it 'renders gitlab.yml with the right data' do - config_sections.each do |config_section| - expect(gitlab_yml[:production][config_section][:inbox_method]).to eq('microsoft_graph') - expect(gitlab_yml[:production][config_section][:inbox_options]).to eq(inbox_options) - end - end - end - - shared_examples 'configured sidekiq delivery method' do - let(:gitlab_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } - let(:gitlab_yml_file_content) { ChefSpec::Renderer.new(chef_run, gitlab_yml_template).content } - let(:gitlab_yml) { YAML.safe_load(gitlab_yml_file_content, [], [], true, symbolize_names: true) } - - before do - stub_gitlab_rb( - gitlab_rails: config_sections.each_with_object({}) do |config_section, memo| - memo.merge!( - "#{config_section}_enabled".to_sym => true, - "#{config_section}_delivery_method".to_sym => 'sidekiq' - ) - end - ) - end - - it 'renders gitlab.yml with the right data' do - config_sections.each do |config_section| - expect(gitlab_yml[:production][config_section][:delivery_method]).to eq('sidekiq') - end - end - end - - shared_examples 'configured webhook delivery method' do - let(:gitlab_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } - let(:gitlab_yml_file_content) { ChefSpec::Renderer.new(chef_run, gitlab_yml_template).content } - let(:gitlab_yml) { YAML.safe_load(gitlab_yml_file_content, [], [], true, symbolize_names: true) } - - before do - stub_gitlab_rb( - external_url: 'http://localhost/gitlab/', # Mind the last "/" character - gitlab_rails: config_sections.each_with_object({}) do |config_section, memo| - memo.merge!( - "#{config_section}_enabled".to_sym => true, - "#{config_section}_delivery_method".to_sym => 'webhook' - ) - end - ) - end - - it 'renders gitlab.yml with the right data' do - config_sections.each do |config_section| - expect(gitlab_yml[:production][config_section][:delivery_method]).to eq('webhook') - expect(gitlab_yml[:production][config_section][:secret_file]).to eq(".gitlab_#{config_section}_secret") - expect(gitlab_yml[:production][config_section][:gitlab_url]).to eq("http://localhost/gitlab") - end - end - - it 'triggers notifications' do - config_sections.each do |config_section| - templatesymlink = chef_run.templatesymlink("Create a gitlab_#{config_section}_secret and create a symlink to Rails root") - expect(templatesymlink).to notify('runit_service[mailroom]').to(:restart).delayed - end - end - - context 'auth token is not specified' do - before do - allow(SecretsHelper).to receive(:generate_base64).with(32).and_return("a" * 32) - end - - it 'renders the correct node attribute with auto-generated auth token' do - config_sections.each do |config_section| - expect(chef_run).to create_templatesymlink("Create a gitlab_#{config_section}_secret and create a symlink to Rails root").with( - owner: 'root', - group: 'root', - mode: '0644' - ).with_variables( - a_hash_including( - secret_token: "a" * 32 - ) - ) - end - end - end - - context 'auth token is set' do - let(:auth_token) { SecureRandom.base64(32) } - - before do - stub_gitlab_rb( - external_url: 'http://localhost/gitlab/', - gitlab_rails: config_sections.each_with_object({}) do |config_section, memo| - memo.merge!( - "#{config_section}_enabled".to_sym => true, - "#{config_section}_delivery_method".to_sym => 'webhook', - "#{config_section}_auth_token".to_sym => "#{auth_token}-#{config_section}" - ) - end - ) - end - - it 'renders the correct node attribute' do - config_sections.each do |config_section| - expect(chef_run).to create_templatesymlink("Create a gitlab_#{config_section}_secret and create a symlink to Rails root").with( - owner: 'root', - group: 'root', - mode: '0644' - ).with_variables( - a_hash_including( - secret_token: "#{auth_token}-#{config_section}" - ) - ) - end - end - end - end - - describe 'when disabled' do - it_behaves_like 'disabled runit service', 'mailroom' - end - - describe 'when enabled' do - context 'when only service_desk_email enabled' do - let(:config_sections) { %i[service_desk_email] } - - before do - stub_gitlab_rb( - gitlab_rails: { - service_desk_email_enabled: true - } - ) - end - - it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' - it_behaves_like 'configured logrotate service', 'mailroom', 'git', 'git' - it_behaves_like 'configured sidekiq delivery method' - end - - context 'when both service_desk_email and incoming_email enabled' do - let(:config_sections) { %i[incoming_email service_desk_email] } - - before do - stub_gitlab_rb( - gitlab_rails: { - incoming_email_enabled: true, - service_desk_email_enabled: true - } - ) - end - - it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' - it_behaves_like 'configured logrotate service', 'mailroom', 'git', 'git' - it_behaves_like 'configured sidekiq delivery method' - end - - context 'default values' do - let(:config_sections) { %i[incoming_email] } - - before do - stub_gitlab_rb( - gitlab_rails: { - incoming_email_enabled: true - } - ) - end - - it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' - it_behaves_like 'configured logrotate service', 'mailroom', 'git', 'git' - - it 'uses --log-exit-as plain' do - expect(chef_run).to render_file("/opt/gitlab/sv/mailroom/run").with_content(/\-\-log\-exit\-as plain/) - end - end - - context 'custom values' do - let(:config_sections) { %i[incoming_email] } - - before do - stub_gitlab_rb( - gitlab_rails: { - incoming_email_enabled: true - }, - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' - it_behaves_like 'configured logrotate service', 'mailroom', 'foo', 'bar' - end - - context 'incoming email with Microsoft Graph' do - let(:config_sections) { %i[incoming_email] } - - it_behaves_like 'renders Microsoft Graph config' - end - - context 'Service Desk email with Microsoft Graph' do - let(:config_sections) { %i[service_desk_email] } - - it_behaves_like 'renders Microsoft Graph config' - end - - context 'Incoming Email webhook delivery method' do - let(:config_sections) { %i[incoming_email] } - - it_behaves_like 'configured webhook delivery method' - end - - context 'Service Desk Email webhook delivery method' do - let(:config_sections) { %i[incoming_email] } - - it_behaves_like 'configured webhook delivery method' - end - - context 'Both Incoming Email and Service Desk Email webhook delivery method' do - let(:config_sections) { %i[incoming_email service_desk_email] } - - it_behaves_like 'configured webhook delivery method' - end - end - - context 'with specified exit_log_format' do - before do - stub_gitlab_rb( - gitlab_rails: { - incoming_email_enabled: true - }, - mailroom: { - exit_log_format: "json" - } - ) - end - - it 'correctly passes the --log-exit-as ' do - expect(chef_run).to render_file("/opt/gitlab/sv/mailroom/run").with_content(/\-\-log\-exit\-as json/) - end - - it_behaves_like 'enabled runit service', 'mailroom', 'root', 'root' - it_behaves_like 'configured logrotate service', 'mailroom', 'git', 'git' - end -end diff --git a/spec/chef/recipes/mattermost_spec.rb b/spec/chef/recipes/mattermost_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvbWF0dGVybW9zdF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/mattermost_spec.rb +++ /dev/null @@ -1,322 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::mattermost' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:default_vars) do - { - 'MM_FILESETTINGS_DIRECTORY' => '/var/opt/gitlab/mattermost/data', - 'MM_GITLABSETTINGS_AUTHENDPOINT' => 'http://gitlab.example.com/oauth/authorize', - 'MM_GITLABSETTINGS_ENABLE' => 'false', - 'MM_GITLABSETTINGS_ID' => '', - 'MM_GITLABSETTINGS_SCOPE' => '', - 'MM_GITLABSETTINGS_SECRET' => '', - 'MM_GITLABSETTINGS_TOKENENDPOINT' => 'http://gitlab.example.com/oauth/token', - 'MM_GITLABSETTINGS_USERAPIENDPOINT' => 'http://gitlab.example.com/api/v4/user', - 'MM_INSTALL_TYPE' => 'gitlab_omnibus', - 'MM_LOGSETTINGS_FILELOCATION' => '/var/log/gitlab/mattermost', - 'MM_PLUGINSETTINGS_CLIENTDIRECTORY' => '/var/opt/gitlab/mattermost/client-plugins', - 'MM_PLUGINSETTINGS_DIRECTORY' => '/var/opt/gitlab/mattermost/plugins', - 'MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS' => ' gitlab.example.com', - 'MM_SERVICESETTINGS_ENABLEAPITEAMDELETION' => 'true', - 'MM_SERVICESETTINGS_LISTENADDRESS' => '127.0.0.1:8065', - 'MM_SERVICESETTINGS_SITEURL' => 'http://mattermost.example.com', - 'MM_SQLSETTINGS_ATRESTENCRYPTKEY' => 'aabbccddeeff11223344556677889900', - 'MM_SQLSETTINGS_DATASOURCE' => 'user=gitlab_mattermost host=/var/opt/gitlab/postgresql port=5432 dbname=mattermost_production', - 'MM_SQLSETTINGS_DRIVERNAME' => 'postgres', - 'MM_TEAMSETTINGS_SITENAME' => 'GitLab Mattermost', - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb(external_url: 'http://gitlab.example.com', mattermost_external_url: 'http://mattermost.example.com') - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:database_exists?).and_return(true) - allow(SecretsHelper).to receive(:generate_hex).with(64).and_return('aaaabbbbccccddddeeeeffff1111222233334444555566667777888899990000') - allow(SecretsHelper).to receive(:generate_hex).with(16).and_return('aabbccddeeff11223344556677889900') - end - - context 'by default' do - it 'creates a default VERSION file and restarts service' do - expect(chef_run).to create_version_file('Create version file for Mattermost').with( - version_file_path: '/var/opt/gitlab/mattermost/VERSION', - version_check_cmd: 'cat /opt/gitlab/embedded/service/mattermost/VERSION' - ) - - expect(chef_run.version_file('Create version file for Mattermost')).to notify('runit_service[mattermost]').to(:hup) - end - end - - context 'service user and group' do - context 'default values' do - it_behaves_like "enabled runit service", "mattermost", "root", "root" - end - - context 'custom user and group' do - before do - stub_gitlab_rb( - external_url: 'http://gitlab.example.com', - mattermost_external_url: 'http://mattermost.example.com', - mattermost: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like "enabled runit service", "mattermost", "root", "root" - end - end - - context 'SiteUrl setting' do - context 'default value' do - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( - variables: default_vars - ) - end - end - - context 'when explicitly set' do - before do - stub_gitlab_rb(mattermost: { - service_site_url: 'http://mattermost.gitlab.example' - }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( - variables: default_vars.merge({ 'MM_SERVICESETTINGS_SITEURL' => 'http://mattermost.gitlab.example' }) - ) - end - end - end - - it 'authorizes mattermost with gitlab' do - allow(MattermostHelper).to receive(:authorize_with_gitlab) - - expect(chef_run).to run_ruby_block('authorize mattermost with gitlab') - .at_converge_time - expect(MattermostHelper).to receive(:authorize_with_gitlab) - .with 'http://gitlab.example.com' - - chef_run.ruby_block('authorize mattermost with gitlab').block.call - end - - it 'populates mattermost configuration options to node attributes' do - stub_gitlab_rb(mattermost: { enable: true, gitlab_id: 'old' }) - allow(MattermostHelper).to receive(:authorize_with_gitlab) do |url| - Gitlab['mattermost']['gitlab_id'] = 'new' - end - - expect(chef_run).to run_ruby_block('populate mattermost configuration options') - .at_converge_time - - chef_run.ruby_block('authorize mattermost with gitlab').block.call - chef_run.ruby_block('populate mattermost configuration options').block.call - - expect(chef_run.node['mattermost']['gitlab_id']).to eq 'new' - end - - context 'populate env variables based on provided gitlab settings' do - before do - stub_gitlab_rb(mattermost: { - enable: true, - gitlab_enable: true, - gitlab_id: 'gitlab_id', - gitlab_secret: 'gitlab_secret', - gitlab_scope: 'scope', - }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( - variables: default_vars.merge( - { - 'MM_GITLABSETTINGS_ENABLE' => 'true', - 'MM_GITLABSETTINGS_SECRET' => 'gitlab_secret', - 'MM_GITLABSETTINGS_ID' => 'gitlab_id', - 'MM_GITLABSETTINGS_SCOPE' => 'scope', - 'MM_GITLABSETTINGS_AUTHENDPOINT' => 'http://gitlab.example.com/oauth/authorize', - 'MM_GITLABSETTINGS_TOKENENDPOINT' => 'http://gitlab.example.com/oauth/token', - 'MM_GITLABSETTINGS_USERAPIENDPOINT' => 'http://gitlab.example.com/api/v4/user', - } - ) - ) - end - end - - context 'allows overrides to the mattermost settings regarding GitLab endpoints' do - before do - stub_gitlab_rb(mattermost: { - enable: true, - gitlab_enable: true, - gitlab_auth_endpoint: 'https://test-endpoint.example.com/test/auth', - gitlab_token_endpoint: 'https://test-endpoint.example.com/test/token', - gitlab_user_api_endpoint: 'https://test-endpoint.example.com/test/user/api' - }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( - variables: default_vars.merge( - { - 'MM_GITLABSETTINGS_ENABLE' => 'true', - 'MM_GITLABSETTINGS_AUTHENDPOINT' => 'https://test-endpoint.example.com/test/auth', - 'MM_GITLABSETTINGS_TOKENENDPOINT' => 'https://test-endpoint.example.com/test/token', - 'MM_GITLABSETTINGS_USERAPIENDPOINT' => 'https://test-endpoint.example.com/test/user/api' - } - ) - ) - end - end - - context 'gitlab is added to untrusted internal connections list' do - context 'when no allowed internal connections are provided by gitlab.rb' do - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with(variables: default_vars) - end - end - - context 'when some allowed internal connections are provided by gitlab.rb' do - before do - stub_gitlab_rb(mattermost: { enable: true, service_allowed_untrusted_internal_connections: 'localhost' }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( - variables: default_vars.merge( - { - 'MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS' => 'localhost gitlab.example.com' - } - ) - ) - end - end - end - - shared_examples 'no gitlab authorization performed' do - it 'does not authorize mattermost with gitlab' do - expect(chef_run).not_to run_ruby_block('authorize mattermost with gitlab') - end - end - - context 'when gitlab authentication parameters are specified explicitly' do - before { stub_gitlab_rb(mattermost: { enable: true, gitlab_enable: true }) } - - it_behaves_like 'no gitlab authorization performed' - end - - context 'when gitlab-rails is disabled' do - before { stub_gitlab_rb(gitlab_rails: { enable: false }) } - - it_behaves_like 'no gitlab authorization performed' - - it 'does not add gitlab automatically to the list of allowed internal addresses' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( - variables: default_vars.merge( - { - 'MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS' => nil - } - ) - ) - end - end - - context 'when database is not running' do - before { allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(false) } - - it_behaves_like 'no gitlab authorization performed' - end - - context 'when mattermost database does not exist' do - before { allow_any_instance_of(PgHelper).to receive(:database_exists?).and_return(false) } - - it_behaves_like 'no gitlab authorization performed' - end - - context 'when a custom env variable is specified' do - before do - stub_gitlab_rb(mattermost: { env: { 'IAM' => 'CUSTOMVAR' } }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/mattermost/env').with( - variables: default_vars.merge( - { - 'IAM' => 'CUSTOMVAR' - } - ) - ) - end - end - - describe 'letsencrypt' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - mattermost_external_url: 'https://mattermost.example.com' - ) - - allow(File).to receive(:exist?).and_call_original - end - - describe 'HTTP to HTTPS redirection' do - context 'by default' do - it 'is enabled' do - expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-mattermost-http.conf').with_content("return 301 https://mattermost.example.com:443$request_uri;") - end - end - - context 'if disabled in gitlab.rb' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - mattermost_external_url: 'https://mattermost.example.com', - mattermost_nginx: { - redirect_http_to_https: false - } - ) - end - - it 'is disabled' do - expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-mattermost-http.conf') - expect(chef_run).not_to render_file('/var/opt/gitlab/nginx/conf/gitlab-mattermost.conf').with_content("return 301 https://mattermost.example.com:443$request_uri;") - end - end - end - - context 'default certificate file is missing' do - before do - allow(File).to receive(:exist?).with('/etc/gitlab/ssl/mattermost.example.com.crt').and_return(false) - end - - it 'adds itself to letsencrypt alt_names' do - expect(chef_run.node['letsencrypt']['alt_names']).to eql(['mattermost.example.com']) - end - - it 'is reflected in the acme_selfsigned' do - expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( - alt_names: ['mattermost.example.com'] - ) - end - end - - context 'default certificate file is present' do - before do - allow(File).to receive(:exist?).with('/etc/gitlab/ssl/mattermost.example.com.crt').and_return(true) - end - - it 'does not alter letsencrypt alt_names' do - expect(chef_run.node['letsencrypt']['alt_names']).to eql([]) - end - - it 'is reflected in the acme_selfsigned' do - expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( - alt_names: [] - ) - end - end - end -end diff --git a/spec/chef/recipes/nginx_spec.rb b/spec/chef/recipes/nginx_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvbmdpbnhfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/nginx_spec.rb +++ /dev/null @@ -1,698 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::nginx' do - let(:chef_runner) do - ChefSpec::SoloRunner.new(step_into: %w(runit_service)) do |node| - node.normal['gitlab']['nginx']['enable'] = true - node.normal['package']['install-dir'] = '/opt/gitlab' - end - end - - let(:chef_run) do - chef_runner.converge('gitlab::config', 'gitlab::nginx') - end - - let(:gitlab_http_config) { '/var/opt/gitlab/nginx/conf/gitlab-http.conf' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(Gitlab).to receive(:[]).with('node') { chef_runner.node } - - # generate a random number to use as error code - @code = rand(1000) - @nginx_errors = { - @code => { - 'title' => 'TEST TITLE', - 'header' => 'TEST HEADER', - 'message' => 'TEST MESSAGE' - } - } - end - - it_behaves_like 'enabled runit service', 'nginx', 'root', 'root' - - it 'creates a custom error_page entry when a custom error is defined' do - allow(Gitlab).to receive(:[]).with('nginx').and_return({ 'custom_error_pages' => @nginx_errors }) - - expect(chef_run).to render_file(gitlab_http_config).with_content { |content| - expect(content).to include("error_page #{@code} /#{@code}-custom.html;") - } - end - - it 'renders an error template when a custom error is defined' do - chef_runner.node.normal['gitlab']['nginx']['custom_error_pages'] = @nginx_errors - expect(chef_run).to render_file("/opt/gitlab/embedded/service/gitlab-rails/public/#{@code}-custom.html").with_content { |content| - expect(content).to include("TEST MESSAGE") - } - end - - it 'creates a standard error_page entry when no custom error is defined' do - chef_runner.node.normal['gitlab']['nginx'].delete('custom_error_pages') - expect(chef_run).to render_file(gitlab_http_config).with_content { |content| - expect(content).to include("error_page 404 /404.html;") - } - end - - it 'enables the proxy_intercept_errors option when custom_error_pages is defined' do - chef_runner.node.normal['gitlab']['nginx']['custom_error_pages'] = @nginx_errors - expect(chef_run).to render_file(gitlab_http_config).with_content { |content| - expect(content).to include("proxy_intercept_errors on") - } - end - - it 'uses the default proxy_intercept_errors option when custom_error_pages is not defined' do - chef_runner.node.normal['gitlab']['nginx'].delete('custom_error_pages') - expect(chef_run).to render_file(gitlab_http_config).with_content { |content| - expect(content).not_to include("proxy_intercept_errors") - } - end -end - -RSpec.describe 'nginx' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - subject { chef_run } - - let(:gitlab_http_config) { '/var/opt/gitlab/nginx/conf/gitlab-http.conf' } - let(:nginx_status_config) { /include \/var\/opt\/gitlab\/nginx\/conf\/nginx-status\.conf;/ } - - let(:basic_nginx_headers) do - { - "Host" => "$http_host", - "X-Real-IP" => "$remote_addr", - "X-Forwarded-Proto" => "http", - "X-Forwarded-For" => "$proxy_add_x_forwarded_for" - } - end - - let(:http_conf) do - { - "gitlab" => "/var/opt/gitlab/nginx/conf/gitlab-http.conf", - "mattermost" => "/var/opt/gitlab/nginx/conf/gitlab-mattermost-http.conf", - "registry" => "/var/opt/gitlab/nginx/conf/gitlab-registry.conf", - "pages" => "/var/opt/gitlab/nginx/conf/gitlab-pages.conf", - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when http external urls are being used' do - before do - stub_gitlab_rb( - external_url: 'http://localhost', - mattermost_external_url: 'http://mattermost.localhost', - registry_external_url: 'http://registry.localhost', - pages_external_url: 'http://pages.localhost' - ) - end - - it 'properly sets the default nginx proxy headers' do - expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to eql(nginx_headers({ - "Host" => "$http_host_with_default", - "Upgrade" => "$http_upgrade", - "Connection" => "$connection_upgrade" - })) - expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to eql(basic_nginx_headers) - expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to eql(nginx_headers({ - "X-Frame-Options" => "SAMEORIGIN", - "Upgrade" => "$http_upgrade", - "Connection" => "$connection_upgrade" - })) - expect(chef_run.node['gitlab']['pages-nginx']['proxy_set_headers']).to eql(basic_nginx_headers) - end - - it 'supports overriding default nginx headers' do - expect_headers = nginx_headers({ "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp" }) - stub_gitlab_rb( - "nginx" => { proxy_set_headers: { "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp" } }, - "mattermost_nginx" => { proxy_set_headers: { "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp" } }, - "registry_nginx" => { proxy_set_headers: { "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp" } } - ) - - expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to include(expect_headers) - expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to include(expect_headers) - expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to include(expect_headers) - end - end - - context 'when https external urls are being used' do - before do - stub_gitlab_rb( - external_url: 'https://localhost', - mattermost_external_url: 'https://mattermost.localhost', - registry_external_url: 'https://registry.localhost', - pages_external_url: 'https://pages.localhost' - ) - end - - it 'properly sets the default nginx proxy ssl forward headers' do - expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to eql(nginx_headers({ - "Host" => "$http_host_with_default", - "X-Forwarded-Proto" => "https", - "X-Forwarded-Ssl" => "on", - "Upgrade" => "$http_upgrade", - "Connection" => "$connection_upgrade" - })) - - expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to eql(nginx_headers({ - "X-Forwarded-Proto" => "https", - "X-Forwarded-Ssl" => "on" - })) - - expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to eql(nginx_headers({ - "X-Forwarded-Proto" => "https", - "X-Forwarded-Ssl" => "on", - "X-Frame-Options" => "SAMEORIGIN", - "Upgrade" => "$http_upgrade", - "Connection" => "$connection_upgrade" - })) - - expect(chef_run.node['gitlab']['pages-nginx']['proxy_set_headers']).to eql(nginx_headers({ - "X-Forwarded-Proto" => "https", - "X-Forwarded-Ssl" => "on" - })) - end - - it 'supports overriding default nginx headers' do - expect_headers = nginx_headers({ "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp", "X-Forwarded-Ssl" => "on", 'Connection' => 'close' }) - set_headers = { "Host" => "nohost.example.com", "X-Forwarded-Proto" => "ftp", 'Connection' => 'close' } - stub_gitlab_rb( - "nginx" => { proxy_set_headers: set_headers }, - "mattermost_nginx" => { proxy_set_headers: set_headers }, - "registry_nginx" => { proxy_set_headers: set_headers }, - "pages_nginx" => { proxy_set_headers: set_headers } - ) - - expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to include(expect_headers) - expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to include(expect_headers) - expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to include(expect_headers) - expect(chef_run.node['gitlab']['pages-nginx']['proxy_set_headers']).to include(expect_headers) - end - - it 'disables Connection header' do - expect_headers = nginx_headers({ "Host" => "nohost.example.com", "X-Forwarded-Proto" => "https", "X-Forwarded-Ssl" => "on" }) - set_headers = { "Host" => "nohost.example.com", "Connection" => nil } - stub_gitlab_rb( - "nginx" => { proxy_set_headers: set_headers }, - "mattermost_nginx" => { proxy_set_headers: set_headers }, - "registry_nginx" => { proxy_set_headers: set_headers }, - "pages_nginx" => { proxy_set_headers: set_headers } - ) - - expect(chef_run.node['gitlab']['nginx']['proxy_set_headers']).to include(expect_headers) - expect(chef_run.node['gitlab']['mattermost-nginx']['proxy_set_headers']).to include(expect_headers) - expect(chef_run.node['gitlab']['registry-nginx']['proxy_set_headers']).to include(expect_headers) - expect(chef_run.node['gitlab']['pages-nginx']['proxy_set_headers']).to include(expect_headers) - end - - it 'does not set ssl_client_certificate by default' do - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content { |content| - expect(content).not_to include("ssl_client_certificate") - } - end - end - - it 'does not set ssl_verify_client by default' do - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content { |content| - expect(content).not_to include("ssl_verify_client") - } - end - end - - it 'does not set ssl_verify_depth by default' do - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content { |content| - expect(content).not_to include("ssl_verify_depth") - } - end - end - - it 'sets the default ssl_verify_depth when ssl_verify_client is defined' do - verify_client = { "ssl_verify_client" => "on" } - stub_gitlab_rb( - "nginx" => verify_client, - "mattermost_nginx" => verify_client, - "registry_nginx" => verify_client, - "pages_nginx" => verify_client - ) - chef_run.converge('gitlab::default') - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content { |content| - expect(content).to include("ssl_verify_depth 1") - } - end - end - - it 'applies nginx verify client settings to gitlab-http' do - stub_gitlab_rb("nginx" => { - "ssl_client_certificate" => "/etc/gitlab/ssl/gitlab-http-ca.crt", - "ssl_verify_client" => "on", - "ssl_verify_depth" => "2", - }) - chef_run.converge('gitlab::default') - expect(chef_run).to render_file(http_conf['gitlab']).with_content { |content| - expect(content).to include("ssl_client_certificate /etc/gitlab/ssl/gitlab-http-ca.crt") - expect(content).to include("ssl_verify_client on") - expect(content).to include("ssl_verify_depth 2") - } - end - - it 'applies nginx request_buffering path regex' do - expect(chef_run).to render_file(http_conf['gitlab']).with_content { |content| - expect(content).to include("location ~ (/api/v\\d/jobs/\\d+/artifacts$|\\.git/git-receive-pack$|\\.git/gitlab-lfs/objects|\\.git/info/lfs/objects/batch$)") - } - end - - it 'applies mattermost_nginx verify client settings to gitlab-mattermost-http' do - stub_gitlab_rb("mattermost_nginx" => { - "ssl_client_certificate" => "/etc/gitlab/ssl/gitlab-mattermost-http-ca.crt", - "ssl_verify_client" => "on", - "ssl_verify_depth" => "3", - }) - chef_run.converge('gitlab::default') - expect(chef_run).to render_file(http_conf['mattermost']).with_content { |content| - expect(content).to include("ssl_client_certificate /etc/gitlab/ssl/gitlab-mattermost-http-ca.crt") - expect(content).to include("ssl_verify_client on") - expect(content).to include("ssl_verify_depth 3") - } - end - - it 'applies registry_nginx verify client settings to gitlab-registry' do - stub_gitlab_rb("registry_nginx" => { - "ssl_client_certificate" => "/etc/gitlab/ssl/gitlab-registry-ca.crt", - "ssl_verify_client" => "off", - "ssl_verify_depth" => "5", - }) - chef_run.converge('gitlab::default') - expect(chef_run).to render_file(http_conf['registry']).with_content { |content| - expect(content).to include("ssl_client_certificate /etc/gitlab/ssl/gitlab-registry-ca.crt") - expect(content).to include("ssl_verify_client off") - expect(content).to include("ssl_verify_depth 5") - } - end - - it 'applies pages_nginx verify client settings to gitlab-pages' do - stub_gitlab_rb("pages_nginx" => { - "ssl_client_certificate" => "/etc/gitlab/ssl/gitlab-pages-ca.crt", - "ssl_verify_client" => "on", - "ssl_verify_depth" => "7", - }) - chef_run.converge('gitlab::default') - expect(chef_run).to render_file(http_conf['pages']).with_content { |content| - expect(content).to include("ssl_client_certificate /etc/gitlab/ssl/gitlab-pages-ca.crt") - expect(content).to include("ssl_verify_client on") - expect(content).to include("ssl_verify_depth 7") - } - end - end - - context 'when is enabled' do - it 'enables nginx status by default' do - expect(chef_run.node['gitlab']['nginx']['status']).to eql({ - "enable" => true, - "listen_addresses" => ["*"], - "fqdn" => "localhost", - "port" => 8060, - "vts_enable" => true, - "options" => { - "server_tokens" => "off", - "access_log" => "off", - "allow" => "127.0.0.1", - "deny" => "all" - } - }) - expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/nginx.conf').with_content(nginx_status_config) - end - - it "supports overrading nginx status default configuration" do - custom_nginx_status_config = { - "enable" => true, - "listen_addresses" => ["127.0.0.1"], - "fqdn" => "dev.example.com", - "port" => 9999, - "vts_enable" => true, - "options" => { - "server_tokens" => "off", - "access_log" => "on", - "allow" => "127.0.0.1", - "deny" => "all" - } - } - - stub_gitlab_rb("nginx" => { - "status" => custom_nginx_status_config - }) - - chef_run.converge('gitlab::default') - - expect(chef_run.node['gitlab']['nginx']['status']).to eql(custom_nginx_status_config) - end - - it "will not load the nginx status config if nginx status is disabled" do - stub_gitlab_rb("nginx" => { "status" => { "enable" => false } }) - expect(chef_run).not_to render_file('/var/opt/gitlab/nginx/conf/nginx.conf').with_content(nginx_status_config) - end - - it 'defaults to redirect_http_to_https off' do - expect(chef_run.node['gitlab']['nginx']['redirect_http_to_https']).to be false - expect(chef_run).to render_file(gitlab_http_config).with_content { |content| - expect(content).not_to include('return 301 https://fauxhai.local:80$request_uri;') - } - end - - it 'enables redirect when redirect_http_to_https is true' do - stub_gitlab_rb(nginx: { listen_https: true, redirect_http_to_https: true }) - expect(chef_run.node['gitlab']['nginx']['redirect_http_to_https']).to be true - expect(chef_run).to render_file(gitlab_http_config).with_content('return 301 https://fauxhai.local:80$request_uri;') - end - - context 'when smartcard authentication is enabled' do - let(:gitlab_smartcard_http_config) { '/var/opt/gitlab/nginx/conf/gitlab-smartcard-http.conf' } - - before do - stub_gitlab_rb( - gitlab_rails: { - smartcard_enabled: true - }, - nginx: { listen_https: true } - ) - end - - it 'listens on a separate port' do - expect(chef_run).to render_file(gitlab_smartcard_http_config).with_content { |content| - expect(content).to include('server_name fauxhai.local;') - expect(content).to include('listen *:3444 ssl http2;') - } - end - - it 'requires client side certificate' do - expect(chef_run).to render_file(gitlab_smartcard_http_config).with_content { |content| - expect(content).to include('ssl_client_certificate /etc/gitlab/ssl/CA.pem') - expect(content).to include('ssl_verify_client on') - expect(content).to include('ssl_verify_depth 2') - } - end - - it 'forwards client side certificate in header' do - expect(chef_run).to render_file(gitlab_smartcard_http_config).with_content('proxy_set_header X-SSL-Client-Certificate') - end - - context 'when smartcard_client_certificate_required_host is set' do - before do - stub_gitlab_rb( - gitlab_rails: { - smartcard_enabled: true, - smartcard_client_certificate_required_host: 'smartcard.fauxhai.local' - }, - nginx: { listen_https: true } - ) - end - - it 'sets smartcard nginx server name' do - expect(chef_run).to render_file(gitlab_smartcard_http_config).with_content { |content| - expect(content).to include('server_name smartcard.fauxhai.local;') - expect(content).to include('listen *:3444 ssl http2;') - } - end - end - end - - context 'when smartcard authentication is disabled' do - let(:gitlab_smartcard_http_config) { '/var/opt/gitlab/nginx/conf/gitlab-smartcard-http.conf' } - - before do - stub_gitlab_rb(gitlab_rails: { smartcard_enabled: false }) - end - - it 'should not add the gitlab smartcard config' do - expect(chef_run).not_to render_file(gitlab_smartcard_http_config) - end - end - end - - context 'when is disabled' do - it 'should not add the nginx status config' do - stub_gitlab_rb("nginx" => { "enable" => false }) - expect(chef_run).not_to render_file('/var/opt/gitlab/nginx/conf/nginx.conf').with_content(nginx_status_config) - end - end - - context 'when grafana is enabled' do - before do - stub_gitlab_rb( - grafana: { enable: true } - ) - end - - it 'applies nginx grafana proxy' do - expect(chef_run).to render_file(http_conf['gitlab']).with_content { |content| - expect(content).to include('location /-/grafana/ {') - expect(content).to include('proxy_pass http://localhost:3000/;') - } - end - end - - context 'when KAS is enabled' do - before do - stub_gitlab_rb( - gitlab_kas: { enable: true } - ) - end - - it 'applies nginx KAS proxy' do - expect(chef_run).to render_file(http_conf['gitlab']).with_content { |content| - expect(content).to include('location = /-/kubernetes-agent/ {') - expect(content).to include('proxy_pass http://localhost:8150/;') - - expect(content).to include('location /-/kubernetes-agent/k8s-proxy/ {') - expect(content).to include('proxy_pass http://localhost:8154/;') - } - end - end - - context 'when hsts is disabled' do - before do - stub_gitlab_rb(nginx: { hsts_max_age: 0 }) - end - it { is_expected.not_to render_file(gitlab_http_config).with_content(/add_header Strict-Transport-Security/) } - end - - it { is_expected.to render_file(gitlab_http_config).with_content(/add_header Strict-Transport-Security "max-age=63072000";/) } - - context 'when referrer_policy is disabled' do - before do - stub_gitlab_rb(nginx: { referrer_policy: false }) - end - - it { is_expected.not_to render_file(gitlab_http_config).with_content(/add_header Referrer-Policy/) } - end - - context 'when referrer_policy is set to origin' do - before do - stub_gitlab_rb(nginx: { referrer_policy: 'origin' }) - end - - it { is_expected.to render_file(gitlab_http_config).with_content(/add_header Referrer-Policy origin;/) } - end - - it { is_expected.to render_file(gitlab_http_config).with_content(/add_header Referrer-Policy strict-origin-when-cross-origin;/) } - - context 'when gzip is disabled' do - before do - stub_gitlab_rb(nginx: { gzip_enabled: false }) - end - it { is_expected.to render_file(gitlab_http_config).with_content(/gzip off;/) } - end - - it { is_expected.to render_file(gitlab_http_config).with_content(/gzip on;/) } - - context 'when include_subdomains is enabled' do - before do - stub_gitlab_rb(nginx: { hsts_include_subdomains: true }) - end - - it { is_expected.to render_file(gitlab_http_config).with_content(/add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";/) } - end - - context 'when max-age is set to 10' do - before do - stub_gitlab_rb(nginx: { hsts_max_age: 10 }) - end - - it { is_expected.to render_file(gitlab_http_config).with_content(/"max-age=10[^"]*"/) } - end - - context 'when error log level is set to debug' do - before do - stub_gitlab_rb(nginx: { error_log_level: 'debug' }) - end - it { is_expected.to render_file(gitlab_http_config).with_content(/error_log \/var\/log\/gitlab\/nginx\/gitlab_error.log debug;/) } - end - - it { is_expected.to render_file(gitlab_http_config).with_content(/error_log \/var\/log\/gitlab\/nginx\/gitlab_error.log error;/) } - - context 'when NGINX RealIP module is configured' do - before do - stub_gitlab_rb( - external_url: 'https://localhost', - mattermost_external_url: 'https://mattermost.localhost', - registry_external_url: 'https://registry.localhost', - pages_external_url: 'https://pages.localhost' - ) - end - - context 'when real_ip_header is configured' do - before do - stub_gitlab_rb( - nginx: { real_ip_header: 'X-FAKE' }, - mattermost_nginx: { real_ip_header: 'X-FAKE' }, - registry_nginx: { real_ip_header: 'X-FAKE' }, - pages_nginx: { real_ip_header: 'X-FAKE' } - ) - end - - it 'populates all config with real_ip_header' do - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content(/real_ip_header X-FAKE/) - end - end - end - - context 'when real_ip_recursive is configured' do - before do - stub_gitlab_rb( - nginx: { real_ip_recursive: 'On' }, - mattermost_nginx: { real_ip_recursive: 'On' }, - registry_nginx: { real_ip_recursive: 'On' }, - pages_nginx: { real_ip_recursive: 'On' } - ) - end - - it 'populates all config with real_up_recursive' do - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content(/real_ip_recursive On/) - end - end - end - - context 'when real_ip_trusted_addresses is configured' do - before do - stub_gitlab_rb( - nginx: { real_ip_trusted_addresses: %w(one two three) }, - mattermost_nginx: { real_ip_trusted_addresses: %w(one two three) }, - registry_nginx: { real_ip_trusted_addresses: %w(one two three) }, - pages_nginx: { real_ip_trusted_addresses: %w(one two three) } - ) - end - - it 'populates all config with all items for real_ip_trusted_addresses' do - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content { |content| - expect(content).to match(/set_real_ip_from one/) - expect(content).to match(/set_real_ip_from two/) - expect(content).to match(/set_real_ip_from three/) - } - end - end - end - - context 'when proxy_protocol is enabled' do - before do - stub_gitlab_rb( - nginx: { proxy_protocol: true }, - mattermost_nginx: { proxy_protocol: true }, - registry_nginx: { proxy_protocol: true }, - pages_nginx: { proxy_protocol: true } - ) - end - - it 'applies nginx proxy_protocol settings' do - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content { |content| - expect(content).to match(/listen .*:\d+ proxy_protocol/) - expect(content).to include('real_ip_header proxy_protocol;') - expect(content).to include('proxy_set_header X-Real-IP $proxy_protocol_addr;') - expect(content).to include('proxy_set_header X-Forwarded-For $proxy_protocol_addr;') - } - end - end - end - - it 'does not set proxy_protocol settings by default' do - http_conf.each_value do |conf| - expect(chef_run).to render_file(conf).with_content { |content| - expect(content).not_to match(/listen .*:\d+ proxy_protocol/) - expect(content).not_to include('real_ip_header proxy_protocol;') - expect(content).not_to include('proxy_set_header X-Real-IP $proxy_protocol_addr;') - expect(content).not_to include('proxy_set_header X-Forwarded-For $proxy_protocol_addr;') - } - end - end - end - - context 'for proxy_custom_buffer_size' do - before do - stub_gitlab_rb( - external_url: 'https://localhost', - mattermost_external_url: 'https://mattermost.localhost', - pages_external_url: 'https://pages.localhost' - ) - end - - context 'when proxy_custom_buffer_size is set' do - before do - stub_gitlab_rb( - nginx: { proxy_custom_buffer_size: '42k' }, - mattermost_nginx: { proxy_custom_buffer_size: '42k' }, - pages_nginx: { proxy_custom_buffer_size: '42k' } - ) - end - - it 'applies nginx proxy_custom_buffer_size settings' do - ['gitlab', 'mattermost', 'pages'].each do |conf| - expect(chef_run).to render_file(http_conf[conf]).with_content { |content| - expect(content).to include('proxy_buffers 8 42k;') - expect(content).to include('proxy_buffer_size 42k;') - } - end - end - end - - it 'does not set proxy_custom_buffer_size by default' do - ['gitlab', 'mattermost', 'pages'].each do |conf| - expect(chef_run).to render_file(http_conf[conf]).with_content { |content| - expect(content).not_to include('proxy_buffers 8 42k;') - expect(content).not_to include('proxy_buffer_size 42k;') - } - end - end - end - - include_examples "consul service discovery", "nginx", "nginx" - - describe 'logrotate settings' do - context 'default values' do - it_behaves_like 'configured logrotate service', 'nginx', 'root', 'root' - end - - context 'specified username and group' do - before do - stub_gitlab_rb( - web_server: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'configured logrotate service', 'nginx', 'root', 'root' - end - end - - def nginx_headers(additional_headers) - basic_nginx_headers.merge(additional_headers) - end -end diff --git a/spec/chef/recipes/postgresql_spec.rb b/spec/chef/recipes/postgresql_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvcG9zdGdyZXNxbF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/postgresql_spec.rb +++ /dev/null @@ -1,880 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'postgresql' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config database_objects)).converge('gitlab::default') } - let(:postgresql_data_dir) { '/var/opt/gitlab/postgresql/data' } - let(:postgresql_ssl_cert) { File.join(postgresql_data_dir, 'server.crt') } - let(:postgresql_ssl_key) { File.join(postgresql_data_dir, 'server.key') } - let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') } - let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' } - let(:pg_hba_conf) { '/var/opt/gitlab/postgresql/data/pg_hba.conf' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('best_version')) - allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('best_version')) - allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('best_version')) - end - - it 'includes the postgresql::bin recipe' do - expect(chef_run).to include_recipe('postgresql::bin') - end - - it 'includes the postgresql::user recipe' do - expect(chef_run).to include_recipe('postgresql::user') - end - - it 'includes postgresql::sysctl recipe' do - expect(chef_run).to include_recipe('postgresql::sysctl') - end - - it 'does not warn the user that a restart is needed by default' do - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - expect(chef_run).not_to run_ruby_block('warn pending postgresql restart') - end - - it 'includes runtime.conf in postgresql.conf' do - expect(chef_run).to render_file(postgresql_conf) - .with_content(/include 'runtime.conf'/) - end - - context 'with default settings' do - it_behaves_like 'enabled runit service', 'postgresql', 'root', 'root', 'gitlab-psql', 'gitlab-psql' - - context 'when rendering postgresql.conf' do - it 'correctly sets the shared_preload_libraries default setting' do - expect(chef_run.node['postgresql']['shared_preload_libraries']) - .to be_nil - - expect(chef_run).to render_file(postgresql_conf) - .with_content(/shared_preload_libraries = ''/) - end - - it 'disables archive mode' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/archive_mode = off/) - end - - it 'enables SSL by default' do - expect(chef_run.node['postgresql']['ssl']) - .to eq('on') - - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/ssl = on/) - end - - it 'sets the default SSL cipher list' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(%r{ssl_ciphers = 'HIGH:MEDIUM:\+3DES:!aNULL:!SSLv3:!TLSv1'}) - end - - it 'sets the default locations of SSL certificates' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/ssl_cert_file = 'server.crt'/) - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/ssl_key_file = 'server.key'/) - expect(chef_run).to render_file( - postgresql_conf - ).with_content(%r{ssl_ca_file = '/opt/gitlab/embedded/ssl/certs/cacert.pem'}) - end - - it 'leaves synchronous_standby_names empty' do - expect(chef_run.node['postgresql']['synchronous_standby_names']) - .to eq('') - - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/synchronous_standby_names = ''/) - end - - it 'disables wal_log_hints setting' do - expect(chef_run.node['postgresql']['wal_log_hints']).to eq('off') - - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/wal_log_hints = off/) - end - - it 'does not set dynamic_shared_memory_type by default' do - expect(chef_run).not_to render_file( - postgresql_conf - ).with_content(/^dynamic_shared_memory_type = /) - end - - it 'sets the max_locks_per_transaction setting' do - expect(chef_run.node['postgresql']['max_locks_per_transaction']) - .to eq(128) - - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/max_locks_per_transaction = 128/) - end - - it 'does not include gitlab-geo.conf' do - expect(chef_run).to render_file(postgresql_conf) - .with_content { |content| - expect(content).not_to match(/include_if_exists 'gitlab-geo.conf'/) - } - end - end - - it 'generates a self-signed SSL certificate and key' do - stub_gitlab_rb(postgresql: { ssl_cert_file: 'certfile', ssl_key_file: 'keyfile' }) - - absolute_cert_path = File.join(postgresql_data_dir, 'certfile') - absolute_key_path = File.join(postgresql_data_dir, 'keyfile') - - expect(chef_run).to create_file(absolute_cert_path).with( - user: 'gitlab-psql', - group: 'gitlab-psql', - mode: 0400 - ) - - expect(chef_run).to create_file(absolute_key_path).with( - user: 'gitlab-psql', - group: 'gitlab-psql', - mode: 0400 - ) - - expect(chef_run).to render_file(absolute_cert_path) - .with_content(/-----BEGIN CERTIFICATE-----/) - expect(chef_run).to render_file(absolute_key_path) - .with_content(/-----BEGIN RSA PRIVATE KEY-----/) - end - - context 'when rendering runtime.conf' do - it 'correctly sets the log_line_prefix default setting' do - expect(chef_run.node['postgresql']['log_line_prefix']) - .to be_nil - - expect(chef_run).to render_file(runtime_conf) - .with_content(/log_line_prefix = ''/) - end - - it 'does not include log_statement by default' do - expect(chef_run).not_to render_file(runtime_conf) - .with_content(/log_statement = /) - end - - it 'sets max_standby settings' do - expect(chef_run).to render_file( - runtime_conf - ).with_content(/max_standby_archive_delay = 30s/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/max_standby_streaming_delay = 30s/) - end - - it 'sets archive settings' do - expect(chef_run).to render_file( - runtime_conf - ).with_content(/archive_command = ''/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/archive_timeout = 0/) - end - - it 'sets logging directory' do - expect(chef_run.node['postgresql']['log_directory']) - .to eq('/var/log/gitlab/postgresql') - - expect(chef_run).to render_file( - runtime_conf - ).with_content(%r(^log_directory = '/var/log/gitlab/postgresql')) - end - - it 'disables hot_standby_feedback' do - expect(chef_run.node['postgresql']['hot_standby_feedback']) - .to eq('off') - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/hot_standby_feedback = off/) - end - - it 'sets the random_page_cost setting' do - expect(chef_run.node['postgresql']['random_page_cost']) - .to eq(2.0) - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/random_page_cost = 2\.0/) - end - - it 'sets the log_temp_files setting' do - expect(chef_run.node['postgresql']['log_temp_files']) - .to eq(-1) - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/log_temp_files = -1/) - end - - it 'disables the log_checkpoints setting' do - expect(chef_run.node['postgresql']['log_checkpoints']) - .to eq('off') - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/log_checkpoints = off/) - end - - it 'sets idle_in_transaction_session_timeout' do - expect(chef_run.node['postgresql']['idle_in_transaction_session_timeout']) - .to eq('60000') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/idle_in_transaction_session_timeout = 60000/) - end - - it 'sets effective_io_concurrency' do - expect(chef_run.node['postgresql']['effective_io_concurrency']) - .to eq(1) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/effective_io_concurrency = 1/) - end - - it 'sets max_worker_processes' do - expect(chef_run.node['postgresql']['max_worker_processes']) - .to eq(8) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/max_worker_processes = 8/) - end - - it 'sets max_parallel_workers_per_gather' do - expect(chef_run.node['postgresql']['max_parallel_workers_per_gather']) - .to eq(0) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/max_parallel_workers_per_gather = 0/) - end - - it 'sets log_lock_waits' do - expect(chef_run.node['postgresql']['log_lock_waits']) - .to eq(1) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/log_lock_waits = 1/) - end - - it 'sets deadlock_timeout' do - expect(chef_run.node['postgresql']['deadlock_timeout']) - .to eq('5s') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/deadlock_timeout = '5s'/) - end - - it 'disables track_io_timing' do - expect(chef_run.node['postgresql']['track_io_timing']) - .to eq('off') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/track_io_timing = 'off'/) - end - - it 'sets default_statistics_target' do - expect(chef_run.node['postgresql']['default_statistics_target']) - .to eq(1000) - - expect(chef_run).to render_file(runtime_conf) - .with_content(/default_statistics_target = 1000/) - end - - it 'enables the synchronous_commit setting' do - expect(chef_run.node['postgresql']['synchronous_commit']) - .to eq('on') - - expect(chef_run).to render_file( - runtime_conf - ).with_content(/synchronous_commit = on/) - end - end - - context 'when rendering pg_hba.conf' do - it 'creates a standard pg_hba.conf' do - expect(chef_run).to render_file(pg_hba_conf) - .with_content('local all all peer map=gitlab') - end - - it 'cert authentication is disabled by default' do - expect(chef_run).to render_file(pg_hba_conf).with_content { |content| - expect(content).to_not match(/cert$/) - } - end - end - end - - context 'with user specified settings' do - before do - stub_gitlab_rb(postgresql: { - shared_preload_libraries: 'pg_stat_statements', - archive_mode: 'on', - username: 'foo', - group: 'bar', - ssl: 'off', - ssl_crl_file: 'revoke.crl', - ssl_ciphers: 'ALL', - log_destination: 'csvlog', - logging_collector: 'on', - log_filename: 'test.log', - log_file_mode: '0600', - log_truncate_on_rotation: 'on', - log_rotation_age: '1d', - log_rotation_size: '10MB', - dynamic_shared_memory_type: 'none', - wal_log_hints: 'on' - }, - geo_secondary_role: { - enable: true - }) - end - - it_behaves_like 'enabled runit service', 'postgresql', 'root', 'root', 'foo', 'bar' - - context 'when rendering postgresql.conf' do - it 'correctly sets the shared_preload_libraries setting' do - expect(chef_run.node['postgresql']['shared_preload_libraries']) - .to eql('pg_stat_statements') - - expect(chef_run).to render_file(postgresql_conf) - .with_content(/shared_preload_libraries = 'pg_stat_statements'/) - end - - it 'enables archive mode' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/archive_mode = on/) - end - - it 'disables SSL' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/ssl = off/) - - expect(chef_run).not_to render_file(postgresql_ssl_cert) - expect(chef_run).not_to render_file(postgresql_ssl_key) - end - - it 'sets the certificate revocation list' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/ssl_crl_file = 'revoke.crl'/) - end - - it 'sets the SSL cipher list' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/ssl_ciphers = 'ALL'/) - end - - it 'includes gitlab-geo.conf in postgresql.conf' do - expect(chef_run).to render_file(postgresql_conf) - .with_content(/include_if_exists 'gitlab-geo.conf'/) - end - - it 'sets user specified logging parameters' do - expect(chef_run).to render_file(postgresql_conf).with_content { |content| - expect(content).to match(/logging_collector = on/) - } - - expect(chef_run).to render_file(runtime_conf).with_content { |content| - expect(content).to match(/log_destination = 'csvlog'/) - expect(content).to match(/log_filename = 'test.log'/) - expect(content).to match(/log_file_mode = 0600/) - expect(content).to match(/log_truncate_on_rotation = on/) - expect(content).to match(/log_rotation_age = 1d/) - expect(content).to match(/log_rotation_size = 10MB/) - } - end - - it 'sets the dynamic_shared_memory_type' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/^dynamic_shared_memory_type = none/) - end - - it 'enables wal_log_hints' do - expect(chef_run).to render_file( - postgresql_conf - ).with_content(/^wal_log_hints = on/) - end - end - - context 'when rendering runtime.conf' do - before do - stub_gitlab_rb(postgresql: { - log_line_prefix: '%a', - log_statement: 'all', - max_standby_archive_delay: '60s', - max_standby_streaming_delay: '120s', - archive_command: 'command', - archive_timeout: '120', - }) - end - - it 'correctly sets the log_line_prefix setting' do - expect(chef_run.node['postgresql']['log_line_prefix']) - .to eql('%a') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/log_line_prefix = '%a'/) - end - - it 'correctly sets the log_statement setting' do - expect(chef_run.node['postgresql']['log_statement']) - .to eql('all') - - expect(chef_run).to render_file(runtime_conf) - .with_content(/log_statement = 'all'/) - end - - it 'sets max_standby settings' do - expect(chef_run).to render_file( - runtime_conf - ).with_content(/max_standby_archive_delay = 60s/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/max_standby_streaming_delay = 120s/) - end - - it 'sets archive settings' do - expect(chef_run).to render_file( - runtime_conf - ).with_content(/archive_command = 'command'/) - expect(chef_run).to render_file( - runtime_conf - ).with_content(/archive_timeout = 120/) - end - end - - context 'when rendering pg_hba.conf' do - before do - stub_gitlab_rb( - postgresql: { - hostssl: true, - trust_auth_cidr_addresses: ['127.0.0.1/32'], - custom_pg_hba_entries: { - foo: [ - { - type: 'host', - database: 'foo', - user: 'bar', - cidr: '127.0.0.1/32', - method: 'trust' - } - ] - }, - cert_auth_addresses: { - '1.2.3.4/32' => { - database: 'fakedatabase', - user: 'fakeuser' - }, - 'fakehostname' => { - database: 'anotherfakedatabase', - user: 'anotherfakeuser' - }, - } - } - ) - end - - it 'prefers hostssl when configured in pg_hba.conf' do - expect(chef_run).to render_file(pg_hba_conf) - .with_content('hostssl all all 127.0.0.1/32 trust') - end - - it 'adds users custom entries to pg_hba.conf' do - expect(chef_run).to render_file(pg_hba_conf) - .with_content('host foo bar 127.0.0.1/32 trust') - end - - it 'allows cert authentication to be enabled' do - expect(chef_run).to render_file(pg_hba_conf).with_content('hostssl fakedatabase fakeuser 1.2.3.4/32 cert') - expect(chef_run).to render_file(pg_hba_conf).with_content('hostssl anotherfakedatabase anotherfakeuser fakehostname cert') - end - end - end - - context 'when postgresql.conf changes' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original - allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('postgresql').and_return(true) - allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_call_original - allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).with('postgresql').and_return(true) - end - - it 'notifies reload postgresql task' do - expect(chef_run).to create_postgresql_config('gitlab') - postgresql_config = chef_run.postgresql_config('gitlab') - expect(postgresql_config).to notify('execute[reload postgresql]').to(:run).immediately - expect(postgresql_config).to notify('execute[start postgresql]').to(:run).immediately - end - end - - context 'when enabling extensions' do - it 'creates the pg_trgm extension when it is possible' do - allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('pg_trgm', 'gitlabhq_production').and_return(true) - expect(chef_run).to enable_postgresql_extension('pg_trgm') - end - - it 'does not create the pg_trgm extension if it is not possible' do - allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('pg_trgm', 'gitlabhq_production').and_return(false) - expect(chef_run).not_to run_execute('enable pg_trgm extension') - end - - context 'when on a secondary database node' do - before do - allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:replica?).and_return(true) - end - - it 'should not activate pg_trgm' do - expect(chef_run).not_to run_execute('enable pg_trgm extension') - end - end - - it 'creates the btree_gist extension when it is possible' do - allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('btree_gist', 'gitlabhq_production').and_return(true) - expect(chef_run).to enable_postgresql_extension('btree_gist') - end - - it 'does not create the btree_gist extension if it is not possible' do - allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('btree_gist', 'gitlabhq_production').and_return(false) - expect(chef_run).not_to run_execute('enable btree_gist extension') - end - end - - context 'when configuring postgresql_user resources' do - before do - stub_gitlab_rb( - { - postgresql: { - sql_user_password: 'fakepassword', - sql_replication_password: 'fakepassword' - } - } - ) - end - - context 'when on the primary database node' do - before do - allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(false) - end - - it 'should set a password for sql_user when sql_user_password is set' do - expect(chef_run).to create_postgresql_user('gitlab').with(password: 'md5fakepassword') - end - - it 'should create the gitlab_replicator user with replication permissions' do - expect(chef_run).to create_postgresql_user('gitlab_replicator').with( - options: %w(replication), - password: 'md5fakepassword' - ) - end - end - - context 'when on a secondary database node' do - before do - allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(true) - allow_any_instance_of(PgHelper).to receive(:replica?).and_return(true) - end - - it 'should not create users' do - expect(chef_run).not_to create_postgresql_user('gitlab') - expect(chef_run).not_to create_postgresql_user('gitlab_replicator') - end - end - end -end - -RSpec.describe 'postgresql 13' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config)).converge('gitlab::default') } - let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') } - let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' } - - before do - allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('13.0')) - allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('13.0')) - end - - it 'configures wal_keep_size instead of wal_keep_segments' do - expect(chef_run).to render_file(runtime_conf).with_content { |content| - expect(content).to include("wal_keep_size") - expect(content).not_to include("wal_keep_segments") - } - end -end - -RSpec.describe 'postgresql 12' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config)).converge('gitlab::default') } - let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') } - let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' } - - before do - allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('12.0')) - allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('12.0')) - end - - it 'configures wal_keep_segments instead of wal_keep_size' do - expect(chef_run).to render_file(runtime_conf).with_content { |content| - expect(content).to include("wal_keep_segments") - expect(content).to_not include("wal_keep_size") - } - end -end - -RSpec.describe 'postgres when version mismatches occur' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config)).converge('gitlab::default') } - let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') } - let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' } - - context 'when data and binary versions differ' do - before do - allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('expectation')) - allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('expectation')) - allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('reality')) - allow(File).to receive(:exists?).and_call_original - allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true) - allow(Dir).to receive(:glob).and_call_original - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/reality*").and_return( - ['/opt/gitlab/embedded/postgresql/reality'] - ) - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/reality/bin/*").and_return( - %w( - /opt/gitlab/embedded/postgresql/reality/bin/foo_one - /opt/gitlab/embedded/postgresql/reality/bin/foo_two - /opt/gitlab/embedded/postgresql/reality/bin/foo_three - ) - ) - end - - it 'corrects symlinks to the correct location' do - allow(FileUtils).to receive(:ln_sf).and_return(true) - %w(foo_one foo_two foo_three).each do |pg_bin| - expect(FileUtils).to receive(:ln_sf).with( - "/opt/gitlab/embedded/postgresql/reality/bin/#{pg_bin}", - "/opt/gitlab/embedded/bin/#{pg_bin}" - ) - end - chef_run.ruby_block('Link postgresql bin files to the correct version').block.call - end - - it 'does not warn the user that a restart is needed by default' do - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - expect(chef_run).not_to run_ruby_block('warn pending postgresql restart') - end - end - - context 'when running version and installed version differ' do - before do - allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('expectation')) - allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('reality')) - end - - it 'warns the user that a restart is needed' do - allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true) - expect(chef_run).to run_ruby_block('warn pending postgresql restart') - end - - it 'does not warns the user that a restart is needed when postgres is stopped' do - expect(chef_run).not_to run_ruby_block('warn pending postgresql restart') - end - end - - context 'when an older data version is present and no longer used' do - before do - allow(Gitlab).to receive(:[]).and_call_original - allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('new_shiny')) - allow_any_instance_of(PGVersion).to receive(:major).and_return('new_shiny') - allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('new_shiny')) - allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('ancient_history')) - allow(File).to receive(:exists?).and_call_original - allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true) - allow(Dir).to receive(:glob).and_call_original - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/ancient_history*").and_return([]) - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/new_shiny*").and_return( - ['/opt/gitlab/embedded/postgresql/new_shiny'] - ) - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/new_shiny/bin/*").and_return( - %w( - /opt/gitlab/embedded/postgresql/new_shiny/bin/foo_one - /opt/gitlab/embedded/postgresql/new_shiny/bin/foo_two - /opt/gitlab/embedded/postgresql/new_shiny/bin/foo_three - ) - ) - end - - it 'corrects symlinks to the correct location' do - allow(FileUtils).to receive(:ln_sf).and_return(true) - %w(foo_one foo_two foo_three).each do |pg_bin| - expect(FileUtils).to receive(:ln_sf).with( - "/opt/gitlab/embedded/postgresql/new_shiny/bin/#{pg_bin}", - "/opt/gitlab/embedded/bin/#{pg_bin}" - ) - end - chef_run.ruby_block('Link postgresql bin files to the correct version').block.call - end - end - - context 'when the expected postgres version is missing' do - before do - allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('how_it_started')) - allow(File).to receive(:exists?).and_call_original - allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true) - allow(Dir).to receive(:glob).and_call_original - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/how_it_started*").and_return([]) - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/how_it_is_going*").and_return([]) - end - - it 'throws an error' do - expect do - chef_run.ruby_block('Link postgresql bin files to the correct version').block.call - end.to raise_error(RuntimeError, /Could not find PostgreSQL binaries/) - end - end -end - -RSpec.describe 'postgresql::bin' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - let(:gitlab_psql_rc) do - <<-EOF -psql_user='gitlab-psql' -psql_group='gitlab-psql' -psql_host='/var/opt/gitlab/postgresql' -psql_port='5432' - EOF - end - - before do - allow(Gitlab). to receive(:[]).and_call_original - end - - context 'when bundled postgresql is disabled' do - before do - stub_gitlab_rb( - postgresql: { - enable: false - } - ) - - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/PG_VERSION').and_return(false) - - allow_any_instance_of(PgHelper).to receive(:database_version).and_return(nil) - version = double("PgHelper", major: 10, minor: 9) - allow_any_instance_of(PgHelper).to receive(:version).and_return(version) - end - - it 'still includes the postgresql::bin recipe' do - expect(chef_run).to include_recipe('postgresql::bin') - end - - it 'includes postgresql::directory_locations' do - expect(chef_run).to include_recipe('postgresql::directory_locations') - end - - it 'creates gitlab-psql-rc' do - expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-psql-rc') - .with_content(gitlab_psql_rc) - end - - # We do expect the ruby block to run, but nothing to be found - it "doesn't link any files by default" do - expect(FileUtils).to_not receive(:ln_sf) - end - - context "with postgresql['version'] set" do - before do - stub_gitlab_rb( - postgresql: { - enable: false, - version: '999' - } - ) - allow(Dir).to receive(:glob).and_call_original - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/999*").and_return( - %w( - /opt/gitlab/embedded/postgresql/999 - ) - ) - allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/999/bin/*").and_return( - %w( - /opt/gitlab/embedded/postgresql/999/bin/foo_one - /opt/gitlab/embedded/postgresql/999/bin/foo_two - /opt/gitlab/embedded/postgresql/999/bin/foo_three - ) - ) - end - - it "doesn't print a warning with a valid postgresql version" do - expect(chef_run).to_not run_ruby_block('check_postgresql_version') - end - - it 'links the specified version' do - allow(FileUtils).to receive(:ln_sf).and_return(true) - %w(foo_one foo_two foo_three).each do |pg_bin| - expect(FileUtils).to receive(:ln_sf).with( - "/opt/gitlab/embedded/postgresql/999/bin/#{pg_bin}", - "/opt/gitlab/embedded/bin/#{pg_bin}" - ) - end - chef_run.ruby_block('Link postgresql bin files to the correct version').block.call - end - end - - context "with an invalid version in postgresql['version']" do - before do - stub_gitlab_rb( - postgresql: { - enable: false, - version: '888' - } - ) - allow(Dir).to receive(:glob).and_call_original - allow(Dir).to receive(:glob).with('/opt/gitlab/embedded/postgresql/888*').and_return([]) - end - - it 'should print a warning' do - expect(chef_run).to run_ruby_block('check_postgresql_version') - end - end - end -end - -RSpec.describe 'default directories' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'postgresql directory' do - context 'with default settings' do - it 'creates postgresql directory' do - expect(chef_run).to create_directory('/var/opt/gitlab/postgresql').with(owner: 'gitlab-psql', mode: '0755', recursive: true) - end - end - - context 'with custom settings' do - before do - stub_gitlab_rb( - postgresql: { - dir: '/mypgdir', - home: '/mypghomedir' - }) - end - - it 'creates postgresql directory with custom path' do - expect(chef_run).to create_directory('/mypgdir').with(owner: 'gitlab-psql', mode: '0755', recursive: true) - end - end - end -end diff --git a/spec/chef/recipes/praefect_spec.rb b/spec/chef/recipes/praefect_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvcHJhZWZlY3Rfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/praefect_spec.rb +++ /dev/null @@ -1,303 +0,0 @@ -require 'chef_helper' -RSpec.describe 'praefect' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service env_dir)).converge('gitlab::default') } - let(:prometheus_grpc_latency_buckets) do - '[0.001, 0.005, 0.025, 0.1, 0.5, 1.0, 10.0, 30.0, 60.0, 300.0, 1500.0]' - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when the defaults are used' do - it_behaves_like 'disabled runit service', 'praefect' - end - - context 'when praefect is enabled' do - let(:config_path) { '/var/opt/gitlab/praefect/config.toml' } - let(:env_dir) { '/opt/gitlab/etc/praefect/env' } - let(:auth_transitioning) { false } - - before do - stub_gitlab_rb(praefect: { - enable: true, - auth_transitioning: auth_transitioning, - }) - end - - it 'creates expected directories with correct permissions' do - expect(chef_run).to create_directory('/var/opt/gitlab/praefect').with(user: 'git', mode: '0700') - end - - it 'creates a default VERSION file and sends hup to service' do - expect(chef_run).to create_version_file('Create Praefect version file').with( - version_file_path: '/var/opt/gitlab/praefect/VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/praefect --version' - ) - - expect(chef_run.version_file('Create Praefect version file')).to notify('runit_service[praefect]').to(:hup) - end - - it 'renders the config.toml' do - rendered = { - 'auth' => { 'token' => '', 'transitioning' => false }, - 'listen_addr' => 'localhost:2305', - 'logging' => { 'format' => 'json' }, - 'prometheus_listen_addr' => 'localhost:9652', - 'prometheus_exclude_database_from_default_metrics' => true, - 'sentry' => {}, - 'database' => { - 'session_pooled' => {}, - }, - 'reconciliation' => {}, - 'failover' => { 'enabled' => true } - } - - expect(chef_run).to render_file(config_path).with_content { |content| - expect(Tomlrb.parse(content)).to eq(rendered) - } - expect(chef_run).not_to render_file(config_path) - .with_content(%r{\[prometheus\]\s+grpc_latency_buckets =}) - end - - it 'renders the env dir files' do - expect(chef_run).to render_file(File.join(env_dir, "GITALY_PID_FILE")) - .with_content('/var/opt/gitlab/praefect/praefect.pid') - expect(chef_run).to render_file(File.join(env_dir, "WRAPPER_JSON_LOGGING")) - .with_content('true') - expect(chef_run).to render_file(File.join(env_dir, "SSL_CERT_DIR")) - .with_content('/opt/gitlab/embedded/ssl/certs/') - end - - it 'renders the service run file with wrapper' do - expect(chef_run).to render_file('/opt/gitlab/sv/praefect/run') - .with_content('/opt/gitlab/embedded/bin/gitaly-wrapper /opt/gitlab/embedded/bin/praefect') - .with_content('exec chpst -e /opt/gitlab/etc/praefect/env') - end - - context 'with custom settings' do - let(:dir) { nil } - let(:socket_path) { '/var/opt/gitlab/praefect/praefect.socket' } - let(:auth_token) { 'secrettoken123' } - let(:auth_transitioning) { false } - let(:sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project' } - let(:sentry_environment) { 'production' } - let(:listen_addr) { 'localhost:4444' } - let(:tls_listen_addr) { 'localhost:5555' } - let(:certificate_path) { '/path/to/cert.pem' } - let(:key_path) { '/path/to/key.pem' } - let(:prom_addr) { 'localhost:1234' } - let(:separate_database_metrics) { false } - let(:log_level) { 'debug' } - let(:log_format) { 'text' } - let(:primaries) { %w[praefect1 praefect2] } - let(:virtual_storages) do - { - 'default' => { - 'default_replication_factor' => 2, - 'nodes' => { - 'praefect1' => { address: 'tcp://node1.internal', token: "praefect1-token" }, - 'praefect2' => { address: 'tcp://node2.internal', token: "praefect2-token" }, - 'praefect3' => { address: 'tcp://node3.internal', token: "praefect3-token" }, - 'praefect4' => { address: 'tcp://node4.internal', token: "praefect4-token" } - }, - 'praefect5' => { address: 'tcp://node5.internal', token: "praefect5-token" } - } - } - end - let(:failover_enabled) { true } - let(:database_host) { 'pg.external' } - let(:database_port) { 2234 } - let(:database_user) { 'praefect-pg' } - let(:database_password) { 'praefect-pg-pass' } - let(:database_dbname) { 'praefect_production' } - let(:database_sslmode) { 'require' } - let(:database_sslcert) { '/path/to/client-cert' } - let(:database_sslkey) { '/path/to/client-key' } - let(:database_sslrootcert) { '/path/to/rootcert' } - let(:database_sslrootcert) { '/path/to/rootcert' } - let(:database_direct_host) { 'pg.internal' } - let(:database_direct_port) { 1234 } - let(:reconciliation_scheduling_interval) { '1m' } - let(:reconciliation_histogram_buckets) { '[1.0, 2.0]' } - - before do - stub_gitlab_rb(praefect: { - enable: true, - dir: dir, - socket_path: socket_path, - auth_token: auth_token, - auth_transitioning: auth_transitioning, - sentry_dsn: sentry_dsn, - sentry_environment: sentry_environment, - listen_addr: listen_addr, - tls_listen_addr: tls_listen_addr, - certificate_path: certificate_path, - key_path: key_path, - prometheus_listen_addr: prom_addr, - prometheus_grpc_latency_buckets: prometheus_grpc_latency_buckets, - separate_database_metrics: separate_database_metrics, - logging_level: log_level, - logging_format: log_format, - failover_enabled: failover_enabled, - virtual_storages: virtual_storages, - database_host: database_host, - database_port: database_port, - database_user: database_user, - database_password: database_password, - database_dbname: database_dbname, - database_sslmode: database_sslmode, - database_sslcert: database_sslcert, - database_sslkey: database_sslkey, - database_sslrootcert: database_sslrootcert, - database_direct_host: database_direct_host, - database_direct_port: database_direct_port, - reconciliation_scheduling_interval: reconciliation_scheduling_interval, - reconciliation_histogram_buckets: reconciliation_histogram_buckets, - }) - end - - it 'renders the config.toml' do - expect(chef_run).to render_file(config_path).with_content { |content| - expect(Tomlrb.parse(content)).to eq( - { - 'auth' => { - 'token' => 'secrettoken123', - 'transitioning' => false - }, - 'database' => { - 'dbname' => 'praefect_production', - 'host' => 'pg.external', - 'password' => 'praefect-pg-pass', - 'port' => 2234, - 'sslcert' => '/path/to/client-cert', - 'sslkey' => '/path/to/client-key', - 'sslmode' => 'require', - 'sslrootcert' => '/path/to/rootcert', - 'user' => 'praefect-pg', - 'session_pooled' => { - 'host' => 'pg.internal', - 'port' => 1234, - } - }, - 'failover' => { - 'enabled' => true - }, - 'logging' => { - 'format' => 'text', - 'level' => 'debug' - }, - 'listen_addr' => 'localhost:4444', - 'prometheus' => { - 'grpc_latency_buckets' => [0.001, 0.005, 0.025, 0.1, 0.5, 1.0, 10.0, 30.0, 60.0, 300.0, 1500.0] - }, - 'reconciliation' => { - 'histogram_buckets' => [1.0, 2.0], - 'scheduling_interval' => '1m' - }, - 'sentry' => { - 'sentry_dsn' => 'https://my_key:my_secret@sentry.io/test_project', - 'sentry_environment' => 'production' - }, - 'prometheus_listen_addr' => 'localhost:1234', - 'prometheus_exclude_database_from_default_metrics' => false, - 'socket_path' => '/var/opt/gitlab/praefect/praefect.socket', - 'tls' => { - 'certificate_path' => '/path/to/cert.pem', - 'key_path' => '/path/to/key.pem' - }, - 'tls_listen_addr' => 'localhost:5555', - 'virtual_storage' => [ - { - 'name' => 'default', - 'default_replication_factor' => 2, - 'node' => [ - { - 'address' => 'tcp://node1.internal', - 'storage' => 'praefect1', - 'token' => 'praefect1-token' - }, - { - 'address' => 'tcp://node2.internal', - 'storage' => 'praefect2', - 'token' => 'praefect2-token' - }, - { - 'address' => 'tcp://node3.internal', - 'storage' => 'praefect3', - 'token' => 'praefect3-token' - }, - { - 'address' => 'tcp://node4.internal', - 'storage' => 'praefect4', - 'token' => 'praefect4-token' - }, - { - 'address' => 'tcp://node5.internal', - 'storage' => 'praefect5', - 'token' => 'praefect5-token' - } - ] - } - ] - } - ) - } - end - - it 'renders the env dir files correctly' do - expect(chef_run).to render_file(File.join(env_dir, "WRAPPER_JSON_LOGGING")) - .with_content('false') - end - - context 'with virtual_storages as an array' do - let(:virtual_storages) { [{ name: 'default', 'nodes' => [{ storage: 'praefect1', address: 'tcp://node1.internal', token: "praefect1-token" }] }] } - - it 'raises an error' do - expect { chef_run }.to raise_error("Praefect virtual_storages must be a hash") - end - end - - context 'with duplicate virtual storage node configured via fallback' do - let(:virtual_storages) { { 'default' => { 'node-1' => {}, 'nodes' => { 'node-1' => {} } } } } - - it 'raises an error' do - allow(LoggingHelper).to receive(:deprecation) - expect(LoggingHelper).to receive(:deprecation).with( - <<~EOS - Configuring the Gitaly nodes directly in the virtual storage's root configuration object has - been deprecated in GitLab 13.12 and will no longer be supported in GitLab 15.0. Move the Gitaly - nodes under the 'nodes' key as described in step 6 of https://docs.gitlab.com/ee/administration/gitaly/praefect.html#praefect. - EOS - ) - - expect { chef_run }.to raise_error("Virtual storage 'default' contains duplicate configuration for node 'node-1'") - end - end - - context 'with nodes within virtual_storages as an array' do - let(:virtual_storages) { { 'default' => [{ storage: 'praefect1', address: 'tcp://node1.internal', token: "praefect1-token" }] } } - - it 'raises an error' do - expect { chef_run }.to raise_error("nodes of a Praefect virtual_storage must be a hash") - end - end - end - - describe 'database migrations' do - it 'runs the migrations' do - expect(chef_run).to run_bash('migrate praefect database') - end - - context 'with auto_migrate off' do - before { stub_gitlab_rb(praefect: { auto_migrate: false }) } - - it 'skips running the migrations' do - expect(chef_run).not_to run_bash('migrate praefect database') - end - end - end - - include_examples "consul service discovery", "praefect", "praefect" - end -end diff --git a/spec/chef/recipes/puma_spec.rb b/spec/chef/recipes/puma_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvcHVtYV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/puma_spec.rb +++ /dev/null @@ -1,260 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::puma with Ubuntu 16.04' do - let(:chef_run) do - runner = ChefSpec::SoloRunner.new( - step_into: %w(runit_service puma_config), - path: 'spec/fixtures/fauxhai/ubuntu/16.04.json' - ) - runner.converge('gitlab::default') - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_default_should_notify?(true) - stub_should_notify?('puma', true) - end - - context 'when puma is enabled' do - it_behaves_like 'enabled runit service', 'puma', 'root', 'root' - - describe 'logrotate settings' do - context 'default values' do - it_behaves_like 'configured logrotate service', 'puma', 'git', 'git' - end - - context 'specified username and group' do - before do - stub_gitlab_rb( - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'configured logrotate service', 'puma', 'foo', 'bar' - end - end - - it 'creates runtime directories' do - expect(chef_run).to create_directory('/var/log/gitlab/puma').with( - owner: 'git', - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/opt/gitlab/var/puma').with( - owner: 'git', - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/var/opt/gitlab/gitlab-rails/sockets').with( - owner: 'git', - group: 'gitlab-www', - mode: '0750' - ) - end - - it 'renders the runit configuration with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') - .with_content { |content| - expect(content).not_to match(/export prometheus_run_dir=\'\'/) - expect(content).not_to match(/rm \/run\/gitlab\/puma/) - expect(content).to match(/-u git:git/) - expect(content).to match(/-U git:git/) - expect(content).to match(/mkdir -p \/run\/gitlab\/puma/) - expect(content).to match(/chmod 0700 \/run\/gitlab\/puma/) - expect(content).to match(/chown git \/run\/gitlab\/puma/) - expect(content).to match(/export prometheus_run_dir=\'\/run\/gitlab\/puma\'/) - expect(content).to match(%r(/opt/gitlab/embedded/bin/bundle exec puma -C /var/opt/gitlab/gitlab-rails/etc/puma.rb)) - } - end - - it 'renders the puma.rb file' do - expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( - tag: 'gitlab-puma-worker', - rackup: 'config.ru', - environment: 'production', - pid: '/opt/gitlab/var/puma/puma.pid', - state_path: '/opt/gitlab/var/puma/puma.state', - listen_socket: '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket', - listen_tcp: '127.0.0.1:8080', - working_directory: '/var/opt/gitlab/gitlab-rails/working', - worker_processes: 2, - min_threads: 4, - max_threads: 4 - ) - expect(chef_run).to create_template('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| - expect(content).to match(/lowlevel_error_handler/) - expect(content).to include('Raven.capture_exception') - } - end - - it 'creates sysctl files' do - expect(chef_run).to create_gitlab_sysctl('net.core.somaxconn').with_value(1024) - end - end - - context 'with custom Puma settings' do - before do - stub_gitlab_rb( - puma: { - worker_timeout: 120, - worker_processes: 4, - min_threads: 5, - max_threads: 10, - listen: '10.0.0.1', - port: 9000, - socket: '/tmp/puma.socket', - state_path: '/tmp/puma.state', - per_worker_max_memory_mb: 1000 - } - ) - end - - it 'renders the puma.rb file' do - expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( - state_path: '/tmp/puma.state', - listen_socket: '/tmp/puma.socket', - listen_tcp: '10.0.0.1:9000', - worker_processes: 4, - min_threads: 5, - max_threads: 10, - per_worker_max_memory_mb: 1000 - ) - expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| - expect(content).to match(%r(-authSocket /tmp/puma.socket)) - } - expect(Gitlab['gitlab_workhorse']['auth_socket']).to eq('/tmp/puma.socket') - end - end - - context 'with custom user and group' do - before do - stub_gitlab_rb( - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'enabled runit service', 'puma', 'root', 'root' - end - - context 'with custom runtime_dir' do - before do - stub_gitlab_rb( - runtime_dir: '/tmp/test-dir' - ) - end - - it 'uses the user-specific runtime_dir' do - expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') - .with_content { |content| - expect(content).to match(%r(export prometheus_run_dir='/tmp/test-dir/gitlab/puma')) - expect(content).to match(%r(mkdir -p /tmp/test-dir/gitlab/puma)) - } - end - end - - context 'with ActionCable enabled' do - before do - stub_gitlab_rb( - actioncable: { - worker_pool_size: 7 - } - ) - end - - it 'renders the runit configuration with ActionCable environment variables' do - expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') - .with_content { |content| - expect(content).to match(/ACTION_CABLE_WORKER_POOL_SIZE=7/) - expect(content).to match(%r(/opt/gitlab/embedded/bin/bundle exec puma -C /var/opt/gitlab/gitlab-rails/etc/puma.rb)) - } - end - end - - include_examples "consul service discovery", "puma", "rails" -end - -RSpec.describe 'gitlab::puma Ubuntu 16.04 with no tmpfs' do - let(:chef_run) do - runner = ChefSpec::SoloRunner.new( - path: 'spec/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json', - step_into: %w(runit_service) - ) - runner.converge('gitlab::default') - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when puma is enabled on a node with no /run or /dev/shm tmpfs' do - it_behaves_like 'enabled runit service', 'puma', 'root', 'root' - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') - .with_content { |content| - expect(content).to match(/export prometheus_run_dir=\'\'/) - expect(content).not_to match(/mkdir -p \/run\/gitlab\/puma/) - } - end - end -end - -RSpec.describe 'gitlab::puma Ubuntu 16.04 Docker' do - let(:chef_run) do - runner = ChefSpec::SoloRunner.new( - path: 'spec/fixtures/fauxhai/ubuntu/16.04-docker.json', - step_into: %w(runit_service) - ) - runner.converge('gitlab::default') - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when puma is enabled on a node with a /dev/shm tmpfs' do - it_behaves_like 'enabled runit service', 'puma', 'root', 'root' - - it 'populates the files with expected configuration' do - expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') - .with_content { |content| - expect(content).to match(/export prometheus_run_dir=\'\/dev\/shm\/gitlab\/puma\'/) - expect(content).to match(/mkdir -p \/dev\/shm\/gitlab\/puma/) - } - end - end -end - -RSpec.describe 'gitlab::puma with more CPUs' do - let(:chef_run) do - runner = ChefSpec::SoloRunner.new( - step_into: %w(runit_service), - path: 'spec/fixtures/fauxhai/ubuntu/16.04-more-cpus.json' - ) - runner.converge('gitlab::default') - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when puma is enabled' do - it 'renders the puma.rb file' do - expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( - environment: 'production', - pid: '/opt/gitlab/var/puma/puma.pid', - state_path: '/opt/gitlab/var/puma/puma.state', - listen_socket: '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket', - listen_tcp: '127.0.0.1:8080', - working_directory: '/var/opt/gitlab/gitlab-rails/working', - worker_processes: 16 - ) - end - end -end diff --git a/spec/chef/recipes/rails_cache_clear_spec.rb b/spec/chef/recipes/rails_cache_clear_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvcmFpbHNfY2FjaGVfY2xlYXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/rails_cache_clear_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'chef_helper' - -# NOTE: We do not try to verify if we pass proper notifications to -# execute['clear the gitlab-rails cache'] resource that's done in other specs -# We just test if we use proper command and that we can change default -# attribute value with gilab.rb setting. - -RSpec.describe 'gitlab::rails-cache-clear' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'test clear cache execution' do - let(:clear_cache_exec) { chef_run.execute('clear the gitlab-rails cache') } - let(:gilab_yml_temp) do - chef_run.find_resource(:templatesymlink, - 'Create a gitlab.yml and create a symlink to Rails root') - end - - it 'check rake_cache_clear default attribute value set to true' do - expect(chef_run.node['gitlab']['gitlab-rails']['rake_cache_clear']) - .to be(true) - end - - it 'check rake_cache_clear attribute value set to true' do - stub_gitlab_rb(gitlab_rails: { rake_cache_clear: true }) - expect(chef_run.node['gitlab']['gitlab-rails']['rake_cache_clear']) - .to be(true) - end - - it 'check rake_cache_clear attribute value set to false' do - stub_gitlab_rb(gitlab_rails: { rake_cache_clear: false }) - expect(chef_run.node['gitlab']['gitlab-rails']['rake_cache_clear']) - .to be(false) - end - - it 'check command used to clear cache' do - expect(clear_cache_exec.command).to match( - '/opt/gitlab/bin/gitlab-rake cache:clear') - expect(clear_cache_exec).to do_nothing - end - end -end diff --git a/spec/chef/recipes/registry_spec.rb b/spec/chef/recipes/registry_spec.rb deleted file mode 100755 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvcmVnaXN0cnlfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/registry_spec.rb +++ /dev/null @@ -1,510 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'registry recipe' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'letsencrypt' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - registry_external_url: 'https://registry.example.com' - ) - - allow(File).to receive(:exist?).and_call_original - end - - describe 'HTTP to HTTPS redirection' do - context 'by default' do - it 'is enabled' do - expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-registry.conf').with_content("return 301 https://registry.example.com$request_uri;") - end - end - - context 'if disabled in gitlab.rb' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - registry_external_url: 'https://registry.example.com', - registry_nginx: { - redirect_http_to_https: false - } - ) - end - - it 'is disabled' do - expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-registry.conf') - expect(chef_run).not_to render_file('/var/opt/gitlab/nginx/conf/gitlab-registry.conf').with_content("return 301 https://registry.example.com$request_uri;") - end - end - - context 'registry on gitlab domain with a different port ' do - before do - stub_gitlab_rb( - external_url: 'https://gitlab.example.com', - registry_external_url: 'https://gitlab.example.com:5005' - ) - end - - it 'is enabled and has correct redirect URL in nginx config' do - expect(chef_run).to render_file('/var/opt/gitlab/nginx/conf/gitlab-registry.conf').with_content("return 301 https://gitlab.example.com:5005$request_uri;") - end - end - end - - context 'default certificate file is missing' do - before do - allow(File).to receive(:exist?).with('/etc/gitlab/ssl/registry.example.com.crt').and_return(false) - end - - it 'adds itself to letsencrypt alt_names' do - expect(chef_run.node['letsencrypt']['alt_names']).to eql(['registry.example.com']) - end - - it 'is reflected in the acme_selfsigned' do - expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( - alt_names: ['registry.example.com'] - ) - end - end - - context 'default certificate file is present' do - before do - allow(File).to receive(:exist?).with('/etc/gitlab/ssl/registry.example.com.crt').and_return(true) - end - - it 'does not alter letsencrypt alt_names' do - expect(chef_run.node['letsencrypt']['alt_names']).to eql([]) - end - - it 'is reflected in the acme_selfsigned' do - expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( - alt_names: [] - ) - end - end - end - - context 'when registry is enabled' do - before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com') } - - it_behaves_like 'enabled registry service' - - it_behaves_like 'renders a valid YAML file', '/var/opt/gitlab/registry/config.yml' - - it 'creates a default VERSION file and restarts service' do - expect(chef_run).to create_version_file('Create version file for Registry').with( - version_file_path: '/var/opt/gitlab/registry/VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/registry --version' - ) - - expect(chef_run.version_file('Create version file for Registry')).to notify('runit_service[registry]').to(:restart) - end - - context 'when registry storagedriver health check is disabled' do - before { stub_gitlab_rb(registry: { health_storagedriver_enabled: false }) } - - it 'creates registry config with specified value' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/health:\s*storagedriver:\s*enabled:\s*false/) - end - end - - context 'when registry validation is enabled' do - before { stub_gitlab_rb(registry: { validation_enabled: true }) } - - it 'creates registry config with specified value' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/^validation:\s*disabled: false$/) - end - end - - context 'when registry middleware is enabled' do - let(:middleware_config) do - { "storage" => [ - { "name" => "googlecdn", - "options" => { - "baseurl" => "https://example.org", - "privatekey" => "/etc/gitlab/googlecdn.key", - "keyname" => "example-key" - } } - ] } - end - - before { stub_gitlab_rb(registry: { middleware: middleware_config }) } - - it 'creates registry config with middleware' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(%r(^middleware: {"storage":)) - end - end - - context 'when a log formatter is specified' do - before { stub_gitlab_rb(registry: { log_formatter: 'json' }) } - - it 'creates the registry config with the specified value' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/log:\s*level: info\s*formatter:\s*json/) - end - - it 'does not append timestamp in logs if logging format is json' do - expect(chef_run).to render_file('/opt/gitlab/sv/registry/log/run') - .with_content(/exec svlogd \/var\/log\/gitlab\/registry/) - end - end - - context 'when schema1 compatibility is enabled' do - before { stub_gitlab_rb(registry: { compatibility_schema1_enabled: true }) } - - it 'creates registry config with specified value' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/compatibility:\s*schema1:\s*enabled:\s*true/) - end - end - end - - context 'when registry port is specified' do - before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com', registry: { registry_http_addr: 'localhost:5001' }) } - - it 'creates registry and rails configs with specified value' do - expect(chef_run).to create_templatesymlink('Create a gitlab.yml and create a symlink to Rails root').with_variables(hash_including('registry_api_url' => 'http://localhost:5001')) - - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/addr: localhost:5001/) - end - end - - context 'when a debug addr is specified' do - before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com', registry: { debug_addr: 'localhost:5005' }) } - - it 'creates the registry config with the specified debug value' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/debug:\n\s*addr: localhost:5005/) - end - end - - context 'when user and group are specified' do - before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com', registry: { username: 'registryuser', group: 'registrygroup' }) } - it 'make registry run file start registry under correct user' do - expect(chef_run).to render_file('/opt/gitlab/sv/registry/run') - .with_content(/-U registryuser:registrygroup/) - expect(chef_run).to render_file('/opt/gitlab/sv/registry/run') - .with_content(/-u registryuser:registrygroup/) - end - end -end - -RSpec.describe 'registry' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:default_vars) do - { - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' - } - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when registry is enabled' do - before { stub_gitlab_rb(registry_external_url: 'https://registry.example.com') } - - it_behaves_like 'enabled registry service' - - context 'when custom storage parameters are specified' do - before do - stub_gitlab_rb( - registry: { - storage: { - s3: { accesskey: 'awsaccesskey', secretkey: 'awssecretkey', bucketname: 'bucketname' } - } - } - ) - end - - it 'uses custom storage instead of the default rootdirectory' do - expect(chef_run.node['registry']['storage']) - .to include(s3: { accesskey: 'awsaccesskey', secretkey: 'awssecretkey', bucketname: 'bucketname' }) - expect(chef_run.node['registry']['storage']) - .not_to include('rootdirectory' => '/var/opt/gitlab/gitlab-rails/shared/registry') - end - - it 'uses the default cache and delete settings if not overridden' do - expect(chef_run.node['registry']['storage']['cache']) - .to eql('blobdescriptor' => 'inmemory') - expect(chef_run.node['registry']['storage']['delete']) - .to eql('enabled' => true) - end - - it 'allows the cache and delete settings to be overridden' do - stub_gitlab_rb(registry: { storage: { cache: 'somewhere-else', delete: { enabled: false } } }) - expect(chef_run.node['registry']['storage']['cache']) - .to eql('somewhere-else') - expect(chef_run.node['registry']['storage']['delete']) - .to eql('enabled' => false) - end - end - - context 'when storage_delete_enabled is false' do - before { stub_gitlab_rb(registry: { storage_delete_enabled: false }) } - - it 'sets the delete enabled field on the storage object' do - expect(chef_run.node['registry']['storage']['delete']) - .to eql('enabled' => false) - end - end - - context 'when notification is configured for Geo replication' do - before do - stub_gitlab_rb( - registry: { - notifications: [ - { - 'name' => 'geo_event', - 'url' => 'https://registry.example.com/notify', - 'timeout' => '500ms', - 'threshold' => 5, - 'backoff' => '1s', - 'headers' => { - "Authorization" => ["mysecret"] - } - } - ] - } - ) - end - - it 'assigns registry_notification_secret variable automatically' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"Authorization":\["mysecret"\]/) - expect(chef_run.node['gitlab']['gitlab-rails']['registry_notification_secret']) - .to eql('mysecret') - end - end - - context 'when registry notification endpoint is configured with the minimum required' do - before do - stub_gitlab_rb( - registry: { - notifications: [ - name: 'test_endpoint', - url: 'https://registry.example.com/notify' - ] - } - ) - end - - it 'creates the registry config with the specified endpoint config' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"name":"test_endpoint"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"url":"https:\/\/registry.example.com\/notify"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"timeout":"500ms"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"threshold":5/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"backoff":"1s"/) - end - end - - context 'when the default values are overridden' do - before do - stub_gitlab_rb( - registry: { - notifications: [ - name: 'test_endpoint', - url: 'https://registry.example.com/notify' - ], - default_notifications_timeout: '5000ms', - default_notifications_threshold: 10, - default_notifications_backoff: '50s', - default_notifications_headers: { - "Authorization" => %w(AUTHORIZATION_EXAMPLE_TOKEN1 AUTHORIZATION_EXAMPLE_TOKEN2) - } - } - ) - end - - it 'creates the registry config overriding the values not set with the new defaults' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"name":"test_endpoint"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"url":"https:\/\/registry.example.com\/notify"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"timeout":"5000ms"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"threshold":10/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"backoff":"50s"/) - end - end - - context 'when registry notification endpoint is configured with all the available variables' do - before do - stub_gitlab_rb( - registry: { - notifications: [ - { - 'name' => 'test_endpoint', - 'url' => 'https://registry.example.com/notify', - 'timeout' => '500ms', - 'threshold' => 5, - 'backoff' => '1s', - 'headers' => { - "Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"] - } - } - ] - } - ) - end - - it 'creates the registry config with the specified endpoint config' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"name":"test_endpoint"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"url":"https:\/\/registry.example.com\/notify"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"timeout":"500ms"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"threshold":5/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"backoff":"1s"/) - end - end - - context 'when 3 registry notification endpoints are configured' do - before do - stub_gitlab_rb( - registry: { - notifications: [ - { - 'name' => 'test_endpoint', - 'url' => 'https://registry.example.com/notify' - }, - { - 'name' => 'test_endpoint2', - 'url' => 'https://registry.example.com/notify2', - 'timeout' => '100ms', - 'threshold' => 2, - 'backoff' => '4s', - 'headers' => { - "Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"] - } - }, - { - 'name' => 'test_endpoint3', - 'url' => 'https://registry.example.com/notify3' - } - ] - } - ) - end - - it 'creates the registry config with the specified endpoint config' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"name":"test_endpoint"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/\"url\":\"https:\/\/registry.example.com\/notify\"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"timeout":"500ms"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"threshold":5/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"backoff":"1s"/) - # Second endpoint - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"name":"test_endpoint2"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"url":"https:\/\/registry.example.com\/notify2"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"timeout":"100ms"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"threshold":2/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"backoff":"4s"/) - # Third endpoint - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"name":"test_endpoint3"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"url":"https:\/\/registry.example.com\/notify3"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"timeout":"500ms"/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"threshold":5/) - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - .with_content(/"backoff":"1s"/) - end - end - - context 'when registry notification endpoint is not configured' do - it 'creates the registry config without the endpoint config' do - expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') - expect(chef_run).not_to render_file('/var/opt/gitlab/registry/config.yml') - .with_content('notifications:') - end - end - - context 'when registry has custom environment variables configured' do - before do - stub_gitlab_rb(registry: { env: { 'HTTP_PROXY' => 'my-proxy' } }) - end - - it 'creates necessary env variable files' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/registry/env').with_variables( - default_vars.merge( - { - 'HTTP_PROXY' => 'my-proxy' - } - ) - ) - end - end - end -end - -RSpec.describe 'auto enabling registry' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - let(:registry_config) { '/var/opt/gitlab/registry/config.yml' } - let(:nginx_config) { '/var/opt/gitlab/nginx/conf/gitlab-registry.conf' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb( - external_url: 'https://gitlab.example.com' - ) - end - - it_behaves_like 'enabled registry service' - - it 'should listen on port 5050 with nginx' do - expect(chef_run).to render_file(nginx_config) - .with_content { |content| - expect(content).to include("listen *:5050 ssl;") - expect(content).to include("server_name gitlab.example.com;") - } - end - - it "should use the default Let's Encrypt certificates" do - expect(chef_run).to render_file(nginx_config) - .with_content { |content| - expect(content).to include("ssl_certificate /etc/gitlab/ssl/gitlab.example.com.crt;") - expect(content).to include("ssl_certificate_key /etc/gitlab/ssl/gitlab.example.com.key;") - } - end - - it 'should point gitlab-rails to the registry' do - expect(chef_run).to create_templatesymlink( - 'Create a gitlab.yml and create a symlink to Rails root' - ).with_variables( - hash_including( - 'registry_host' => 'gitlab.example.com', - 'registry_port' => '5050' - ) - ) - end -end diff --git a/spec/chef/recipes/remote-syslog_spec.rb b/spec/chef/recipes/remote-syslog_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvcmVtb3RlLXN5c2xvZ19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/remote-syslog_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::remote-syslog' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'creates service' do - before do - stub_gitlab_rb( - remote_syslog: { - enable: true - } - ) - end - it_behaves_like "enabled runit service", "remote-syslog", "root", "root" - end - - context 'when remote logger is enabled' do - before do - stub_gitlab_rb( - remote_syslog: { - enable: true - }, - logging: { - udp_log_shipping_hostname: "example.com" - } - ) - end - - it 'creates the remote_syslog config' do - expect(chef_run.node['gitlab']['remote-syslog']['enable']).to eq true - end - - it 'creates remote-syslog config file with correct log directories' do - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml') - - # Checking if log_directory of both type of services - those accessible - # via node[service] and those via node['gitlab'][service] are populated. - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/redis\/\*.log/) - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/nginx\/\*.log/) - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/gitlab-rails\/\*.log/) - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/postgresql\/\*.log/) - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/sidekiq\/\*.log/) - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/gitlab-workhorse\/\*.log/) - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/gitlab-pages\/\*.log/) - expect(chef_run).to render_file('/var/opt/gitlab/remote-syslog/remote_syslog.yml').with_content(/- \/var\/log\/gitlab\/gitlab-kas\/\*.log/) - end - - it 'creates the remote-syslog sv file without setting hostname' do - stub_gitlab_rb( - remote_syslog: { - enable: true - } - ) - contents = <<~END - #!/bin/sh - exec 2>&1 - - exec /opt/gitlab/embedded/bin/remote_syslog --no-detach --debug-level DEBUG -c /var/opt/gitlab/remote-syslog/remote_syslog.yml - END - expect(chef_run).to render_file('/opt/gitlab/sv/remote-syslog/run').with_content(contents.chomp) - end - - it 'creates the remote-syslog sv file with setting hostname' do - stub_gitlab_rb( - remote_syslog: { - enable: true - }, - logging: { - udp_log_shipping_hostname: "example.com" - } - ) - contents = <<~END - #!/bin/sh - exec 2>&1 - - exec /opt/gitlab/embedded/bin/remote_syslog --no-detach --debug-level DEBUG -c /var/opt/gitlab/remote-syslog/remote_syslog.yml --hostname example.com - END - expect(chef_run).to render_file('/opt/gitlab/sv/remote-syslog/run').with_content(contents.chomp) - end - end -end diff --git a/spec/chef/recipes/selinux_spec.rb b/spec/chef/recipes/selinux_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvc2VsaW51eF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/selinux_spec.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::gitlab-selinux' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink storage_directory)).converge('gitlab::default') } - let(:templatesymlink) { chef_run.templatesymlink('Create a config.yml and create a symlink to Rails root') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_default_should_notify?(true) - end - - context 'when NOT running on selinux' do - before do - allow_any_instance_of(ShellOutHelper).to receive(:success?).with('id -Z').and_return(false) - end - - it 'should not run the semanage bash command' do - expect(templatesymlink).to_not notify('bash[Set proper security context on ssh files for selinux]').delayed - end - end - - context 'when running on selinux' do - before do - allow_any_instance_of(ShellOutHelper).to receive(:success?).with('id -Z').and_return(true) - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with('/var/opt/gitlab/.ssh').and_return(true) - allow(File).to receive(:exist?).with('/var/opt/gitlab/.ssh/authorized_keys').and_return(true) - allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret').and_return(true) - allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-shell/config.yml').and_return(true) - allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-workhorse/sockets').and_return(true) - end - - let(:bash_block) { chef_run.bash('Set proper security context on ssh files for selinux') } - - def semanage_fcontext(filename) - "semanage fcontext -a -t gitlab_shell_t '#{filename}'" - end - - it 'should run the semanage bash command' do - expect(templatesymlink).to notify('bash[Set proper security context on ssh files for selinux]').delayed - end - - it 'sets the security context of gitlab-shell files' do - lines = bash_block.code.split("\n") - files = %w(/var/opt/gitlab/.ssh(/.*)? - /var/opt/gitlab/.ssh/authorized_keys - /var/opt/gitlab/gitlab-shell/config.yml - /var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret - /var/opt/gitlab/gitlab-workhorse/sockets) - managed_files = files.map { |file| semanage_fcontext(file) } - - expect(lines).to include(*managed_files) - expect(lines).to include("restorecon -R -v '/var/opt/gitlab/.ssh'") - expect(lines).to include("restorecon -v '/var/opt/gitlab/.ssh/authorized_keys'") - expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-shell/config.yml'") - expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret'") - expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-workhorse/sockets'") - end - - context 'and the user configured a custom workhorse sockets directory' do - let(:user_sockets_directory) { '/how/do/you/do' } - before do - stub_gitlab_rb( - gitlab_workhorse: { - listen_network: 'unix', - sockets_directory: user_sockets_directory - } - ) - end - - it 'sets the security context of a custom workhorse sockets directory' do - allow(File).to receive(:exist?).with(user_sockets_directory).and_return(true) - lines = bash_block.code.split("\n") - files = [user_sockets_directory] - managed_files = files.map { |file| semanage_fcontext(file) } - - expect(lines).to include(*managed_files) - expect(lines).to include("restorecon -v '#{user_sockets_directory}'") - end - end - - context 'when gitlab-rails is disabled' do - before do - stub_gitlab_rb( - gitlab_rails: { - enable: false - } - ) - end - - it 'should not attempt to set security context' do - expect(chef_run).not_to run_bash('Set proper security context on ssh files for selinux') - end - end - end -end diff --git a/spec/chef/recipes/sidekiq_spec.rb b/spec/chef/recipes/sidekiq_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvc2lkZWtpcV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/sidekiq_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::sidekiq' do - let(:chef_run) do - runner = ChefSpec::SoloRunner.new( - step_into: %w(sidekiq_service runit_service), - path: 'spec/fixtures/fauxhai/ubuntu/16.04.json' - ) - runner.converge('gitlab::default') - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'with default values' do - it 'correctly renders out the sidekiq service file' do - expect(chef_run).to render_file("/opt/gitlab/sv/sidekiq/run") - .with_content { |content| - expect(content).not_to match(/export prometheus_run_dir=\'\'/) - expect(content).to match(/mkdir -p \/run\/gitlab\/sidekiq/) - expect(content).to match(/rm \/run\/gitlab\/sidekiq/) - expect(content).to match(/chmod 0700 \/run\/gitlab\/sidekiq/) - expect(content).to match(/chown git \/run\/gitlab\/sidekiq/) - expect(content).to match(/export prometheus_run_dir=\'\/run\/gitlab\/sidekiq\'/) - expect(content).to match(%r{bin/sidekiq-cluster}) - expect(content).to match(/-m 50/) # max_concurrency - expect(content).to match(/--timeout 25/) # shutdown timeout - expect(content).to match(/\*/) # all queues - } - end - - it_behaves_like "enabled runit service", "sidekiq", "root", "root" - end - - describe 'log_format' do - context 'by default' do - it 'does not pass timestamp flag to svlogd' do - expect(chef_run).not_to render_file("/opt/gitlab/sv/sidekiq/log/run").with_content(/-tt/) - end - end - - context 'when user specifies text log format' do - before do - stub_gitlab_rb( - sidekiq: { log_format: 'text' } - ) - end - - it 'passes timestamp flag to svlogd' do - expect(chef_run).to render_file("/opt/gitlab/sv/sidekiq/log/run").with_content(/-tt/) - end - end - end - - include_examples "consul service discovery", "sidekiq", "sidekiq" -end diff --git a/spec/chef/recipes/storage-check_spec.rb b/spec/chef/recipes/storage-check_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvc3RvcmFnZS1jaGVja19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/storage-check_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright:: Copyright (c) 2017 GitLab Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'chef_helper' - -RSpec.describe 'gitlab::storage-check' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - describe 'when disabled' do - it_behaves_like 'disabled runit service', 'storage-check' - end - - describe 'when enabled' do - context 'default values' do - before do - stub_gitlab_rb( - storage_check: { enable: true } - ) - end - - it_behaves_like 'enabled runit service', 'storage-check', 'root', 'root' - end - - context 'custom values' do - before do - stub_gitlab_rb( - storage_check: { - enable: true - }, - user: { - username: 'foo', - group: 'bar' - } - ) - end - - it_behaves_like 'enabled runit service', 'storage-check', 'root', 'root' - end - end -end diff --git a/spec/chef/recipes/users_spec.rb b/spec/chef/recipes/users_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlY2lwZXMvdXNlcnNfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/recipes/users_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'gitlab::users' do - let(:chef_run) { ChefSpec::SoloRunner.new.converge('gitlab::users') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'with default attributes' do - it 'creates gitlab user' do - account_params = { - username: 'git', - groupname: 'git', - home: '/var/opt/gitlab', - shell: '/bin/sh' - } - - expect(chef_run).to create_directory('/var/opt/gitlab') - expect(chef_run).to create_account('GitLab user and group').with(account_params) - end - - it 'creates .gitconfig' do - template_params = { - owner: 'git', - group: 'git', - mode: '0644' - } - - expect(chef_run).to create_template('/var/opt/gitlab/.gitconfig').with(template_params) - expect(chef_run).to render_file('/var/opt/gitlab/.gitconfig').with_content { |content| - expect(content).to match(/name = GitLab/) - expect(content).to match(/email = gitlab@fauxhai.local/) - expect(content).to match(/fsyncObjectFiles = true/) - } - end - - it 'creates .bundle directory' do - directory_params = { - owner: 'git', - group: 'git' - } - - expect(chef_run).to create_directory('/var/opt/gitlab/.bundle').with(directory_params) - end - end -end diff --git a/spec/chef/redis/redis_spec.rb b/spec/chef/redis/redis_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3JlZGlzL3JlZGlzX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/chef/redis/redis_spec.rb +++ /dev/null @@ -1,379 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'redis' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(redis_service runit_service)).converge('gitlab::default') } - let(:redis_conf) { '/var/opt/gitlab/redis/redis.conf' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'by default' do - let(:gitlab_redis_cli_rc) do - <<-EOF -redis_dir='/var/opt/gitlab/redis' -redis_host='127.0.0.1' -redis_port='0' -redis_socket='/var/opt/gitlab/redis/redis.socket' - EOF - end - - it 'enables the redis service' do - expect(chef_run).to create_redis_service('redis') - end - - it 'creates redis config with default values' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content { |content| - expect(content).to match(/client-output-buffer-limit normal 0 0 0/) - expect(content).to match(/client-output-buffer-limit replica 256mb 64mb 60/) - expect(content).to match(/client-output-buffer-limit pubsub 32mb 8mb 60/) - expect(content).to match(/^hz 10/) - expect(content).to match(/^save 900 1/) - expect(content).to match(/^save 300 10/) - expect(content).to match(/^save 60 10000/) - expect(content).to match(/^maxmemory 0/) - expect(content).to match(/^maxmemory-policy noeviction/) - expect(content).to match(/^maxmemory-samples 5/) - expect(content).to match(/^tcp-backlog 511/) - expect(content).to match(/^rename-command KEYS ""$/) - expect(content).to match(/^lazyfree-lazy-eviction no$/) - expect(content).to match(/^lazyfree-lazy-expire no$/) - expect(content).to match(/^io-threads 1$/) - expect(content).to match(/^io-threads-do-reads no$/) - expect(content).to match(/^stop-writes-on-bgsave-error yes$/) - expect(content).not_to match(/^replicaof/) - expect(content).not_to match(/^tls-/) - } - end - - it 'creates redis user and group' do - expect(chef_run).to create_account('user and group for redis').with(username: 'gitlab-redis', groupname: 'gitlab-redis') - end - - it_behaves_like 'enabled runit service', 'redis', 'root', 'root' - - it 'creates gitlab-redis-cli-rc' do - expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-redis-cli-rc') - .with_content(gitlab_redis_cli_rc) - end - - describe 'pending restart check' do - context 'when running version is same as installed version' do - before do - allow_any_instance_of(RedisHelper).to receive(:running_version).and_return('3.2.12') - allow_any_instance_of(RedisHelper).to receive(:installed_version).and_return('3.2.12') - end - - it 'does not raise a warning' do - expect(chef_run).not_to run_ruby_block('warn pending redis restart') - end - end - - context 'when running version is different than installed version' do - before do - allow_any_instance_of(RedisHelper).to receive(:running_version).and_return('3.2.12') - allow_any_instance_of(RedisHelper).to receive(:installed_version).and_return('5.0.9') - end - - it 'raises a warning' do - expect(chef_run).to run_ruby_block('warn pending redis restart') - end - end - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - redis: { - client_output_buffer_limit_normal: "5 5 5", - client_output_buffer_limit_replica: "512mb 128mb 120", - client_output_buffer_limit_pubsub: "64mb 16mb 120", - save: ["10 15000"], - maxmemory: "32gb", - maxmemory_policy: "allkeys-url", - maxmemory_samples: 10, - tcp_backlog: 1024, - hz: 100, - username: 'foo', - group: 'bar', - rename_commands: { - "FAKE_COMMAND" => "RENAMED_FAKE_COMMAND", - "DISABLED_FAKE_COMMAND" => "" - } - } - ) - end - - it 'creates redis config with custom values' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/client-output-buffer-limit normal 5 5 5/) - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/client-output-buffer-limit replica 512mb 128mb 120/) - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/client-output-buffer-limit pubsub 64mb 16mb 120/) - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^save 10 15000/) - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^maxmemory 32gb/) - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^maxmemory-policy allkeys-url/) - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^maxmemory-samples 10/) - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^tcp-backlog 1024/) - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^hz 100/) - end - - it 'does not include the default renamed keys in redis.conf' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content { |content| - expect(content).not_to match(/^rename-command KEYS ""$/) - expect(content).to match(/^rename-command FAKE_COMMAND "RENAMED_FAKE_COMMAND"$/) - expect(content).to match(/^rename-command DISABLED_FAKE_COMMAND ""$/) - } - end - - it 'creates redis user and group' do - expect(chef_run).to create_account('user and group for redis').with(username: 'foo', groupname: 'bar') - end - - it_behaves_like 'enabled runit service', 'redis', 'root', 'root' - end - - context 'with snapshotting disabled' do - before do - stub_gitlab_rb( - redis: { - save: [] - } - ) - end - it 'creates redis config without save setting' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - expect(chef_run).not_to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^save/) - end - end - - context 'with snapshotting cleared' do - before do - stub_gitlab_rb( - redis: { - save: [""] - } - ) - end - it 'creates redis config without save setting' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^save ""/) - end - end - - context 'with a replica configured' do - let(:redis_host) { '1.2.3.4' } - let(:redis_port) { 6370 } - let(:master_ip) { '10.0.0.0' } - let(:master_port) { 6371 } - - let(:gitlab_redis_cli_rc) do - <<-EOF -redis_dir='/var/opt/gitlab/redis' -redis_host='1.2.3.4' -redis_port='6370' -redis_socket='' - EOF - end - - before do - stub_gitlab_rb( - redis: { - bind: redis_host, - port: redis_port, - master_ip: master_ip, - master_port: master_port, - master_password: 'password', - master: false - } - ) - end - - it 'includes replicaof' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^replicaof #{master_ip} #{master_port}/) - end - - it 'creates gitlab-redis-cli-rc' do - expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-redis-cli-rc') - .with_content(gitlab_redis_cli_rc) - end - end - - context 'in HA mode with Sentinels' do - let(:redis_host) { '1.2.3.4' } - let(:redis_port) { 6370 } - let(:master_ip) { '10.0.0.0' } - let(:master_port) { 6371 } - - let(:gitlab_redis_cli_rc) do - <<-EOF -redis_dir='/var/opt/gitlab/redis' -redis_host='1.2.3.4' -redis_port='6370' -redis_socket='' - EOF - end - - before do - stub_gitlab_rb( - redis: { - bind: redis_host, - port: redis_port, - ha: true, - master_ip: master_ip, - master_port: master_port, - master_password: 'password', - master: false - } - ) - end - - it 'omits replicaof' do - expect(chef_run).not_to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content(/^replicaof/) - end - - it 'creates gitlab-redis-cli-rc' do - expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-redis-cli-rc') - .with_content(gitlab_redis_cli_rc) - end - end - - context 'with rename_commands disabled' do - before do - stub_gitlab_rb( - redis: { - rename_commands: {} - } - ) - end - - it 'should not rename any commands' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content { |content| - expect(content).not_to match(/^rename-command/) - } - end - end - - context 'with lazy eviction enabled' do - before do - stub_gitlab_rb( - redis: { - lazyfree_lazy_eviction: true - } - ) - end - - it 'creates redis config with lazyfree-lazy-eviction yes' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content { |content| - expect(content).to match(/^lazyfree-lazy-eviction yes$/) - expect(content).to match(/^lazyfree-lazy-expire no$/) - } - end - end - - context 'with lazy eviction enabled' do - before do - stub_gitlab_rb( - redis: { - io_threads: 4, - io_threads_do_reads: true - } - ) - end - - it 'creates redis config with lazyfree-lazy-eviction yes' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content { |content| - expect(content).to match(/^io-threads 4$/) - expect(content).to match(/^io-threads-do-reads yes$/) - } - end - end - - context 'with stop writes on bgsave error disabled' do - before do - stub_gitlab_rb( - redis: { - stop_writes_on_bgsave_error: false - } - ) - end - - it 'creates redis config with stop-writes-on-bgsave-error no' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content { |content| - expect(content).to match(/^stop-writes-on-bgsave-error no$/) - } - end - end - - context 'with tls settings specified' do - before do - stub_gitlab_rb( - redis: { - tls_port: 6380, - tls_cert_file: '/etc/gitlab/ssl/redis.crt', - tls_key_file: '/etc/gitlab/ssl/redis.key', - tls_dh_params_file: '/etc/gitlab/ssl/redis-dhparams', - tls_ca_cert_file: '/etc/gitlab/ssl/redis-ca.crt', - tls_ca_cert_dir: '/opt/gitlab/embedded/ssl/certs', - tls_auth_clients: 'no', - tls_replication: 'yes', - tls_cluster: 'yes', - tls_protocols: 'TLSv1.2 TLSv1.3', - tls_ciphers: 'DEFAULT:!MEDIUM', - tls_ciphersuites: 'TLS_CHACHA20_POLY1305_SHA256', - tls_prefer_server_ciphers: 'yes', - tls_session_caching: 'no', - tls_session_cache_size: 10000, - tls_session_cache_timeout: 120 - } - ) - end - - it 'renders redis config with tls settings' do - expect(chef_run).to render_file('/var/opt/gitlab/redis/redis.conf') - .with_content { |content| - expect(content).to match(%r{^tls-port 6380$}) - expect(content).to match(%r{^tls-cert-file /etc/gitlab/ssl/redis.crt$}) - expect(content).to match(%r{^tls-key-file /etc/gitlab/ssl/redis.key$}) - expect(content).to match(%r{^tls-dh-params-file /etc/gitlab/ssl/redis-dhparams$}) - expect(content).to match(%r{^tls-ca-cert-file /etc/gitlab/ssl/redis-ca.crt$}) - expect(content).to match(%r{^tls-ca-cert-dir /opt/gitlab/embedded/ssl/certs$}) - expect(content).to match(%r{^tls-auth-clients no$}) - expect(content).to match(%r{^tls-replication yes$}) - expect(content).to match(%r{^tls-cluster yes$}) - expect(content).to match(%r{^tls-protocols "TLSv1.2 TLSv1.3"$}) - expect(content).to match(%r{^tls-ciphers DEFAULT:!MEDIUM$}) - expect(content).to match(%r{^tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256$}) - expect(content).to match(%r{^tls-prefer-server-ciphers yes$}) - expect(content).to match(%r{^tls-session-caching no$}) - expect(content).to match(%r{^tls-session-cache-size 10000$}) - expect(content).to match(%r{^tls-session-cache-timeout 120$}) - } - end - end - - context 'with redis disabled' do - before do - stub_gitlab_rb(redis: { enable: false }) - end - - it_behaves_like 'disabled runit service', 'redis', 'root', 'root' - end -end diff --git a/spec/chef/secrets_spec.rb b/spec/chef/secrets_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3NlY3JldHNfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/secrets_spec.rb +++ /dev/null @@ -1,375 +0,0 @@ -require 'chef_helper' -require 'base64' - -RSpec.describe 'secrets' do - let(:chef_run) { ChefSpec::SoloRunner.new.converge('gitlab::default') } - - HEX_KEY = /\h{128}/.freeze - RSA_KEY = /\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m.freeze - - def stub_gitlab_secrets_json(secrets) - allow(File).to receive(:read).with('/etc/gitlab/gitlab-secrets.json').and_return(JSON.generate(secrets)) - end - - before do - allow(File).to receive(:directory?).and_call_original - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:read).and_call_original - allow(File).to receive(:open).and_call_original - allow(File).to receive(:open).with('/etc/gitlab/initial_root_password', 'w', 0600).and_yield(double(:file, write: true)).once - end - - context 'when /etc/gitlab does not exist' do - it 'does not write secrets to the file' do - allow(File).to receive(:directory?).with('/etc/gitlab').and_return(false) - expect(File).not_to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w') - - chef_run - end - end - - context 'when /etc/gitlab exists' do - let(:file) { double(:file) } - let(:new_secrets) { @new_secrets } - let(:gitlab_rb_ci_jwt_signing_key) { SecretsHelper.generate_rsa(4096).to_pem } - - before do - allow(SecretsHelper).to receive(:system) - allow(File).to receive(:directory?).with('/etc/gitlab').and_return(true) - allow(File).to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w', 0600).and_yield(file).once - allow(file).to receive(:puts) { |json| @new_secrets = JSON.parse(json) } - allow(file).to receive(:chmod).and_return(true) - end - - context 'when there are no existing secrets' do - before do - allow(File).to receive(:exist?).with('/etc/gitlab/gitlab-secrets.json').and_return(false) - - chef_run - end - - it 'writes new secrets to the file, with different values for each' do - rails_keys = new_secrets['gitlab_rails'] - hex_keys = rails_keys.values_at('db_key_base', 'otp_key_base', 'secret_key_base', 'encrypted_settings_key_base') - rsa_keys = rails_keys.values_at('openid_connect_signing_key', 'ci_jwt_signing_key') - - expect(rails_keys.to_a.uniq).to eq(rails_keys.to_a) - expect(hex_keys).to all(match(HEX_KEY)) - expect(rsa_keys).to all(match(RSA_KEY)) - end - - it 'does not write legacy keys' do - expect(new_secrets).not_to have_key('gitlab_ci') - expect(new_secrets['gitlab_rails']).not_to have_key('jws_private_key') - end - - it 'generates an appropriate secret for gitlab-workhorse' do - workhorse_secret = new_secrets['gitlab_workhorse']['secret_token'] - expect(Base64.strict_decode64(workhorse_secret).length).to eq(32) - end - - it 'generates an appropriate shared secret for gitlab-pages' do - pages_shared_secret = new_secrets['gitlab_pages']['api_secret_key'] - expect(Base64.strict_decode64(pages_shared_secret).length).to eq(32) - end - - it 'generates an appropriate shared secret for gitlab-kas' do - kas_shared_secret = new_secrets['gitlab_kas']['api_secret_key'] - expect(Base64.strict_decode64(kas_shared_secret).length).to eq(32) - end - end - - context 'gitlab.rb provided gitlab_pages.api_secret_key' do - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - it 'fails when provided gitlab_pages.shared_secret is not 32 bytes' do - stub_gitlab_rb(gitlab_pages: { api_secret_key: SecureRandom.base64(16) }) - - expect { chef_run }.to raise_error(RuntimeError, /gitlab_pages\['api_secret_key'\] should be exactly 32 bytes/) - end - - it 'accepts provided gitlab_pages.api_secret_key when it is 32 bytes' do - api_secret_key = SecureRandom.base64(32) - stub_gitlab_rb(gitlab_pages: { api_secret_key: api_secret_key }) - - expect { chef_run }.not_to raise_error - expect(new_secrets['gitlab_pages']['api_secret_key']).to eq(api_secret_key) - end - end - - context 'gitlab.rb provided gitlab_kas.api_secret_key' do - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - it 'fails when provided gitlab_kas.shared_secret is not 32 bytes' do - stub_gitlab_rb(gitlab_kas: { api_secret_key: SecureRandom.base64(16) }) - - expect { chef_run }.to raise_error(RuntimeError, /gitlab_kas\['api_secret_key'\] should be exactly 32 bytes/) - end - - it 'accepts provided gitlab_kas.api_secret_key when it is 32 bytes' do - api_secret_key = SecureRandom.base64(32) - stub_gitlab_rb(gitlab_kas: { api_secret_key: api_secret_key }) - - expect { chef_run }.not_to raise_error - expect(new_secrets['gitlab_kas']['api_secret_key']).to eq(api_secret_key) - end - end - - context 'when there are existing secrets in /etc/gitlab/gitlab-secrets.json' do - before do - allow(SecretsHelper).to receive(:system) - allow(File).to receive(:directory?).with('/etc/gitlab').and_return(true) - allow(File).to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w').and_yield(file).once - allow(File).to receive(:exist?).with('/etc/gitlab/gitlab-secrets.json').and_return(true) - end - - context 'when secrets are only partially present' do - before do - stub_gitlab_secrets_json(gitlab_ci: { db_key_base: 'json_ci_db_key_base' }) - chef_run - end - - it 'uses secrets from /etc/gitlab/gitlab-secrets.json where available' do - expect(new_secrets['gitlab_rails']['db_key_base']).to eq('json_ci_db_key_base') - end - - it 'falls back further to generating new secrets' do - expect(new_secrets['gitlab_rails']['otp_key_base']).to match(HEX_KEY) - end - end - - context 'when secrets exist under legacy keys' do - before do - stub_gitlab_secrets_json( - gitlab_ci: { db_key_base: 'json_ci_db_key_base', secret_token: 'json_ci_secret_token' }, - gitlab_rails: { - secret_token: 'json_rails_secret_token', - jws_private_key: 'json_rails_jws_private_key' - } - ) - - chef_run - end - - it 'moves gitlab_ci.db_key_base to gitlab_rails.db_key_base' do - expect(new_secrets['gitlab_rails']['db_key_base']).to eq('json_ci_db_key_base') - end - - it 'moves gitlab_rails.secret_token to gitlab_rails.otp_key_base' do - expect(new_secrets['gitlab_rails']['otp_key_base']).to eq('json_rails_secret_token') - end - - it 'moves gitlab_ci.db_key_base to gitlab_rails.secret_key_base' do - expect(new_secrets['gitlab_rails']['secret_key_base']).to eq('json_ci_db_key_base') - end - - it 'moves gitlab_rails.jws_private_key to gitlab_rails.openid_connect_signing_key' do - expect(new_secrets['gitlab_rails']['openid_connect_signing_key']).to eq('json_rails_jws_private_key') - end - - it 'ignores other, unused, secrets' do - expect(new_secrets.inspect).not_to include('json_ci_secret_token') - end - end - end - - context 'when there are existing secrets in /etc/gitlab/gitlab.rb and /etc/gitlab/gitlab-secrets.json' do - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(File).to receive(:exist?).with('/etc/gitlab/gitlab-secrets.json').and_return(true) - end - - context 'when secrets are only partially present' do - before do - stub_gitlab_secrets_json( - gitlab_ci: { db_key_base: 'json_ci_db_key_base' }, - gitlab_rails: { - secret_token: 'json_rails_secret_token', - jws_private_key: 'json_rails_jws_private_key' - } - ) - - stub_gitlab_rb(gitlab_ci: { db_key_base: 'rb_ci_db_key_base' }) - - chef_run - end - - it 'uses secrets from /etc/gitlab/gitlab.rb when available' do - expect(new_secrets['gitlab_rails']['db_key_base']).to eq('rb_ci_db_key_base') - end - - it 'falls back to secrets from /etc/gitlab/gitlab-secrets.json' do - expect(new_secrets['gitlab_rails']['otp_key_base']).to eq('json_rails_secret_token') - end - - it 'falls back further to generating new secrets' do - expect(new_secrets['gitlab_shell']['secret_token']).to match(HEX_KEY) - end - end - - context 'when secrets exist under legacy keys' do - before do - stub_gitlab_rb(gitlab_ci: { db_key_base: 'rb_ci_db_key_base', - secret_token: 'rb_ci_secret_token' }) - - stub_gitlab_secrets_json( - gitlab_rails: { - secret_token: 'json_rails_secret_token', - jws_private_key: 'json_rails_jws_private_key', - encrypted_settings_key_base: 'encrypted_settings_key_base', - ci_jwt_signing_key: gitlab_rb_ci_jwt_signing_key - } - ) - - chef_run - end - - it 'moves gitlab_ci.db_key_base to gitlab_rails.db_key_base' do - expect(new_secrets['gitlab_rails']['db_key_base']).to eq('rb_ci_db_key_base') - end - - it 'moves gitlab_rails.secret_token to gitlab_rails.otp_key_base' do - expect(new_secrets['gitlab_rails']['otp_key_base']).to eq('json_rails_secret_token') - end - - it 'moves gitlab_ci.db_key_base to gitlab_rails.secret_key_base' do - expect(new_secrets['gitlab_rails']['secret_key_base']).to eq('rb_ci_db_key_base') - end - - it 'moves gitlab_rails.jws_private_key to gitlab_rails.openid_connect_signing_key' do - expect(new_secrets['gitlab_rails']['openid_connect_signing_key']).to eq('json_rails_jws_private_key') - end - - it 'ignores other, unused, secrets' do - expect(new_secrets.inspect).not_to include('rb_ci_secret_token') - end - - it 'writes the correct data to secrets.yml' do - expect(chef_run).to create_templatesymlink('Create a secrets.yml and create a symlink to Rails root').with_variables( - 'secrets' => { - 'production' => { - 'db_key_base' => 'rb_ci_db_key_base', - 'secret_key_base' => 'rb_ci_db_key_base', - 'otp_key_base' => 'json_rails_secret_token', - 'encrypted_settings_key_base' => 'encrypted_settings_key_base', - 'openid_connect_signing_key' => 'json_rails_jws_private_key', - 'ci_jwt_signing_key' => gitlab_rb_ci_jwt_signing_key - } - } - ) - end - - it 'deletes the secret file' do - expect(chef_run).to delete_file('/var/opt/gitlab/gitlab-rails/etc/secret') - expect(chef_run).to delete_file('/opt/gitlab/embedded/service/gitlab-rails/.secret') - end - end - - context 'when secrets are ambiguous and cannot be migrated automatically' do - before { stub_gitlab_secrets_json({}) } - - it 'fails when gitlab_ci.db_key_base and gitlab_rails.db_key_base are different' do - stub_gitlab_rb( - gitlab_rails: { db_key_base: 'rb_rails_db_key_base' }, - gitlab_ci: { db_key_base: 'rb_ci_db_key_base' } - ) - - expect(File).not_to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w') - expect { chef_run }.to raise_error(RuntimeError, /db_key_base/) - end - - it 'fails when the secret file does not match gitlab_rails.otp_key_base' do - secret_file = '/var/opt/gitlab/gitlab-rails/etc/secret' - - stub_gitlab_rb(gitlab_rails: { otp_key_base: 'rb_rails_otp_key_base', - secret_key_base: 'rb_rails_secret_key_base' }) - - allow(File).to receive(:exist?).with(secret_file).and_return(true) - allow(File).to receive(:read).with(secret_file).and_return('secret_key_base') - - expect { chef_run }.to raise_error(RuntimeError, /otp_key_base/) - end - end - - context 'ci_jwt_signing_key' do - let(:secrets_json_ci_jwt_signing_key) { SecretsHelper.generate_rsa(4096).to_pem } - - it 'uses the key from /etc/gitlab/gitlab.rb when available' do - stub_gitlab_secrets_json( - gitlab_rails: { ci_jwt_signing_key: secrets_json_ci_jwt_signing_key } - ) - - stub_gitlab_rb( - gitlab_rails: { ci_jwt_signing_key: gitlab_rb_ci_jwt_signing_key } - ) - - chef_run - - expect(new_secrets['gitlab_rails']['ci_jwt_signing_key']).to eq(gitlab_rb_ci_jwt_signing_key) - end - - it 'uses the key from /etc/gitlab/gitlab-secrets.json when available' do - stub_gitlab_secrets_json( - gitlab_rails: { ci_jwt_signing_key: secrets_json_ci_jwt_signing_key } - ) - - chef_run - - expect(new_secrets['gitlab_rails']['ci_jwt_signing_key']).to eq(secrets_json_ci_jwt_signing_key) - end - - it 'rejects invalid RSA keys' do - stub_gitlab_secrets_json({}) - stub_gitlab_rb( - gitlab_rails: { ci_jwt_signing_key: 'invalid key' } - ) - - expect { chef_run }.to raise_error('ci_jwt_signing_key: The provided key is not valid RSA key') - end - - it 'rejects RSA public keys' do - public_key = SecretsHelper.generate_rsa(4096).public_key.to_pem - - stub_gitlab_secrets_json({}) - stub_gitlab_rb( - gitlab_rails: { ci_jwt_signing_key: public_key } - ) - - expect { chef_run }.to raise_error('ci_jwt_signing_key: The provided key is not private RSA key') - end - - it 'writes the correct data to secrets.yml' do - stub_gitlab_secrets_json({}) - - stub_gitlab_rb( - gitlab_rails: { - db_key_base: 'rb_ci_db_key_base', - secret_key_base: 'rb_ci_db_key_base', - otp_key_base: 'json_rails_secret_token', - encrypted_settings_key_base: 'encrypted_settings_key_base', - openid_connect_signing_key: 'json_rails_jws_private_key', - ci_jwt_signing_key: gitlab_rb_ci_jwt_signing_key - } - ) - - expect(chef_run).to create_templatesymlink('Create a secrets.yml and create a symlink to Rails root').with_variables( - 'secrets' => { - 'production' => { - 'db_key_base' => 'rb_ci_db_key_base', - 'secret_key_base' => 'rb_ci_db_key_base', - 'otp_key_base' => 'json_rails_secret_token', - 'encrypted_settings_key_base' => 'encrypted_settings_key_base', - 'openid_connect_signing_key' => 'json_rails_jws_private_key', - 'ci_jwt_signing_key' => gitlab_rb_ci_jwt_signing_key - } - } - ) - end - end - end - end -end diff --git a/spec/chef/spamcheck/recipes/spamcheck_spec.rb b/spec/chef/spamcheck/recipes/spamcheck_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmL3NwYW1jaGVjay9yZWNpcGVzL3NwYW1jaGVja19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/spamcheck/recipes/spamcheck_spec.rb +++ /dev/null @@ -1,274 +0,0 @@ -require 'chef_helper' -require 'toml-rb' - -RSpec.describe 'spamcheck' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab-ee::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'when spamcheck is disabled (default)' do - it 'includes spamcheck::disable recipe' do - expect(chef_run).to include_recipe('spamcheck::disable') - end - end - - context 'when spamcheck is enabled' do - before do - stub_gitlab_rb( - spamcheck: { - enable: true - } - ) - end - - it 'includes spamcheck::enable recipe' do - expect(chef_run).to include_recipe('spamcheck::enable') - end - end - - describe 'spamcheck::enable' do - context 'with default values' do - before do - stub_gitlab_rb( - spamcheck: { - enable: true - } - ) - end - - it 'creates necessary directories at default locations' do - %w[ - /var/opt/gitlab/spamcheck - /var/opt/gitlab/spamcheck/sockets - /var/log/gitlab/spamcheck - /var/log/gitlab/spam-classifier - ].each do |dir| - expect(chef_run).to create_directory(dir).with( - owner: 'git', - mode: '0700', - recursive: true - ) - end - end - - it 'creates config.toml with default values' do - actual_content = get_rendered_toml(chef_run, '/var/opt/gitlab/spamcheck/config.toml') - expected_content = { - grpc: { - port: '8001' - }, - rest: { - externalPort: '' - }, - logger: { - level: 'info', - format: 'json', - output: 'stdout' - }, - monitor: { - address: ":8003" - }, - extraAttributes: { - monitorMode: 'false' - }, - filter: { - allowList: {}, - denyList: {} - }, - preprocessor: { - socketPath: '/var/opt/gitlab/spamcheck/sockets/preprocessor.sock' - }, - modelAttributes: { - modelPath: '/opt/gitlab/embedded/service/spam-classifier/model/issues/tflite/model.tflite' - } - } - expect(actual_content).to eq(expected_content) - end - - it 'creates env directory with default variables' do - expect(chef_run).to create_env_dir('/opt/gitlab/etc/spamcheck/env').with_variables( - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/' - ) - end - - it_behaves_like "enabled runit service", "spamcheck", "root", "root" - it_behaves_like "enabled runit service", "spam-classifier", "root", "root" - - it 'creates runit files for spamcheck service' do - expected_content = <<~EOS - #!/bin/bash - - # Let runit capture all script error messages - exec 2>&1 - - exec chpst -e /opt/gitlab/etc/spamcheck/env -P \\ - -u git:git \\ - -U git:git \\ - /opt/gitlab/embedded/bin/spamcheck -config /var/opt/gitlab/spamcheck/config.toml - EOS - - expect(chef_run).to render_file('/opt/gitlab/sv/spamcheck/run').with_content(expected_content) - end - - it 'creates runit files for spam-classifier service' do - expected_content = <<~EOS - #!/bin/bash - - # Let runit capture all script error messages - exec 2>&1 - - exec chpst -e /opt/gitlab/etc/spamcheck/env -P \\ - -u git:git \\ - -U git:git \\ - /opt/gitlab/embedded/bin/python3 /opt/gitlab/embedded/service/spam-classifier/preprocessor/preprocess.py \\ - --tokenizer-pickle-path /opt/gitlab/embedded/service/spam-classifier/preprocessor/tokenizer.pickle \\ - --log-dir /var/log/gitlab/spam-classifier \\ - --socket-dir /var/opt/gitlab/spamcheck/sockets - EOS - - expect(chef_run).to render_file('/opt/gitlab/sv/spam-classifier/run').with_content(expected_content) - end - end - - context 'with user specified values' do - before do - stub_gitlab_rb( - user: { - username: 'randomuser', - group: 'randomgroup' - }, - spamcheck: { - enable: true, - dir: '/data/spamcheck', - port: 5001, - external_port: 5002, - monitoring_address: ':5003', - log_level: 'debug', - log_format: 'text', - log_output: 'file', - monitor_mode: true, - allowlist: { - '14' => 'spamtest/hello' - }, - denylist: { - '15' => 'foobar/random' - }, - env_directory: '/env/spamcheck', - log_directory: '/log/spamcheck', - env: { - 'FOO' => 'BAR' - }, - classifier: { - log_directory: '/log/spam-classifier' - } - } - ) - end - - it 'creates necessary directories at user specified locations' do - %w[ - /data/spamcheck - /data/spamcheck/sockets - /log/spamcheck - /log/spam-classifier - ].each do |dir| - expect(chef_run).to create_directory(dir).with( - owner: 'randomuser', - mode: '0700', - recursive: true - ) - end - end - - it 'creates config.toml with user specified values' do - actual_content = get_rendered_toml(chef_run, '/data/spamcheck/config.toml') - expected_content = { - grpc: { - port: '5001' - }, - rest: { - externalPort: '5002' - }, - logger: { - level: 'debug', - format: 'text', - output: 'file' - }, - monitor: { - address: ":5003" - }, - extraAttributes: { - monitorMode: 'true' - }, - filter: { - allowList: { - '14': 'spamtest/hello' - }, - denyList: { - '15': 'foobar/random' - } - }, - preprocessor: { - socketPath: '/data/spamcheck/sockets/preprocessor.sock' - }, - modelAttributes: { - modelPath: '/opt/gitlab/embedded/service/spam-classifier/model/issues/tflite/model.tflite' - } - } - expect(actual_content).to eq(expected_content) - end - - it 'creates env directory with user specified and default variables' do - expect(chef_run).to create_env_dir('/env/spamcheck').with_variables( - 'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/', - 'FOO' => 'BAR' - ) - end - - it_behaves_like "enabled runit service", "spamcheck", "root", "root" - it_behaves_like "enabled runit service", "spam-classifier", "root", "root" - - it 'creates runit files for spamcheck service' do - expected_content = <<~EOS - #!/bin/bash - - # Let runit capture all script error messages - exec 2>&1 - - exec chpst -e /env/spamcheck -P \\ - -u randomuser:randomgroup \\ - -U randomuser:randomgroup \\ - /opt/gitlab/embedded/bin/spamcheck -config /data/spamcheck/config.toml - EOS - - expect(chef_run).to render_file('/opt/gitlab/sv/spamcheck/run').with_content(expected_content) - end - - it 'creates runit files for spam-classifier service' do - expected_content = <<~EOS - #!/bin/bash - - # Let runit capture all script error messages - exec 2>&1 - - exec chpst -e /env/spamcheck -P \\ - -u randomuser:randomgroup \\ - -U randomuser:randomgroup \\ - /opt/gitlab/embedded/bin/python3 /opt/gitlab/embedded/service/spam-classifier/preprocessor/preprocess.py \\ - --tokenizer-pickle-path /opt/gitlab/embedded/service/spam-classifier/preprocessor/tokenizer.pickle \\ - --log-dir /log/spam-classifier \\ - --socket-dir /data/spamcheck/sockets - EOS - - expect(chef_run).to render_file('/opt/gitlab/sv/spam-classifier/run').with_content(expected_content) - end - end - end - - describe 'spamcheck::disable' do - it_behaves_like "disabled runit service", "spamcheck", "root", "root" - it_behaves_like "disabled runit service", "spam-classifier", "root", "root" - end -end diff --git a/spec/chef/support/shared_context/ctl_shared_context.rb b/spec/chef/support/shared_context/ctl_shared_context.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2NvbnRleHQvY3RsX3NoYXJlZF9jb250ZXh0LnJi --- /dev/null +++ b/spec/chef/support/shared_context/ctl_shared_context.rb @@ -0,0 +1,24 @@ +require 'spec_helper' +require 'omnibus-ctl' + +RSpec.shared_context 'ctl' do + let(:ctl) { Omnibus::Ctl.new('testing-ctl') } + + before do + allow_any_instance_of(Omnibus::Ctl).to receive(:require).and_call_original + + allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( + "/opt/testing-ctl/embedded/cookbooks/package/libraries/gitlab_cluster" + ) do + require_relative("../../../../files/gitlab-cookbooks/package/libraries/gitlab_cluster") + end + + allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( + "/opt/testing-ctl/embedded/service/omnibus-ctl-ee/lib/geo/#{command_script}" + ) do + require_relative("../../../../files/gitlab-ctl-commands-ee/lib/geo/#{command_script}") + end + + ctl.load_file("files/gitlab-ctl-commands-ee/#{command_script}.rb") + end +end diff --git a/spec/chef/support/shared_context/gitlab_rails_shared_context.rb b/spec/chef/support/shared_context/gitlab_rails_shared_context.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2NvbnRleHQvZ2l0bGFiX3JhaWxzX3NoYXJlZF9jb250ZXh0LnJi --- /dev/null +++ b/spec/chef/support/shared_context/gitlab_rails_shared_context.rb @@ -0,0 +1,14 @@ +require 'chef_helper' + +RSpec.shared_context 'gitlab-rails' do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: 'templatesymlink').converge('gitlab::default') } + let(:gitlab_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } + let(:gitlab_yml_file_content) { ChefSpec::Renderer.new(chef_run, gitlab_yml_template).content } + let(:gitlab_yml) { YAML.safe_load(gitlab_yml_file_content, [], [], true, symbolize_names: true) } + let(:config_dir) { '/var/opt/gitlab/gitlab-rails/etc/' } + + before do + allow(Gitlab).to receive(:[]).and_call_original + allow(File).to receive(:symlink?).and_call_original + end +end diff --git a/spec/chef/support/shared_context/object_store_shared_context.rb b/spec/chef/support/shared_context/object_store_shared_context.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2NvbnRleHQvb2JqZWN0X3N0b3JlX3NoYXJlZF9jb250ZXh0LnJi --- /dev/null +++ b/spec/chef/support/shared_context/object_store_shared_context.rb @@ -0,0 +1,42 @@ +require 'spec_helper' +require 'omnibus-ctl' + +RSpec.shared_context 'object storage config' do + let(:object_config) do + { + artifacts: { bucket: 'artifacts' }, + lfs: { bucket: 'lfs-objects' }, + dependency_proxy: { bucket: 'dependency_proxy' }, + external_diffs: { bucket: 'external_diffs' }, + packages: { bucket: 'packages' }, + terraform_state: { enabled: false, bucket: 'terraform' }, + ci_secure_files: { bucket: 'ci_secure_files' }, + uploads: { bucket: 'uploads' }, + pages: { bucket: 'pages' } + } + end + let(:aws_connection_hash) do + { + 'provider' => 'AWS', + 'region' => 'eu-west-1', + 'aws_access_key_id' => 'AKIAKIAKI', + 'aws_secret_access_key' => 'secret123' + } + end + let(:aws_storage_options_hash) do + { + 'server_side_encryption' => 'AES256', + 'server_side_encryption_kms_key_id' => 'arn:aws:12345' + } + end + let(:azure_connection_hash) do + { + 'provider' => 'AzureRM', + 'azure_storage_account_name' => 'testaccount', + 'azure_storage_access_key' => '1234abcd' + } + end + + let(:aws_connection_data) { JSON.parse(aws_connection_hash.to_json, symbolize_names: true) } + let(:aws_storage_options) { JSON.parse(aws_storage_options_hash.to_json, symbolize_names: true) } +end diff --git a/spec/chef/support/shared_examples/consul_service_name.rb b/spec/chef/support/shared_examples/consul_service_name.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2V4YW1wbGVzL2NvbnN1bF9zZXJ2aWNlX25hbWUucmI= --- /dev/null +++ b/spec/chef/support/shared_examples/consul_service_name.rb @@ -0,0 +1,44 @@ +RSpec.shared_examples 'consul service discovery' do |gitlab_rb_key, service_name| + let(:gitlab_rb_setting) do + { + consul: { + enable: true, + monitoring_service_discovery: true + } + } + end + + describe 'consul service discovery' do + let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } + + context 'by default' do + it 'is not registered as consul service' do + expect(chef_run).not_to create_consul_service(service_name) + end + end + + context 'when enabled' do + before do + stub_gitlab_rb(gitlab_rb_setting) + end + + it 'is registered as a consul service with default name' do + expect(chef_run).to create_consul_service(service_name) + end + + context 'with user specified service name' do + before do + gitlab_rb_setting[gitlab_rb_key] = { + enable: true, + consul_service_name: "#{service_name}-foobar" + } + stub_gitlab_rb(gitlab_rb_setting) + end + + it 'is registered as a consul service with user specified name' do + expect(chef_run).to create_consul_service("#{service_name}-foobar") + end + end + end + end +end diff --git a/spec/chef/support/shared_examples/geo_promotion_command_accepts_option_shared_examples.rb b/spec/chef/support/shared_examples/geo_promotion_command_accepts_option_shared_examples.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2V4YW1wbGVzL2dlb19wcm9tb3Rpb25fY29tbWFuZF9hY2NlcHRzX29wdGlvbl9zaGFyZWRfZXhhbXBsZXMucmI= --- /dev/null +++ b/spec/chef/support/shared_examples/geo_promotion_command_accepts_option_shared_examples.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +RSpec.shared_examples 'geo promotion command accepts option' do |passed_option, expected_option| + it 'accepts given option' do + allow_any_instance_of(klass).to receive(:execute).and_return(true) + + # ARGV contains the commands that were passed to rspec, which are + # invalid for the omnibus-ctl commands + oldargv = ARGV + ARGV = [passed_option] # rubocop:disable Style/MutableConstant + + expect(klass).to receive(:new).with( + anything, expected_option).and_call_original + + ctl.send(command_script, ARGV) + + ARGV = oldargv + end +end diff --git a/spec/chef/support/shared_examples/gitlab_geo_promotion_commands_shared_examples.rb b/spec/chef/support/shared_examples/gitlab_geo_promotion_commands_shared_examples.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2V4YW1wbGVzL2dpdGxhYl9nZW9fcHJvbW90aW9uX2NvbW1hbmRzX3NoYXJlZF9leGFtcGxlcy5yYg== --- /dev/null +++ b/spec/chef/support/shared_examples/gitlab_geo_promotion_commands_shared_examples.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +RSpec.shared_examples 'gitlab geo promotion commands' do + it 'appends a geo replication command' do + expect(ctl.get_all_commands_hash).to include(command_name) + end + + it 'executes the command when called' do + # ARGV contains the commands that were passed to rspec, which are + # invalid for the omnibus-ctl commands + oldargv = ARGV + ARGV = [] # rubocop:disable Style/MutableConstant + + expect_any_instance_of(klass).to receive(:execute) + + ctl.send(command_script) + + ARGV = oldargv + end +end diff --git a/spec/chef/support/shared_examples/gitlab_yml_object_storage_shared_examples.rb b/spec/chef/support/shared_examples/gitlab_yml_object_storage_shared_examples.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2V4YW1wbGVzL2dpdGxhYl95bWxfb2JqZWN0X3N0b3JhZ2Vfc2hhcmVkX2V4YW1wbGVzLnJi --- /dev/null +++ b/spec/chef/support/shared_examples/gitlab_yml_object_storage_shared_examples.rb @@ -0,0 +1,69 @@ +RSpec.shared_examples 'renders object storage settings in gitlab.yml' do |component, component_default = {}, workhorse_accelerated = true| + include_context 'gitlab-rails' + include_context 'object storage config' + + describe "for #{component}" do + context 'with default values' do + it 'renders gitlab.yml with object storage disabled and other default values' do + default_values = { + enabled: false, + remote_directory: component, + connection: {} + } + + if workhorse_accelerated + default_values.merge!( + direct_upload: false, + background_upload: true, + proxy_download: false + ) + end + + default_values.merge!(component_default) + + config = gitlab_yml[:production][component.to_sym][:object_store] + expect(config).to eq(default_values) + end + end + + context 'with user specified values' do + before do + gitlab_rails_config = { + "#{component}_object_store_enabled" => true, + "#{component}_object_store_remote_directory" => 'foobar', + "#{component}_object_store_connection" => aws_connection_hash + } + + if workhorse_accelerated + gitlab_rails_config.merge!( + "#{component}_object_store_direct_upload" => true, + "#{component}_object_store_background_upload" => false, + "#{component}_object_store_proxy_download" => true + ) + end + + stub_gitlab_rb( + gitlab_rails: gitlab_rails_config.transform_keys(&:to_sym) + ) + end + + it 'renders gitlab.yml with user specified values' do + expected_output = { + enabled: true, + connection: aws_connection_data, + remote_directory: 'foobar' + } + + if workhorse_accelerated + expected_output.merge!( + direct_upload: true, + background_upload: false, + proxy_download: true + ) + end + + expect(gitlab_yml[:production][component.to_sym][:object_store]).to eq(expected_output) + end + end + end +end diff --git a/spec/chef/support/shared_examples/logrotate_config.rb b/spec/chef/support/shared_examples/logrotate_config.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2V4YW1wbGVzL2xvZ3JvdGF0ZV9jb25maWcucmI= --- /dev/null +++ b/spec/chef/support/shared_examples/logrotate_config.rb @@ -0,0 +1,9 @@ +RSpec.shared_examples 'configured logrotate service' do |svc, username = 'git', group = 'git'| + it 'creates logrotate config file' do + expect(chef_run).to render_file("/var/opt/gitlab/logrotate/logrotate.d/#{svc}") + end + + it 'specifies su parameter in logrotate config' do + expect(chef_run).to render_file("/var/opt/gitlab/logrotate/logrotate.d/#{svc}").with_content(/su #{username} #{group}/) + end +end diff --git a/spec/chef/support/shared_examples/registry_service.rb b/spec/chef/support/shared_examples/registry_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2V4YW1wbGVzL3JlZ2lzdHJ5X3NlcnZpY2UucmI= --- /dev/null +++ b/spec/chef/support/shared_examples/registry_service.rb @@ -0,0 +1,102 @@ +RSpec.shared_examples 'enabled registry service' do + it 'creates default set of directories' do + expect(chef_run.node['registry']['dir']) + .to eql('/var/opt/gitlab/registry') + expect(chef_run.node['registry']['log_directory']) + .to eql('/var/log/gitlab/registry') + expect(chef_run.node['gitlab']['gitlab-rails']['registry_path']) + .to eql('/var/opt/gitlab/gitlab-rails/shared/registry') + + expect(chef_run).to create_directory('/var/opt/gitlab/registry') + expect(chef_run).to create_directory('/var/log/gitlab/registry').with( + owner: 'registry', + group: nil, + mode: '0700' + ) + expect(chef_run).to create_directory('/var/opt/gitlab/gitlab-rails/shared/registry').with( + owner: 'registry', + group: 'git', + mode: '0770' + ) + end + + it 'creates default user and group' do + expect(chef_run.node['registry']['username']) + .to eql('registry') + expect(chef_run.node['registry']['group']) + .to eql('registry') + + expect(chef_run).to create_account('Docker registry user and group').with( + username: 'registry', + groupname: 'registry', + uid: nil, + gid: nil, + system: true, + home: '/var/opt/gitlab/registry' + ) + end + + it 'creates default self signed key-certificate pair' do + expect(chef_run).to create_file('/var/opt/gitlab/registry/gitlab-registry.crt').with( + user: 'registry', + group: 'registry' + ) + expect(chef_run).to create_file('/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key').with( + user: 'git', + group: 'git' + ) + + expect(chef_run).to render_file('/var/opt/gitlab/registry/gitlab-registry.crt') + .with_content(/-----BEGIN CERTIFICATE-----/) + expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key') + .with_content(/-----BEGIN RSA PRIVATE KEY-----/) + end + + it 'creates registry config.yml template' do + expect(chef_run).to create_template('/var/opt/gitlab/registry/config.yml').with( + owner: 'registry', + group: nil, + mode: '0644' + ) + expect(chef_run).to( + render_file('/var/opt/gitlab/registry/config.yml').with_content do |content| + expect(content).to match(/version: 0.1/) + expect(content).to match(/realm: .*\/jwt\/auth/) + expect(content).to match(/addr: localhost:5000/) + expect(content).to match(%r(storage: {"filesystem":{"rootdirectory":"/var/opt/gitlab/gitlab-rails/shared/registry"})) + expect(content).to match(/health:\s*storagedriver:\s*enabled:\s*true/) + expect(content).to match(/log:\s*level: info\s*formatter:\s*text/) + expect(content).to match(/validation:\s*disabled: true$/) + expect(content).not_to match(/^compatibility:/) + expect(content).not_to match(/^middleware:/) + end + ) + end + + it 'populates default settings for svlogd' do + expect(chef_run).to render_file('/opt/gitlab/sv/registry/log/run') + .with_content(/exec svlogd -tt \/var\/log\/gitlab\/registry/) + end + + it 'creates a default VERSION file and restarts service' do + expect(chef_run).to create_version_file('Create version file for Registry').with( + version_file_path: '/var/opt/gitlab/registry/VERSION', + version_check_cmd: '/opt/gitlab/embedded/bin/registry --version' + ) + + expect(chef_run.version_file('Create version file for Registry')).to notify('runit_service[registry]').to(:restart) + end + + it 'creates gitlab-rails config with default values' do + expect(chef_run).to create_templatesymlink('Create a gitlab.yml and create a symlink to Rails root').with_variables(hash_including('registry_api_url' => 'http://localhost:5000')) + end + + it 'sets default storage options' do + expect(chef_run.node['registry']['storage']['filesystem']) + .to eql('rootdirectory' => '/var/opt/gitlab/gitlab-rails/shared/registry') + expect(chef_run.node['registry']['storage']['cache']) + .to eql('blobdescriptor' => 'inmemory') + expect(chef_run.node['registry']['storage']['delete']) + .to eql('enabled' => true) + end +end diff --git a/spec/chef/support/shared_examples/runit_service.rb b/spec/chef/support/shared_examples/runit_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2V4YW1wbGVzL3J1bml0X3NlcnZpY2UucmI= --- /dev/null +++ b/spec/chef/support/shared_examples/runit_service.rb @@ -0,0 +1,120 @@ +def get_env_string(username, groupname) + env_string = "" + env_string << username if username + env_string << ":#{groupname}" if groupname + + env_string +end + +RSpec.shared_examples 'enabled runit service' do |svc_name, owner, group, username = nil, groupname = nil| + it 'creates directories' do + expect(chef_run).to create_directory("/opt/gitlab/sv/#{svc_name}").with( + owner: owner, + group: group, + mode: '0755' + ) + expect(chef_run).to create_directory("/opt/gitlab/sv/#{svc_name}/log").with( + owner: owner, + group: group, + mode: '0755' + ) + expect(chef_run).to create_directory("/opt/gitlab/sv/#{svc_name}/log/main").with( + owner: owner, + group: group, + mode: '0755' + ) + end + + it 'creates files' do + expect(chef_run).to create_template("/opt/gitlab/sv/#{svc_name}/run").with( + owner: owner, + group: group, + mode: '0755' + ) + expect(chef_run).to create_template("/opt/gitlab/sv/#{svc_name}/log/run").with( + owner: owner, + group: group, + mode: '0755' + ) + expect(chef_run).to create_template("/opt/gitlab/sv/#{svc_name}/log/config").with( + owner: owner, + group: group, + mode: '0644' + ) + + env_string = get_env_string(username, groupname) + + if env_string.empty? + expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/run").with_content(%r{.*}) + else + expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/run").with_content(/-u #{env_string}/) + expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/run").with_content(/-U #{env_string}/) + end + + expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/log/run").with_content(%r{.*}) + expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/log/config") + end + + it 'verifies that file ownership persists' do + expect(chef_run.template("/opt/gitlab/sv/#{svc_name}/log/config")).to notify("ruby_block[verify_chown_persisted_on_#{svc_name}]") + end + + it 'raises an error when file ownership does not persist' do + log_config_re = /\/opt\/gitlab\/sv\/.*\/log\/config/ + allow_any_instance_of(OmnibusHelper).to receive(:expected_user?).and_return(true) + allow_any_instance_of(OmnibusHelper).to receive(:expected_user?).with(log_config_re, anything).and_return(false) + + allow_any_instance_of(OmnibusHelper).to receive(:expected_group?).and_return(true) + allow_any_instance_of(OmnibusHelper).to receive(:expected_group?).with(log_config_re, anything).and_return(false) + + block = chef_run.find_resource('ruby_block', "verify_chown_persisted_on_#{svc_name}") + expect { block.block.call }.to raise_error("Unable to persist filesystem ownership changes of /opt/gitlab/sv/#{svc_name}/log/config. See https://docs.gitlab.com/ee/administration/high_availability/nfs.html#recommended-options for guidance.") + end + + it 'creates the symlink to the service directory' do + expect(chef_run).to create_link("/opt/gitlab/init/#{svc_name}").with(to: '/opt/gitlab/embedded/bin/sv') + end + + context 'gitlab customization' do + # These are specs related to changes we have made to the upstream runit cookbook + before do + %w(ok status control).each do |target| + file_name = ::File.join('/opt/gitlab/service', svc_name, 'log', 'supervise', target) + allow_any_instance_of(OmnibusHelper).to receive(:expected_owner?).with(file_name, username, groupname).and_return(false) + end + end + + it 'sets the supervise log files with correct permissions' do + # For some services, we set the ownership on the sv log files to allow supervisor_owner to run commands + unless username.nil? + expect(chef_run).to create_directory("/opt/gitlab/service/#{svc_name}/log/supervise").with(mode: '0755') + %w(ok status control).each do |target| + file_name = ::File.join('/opt/gitlab/service', svc_name, 'log', 'supervise', target) + expect(chef_run).to touch_file(file_name).with( + owner: username, + group: groupname + ) + end + end + end + end +end + +RSpec.shared_examples 'disabled runit service' do |svc_name| + it 'does not create directories' do + expect(chef_run).not_to create_directory("/opt/gitlab/sv/#{svc_name}") + expect(chef_run).not_to create_directory("/opt/gitlab/sv/#{svc_name}/log") + expect(chef_run).not_to create_directory("/opt/gitlab/sv/#{svc_name}/log/main") + end + + it 'does not create files' do + expect(chef_run).not_to create_template("/opt/gitlab/sv/#{svc_name}/run") + expect(chef_run).not_to create_template("/opt/gitlab/sv/#{svc_name}/log/run") + expect(chef_run).not_to create_template("/var/log/gitlab/#{svc_name}/config") + end + + it 'runs the disable service ruby block' do + allow_any_instance_of(Chef::Provider::RunitService).to receive(:enabled?).and_return(true) + expect(chef_run).to run_ruby_block("disable #{svc_name}") + end +end diff --git a/spec/chef/support/shared_examples/yaml_check.rb b/spec/chef/support/shared_examples/yaml_check.rb new file mode 100644 index 0000000000000000000000000000000000000000..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmL3N1cHBvcnQvc2hhcmVkX2V4YW1wbGVzL3lhbWxfY2hlY2sucmI= --- /dev/null +++ b/spec/chef/support/shared_examples/yaml_check.rb @@ -0,0 +1,9 @@ +require 'yaml' + +RSpec.shared_examples 'renders a valid YAML file' do |filename| + it 'parses YAML with no errors' do + expect(chef_run).to render_file(filename).with_content { |content| + expect { YAML.parse(content) }.not_to raise_error + } + end +end diff --git a/spec/chef_helper.rb b/spec/chef_helper.rb index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9jaGVmX2hlbHBlci5yYg==..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9jaGVmX2hlbHBlci5yYg== 100644 --- a/spec/chef_helper.rb +++ b/spec/chef_helper.rb @@ -5,6 +5,13 @@ require 'chefspec' require 'ohai' -# Load our cookbook libraries so we can stub them in our tests -cookbooks = %w(package gitlab gitaly mattermost gitlab-ee letsencrypt monitoring patroni gitlab-kas gitlab-pages pgbouncer consul) +# Load chef specific support libraries to provide common convenience methods for our tests +Dir["./spec/chef/support/**/*.rb"].each { |library| require library } + +# Load our cookbook libraries so we can stub them in our tests. package, gitlab +# and gitlab-ee needs to be loaded first as others depend on them for proper +# functionality. +cookbooks = %w[package gitlab gitlab-ee].map { |cookbook| File.join(__dir__, "../files/gitlab-cookbooks/#{cookbook}") } +cookbooks = cookbooks.concat(Dir[File.join(__dir__, "../files/gitlab-cookbooks/*")].select { |d| File.directory?(d) }).uniq + cookbooks.each do |cookbook| @@ -10,5 +17,5 @@ cookbooks.each do |cookbook| - Dir[File.join(__dir__, "../files/gitlab-cookbooks/#{cookbook}/libraries/**/*.rb")].each { |f| require f } + Dir["#{cookbook}/libraries/**/*.rb"].each { |library| require library } end def deep_clone(obj) @@ -35,7 +42,7 @@ config.platform = platform config.version = version - config.cookbook_path = ['files/gitlab-cookbooks/', 'spec/fixtures/cookbooks'] + config.cookbook_path = ['files/gitlab-cookbooks/', 'spec/chef/fixtures/cookbooks'] config.log_level = :error config.before do @@ -66,7 +73,9 @@ # Prevent chef converge from reloading any of our previously loaded libraries allow(Kernel).to receive(:load).and_call_original - cookbooks.each do |cookbook| + + cookbooks.each do |cookbook_path| + cookbook = File.basename(cookbook_path) allow(Kernel).to receive(:load).with(%r{#{cookbook}/libraries}).and_return(true) end diff --git a/spec/fixtures/cookbooks/acme/attributes/default.rb b/spec/fixtures/cookbooks/acme/attributes/default.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvYWNtZS9hdHRyaWJ1dGVzL2RlZmF1bHQucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/acme/attributes/default.rb +++ /dev/null @@ -1,1 +0,0 @@ -default['acme']['key_size'] = 2048 diff --git a/spec/fixtures/cookbooks/acme/metadata.rb b/spec/fixtures/cookbooks/acme/metadata.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvYWNtZS9tZXRhZGF0YS5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/acme/metadata.rb +++ /dev/null @@ -1,1 +0,0 @@ -name 'acme' diff --git a/spec/fixtures/cookbooks/acme/resources/certificate.rb b/spec/fixtures/cookbooks/acme/resources/certificate.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvYWNtZS9yZXNvdXJjZXMvY2VydGlmaWNhdGUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/acme/resources/certificate.rb +++ /dev/null @@ -1,15 +0,0 @@ -property :cn, String, name_property: true -property :crt, String -property :dir, [String, nil] -property :key, String -property :owner, [String, nil] -property :chain, [String, nil] -property :wwwroot, String -property :alt_names, Array -property :key_size, [Integer, nil] -property :crt, [String, nil] -property :group, [String, nil] -property :contact, [String, Array, nil] - -actions :create -default_action :create diff --git a/spec/fixtures/cookbooks/acme/resources/selfsigned.rb b/spec/fixtures/cookbooks/acme/resources/selfsigned.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvYWNtZS9yZXNvdXJjZXMvc2VsZnNpZ25lZC5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/acme/resources/selfsigned.rb +++ /dev/null @@ -1,8 +0,0 @@ -property :crt, String -property :key, String -property :key_size, Integer -property :chain, String -property :alt_names, Array - -actions :create -default_action :create diff --git a/spec/fixtures/cookbooks/test_consul/metadata.rb b/spec/fixtures/cookbooks/test_consul/metadata.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jb25zdWwvbWV0YWRhdGEucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_consul/metadata.rb +++ /dev/null @@ -1,2 +0,0 @@ -name 'test_consul' -depends 'consul' diff --git a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_address_port.rb b/spec/fixtures/cookbooks/test_consul/recipes/consul_service_address_port.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jb25zdWwvcmVjaXBlcy9jb25zdWxfc2VydmljZV9hZGRyZXNzX3BvcnQucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_address_port.rb +++ /dev/null @@ -1,6 +0,0 @@ -include_recipe 'test_consul::reload_consul' - -consul_service 'node-exporter' do - ip_address '0.0.0.0' - port 1234 -end diff --git a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_delete.rb b/spec/fixtures/cookbooks/test_consul/recipes/consul_service_delete.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jb25zdWwvcmVjaXBlcy9jb25zdWxfc2VydmljZV9kZWxldGUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_delete.rb +++ /dev/null @@ -1,5 +0,0 @@ -include_recipe 'test_consul::reload_consul' - -consul_service 'delete_me' do - action :delete -end diff --git a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_delete_no_reload.rb b/spec/fixtures/cookbooks/test_consul/recipes/consul_service_delete_no_reload.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jb25zdWwvcmVjaXBlcy9jb25zdWxfc2VydmljZV9kZWxldGVfbm9fcmVsb2FkLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_delete_no_reload.rb +++ /dev/null @@ -1,6 +0,0 @@ -include_recipe 'test_consul::reload_consul' - -consul_service 'delete_no_reload' do - action :delete - reload_service false -end diff --git a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_meta.rb b/spec/fixtures/cookbooks/test_consul/recipes/consul_service_meta.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jb25zdWwvcmVjaXBlcy9jb25zdWxfc2VydmljZV9tZXRhLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_meta.rb +++ /dev/null @@ -1,9 +0,0 @@ -include_recipe 'test_consul::reload_consul' - -meta = { some_key: "value" } - -consul_service 'node-exporter' do - ip_address '0.0.0.0' - port 1234 - meta meta -end diff --git a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_socket.rb b/spec/fixtures/cookbooks/test_consul/recipes/consul_service_socket.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jb25zdWwvcmVjaXBlcy9jb25zdWxfc2VydmljZV9zb2NrZXQucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_consul/recipes/consul_service_socket.rb +++ /dev/null @@ -1,6 +0,0 @@ -include_recipe 'test_consul::reload_consul' - -consul_service 'node-exporter' do - socket_address '0.0.0.0:5678' - reload_service false -end diff --git a/spec/fixtures/cookbooks/test_consul/recipes/reload_consul.rb b/spec/fixtures/cookbooks/test_consul/recipes/reload_consul.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jb25zdWwvcmVjaXBlcy9yZWxvYWRfY29uc3VsLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_consul/recipes/reload_consul.rb +++ /dev/null @@ -1,4 +0,0 @@ -# A stub to satisfy the `consul_service` -execute 'reload consul' do - action :nothing -end diff --git a/spec/fixtures/cookbooks/test_crond/metadata.rb b/spec/fixtures/cookbooks/test_crond/metadata.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jcm9uZC9tZXRhZGF0YS5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_crond/metadata.rb +++ /dev/null @@ -1,2 +0,0 @@ -name 'test_crond' -depends 'crond' diff --git a/spec/fixtures/cookbooks/test_crond/recipes/crond_job_delete.rb b/spec/fixtures/cookbooks/test_crond/recipes/crond_job_delete.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jcm9uZC9yZWNpcGVzL2Nyb25kX2pvYl9kZWxldGUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_crond/recipes/crond_job_delete.rb +++ /dev/null @@ -1,3 +0,0 @@ -crond_job 'delete' do - action :delete -end diff --git a/spec/fixtures/cookbooks/test_crond/recipes/crond_job_minimal.rb b/spec/fixtures/cookbooks/test_crond/recipes/crond_job_minimal.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9jcm9uZC9yZWNpcGVzL2Nyb25kX2pvYl9taW5pbWFsLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_crond/recipes/crond_job_minimal.rb +++ /dev/null @@ -1,4 +0,0 @@ -crond_job 'minimal' do - user "rspec" - command "echo 'Hello world'" -end diff --git a/spec/fixtures/cookbooks/test_gitlab/metadata.rb b/spec/fixtures/cookbooks/test_gitlab/metadata.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9naXRsYWIvbWV0YWRhdGEucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_gitlab/metadata.rb +++ /dev/null @@ -1,2 +0,0 @@ -name 'test_gitlab' -depends 'gitlab' diff --git a/spec/fixtures/cookbooks/test_gitlab/recipes/puma_config_create.rb b/spec/fixtures/cookbooks/test_gitlab/recipes/puma_config_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9naXRsYWIvcmVjaXBlcy9wdW1hX2NvbmZpZ19jcmVhdGUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_gitlab/recipes/puma_config_create.rb +++ /dev/null @@ -1,12 +0,0 @@ -puma_config '/var/opt/gitlab/gitlab-rails/etc/puma.rb' do - environment 'production' - pid '/opt/gitlab/var/puma/puma.pid' - state_path '/opt/gitlab/var/puma/puma.state' - listen_socket '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket' - listen_tcp '127.0.0.1:8080' - working_directory '/var/opt/gitlab/gitlab-rails/working' - worker_processes 2 - cookbook 'gitlab' - - action :create -end diff --git a/spec/fixtures/cookbooks/test_gitlab/recipes/puma_config_custom.rb b/spec/fixtures/cookbooks/test_gitlab/recipes/puma_config_custom.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9naXRsYWIvcmVjaXBlcy9wdW1hX2NvbmZpZ19jdXN0b20ucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_gitlab/recipes/puma_config_custom.rb +++ /dev/null @@ -1,14 +0,0 @@ -puma_config '/var/opt/gitlab/gitlab-rails/etc/puma.rb' do - environment 'production' - pid '/opt/gitlab/var/puma/puma.pid' - state_path '/opt/gitlab/var/puma/puma.state' - listen_socket '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket' - listen_tcp '127.0.0.1:8080' - working_directory '/var/opt/gitlab/gitlab-rails/working' - worker_processes 2 - per_worker_max_memory_mb 1000 - install_dir '/opt/custom/gitlab' - cookbook 'gitlab' - - action :create -end diff --git a/spec/fixtures/cookbooks/test_gitlab/recipes/rails_migration_run.rb b/spec/fixtures/cookbooks/test_gitlab/recipes/rails_migration_run.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9naXRsYWIvcmVjaXBlcy9yYWlsc19taWdyYXRpb25fcnVuLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_gitlab/recipes/rails_migration_run.rb +++ /dev/null @@ -1,12 +0,0 @@ -ruby_block 'test-dependent' do - true -end - -rails_migration 'gitlab-test' do - rake_task 'gitlab:db:configure' - logfile_prefix 'gitlab-test-db-migrate' - helper RailsMigrationHelper.new(node) - environment Hash['SOME_ENV' => 'some-value'] - - dependent_services ["ruby_block[test-dependent]"] -end diff --git a/spec/fixtures/cookbooks/test_gitlab_ee/metadata.rb b/spec/fixtures/cookbooks/test_gitlab_ee/metadata.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9naXRsYWJfZWUvbWV0YWRhdGEucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_gitlab_ee/metadata.rb +++ /dev/null @@ -1,2 +0,0 @@ -name 'test_gitlab_ee' -depends 'gitlab-ee' diff --git a/spec/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_geo.rb b/spec/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_geo.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9naXRsYWJfZWUvcmVjaXBlcy9wZ2JvdW5jZXJfdXNlcl9jcmVhdGVfZ2VvLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_geo.rb +++ /dev/null @@ -1,8 +0,0 @@ -pgbouncer_user 'geo' do - helper GeoPgHelper.new(node) - user 'pgbouncer-geo' - password 'fakepassword-geo' - database 'fakedb-geo' - add_auth_function true - action :create -end diff --git a/spec/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_rails.rb b/spec/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_rails.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9naXRsYWJfZWUvcmVjaXBlcy9wZ2JvdW5jZXJfdXNlcl9jcmVhdGVfcmFpbHMucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_gitlab_ee/recipes/pgbouncer_user_create_rails.rb +++ /dev/null @@ -1,8 +0,0 @@ -pgbouncer_user 'rails' do - helper PgHelper.new(node) - user 'pgbouncer-rails' - password 'fakepassword-rails' - database 'fakedb-rails' - add_auth_function true - action :create -end diff --git a/spec/fixtures/cookbooks/test_package/metadata.rb b/spec/fixtures/cookbooks/test_package/metadata.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL21ldGFkYXRhLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/metadata.rb +++ /dev/null @@ -1,3 +0,0 @@ -name 'test_package' -depends 'package' -depends 'runit' diff --git a/spec/fixtures/cookbooks/test_package/recipes/account_create.rb b/spec/fixtures/cookbooks/test_package/recipes/account_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3JlY2lwZXMvYWNjb3VudF9jcmVhdGUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/recipes/account_create.rb +++ /dev/null @@ -1,7 +0,0 @@ -account 'foobar' do - username 'foo' - groupname 'bar' - manage true - - action :create -end diff --git a/spec/fixtures/cookbooks/test_package/recipes/account_remove.rb b/spec/fixtures/cookbooks/test_package/recipes/account_remove.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3JlY2lwZXMvYWNjb3VudF9yZW1vdmUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/recipes/account_remove.rb +++ /dev/null @@ -1,7 +0,0 @@ -account 'foobar' do - username 'foo' - groupname 'bar' - manage true - - action :remove -end diff --git a/spec/fixtures/cookbooks/test_package/recipes/env_dir_create.rb b/spec/fixtures/cookbooks/test_package/recipes/env_dir_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3JlY2lwZXMvZW52X2Rpcl9jcmVhdGUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/recipes/env_dir_create.rb +++ /dev/null @@ -1,8 +0,0 @@ -vars = { - 'FOO' => 'Lorem', - 'BAR' => 'Ipsum' -} - -env_dir '/tmp/env' do - variables vars -end diff --git a/spec/fixtures/cookbooks/test_package/recipes/gitlab_sysctl_create.rb b/spec/fixtures/cookbooks/test_package/recipes/gitlab_sysctl_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3JlY2lwZXMvZ2l0bGFiX3N5c2N0bF9jcmVhdGUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/recipes/gitlab_sysctl_create.rb +++ /dev/null @@ -1,3 +0,0 @@ -gitlab_sysctl 'foo' do - value 15000 -end diff --git a/spec/fixtures/cookbooks/test_package/recipes/storage_directory_create.rb b/spec/fixtures/cookbooks/test_package/recipes/storage_directory_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3JlY2lwZXMvc3RvcmFnZV9kaXJlY3RvcnlfY3JlYXRlLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/recipes/storage_directory_create.rb +++ /dev/null @@ -1,5 +0,0 @@ -storage_directory '/tmp/bar' do - owner 'gitfoo' - group 'gitbar' - mode '0700' -end diff --git a/spec/fixtures/cookbooks/test_package/recipes/templatesymlink_create.rb b/spec/fixtures/cookbooks/test_package/recipes/templatesymlink_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3JlY2lwZXMvdGVtcGxhdGVzeW1saW5rX2NyZWF0ZS5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/recipes/templatesymlink_create.rb +++ /dev/null @@ -1,6 +0,0 @@ -templatesymlink 'sample template' do - link_from '/opt/gitlab/embedded/service/gitlab-rails/config/database.yml' - link_to '/var/opt/gitlab/gitlab-rails/etc/database.yml' - source 'test-template.erb' - variables(value: 500) -end diff --git a/spec/fixtures/cookbooks/test_package/recipes/templatesymlink_delete.rb b/spec/fixtures/cookbooks/test_package/recipes/templatesymlink_delete.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3JlY2lwZXMvdGVtcGxhdGVzeW1saW5rX2RlbGV0ZS5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/recipes/templatesymlink_delete.rb +++ /dev/null @@ -1,6 +0,0 @@ -templatesymlink 'delete' do - link_from '/opt/gitlab/embedded/service/gitlab-rails/config/database.yml' - link_to '/var/opt/gitlab/gitlab-rails/etc/database.yml' - - action :delete -end diff --git a/spec/fixtures/cookbooks/test_package/recipes/version_file_create.rb b/spec/fixtures/cookbooks/test_package/recipes/version_file_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3JlY2lwZXMvdmVyc2lvbl9maWxlX2NyZWF0ZS5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/recipes/version_file_create.rb +++ /dev/null @@ -1,8 +0,0 @@ -runit_service 'foo' do -end - -version_file 'Test version file creation' do - version_file_path '/tmp/VERSION_TEST' - version_check_cmd 'echo 1.0.0-test' - notifies :hup, "runit_service[foo]" -end diff --git a/spec/fixtures/cookbooks/test_package/templates/default/test-template.erb b/spec/fixtures/cookbooks/test_package/templates/default/test-template.erb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wYWNrYWdlL3RlbXBsYXRlcy9kZWZhdWx0L3Rlc3QtdGVtcGxhdGUuZXJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_package/templates/default/test-template.erb +++ /dev/null @@ -1,1 +0,0 @@ -value: <%= @value %> diff --git a/spec/fixtures/cookbooks/test_postgresql/metadata.rb b/spec/fixtures/cookbooks/test_postgresql/metadata.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL21ldGFkYXRhLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/metadata.rb +++ /dev/null @@ -1,3 +0,0 @@ -name 'test_postgresql' -depends 'postgresql' -depends 'gitlab' diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_config.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_config.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF9jb25maWcucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_config.rb +++ /dev/null @@ -1,1 +0,0 @@ -postgresql_config 'testconfig' diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF9kYXRhYmFzZV9jcmVhdGUucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create.rb +++ /dev/null @@ -1,2 +0,0 @@ -postgresql_database 'example' do -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create_with_options.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create_with_options.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF9kYXRhYmFzZV9jcmVhdGVfd2l0aF9vcHRpb25zLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_database_create_with_options.rb +++ /dev/null @@ -1,5 +0,0 @@ -postgresql_database 'fakedb' do - database_port 9999 - database_socket '/fake/dir' - owner 'fakeuser' -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_extension_enable.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_extension_enable.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF9leHRlbnNpb25fZW5hYmxlLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_extension_enable.rb +++ /dev/null @@ -1,3 +0,0 @@ -postgresql_extension 'foobar' do - database 'lorem' -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_query_run.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_query_run.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF9xdWVyeV9ydW4ucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_query_run.rb +++ /dev/null @@ -1,12 +0,0 @@ -require_relative '../../../../../files/gitlab-cookbooks/gitlab/libraries/helpers/geo_pg_helper' - -postgresql_query 'create schema' do - db_name 'omnibus_test' - query "CREATE SCHEMA example AUTHORIZATION foobar;" -end - -postgresql_query 'create schema' do - db_name 'omnibus_test' - query "CREATE SCHEMA example AUTHORIZATION foobar;" - helper GeoPgHelper.new(node) -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_schema_create.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_schema_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF9zY2hlbWFfY3JlYXRlLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_schema_create.rb +++ /dev/null @@ -1,3 +0,0 @@ -postgresql_schema 'example' do - database 'omnibus_gitlab_test' -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_create.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_create.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF91c2VyX2NyZWF0ZS5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_create.rb +++ /dev/null @@ -1,2 +0,0 @@ -postgresql_user 'example' do -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_superuser.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_superuser.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF91c2VyX29wdGlvbnNfc3VwZXJ1c2VyLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_superuser.rb +++ /dev/null @@ -1,3 +0,0 @@ -postgresql_user 'example' do - options ['SUPERUSER'] -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_unspecified.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_unspecified.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF91c2VyX29wdGlvbnNfdW5zcGVjaWZpZWQucmI=..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_options_unspecified.rb +++ /dev/null @@ -1,2 +0,0 @@ -postgresql_user 'example' do -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_empty.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_empty.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF91c2VyX3Bhc3N3b3JkX2VtcHR5LnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_empty.rb +++ /dev/null @@ -1,3 +0,0 @@ -postgresql_user 'empty_password' do - password '' -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_md5.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_md5.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF91c2VyX3Bhc3N3b3JkX21kNS5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_md5.rb +++ /dev/null @@ -1,5 +0,0 @@ -postgresql_user 'md5_password' do - # This is not a secret password: - # ruby -rdigest -e 'puts Digest::MD5.hexdigest("toomanysecrets" + "md5_password")' - password 'e99b79fbdf9b997e6918df2385e60f5c' -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_nil.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_nil.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF91c2VyX3Bhc3N3b3JkX25pbC5yYg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_nil.rb +++ /dev/null @@ -1,3 +0,0 @@ -postgresql_user 'nil_password' do - password nil -end diff --git a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_unspecified.rb b/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_unspecified.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9jb29rYm9va3MvdGVzdF9wb3N0Z3Jlc3FsL3JlY2lwZXMvcG9zdGdyZXNxbF91c2VyX3Bhc3N3b3JkX3Vuc3BlY2lmaWVkLnJi..0000000000000000000000000000000000000000 --- a/spec/fixtures/cookbooks/test_postgresql/recipes/postgresql_user_password_unspecified.rb +++ /dev/null @@ -1,2 +0,0 @@ -postgresql_user 'no_password' do -end diff --git a/spec/fixtures/fauxhai/ubuntu/16.04-docker.json b/spec/fixtures/fauxhai/ubuntu/16.04-docker.json deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9mYXV4aGFpL3VidW50dS8xNi4wNC1kb2NrZXIuanNvbg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/fauxhai/ubuntu/16.04-docker.json +++ /dev/null @@ -1,2130 +0,0 @@ -{ - "block_device": { - "sda": { - "size": "1677721600", - "removable": "0", - "model": "PersistentDisk", - "rev": "1", - "state": "running", - "timeout": "30", - "vendor": "Google", - "queue_depth": "256", - "rotational": "1", - "physical_block_size": "4096", - "logical_block_size": "512" - }, - "loop0": { - "size": "177368", - "removable": "0", - "rotational": "1", - "physical_block_size": "512", - "logical_block_size": "512" - }, - "loop1": { - "size": "177960", - "removable": "0", - "rotational": "1", - "physical_block_size": "512", - "logical_block_size": "512" - }, - "loop2": { - "size": "177928", - "removable": "0", - "rotational": "1", - "physical_block_size": "512", - "logical_block_size": "512" - }, - "loop3": { - "size": "0", - "removable": "0", - "rotational": "1", - "physical_block_size": "512", - "logical_block_size": "512" - }, - "loop4": { - "size": "0", - "removable": "0", - "rotational": "1", - "physical_block_size": "512", - "logical_block_size": "512" - }, - "loop5": { - "size": "0", - "removable": "0", - "rotational": "1", - "physical_block_size": "512", - "logical_block_size": "512" - }, - "loop6": { - "size": "0", - "removable": "0", - "rotational": "1", - "physical_block_size": "512", - "logical_block_size": "512" - }, - "loop7": { - "size": "0", - "removable": "0", - "rotational": "1", - "physical_block_size": "512", - "logical_block_size": "512" - } - }, - "chef_packages": { - "ohai": { - "version": "14.3.0", - "ohai_root": "/var/lib/gems/2.4.0/gems/ohai-14.3.0/lib/ohai" - } - }, - "command": { - "ps": "ps -ef" - }, - "counters": { - "network": { - "interfaces": { - "lo": { - "tx": { - "queuelen": "1", - "bytes": 0, - "packets": 0, - "errors": 0, - "drop": 0, - "carrier": 0, - "collisions": 0 - }, - "rx": { - "bytes": 0, - "packets": 0, - "errors": 0, - "drop": 0, - "overrun": 0 - } - }, - "eth0": { - "rx": { - "bytes": 0, - "packets": 0, - "errors": 0, - "drop": 0, - "overrun": 0, - "frame": 0, - "compressed": 0, - "multicast": 0 - }, - "tx": { - "bytes": 0, - "packets": 0, - "errors": 0, - "drop": 0, - "overrun": 0, - "collisions": 0, - "carrier": 0, - "compressed": 0 - } - } - } - } - }, - "cpu": { - "real": 1, - "total": 1, - "cores": 1 - }, - "current_user": "fauxhai", - "dmi": { - }, - "domain": "local", - "filesystem": { - "by_device": { - "overlay": { - "kb_size": "813139348", - "kb_used": "314504628", - "kb_available": "498618336", - "percent_used": "39%", - "total_inodes": "102400000", - "inodes_used": "7082272", - "inodes_available": "95317728", - "inodes_percent_used": "7%", - "fs_type": "overlay", - "mount_options": [ - "rw", - "relatime", - "lowerdir=/var/lib/docker/overlay2/l/UVFEI53IFKBMSYIBD6MEWPNZ6F:/var/lib/docker/overlay2/l/BBMNBZRYFC727VBBGOEXGFD3HA:/var/lib/docker/overlay2/l/3CXZ5G5TCWJEUVMPSRSMBQD6Z4:/var/lib/docker/overlay2/l/GCI6OPPHSEJXZTDES7HKM4FJOA:/var/lib/docker/overlay2/l/XQ7VNACY7RTHN2VODETXL7PG2B:/var/lib/docker/overlay2/l/O63N6IYQUXRP6ATLHTRWF76PUK", - "upperdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/diff", - "workdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/work" - ], - "mounts": [ - "/" - ] - }, - "tmpfs": { - "kb_size": "3829544", - "kb_used": "0", - "kb_available": "3829544", - "percent_used": "0%", - "total_inodes": "957386", - "inodes_used": "1", - "inodes_available": "957385", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ], - "mounts": [ - "/dev", - "/sys/fs/cgroup", - "/sys/firmware", - "/proc/kcore", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug" - ] - }, - "/dev/sda1": { - "kb_size": "813139348", - "kb_used": "314504628", - "kb_available": "498618336", - "percent_used": "39%", - "total_inodes": "102400000", - "inodes_used": "7082272", - "inodes_available": "95317728", - "inodes_percent_used": "7%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "mounts": [ - "/etc/hosts", - "/etc/resolv.conf", - "/etc/hostname" - ] - }, - "shm": { - "kb_size": "65536", - "kb_used": "0", - "kb_available": "65536", - "percent_used": "0%", - "total_inodes": "957386", - "inodes_used": "1", - "inodes_available": "957385", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=65536k" - ], - "mounts": [ - "/dev/shm" - ] - }, - "proc": { - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ], - "mounts": [ - "/proc", - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger" - ] - }, - "devpts": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=666" - ], - "mounts": [ - "/dev/pts", - "/dev/console" - ] - }, - "sysfs": { - "fs_type": "sysfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys" - ] - }, - "cgroup": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ], - "mounts": [ - "/sys/fs/cgroup/systemd", - "/sys/fs/cgroup/freezer", - "/sys/fs/cgroup/hugetlb", - "/sys/fs/cgroup/cpu,cpuacct", - "/sys/fs/cgroup/net_cls,net_prio", - "/sys/fs/cgroup/pids", - "/sys/fs/cgroup/blkio", - "/sys/fs/cgroup/devices", - "/sys/fs/cgroup/cpuset", - "/sys/fs/cgroup/perf_event", - "/sys/fs/cgroup/memory" - ] - }, - "mqueue": { - "fs_type": "mqueue", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/dev/mqueue" - ] - }, - "sda": { - "mounts": [ - - ] - }, - "sda1": { - "mounts": [ - - ] - }, - "loop0": { - "mounts": [ - - ] - }, - "loop1": { - "mounts": [ - - ] - }, - "loop2": { - "mounts": [ - - ] - } - }, - "by_mountpoint": { - "/": { - "kb_size": "813139348", - "kb_used": "314504628", - "kb_available": "498618336", - "percent_used": "39%", - "total_inodes": "102400000", - "inodes_used": "7082272", - "inodes_available": "95317728", - "inodes_percent_used": "7%", - "fs_type": "overlay", - "mount_options": [ - "rw", - "relatime", - "lowerdir=/var/lib/docker/overlay2/l/UVFEI53IFKBMSYIBD6MEWPNZ6F:/var/lib/docker/overlay2/l/BBMNBZRYFC727VBBGOEXGFD3HA:/var/lib/docker/overlay2/l/3CXZ5G5TCWJEUVMPSRSMBQD6Z4:/var/lib/docker/overlay2/l/GCI6OPPHSEJXZTDES7HKM4FJOA:/var/lib/docker/overlay2/l/XQ7VNACY7RTHN2VODETXL7PG2B:/var/lib/docker/overlay2/l/O63N6IYQUXRP6ATLHTRWF76PUK", - "upperdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/diff", - "workdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/work" - ], - "devices": [ - "overlay" - ] - }, - "/dev": { - "kb_size": "65536", - "kb_used": "0", - "kb_available": "65536", - "percent_used": "0%", - "total_inodes": "957386", - "inodes_used": "17", - "inodes_available": "957369", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/sys/fs/cgroup": { - "kb_size": "3829544", - "kb_used": "0", - "kb_available": "3829544", - "percent_used": "0%", - "total_inodes": "957386", - "inodes_used": "16", - "inodes_available": "957370", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/etc/hosts": { - "kb_size": "813139348", - "kb_used": "314504628", - "kb_available": "498618336", - "percent_used": "39%", - "total_inodes": "102400000", - "inodes_used": "7082272", - "inodes_available": "95317728", - "inodes_percent_used": "7%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "devices": [ - "/dev/sda1" - ] - }, - "/dev/shm": { - "kb_size": "65536", - "kb_used": "0", - "kb_available": "65536", - "percent_used": "0%", - "total_inodes": "957386", - "inodes_used": "1", - "inodes_available": "957385", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=65536k" - ], - "devices": [ - "shm" - ] - }, - "/sys/firmware": { - "kb_size": "3829544", - "kb_used": "0", - "kb_available": "3829544", - "percent_used": "0%", - "total_inodes": "957386", - "inodes_used": "1", - "inodes_available": "957385", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "relatime" - ], - "devices": [ - "tmpfs" - ] - }, - "/proc": { - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/dev/pts": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=666" - ], - "devices": [ - "devpts" - ] - }, - "/sys": { - "fs_type": "sysfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "sysfs" - ] - }, - "/sys/fs/cgroup/systemd": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "xattr", - "release_agent=/lib/systemd/systemd-cgroups-agent", - "name=systemd" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/freezer": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "freezer" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/hugetlb": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/cpu,cpuacct": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpu", - "cpuacct" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/net_cls,net_prio": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "net_cls", - "net_prio" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/pids": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "pids" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/blkio": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "blkio" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/devices": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "devices" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/cpuset": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpuset" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/perf_event": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "perf_event" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/memory": { - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ], - "devices": [ - "cgroup" - ] - }, - "/dev/mqueue": { - "fs_type": "mqueue", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "mqueue" - ] - }, - "/etc/resolv.conf": { - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "devices": [ - "/dev/sda1" - ] - }, - "/etc/hostname": { - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "devices": [ - "/dev/sda1" - ] - }, - "/dev/console": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=666" - ], - "devices": [ - "devpts" - ] - }, - "/proc/bus": { - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/proc/fs": { - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/proc/irq": { - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/proc/sys": { - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/proc/sysrq-trigger": { - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/proc/kcore": { - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/proc/timer_list": { - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/proc/timer_stats": { - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/proc/sched_debug": { - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - } - }, - "by_pair": { - "overlay,/": { - "device": "overlay", - "kb_size": "813139348", - "kb_used": "314504628", - "kb_available": "498618336", - "percent_used": "39%", - "mount": "/", - "total_inodes": "102400000", - "inodes_used": "7082272", - "inodes_available": "95317728", - "inodes_percent_used": "7%", - "fs_type": "overlay", - "mount_options": [ - "rw", - "relatime", - "lowerdir=/var/lib/docker/overlay2/l/UVFEI53IFKBMSYIBD6MEWPNZ6F:/var/lib/docker/overlay2/l/BBMNBZRYFC727VBBGOEXGFD3HA:/var/lib/docker/overlay2/l/3CXZ5G5TCWJEUVMPSRSMBQD6Z4:/var/lib/docker/overlay2/l/GCI6OPPHSEJXZTDES7HKM4FJOA:/var/lib/docker/overlay2/l/XQ7VNACY7RTHN2VODETXL7PG2B:/var/lib/docker/overlay2/l/O63N6IYQUXRP6ATLHTRWF76PUK", - "upperdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/diff", - "workdir=/var/lib/docker/overlay2/a7bc5c3ab80b1b7dd4459606daa24928d243be3cce39890ed9a7ea796f2d3c05/work" - ] - }, - "tmpfs,/dev": { - "device": "tmpfs", - "kb_size": "65536", - "kb_used": "0", - "kb_available": "65536", - "percent_used": "0%", - "mount": "/dev", - "total_inodes": "957386", - "inodes_used": "17", - "inodes_available": "957369", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ] - }, - "tmpfs,/sys/fs/cgroup": { - "device": "tmpfs", - "kb_size": "3829544", - "kb_used": "0", - "kb_available": "3829544", - "percent_used": "0%", - "mount": "/sys/fs/cgroup", - "total_inodes": "957386", - "inodes_used": "16", - "inodes_available": "957370", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "mode=755" - ] - }, - "/dev/sda1,/etc/hosts": { - "device": "/dev/sda1", - "kb_size": "813139348", - "kb_used": "314504628", - "kb_available": "498618336", - "percent_used": "39%", - "mount": "/etc/hosts", - "total_inodes": "102400000", - "inodes_used": "7082272", - "inodes_available": "95317728", - "inodes_percent_used": "7%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ] - }, - "shm,/dev/shm": { - "device": "shm", - "kb_size": "65536", - "kb_used": "0", - "kb_available": "65536", - "percent_used": "0%", - "mount": "/dev/shm", - "total_inodes": "957386", - "inodes_used": "1", - "inodes_available": "957385", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=65536k" - ] - }, - "tmpfs,/sys/firmware": { - "device": "tmpfs", - "kb_size": "3829544", - "kb_used": "0", - "kb_available": "3829544", - "percent_used": "0%", - "mount": "/sys/firmware", - "total_inodes": "957386", - "inodes_used": "1", - "inodes_available": "957385", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "relatime" - ] - }, - "proc,/proc": { - "device": "proc", - "mount": "/proc", - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "devpts,/dev/pts": { - "device": "devpts", - "mount": "/dev/pts", - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=666" - ] - }, - "sysfs,/sys": { - "device": "sysfs", - "mount": "/sys", - "fs_type": "sysfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "cgroup,/sys/fs/cgroup/systemd": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/systemd", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "xattr", - "release_agent=/lib/systemd/systemd-cgroups-agent", - "name=systemd" - ] - }, - "cgroup,/sys/fs/cgroup/freezer": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/freezer", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "freezer" - ] - }, - "cgroup,/sys/fs/cgroup/hugetlb": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/hugetlb", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ] - }, - "cgroup,/sys/fs/cgroup/cpu,cpuacct": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/cpu,cpuacct", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpu", - "cpuacct" - ] - }, - "cgroup,/sys/fs/cgroup/net_cls,net_prio": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/net_cls,net_prio", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "net_cls", - "net_prio" - ] - }, - "cgroup,/sys/fs/cgroup/pids": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/pids", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "pids" - ] - }, - "cgroup,/sys/fs/cgroup/blkio": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/blkio", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "blkio" - ] - }, - "cgroup,/sys/fs/cgroup/devices": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/devices", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "devices" - ] - }, - "cgroup,/sys/fs/cgroup/cpuset": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/cpuset", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpuset" - ] - }, - "cgroup,/sys/fs/cgroup/perf_event": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/perf_event", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "perf_event" - ] - }, - "cgroup,/sys/fs/cgroup/memory": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/memory", - "fs_type": "cgroup", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ] - }, - "mqueue,/dev/mqueue": { - "device": "mqueue", - "mount": "/dev/mqueue", - "fs_type": "mqueue", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "/dev/sda1,/etc/resolv.conf": { - "device": "/dev/sda1", - "mount": "/etc/resolv.conf", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ] - }, - "/dev/sda1,/etc/hostname": { - "device": "/dev/sda1", - "mount": "/etc/hostname", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ] - }, - "devpts,/dev/console": { - "device": "devpts", - "mount": "/dev/console", - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=666" - ] - }, - "proc,/proc/bus": { - "device": "proc", - "mount": "/proc/bus", - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ] - }, - "proc,/proc/fs": { - "device": "proc", - "mount": "/proc/fs", - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ] - }, - "proc,/proc/irq": { - "device": "proc", - "mount": "/proc/irq", - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ] - }, - "proc,/proc/sys": { - "device": "proc", - "mount": "/proc/sys", - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ] - }, - "proc,/proc/sysrq-trigger": { - "device": "proc", - "mount": "/proc/sysrq-trigger", - "fs_type": "proc", - "mount_options": [ - "ro", - "relatime" - ] - }, - "tmpfs,/proc/kcore": { - "device": "tmpfs", - "mount": "/proc/kcore", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ] - }, - "tmpfs,/proc/timer_list": { - "device": "tmpfs", - "mount": "/proc/timer_list", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ] - }, - "tmpfs,/proc/timer_stats": { - "device": "tmpfs", - "mount": "/proc/timer_stats", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ] - }, - "tmpfs,/proc/sched_debug": { - "device": "tmpfs", - "mount": "/proc/sched_debug", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "size=65536k", - "mode=755" - ] - }, - "sda,": { - "device": "sda" - }, - "sda1,": { - "device": "sda1" - }, - "loop0,": { - "device": "loop0" - }, - "loop1,": { - "device": "loop1" - }, - "loop2,": { - "device": "loop2" - } - } - }, - "fips": { - "kernel": { - "enabled": false - } - }, - "fqdn": "fauxhai.local", - "hostname": "Fauxhai", - "idle": "30 days 15 hours 07 minutes 30 seconds", - "idletime_seconds": 2646450, - "init_package": "bash", - "ipaddress": "10.0.0.2", - "kernel": { - "name": "Linux", - "release": "4.4.0-130-generic", - "version": "#156-Ubuntu SMP Thu Jun 14 08:53:28 UTC 2018", - "machine": "x86_64", - "processor": "x86_64", - "os": "GNU/Linux", - "modules": { - } - }, - "keys": { - "ssh": { - "host_dsa_public": "ssh-dss AAAAB3NzaC1kc3MAAACBAJFo9BLAw4WKEs5hgipk5m423FzBsDXCZSMcC9ca/om/1VYzMqImixGe3uICDzNFUWxFoLJTQAOccyzo6MXZiQqwWJDLFi5qOSr6w2XcMyE+zd4wOyMoDiVM5fizmG8K3FzrqvGjwBcHcBdOQnavSijoj38DN25J9zhrid5BY4WlAAAAFQDxXrCyG52XCzn3FV4ej38wJBkomQAAAIBovGPJ4mP2P6BK8lHl0PPbktwQbWlpJ13oz6REJFDVcUi7vV26bX/BjQX+ohzZQzljdz1SpUbPc/8nuA4darYkVh91eBi307EN8IdxRHj2eBgp/ZG4yshIebG3WHrwJD/xUjjZ1MRfyDT1ermVi4LvjjPgWDxLZnPpMaR6S1nzgQAAAIEAj0Vd6DCWslvlsZ8+N53HWsqPi3gnx35JoLPz9Z2epkKIKqmEHav+93G3hdfztVa4I4t3phoPniQchYryF5+RNg8hqxKzjNtrIqUYCeuf2NJrksNsH7OZygPHZpqt4kTuwAGZxjxEGfAI0y8DhkU2ntp2LnzRnWH106BQBCmcXwo= fauxhai.local", - "host_rsa_public": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtLCeqtqr/HbnORckw1ukdLhpfGoOPFi5/esKEokzTqq1gsgQ2V8emmyjfq1i6XXfRtSBxkdlHv/GWdP5wBTuE2G85MzBkVSQPvmwQN8lX/JMPEEtKXkeOo0o92/PiSmvY4eRsdF0mw40Uvg7jtE3f3fxj497kzh5fKtkrHnF4x9gXCbVdr3FqXJfggR5IJwAxToerbK7x/uRS+7YuZI9Pip3tt14nv9ezwXcuGb/tvjWOZINiFl8izVIFKi7sxfTX09p4NgamxRS7TD2Yd0jT8nEoF9UZTsgXcJ1kDSx7N7NxFfNfP6rCdOGRRz4gUhXtsUjG/XkxPeCwZ7A9VnOD fauxhai.local" - } - }, - "languages": { - "ruby": { - "platform": "x86_64-linux-gnu", - "version": "2.4.4", - "release_date": "2018-03-28", - "target": "x86_64-pc-linux-gnu", - "target_cpu": "x86_64", - "target_vendor": "pc", - "target_os": "linux-gnu", - "host": "x86_64-pc-linux-gnu", - "host_cpu": "x86_64", - "host_os": "linux-gnu", - "host_vendor": "pc", - "bin_dir": "/usr/local/bin", - "ruby_bin": "/usr/local/bin/ruby", - "gems_dir": "/usr/local/gems", - "gem_bin": "/usr/local/bin/gem" - }, - "powershell": null - }, - "lsb": { - "id": "Ubuntu", - "release": "16.04", - "codename": "xenial", - "description": "Ubuntu 16.04.3 LTS" - }, - "macaddress": "11:11:11:11:11:11", - "machinename": "Fauxhai", - "memory": { - "total": "1048576kB" - }, - "network": { - "interfaces": { - "lo": { - "mtu": "65536", - "flags": [ - "LOOPBACK", - "UP", - "LOWER_UP" - ], - "encapsulation": "Loopback", - "addresses": { - "127.0.0.1": { - "family": "inet", - "prefixlen": "8", - "netmask": "255.0.0.0", - "scope": "Node", - "ip_scope": "LOOPBACK" - }, - "::1": { - "family": "inet6", - "prefixlen": "128", - "scope": "Node", - "tags": [ - - ], - "ip_scope": "LINK LOCAL LOOPBACK" - } - }, - "state": "unknown" - }, - "eth0": { - "type": "eth", - "number": "0", - "mtu": "1500", - "flags": [ - "BROADCAST", - "MULTICAST", - "UP", - "LOWER_UP" - ], - "encapsulation": "Ethernet", - "addresses": { - "11:11:11:11:11:11": { - "family": "lladdr" - }, - "10.0.0.2": { - "family": "inet", - "prefixlen": "24", - "netmask": "255.255.255.0", - "broadcast": "10.0.0.255", - "scope": "Global", - "ip_scope": "RFC1918 PRIVATE" - }, - "fe80::11:1111:1111:1111": { - "family": "inet6", - "prefixlen": "64", - "scope": "Link", - "tags": [ - - ], - "ip_scope": "LINK LOCAL UNICAST" - } - }, - "state": "up", - "arp": { - "10.0.0.1": "fe:ff:ff:ff:ff:ff" - }, - "routes": [ - { - "destination": "default", - "family": "inet", - "via": "10.0.0.1" - }, - { - "destination": "10.0.0.0/24", - "family": "inet", - "scope": "link", - "proto": "kernel", - "src": "10.0.0.2" - }, - { - "destination": "fe80::/64", - "family": "inet6", - "metric": "256", - "proto": "kernel" - } - ], - "ring_params": { - } - } - }, - "default_interface": "eth0", - "default_gateway": "10.0.0.1" - }, - "ohai_time": 1532332846.1504228, - "os": "linux", - "os_version": "4.4.0-130-generic", - "packages": { - "adduser": { - "version": "3.113+nmu3ubuntu4", - "arch": "all" - }, - "apt": { - "version": "1.2.27", - "arch": "amd64" - }, - "apt-utils": { - "version": "1.2.27", - "arch": "amd64" - }, - "base-files": { - "version": "9.4ubuntu4.5", - "arch": "amd64" - }, - "base-passwd": { - "version": "3.5.39", - "arch": "amd64" - }, - "bash": { - "version": "4.3-14ubuntu1.2", - "arch": "amd64" - }, - "bsdutils": { - "version": "1:2.27.1-6ubuntu3.3", - "arch": "amd64" - }, - "ca-certificates": { - "version": "20170717~16.04.1", - "arch": "all" - }, - "coreutils": { - "version": "8.25-2ubuntu3~16.04", - "arch": "amd64" - }, - "cron": { - "version": "3.0pl1-128ubuntu2", - "arch": "amd64" - }, - "curl": { - "version": "7.47.0-1ubuntu2.8", - "arch": "amd64" - }, - "dash": { - "version": "0.5.8-2.1ubuntu2", - "arch": "amd64" - }, - "dbus": { - "version": "1.10.6-1ubuntu3.3", - "arch": "amd64" - }, - "debconf": { - "version": "1.5.58ubuntu1", - "arch": "all" - }, - "debianutils": { - "version": "4.7", - "arch": "amd64" - }, - "diffutils": { - "version": "1:3.3-3", - "arch": "amd64" - }, - "dpkg": { - "version": "1.18.4ubuntu1.3", - "arch": "amd64" - }, - "dpkg-dev": { - "version": "1.18.4ubuntu1.4", - "arch": "all" - }, - "e2fslibs": { - "version": "1.42.13-1ubuntu1", - "arch": "amd64" - }, - "e2fsprogs": { - "version": "1.42.13-1ubuntu1", - "arch": "amd64" - }, - "fakeroot": { - "version": "1.20.2-1ubuntu1", - "arch": "amd64" - }, - "findutils": { - "version": "4.6.0+git+20160126-2", - "arch": "amd64" - }, - "gcc-5-base": { - "version": "5.4.0-6ubuntu1~16.04.10", - "arch": "amd64" - }, - "gcc-6-base": { - "version": "6.0.1-0ubuntu1", - "arch": "amd64" - }, - "gnupg": { - "version": "1.4.20-1ubuntu3.1", - "arch": "amd64" - }, - "gpgv": { - "version": "1.4.20-1ubuntu3.1", - "arch": "amd64" - }, - "grep": { - "version": "2.25-1~16.04.1", - "arch": "amd64" - }, - "gzip": { - "version": "1.6-4ubuntu1", - "arch": "amd64" - }, - "hostname": { - "version": "3.16ubuntu2", - "arch": "amd64" - }, - "ifupdown": { - "version": "0.8.10ubuntu1.4", - "arch": "amd64" - }, - "init": { - "version": "1.29ubuntu4", - "arch": "amd64" - }, - "init-system-helpers": { - "version": "1.29ubuntu4", - "arch": "all" - }, - "initscripts": { - "version": "2.88dsf-59.3ubuntu2", - "arch": "amd64" - }, - "insserv": { - "version": "1.14.0-5ubuntu3", - "arch": "amd64" - }, - "iproute2": { - "version": "4.3.0-1ubuntu3.16.04.3", - "arch": "amd64" - }, - "isc-dhcp-client": { - "version": "4.3.3-5ubuntu12.10", - "arch": "amd64" - }, - "javascript-common": { - "version": "11", - "arch": "all" - }, - "krb5-locales": { - "version": "1.13.2+dfsg-5ubuntu2", - "arch": "all" - }, - "libacl1": { - "version": "2.2.52-3", - "arch": "amd64" - }, - "libapparmor1": { - "version": "2.10.95-0ubuntu2.7", - "arch": "amd64" - }, - "libapt-inst2.0": { - "version": "1.2.27", - "arch": "amd64" - }, - "libapt-pkg5.0": { - "version": "1.2.27", - "arch": "amd64" - }, - "libasn1-8-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "libatm1": { - "version": "1:2.5.1-1.5", - "arch": "amd64" - }, - "libattr1": { - "version": "1:2.4.47-2", - "arch": "amd64" - }, - "libaudit-common": { - "version": "1:2.4.5-1ubuntu2", - "arch": "all" - }, - "libaudit1": { - "version": "1:2.4.5-1ubuntu2", - "arch": "amd64" - }, - "libblkid1": { - "version": "2.27.1-6ubuntu3.3", - "arch": "amd64" - }, - "libbz2-1.0": { - "version": "1.0.6-8", - "arch": "amd64" - }, - "libc-bin": { - "version": "2.23-0ubuntu9", - "arch": "amd64" - }, - "libc6": { - "version": "2.23-0ubuntu10", - "arch": "amd64" - }, - "libcap-ng0": { - "version": "0.7.7-1", - "arch": "amd64" - }, - "libcap2": { - "version": "1:2.24-12", - "arch": "amd64" - }, - "libcap2-bin": { - "version": "1:2.24-12", - "arch": "amd64" - }, - "libcomerr2": { - "version": "1.42.13-1ubuntu1", - "arch": "amd64" - }, - "libcryptsetup4": { - "version": "2:1.6.6-5ubuntu2.1", - "arch": "amd64" - }, - "libcurl3-gnutls": { - "version": "7.47.0-1ubuntu2.8", - "arch": "amd64" - }, - "libdb5.3": { - "version": "5.3.28-11ubuntu0.1", - "arch": "amd64" - }, - "libdbus-1-3": { - "version": "1.10.6-1ubuntu3.3", - "arch": "amd64" - }, - "libdebconfclient0": { - "version": "0.198ubuntu1", - "arch": "amd64" - }, - "libdevmapper1.02.1": { - "version": "2:1.02.110-1ubuntu10", - "arch": "amd64" - }, - "libexpat1": { - "version": "2.1.0-7ubuntu0.16.04.3", - "arch": "amd64" - }, - "libfakeroot": { - "version": "1.20.2-1ubuntu1", - "arch": "amd64" - }, - "libfdisk1": { - "version": "2.27.1-6ubuntu3.3", - "arch": "amd64" - }, - "libffi6": { - "version": "3.2.1-4", - "arch": "amd64" - }, - "libgcc1": { - "version": "1:6.0.1-0ubuntu1", - "arch": "amd64" - }, - "libgcrypt20": { - "version": "1.6.5-2ubuntu0.3", - "arch": "amd64" - }, - "libgdbm3": { - "version": "1.8.3-13.1", - "arch": "amd64" - }, - "libglib2.0-0": { - "version": "2.48.2-0ubuntu1", - "arch": "amd64" - }, - "libgmp-dev": { - "version": "2:6.1.0+dfsg-2", - "arch": "amd64" - }, - "libgmp10": { - "version": "2:6.1.0+dfsg-2", - "arch": "amd64" - }, - "libgmpxx4ldbl": { - "version": "2:6.1.0+dfsg-2", - "arch": "amd64" - }, - "libgnutls30": { - "version": "3.4.10-4ubuntu1.4", - "arch": "amd64" - }, - "libgpg-error0": { - "version": "1.21-2ubuntu1", - "arch": "amd64" - }, - "libgssapi-krb5-2": { - "version": "1.13.2+dfsg-5ubuntu2", - "arch": "amd64" - }, - "libgssapi3-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "libhcrypto4-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "libheimbase1-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "libheimntlm0-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "libhogweed4": { - "version": "3.2-1ubuntu0.16.04.1", - "arch": "amd64" - }, - "libhx509-5-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "libidn11": { - "version": "1.32-3ubuntu1.2", - "arch": "amd64" - }, - "libjs-jquery": { - "version": "1.11.3+dfsg-4", - "arch": "all" - }, - "libk5crypto3": { - "version": "1.13.2+dfsg-5ubuntu2", - "arch": "amd64" - }, - "libkeyutils1": { - "version": "1.5.9-8ubuntu1", - "arch": "amd64" - }, - "libkmod2": { - "version": "22-1ubuntu5", - "arch": "amd64" - }, - "libkrb5-26-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "libkrb5-3": { - "version": "1.13.2+dfsg-5ubuntu2", - "arch": "amd64" - }, - "libkrb5support0": { - "version": "1.13.2+dfsg-5ubuntu2", - "arch": "amd64" - }, - "libldap-2.4-2": { - "version": "2.4.42+dfsg-2ubuntu3.3", - "arch": "amd64" - }, - "liblz4-1": { - "version": "0.0~r131-2ubuntu2", - "arch": "amd64" - }, - "liblzma5": { - "version": "5.1.1alpha+20120614-2ubuntu2", - "arch": "amd64" - }, - "libmagic1": { - "version": "1:5.25-2ubuntu1.1", - "arch": "amd64" - }, - "libmnl0": { - "version": "1.0.3-5", - "arch": "amd64" - }, - "libmount1": { - "version": "2.27.1-6ubuntu3.3", - "arch": "amd64" - }, - "libmpc3": { - "version": "1.0.3-1", - "arch": "amd64" - }, - "libncurses5": { - "version": "6.0+20160213-1ubuntu1", - "arch": "amd64" - }, - "libncursesw5": { - "version": "6.0+20160213-1ubuntu1", - "arch": "amd64" - }, - "libnettle6": { - "version": "3.2-1ubuntu0.16.04.1", - "arch": "amd64" - }, - "libp11-kit0": { - "version": "0.23.2-5~ubuntu16.04.1", - "arch": "amd64" - }, - "libpam-modules": { - "version": "1.1.8-3.2ubuntu2", - "arch": "amd64" - }, - "libpam-modules-bin": { - "version": "1.1.8-3.2ubuntu2", - "arch": "amd64" - }, - "libpam-runtime": { - "version": "1.1.8-3.2ubuntu2", - "arch": "all" - }, - "libpam0g": { - "version": "1.1.8-3.2ubuntu2", - "arch": "amd64" - }, - "libpcre3": { - "version": "2:8.38-3.1", - "arch": "amd64" - }, - "libperl5.22": { - "version": "5.22.1-9ubuntu0.5", - "arch": "amd64" - }, - "libprocps4": { - "version": "2:3.3.10-4ubuntu2.3", - "arch": "amd64" - }, - "libpython3.5-minimal": { - "version": "3.5.2-2ubuntu0~16.04.4", - "arch": "amd64" - }, - "libreadline6": { - "version": "6.3-8ubuntu2", - "arch": "amd64" - }, - "libroken18-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "librtmp1": { - "version": "2.4+20151223.gitfa8646d-1ubuntu0.1", - "arch": "amd64" - }, - "libruby2.4": { - "version": "2.4.4-1bbox1~xenial1", - "arch": "amd64" - }, - "libsasl2-2": { - "version": "2.1.26.dfsg1-14build1", - "arch": "amd64" - }, - "libsasl2-modules": { - "version": "2.1.26.dfsg1-14build1", - "arch": "amd64" - }, - "libsasl2-modules-db": { - "version": "2.1.26.dfsg1-14build1", - "arch": "amd64" - }, - "libseccomp2": { - "version": "2.2.3-3ubuntu3", - "arch": "amd64" - }, - "libselinux1": { - "version": "2.4-3build2", - "arch": "amd64" - }, - "libsemanage-common": { - "version": "2.3-1build3", - "arch": "all" - }, - "libsemanage1": { - "version": "2.3-1build3", - "arch": "amd64" - }, - "libsepol1": { - "version": "2.4-2", - "arch": "amd64" - }, - "libsmartcols1": { - "version": "2.27.1-6ubuntu3.3", - "arch": "amd64" - }, - "libsqlite3-0": { - "version": "3.11.0-1ubuntu1", - "arch": "amd64" - }, - "libss2": { - "version": "1.42.13-1ubuntu1", - "arch": "amd64" - }, - "libssl1.0.0": { - "version": "1.0.2g-1ubuntu4.13", - "arch": "amd64" - }, - "libstdc++6": { - "version": "5.4.0-6ubuntu1~16.04.10", - "arch": "amd64" - }, - "libsystemd0": { - "version": "229-4ubuntu21", - "arch": "amd64" - }, - "libtasn1-6": { - "version": "4.7-3ubuntu0.16.04.3", - "arch": "amd64" - }, - "libtinfo5": { - "version": "6.0+20160213-1ubuntu1", - "arch": "amd64" - }, - "libudev1": { - "version": "229-4ubuntu21", - "arch": "amd64" - }, - "libusb-0.1-4": { - "version": "2:0.1.12-28", - "arch": "amd64" - }, - "libustr-1.0-1": { - "version": "1.0.4-5", - "arch": "amd64" - }, - "libuuid1": { - "version": "2.27.1-6ubuntu3.3", - "arch": "amd64" - }, - "libwind0-heimdal": { - "version": "1.7~git20150920+dfsg-4ubuntu1.16.04.1", - "arch": "amd64" - }, - "libyaml-0-2": { - "version": "0.1.6-3", - "arch": "amd64" - }, - "login": { - "version": "1:4.2-3.1ubuntu5.3", - "arch": "amd64" - }, - "lsb-base": { - "version": "9.20160110ubuntu0.2", - "arch": "all" - }, - "lsb-release": { - "version": "9.20160110ubuntu0.2", - "arch": "all" - }, - "makedev": { - "version": "2.3.1-93ubuntu2~ubuntu16.04.1", - "arch": "all" - }, - "mawk": { - "version": "1.3.3-17ubuntu2", - "arch": "amd64" - }, - "mime-support": { - "version": "3.59ubuntu1", - "arch": "all" - }, - "mount": { - "version": "2.27.1-6ubuntu3.3", - "arch": "amd64" - }, - "multiarch-support": { - "version": "2.23-0ubuntu9", - "arch": "amd64" - }, - "ncurses-base": { - "version": "6.0+20160213-1ubuntu1", - "arch": "all" - }, - "ncurses-bin": { - "version": "6.0+20160213-1ubuntu1", - "arch": "amd64" - }, - "netbase": { - "version": "5.3", - "arch": "all" - }, - "openssl": { - "version": "1.0.2g-1ubuntu4.13", - "arch": "amd64" - }, - "passwd": { - "version": "1:4.2-3.1ubuntu5.3", - "arch": "amd64" - }, - "perl": { - "version": "5.22.1-9ubuntu0.5", - "arch": "amd64" - }, - "perl-base": { - "version": "5.22.1-9ubuntu0.5", - "arch": "amd64" - }, - "procps": { - "version": "2:3.3.10-4ubuntu2.3", - "arch": "amd64" - }, - "python3": { - "version": "3.5.1-3", - "arch": "amd64" - }, - "python3.5-minimal": { - "version": "3.5.2-2ubuntu0~16.04.4", - "arch": "amd64" - }, - "readline-common": { - "version": "6.3-8ubuntu2", - "arch": "all" - }, - "ruby2.4": { - "version": "2.4.4-1bbox1~xenial1", - "arch": "amd64" - }, - "ruby2.4-dev": { - "version": "2.4.4-1bbox1~xenial1", - "arch": "amd64" - }, - "rubygems-integration": { - "version": "1.10", - "arch": "all" - }, - "sed": { - "version": "4.2.2-7", - "arch": "amd64" - }, - "sensible-utils": { - "version": "0.0.9", - "arch": "all" - }, - "sgml-base": { - "version": "1.26+nmu4ubuntu1", - "arch": "all" - }, - "shared-mime-info": { - "version": "1.5-2ubuntu0.1", - "arch": "amd64" - }, - "systemd": { - "version": "229-4ubuntu21", - "arch": "amd64" - }, - "systemd-sysv": { - "version": "229-4ubuntu21", - "arch": "amd64" - }, - "sysv-rc": { - "version": "2.88dsf-59.3ubuntu2", - "arch": "all" - }, - "sysvinit-utils": { - "version": "2.88dsf-59.3ubuntu2", - "arch": "amd64" - }, - "tar": { - "version": "1.28-2.1ubuntu0.1", - "arch": "amd64" - }, - "ubuntu-keyring": { - "version": "2012.05.19", - "arch": "all" - }, - "ucf": { - "version": "3.0036", - "arch": "all" - }, - "unattended-upgrades": { - "version": "0.90ubuntu0.9", - "arch": "all" - }, - "unzip": { - "version": "6.0-20ubuntu1", - "arch": "amd64" - }, - "util-linux": { - "version": "2.27.1-6ubuntu3.3", - "arch": "amd64" - }, - "xdg-user-dirs": { - "version": "0.15-2ubuntu6.16.04.1", - "arch": "amd64" - }, - "xml-core": { - "version": "0.13+nmu2", - "arch": "all" - }, - "zlib1g": { - "version": "1:1.2.8.dfsg-2ubuntu4.1", - "arch": "amd64" - } - }, - "platform": "ubuntu", - "platform_family": "debian", - "platform_version": "16.04", - "root_group": "root", - "shard_seed": 161653957, - "shells": [ - "/bin/sh", - "/bin/dash", - "/bin/bash", - "/bin/rbash" - ], - "time": { - "timezone": "GMT" - }, - "uptime": "30 days 15 hours 07 minutes 30 seconds", - "uptime_seconds": 2646450, - "virtualization": { - "systems": { - } - } -} diff --git a/spec/fixtures/fauxhai/ubuntu/16.04-more-cpus.json b/spec/fixtures/fauxhai/ubuntu/16.04-more-cpus.json deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9mYXV4aGFpL3VidW50dS8xNi4wNC1tb3JlLWNwdXMuanNvbg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/fauxhai/ubuntu/16.04-more-cpus.json +++ /dev/null @@ -1,1519 +0,0 @@ -{ - "kernel": { - "name": "Linux", - "release": "4.8.0-52-generic", - "version": "#55~16.04.1-Ubuntu SMP Fri Apr 28 14:36:29 UTC 2017", - "machine": "x86_64", - "processor": "x86_64", - "os": "GNU/Linux", - "modules": { - "ip6table_filter": { - "size": "16384", - "refcount": "1" - }, - "ip6_tables": { - "size": "28672", - "refcount": "1" - }, - "iptable_filter": { - "size": "16384", - "refcount": "1" - }, - "ip_tables": { - "size": "24576", - "refcount": "1" - }, - "x_tables": { - "size": "36864", - "refcount": "4" - }, - "ppdev": { - "size": "20480", - "refcount": "0" - }, - "input_leds": { - "size": "16384", - "refcount": "0" - }, - "serio_raw": { - "size": "16384", - "refcount": "0" - }, - "parport_pc": { - "size": "32768", - "refcount": "0" - }, - "pvpanic": { - "size": "16384", - "refcount": "0" - }, - "parport": { - "size": "49152", - "refcount": "2" - }, - "ib_iser": { - "size": "49152", - "refcount": "0", - "version": "1.6" - }, - "rdma_cm": { - "size": "57344", - "refcount": "1" - }, - "iw_cm": { - "size": "49152", - "refcount": "1" - }, - "ib_cm": { - "size": "45056", - "refcount": "1" - }, - "ib_core": { - "size": "212992", - "refcount": "4" - }, - "configfs": { - "size": "40960", - "refcount": "2", - "version": "0.0.2" - }, - "iscsi_tcp": { - "size": "20480", - "refcount": "0" - }, - "libiscsi_tcp": { - "size": "24576", - "refcount": "1" - }, - "libiscsi": { - "size": "53248", - "refcount": "3" - }, - "scsi_transport_iscsi": { - "size": "98304", - "refcount": "4", - "version": "2.0-870" - }, - "autofs4": { - "size": "40960", - "refcount": "2" - }, - "btrfs": { - "size": "1060864", - "refcount": "0" - }, - "raid10": { - "size": "49152", - "refcount": "0" - }, - "raid456": { - "size": "110592", - "refcount": "0" - }, - "async_raid6_recov": { - "size": "20480", - "refcount": "1" - }, - "async_memcpy": { - "size": "16384", - "refcount": "2" - }, - "async_pq": { - "size": "16384", - "refcount": "2" - }, - "async_xor": { - "size": "16384", - "refcount": "3" - }, - "async_tx": { - "size": "16384", - "refcount": "5" - }, - "xor": { - "size": "24576", - "refcount": "2" - }, - "raid6_pq": { - "size": "102400", - "refcount": "4" - }, - "libcrc32c": { - "size": "16384", - "refcount": "1" - }, - "raid1": { - "size": "40960", - "refcount": "0" - }, - "raid0": { - "size": "20480", - "refcount": "0" - }, - "multipath": { - "size": "16384", - "refcount": "0" - }, - "linear": { - "size": "16384", - "refcount": "0" - }, - "crct10dif_pclmul": { - "size": "16384", - "refcount": "0" - }, - "crc32_pclmul": { - "size": "16384", - "refcount": "0" - }, - "ghash_clmulni_intel": { - "size": "16384", - "refcount": "0" - }, - "aesni_intel": { - "size": "167936", - "refcount": "0" - }, - "aes_x86_64": { - "size": "20480", - "refcount": "1" - }, - "lrw": { - "size": "16384", - "refcount": "1" - }, - "glue_helper": { - "size": "16384", - "refcount": "1" - }, - "ablk_helper": { - "size": "16384", - "refcount": "1" - }, - "cryptd": { - "size": "24576", - "refcount": "3" - }, - "psmouse": { - "size": "139264", - "refcount": "0" - }, - "virtio_net": { - "size": "32768", - "refcount": "0" - }, - "virtio_scsi": { - "size": "20480", - "refcount": "1" - } - } - }, - "os": "linux", - "os_version": "4.8.0-52-generic", - "lsb": { - "id": "Ubuntu", - "description": "Ubuntu 16.04.2 LTS", - "release": "16.04", - "codename": "xenial" - }, - "platform": "ubuntu", - "platform_version": "16.04", - "platform_family": "debian", - "filesystem": { - "by_device": { - "udev": { - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ], - "mounts": [ - "/dev" - ] - }, - "tmpfs": { - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ], - "mounts": [ - "/run", - "/dev/shm", - "/run/lock", - "/sys/fs/cgroup", - "/run/user/1001" - ] - }, - "/dev/sda1": { - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs", - "mounts": [ - "/" - ] - }, - "sysfs": { - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys" - ] - }, - "proc": { - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/proc" - ] - }, - "devpts": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ], - "mounts": [ - "/dev/pts" - ] - }, - "securityfs": { - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys/kernel/security" - ] - }, - "cgroup": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ], - "mounts": [ - "/sys/fs/cgroup/systemd", - "/sys/fs/cgroup/perf_event", - "/sys/fs/cgroup/blkio", - "/sys/fs/cgroup/devices", - "/sys/fs/cgroup/net_cls,net_prio", - "/sys/fs/cgroup/cpuset", - "/sys/fs/cgroup/cpu,cpuacct", - "/sys/fs/cgroup/memory", - "/sys/fs/cgroup/freezer", - "/sys/fs/cgroup/pids", - "/sys/fs/cgroup/hugetlb" - ] - }, - "pstore": { - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys/fs/pstore" - ] - }, - "hugetlbfs": { - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/dev/hugepages" - ] - }, - "mqueue": { - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/dev/mqueue" - ] - }, - "systemd-1": { - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ], - "mounts": [ - "/proc/sys/fs/binfmt_misc" - ] - }, - "debugfs": { - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/kernel/debug" - ] - }, - "fusectl": { - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/fs/fuse/connections" - ] - }, - "configfs": { - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/kernel/config" - ] - }, - "lxcfs": { - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ], - "mounts": [ - "/var/lib/lxcfs" - ] - }, - "/dev/sda": { - } - }, - "by_mountpoint": { - "/dev": { - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ], - "devices": [ - "udev" - ] - }, - "/run": { - "kb_size": "378828", - "kb_used": "5292", - "kb_available": "373536", - "percent_used": "2%", - "total_inodes": "473531", - "inodes_used": "460", - "inodes_available": "473071", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "size=378828k", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/": { - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs", - "devices": [ - "/dev/sda1" - ] - }, - "/dev/shm": { - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "1", - "inodes_available": "473530", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev" - ], - "devices": [ - "tmpfs" - ] - }, - "/run/lock": { - "kb_size": "5120", - "kb_used": "0", - "kb_available": "5120", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "8", - "inodes_available": "473523", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=5120k" - ], - "devices": [ - "tmpfs" - ] - }, - "/sys/fs/cgroup": { - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "16", - "inodes_available": "473515", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/run/user/1001": { - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ], - "devices": [ - "tmpfs" - ] - }, - "/sys": { - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "sysfs" - ] - }, - "/proc": { - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/dev/pts": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ], - "devices": [ - "devpts" - ] - }, - "/sys/kernel/security": { - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "securityfs" - ] - }, - "/sys/fs/cgroup/systemd": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "xattr", - "release_agent=/lib/systemd/systemd-cgroups-agent", - "name=systemd" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/pstore": { - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "pstore" - ] - }, - "/sys/fs/cgroup/perf_event": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "perf_event" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/blkio": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "blkio" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/devices": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "devices" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/net_cls,net_prio": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "net_cls", - "net_prio" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/cpuset": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpuset" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/cpu,cpuacct": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpu", - "cpuacct" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/memory": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/freezer": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "freezer" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/pids": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "pids" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/hugetlb": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ], - "devices": [ - "cgroup" - ] - }, - "/dev/hugepages": { - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "hugetlbfs" - ] - }, - "/dev/mqueue": { - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "mqueue" - ] - }, - "/proc/sys/fs/binfmt_misc": { - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ], - "devices": [ - "systemd-1" - ] - }, - "/sys/kernel/debug": { - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "debugfs" - ] - }, - "/sys/fs/fuse/connections": { - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "fusectl" - ] - }, - "/sys/kernel/config": { - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "configfs" - ] - }, - "/var/lib/lxcfs": { - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ], - "devices": [ - "lxcfs" - ] - } - }, - "by_pair": { - "udev,/dev": { - "device": "udev", - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "mount": "/dev", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ] - }, - "tmpfs,/run": { - "device": "tmpfs", - "kb_size": "378828", - "kb_used": "5292", - "kb_available": "373536", - "percent_used": "2%", - "mount": "/run", - "total_inodes": "473531", - "inodes_used": "460", - "inodes_available": "473071", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "size=378828k", - "mode=755" - ] - }, - "/dev/sda1,/": { - "device": "/dev/sda1", - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "mount": "/", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs" - }, - "tmpfs,/dev/shm": { - "device": "tmpfs", - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "mount": "/dev/shm", - "total_inodes": "473531", - "inodes_used": "1", - "inodes_available": "473530", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev" - ] - }, - "tmpfs,/run/lock": { - "device": "tmpfs", - "kb_size": "5120", - "kb_used": "0", - "kb_available": "5120", - "percent_used": "0%", - "mount": "/run/lock", - "total_inodes": "473531", - "inodes_used": "8", - "inodes_available": "473523", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=5120k" - ] - }, - "tmpfs,/sys/fs/cgroup": { - "device": "tmpfs", - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "mount": "/sys/fs/cgroup", - "total_inodes": "473531", - "inodes_used": "16", - "inodes_available": "473515", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "mode=755" - ] - }, - "tmpfs,/run/user/1001": { - "device": "tmpfs", - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "mount": "/run/user/1001", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ] - }, - "sysfs,/sys": { - "device": "sysfs", - "mount": "/sys", - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "proc,/proc": { - "device": "proc", - "mount": "/proc", - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "devpts,/dev/pts": { - "device": "devpts", - "mount": "/dev/pts", - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ] - }, - "securityfs,/sys/kernel/security": { - "device": "securityfs", - "mount": "/sys/kernel/security", - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "cgroup,/sys/fs/cgroup/systemd": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/systemd", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "xattr", - "release_agent=/lib/systemd/systemd-cgroups-agent", - "name=systemd" - ] - }, - "pstore,/sys/fs/pstore": { - "device": "pstore", - "mount": "/sys/fs/pstore", - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "cgroup,/sys/fs/cgroup/perf_event": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/perf_event", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "perf_event" - ] - }, - "cgroup,/sys/fs/cgroup/blkio": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/blkio", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "blkio" - ] - }, - "cgroup,/sys/fs/cgroup/devices": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/devices", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "devices" - ] - }, - "cgroup,/sys/fs/cgroup/net_cls,net_prio": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/net_cls,net_prio", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "net_cls", - "net_prio" - ] - }, - "cgroup,/sys/fs/cgroup/cpuset": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/cpuset", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpuset" - ] - }, - "cgroup,/sys/fs/cgroup/cpu,cpuacct": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/cpu,cpuacct", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpu", - "cpuacct" - ] - }, - "cgroup,/sys/fs/cgroup/memory": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/memory", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ] - }, - "cgroup,/sys/fs/cgroup/freezer": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/freezer", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "freezer" - ] - }, - "cgroup,/sys/fs/cgroup/pids": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/pids", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "pids" - ] - }, - "cgroup,/sys/fs/cgroup/hugetlb": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/hugetlb", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ] - }, - "hugetlbfs,/dev/hugepages": { - "device": "hugetlbfs", - "mount": "/dev/hugepages", - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "mqueue,/dev/mqueue": { - "device": "mqueue", - "mount": "/dev/mqueue", - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ] - }, - "systemd-1,/proc/sys/fs/binfmt_misc": { - "device": "systemd-1", - "mount": "/proc/sys/fs/binfmt_misc", - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ] - }, - "debugfs,/sys/kernel/debug": { - "device": "debugfs", - "mount": "/sys/kernel/debug", - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "fusectl,/sys/fs/fuse/connections": { - "device": "fusectl", - "mount": "/sys/fs/fuse/connections", - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ] - }, - "configfs,/sys/kernel/config": { - "device": "configfs", - "mount": "/sys/kernel/config", - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "lxcfs,/var/lib/lxcfs": { - "device": "lxcfs", - "mount": "/var/lib/lxcfs", - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ] - }, - "/dev/sda,": { - "device": "/dev/sda" - } - } - }, - "dmi": { - "dmidecode_version": "3.0" - }, - "init_package": "systemd", - "ohai_time": 1496219099.5081818, - "command": { - "ps": "ps -ef" - }, - "root_group": "root", - "languages": { - "ruby": { - "platform": "x86_64-linux-gnu", - "version": "2.3.1", - "release_date": "2016-04-26", - "target": "x86_64-pc-linux-gnu", - "target_cpu": "x86_64", - "target_vendor": "pc", - "target_os": "linux-gnu", - "host": "x86_64-pc-linux-gnu", - "host_cpu": "x86_64", - "host_os": "linux-gnu", - "host_vendor": "pc", - "bin_dir": "/usr/local/bin", - "ruby_bin": "/usr/local/bin/ruby", - "gems_dir": "/usr/local/gems", - "gem_bin": "/usr/local/bin/gem" - }, - "powershell": null - }, - "chef_packages": { - "chef": { - "version": "13.1.31", - "chef_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-13.1.31/lib" - }, - "ohai": { - "version": "13.1.0", - "ohai_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/ohai-13.1.0/lib/ohai" - } - }, - "counters": { - "network": { - "interfaces": { - "eth0": { - "rx": { - "bytes": "0", - "packets": "0", - "errors": "0", - "drop": 0, - "overrun": 0, - "frame": 0, - "compressed": 0, - "multicast": 0 - }, - "tx": { - "bytes": "342", - "packets": "0", - "errors": "0", - "drop": 0, - "overrun": 0, - "collisions": "0", - "carrier": 0, - "compressed": 0 - } - } - } - } - }, - "current_user": "fauxhai", - "domain": "local", - "etc": { - "passwd": { - "fauxhai": { - "dir": "/home/fauxhai", - "gid": 0, - "uid": 0, - "shell": "/bin/bash", - "gecos": "Fauxhai" - } - }, - "group": { - "fauxhai": { - "gid": 0, - "members": [ - "fauxhai" - ] - } - } - }, - "hostname": "Fauxhai", - "fqdn": "fauxhai.local", - "ipaddress": "10.0.0.2", - "keys": { - "ssh": { - "host_dsa_public": "ssh-dss AAAAB3NzaC1kc3MAAACBAJFo9BLAw4WKEs5hgipk5m423FzBsDXCZSMcC9ca/om/1VYzMqImixGe3uICDzNFUWxFoLJTQAOccyzo6MXZiQqwWJDLFi5qOSr6w2XcMyE+zd4wOyMoDiVM5fizmG8K3FzrqvGjwBcHcBdOQnavSijoj38DN25J9zhrid5BY4WlAAAAFQDxXrCyG52XCzn3FV4ej38wJBkomQAAAIBovGPJ4mP2P6BK8lHl0PPbktwQbWlpJ13oz6REJFDVcUi7vV26bX/BjQX+ohzZQzljdz1SpUbPc/8nuA4darYkVh91eBi307EN8IdxRHj2eBgp/ZG4yshIebG3WHrwJD/xUjjZ1MRfyDT1ermVi4LvjjPgWDxLZnPpMaR6S1nzgQAAAIEAj0Vd6DCWslvlsZ8+N53HWsqPi3gnx35JoLPz9Z2epkKIKqmEHav+93G3hdfztVa4I4t3phoPniQchYryF5+RNg8hqxKzjNtrIqUYCeuf2NJrksNsH7OZygPHZpqt4kTuwAGZxjxEGfAI0y8DhkU2ntp2LnzRnWH106BQBCmcXwo= fauxhai.local", - "host_rsa_public": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtLCeqtqr/HbnORckw1ukdLhpfGoOPFi5/esKEokzTqq1gsgQ2V8emmyjfq1i6XXfRtSBxkdlHv/GWdP5wBTuE2G85MzBkVSQPvmwQN8lX/JMPEEtKXkeOo0o92/PiSmvY4eRsdF0mw40Uvg7jtE3f3fxj497kzh5fKtkrHnF4x9gXCbVdr3FqXJfggR5IJwAxToerbK7x/uRS+7YuZI9Pip3tt14nv9ezwXcuGb/tvjWOZINiFl8izVIFKi7sxfTX09p4NgamxRS7TD2Yd0jT8nEoF9UZTsgXcJ1kDSx7N7NxFfNfP6rCdOGRRz4gUhXtsUjG/XkxPeCwZ7A9VnOD fauxhai.local" - } - }, - "macaddress": "11:11:11:11:11:11", - "network": { - "default_gateway": "10.0.0.1", - "default_interface": "eth0", - "settings": { - }, - "interfaces": { - "eth0": { - "addresses": { - "10.0.0.2": { - "broadcast": "10.0.0.255", - "family": "inet", - "netmask": "255.255.255.0", - "prefixlen": "24", - "scope": "Global" - } - }, - "arp": { - "10.0.0.1": "fe:ff:ff:ff:ff:ff" - }, - "encapsulation": "Ethernet", - "flags": [ - "BROADCAST", - "MULTICAST", - "UP", - "LOWER_UP" - ], - "mtu": "1500", - "number": "0", - "routes": [ - { - "destination": "10.0.0.0/24", - "family": "inet", - "scope": "link", - "proto": "kernel", - "src": "10.0.0.2" - } - ], - "state": "up", - "type": "eth" - } - } - }, - "uptime": "30 days 15 hours 07 minutes 30 seconds", - "uptime_seconds": 2646450, - "cpu": { - "real": 16, - "total": 16, - "cores": 16 - }, - "memory": { - "total": "33554432kB" - }, - "virtualization": { - "systems": { - } - } -} diff --git a/spec/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json b/spec/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9mYXV4aGFpL3VidW50dS8xNi4wNC1uby1ydW4tdG1wZnMuanNvbg==..0000000000000000000000000000000000000000 --- a/spec/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json +++ /dev/null @@ -1,1438 +0,0 @@ -{ - "kernel": { - "name": "Linux", - "release": "4.8.0-52-generic", - "version": "#55~16.04.1-Ubuntu SMP Fri Apr 28 14:36:29 UTC 2017", - "machine": "x86_64", - "processor": "x86_64", - "os": "GNU/Linux", - "modules": { - "ip6table_filter": { - "size": "16384", - "refcount": "1" - }, - "ip6_tables": { - "size": "28672", - "refcount": "1" - }, - "iptable_filter": { - "size": "16384", - "refcount": "1" - }, - "ip_tables": { - "size": "24576", - "refcount": "1" - }, - "x_tables": { - "size": "36864", - "refcount": "4" - }, - "ppdev": { - "size": "20480", - "refcount": "0" - }, - "input_leds": { - "size": "16384", - "refcount": "0" - }, - "serio_raw": { - "size": "16384", - "refcount": "0" - }, - "parport_pc": { - "size": "32768", - "refcount": "0" - }, - "pvpanic": { - "size": "16384", - "refcount": "0" - }, - "parport": { - "size": "49152", - "refcount": "2" - }, - "ib_iser": { - "size": "49152", - "refcount": "0", - "version": "1.6" - }, - "rdma_cm": { - "size": "57344", - "refcount": "1" - }, - "iw_cm": { - "size": "49152", - "refcount": "1" - }, - "ib_cm": { - "size": "45056", - "refcount": "1" - }, - "ib_core": { - "size": "212992", - "refcount": "4" - }, - "configfs": { - "size": "40960", - "refcount": "2", - "version": "0.0.2" - }, - "iscsi_tcp": { - "size": "20480", - "refcount": "0" - }, - "libiscsi_tcp": { - "size": "24576", - "refcount": "1" - }, - "libiscsi": { - "size": "53248", - "refcount": "3" - }, - "scsi_transport_iscsi": { - "size": "98304", - "refcount": "4", - "version": "2.0-870" - }, - "autofs4": { - "size": "40960", - "refcount": "2" - }, - "btrfs": { - "size": "1060864", - "refcount": "0" - }, - "raid10": { - "size": "49152", - "refcount": "0" - }, - "raid456": { - "size": "110592", - "refcount": "0" - }, - "async_raid6_recov": { - "size": "20480", - "refcount": "1" - }, - "async_memcpy": { - "size": "16384", - "refcount": "2" - }, - "async_pq": { - "size": "16384", - "refcount": "2" - }, - "async_xor": { - "size": "16384", - "refcount": "3" - }, - "async_tx": { - "size": "16384", - "refcount": "5" - }, - "xor": { - "size": "24576", - "refcount": "2" - }, - "raid6_pq": { - "size": "102400", - "refcount": "4" - }, - "libcrc32c": { - "size": "16384", - "refcount": "1" - }, - "raid1": { - "size": "40960", - "refcount": "0" - }, - "raid0": { - "size": "20480", - "refcount": "0" - }, - "multipath": { - "size": "16384", - "refcount": "0" - }, - "linear": { - "size": "16384", - "refcount": "0" - }, - "crct10dif_pclmul": { - "size": "16384", - "refcount": "0" - }, - "crc32_pclmul": { - "size": "16384", - "refcount": "0" - }, - "ghash_clmulni_intel": { - "size": "16384", - "refcount": "0" - }, - "aesni_intel": { - "size": "167936", - "refcount": "0" - }, - "aes_x86_64": { - "size": "20480", - "refcount": "1" - }, - "lrw": { - "size": "16384", - "refcount": "1" - }, - "glue_helper": { - "size": "16384", - "refcount": "1" - }, - "ablk_helper": { - "size": "16384", - "refcount": "1" - }, - "cryptd": { - "size": "24576", - "refcount": "3" - }, - "psmouse": { - "size": "139264", - "refcount": "0" - }, - "virtio_net": { - "size": "32768", - "refcount": "0" - }, - "virtio_scsi": { - "size": "20480", - "refcount": "1" - } - } - }, - "os": "linux", - "os_version": "4.8.0-52-generic", - "lsb": { - "id": "Ubuntu", - "description": "Ubuntu 16.04.2 LTS", - "release": "16.04", - "codename": "xenial" - }, - "platform": "ubuntu", - "platform_version": "16.04", - "platform_family": "debian", - "filesystem": { - "by_device": { - "udev": { - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ], - "mounts": [ - "/dev" - ] - }, - "tmpfs": { - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ], - "mounts": [ - "/dev/shm", - "/run/lock", - "/sys/fs/cgroup", - "/run/user/1001" - ] - }, - "/dev/sda1": { - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs", - "mounts": [ - "/" - ] - }, - "sysfs": { - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys" - ] - }, - "proc": { - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/proc" - ] - }, - "devpts": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ], - "mounts": [ - "/dev/pts" - ] - }, - "securityfs": { - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys/kernel/security" - ] - }, - "cgroup": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ], - "mounts": [ - "/sys/fs/cgroup/systemd", - "/sys/fs/cgroup/perf_event", - "/sys/fs/cgroup/blkio", - "/sys/fs/cgroup/devices", - "/sys/fs/cgroup/net_cls,net_prio", - "/sys/fs/cgroup/cpuset", - "/sys/fs/cgroup/cpu,cpuacct", - "/sys/fs/cgroup/memory", - "/sys/fs/cgroup/freezer", - "/sys/fs/cgroup/pids", - "/sys/fs/cgroup/hugetlb" - ] - }, - "pstore": { - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys/fs/pstore" - ] - }, - "hugetlbfs": { - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/dev/hugepages" - ] - }, - "mqueue": { - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/dev/mqueue" - ] - }, - "systemd-1": { - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ], - "mounts": [ - "/proc/sys/fs/binfmt_misc" - ] - }, - "debugfs": { - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/kernel/debug" - ] - }, - "fusectl": { - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/fs/fuse/connections" - ] - }, - "configfs": { - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/kernel/config" - ] - }, - "lxcfs": { - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ], - "mounts": [ - "/var/lib/lxcfs" - ] - }, - "/dev/sda": { - } - }, - "by_mountpoint": { - "/dev": { - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ], - "devices": [ - "udev" - ] - }, - "/": { - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs", - "devices": [ - "/dev/sda1" - ] - }, - "/run/lock": { - "kb_size": "5120", - "kb_used": "0", - "kb_available": "5120", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "8", - "inodes_available": "473523", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=5120k" - ], - "devices": [ - "tmpfs" - ] - }, - "/sys/fs/cgroup": { - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "16", - "inodes_available": "473515", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/run/user/1001": { - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ], - "devices": [ - "tmpfs" - ] - }, - "/sys": { - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "sysfs" - ] - }, - "/proc": { - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/dev/pts": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ], - "devices": [ - "devpts" - ] - }, - "/sys/kernel/security": { - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "securityfs" - ] - }, - "/sys/fs/cgroup/systemd": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "xattr", - "release_agent=/lib/systemd/systemd-cgroups-agent", - "name=systemd" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/pstore": { - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "pstore" - ] - }, - "/sys/fs/cgroup/perf_event": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "perf_event" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/blkio": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "blkio" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/devices": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "devices" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/net_cls,net_prio": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "net_cls", - "net_prio" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/cpuset": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpuset" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/cpu,cpuacct": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpu", - "cpuacct" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/memory": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/freezer": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "freezer" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/pids": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "pids" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/hugetlb": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ], - "devices": [ - "cgroup" - ] - }, - "/dev/hugepages": { - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "hugetlbfs" - ] - }, - "/dev/mqueue": { - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "mqueue" - ] - }, - "/proc/sys/fs/binfmt_misc": { - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ], - "devices": [ - "systemd-1" - ] - }, - "/sys/kernel/debug": { - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "debugfs" - ] - }, - "/sys/fs/fuse/connections": { - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "fusectl" - ] - }, - "/sys/kernel/config": { - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "configfs" - ] - }, - "/var/lib/lxcfs": { - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ], - "devices": [ - "lxcfs" - ] - } - }, - "by_pair": { - "udev,/dev": { - "device": "udev", - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "mount": "/dev", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ] - }, - "/dev/sda1,/": { - "device": "/dev/sda1", - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "mount": "/", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs" - }, - "tmpfs,/run/lock": { - "device": "tmpfs", - "kb_size": "5120", - "kb_used": "0", - "kb_available": "5120", - "percent_used": "0%", - "mount": "/run/lock", - "total_inodes": "473531", - "inodes_used": "8", - "inodes_available": "473523", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=5120k" - ] - }, - "tmpfs,/sys/fs/cgroup": { - "device": "tmpfs", - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "mount": "/sys/fs/cgroup", - "total_inodes": "473531", - "inodes_used": "16", - "inodes_available": "473515", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "mode=755" - ] - }, - "tmpfs,/run/user/1001": { - "device": "tmpfs", - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "mount": "/run/user/1001", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ] - }, - "sysfs,/sys": { - "device": "sysfs", - "mount": "/sys", - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "proc,/proc": { - "device": "proc", - "mount": "/proc", - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "devpts,/dev/pts": { - "device": "devpts", - "mount": "/dev/pts", - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ] - }, - "securityfs,/sys/kernel/security": { - "device": "securityfs", - "mount": "/sys/kernel/security", - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "cgroup,/sys/fs/cgroup/systemd": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/systemd", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "xattr", - "release_agent=/lib/systemd/systemd-cgroups-agent", - "name=systemd" - ] - }, - "pstore,/sys/fs/pstore": { - "device": "pstore", - "mount": "/sys/fs/pstore", - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "cgroup,/sys/fs/cgroup/perf_event": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/perf_event", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "perf_event" - ] - }, - "cgroup,/sys/fs/cgroup/blkio": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/blkio", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "blkio" - ] - }, - "cgroup,/sys/fs/cgroup/devices": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/devices", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "devices" - ] - }, - "cgroup,/sys/fs/cgroup/net_cls,net_prio": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/net_cls,net_prio", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "net_cls", - "net_prio" - ] - }, - "cgroup,/sys/fs/cgroup/cpuset": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/cpuset", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpuset" - ] - }, - "cgroup,/sys/fs/cgroup/cpu,cpuacct": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/cpu,cpuacct", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpu", - "cpuacct" - ] - }, - "cgroup,/sys/fs/cgroup/memory": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/memory", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ] - }, - "cgroup,/sys/fs/cgroup/freezer": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/freezer", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "freezer" - ] - }, - "cgroup,/sys/fs/cgroup/pids": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/pids", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "pids" - ] - }, - "cgroup,/sys/fs/cgroup/hugetlb": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/hugetlb", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ] - }, - "hugetlbfs,/dev/hugepages": { - "device": "hugetlbfs", - "mount": "/dev/hugepages", - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "mqueue,/dev/mqueue": { - "device": "mqueue", - "mount": "/dev/mqueue", - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ] - }, - "systemd-1,/proc/sys/fs/binfmt_misc": { - "device": "systemd-1", - "mount": "/proc/sys/fs/binfmt_misc", - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ] - }, - "debugfs,/sys/kernel/debug": { - "device": "debugfs", - "mount": "/sys/kernel/debug", - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "fusectl,/sys/fs/fuse/connections": { - "device": "fusectl", - "mount": "/sys/fs/fuse/connections", - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ] - }, - "configfs,/sys/kernel/config": { - "device": "configfs", - "mount": "/sys/kernel/config", - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "lxcfs,/var/lib/lxcfs": { - "device": "lxcfs", - "mount": "/var/lib/lxcfs", - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ] - }, - "/dev/sda,": { - "device": "/dev/sda" - } - } - }, - "dmi": { - "dmidecode_version": "3.0" - }, - "init_package": "systemd", - "ohai_time": 1496219099.5081818, - "command": { - "ps": "ps -ef" - }, - "root_group": "root", - "languages": { - "ruby": { - "platform": "x86_64-linux-gnu", - "version": "2.3.1", - "release_date": "2016-04-26", - "target": "x86_64-pc-linux-gnu", - "target_cpu": "x86_64", - "target_vendor": "pc", - "target_os": "linux-gnu", - "host": "x86_64-pc-linux-gnu", - "host_cpu": "x86_64", - "host_os": "linux-gnu", - "host_vendor": "pc", - "bin_dir": "/usr/local/bin", - "ruby_bin": "/usr/local/bin/ruby", - "gems_dir": "/usr/local/gems", - "gem_bin": "/usr/local/bin/gem" - }, - "powershell": null - }, - "chef_packages": { - "chef": { - "version": "13.1.31", - "chef_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-13.1.31/lib" - }, - "ohai": { - "version": "13.1.0", - "ohai_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/ohai-13.1.0/lib/ohai" - } - }, - "counters": { - "network": { - "interfaces": { - "eth0": { - "rx": { - "bytes": "0", - "packets": "0", - "errors": "0", - "drop": 0, - "overrun": 0, - "frame": 0, - "compressed": 0, - "multicast": 0 - }, - "tx": { - "bytes": "342", - "packets": "0", - "errors": "0", - "drop": 0, - "overrun": 0, - "collisions": "0", - "carrier": 0, - "compressed": 0 - } - } - } - } - }, - "current_user": "fauxhai", - "domain": "local", - "etc": { - "passwd": { - "fauxhai": { - "dir": "/home/fauxhai", - "gid": 0, - "uid": 0, - "shell": "/bin/bash", - "gecos": "Fauxhai" - } - }, - "group": { - "fauxhai": { - "gid": 0, - "members": [ - "fauxhai" - ] - } - } - }, - "hostname": "Fauxhai", - "fqdn": "fauxhai.local", - "ipaddress": "10.0.0.2", - "keys": { - "ssh": { - "host_dsa_public": "ssh-dss AAAAB3NzaC1kc3MAAACBAJFo9BLAw4WKEs5hgipk5m423FzBsDXCZSMcC9ca/om/1VYzMqImixGe3uICDzNFUWxFoLJTQAOccyzo6MXZiQqwWJDLFi5qOSr6w2XcMyE+zd4wOyMoDiVM5fizmG8K3FzrqvGjwBcHcBdOQnavSijoj38DN25J9zhrid5BY4WlAAAAFQDxXrCyG52XCzn3FV4ej38wJBkomQAAAIBovGPJ4mP2P6BK8lHl0PPbktwQbWlpJ13oz6REJFDVcUi7vV26bX/BjQX+ohzZQzljdz1SpUbPc/8nuA4darYkVh91eBi307EN8IdxRHj2eBgp/ZG4yshIebG3WHrwJD/xUjjZ1MRfyDT1ermVi4LvjjPgWDxLZnPpMaR6S1nzgQAAAIEAj0Vd6DCWslvlsZ8+N53HWsqPi3gnx35JoLPz9Z2epkKIKqmEHav+93G3hdfztVa4I4t3phoPniQchYryF5+RNg8hqxKzjNtrIqUYCeuf2NJrksNsH7OZygPHZpqt4kTuwAGZxjxEGfAI0y8DhkU2ntp2LnzRnWH106BQBCmcXwo= fauxhai.local", - "host_rsa_public": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtLCeqtqr/HbnORckw1ukdLhpfGoOPFi5/esKEokzTqq1gsgQ2V8emmyjfq1i6XXfRtSBxkdlHv/GWdP5wBTuE2G85MzBkVSQPvmwQN8lX/JMPEEtKXkeOo0o92/PiSmvY4eRsdF0mw40Uvg7jtE3f3fxj497kzh5fKtkrHnF4x9gXCbVdr3FqXJfggR5IJwAxToerbK7x/uRS+7YuZI9Pip3tt14nv9ezwXcuGb/tvjWOZINiFl8izVIFKi7sxfTX09p4NgamxRS7TD2Yd0jT8nEoF9UZTsgXcJ1kDSx7N7NxFfNfP6rCdOGRRz4gUhXtsUjG/XkxPeCwZ7A9VnOD fauxhai.local" - } - }, - "macaddress": "11:11:11:11:11:11", - "network": { - "default_gateway": "10.0.0.1", - "default_interface": "eth0", - "settings": { - }, - "interfaces": { - "eth0": { - "addresses": { - "10.0.0.2": { - "broadcast": "10.0.0.255", - "family": "inet", - "netmask": "255.255.255.0", - "prefixlen": "24", - "scope": "Global" - } - }, - "arp": { - "10.0.0.1": "fe:ff:ff:ff:ff:ff" - }, - "encapsulation": "Ethernet", - "flags": [ - "BROADCAST", - "MULTICAST", - "UP", - "LOWER_UP" - ], - "mtu": "1500", - "number": "0", - "routes": [ - { - "destination": "10.0.0.0/24", - "family": "inet", - "scope": "link", - "proto": "kernel", - "src": "10.0.0.2" - } - ], - "state": "up", - "type": "eth" - } - } - }, - "uptime": "30 days 15 hours 07 minutes 30 seconds", - "uptime_seconds": 2646450, - "cpu": { - "real": 1, - "total": 1, - "cores": 1 - }, - "memory": { - "total": "1048576kB" - }, - "virtualization": { - "systems": { - } - } -} diff --git a/spec/fixtures/fauxhai/ubuntu/16.04.json b/spec/fixtures/fauxhai/ubuntu/16.04.json deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9mYXV4aGFpL3VidW50dS8xNi4wNC5qc29u..0000000000000000000000000000000000000000 --- a/spec/fixtures/fauxhai/ubuntu/16.04.json +++ /dev/null @@ -1,1519 +0,0 @@ -{ - "kernel": { - "name": "Linux", - "release": "4.8.0-52-generic", - "version": "#55~16.04.1-Ubuntu SMP Fri Apr 28 14:36:29 UTC 2017", - "machine": "x86_64", - "processor": "x86_64", - "os": "GNU/Linux", - "modules": { - "ip6table_filter": { - "size": "16384", - "refcount": "1" - }, - "ip6_tables": { - "size": "28672", - "refcount": "1" - }, - "iptable_filter": { - "size": "16384", - "refcount": "1" - }, - "ip_tables": { - "size": "24576", - "refcount": "1" - }, - "x_tables": { - "size": "36864", - "refcount": "4" - }, - "ppdev": { - "size": "20480", - "refcount": "0" - }, - "input_leds": { - "size": "16384", - "refcount": "0" - }, - "serio_raw": { - "size": "16384", - "refcount": "0" - }, - "parport_pc": { - "size": "32768", - "refcount": "0" - }, - "pvpanic": { - "size": "16384", - "refcount": "0" - }, - "parport": { - "size": "49152", - "refcount": "2" - }, - "ib_iser": { - "size": "49152", - "refcount": "0", - "version": "1.6" - }, - "rdma_cm": { - "size": "57344", - "refcount": "1" - }, - "iw_cm": { - "size": "49152", - "refcount": "1" - }, - "ib_cm": { - "size": "45056", - "refcount": "1" - }, - "ib_core": { - "size": "212992", - "refcount": "4" - }, - "configfs": { - "size": "40960", - "refcount": "2", - "version": "0.0.2" - }, - "iscsi_tcp": { - "size": "20480", - "refcount": "0" - }, - "libiscsi_tcp": { - "size": "24576", - "refcount": "1" - }, - "libiscsi": { - "size": "53248", - "refcount": "3" - }, - "scsi_transport_iscsi": { - "size": "98304", - "refcount": "4", - "version": "2.0-870" - }, - "autofs4": { - "size": "40960", - "refcount": "2" - }, - "btrfs": { - "size": "1060864", - "refcount": "0" - }, - "raid10": { - "size": "49152", - "refcount": "0" - }, - "raid456": { - "size": "110592", - "refcount": "0" - }, - "async_raid6_recov": { - "size": "20480", - "refcount": "1" - }, - "async_memcpy": { - "size": "16384", - "refcount": "2" - }, - "async_pq": { - "size": "16384", - "refcount": "2" - }, - "async_xor": { - "size": "16384", - "refcount": "3" - }, - "async_tx": { - "size": "16384", - "refcount": "5" - }, - "xor": { - "size": "24576", - "refcount": "2" - }, - "raid6_pq": { - "size": "102400", - "refcount": "4" - }, - "libcrc32c": { - "size": "16384", - "refcount": "1" - }, - "raid1": { - "size": "40960", - "refcount": "0" - }, - "raid0": { - "size": "20480", - "refcount": "0" - }, - "multipath": { - "size": "16384", - "refcount": "0" - }, - "linear": { - "size": "16384", - "refcount": "0" - }, - "crct10dif_pclmul": { - "size": "16384", - "refcount": "0" - }, - "crc32_pclmul": { - "size": "16384", - "refcount": "0" - }, - "ghash_clmulni_intel": { - "size": "16384", - "refcount": "0" - }, - "aesni_intel": { - "size": "167936", - "refcount": "0" - }, - "aes_x86_64": { - "size": "20480", - "refcount": "1" - }, - "lrw": { - "size": "16384", - "refcount": "1" - }, - "glue_helper": { - "size": "16384", - "refcount": "1" - }, - "ablk_helper": { - "size": "16384", - "refcount": "1" - }, - "cryptd": { - "size": "24576", - "refcount": "3" - }, - "psmouse": { - "size": "139264", - "refcount": "0" - }, - "virtio_net": { - "size": "32768", - "refcount": "0" - }, - "virtio_scsi": { - "size": "20480", - "refcount": "1" - } - } - }, - "os": "linux", - "os_version": "4.8.0-52-generic", - "lsb": { - "id": "Ubuntu", - "description": "Ubuntu 16.04.2 LTS", - "release": "16.04", - "codename": "xenial" - }, - "platform": "ubuntu", - "platform_version": "16.04", - "platform_family": "debian", - "filesystem": { - "by_device": { - "udev": { - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ], - "mounts": [ - "/dev" - ] - }, - "tmpfs": { - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ], - "mounts": [ - "/run", - "/dev/shm", - "/run/lock", - "/sys/fs/cgroup", - "/run/user/1001" - ] - }, - "/dev/sda1": { - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs", - "mounts": [ - "/" - ] - }, - "sysfs": { - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys" - ] - }, - "proc": { - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/proc" - ] - }, - "devpts": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ], - "mounts": [ - "/dev/pts" - ] - }, - "securityfs": { - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys/kernel/security" - ] - }, - "cgroup": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ], - "mounts": [ - "/sys/fs/cgroup/systemd", - "/sys/fs/cgroup/perf_event", - "/sys/fs/cgroup/blkio", - "/sys/fs/cgroup/devices", - "/sys/fs/cgroup/net_cls,net_prio", - "/sys/fs/cgroup/cpuset", - "/sys/fs/cgroup/cpu,cpuacct", - "/sys/fs/cgroup/memory", - "/sys/fs/cgroup/freezer", - "/sys/fs/cgroup/pids", - "/sys/fs/cgroup/hugetlb" - ] - }, - "pstore": { - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "mounts": [ - "/sys/fs/pstore" - ] - }, - "hugetlbfs": { - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/dev/hugepages" - ] - }, - "mqueue": { - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/dev/mqueue" - ] - }, - "systemd-1": { - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ], - "mounts": [ - "/proc/sys/fs/binfmt_misc" - ] - }, - "debugfs": { - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/kernel/debug" - ] - }, - "fusectl": { - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/fs/fuse/connections" - ] - }, - "configfs": { - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ], - "mounts": [ - "/sys/kernel/config" - ] - }, - "lxcfs": { - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ], - "mounts": [ - "/var/lib/lxcfs" - ] - }, - "/dev/sda": { - } - }, - "by_mountpoint": { - "/dev": { - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ], - "devices": [ - "udev" - ] - }, - "/run": { - "kb_size": "378828", - "kb_used": "5292", - "kb_available": "373536", - "percent_used": "2%", - "total_inodes": "473531", - "inodes_used": "460", - "inodes_available": "473071", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "size=378828k", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/": { - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs", - "devices": [ - "/dev/sda1" - ] - }, - "/dev/shm": { - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "1", - "inodes_available": "473530", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev" - ], - "devices": [ - "tmpfs" - ] - }, - "/run/lock": { - "kb_size": "5120", - "kb_used": "0", - "kb_available": "5120", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "8", - "inodes_available": "473523", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=5120k" - ], - "devices": [ - "tmpfs" - ] - }, - "/sys/fs/cgroup": { - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "16", - "inodes_available": "473515", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "mode=755" - ], - "devices": [ - "tmpfs" - ] - }, - "/run/user/1001": { - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ], - "devices": [ - "tmpfs" - ] - }, - "/sys": { - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "sysfs" - ] - }, - "/proc": { - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "proc" - ] - }, - "/dev/pts": { - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ], - "devices": [ - "devpts" - ] - }, - "/sys/kernel/security": { - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "securityfs" - ] - }, - "/sys/fs/cgroup/systemd": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "xattr", - "release_agent=/lib/systemd/systemd-cgroups-agent", - "name=systemd" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/pstore": { - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ], - "devices": [ - "pstore" - ] - }, - "/sys/fs/cgroup/perf_event": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "perf_event" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/blkio": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "blkio" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/devices": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "devices" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/net_cls,net_prio": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "net_cls", - "net_prio" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/cpuset": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpuset" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/cpu,cpuacct": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpu", - "cpuacct" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/memory": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/freezer": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "freezer" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/pids": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "pids" - ], - "devices": [ - "cgroup" - ] - }, - "/sys/fs/cgroup/hugetlb": { - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ], - "devices": [ - "cgroup" - ] - }, - "/dev/hugepages": { - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "hugetlbfs" - ] - }, - "/dev/mqueue": { - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "mqueue" - ] - }, - "/proc/sys/fs/binfmt_misc": { - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ], - "devices": [ - "systemd-1" - ] - }, - "/sys/kernel/debug": { - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "debugfs" - ] - }, - "/sys/fs/fuse/connections": { - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "fusectl" - ] - }, - "/sys/kernel/config": { - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ], - "devices": [ - "configfs" - ] - }, - "/var/lib/lxcfs": { - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ], - "devices": [ - "lxcfs" - ] - } - }, - "by_pair": { - "udev,/dev": { - "device": "udev", - "kb_size": "1886700", - "kb_used": "0", - "kb_available": "1886700", - "percent_used": "0%", - "mount": "/dev", - "total_inodes": "471675", - "inodes_used": "371", - "inodes_available": "471304", - "inodes_percent_used": "1%", - "fs_type": "devtmpfs", - "mount_options": [ - "rw", - "nosuid", - "relatime", - "size=1886700k", - "nr_inodes=471675", - "mode=755" - ] - }, - "tmpfs,/run": { - "device": "tmpfs", - "kb_size": "378828", - "kb_used": "5292", - "kb_available": "373536", - "percent_used": "2%", - "mount": "/run", - "total_inodes": "473531", - "inodes_used": "460", - "inodes_available": "473071", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "size=378828k", - "mode=755" - ] - }, - "/dev/sda1,/": { - "device": "/dev/sda1", - "kb_size": "10098468", - "kb_used": "1401956", - "kb_available": "8680128", - "percent_used": "14%", - "mount": "/", - "total_inodes": "1280000", - "inodes_used": "90038", - "inodes_available": "1189962", - "inodes_percent_used": "8%", - "fs_type": "ext4", - "mount_options": [ - "rw", - "relatime", - "data=ordered" - ], - "uuid": "67e1c0a6-cdc9-4247-b2e5-dd20ef042025", - "label": "cloudimg-rootfs" - }, - "tmpfs,/dev/shm": { - "device": "tmpfs", - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "mount": "/dev/shm", - "total_inodes": "473531", - "inodes_used": "1", - "inodes_available": "473530", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev" - ] - }, - "tmpfs,/run/lock": { - "device": "tmpfs", - "kb_size": "5120", - "kb_used": "0", - "kb_available": "5120", - "percent_used": "0%", - "mount": "/run/lock", - "total_inodes": "473531", - "inodes_used": "8", - "inodes_available": "473523", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "size=5120k" - ] - }, - "tmpfs,/sys/fs/cgroup": { - "device": "tmpfs", - "kb_size": "1894124", - "kb_used": "0", - "kb_available": "1894124", - "percent_used": "0%", - "mount": "/sys/fs/cgroup", - "total_inodes": "473531", - "inodes_used": "16", - "inodes_available": "473515", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "ro", - "nosuid", - "nodev", - "noexec", - "mode=755" - ] - }, - "tmpfs,/run/user/1001": { - "device": "tmpfs", - "kb_size": "378828", - "kb_used": "0", - "kb_available": "378828", - "percent_used": "0%", - "mount": "/run/user/1001", - "total_inodes": "473531", - "inodes_used": "4", - "inodes_available": "473527", - "inodes_percent_used": "1%", - "fs_type": "tmpfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "size=378828k", - "mode=700", - "uid=1001", - "gid=1002" - ] - }, - "sysfs,/sys": { - "device": "sysfs", - "mount": "/sys", - "fs_type": "sysfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "proc,/proc": { - "device": "proc", - "mount": "/proc", - "fs_type": "proc", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "devpts,/dev/pts": { - "device": "devpts", - "mount": "/dev/pts", - "fs_type": "devpts", - "mount_options": [ - "rw", - "nosuid", - "noexec", - "relatime", - "gid=5", - "mode=620", - "ptmxmode=000" - ] - }, - "securityfs,/sys/kernel/security": { - "device": "securityfs", - "mount": "/sys/kernel/security", - "fs_type": "securityfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "cgroup,/sys/fs/cgroup/systemd": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/systemd", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "xattr", - "release_agent=/lib/systemd/systemd-cgroups-agent", - "name=systemd" - ] - }, - "pstore,/sys/fs/pstore": { - "device": "pstore", - "mount": "/sys/fs/pstore", - "fs_type": "pstore", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime" - ] - }, - "cgroup,/sys/fs/cgroup/perf_event": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/perf_event", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "perf_event" - ] - }, - "cgroup,/sys/fs/cgroup/blkio": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/blkio", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "blkio" - ] - }, - "cgroup,/sys/fs/cgroup/devices": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/devices", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "devices" - ] - }, - "cgroup,/sys/fs/cgroup/net_cls,net_prio": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/net_cls,net_prio", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "net_cls", - "net_prio" - ] - }, - "cgroup,/sys/fs/cgroup/cpuset": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/cpuset", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpuset" - ] - }, - "cgroup,/sys/fs/cgroup/cpu,cpuacct": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/cpu,cpuacct", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "cpu", - "cpuacct" - ] - }, - "cgroup,/sys/fs/cgroup/memory": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/memory", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "memory" - ] - }, - "cgroup,/sys/fs/cgroup/freezer": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/freezer", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "freezer" - ] - }, - "cgroup,/sys/fs/cgroup/pids": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/pids", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "pids" - ] - }, - "cgroup,/sys/fs/cgroup/hugetlb": { - "device": "cgroup", - "mount": "/sys/fs/cgroup/hugetlb", - "fs_type": "cgroup", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "noexec", - "relatime", - "hugetlb" - ] - }, - "hugetlbfs,/dev/hugepages": { - "device": "hugetlbfs", - "mount": "/dev/hugepages", - "fs_type": "hugetlbfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "mqueue,/dev/mqueue": { - "device": "mqueue", - "mount": "/dev/mqueue", - "fs_type": "mqueue", - "mount_options": [ - "rw", - "relatime" - ] - }, - "systemd-1,/proc/sys/fs/binfmt_misc": { - "device": "systemd-1", - "mount": "/proc/sys/fs/binfmt_misc", - "fs_type": "autofs", - "mount_options": [ - "rw", - "relatime", - "fd=33", - "pgrp=1", - "timeout=0", - "minproto=5", - "maxproto=5", - "direct", - "pipe_ino=10449" - ] - }, - "debugfs,/sys/kernel/debug": { - "device": "debugfs", - "mount": "/sys/kernel/debug", - "fs_type": "debugfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "fusectl,/sys/fs/fuse/connections": { - "device": "fusectl", - "mount": "/sys/fs/fuse/connections", - "fs_type": "fusectl", - "mount_options": [ - "rw", - "relatime" - ] - }, - "configfs,/sys/kernel/config": { - "device": "configfs", - "mount": "/sys/kernel/config", - "fs_type": "configfs", - "mount_options": [ - "rw", - "relatime" - ] - }, - "lxcfs,/var/lib/lxcfs": { - "device": "lxcfs", - "mount": "/var/lib/lxcfs", - "fs_type": "fuse.lxcfs", - "mount_options": [ - "rw", - "nosuid", - "nodev", - "relatime", - "user_id=0", - "group_id=0", - "allow_other" - ] - }, - "/dev/sda,": { - "device": "/dev/sda" - } - } - }, - "dmi": { - "dmidecode_version": "3.0" - }, - "init_package": "systemd", - "ohai_time": 1496219099.5081818, - "command": { - "ps": "ps -ef" - }, - "root_group": "root", - "languages": { - "ruby": { - "platform": "x86_64-linux-gnu", - "version": "2.3.1", - "release_date": "2016-04-26", - "target": "x86_64-pc-linux-gnu", - "target_cpu": "x86_64", - "target_vendor": "pc", - "target_os": "linux-gnu", - "host": "x86_64-pc-linux-gnu", - "host_cpu": "x86_64", - "host_os": "linux-gnu", - "host_vendor": "pc", - "bin_dir": "/usr/local/bin", - "ruby_bin": "/usr/local/bin/ruby", - "gems_dir": "/usr/local/gems", - "gem_bin": "/usr/local/bin/gem" - }, - "powershell": null - }, - "chef_packages": { - "chef": { - "version": "13.1.31", - "chef_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-13.1.31/lib" - }, - "ohai": { - "version": "13.1.0", - "ohai_root": "/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/ohai-13.1.0/lib/ohai" - } - }, - "counters": { - "network": { - "interfaces": { - "eth0": { - "rx": { - "bytes": "0", - "packets": "0", - "errors": "0", - "drop": 0, - "overrun": 0, - "frame": 0, - "compressed": 0, - "multicast": 0 - }, - "tx": { - "bytes": "342", - "packets": "0", - "errors": "0", - "drop": 0, - "overrun": 0, - "collisions": "0", - "carrier": 0, - "compressed": 0 - } - } - } - } - }, - "current_user": "fauxhai", - "domain": "local", - "etc": { - "passwd": { - "fauxhai": { - "dir": "/home/fauxhai", - "gid": 0, - "uid": 0, - "shell": "/bin/bash", - "gecos": "Fauxhai" - } - }, - "group": { - "fauxhai": { - "gid": 0, - "members": [ - "fauxhai" - ] - } - } - }, - "hostname": "Fauxhai", - "fqdn": "fauxhai.local", - "ipaddress": "10.0.0.2", - "keys": { - "ssh": { - "host_dsa_public": "ssh-dss AAAAB3NzaC1kc3MAAACBAJFo9BLAw4WKEs5hgipk5m423FzBsDXCZSMcC9ca/om/1VYzMqImixGe3uICDzNFUWxFoLJTQAOccyzo6MXZiQqwWJDLFi5qOSr6w2XcMyE+zd4wOyMoDiVM5fizmG8K3FzrqvGjwBcHcBdOQnavSijoj38DN25J9zhrid5BY4WlAAAAFQDxXrCyG52XCzn3FV4ej38wJBkomQAAAIBovGPJ4mP2P6BK8lHl0PPbktwQbWlpJ13oz6REJFDVcUi7vV26bX/BjQX+ohzZQzljdz1SpUbPc/8nuA4darYkVh91eBi307EN8IdxRHj2eBgp/ZG4yshIebG3WHrwJD/xUjjZ1MRfyDT1ermVi4LvjjPgWDxLZnPpMaR6S1nzgQAAAIEAj0Vd6DCWslvlsZ8+N53HWsqPi3gnx35JoLPz9Z2epkKIKqmEHav+93G3hdfztVa4I4t3phoPniQchYryF5+RNg8hqxKzjNtrIqUYCeuf2NJrksNsH7OZygPHZpqt4kTuwAGZxjxEGfAI0y8DhkU2ntp2LnzRnWH106BQBCmcXwo= fauxhai.local", - "host_rsa_public": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtLCeqtqr/HbnORckw1ukdLhpfGoOPFi5/esKEokzTqq1gsgQ2V8emmyjfq1i6XXfRtSBxkdlHv/GWdP5wBTuE2G85MzBkVSQPvmwQN8lX/JMPEEtKXkeOo0o92/PiSmvY4eRsdF0mw40Uvg7jtE3f3fxj497kzh5fKtkrHnF4x9gXCbVdr3FqXJfggR5IJwAxToerbK7x/uRS+7YuZI9Pip3tt14nv9ezwXcuGb/tvjWOZINiFl8izVIFKi7sxfTX09p4NgamxRS7TD2Yd0jT8nEoF9UZTsgXcJ1kDSx7N7NxFfNfP6rCdOGRRz4gUhXtsUjG/XkxPeCwZ7A9VnOD fauxhai.local" - } - }, - "macaddress": "11:11:11:11:11:11", - "network": { - "default_gateway": "10.0.0.1", - "default_interface": "eth0", - "settings": { - }, - "interfaces": { - "eth0": { - "addresses": { - "10.0.0.2": { - "broadcast": "10.0.0.255", - "family": "inet", - "netmask": "255.255.255.0", - "prefixlen": "24", - "scope": "Global" - } - }, - "arp": { - "10.0.0.1": "fe:ff:ff:ff:ff:ff" - }, - "encapsulation": "Ethernet", - "flags": [ - "BROADCAST", - "MULTICAST", - "UP", - "LOWER_UP" - ], - "mtu": "1500", - "number": "0", - "routes": [ - { - "destination": "10.0.0.0/24", - "family": "inet", - "scope": "link", - "proto": "kernel", - "src": "10.0.0.2" - } - ], - "state": "up", - "type": "eth" - } - } - }, - "uptime": "30 days 15 hours 07 minutes 30 seconds", - "uptime_seconds": 2646450, - "cpu": { - "real": 1, - "total": 1, - "cores": 1 - }, - "memory": { - "total": "1048576kB" - }, - "virtualization": { - "systems": { - } - } -} diff --git a/spec/fixtures/migration/failed-migration-status-file b/spec/fixtures/migration/failed-migration-status-file deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9taWdyYXRpb24vZmFpbGVkLW1pZ3JhdGlvbi1zdGF0dXMtZmlsZQ==..0000000000000000000000000000000000000000 --- a/spec/fixtures/migration/failed-migration-status-file +++ /dev/null @@ -1,1 +0,0 @@ -1 diff --git a/spec/fixtures/migration/successful-migration-status-file b/spec/fixtures/migration/successful-migration-status-file deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9maXh0dXJlcy9taWdyYXRpb24vc3VjY2Vzc2Z1bC1taWdyYXRpb24tc3RhdHVzLWZpbGU=..0000000000000000000000000000000000000000 --- a/spec/fixtures/migration/successful-migration-status-file +++ /dev/null @@ -1,1 +0,0 @@ -0 diff --git a/spec/gitlab-ctl-commands-ee/geo_replication_spec.rb b/spec/gitlab-ctl-commands-ee/geo_replication_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2dlb19yZXBsaWNhdGlvbl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/geo_replication_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'spec_helper' -require 'omnibus-ctl' - -RSpec.describe 'gitlab-ctl geo-replication' do - let(:toggle_command) { double(Geo::ReplicationToggleCommand) } - - subject { Omnibus::Ctl.new('testing-ctl') } - - before do - allow_any_instance_of(Omnibus::Ctl).to receive(:require).and_call_original - allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( - '/opt/testing-ctl/embedded/service/omnibus-ctl-ee/lib/geo/replication_process' - ) do - require_relative('../../files/gitlab-ctl-commands-ee/lib/geo/replication_process') - end - allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( - '/opt/testing-ctl/embedded/service/omnibus-ctl-ee/lib/geo/replication_toggle_command' - ) do - require_relative('../../files/gitlab-ctl-commands-ee/lib/geo/replication_toggle_command') - end - - subject.load_file('files/gitlab-ctl-commands-ee/geo_replication.rb') - end - - it 'appends the geo replication pause and resume commands' do - expect(subject.get_all_commands_hash).to include('geo-replication-pause') - expect(subject.get_all_commands_hash).to include('geo-replication-resume') - end - - describe 'pause' do - it 'calls pause' do - expect(Geo::ReplicationToggleCommand).to receive(:new).with(anything, 'pause', anything).and_return(toggle_command) - expect(toggle_command).to receive(:execute!) - - subject.geo_replication_pause - end - end - - describe 'resume' do - it 'calls resume' do - expect(Geo::ReplicationToggleCommand).to receive(:new).with(anything, 'resume', anything).and_return(toggle_command) - expect(toggle_command).to receive(:execute!) - - subject.geo_replication_resume - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/geo_spec.rb b/spec/gitlab-ctl-commands-ee/geo_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2dlb19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/geo_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'omnibus-ctl' -require 'optparse' - -require_relative('../../files/gitlab-ctl-commands/lib/gitlab_ctl') -require_relative('../../files/gitlab-ctl-commands-ee/lib/geo') - -RSpec.describe 'gitlab-ctl geo' do - commands = %w(promote) - - command_lines = { - 'promote' => [] - } - - command_options = { - 'promote' => {} - } - - describe '.parse_options' do - before do - allow(Geo::Utils).to receive(:warn_and_exit).and_call_original - allow(Kernel).to receive(:exit) { |code| raise "Kernel.exit(#{code})" } - allow(Kernel).to receive(:warn) - end - - it 'throws an error when global options are invalid' do - expect { Geo.parse_options(%w(geo --foo)) }.to raise_error(OptionParser::ParseError) - end - - it 'throws an error when sub-command is not specified' do - expect { Geo.parse_options(%w(geo -v)) }.to raise_error(OptionParser::ParseError) - end - - it 'throws an error when sub-command is not defined' do - expect { Geo.parse_options(%w(geo -v foo)) }.to raise_error(OptionParser::ParseError) - end - - it 'recognizes global options' do - expect(Geo.parse_options(%w(geo -v -q promote))).to include(quiet: true, verbose: true) - end - - context 'when sub-command is passed' do - commands.each do |cmd| - it "parses #{cmd} options" do - cmd_line = command_lines[cmd] || [] - cmd_opts = command_options[cmd] || {} - cmd_opts[:command] = cmd - - expect(Geo.parse_options(%W(geo #{cmd}) + cmd_line)).to include(cmd_opts) - end - end - end - - context 'when help option is passed' do - it 'shows help message and exit for global help option' do - expect { Geo.parse_options(%w(geo -h)) }.to raise_error('Kernel.exit(0)') - expect(Geo::Utils).to have_received(:warn_and_exit).with(/Usage help/) - end - - commands.each do |cmd| - it "shows help message and exit for #{cmd} help option" do - expect { Geo.parse_options(%W(geo #{cmd} -h)) }.to raise_error('Kernel.exit(0)') - expect(Geo::Utils).to have_received(:warn_and_exit).with(instance_of(OptionParser)) - end - end - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/consul_spec.rb b/spec/gitlab-ctl-commands-ee/lib/consul_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9jb25zdWxfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/consul_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands-ee/lib') - -require 'consul' - -RSpec.describe ConsulHandler do - describe '#initialize' do - it 'creates instance based on args' do - instance = ConsulHandler.new([nil, nil, 'consul', 'kv', 'set'], 'rspec') - expect(instance.command).to eq(ConsulHandler::Kv) - expect(instance.subcommand).to eq('set') - expect(instance.input).to eq('rspec') - end - end - - describe '#execute' do - it 'calls the method on command' do - instance = ConsulHandler.new([nil, nil, 'consul', 'kv', 'set'], 'rspec') - instance.command = spy - expect(instance.command).to receive(:set).with('rspec') - instance.execute - end - end - - let(:consul_cmd) { '/opt/gitlab/embedded/bin/consul' } - - describe ConsulHandler::Kv do - it 'allows nil values' do - results = double('results', run_command: [], error!: nil, stdout: '') - expect(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} kv put foo ").and_return(results) - described_class.send(:put, 'foo') - end - - describe '#get' do - context 'existing key' do - before do - results = double('results', run_command: [], error!: nil, stdout: 'bar') - allow(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} kv get foo").and_return(results) - end - - it 'returns the expected key' do - expect(described_class.get('foo')).to eq('bar') - end - end - - context 'non-existing key' do - before do - results = double('results', run_command: [], stdout: '', stderr: 'oops') - allow(results).to receive(:error!).and_raise(StandardError) - allow(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} kv get foo").and_return(results) - end - - it 'raises an error' do - expect { described_class.get('foo') }.to raise_error(ConsulHandler::ConsulError, 'StandardError: oops') - end - end - end - end - - describe ConsulHandler::Encrypt do - describe '#keygen' do - it 'returns the generated key' do - generated_key = 'BYX6EPaUiUI0TDdm6gAMmmLnpJSwePJ33Xwh6rjCYbg=' - results = double('results', run_command: [], error!: nil, stdout: generated_key) - allow(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} keygen").and_return(results) - - expect(described_class.keygen).to eq(generated_key) - end - - it 'raises an error' do - results = double('results', run_command: [], stdout: '', stderr: 'oops') - allow(results).to receive(:error!).and_raise(StandardError) - allow(Mixlib::ShellOut).to receive(:new).with("#{consul_cmd} keygen").and_return(results) - - expect { described_class.keygen }.to raise_error(ConsulHandler::ConsulError, 'StandardError: oops') - end - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/geo/pitr_file_spec.rb b/spec/gitlab-ctl-commands-ee/lib/geo/pitr_file_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9nZW8vcGl0cl9maWxlX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/geo/pitr_file_spec.rb +++ /dev/null @@ -1,105 +0,0 @@ -require 'spec_helper' -require 'geo/pitr_file' - -RSpec.describe Geo::PitrFile do - let(:lsn) { 'imafakelsn' } - let(:filepath) { '/fake/file/path' } - subject { described_class.new(filepath, consul_key: 'fake-key') } - - context 'with a local file' do - before do - allow(Dir).to receive(:exist?).with('/opt/gitlab/service/consul').and_return(false) - end - - it "doesn't use consul" do - expect(subject.use_consul?).to be(false) - end - - context '#create' do - it 'creates the pitr file' do - expect(File).to receive(:write).with(filepath, lsn) - subject.create(lsn) - end - end - - context '#delete' do - it 'removes the pitr file' do - allow(File).to receive(:exist?).with(filepath).and_return(true) - expect(File).to receive(:delete).with(filepath) - - subject.delete - end - - it 'does not raise an error if the file does not exist' do - allow(File).to receive(:exist?).with(filepath).and_return(false) - allow(File).to receive(:delete).with(filepath).and_raise(Errno::ENOENT) - - expect { subject.delete }.to_not raise_error(Geo::PitrFileError, "Unable to delete PITR") - end - end - - context '#get' do - before do - allow(File).to receive(:read).with(filepath).and_return(lsn) - end - - it 'returns the correct lsn' do - expect(subject.get).to eq(lsn) - end - - it 'raises an error if the file does not exist' do - allow(File).to receive(:read).with(filepath).and_raise(Errno::ENOENT) - - expect { subject.get }.to raise_error(Geo::PitrFileError, "Unable to fetch PITR") - end - end - end - - context 'when consul is enabled' do - before do - allow(Dir).to receive(:exist?).with('/opt/gitlab/service/consul').and_return(true) - end - - it "uses consul" do - expect(subject.use_consul?).to be(true) - end - - context '#create' do - it 'create the write key entry' do - expect(ConsulHandler::Kv).to receive(:put).with(subject.consul_key, lsn) - - subject.create(lsn) - end - end - - context '#delete' do - it 'removes the consul key' do - expect(ConsulHandler::Kv).to receive(:delete).with(subject.consul_key) - - subject.delete - end - - it 'raises an error if the key does not exist' do - expect { subject.delete }.to raise_error(Geo::PitrFileError, 'Unable to delete PITR') - end - end - - context '#get' do - context 'existing key' do - before do - allow(ConsulHandler::Kv).to receive(:get).with(subject.consul_key).and_return(lsn) - end - - it 'returns the correct lsn' do - expect(subject.get).to eq(lsn) - end - end - - context 'non-existing key' do - it 'raises an error if the key does not exist' do - expect { subject.get }.to raise_error(Geo::PitrFileError, 'Unable to fetch PITR') - end - end - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/geo/promote_db_spec.rb b/spec/gitlab-ctl-commands-ee/lib/geo/promote_db_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9nZW8vcHJvbW90ZV9kYl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/geo/promote_db_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'spec_helper' -require 'geo/promote_db' -require 'gitlab_ctl/util' - -RSpec.describe Geo::PromoteDb, '#execute' do - let(:instance) { double(base_path: '/opt/gitlab/embedded') } - - subject(:command) { described_class.new(instance) } - - before do - allow($stdout).to receive(:puts) - allow($stdout).to receive(:print) - - allow(command).to receive(:run_command).with(any_args) - - allow(command).to receive(:run_command).and_return(double('error!' => nil)) - end - - context 'when PITR file does not exist' do - it 'does not run PITR recovery' do - expect(command).not_to receive(:write_recovery_settings) - - command.execute - end - end - - context 'postgres dir is in non-default location' do - it 'uses the specified postgres directory when writing ' do - expected_dir = '/non/default/location' - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'postgresql' => { 'dir' => expected_dir } }) - - expect(described_class.new(instance).postgresql_dir_path).to eq(expected_dir) - end - end - - context 'when PITR file exists' do - let(:lsn) { '16/B374D848' } - - before do - allow(command).to receive(:lsn_from_pitr_file).and_return(lsn) - allow(command).to receive(:write_geo_config_file).and_return(true) - end - - it 'writes out the recovery settings' do - expect(command).to receive(:write_recovery_settings).with(lsn) - - expect { command.execute }.to output( - /Recovery to point #{lsn} and promoting.../).to_stdout - end - - it 'writes out the geo configuration file' do - expect(command).to receive(:write_geo_config_file).once - - command.execute - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/geo/promote_spec.rb b/spec/gitlab-ctl-commands-ee/lib/geo/promote_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9nZW8vcHJvbW90ZV9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/geo/promote_spec.rb +++ /dev/null @@ -1,499 +0,0 @@ -require 'spec_helper' -require 'geo/promote' -require 'geo/promote_db' -require 'gitlab_ctl/util' - -require_relative('../../../../files/gitlab-ctl-commands-ee/lib/patroni') - -RSpec.describe Geo::Promote, '#execute' do - let(:base_path) { '/opt/gitlab/embedded' } - let(:ctl) { double(base_path: base_path, data_path: '/var/opt/gitlab/postgresql/data') } - let(:config_path) { Dir.mktmpdir } - let(:gitlab_cluster_config_path) { File.join(config_path, 'gitlab-cluster.json') } - let(:options) { { force: false } } - - subject(:command) { described_class.new(ctl, options) } - - before do - stub_const('GitlabCluster::CONFIG_PATH', config_path) - stub_const('GitlabCluster::JSON_FILE', gitlab_cluster_config_path) - - allow($stdin).to receive(:gets).and_return('y') - allow($stdout).to receive(:puts) - allow($stdout).to receive(:print) - - allow(ctl).to receive(:log).with(any_args) - allow(ctl).to receive(:service_enabled?).and_return(false) - allow(command).to receive(:run_command).with(any_args) - end - - around do |example| - example.run - rescue SystemExit - end - - after do - FileUtils.rm_rf(config_path) - end - - describe '#execute' do - context 'when not running in force mode' do - it 'asks for confirmation' do - expect { command.execute }.to output(/Are you sure you want to proceed\?/).to_stdout - end - end - - context 'when running in force mode' do - let(:options) { { force: true } } - - it 'does not ask for final confirmation' do - expect { command.execute }.to_not output(/Are you sure you want to proceed\?/).to_stdout - end - end - - context 'when there are no services to promote' do - before do - allow(ctl).to receive(:service_enabled?).and_return(false) - end - - it 'prints a message' do - expect { command.execute }.to output( - /No actions are required to promote this node./).to_stdout - end - - it 'returns 0 as exit code' do - expect { command.execute }.to raise_error(SystemExit) do |error| - expect(error.status).to eq(0) - end - end - end - - context 'when puma is enabled' do - before do - stub_service_enabled('puma') - end - - shared_examples 'promote secondary site to primary site' do - context 'on a primary node' do - it 'does not try to promote the secondary site to primary site' do - stub_primary_node - - allow(command).to receive(:run_reconfigure) - allow(command).to receive(:restart_services) - - expect(command).not_to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:set_secondary_as_primary", live: true) - - command.execute - end - end - - context 'on a secondary node' do - before do - stub_secondary_node - allow(command).to receive(:run_reconfigure) - end - - it 'promotes the secondary site to primary site' do - allow(command).to receive(:restart_services) - - expect(command).to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:set_secondary_as_primary", live: true).once.and_return(double(error?: false)) - - command.execute - end - - it 'restarts the puma service' do - allow(command).to receive(:promote_to_primary) - - expect(ctl).to receive(:run_sv_command_for_service).with('restart', 'puma').once.and_return(double(zero?: true)) - - command.execute - end - end - - context 'on a misconfigured node' do - it 'aborts promotion with an error message' do - stub_misconfigured_node - - expect { command.execute }.to output(/Unable to detect the role of this Geo node/).to_stdout - end - end - end - - context 'on a single-server secondary site' do - before do - stub_single_server_secondary_site - end - - it 'toggles the Geo primary/secondary roles' do - allow(command).to receive(:run_reconfigure) - allow(command).to receive(:promote_to_primary) - allow(command).to receive(:restart_services) - - command.execute - - expect(read_file_content(gitlab_cluster_config_path)).to eq("primary" => true, "secondary" => false) - end - - it 'runs reconfigure' do - allow(command).to receive(:toggle_geo_services) - allow(command).to receive(:promote_to_primary) - allow(command).to receive(:restart_services) - - expect(ctl).to receive(:run_chef).with(reconfigure_cmd).once.and_return(double(success?: true)) - - command.execute - end - - include_examples 'promote secondary site to primary site' - end - - context 'on a multiple-server secondary site' do - before do - stub_multiple_server_secondary_site - end - - include_examples 'promote secondary site to primary site' - - it 'disables Geo secondary settings' do - allow(command).to receive(:run_reconfigure) - allow(command).to receive(:promote_to_primary) - allow(command).to receive(:restart_services) - - command.execute - - expect(read_file_content(gitlab_cluster_config_path)).to eq("geo_secondary" => { "enable" => false }) - end - - it 'runs reconfigure' do - allow(command).to receive(:promote_to_primary) - allow(command).to receive(:restart_services) - - expect(ctl).to receive(:run_chef).with(reconfigure_cmd).once.and_return(double(success?: true)) - - command.execute - end - end - end - - context 'when gitaly is enabled' do - before do - stub_service_enabled('gitaly') - end - - it 'restarts the gitaly service' do - allow(command).to receive(:toggle_geo_services) - allow(command).to receive(:promote_to_primary) - allow(command).to receive(:run_reconfigure) - - expect(ctl).to receive(:run_sv_command_for_service).with('restart', 'gitaly').once.and_return(double(zero?: true)) - - command.execute - end - end - - context 'when praefect is enabled' do - before do - stub_service_enabled('praefect') - end - - it 'restarts the praefect service' do - allow(command).to receive(:toggle_geo_services) - allow(command).to receive(:promote_to_primary) - allow(command).to receive(:run_reconfigure) - - expect(ctl).to receive(:run_sv_command_for_service).with('restart', 'praefect').once.and_return(double(zero?: true)) - - command.execute - end - end - - context 'when workhorse is enabled' do - before do - stub_service_enabled('gitlab-workhorse') - end - - it 'restarts the workhorse service' do - allow(command).to receive(:toggle_geo_services) - allow(command).to receive(:promote_to_primary) - allow(command).to receive(:run_reconfigure) - - expect(ctl).to receive(:run_sv_command_for_service).with('restart', 'gitlab-workhorse').once.and_return(double(zero?: true)) - - command.execute - end - end - - shared_examples 'single-server secondary site' do - context 'on a single-server secondary site' do - before do - stub_single_server_secondary_site - end - - it 'toggles the Geo primary/secondary roles' do - allow(command).to receive(:run_reconfigure) - - command.execute - - expect(read_file_content(gitlab_cluster_config_path)).to eq("primary" => true, "secondary" => false) - end - - it 'runs reconfigure' do - expect(ctl).to receive(:run_chef).with(reconfigure_cmd).once.and_return(double(success?: true)) - - command.execute - end - end - end - - shared_examples 'multiple-server secondary site' do |settings| - context 'on a multiple-server secondary site' do - before do - stub_multiple_server_secondary_site - end - - it 'disables Geo secondary settings' do - allow(command).to receive(:run_reconfigure) - - command.execute - - expect(read_file_content(gitlab_cluster_config_path)).to eq(settings) - end - end - end - - context 'when sidekiq is enabled' do - before do - stub_service_enabled('sidekiq') - end - - include_examples 'single-server secondary site' - include_examples 'multiple-server secondary site', "geo_secondary" => { "enable" => false } - end - - context 'when geo-logcursor is enabled' do - before do - allow(ctl).to receive(:service_enabled?).with('geo-logcursor').and_return(true) - end - - include_examples 'single-server secondary site' - include_examples 'multiple-server secondary site', "geo_logcursor" => { "enable" => false } - end - - context 'when geo-postgresql is enabled' do - before do - allow(ctl).to receive(:service_enabled?).with('geo-postgresql').and_return(true) - end - - include_examples 'single-server secondary site' - include_examples 'multiple-server secondary site', "geo_postgresql" => { "enable" => false } - end - - context 'when postgresql is enabled' do - before do - stub_service_enabled('postgresql') - end - - context 'when PostgreSQL is in recovery mode' do - before do - stub_pg_is_in_recovery - end - - it 'promotes database to begin read-write operations' do - allow(command).to receive(:run_reconfigure) - - expect_any_instance_of(Geo::PromoteDb).to receive(:execute).once.and_return(true) - - command.execute - end - end - - context 'when PostgreSQL is not in recovery mode' do - before do - stub_pg_is_not_in_recovery - end - - it 'does not promote the PostgreSQL database' do - allow(command).to receive(:run_reconfigure) - - expect_any_instance_of(Geo::PromoteDb).not_to receive(:execute) - - command.execute - end - end - - it 'runs reconfigure' do - allow(command).to receive(:promote_database) - - expect(ctl).to receive(:run_chef).with(reconfigure_cmd).once.and_return(double(success?: true)) - - command.execute - end - end - - context 'when patroni is enabled' do - before do - stub_service_enabled('patroni') - end - - shared_examples 'promotes a Patroni leader' do - context 'when PostgreSQL is in recovery mode' do - before do - stub_pg_is_in_recovery - end - - it 'promotes database to begin read-write operations' do - allow(command).to receive(:disable_patroni_standby_cluster) - allow(command).to receive(:run_reconfigure) - - expect_any_instance_of(Geo::PromoteDb).to receive(:execute).once.and_return(true) - - command.execute - end - end - - context 'when PostgreSQL is not in recovery mode' do - before do - stub_pg_is_not_in_recovery - end - - it 'does not promote the PostgreSQL database' do - allow(command).to receive(:disable_patroni_standby_cluster) - allow(command).to receive(:run_reconfigure) - - expect_any_instance_of(Geo::PromoteDb).not_to receive(:execute) - - command.execute - end - end - - it 'disables Patroni Standby cluster settings' do - allow(command).to receive(:promote_postgresql_read_write) - allow(command).to receive(:run_reconfigure) - - command.execute - - expect(read_file_content(gitlab_cluster_config_path)).to eq("patroni" => { "standby_cluster" => { "enable" => false } }) - end - - it 'pauses Patroni, runs reconfigure and resume Patroni' do - allow(command).to receive(:promote_postgresql_read_write) - allow(command).to receive(:disable_patroni_standby_cluster) - - expect(command).to receive(:run_command).with(patroni_pause_cmd).twice.and_return(double(success?: true)) - expect(ctl).to receive(:run_chef).with(reconfigure_cmd).twice.and_return(double(success?: true)) - expect(command).to receive(:run_command).with(patroni_resume_cmd).twice.and_return(double(success?: true)) - - command.execute - end - end - - context 'on a Patroni standby leader' do - before do - allow(Patroni::Client).to receive(:new).and_return(double(standby_leader?: true, leader?: false, replica?: false)) - end - - include_examples 'promotes a Patroni leader' - end - - context 'on a Patroni leader' do - before do - allow(Patroni::Client).to receive(:new).and_return(double(standby_leader?: false, leader?: true, replica?: false)) - end - - include_examples 'promotes a Patroni leader' - end - - context 'on a Patroni replica' do - before do - allow(Patroni::Client).to receive(:new).and_return(double(standby_leader?: false, leader?: false, replica?: true)) - end - - it 'does not promote the PostgreSQL database' do - allow(command).to receive(:run_reconfigure) - - expect_any_instance_of(Geo::PromoteDb).not_to receive(:execute) - - command.execute - end - - it 'disables Patroni Standby cluster settings' do - allow(command).to receive(:promote_postgresql_read_write) - allow(command).to receive(:run_reconfigure) - - command.execute - - expect(read_file_content(gitlab_cluster_config_path)).to eq("patroni" => { "standby_cluster" => { "enable" => false } }) - end - - it 'runs reconfigure' do - allow(command).to receive(:promote_postgresql_read_write) - allow(command).to receive(:disable_patroni_standby_cluster) - - expect(command).not_to receive(:run_command).with(patroni_pause_cmd) - expect(ctl).to receive(:run_chef).with(reconfigure_cmd).twice.and_return(double(success?: true)) - expect(command).not_to receive(:run_command).with(patroni_resume_cmd) - - command.execute - end - end - end - - context 'when promotion succeeds' do - before do - allow(command).to receive(:ask_for_confirmation) - allow(command).to receive(:check_running_services) - allow(command).to receive(:promote_database) - allow(command).to receive(:toggle_geo_services) - allow(command).to receive(:promote_to_primary) - allow(command).to receive(:run_reconfigure) - allow(command).to receive(:restart_services) - end - - it 'prints a success message' do - expect { command.execute }.to output( - /You successfully promoted the current node! It might take some time to reload the services, and for the changes to take effect./).to_stdout - end - end - - let(:patroni_pause_cmd) { "#{base_path}/bin/gitlab-ctl patroni pause" } - let(:patroni_resume_cmd) { "#{base_path}/bin/gitlab-ctl patroni resume" } - let(:pg_is_in_recovery_cmd) { "#{base_path}/bin/gitlab-psql -c \"SELECT pg_is_in_recovery();\" -q -t" } - let(:reconfigure_cmd) { "#{base_path}/embedded/cookbooks/dna.json" } - - def stub_service_enabled(service) - allow(ctl).to receive(:service_enabled?).with(service).and_return(true) - end - - def stub_primary_node - allow(command).to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:site:role", live: true).and_return(double(error?: false, stdout: 'primary')) - end - - def stub_secondary_node - allow(command).to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:site:role", live: true).and_return(double(error?: false, stdout: 'secondary')) - end - - def stub_misconfigured_node - allow(command).to receive(:run_command).with("#{base_path}/bin/gitlab-rake geo:site:role", live: true).and_return(double(error?: true, stdout: 'misconfigured')) - end - - def stub_single_server_secondary_site - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return('roles' => { 'geo-secondary' => { 'enable' => true } }) - end - - def stub_multiple_server_secondary_site - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return('roles' => {}) - end - - def stub_pg_is_in_recovery - allow(command).to receive(:run_command).with(pg_is_in_recovery_cmd, anything).and_return(double(error?: false, stdout: 't')) - end - - def stub_pg_is_not_in_recovery - allow(command).to receive(:run_command).with(pg_is_in_recovery_cmd, anything).and_return(double(error?: false, stdout: 'f')) - end - - def read_file_content(fullpath) - JSON.parse(File.read(fullpath)) - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb b/spec/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9nZW8vcHJvbW90ZV90b19wcmltYXJ5X25vZGVfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/geo/promote_to_primary_node_spec.rb +++ /dev/null @@ -1,275 +0,0 @@ -require 'spec_helper' -require 'fileutils' -require 'geo/promote_to_primary_node' -require 'geo/promote_db' -require 'geo/promotion_preflight_checks' -require 'gitlab_ctl/util' - -RSpec.describe Geo::PromoteToPrimaryNode, '#execute' do - let(:options) { { skip_preflight_checks: true } } - - let(:instance) { double(base_path: '/opt/gitlab/embedded', data_path: '/var/opt/gitlab/postgresql/data') } - - subject(:command) { described_class.new(instance, options) } - - let(:config_path) { Dir.mktmpdir } - - before do - allow($stdout).to receive(:puts) - allow($stdout).to receive(:print) - - allow(command).to receive(:run_command).with(any_args) - end - - after do - FileUtils.rm_rf(config_path) - end - - describe '#promote_postgresql_to_primary' do - before do - allow(STDIN).to receive(:gets).and_return('y') - - allow(command).to receive(:toggle_geo_roles).and_return(true) - allow(command).to receive(:reconfigure).and_return(true) - allow(command).to receive(:promote_to_primary).and_return(true) - allow(command).to receive(:success_message).and_return(true) - end - - it 'promotes the database' do - expect_any_instance_of(Geo::PromoteDb).to receive(:execute) - - command.execute - end - end - - describe '#run_preflight_checks' do - before do - allow(STDIN).to receive(:gets).and_return('y') - - allow(command).to receive(:toggle_geo_roles).and_return(true) - allow(command).to receive(:promote_postgresql_to_primary).and_return(true) - allow(command).to receive(:reconfigure).and_return(true) - allow(command).to receive(:promote_to_primary).and_return(true) - allow(command).to receive(:success_message).and_return(true) - end - - context 'when `--skip-preflight-checks` is passed' do - it 'does not run execute promotion preflight checks' do - expect_any_instance_of(Geo::PromotionPreflightChecks).not_to receive(:execute) - - command.execute - end - end - - context 'when `--skip-preflight-checks` is not passed' do - let(:options) { { confirm_primary_is_down: true } } - - before do - allow_any_instance_of(Geo::PromotionPreflightChecks).to receive( - :execute).and_return(true) - end - - it 'runs preflight checks' do - expect_any_instance_of(Geo::PromotionPreflightChecks).to receive(:execute) - - command.execute - end - - it 'passes given options to preflight checks command' do - expect(Geo::PromotionPreflightChecks).to receive(:new).with( - '/opt/gitlab/embedded', options).and_call_original - - command.execute - end - end - end - - describe '#toggle_geo_roles' do - let(:gitlab_cluster_config_path) { File.join(config_path, 'gitlab-cluster.json') } - - before do - stub_const('GitlabCluster::CONFIG_PATH', config_path) - stub_const('GitlabCluster::JSON_FILE', gitlab_cluster_config_path) - - allow(STDIN).to receive(:gets).and_return('y') - - allow(command).to receive(:run_preflight_checks).and_return(true) - allow(command).to receive(:promote_postgresql_to_primary).and_return(true) - allow(command).to receive(:reconfigure).and_return(true) - allow(command).to receive(:promote_to_primary).and_return(true) - allow(command).to receive(:success_message).and_return(true) - end - - context 'when the cluster configuration file does not exist' do - it 'creates the file with the Geo primary role enabled and secondary role disabled' do - command.execute - - expect(File.exist?(gitlab_cluster_config_path)).to eq(true) - expect(read_file_content(gitlab_cluster_config_path)).to eq("primary" => true, "secondary" => false) - end - end - - context 'when the cluster configuration file exists' do - it 'disables the Geo secondary role' do - write_file_content(gitlab_cluster_config_path, primary: false, secondary: true) - - command.execute - - expect(read_file_content(gitlab_cluster_config_path)).to eq("primary" => true, "secondary" => false) - end - end - - def read_file_content(fullpath) - JSON.parse(File.read(fullpath)) - end - - def write_file_content(fullpath, content) - File.open(fullpath, 'w') do |f| - f.write(content.to_json) - f.chmod(0600) - end - end - end - - context 'when preflight checks pass' do - before do - allow(STDIN).to receive(:gets).and_return('y') - - allow_any_instance_of(Geo::PromotionPreflightChecks).to receive( - :execute).and_return(true) - - allow(command).to receive(:toggle_geo_roles).and_return(true) - allow(command).to receive(:promote_postgresql_to_primary).and_return(true) - allow(command).to receive(:reconfigure).and_return(true) - allow(command).to receive(:promote_to_primary).and_return(true) - allow(command).to receive(:success_message).and_return(true) - end - - context 'when running in force mode' do - let(:options) { { force: true } } - - it 'does not ask for final confirmation' do - expect { command.execute }.not_to output( - /WARNING\: Secondary will now be promoted to primary./).to_stdout - end - end - - context 'when not running in force mode' do - let(:options) { { force: false } } - - it 'asks for confirmation' do - expect { command.execute }.to output( - /WARNING\: Secondary will now be promoted to primary./).to_stdout - end - - context 'when final confirmation is given' do - it 'calls all the subcommands' do - expect(command).to receive(:toggle_geo_roles) - expect(command).to receive(:promote_postgresql_to_primary) - expect(command).to receive(:reconfigure) - expect(command).to receive(:promote_to_primary) - expect(command).to receive(:success_message) - - command.execute - end - end - end - end - - context 'when preflight checks fail' do - around do |example| - example.run - rescue SystemExit - end - - before do - allow(STDIN).to receive(:gets).and_return('n') - - allow(command).to receive(:promote_postgresql_to_primary).and_return(true) - allow(command).to receive(:reconfigure).and_return(true) - allow(command).to receive(:promote_to_primary).and_return(true) - - allow_any_instance_of(Geo::PromotionPreflightChecks).to receive( - :execute).and_raise(SystemExit) - end - - context 'when running in force mode' do - let(:options) { { force: true } } - - it 'asks for confirmation' do - expect { command.execute }.to output( - /Are you sure you want to proceed?/ - ).to_stdout - end - - it 'exits with 1 if user denies' do - allow(STDIN).to receive(:gets).and_return('n') - - expect { command.execute }.to raise_error(SystemExit) do |error| - expect(error.status).to eq(1) - end - end - - it 'calls all the subcommands if user affirms' do - allow(STDIN).to receive(:gets).and_return('y') - - is_expected.to receive(:toggle_geo_roles) - is_expected.to receive(:promote_postgresql_to_primary) - is_expected.to receive(:reconfigure) - is_expected.to receive(:promote_to_primary) - is_expected.to receive(:success_message) - - command.execute - end - end - - context 'when not running in force mode' do - let(:options) { { force: false } } - - it 'exits with 1' do - expect { command.execute }.to raise_error(SystemExit) - end - end - end - - context 'when writing to the cluster configuration file fail' do - around do |example| - example.run - rescue SystemExit - end - - before do - allow(STDIN).to receive(:gets).and_return('y') - - allow(command).to receive(:run_preflight_checks).and_return(true) - - allow(GitlabCluster.config).to receive(:save).and_return(false) - end - - it 'exits with 1' do - expect { command.execute }.to raise_error(SystemExit) - end - end - - context 'when writing to the cluster configuration file succeed' do - before do - allow(STDIN).to receive(:gets).and_return('y') - - allow(command).to receive(:promote_postgresql_to_primary).and_return(true) - allow(command).to receive(:reconfigure).and_return(true) - allow(command).to receive(:promote_to_primary).and_return(true) - allow(command).to receive(:success_message).and_return(true) - - allow(GitlabCluster.config).to receive(:save).and_return(true) - end - - it 'calls all the subcommands' do - expect(command).to receive(:promote_postgresql_to_primary) - expect(command).to receive(:reconfigure) - expect(command).to receive(:promote_to_primary) - expect(command).to receive(:success_message) - - command.execute - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/geo/promotion_preflight_checks_spec.rb b/spec/gitlab-ctl-commands-ee/lib/geo/promotion_preflight_checks_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9nZW8vcHJvbW90aW9uX3ByZWZsaWdodF9jaGVja3Nfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/geo/promotion_preflight_checks_spec.rb +++ /dev/null @@ -1,145 +0,0 @@ -require 'spec_helper' -require 'geo/promotion_preflight_checks' -require 'gitlab_ctl/util' - -RSpec.describe Geo::PromotionPreflightChecks, '#execute' do - let(:confirmation) { 'y' } - let(:options) { { confirm_primary_is_down: true } } - - subject(:command) { described_class.new(nil, options) } - - before do - allow(STDIN).to receive(:gets).and_return(confirmation) - allow(command).to receive(:run_command).with(any_args) - - shell_out_object = double.tap do |shell_out_object| - allow(shell_out_object).to receive(:stdout).and_return('SUCCESS') - allow(shell_out_object).to receive(:error?).and_return(false) - end - - allow(command).to receive(:run_command).with( - 'gitlab-rake gitlab:geo:check_replication_verification_status') - .and_return(shell_out_object) - end - - it 'prints preflight check instructions' do - expect { command.execute }.to output( - /Ensure you have completed the following manual preflight checks/) - .to_stdout - end - - context 'when manual checks are confirmed' do - it 'does not raise an error' do - expect { command.execute }.to_not raise_error - end - - it 'runs `check_replication_verification_status` task' do - is_expected.to receive(:run_command).with( - 'gitlab-rake gitlab:geo:check_replication_verification_status').once - - command.execute - end - - context 'when gitlab:geo:check_replication_verification_status fails' do - around do |example| - example.run - rescue SystemExit - end - - before do - shell_out_object = double.tap do |shell_out_object| - allow(shell_out_object).to receive(:stdout).and_return('error message') - allow(shell_out_object).to receive(:error?).and_return(true) - end - - allow(command).to receive(:run_command).with( - 'gitlab-rake gitlab:geo:check_replication_verification_status') - .once - .and_return(shell_out_object) - end - - it 'prints error message when rake task errors' do - expect { command.execute }.to output( - /error message\nERROR: Replication\/verification is incomplete./).to_stdout - end - - it 'exits with 1 when rake task errors' do - expect { command.execute }.to raise_error(SystemExit) do |error| - expect(error.status).to eq(1) - end - end - end - - context 'when gitlab:geo:check_replication_verification_status passes' do - it 'prints a success message' do - expect { command.execute }.to output( - /All preflight checks have passed. This node can now be promoted./) - .to_stdout - end - end - end - - context 'when manual checks are not confirmed' do - let(:confirmation) { 'n' } - - around do |example| - example.run - rescue SystemExit - end - - it 'print error message' do - expect { command.execute }.to output( - /ERROR: Manual preflight checks were not performed/ - ).to_stdout - end - end - - describe 'option --confirm-primary-is-down' do - before do - allow(command).to receive(:confirm_manual_checks).and_return(true) - end - - context 'when the option is not passed' do - let(:options) { {} } - let(:confirmation) { 'n' } - - around do |example| - example.run - rescue SystemExit - end - - it 'asks user for confirmation' do - expect { command.execute }.to output( - /Is primary down? (N\/y)/) - .to_stdout - end - - it 'prints an error message when user doesn not select y/Y' do - expect { command.execute }.to output( - /ERROR: Primary node must be down./) - .to_stdout - end - end - - context 'when the option is passed' do - it 'does not ask user for confirmation' do - expect { command.execute }.not_to output( - /Is primary down? (N\/y)/) - .to_stdout - end - end - end - - context 'when all checks pass' do - before do - allow(command).to receive(:confirm_manual_checks).and_return(true) - allow(command).to receive(:confirm_primary_is_down).and_return(true) - end - - it 'prints a success message' do - expect { command.execute }.to output( - /All preflight checks have passed. This node can now be promoted./) - .to_stdout - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/geo/replication_process_spec.rb b/spec/gitlab-ctl-commands-ee/lib/geo/replication_process_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9nZW8vcmVwbGljYXRpb25fcHJvY2Vzc19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/geo/replication_process_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << './files/gitlab-ctl-commands-ee/lib' -$LOAD_PATH << './files/gitlab-ctl-commands/lib' - -require 'geo/replication_process' -require 'gitlab_ctl/util' - -RSpec.describe Geo::ReplicationProcess do - let(:error_text) { 'AN ERROR' } - let(:good_status) { double('Command status', error?: false) } - let(:bad_status) { double('Command status', error?: true, stdout: error_text) } - let(:pause_cmd) { %r{gitlab-psql .* -c 'SELECT pg_wal_replay_pause\(\);'} } - let(:resume_cmd) { %r{gitlab-psql .* -c 'SELECT pg_wal_replay_resume\(\);'} } - let(:instance) { double(base_path: '/opt/gitlab/embedded', data_path: '/var/opt/gitlab/postgresql/data') } - let(:db_name) { 'gitlab_db_name' } - let(:options) do - { - db_name: db_name - } - end - - subject { described_class.new(instance, options) } - - describe '#pause' do - it 'logs a message if the rake task throws an error' do - expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:pause/).and_return(bad_status) - expect(GitlabCtl::Util).to receive(:run_command).with(pause_cmd).and_return(good_status) - - expect do - subject.pause - end.to output(/#{error_text}/).to_stdout - end - - it 'raises an exception if unable to pause replication' do - expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:pause/).and_return(good_status) - expect(GitlabCtl::Util).to receive(:run_command).with(pause_cmd).and_return(bad_status) - - expect do - subject.pause - end.to raise_error(/Unable to pause postgres replication/) - end - - it 'provides the requested database from the options' do - expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:pause/).and_return(good_status) - expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-psql -d #{db_name}/).and_return(good_status) - - subject.pause - end - end - - describe '#resume' do - it 'raises an exception if unable to resume pg replication' do - expect(GitlabCtl::Util).to receive(:run_command).with(resume_cmd).and_return(bad_status) - - expect do - subject.resume - end.to raise_error(/Unable to resume postgres replication/) - end - - it 'raises an error if rake task to resume fails' do - expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:resume/).and_return(bad_status) - expect(GitlabCtl::Util).to receive(:run_command).with(resume_cmd).and_return(good_status) - - expect do - subject.resume - end.to raise_error(/Unable to resume replication from primary/) - end - - it 'provides the requested database from the options' do - expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-rake geo:replication:resume/).and_return(good_status) - expect(GitlabCtl::Util).to receive(:run_command).with(/gitlab-psql -d #{db_name}/).and_return(good_status) - - subject.resume - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/geo/replication_spec.rb b/spec/gitlab-ctl-commands-ee/lib/geo/replication_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9nZW8vcmVwbGljYXRpb25fc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/geo/replication_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << './files/gitlab-ctl-commands-ee/lib' -$LOAD_PATH << './files/gitlab-ctl-commands/lib' - -require 'geo/replication' -require 'gitlab_ctl/util' - -RSpec.describe Geo::Replication, '#execute' do - let(:command) { spy('command spy', error?: false) } - let(:instance) { double(base_path: '/opt/gitlab/embedded', data_path: '/var/opt/gitlab') } - let(:file) { spy('file spy') } - let(:options) do - { - now: true, - host: 'localhost', - port: 9999, - user: 'my-user', - sslmode: 'disable', - sslcompression: 1, - recovery_target_timeline: 'latest', - db_name: 'gitlab_db_name' - } - end - - subject { described_class.new(instance, options) } - - before do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'postgresql' => { 'dir' => '/var/opt/gitlab/postgresql' } }) - - allow(instance).to receive(:service_enabled?).and_return(true) - allow(STDOUT).to receive(:puts) - allow(subject).to receive(:print) - allow(File).to receive(:open).and_yield(file) - allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/standby.signal').and_return(true) - allow(File).to receive(:exist?).with(anything).and_call_original - allow(File).to receive(:write).with('/var/opt/gitlab/postgresql/data/standby.signal', "") - - allow(GitlabCtl::Util).to receive(:run_command).and_return(command) - allow(subject).to receive(:postgresql_user).and_return('gitlab-psql') - allow(subject).to receive(:postgresql_group).and_return('gitlab-psql') - allow(subject).to receive(:postgresql_version).and_return(12) - end - - it 'replicates geo database' do - expect(subject).to receive(:ask_pass).and_return('password') - expect(GitlabCtl::Util).to receive(:run_command) - .with(%r{/embedded/bin/pg_basebackup}, anything) - - subject.execute - end - - it 'uses the db_name option' do - expect(subject).to receive(:ask_pass).and_return('password') - expect(GitlabCtl::Util).to receive(:run_command) - .with(%r{gitlab_db_name}, anything) - - subject.execute - end - - it 'uses the supplied postgres directory' do - expected_dir = '/non/default/location' - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'postgresql' => { 'dir' => expected_dir } }) - - expect(described_class.new(instance, options).postgresql_dir_path).to eq(expected_dir) - end - - it 'writes recovery settings to postgresql.conf and creates a standby file' do - allow(File).to receive(:write) - allow(STDIN).to receive(:gets).and_return("pass\n") - allow(subject).to receive(:ask_pass).and_return('password') - - expect(subject).to receive(:write_recovery_settings!) - expect(subject).to receive(:create_standby_file!) - - subject.execute - end - - context 'when there is TTY available' do - before do - allow(STDIN).to receive(:tty?).and_return(true) - end - - it 'asks for database password in an interactive mode' do - expect(STDIN).to receive(:getpass).and_return('password') - - subject.execute - end - end - - context 'when there is no TTY available' do - before do - allow(STDIN).to receive(:tty?).and_return(false) - end - - it 'asks for a database password in a non-interactive mode' do - expect(STDIN).to receive(:gets).and_return('pass') - - subject.execute - - expect(file).to have_received(:write).with(/recovery_target_timeline = 'latest'/) - end - - it 'strips the password' do - allow(STDIN).to receive(:gets).and_return("pass\n") - - subject.execute - - expect(file).to have_received(:write).with("localhost:9999:*:my-user:pass\n") - expect(file).to have_received(:write).with(/password=pass sslmode=disable sslcompression=1'\n/) - end - end - - context 'with a custom port' do - let(:options) { { now: true, skip_backup: true, host: 'localhost', port: 9999, user: 'my-user', slot_name: 'foo', db_name: 'gitlab_db_name' } } - let(:cmd) { %q(PGPASSFILE=/var/opt/gitlab/postgresql/.pgpass /opt/gitlab/embedded/bin/gitlab-psql -h localhost -p 9999 -U my-user -d gitlab_db_name -t -c "SELECT slot_name FROM pg_create_physical_replication_slot('foo');") } - let(:status) { double(error?: false, stdout: '') } - - it 'executes a gitlab-psql call to check replication slots' do - expect(subject).to receive(:check_gitlab_active?).and_return(true) - expect(subject).to receive(:ask_pass).and_return('mypassword') - - allow(GitlabCtl::Util).to receive(:run_command).and_return(status) - expect(GitlabCtl::Util).to receive(:run_command).with(cmd, anything).and_return(status) - - subject.execute - end - end - - context 'when user has to provide a confirmation text' do - let(:options) { { now: false, host: 'localhost', port: 9999, user: 'my-user', slot_name: 'foo' } } - - it 'asks for confirmation string' do - allow(subject).to receive(:ask_pass).and_return('mypass') - expect(STDIN).to receive(:gets).and_return('replicate') - expect(GitlabCtl::Util).to receive(:run_command) - .with("PGPASSFILE=/var/opt/gitlab/postgresql/.pgpass /opt/gitlab/embedded/embedded/bin/pg_basebackup -h localhost -p 9999 -D /var/opt/gitlab/postgresql/data -U my-user -v -P -X stream -S foo", - anything) - - subject.execute - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/geo/replication_toggle_command_spec.rb b/spec/gitlab-ctl-commands-ee/lib/geo/replication_toggle_command_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9nZW8vcmVwbGljYXRpb25fdG9nZ2xlX2NvbW1hbmRfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/geo/replication_toggle_command_spec.rb +++ /dev/null @@ -1,90 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << './files/gitlab-ctl-commands-ee/lib' -$LOAD_PATH << './files/gitlab-ctl-commands/lib' - -require 'geo/replication_toggle_command' -require 'geo/promote_to_primary_node' -require 'gitlab_ctl/util' - -RSpec.describe Geo::ReplicationToggleCommand do - let(:status) { double('Command status', error?: false) } - let(:arguments) { [] } - let(:ctl_instance) { double('gitlab-ctl instance', base_path: '', data_path: 'data_path') } - - before do - allow_any_instance_of(Geo::ReplicationToggleCommand).to receive(:current_lsn).and_return('16/B374D848') - end - - describe 'pause' do - subject { described_class.new(ctl_instance, 'pause', arguments) } - - it 'calls pause' do - expect_any_instance_of(Geo::ReplicationProcess).to receive(:pause) - expect(File).to receive(:write).with('data_path/postgresql/data/geo-pitr-file', '16/B374D848') - - expect { subject.execute! }.to output(/Create Geo point-in-time recovery file/).to_stdout - end - - it 'rescues and exits if postgres has an error' do - expect_any_instance_of(Geo::ReplicationProcess).to receive(:pause).and_raise(Geo::PsqlError, "Oh nose!") - - expect do - expect { subject.execute! }.to raise_error(SystemExit) - end.to output(/Postgres encountered an error: Oh nose!/).to_stdout - end - - context 'database specified' do - let(:arguments) { %w(--db_name=database_i_want) } - - it 'uses the specified database' do - expect(Geo::ReplicationProcess).to receive(:new).with(any_args, { db_name: 'database_i_want' }).and_call_original - expect_any_instance_of(Geo::ReplicationProcess).to receive(:pause) - expect(File).to receive(:write).with('data_path/postgresql/data/geo-pitr-file', '16/B374D848') - - expect { subject.execute! }.to output(/Create Geo point-in-time recovery file/).to_stdout - end - end - end - - describe 'resume' do - subject { described_class.new(ctl_instance, 'resume', arguments) } - - before do - allow(File).to receive(:delete).with('data_path/postgresql/data/geo-pitr-file').and_return(1) - end - - it 'calls resume' do - expect_any_instance_of(Geo::ReplicationProcess).to receive(:resume) - - expect { subject.execute! }.to output(/Remove Geo point-in-time recovery file/).to_stdout - end - - it 'rescues and exits if postgres has an error' do - expect_any_instance_of(Geo::ReplicationProcess).to receive(:resume).and_raise(Geo::PsqlError, "Oh nose!") - - expect do - expect { subject.execute! }.to raise_error(SystemExit) - end.to output(/Postgres encountered an error: Oh nose!/).to_stdout - end - - it 'rescues and exits if rake has an error' do - expect_any_instance_of(Geo::ReplicationProcess).to receive(:resume).and_raise(Geo::RakeError, "Oh nose!") - - expect do - expect { subject.execute! }.to raise_error(SystemExit) - end.to output(/Rake encountered an error: Oh nose!/).to_stdout - end - - context 'database specified' do - let(:arguments) { %w(--db_name=database_i_want) } - - it 'uses the specified database' do - expect(Geo::ReplicationProcess).to receive(:new).with(any_args, { db_name: 'database_i_want' }).and_call_original - expect_any_instance_of(Geo::ReplicationProcess).to receive(:resume) - - expect { subject.execute! }.to output(/Remove Geo point-in-time recovery file/).to_stdout - end - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/patroni_spec.rb b/spec/gitlab-ctl-commands-ee/lib/patroni_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9wYXRyb25pX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/patroni_spec.rb +++ /dev/null @@ -1,225 +0,0 @@ -require 'spec_helper' -require 'omnibus-ctl' -require 'optparse' - -require_relative('../../../files/gitlab-ctl-commands/lib/gitlab_ctl') -require_relative('../../../files/gitlab-ctl-commands-ee/lib/patroni') - -RSpec.describe 'Patroni' do - core_commands = %w(bootstrap check-leader check-replica check-standby-leader reinitialize-replica) - additional_commands = %w(members pause resume failover switchover restart reload) - all_commands = core_commands + additional_commands - - command_lines = { - 'bootstrap' => %w(--srcdir=SRCDIR --scope=SCOPE --datadir=DATADIR), - 'pause' => %w(-w), - 'resume' => %w(--wait), - 'failover' => %w(--master MASTER --candidate CANDIDATE), - 'switchover' => %w(--master MASTER --candidate CANDIDATE --scheduled SCHEDULED), - 'reinitialize-replica' => %w(--wait --member MEMBER), - 'restart' => [], - 'reload' => [] - } - - command_options = { - 'bootstrap' => { srcdir: 'SRCDIR', scope: 'SCOPE', datadir: 'DATADIR' }, - 'pause' => { wait: true }, - 'resume' => { wait: true }, - 'failover' => { master: 'MASTER', candidate: 'CANDIDATE' }, - 'switchover' => { master: 'MASTER', candidate: 'CANDIDATE', scheduled: 'SCHEDULED' }, - 'reinitialize-replica' => { wait: true, member: 'MEMBER' }, - 'restart' => {}, - 'reload' => {} - - } - - patronictl_command = { - 'members' => 'list', - 'pause' => 'pause -w', - 'resume' => 'resume -w', - 'failover' => 'failover --force --master MASTER --candidate CANDIDATE', - 'switchover' => 'switchover --force --master MASTER --candidate CANDIDATE --scheduled SCHEDULED', - 'restart' => 'restart --force fake-scope fake-node', - 'reload' => 'reload --force fake-scope fake-node' - } - - describe '.parse_options' do - before do - allow(Patroni::Utils).to receive(:warn_and_exit).and_call_original - allow(Kernel).to receive(:exit) { |code| raise "Kernel.exit(#{code})" } - allow(Kernel).to receive(:warn) - end - - it 'should throw error when global options are invalid' do - expect { Patroni.parse_options(%w(patroni --foo)) }.to raise_error(OptionParser::ParseError) - end - - it 'should throw error when sub-command is not specified' do - expect { Patroni.parse_options(%w(patroni -v)) }.to raise_error(OptionParser::ParseError) - end - - it 'should throw error when sub-command is not defined' do - expect { Patroni.parse_options(%w(patroni -v foo)) }.to raise_error(OptionParser::ParseError) - end - - it 'should recognize global options' do - expect(Patroni.parse_options(%w(patroni -v -q members))).to include(quiet: true, verbose: true) - end - - context 'when sub-command is passed' do - all_commands.each do |cmd| - it "should parse #{cmd} options" do - cmd_line = command_lines[cmd] || [] - cmd_opts = command_options[cmd] || {} - cmd_opts[:command] = cmd - - expect(Patroni.parse_options(%W(patroni #{cmd}) + cmd_line)).to include(cmd_opts) - end - end - end - - context 'when help option is passed' do - it 'should show help message and exit for global help option' do - expect { Patroni.parse_options(%w(patroni -h)) }.to raise_error('Kernel.exit(0)') - expect(Patroni::Utils).to have_received(:warn_and_exit).with(/Usage help/) - end - - all_commands.each do |cmd| - it "should show help message and exit for #{cmd} help option" do - expect { Patroni.parse_options(%W(patroni #{cmd} -h)) }.to raise_error('Kernel.exit(0)') - expect(Patroni::Utils).to have_received(:warn_and_exit).with(instance_of(OptionParser)) - end - end - end - end - - describe 'additional commands' do - before do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'patroni' => { 'config_dir' => '/fake' } }) - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({ 'patroni' => { 'scope' => 'fake-scope', 'name' => 'fake-node' } }) - allow(GitlabCtl::Util).to receive(:run_command) - end - - additional_commands.each do |cmd| - it "should run the relevant patronictl command for #{cmd}" do - Patroni.send(cmd.to_sym, command_options[cmd] || {}) - - expect(GitlabCtl::Util).to have_received(:run_command).with( - "/opt/gitlab/embedded/bin/patronictl -c /fake/patroni.yaml #{patronictl_command[cmd]}", - user: 'root', live: false) - end - end - end - - describe 'command output on non-Patroni node' do - before do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'patroni' => nil }) - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({ 'patroni' => nil }) - end - - additional_commands.each do |cmd| - it "should raise errors when running command #{cmd}" do - expect { Patroni.send(cmd.to_sym, command_options[cmd]) }.to( - raise_error(RuntimeError, /no Patroni configuration/) - ) - end - end - end - - describe '.init_db' do - before do - allow(GitlabCtl::Util).to receive(:run_command) - end - - it 'should call initdb command with the specified options' do - Patroni.init_db(command_options['bootstrap']) - - expect(GitlabCtl::Util).to have_received(:run_command).with('/opt/gitlab/embedded/bin/initdb -D DATADIR -E UTF8') - end - end - - describe '.copy_config' do - before do - allow(FileUtils).to receive(:cp_r) - end - - it 'should call initdb command with the specified options' do - Patroni.copy_config(command_options['bootstrap']) - - expect(FileUtils).to have_received(:cp_r).with('SRCDIR/.', 'DATADIR') - end - end - - describe '.leader?, .replica?, and .standyLeader?' do - before do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'patroni' => { 'api_address' => 'http://localhost:8009' } }) - allow_any_instance_of(Patroni::Client).to receive(:get).with('/leader').and_yield(Struct.new(:code).new(leader_status)) - allow_any_instance_of(Patroni::Client).to receive(:get).with('/replica').and_yield(Struct.new(:code).new(replica_status)) - allow_any_instance_of(Patroni::Client).to receive(:get).with('/standby-leader').and_yield(Struct.new(:code).new(standby_leader_status)) - end - - context 'when node is leader' do - let(:leader_status) { '200' } - let(:replica_status) { '503' } - let(:standby_leader_status) { '503' } - - it 'should identify node role' do - expect(Patroni.leader?({})).to be(true) - expect(Patroni.replica?({})).to be(false) - expect(Patroni.standby_leader?({})).to be(false) - end - end - - context 'when node is replica' do - let(:leader_status) { '503' } - let(:replica_status) { '200' } - let(:standby_leader_status) { '503' } - - it 'should identify node role' do - expect(Patroni.leader?({})).to be(false) - expect(Patroni.replica?({})).to be(true) - expect(Patroni.standby_leader?({})).to be(false) - end - end - - context 'when node is standby leader' do - let(:leader_status) { '503' } - let(:replica_status) { '503' } - let(:standby_leader_status) { '200' } - - it 'should identify node role' do - expect(Patroni.leader?({})).to be(false) - expect(Patroni.replica?({})).to be(false) - expect(Patroni.standby_leader?({})).to be(true) - end - end - end - - describe '.reinitialize_replica' do - before do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ 'patroni' => { 'config_dir' => '/fake' } }) - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({ 'patroni' => { 'scope' => 'fake-scope', 'name' => 'fake-node' } }) - allow(GitlabCtl::Util).to receive(:run_command) - end - - context 'when member option is set' do - it 'calls reinit command with the specified options to reinitialize the cluster member' do - Patroni.reinitialize_replica(command_options['reinitialize-replica']) - - expect(GitlabCtl::Util).to have_received(:run_command).with( - '/opt/gitlab/embedded/bin/patronictl -c /fake/patroni.yaml reinit --force --wait fake-scope MEMBER', - user: 'root', live: true) - end - end - - context 'when member option is not set' do - it 'calls reinit command with the specified options to reinitialize the current cluster member' do - Patroni.reinitialize_replica(command_options['reinitialize-replica'].slice(:wait)) - - expect(GitlabCtl::Util).to have_received(:run_command).with( - '/opt/gitlab/embedded/bin/patronictl -c /fake/patroni.yaml reinit --force --wait fake-scope fake-node', - user: 'root', live: true) - end - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/pgbouncer_spec.rb b/spec/gitlab-ctl-commands-ee/lib/pgbouncer_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9wZ2JvdW5jZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/pgbouncer_spec.rb +++ /dev/null @@ -1,120 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands-ee/lib') - -require 'pgbouncer' - -RSpec.describe Pgbouncer::Databases do - let(:fake_ohai) do - { - 'gitlab' => { - 'gitlab-rails' => { - 'db_database' => 'fake_database' - }, - }, - 'pgbouncer' => { - 'databases_ini' => '/fakedata/pgbouncer/databases.ini', - 'databases_json' => '/fakedata/pgbouncer/databases.json' - } - } - end - - let(:fake_databases_json) do - { - fakedb: { - host: 'fakehost', - user: 'fakeuser', - port: 9999, - password: 'dslgkjdfgklfsd' - } - }.to_json.to_s - end - - before do - allow(GitlabCtl::Util).to receive(:get_fqdn).and_return('fakehost') - allow(Dir).to receive(:exist?).and_call_original - allow(Dir).to receive(:exist?).with('/fakedata/pgbouncer').and_return(true) - allow(File).to receive(:exist?).with('/fakedata/pgbouncer/databases.json').and_return(true) - allow(File).to receive(:read).and_call_original - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return(fake_ohai) - allow(File).to receive(:read).with('/fakedata/pgbouncer/databases.json').and_return(fake_databases_json) - @obj = Pgbouncer::Databases.new({}, '/fakeinstall', '/fakedata') - end - - context 'default behavior' do - it 'allows creating a new object' do - expect(@obj.class).to be(Pgbouncer::Databases) - end - - it 'creates the database object' do - results = { - "fakedb" => "host=fakehost port=9999 auth_user=fakeuser" - } - expect(@obj.databases).to eq(results) - end - - it 'renders the template' do - expect(@obj.render).to eq( - "[databases]\n\nfakedb = host=fakehost port=9999 auth_user=fakeuser\n\n" - ) - end - end - - context 'with options' do - before do - options = { - 'databases_ini' => '/another/databases.ini', - 'databases_json' => '/another/databases.json', - 'port' => 8888, - 'user' => 'fakeuser' - } - allow(File).to receive(:exist?).with('/another/databases.json').and_return(true) - allow(File).to receive(:read).with('/another/databases.json').and_return(fake_databases_json) - @obj = Pgbouncer::Databases.new(options, '/fakeinstall', '/fakedata') - end - - it 'sets the custom options' do - expect(@obj.ini_file).to eq('/another/databases.ini') - expect(@obj.json_file).to eq('/another/databases.json') - end - - it 'renders the template' do - expect(@obj.render).to eq( - "[databases]\n\nfakedb = host=fakehost port=8888 auth_user=fakeuser\n\n" - ) - end - end - - context 'with empty databases.json' do - before do - allow(File).to receive(:read).with('/fakedata/pgbouncer/databases.json').and_return({}.to_s) - @obj = Pgbouncer::Databases.new({}, '/fakeinstall', '/fakedata') - end - - it 'should generate an databases.ini with sane defaults' do - expect(@obj.render).to eq( - "[databases]\n\nfake_database = \n\n" - ) - end - end - - context 'with pgbouncer listening' do - before do - allow(@obj).to receive(:show_databases).and_return("nyan") - end - - it 'should be running' do - expect(@obj.running?).to be true - end - end - - context 'with pgbouncer not listening' do - before do - allow(@obj).to receive(:show_databases).and_raise(GitlabCtl::Errors::ExecutionError.new("nya", "nya", "neko")) - end - - it 'should not be running' do - expect(@obj.running?).to be false - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/lib/postgresql/ee_spec.rb b/spec/gitlab-ctl-commands-ee/lib/postgresql/ee_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL2xpYi9wb3N0Z3Jlc3FsL2VlX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/lib/postgresql/ee_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands-ee/lib') - -require 'gitlab_ctl/util' -require 'postgresql/ee' - -RSpec.describe GitlabCtl::PostgreSQL::EE do - describe "#get_primary" do - context 'when Consul disabled' do - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes) - .and_return({}) - end - - it 'should raise an error' do - expect { described_class.get_primary } .to raise_error(/Consul agent is not enabled/) - end - end - - context 'when PostgreSQL service name is not defined' do - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes) - .and_return( - { - 'consul' => { - 'enable' => true, - } - } - ) - end - - it 'should raise an error' do - expect { described_class.get_primary } .to raise_error(/PostgreSQL service name is not defined/) - end - end - - context 'when required settings are available' do - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes) - .and_return( - { - 'consul' => { - 'enable' => true, - }, - 'patroni' => { - 'scope' => 'fake' - } - } - ) - allow_any_instance_of(Resolv::DNS).to receive(:getresources) - .with('master.fake.service.consul', Resolv::DNS::Resource::IN::SRV) - .and_return([Struct.new(:target, :port).new('fake.address', 6432)]) - allow_any_instance_of(Resolv::DNS).to receive(:getaddress) - .with('fake.address') - .and_return('1.2.3.4') - end - - it 'should get the list of PostgreSQL endpoints' do - expect(described_class.get_primary).to eq ['1.2.3.4:6432'] - end - end - end -end diff --git a/spec/gitlab-ctl-commands-ee/promote_db_spec.rb b/spec/gitlab-ctl-commands-ee/promote_db_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL3Byb21vdGVfZGJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/promote_db_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'geo/promote_db' - -RSpec.describe 'gitlab-ctl promote-db' do - let(:klass) { Geo::PromoteDb } - let(:command_name) { 'promote-db' } - let(:command_script) { 'promote_db' } - - include_context 'ctl' - - it_behaves_like 'gitlab geo promotion commands', 'promote-db' - - # rubocop:disable Style/MutableConstant, Lint/ConstantDefinitionInBlock - it 'prints the deprecation message' do - # ARGV contains the commands that were passed to rspec, which are - # invalid for the omnibus-ctl commands - oldargv = ARGV - ARGV = [] - - expect_any_instance_of(klass).to receive(:execute) - - expect { ctl.send(command_script) }.to output( - /WARNING: As of GitLab 14.5, this command is deprecated/).to_stdout - - ARGV = oldargv - end - # rubocop:enable Style/MutableConstant, Lint/ConstantDefinitionInBlock -end diff --git a/spec/gitlab-ctl-commands-ee/promote_to_primary_node_spec.rb b/spec/gitlab-ctl-commands-ee/promote_to_primary_node_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL3Byb21vdGVfdG9fcHJpbWFyeV9ub2RlX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/promote_to_primary_node_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'geo/promote_to_primary_node' - -RSpec.describe 'gitlab-ctl promote-to-primary-node' do - let(:klass) { Geo::PromoteToPrimaryNode } - let(:command_name) { 'promote-to-primary-node' } - let(:command_script) { 'promote_to_primary_node' } - - include_context 'ctl' - - it_behaves_like 'gitlab geo promotion commands', 'promote-to-primary-node' - - it_behaves_like 'geo promotion command accepts option', - '--confirm-primary-is-down', - { confirm_primary_is_down: true } - - it_behaves_like 'geo promotion command accepts option', - '--skip-preflight-checks', - { skip_preflight_checks: true } - - it_behaves_like 'geo promotion command accepts option', - '--force', - { force: true } - - # rubocop:disable Style/MutableConstant, Lint/ConstantDefinitionInBlock - it 'prints the deprecation message' do - # ARGV contains the commands that were passed to rspec, which are - # invalid for the omnibus-ctl commands - oldargv = ARGV - ARGV = [] # rubocop:disable Style/MutableConstant - - expect_any_instance_of(klass).to receive(:execute) - - expect { ctl.send(command_script) }.to output( - /WARNING: As of GitLab 14.5, this command is deprecated/).to_stdout - - ARGV = oldargv - end - # rubocop:enable Style/MutableConstant, Lint/ConstantDefinitionInBlock -end diff --git a/spec/gitlab-ctl-commands-ee/promotion_preflight_checks_spec.rb b/spec/gitlab-ctl-commands-ee/promotion_preflight_checks_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL3Byb21vdGlvbl9wcmVmbGlnaHRfY2hlY2tzX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/promotion_preflight_checks_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' -require 'geo/promotion_preflight_checks' - -RSpec.shared_context 'promotion-preflight-checks' do - let(:command_name) { 'promotion-preflight-checks' } - let(:klass) { Geo::PromotionPreflightChecks } - let(:command_script) { 'promotion_preflight_checks' } -end - -RSpec.describe 'gitlab-ctl promotion-preflight-checks' do - include_context 'promotion-preflight-checks' - include_context 'ctl' - - it_behaves_like 'gitlab geo promotion commands' - - it_behaves_like 'geo promotion command accepts option', - '--confirm-primary-is-down', - { confirm_primary_is_down: true } -end diff --git a/spec/gitlab-ctl-commands-ee/replicate_geo_database_spec.rb b/spec/gitlab-ctl-commands-ee/replicate_geo_database_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzLWVlL3JlcGxpY2F0ZV9nZW9fZGF0YWJhc2Vfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands-ee/replicate_geo_database_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -require 'spec_helper' -require 'omnibus-ctl' - -RSpec.describe 'gitlab-ctl replicate-geo-database' do - subject { Omnibus::Ctl.new('testing-ctl') } - - let(:required_arguments) do - %w(--host=gitlab-primary.geo - --slot-name=gitlab_primary_geo) - end - - before do - allow_any_instance_of(Omnibus::Ctl).to receive(:require).and_call_original - allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( - '/opt/testing-ctl/embedded/service/omnibus-ctl-ee/lib/geo/replication' - ) do - require_relative('../../files/gitlab-ctl-commands-ee/lib/geo/replication') - end - - subject.load_file('files/gitlab-ctl-commands-ee/replicate_geo_database.rb') - end - - it 'appends a geo replication command' do - expect(subject.get_all_commands_hash).to include('replicate-geo-database') - end - - describe "#replicate_geo_database" do - it 'executes the geo replication command' do - stub_command_arguments(required_arguments) - - expect(Geo::Replication).to receive(:new).and_call_original - .with(subject, hash_including(host: 'gitlab-primary.geo', - slot_name: 'gitlab_primary_geo')) - - expect_any_instance_of(Geo::Replication).to receive(:execute) - - replicate_geo_database - end - - it 'applies defaults to optional arguments' do - stub_command_arguments(required_arguments) - - expect(Geo::Replication).to receive(:new).and_call_original - .with(subject, hash_including(host: 'gitlab-primary.geo', - slot_name: 'gitlab_primary_geo', - user: 'gitlab_replicator', - port: 5432, - password: nil, - now: false, - force: false, - skip_backup: false, - skip_replication_slot: false, - backup_timeout: 1800, - sslmode: 'verify-full', - sslcompression: 0, - recovery_target_timeline: 'latest', - db_name: 'gitlabhq_production')) - - expect_any_instance_of(Geo::Replication).to receive(:execute) - - replicate_geo_database - end - - it 'requires the host argument' do - stub_command_arguments(%w(--slot-name=gitlab_primary_geo)) - - expect(Geo::Replication).not_to receive(:new) - - # Important to catch this SystemExit or else RSpec exits - expect { subject.replicate_geo_database }.to raise_error(SystemExit).and( - output(/missing argument: host/).to_stdout - ) - end - - it 'requires the slot-name argument' do - stub_command_arguments(%w(--host=gitlab-primary.geo)) - - expect(Geo::Replication).not_to receive(:new) - - # Important to catch this SystemExit or else RSpec exits - expect { subject.replicate_geo_database }.to raise_error(SystemExit).and( - output(/missing argument: --slot-name/).to_stdout - ) - end - - context 'with SSL compression enabled' do - it 'enables SSL compression' do - stub_command_arguments( - required_arguments + - %w(--sslmode=disable - --sslcompression=1 - --no-wait)) - - expect(Geo::Replication).to receive(:new).and_call_original - .with(subject, hash_including(host: 'gitlab-primary.geo', - slot_name: 'gitlab_primary_geo', - sslmode: 'disable', - sslcompression: 1, - now: true)) - - expect_any_instance_of(Geo::Replication).to receive(:execute) - - replicate_geo_database - end - end - - context 'with db_name specified' do - it 'sets the db_name option' do - stub_command_arguments( - required_arguments + - %w(--db-name=custom_db_name)) - - expect(Geo::Replication).to receive(:new).and_call_original - .with(subject, hash_including(host: 'gitlab-primary.geo', - slot_name: 'gitlab_primary_geo', - db_name: 'custom_db_name')) - - expect_any_instance_of(Geo::Replication).to receive(:execute) - - replicate_geo_database - end - end - end - - def stub_command_arguments(arguments) - expect_any_instance_of(Omnibus::Ctl::GeoReplicationCommand) - .to receive(:arguments).and_return(arguments) - end - - def replicate_geo_database - # GeoReplicationCommand is designed to `exit 1` when there is a problem - # during option parsing. Tests that trigger this exit will also silently - # exit RSpec immediately, *and* they will *not* fail. - # - # So if a test expects a successful call, we *must* catch at least - # SystemExit for the test to be valid. And since other errors are always - # possible, and should also fail the test, we don't need to specify - # SystemExit here. - # - # In short, this is necessary for a failable test. - expect { subject.replicate_geo_database }.not_to raise_error - end -end diff --git a/spec/gitlab-ctl-commands/gitlab_ctl_spec.rb b/spec/gitlab-ctl-commands/gitlab_ctl_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzL2dpdGxhYl9jdGxfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands/gitlab_ctl_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' -require 'omnibus-ctl' - -RSpec.describe 'gitlab-ctl' do - before do - allow_any_instance_of(Omnibus::Ctl).to receive(:require).and_call_original - allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( - '/opt/testing-ctl/embedded/service/omnibus-ctl/lib/gitlab_ctl' - ) do - require_relative('../../files/gitlab-ctl-commands/lib/gitlab_ctl') - end - end - - let(:ctl_dir) { "#{File.dirname(__FILE__)}/../../files/gitlab-ctl-commands" } - - it 'should be able to load all files' do - # ARGV contains the commands that were passed to rspec, which are - # invalid for the omnibus-ctl commands - oldargv = ARGV - ARGV = [] # rubocop:disable Style/MutableConstant - expect do - ctl = Omnibus::Ctl.new('testing-ctl') - ctl.load_files(@ctl_dir) - end.not_to raise_error - ARGV = oldargv - end -end diff --git a/spec/gitlab-ctl-commands/lib/gitlab_ctl_backup_spec.rb b/spec/gitlab-ctl-commands/lib/gitlab_ctl_backup_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzL2xpYi9naXRsYWJfY3RsX2JhY2t1cF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands/lib/gitlab_ctl_backup_spec.rb +++ /dev/null @@ -1,318 +0,0 @@ -require 'chef_helper' - -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') - -require 'gitlab_ctl' - -RSpec.describe GitlabCtl::Backup do - let(:backup_dir_path) { '/etc/gitlab/config_backup' } - - # Valid backup files match the regular expression - let(:future_valid_backup_files) do - ['gitlab_config_2302886428_2042_12_22.tar'] - end - - let(:past_valid_backup_files) do - [ - 'gitlab_config_1603388428_2020_10_22.tar', - 'gitlab_config_1606070428_2020_11_22.tar', - 'gitlab_config_1608662428_2020_12_22.tar', - ] - end - - let(:valid_backup_files) do - future_valid_backup_files + past_valid_backup_files - end - - let(:invalid_backup_files) do - [ - 'lab_config_1600793789_2020_09_22.tar', - 'gitlab_config_manual.tar', - 'my_cool_backup.tar' - ] - end - - let(:all_backup_files) do - valid_backup_files + invalid_backup_files - end - - let(:warning_message) { "WARNING: In GitLab 14.0 we will begin removing all configuration backups older than" } - - before do - allow(GitlabCtl::Backup).to receive(:print_warning?).and_return(false) - allow(File).to receive(:exist?).and_return(true) - allow(FileUtils).to receive(:chmod) - allow(FileUtils).to receive(:chown) - allow(FileUtils).to receive(:mkdir) - allow_any_instance_of(Kernel).to receive(:system).and_return(true) - allow_any_instance_of(Kernel).to receive(:exit!) - allow(Dir).to receive(:chdir).and_yield - allow(FileUtils).to receive(:rm) - allow(Time).to receive(:now).and_return(Time.utc(2021)) - # Don't let messages output during test - allow(STDOUT).to receive(:write) - end - - context 'with default settings' do - let(:options) { { delete_old_backups: nil } } - - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({}) - end - - it 'should default to deleting old backups' do - backup = GitlabCtl::Backup.new - expect(backup.wants_pruned).to eq(true) - end - - context 'when the backup path is readable by non-root' do - before do - allow_any_instance_of(GitlabCtl::Backup).to receive(:secure?).and_return(false) - end - - it 'should warn the administrator' do - expect { GitlabCtl::Backup.perform }.to output(/WARNING: #{backup_dir_path} may be read by non-root users/).to_stderr - end - end - - context 'when the backup path is readable by only root' do - let(:archive_name) { 'gitlab_config_8675309_1981_11_16.tar' } - - before do - allow_any_instance_of(GitlabCtl::Backup).to receive(:secure?).and_return(true) - allow_any_instance_of(GitlabCtl::Backup).to receive(:archive_name).and_return(archive_name) - end - - it 'should use proper tar command' do - expect_any_instance_of(Kernel).to receive(:system).with( - *%W(tar --absolute-names --dereference --verbose --create --file /etc/gitlab/config_backup/#{archive_name} - --exclude /etc/gitlab/config_backup -- /etc/gitlab) - ) - GitlabCtl::Backup.perform - end - - it 'should set proper file mode on archive file' do - expect(FileUtils).to receive(:chmod).with(0600, %r{#{backup_dir_path}/#{archive_name}}) - GitlabCtl::Backup.perform - end - - it 'should notify about archive creation starting' do - expect { GitlabCtl::Backup.perform }.to output( - %r{Creating configuration backup archive: #{archive_name}} - ).to_stdout - end - - it 'should put notify about archive creation completion' do - expect { GitlabCtl::Backup.perform }.to output( - %r{Configuration backup archive complete: #{backup_dir_path}/#{archive_name}} - ).to_stdout - end - - it 'should be able to accept a backup directory path argument' do - custom_dir = "/TanukisOfUnusualSize" - options = { backup_path: custom_dir } - expect { GitlabCtl::Backup.perform(options) }.to output( - %r{Configuration backup archive complete: #{custom_dir}/#{archive_name}} - ).to_stdout - end - - context 'when etc backup path does not exist' do - before do - allow(File).to receive(:exist?).with(backup_dir_path).and_return(false) - end - - it 'should log proper message' do - expect { GitlabCtl::Backup.perform }.to output( - %r{Could not find '#{backup_dir_path}' directory\. Creating\.}).to_stdout - end - - it 'should create directory' do - expect(FileUtils).to receive(:mkdir).with(backup_dir_path, mode: 0700) - GitlabCtl::Backup.perform - end - - it 'should set proper owner and group' do - expect(FileUtils).to receive(:chown).with('root', 'root', backup_dir_path) - GitlabCtl::Backup.perform - end - - context 'when /etc/gitlab is NFS share' do - before do - allow(STDERR).to receive(:write) - allow(FileUtils).to receive(:chown).with('root', 'root', backup_dir_path).and_raise(Errno::EPERM) - end - - it 'should put proper output to STDERR' do - expect { GitlabCtl::Backup.perform }.to output( - /Warning: Could not change owner of #{backup_dir_path} to 'root:root'. As a result your backups may be accessible to some non-root users./).to_stderr - end - end - end - end - - context 'when etc path does not exist' do - let(:etc_path) { '/etc/gitlab' } - - before do - allow(File).to receive(:exist?).with(etc_path).and_return(false) - end - - it "should abort with proper message" do - expect { GitlabCtl::Backup.perform }.to output(/Could not find '#{etc_path}' directory. Is your package installed correctly?/).to_stderr.and raise_error - end - end - end - - context 'when backup_keep_time is non-zero' do - let(:options) { { delete_old_backups: true } } - let(:node_config) do - { - 'gitlab' => { - 'gitlab-rails' => { - 'backup_keep_time' => 1 - } - } - } - end - - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return(node_config) - allow(GitlabCtl::Backup).to receive(:secure?).and_return(true) - @backup = GitlabCtl::Backup.new(options) - end - - context 'when no valid files exist' do - before do - allow(Dir).to receive(:glob).and_return(invalid_backup_files) - allow_any_instance_of(Kernel).to receive(:warn) - end - - it 'should find no files to remove' do - expect(@backup.removable_archives.length).to equal(0) - expect(FileUtils).not_to have_received(:rm) - expect { @backup.prune }.to output(/done. Removed 0 older configuration backups./).to_stdout - end - end - - context 'when files exist to remove' do - before do - allow(Dir).to receive(:glob).and_return(all_backup_files) - end - - it 'should identify the correct files to remove' do - removed = past_valid_backup_files.map do |x| - File.join(backup_dir_path, x) - end - - expect(@backup.removable_archives).to match_array(removed) - @backup.remove_backups - removed.each do |f| - expect(FileUtils).to have_received(:rm).with(f) - end - end - - context 'when file removal fails' do - let(:failed_file) { File.join(backup_dir_path, past_valid_backup_files[1]) } - let(:message) { "Permission denied @ unlink_internal - #{failed_file}" } - let(:removed_files) do - files = past_valid_backup_files.map do |f| - File.join(backup_dir_path, f) - end - - files.select! { |f| f != failed_file } - end - - before do - allow(FileUtils).to receive(:rm).with(failed_file).and_raise(Errno::EACCES, message) - end - - it 'removes the remaining expected files' do - allow_any_instance_of(Kernel).to receive(:warn) - @backup.remove_backups - removed_files.each do |filename| - expect(FileUtils).to have_received(:rm).with(filename) - end - end - - it 'sets the proper file removal count' do - allow_any_instance_of(Kernel).to receive(:warn) - expect { @backup.remove_backups }.to output(/done. Removed #{removed_files.length} older configuration backups./).to_stdout - end - - it 'prints the error from file that could not be removed' do - expect { @backup.remove_backups }.to output(a_string_matching(message)).to_stderr - end - end - end - - context 'when the only valid files are after backup_keep_time' do - before do - allow(Dir).to receive(:glob).and_return(future_valid_backup_files) - end - - it 'should find no files to remove' do - allow_any_instance_of(Kernel).to receive(:warn) - expect(@backup.removable_archives.length).to equal(0) - expect(FileUtils).not_to have_received(:rm) - expect { @backup.prune }.to output(/done. Removed 0 older configuration backups./).to_stdout - end - end - end - - context 'when backup_keep_time is zero' do - let(:options) { { delete_old_backups: nil } } - let(:node_config) do - { - 'gitlab' => { - 'gitlab-rails' => { - 'backup_keep_time' => 0 - } - } - } - end - - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return(node_config) - allow(GitlabCtl::Backup).to receive(:secure?).and_return(true) - allow(Dir).to receive(:glob).and_return(all_backup_files) - @backup = GitlabCtl::Backup.new(options) - end - - it 'should skip performing a backup' do - expect { @backup.prune }.to output(/Keeping all older configuration backups/).to_stdout - expect(FileUtils).not_to have_received(:rm) - end - end - - context 'when node attributes invocation fails' do - let(:options) { { delete_old_backups: nil } } - - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_raise(GitlabCtl::Errors::NodeError, "Oh no, node failed") - allow_any_instance_of(Kernel).to receive(:warn) - allow(Dir).to receive(:glob).and_return(all_backup_files) - end - - it 'should not remove old archives' do - backup = GitlabCtl::Backup.new(options) - expect { backup.prune }.to output(/Keeping all older configuration backups/).to_stdout - expect(FileUtils).not_to have_received(:rm) - end - end - - context 'when node attributes returns an empty hash' do - let(:options) { { delete_old_backups: nil } } - - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({}) - allow_any_instance_of(Kernel).to receive(:warn) - allow(Dir).to receive(:glob).and_return(all_backup_files) - end - - it 'should not remove old archives' do - backup = GitlabCtl::Backup.new(options) - expect { backup.prune }.to output(/Keeping all older configuration backups/).to_stdout - expect(FileUtils).not_to have_received(:rm) - end - end -end diff --git a/spec/gitlab-ctl-commands/lib/gitlab_ctl_pg_upgrade_spec.rb b/spec/gitlab-ctl-commands/lib/gitlab_ctl_pg_upgrade_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzL2xpYi9naXRsYWJfY3RsX3BnX3VwZ3JhZGVfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands/lib/gitlab_ctl_pg_upgrade_spec.rb +++ /dev/null @@ -1,145 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') - -require 'gitlab_ctl' - -def stub_postgresql_json_attributes(config = nil) - options = config || {} - options['port'] ||= '5432' - node_data = { postgresql: options.transform_keys(&:to_sym) } - public_data = { 'postgresql' => options } - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return(node_data) - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return(public_data) -end - -RSpec.describe GitlabCtl::PgUpgrade do - before do - @fake_default_dir = '/fake/data/postgresql/data' - allow(GitlabCtl::Util).to receive(:get_command_output).with( - "/fakebasedir/embedded/bin/pg_ctl --version" - ).and_return('fakeoldverision') - end - - context 'with a default configuration' do - before do - stub_postgresql_json_attributes - @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) - end - - it 'should create a new object' do - expect(@dbw).to be_instance_of(GitlabCtl::PgUpgrade) - end - - it 'should allow for a custom base directory' do - expect(@dbw.base_path).to eq('/fakebasedir') - end - - it 'should use the specified timeout' do - expect(@dbw.timeout).to eq(123) - end - - it 'should set tmp_data_dir to data_dir if tmp_dir is nil on initialization' do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({}) - - expect(@dbw.tmp_data_dir).to eq(@dbw.data_dir) - end - - it 'should return the appropriate data version' do - stub_postgresql_json_attributes - allow(File).to receive(:read).with( - File.join(@fake_default_dir, 'PG_VERSION') - ).and_return("99.99\n") - - expect(@dbw.fetch_data_version).to eq('99.99') - end - - context 'when determining if there is enough free space to perform an upgrade' do - before do - stub_postgresql_json_attributes - end - - it 'detects when there is not enough available disk space for upgrade' do - allow(GitlabCtl::Util).to receive(:get_command_output).with( - "du -s --block-size=1m #{@dbw.data_dir}", nil, 123 - ).and_return("200000\n#{@dbw.data_dir}") - - allow(GitlabCtl::Util).to receive(:get_command_output).with( - "df -P --block-size=1m #{@dbw.data_dir} | awk '{print $4}'", nil, 123 - ).and_return("Available\n220000") - - expect(@dbw.enough_free_space?(@dbw.data_dir)).to eq(false) - end - - it 'detects when there is enough available disk space for upgrade' do - allow(GitlabCtl::Util).to receive(:get_command_output).with( - "du -s --block-size=1m #{@dbw.data_dir}", nil, 123 - ).and_return("200000\n#{@dbw.data_dir}") - - allow(GitlabCtl::Util).to receive(:get_command_output).with( - "df -P --block-size=1m #{@dbw.data_dir} | awk '{print $4}'", nil, 123 - ).and_return("Available\n250000") - - expect(@dbw.enough_free_space?(@dbw.data_dir)).to eq(true) - end - end - - context 'when a failed upgrade attempt happened' do - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return({}) - end - - let(:target_data_dir) { "future_data_dir" } - describe '#upgrade_artifact_exists?' do - it 'returns false when the directory does not exist' do - expect(@dbw.upgrade_artifact_exists?(target_data_dir)).to be false - end - - it 'returns false when the directory exists with no data' do - allow(File).to receive(:exist?).with(target_data_dir).and_return(true) - allow(Dir).to receive(:empty?).with(target_data_dir).and_return(true) - expect(@dbw.upgrade_artifact_exists?(target_data_dir)).to be false - end - - it 'returns true when the directory exists with data' do - allow(File).to receive(:exist?).with(target_data_dir).and_return(true) - allow(Dir).to receive(:empty?).with(target_data_dir).and_return(false) - expect(@dbw.upgrade_artifact_exists?(target_data_dir)).to be true - end - end - end - end - - it 'should use the configured port when running pg_upgrade' do - stub_postgresql_json_attributes({ 'port' => '1959' }) - @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) - expect(@dbw.port).to eq('1959') - end - - it 'should call pg_command with the appropriate command' do - stub_postgresql_json_attributes({ 'username' => 'arbitrary-user-name' }) - @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) - expect(GitlabCtl::Util).to receive( - :get_command_output - ).with('su - arbitrary-user-name -c "fake command"', nil, 123) - @dbw.run_pg_command('fake command') - end - - context 'when an explicit data directory is specified' do - it 'should use it as data_dir' do - stub_postgresql_json_attributes({ 'dir' => 'randomdir' }) - @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) - - expect(@dbw.data_dir).to eq('randomdir/data') - end - end - - context 'when an explicit data directory is not specified' do - it 'should use find data_dir using dir/data' do - stub_postgresql_json_attributes({ 'dir' => 'parentdir' }) - @dbw = GitlabCtl::PgUpgrade.new('/fakebasedir', '/fake/data', 'fakenewversion', nil, 123) - - expect(@dbw.data_dir).to eq('parentdir/data') - end - end -end diff --git a/spec/gitlab-ctl-commands/lib/postgresql/replication_spec.rb b/spec/gitlab-ctl-commands/lib/postgresql/replication_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzL2xpYi9wb3N0Z3Jlc3FsL3JlcGxpY2F0aW9uX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands/lib/postgresql/replication_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'spec_helper' -$LOAD_PATH << File.join(__dir__, '../../../../files/gitlab-ctl-commands/lib') - -require 'postgresql/replication' - -RSpec.describe PostgreSQL::Replication do - let(:ctl) { spy('gitlab ctl') } - let(:util) { spy('gitlab ctl util', error?: false) } - let(:attributes) { spy('node attributes spy') } - - subject { described_class.new(ctl) } - - before do - stub_const('GitlabCtl::Util', util) - - allow(util).to receive(:get_node_attributes) - .and_return(attributes) - end - - it 'asks for the password' do - expect(subject).to receive(:ask_password).and_return('pass').once - - subject.set_password! - end - - it 'sets a password' do - allow(subject).to receive(:ask_password).and_return('mypass') - - subject.set_password! - - expect(util).to have_received(:run_command) - .with(%r{WITH ENCRYPTED PASSWORD 'mypass'}) - end - - context 'when postgresql is enabled and patroni disabled' do - before do - allow(ctl).to receive(:service_enabled?).with('postgresql').and_return(true) - allow(ctl).to receive(:service_enabled?).with('patroni').and_return(false) - end - - it 'sets a password for user defined in `sql_replication_user` attribute' do - allow(subject).to receive(:ask_password).and_return('mypass') - allow(attributes).to receive(:to_s).and_return('myuser') - - subject.set_password! - - expect(util).to have_received(:run_command) - .with(%r{ALTER USER myuser WITH ENCRYPTED PASSWORD 'mypass'}) - end - end - - it 'raises an error if replication user is not configured' do - allow(subject).to receive(:ask_password).and_return('mypass') - allow(attributes).to receive(:dig).and_return(nil) - - expect { subject.set_password! }.to raise_error(ArgumentError) - end - - context 'when postgresql is disabled and patroni enabled' do - before do - allow(ctl).to receive(:service_enabled?).with('postgresql').and_return(false) - allow(ctl).to receive(:service_enabled?).with('patroni').and_return(true) - end - - it 'sets a password for user defined in `sql_replication_user` attribute' do - allow(subject).to receive(:ask_password).and_return('mypass') - allow(attributes).to receive(:to_s).and_return('myuser') - - subject.set_password! - - expect(util).to have_received(:run_command) - .with(%r{ALTER USER myuser WITH ENCRYPTED PASSWORD 'mypass'}) - end - end - - context 'when postgresql and patroni services are disabled' do - around do |example| - example.run - rescue SystemExit - end - - before do - allow(ctl).to receive(:service_enabled?).with('postgresql').and_return(false) - allow(ctl).to receive(:service_enabled?).with('patroni').and_return(false) - end - - it 'prints an error message' do - expect { subject.set_password! }.to output( - /There is no PostgreSQL instance enabled in Omnibus/).to_stdout - end - - it 'exits with 1' do - expect { subject.set_password! }.to raise_error(SystemExit) do |error| - expect(error.status).to eq(1) - end - end - end -end diff --git a/spec/gitlab-ctl-commands/lib/postgresql_spec.rb b/spec/gitlab-ctl-commands/lib/postgresql_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzL2xpYi9wb3N0Z3Jlc3FsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands/lib/postgresql_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') - -require 'postgresql' - -RSpec.describe GitlabCtl::PostgreSQL do - describe "#postgresql_username" do - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return( - { - 'postgresql' => { - 'username' => 'bar' - } - } - ) - end - it 'detects username correctly' do - expect(described_class.postgresql_username).to eq('bar') - end - end - - describe '#postgresql_group' do - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return( - { - 'postgresql' => { - 'group' => 'foo' - } - } - ) - end - - it 'returns the correct group' do - expect(described_class.postgresql_group).to eq('foo') - end - end - - describe '#postgresql_version' do - before do - allow(GitlabCtl::Util).to receive(:get_node_attributes).and_return( - { - 'postgresql' => { - 'dir' => '/var/opt/gitlab' - } - } - ) - end - - context 'when PG_VERSION file exists' do - before do - allow(File).to receive(:exist?).and_return(true) - allow(File).to receive(:read).and_return('12\n') - end - - it 'returns the version' do - expect(described_class.postgresql_version).to eq(12) - end - end - - context 'when PG_VERSION file exists' do - before do - allow(File).to receive(:exist?).and_return(false) - end - - it 'returns nil' do - expect(described_class.postgresql_version).to be_nil - end - end - end -end diff --git a/spec/gitlab-ctl-commands/lib/praefect_spec.rb b/spec/gitlab-ctl-commands/lib/praefect_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzL2xpYi9wcmFlZmVjdF9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands/lib/praefect_spec.rb +++ /dev/null @@ -1,207 +0,0 @@ -require 'spec_helper' -require 'optparse' - -require_relative('../../../files/gitlab-ctl-commands/lib/praefect') - -RSpec.describe Praefect do - describe '.parse_options!' do - before do - allow(Kernel).to receive(:exit) { |code| raise "Kernel.exit(#{code})" } - end - - shared_examples 'unknown option is specified' do - it 'throws an error' do - expect { Praefect.parse_options!(%W(praefect #{command} --unknown)) }.to raise_error(OptionParser::InvalidOption, /unknown/) - end - end - - it 'throws an error when command is not specified' do - expect { Praefect.parse_options!(%w(praefect)) }.to raise_error(OptionParser::ParseError, /Praefect command is not specified/) - end - - it 'throws an error when unknown command is specified' do - expect { Praefect.parse_options!(%w(praefect unknown-command)) }.to raise_error(OptionParser::ParseError, /Unknown Praefect command: unknown-command/) - end - - shared_examples 'parses repository options' do - it 'throws an error when an argument for --virtual-storage-name is not specified' do - expect { Praefect.parse_options!(%W(praefect #{command} --dir dir --virtual-storage-name)) }.to raise_error(OptionParser::MissingArgument, /virtual-storage-name/) - end - - it 'throws an error when --virtual-storage-name is not specified' do - expect { Praefect.parse_options!(%W(praefect #{command} --dir dir)) }.to raise_error(OptionParser::ParseError, /Option --virtual-storage-name must be specified/) - end - - it 'throws an error when an argument for --repository-relative-path is not specified' do - expect { Praefect.parse_options!(%W(praefect #{command} --dir dir --repository-relative-path)) }.to raise_error(OptionParser::MissingArgument, /repository-relative-path/) - end - - it 'throws an error when --repository-relative-path is not specified' do - expect { Praefect.parse_options!(%W(praefect #{command} --dir dir --virtual-storage-name name)) }.to raise_error(OptionParser::ParseError, /Option --repository-relative-path must be specified/) - end - - it 'successfully parses correct params' do - expected_options = { command: command, dir: 'dir', virtual_storage_name: 'name', repository_relative_path: 'path' } - - expect(Praefect.parse_options!(%W(praefect #{command} --dir dir --virtual-storage-name name --repository-relative-path path))).to eq(expected_options) - end - end - - context 'when command is remove-repository' do - let(:command) { 'remove-repository' } - - it_behaves_like 'parses repository options' - it_behaves_like 'unknown option is specified' - - it 'successfully parses apply' do - expect(Praefect.parse_options!(%W(praefect #{command} - --dir dir - --virtual-storage-name name - --repository-relative-path path - --apply))).to include(apply: true) - - expect(Praefect.parse_options!(%W(praefect #{command} - --dir dir - --virtual-storage-name name - --repository-relative-path path - --apply something))).to include(apply: true) - - expect(Praefect.parse_options!(%W(praefect #{command} - --dir dir - --virtual-storage-name name - --repository-relative-path path))).not_to include(:apply) - end - end - - context 'when command is check' do - let(:command) { 'check' } - - it_behaves_like 'unknown option is specified' - end - - context 'when command is track-repository' do - let(:command) { 'track-repository' } - - it_behaves_like 'parses repository options' - it_behaves_like 'unknown option is specified' - - it 'successfully parses authoritative-storage' do - expected_options = { command: command, - dir: 'dir', - virtual_storage_name: 'name', - repository_relative_path: 'path', - authoritative_storage: 'storage-1', - replicate_immediately: true } - - expect(Praefect.parse_options!(%W(praefect #{command} - --dir dir - --virtual-storage-name name - --repository-relative-path path - --authoritative-storage storage-1 - --replicate-immediately))).to eq(expected_options) - end - end - - context 'when help option is passed' do - it 'shows help message and exit for global help option' do - expect(Kernel).to receive(:puts) do |msg| - expect(msg.to_s).to match(/gitlab-ctl praefect command/) - end - - expect { Praefect.parse_options!(%w(praefect -h)) }.to raise_error('Kernel.exit(0)') - end - - ['remove-repository', 'track-repository'].each do |cmd| - it "shows help message and exit for #{cmd} help option" do - expect(Kernel).to receive(:puts) do |msg| - expect(msg.to_s).to match(/gitlab-ctl praefect #{cmd}/) - end - - expect { Praefect.parse_options!(%W(praefect #{cmd} -h)) }.to raise_error('Kernel.exit(0)') - end - end - end - end - - describe '#execute' do - let(:repository_options) { { virtual_storage_name: 'storage-name', repository_relative_path: 'repository-path' } } - - context 'when package is not installed correctly' do - it 'aborts the execution if a path does not exit' do - allow(File).to receive(:exists?).twice.and_return(false) - expect(Kernel).to receive(:abort).and_raise('aborted') - - expect { Praefect.execute({}) }.to raise_error('aborted') - end - end - - shared_examples 'executes the command' do - it 'successfully executes' do - allow(File).to receive(:exist?).and_return(true) - expect(Kernel).not_to receive(:abort) - args = [ - Praefect::EXEC_PATH, '-config', 'dir/config.toml', command, - ] - - args += command_args - - expect(Kernel).to receive(:system).with(*args).and_return('result!') - expect(Kernel).not_to receive(:exit!) - - common_options = { - command: command, - dir: 'dir' - } - - Praefect.execute(common_options.merge(command_options)) - end - end - - context 'check command' do - let(:command) { 'check' } - let(:command_args) { [] } - let(:command_options) { {} } - - it_behaves_like 'executes the command' - end - - context 'remove-repository command' do - let(:command) { 'list-untracked-repositories' } - let(:command_args) { [] } - let(:command_options) { {} } - - it_behaves_like 'executes the command' - end - - context 'repository commands' do - let(:command_args) { ['-virtual-storage', 'storage-name', '-repository', 'repository-path'] } - let(:command_options) { repository_options } - - context 'remove-repository command' do - let(:command) { 'remove-repository' } - - it_behaves_like 'executes the command' - end - - context 'track-repository command' do - let(:command) { 'track-repository' } - let(:command_args) { ['-virtual-storage', 'storage-name', '-repository', 'repository-path', '-authoritative-storage', 'storage-1'] } - let(:command_options) { repository_options.merge(authoritative_storage: 'storage-1') } - - it_behaves_like 'executes the command' - - context 'with replicate-immediately' do - let(:command_args) do - ['-virtual-storage', 'storage-name', - '-repository', 'repository-path', - '-authoritative-storage', 'storage-1', - '-replicate-immediately'] - end - let(:command_options) { repository_options.merge(authoritative_storage: 'storage-1', replicate_immediately: true) } - - it_behaves_like 'executes the command' - end - end - end - end -end diff --git a/spec/gitlab-ctl-commands/lib/upgrade_check_spec.rb b/spec/gitlab-ctl-commands/lib/upgrade_check_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzL2xpYi91cGdyYWRlX2NoZWNrX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands/lib/upgrade_check_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'chef_helper' - -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') - -require 'gitlab_ctl/upgrade_check' - -RSpec.describe GitlabCtl::UpgradeCheck do - let(:latest) { '13.1.3' } - let(:previous_major) { '12.5.10' } - let(:previous_major_latest) { '12.10.13' } - let(:current_major) { '13.0.8' } - let(:old) { '11.5.11' } - - context 'not an upgrade' do - it 'returns true' do - expect(described_class.valid?(nil, latest)).to be true - end - end - - context 'valid upgrade paths' do - it 'returns true for an upgrade from the current major version' do - expect(described_class.valid?(current_major, latest)).to be true - end - end - - context 'invalid upgrade paths' do - it 'returns false for an upgrade from 11.5 to the latest' do - expect(described_class.valid?(old, latest)).to be false - end - - it 'returns false for an upgrade from 12.5 to the latest' do - expect(described_class.valid?(previous_major, latest)).to be false - end - - it 'returns false upgrading from previous_major_latest to the latest' do - expect(described_class.valid?(previous_major_latest, latest)).to be false - end - end -end diff --git a/spec/gitlab-ctl-commands/lib/util_spec.rb b/spec/gitlab-ctl-commands/lib/util_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9naXRsYWItY3RsLWNvbW1hbmRzL2xpYi91dGlsX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/gitlab-ctl-commands/lib/util_spec.rb +++ /dev/null @@ -1,262 +0,0 @@ -require 'spec_helper' - -$LOAD_PATH << File.join(__dir__, '../../../files/gitlab-ctl-commands/lib') - -require 'gitlab_ctl' - -RSpec.describe GitlabCtl::Util do - context 'when there is no TTY available' do - before do - allow(STDIN).to receive(:tty?).and_return(false) - end - - it 'asks for a database password in a non-interactive mode' do - allow(STDIN).to receive(:gets).and_return('pass') - - expect(described_class.get_password).to eq('pass') - end - - it 'strips a new line character from the password' do - allow(STDIN).to receive(:tty?).and_return(false) - allow(STDIN).to receive(:gets).and_return("mypass\n") - - expect(described_class.get_password).to eq('mypass') - end - end - - context 'when there is TTY available' do - before do - allow(STDIN).to receive(:tty?).and_return(true) - end - - it 'asks for confirmation' do - expect(STDIN).to receive(:getpass).twice.and_return("mypass") - - expect(described_class.get_password(do_confirm: true)).to eq('mypass') - end - - it 'skips confirmation' do - expect(STDIN).to receive(:getpass).and_return("mypass") - - expect(described_class.get_password(do_confirm: false)).to eq('mypass') - end - end - - describe '#roles' do - let(:fake_base_path) { '/foo' } - let(:fake_hostname) { 'fakehost.fakedomain' } - let(:fake_node_file) { "#{fake_base_path}/embedded/nodes/#{fake_hostname}.json" } - - before do - hostname = double('hostname') - allow(hostname).to receive(:stdout).and_return(fake_hostname) - allow(GitlabCtl::Util).to receive(:run_command).with('hostname -f').and_return(hostname) - allow(File).to receive(:exist?).with(fake_node_file).and_return(true) - end - - it 'returns an empty list when no roles are defined' do - empty_node = { - normal: { - roles: {} - } - } - allow(File).to receive(:read).with(fake_node_file).and_return(empty_node.to_json) - expect(described_class.roles(fake_base_path)).to eq([]) - end - - it 'returns a list of roles that are defined' do - roled_node = { - normal: { - roles: { - role_one: { - enable: true - }, - role_two: { - enable: false - }, - role_three: { - enable: true - } - } - } - } - allow(File).to receive(:read).with(fake_node_file).and_return(roled_node.to_json) - expect(described_class.roles(fake_base_path)).to eq(%w[role_one role_three]) - end - - it 'when no roles are defined' do - roled_node = {} - allow(File).to receive(:read).with(fake_node_file).and_return(roled_node.to_json) - - expect(described_class.roles(fake_base_path)).to eq([]) - end - - it 'when roles is not a Hash' do - roled_node = { roles: "test" } - allow(File).to receive(:read).with(fake_node_file).and_return(roled_node.to_json) - - expect(described_class.roles(fake_base_path)).to eq([]) - end - end - - describe '#parse_json_file' do - it 'fails on malformed JSON file' do - malformed_json = <<~MSG - { - 'foo': 'bar' - MSG - allow(File).to receive(:read).with('/tmp/foo').and_return(malformed_json) - - expect { GitlabCtl::Util.parse_json_file('/tmp/foo') }.to raise_error(GitlabCtl::Errors::NodeError, "Error reading /tmp/foo, has reconfigure been run yet?") - end - - it 'do not fail on empty json file' do - allow(File).to receive(:read).with('/tmp/foo').and_return('{}') - - expect(GitlabCtl::Util.parse_json_file('/tmp/foo')).to eq({}) - end - - it 'fails on incomplete but valid node attribute file' do - incomplete_node_attributes = <<~MSG - { - "name": "a-random-server" - } - MSG - allow(File).to receive(:read).with('/opt/gitlab/embedded/nodes/12345.json').and_return(incomplete_node_attributes) - - expect { GitlabCtl::Util.parse_json_file('/opt/gitlab/embedded/nodes/12345.json') }.to raise_error(GitlabCtl::Errors::NodeError, "Attributes not found in /opt/gitlab/embedded/nodes/12345.json, has reconfigure been run yet?") - end - end - - describe '#parse_duration' do - it 'should raise error for nil, empty, or malformed inputs' do - expect { GitlabCtl::Util.parse_duration(nil) }.to raise_error ArgumentError, 'invalid value for duration: ``' - expect { GitlabCtl::Util.parse_duration('') }.to raise_error ArgumentError, 'invalid value for duration: ``' - expect { GitlabCtl::Util.parse_duration('foo') }.to raise_error ArgumentError, 'invalid value for duration: `foo`' - expect { GitlabCtl::Util.parse_duration('123foo') }.to raise_error ArgumentError, 'invalid value for duration: `123foo`' - expect { GitlabCtl::Util.parse_duration('foo123') }.to raise_error ArgumentError, 'invalid value for duration: `foo123`' - end - - it 'should parse unformatted inputs into milliseconds' do - expect(GitlabCtl::Util.parse_duration('123')).to eq(123) - expect(GitlabCtl::Util.parse_duration('123.456')).to eq(123) - end - - it 'should recognize and parse different duration units' do - expect(GitlabCtl::Util.parse_duration('123.456ms')).to eq(123) - expect(GitlabCtl::Util.parse_duration('123.456s')).to eq(123.456 * 1000) - expect(GitlabCtl::Util.parse_duration('123.456m')).to eq(123.456 * 1000 * 60) - expect(GitlabCtl::Util.parse_duration('123.456h')).to eq(123.456 * 1000 * 60 * 60) - expect(GitlabCtl::Util.parse_duration('123.456d')).to eq(123.456 * 1000 * 60 * 60 * 24) - end - - it 'should parse mixed unit inputs in any order' do - expect(GitlabCtl::Util.parse_duration('1.1d2.2h3.3m4.4s5.5ms')).to eq( - 1.1 * 1000 * 60 * 60 * 24 + - 2.2 * 1000 * 60 * 60 + - 3.3 * 1000 * 60 + - 4.4 * 1000 + - 5 - ) - expect(GitlabCtl::Util.parse_duration('5.5ms4.4s3.3m2.2h1.1d')).to eq( - 1.1 * 1000 * 60 * 60 * 24 + - 2.2 * 1000 * 60 * 60 + - 3.3 * 1000 * 60 + - 4.4 * 1000 + - 5 - ) - end - - it 'should break and return when input is partially valid' do - expect(GitlabCtl::Util.parse_duration('1h2m3foo')).to eq( - 1 * 1000 * 60 * 60 + - 2 * 1000 * 60 - ) - expect(GitlabCtl::Util.parse_duration('1h2m3')).to eq( - 1 * 1000 * 60 * 60 + - 2 * 1000 * 60 - ) - end - end - - describe '#chef_run' do - it 'should accept and use custom log files' do - allow(described_class).to receive(:run_command).and_return('success') - expect(described_class).to receive(:run_command).with(/-L log_ago_and_far_away/) - described_class.chef_run('config', 'attributes', 'log_ago_and_far_away') - end - - it 'should not pass the log file path unless set' do - allow(described_class).to receive(:run_command).and_return('success') - expect(described_class).not_to receive(:run_command).with(/ -L /) - described_class.chef_run('config', 'attributes') - end - end - - describe '#get_node_attributes' do - context 'when node file is missing' do - before do - allow(File).to receive(:exist?).with(%r{/opt/gitlab/embedded/nodes.*json}).and_return(false) - allow(Dir).to receive(:glob?).with(%r{/opt/gitlab/embedded/}).and_return([]) - end - - it 'raises an error' do - expect { GitlabCtl::Util.get_node_attributes }.to raise_error(GitlabCtl::Errors::NodeError, "Node attributes JSON file not found in /opt/gitlab/embedded/nodes, has reconfigure been run yet?") - end - end - end - - describe "#public_attributes_missing?" do - context 'when the public attributes file is missing' do - before do - allow(File).to receive(:exist?).with(%r{/var/opt/gitlab/public_attributes.json}).and_return(false) - end - - it 'returns true' do - expect(GitlabCtl::Util.public_attributes_missing?).to be true - end - end - end - - describe "#public_attributes_broken?" do - context 'when the public attributes file is broken' do - before do - allow(File).to receive(:exist?).with(%r{/var/opt/gitlab/public_attributes.json}).and_return(true) - end - - it 'returns true when the attribute is missing' do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({}) - expect(GitlabCtl::Util.public_attributes_broken?).to be true - end - - it 'returns false by default when the gitlab attribute is found' do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ "gitlab" => "hithere" }) - expect(GitlabCtl::Util.public_attributes_broken?).to be false - end - - context 'when using an alternate key to check for broken' do - it 'returns true when the key is missing' do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({}) - expect(GitlabCtl::Util.public_attributes_broken?('funky')).to be true - end - - it 'returns false when the key is present' do - allow(GitlabCtl::Util).to receive(:get_public_node_attributes).and_return({ "funky" => "llama" }) - expect(GitlabCtl::Util.public_attributes_broken?('funky')).to be false - end - end - end - end - - describe '#get_public_node_attributes' do - context 'when node file is missing' do - before do - allow(GitlabCtl::Util).to receive(:public_attributes_missing?).and_return(true) - end - - it 'returns empty hash' do - expect(GitlabCtl::Util.get_public_node_attributes).to eq({}) - end - end - end -end diff --git a/spec/libraries/config_mash_spec.rb b/spec/libraries/config_mash_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvY29uZmlnX21hc2hfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/libraries/config_mash_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'spec_helper' -require_relative '../../files/gitlab-cookbooks/package/libraries/config_mash' - -RSpec.describe Gitlab::ConfigMash do - subject(:mash) { described_class.new } - - describe '.auto_vivify?' do - subject { described_class } - - it 'returns true when inside auto_vivify block' do - subject.auto_vivify do - expect(subject.auto_vivify?).to be_truthy - end - end - - it 'returns false when not in auto_vivify block' do - expect(subject.auto_vivify?).to be_falsey - end - end - - describe '#[]' do - it 'behaves like a normal Mash by default' do - mash['param1'] = 'value1' - expect(mash['param1']).to eq 'value1' - expect(mash['param2']).to be_nil - expect { mash['param2']['nested'] }.to raise_error(NoMethodError, /nil:NilClass/) - end - - it 'allows nested undefined reads when in an auto-vivify block' do - mash['param1'] = 'value1' - expect(mash['param1']).to eq 'value1' - expect(mash['param2']).to be_nil - - Gitlab::ConfigMash.auto_vivify do - expect(mash['param2']).not_to be_nil - expect { mash['param2']['nested'] }.not_to raise_error - mash['param3']['enable'] = true - expect(mash['param3']['enable']).to eq true - end - - # confirm it is turned off after the block - expect(mash['param4']).to be_nil - end - end - - describe 'convert_value' do - it 'returns passed value when its a ConfigMash' do - another_mash = Gitlab::ConfigMash.new - - converted = mash.convert_value(another_mash) - - expect(converted).to be_a(Gitlab::ConfigMash) - expect(converted).to eq(another_mash) - end - - it 'returns a ConfigMash with the same data when passed a Hash' do - hash = { 'key' => 'value' } - - converted = mash.convert_value(hash) - - expect(converted).to be_a(Gitlab::ConfigMash) - expect(converted.keys).to match_array('key') - expect(converted.values).to match_array('value') - end - end -end diff --git a/spec/libraries/deprecations_spec.rb b/spec/libraries/deprecations_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvZGVwcmVjYXRpb25zX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/deprecations_spec.rb +++ /dev/null @@ -1,222 +0,0 @@ -require 'spec_helper' -require_relative '../../files/gitlab-cookbooks/package/libraries/deprecations' - -RSpec.describe Gitlab::Deprecations do - let(:valid_config) { { gitlab: { nginx: { listen_addresses: "SomeRandomString" } } } } - let(:invalid_config) do - { - "gitlab" => { - "nginx" => { - "listen_address" => "SomeRandomString" - }, - "gitlab-rails" => { - "stuck_ci_builds_worker_cron" => "5 * * * *" - } - }, - "mattermost" => { - "system_read_timeout" => 50, - "log_file_directory" => "/my/random/path" - }, - "monitoring" => { - "gitlab-monitor" => { - "enable" => false - } - } - } - end - - let(:conf1) do - { - config_keys: %w(gitlab nginx listen_address), - deprecation: '8.10', - removal: '11.0', - note: "Use nginx['listen_addresses'] instead." - } - end - - let(:conf2) do - { - config_keys: %w(gitlab gitlab-rails stuck_ci_builds_worker_cron), - deprecation: '9.0', - removal: '12.0', - note: "Use gitlab_rails['stuck_ci_jobs_worker_cron'] instead." - } - end - - let(:conf3) do - { - config_keys: %w(gitlab gitlab-shell git_data_directories), - deprecation: '8.10', - removal: '11.0', - note: "Use git_data_dirs instead." - } - end - - let(:conf4) do - { - config_keys: %w(monitoring gitlab-monitor enable), - deprecation: '12.0', - removal: '13.0', - note: "Use gitlab_exporter['enable'] instead." - } - end - - let(:deprecation_list) do - [ - conf1, - conf2, - conf3, - conf4, - ] - end - - before(:each) do - allow(Gitlab::Deprecations).to receive(:list).and_return(deprecation_list) - end - - describe '.applicable_deprecations' do - it 'detects valid configuration' do - expect(described_class.applicable_deprecations("11.0", valid_config, :deprecation)).to eq([]) - end - - it 'distinguishes from deprecated and removed configuration' do - expect(described_class.applicable_deprecations("11.0", invalid_config, :deprecation)).to include(conf1) - expect(described_class.applicable_deprecations("11.0", invalid_config, :deprecation)).to include(conf2) - expect(described_class.applicable_deprecations("12.0", invalid_config, :deprecation)).to include(conf1) - expect(described_class.applicable_deprecations("12.0", invalid_config, :deprecation)).to include(conf2) - - expect(described_class.applicable_deprecations("11.0", invalid_config, :removal)).not_to include(conf2) - expect(described_class.applicable_deprecations("12.0", invalid_config, :removal)).to include(conf2) - end - - it 'also detects deprecated falsey values' do - expect(described_class.applicable_deprecations("12.0", invalid_config, :deprecation)).to include(conf4) - end - end - - describe '.check_config' do - it 'detects valid_config configuration' do - expect(described_class.check_config("11.0", valid_config)).to eq([]) - end - - it 'detects deprecated configuration for specified version and ignores not yet deprecated ones' do - message_1 = "* nginx['listen_address'] has been deprecated since 8.10 and was removed in 11.0. Use nginx['listen_addresses'] instead." - message_2 = "* gitlab_rails['stuck_ci_builds_worker_cron'] has been deprecated since 9.0 and was removed in 12.0. Use gitlab_rails['stuck_ci_jobs_worker_cron'] instead." - message_3 = "* gitlab_monitor['enable'] has been deprecated since 12.0 and will be removed in 13.0. Use gitlab_exporter['enable'] instead." - - expect(described_class.check_config("11.0", invalid_config)).to include(message_1) - expect(described_class.check_config("11.0", invalid_config)).not_to include(message_2) - expect(described_class.check_config("12.0", invalid_config)).to include(message_2) - expect(described_class.check_config("12.0", invalid_config, :deprecation)).to include(message_3) - end - end - - describe '.identify_deprecated_config' do - it 'detects deprecations correctly from list of supported keys' do - mattermost_supported_keys = %w(log_file_directory) - output = [ - { - config_keys: %w(mattermost system_read_timeout), - deprecation: '10.2', - removal: '11.0', - note: nil - } - ] - expect(described_class.identify_deprecated_config(invalid_config, ["mattermost"], mattermost_supported_keys, "10.2", "11.0")).to eq(output) - end - end - - describe '.deprecate_only_if_value' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'during removals' do - context 'when deprecated config is not set' do - it 'does not raise warning' do - expect(described_class.deprecate_only_if_value('14.0', chef_run.node.normal, :removal, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq([]) - end - end - - context 'when deprecated config is set to an acceptable value' do - before do - stub_gitlab_rb( - puma: { enable: false } - ) - end - it 'does not raise warning' do - expect(described_class.deprecate_only_if_value('14.0', chef_run.node.normal, :removal, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq([]) - end - end - - context 'when deprecated config is set to an unacceptable value' do - before do - stub_gitlab_rb( - puma: { enable: true } - ) - end - it 'raises warning' do - expect(described_class.deprecate_only_if_value('14.0', chef_run.node.normal, :removal, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq(["* puma[enable] has been deprecated since 13.10 and was removed in 14.0."]) - end - end - end - - context 'during deprecations' do - context 'when deprecated config is not set' do - it 'does not raise warning' do - expect(described_class.deprecate_only_if_value('13.12', chef_run.node.normal, :deprecation, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq([]) - end - end - - context 'when deprecated config is set to an acceptable value' do - before do - stub_gitlab_rb( - puma: { enable: false } - ) - end - it 'raises warning' do - expect(described_class.deprecate_only_if_value('13.12', chef_run.node.normal, :deprecation, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq(["* puma[enable] has been deprecated since 13.10 and will be removed in 14.0."]) - end - end - - context 'when deprecated config is set to an unacceptable value' do - before do - stub_gitlab_rb( - puma: { enable: true } - ) - end - it 'raises warning' do - expect(described_class.deprecate_only_if_value('13.12', chef_run.node.normal, :deprecation, ['gitlab', 'puma'], 'enable', true, '13.10', '14.0')).to eq(["* puma[enable] has been deprecated since 13.10 and will be removed in 14.0."]) - end - end - end - end - - describe 'NodeAttribute' do - before do - Gitlab::Deprecations::NodeAttribute.log_deprecations = true - end - - after do - Gitlab::Deprecations::NodeAttribute.log_deprecations = false - end - - it 'Logs deprecations for passed variables and proxies to new object' do - config = { 'monitoring' => { 'test' => 'test-value' } } - config['prometheus'] = Gitlab::Deprecations::NodeAttribute.new(config['monitoring'], "config['prometheus']", "config['monitoring']") - - expect(config['prometheus']['test']).to eq('test-value') - expect_logged_deprecation(/Accessing config\['prometheus'\] is deprecated/) - end - - it 'Logs deprecations for passed variables and proxies to new Proc if provided' do - config = { 'monitoring' => { 'test' => 'test-value' } } - config['prometheus'] = Gitlab::Deprecations::NodeAttribute.new(proc { config['monitoring'] }, "config['prometheus']", "config['monitoring']") - - expect(config['prometheus']['test']).to eq('test-value') - expect_logged_deprecation(/Accessing config\['prometheus'\] is deprecated/) - end - end -end diff --git a/spec/libraries/gitaly_spec.rb b/spec/libraries/gitaly_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvZ2l0YWx5X3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/gitaly_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'chef_helper' - -RSpec.describe Gitaly do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - before { allow(Gitlab).to receive(:[]).and_call_original } - - describe 'by default' do - it 'provides settings needed for gitaly to run' do - expect(chef_run.node['gitaly']['env']).to include( - 'HOME' => '/var/opt/gitlab', - 'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin' - ) - end - - it 'does not include known settings in the environment' do - expect(chef_run.node['gitaly']['env']).not_to include('GITALY_ENABLE') - end - end -end diff --git a/spec/libraries/gitlab_spec.rb b/spec/libraries/gitlab_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvZ2l0bGFiX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/gitlab_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'spec_helper' -require_relative '../../files/gitlab-cookbooks/package/libraries/config/gitlab' - -RSpec.describe Gitlab do - context 'when using an attribute_block' do - it 'sets top level attributes to the provided root' do - Gitlab.attribute_block('gitlab') do - expect(Gitlab.attribute('test_attribute')[:parent]).to eq 'gitlab' - end - expect(Gitlab['test_attribute']).not_to be_nil - expect(Gitlab.hyphenate_config_keys['gitlab']).to include('test-attribute') - end - end - - it 'sets top level attributes when no parent is provided' do - Gitlab.attribute('test_attribute') - expect(Gitlab['test_attribute']).not_to be_nil - expect(Gitlab.hyphenate_config_keys).to include('test-attribute') - end - - it 'properly defines roles' do - role = Gitlab.role('test_node') - expect(Gitlab['test_node_role']).not_to be_nil - expect(Gitlab.hyphenate_config_keys['roles']).to include('test-node') - expect(role).to include(manage_services: true) - end - - it 'supports overriding role default configuration' do - role = Gitlab.role('test_node', manage_services: false) - expect(Gitlab['test_node_role']).not_to be_nil - expect(role).to include(manage_services: false) - end - - it 'supports overriding attribute default configuration' do - attribute = Gitlab.attribute('test_attribute', parent: 'example', priority: 40, enable: false, default: '') - expect(Gitlab['test_attribute']).to eq('') - expect(attribute).to include(parent: 'example', priority: 40, enable: false) - end - - it 'disables ee attributes when EE is not enabled' do - allow(Gitlab).to receive(:[]).and_call_original - allow(Gitlab).to receive(:[]).with('edition').and_return(:ce) - expect(Gitlab.ee_attribute('test_attribute')[:ee]).to eq true - expect(Gitlab['test_attribute']).not_to be_nil - expect(Gitlab.hyphenate_config_keys).not_to include('test-attribute') - end - - it 'enables ee attributes when EE is enabled' do - allow(Gitlab).to receive(:[]).and_call_original - allow(Gitlab).to receive(:[]).with('edition').and_return(:ee) - expect(Gitlab.ee_attribute('test_attribute')[:ee]).to eq true - expect(Gitlab['test_attribute']).not_to be_nil - expect(Gitlab.hyphenate_config_keys).to include('test-attribute') - end - - it 'sorts attributes by sequence' do - Gitlab.attribute('last', priority: 99) - Gitlab.attribute('other1') - Gitlab.attribute('first', priority: -99) - Gitlab.attribute('other2') - - expect(Gitlab.send(:sorted_settings).first[0]).to eq 'first' - expect(Gitlab.send(:sorted_settings).last[0]).to eq 'last' - end - - it 'filters ee settings when sorting' do - Gitlab.attribute('test_attribute1') - Gitlab.attribute('test_attribute2', ee: true) - allow(Gitlab).to receive(:[]).and_call_original - allow(Gitlab).to receive(:[]).with('edition').and_return(:ce) - expect(Gitlab.send(:sorted_settings).map(&:first)).to include('test_attribute1') - expect(Gitlab.send(:sorted_settings).map(&:first)).not_to include('test_attribute2') - end - - it 'allows passing a block to the attribute use method' do - attribute = Gitlab.attribute('test_attribute').use { 'test' } - expect(attribute.handler).to eq('test') - end -end diff --git a/spec/libraries/helper_spec.rb b/spec/libraries/helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/helper_spec.rb +++ /dev/null @@ -1,254 +0,0 @@ -require 'chef_helper' - -RSpec.shared_examples 'Postgres helpers' do |service_name, service_cmd, edition| - let(:chef_run) do - ChefSpec::SoloRunner.new do |node| - node.normal['gitlab'][service_name]['dir'] = '/fakedir' - node.normal['package']['install-dir'] = '/fake/install/dir' - end.converge("#{edition}::config") - end - - let(:node) { chef_run.node } - - let!(:helper) do - described_class.new(node) - end - - before do - allow(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/psql --version') { 'YYYYYYYY XXXXXXX' } - end - - it 'is associated with a valid service' do - # this is a validation to make sure we are passing a valid/existing service_name to the shared example - expect(node['gitlab'][service_name].to_h).not_to be_empty - end - - describe '#version' do - it 'returns a valid version' do - expect(helper.version).to eq('XXXXXXX') - end - end - - describe '#database_version' do - it 'returns a valid database_version' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with('/fakedir/data/PG_VERSION') { true } - allow(File).to receive(:read).and_call_original - allow(File).to receive(:read).with('/fakedir/data/PG_VERSION') { '111.222' } - allow(Dir).to receive(:glob).with('/fake/install/dir/embedded/postgresql/*') { %w(111.222.18 222.333.11) } - - # We mock this in chef_helper.rb. Override the mock to call the original - allow_any_instance_of(described_class).to receive(:database_version).and_call_original - expect(helper.database_version).to eq('111.222') - end - end - - describe '#extension_exists?' do - it 'returns whether an extension exists' do - expect(helper).to receive(:success?).with("/opt/gitlab/bin/#{service_cmd} -d 'template1' -c 'select name from pg_available_extensions' -A | grep -x myextension") - helper.extension_exists?('myextension') - end - end - - describe '#extension_enabled?' do - it 'returns whether an extension exists' do - expect(helper).to receive(:success?).with("/opt/gitlab/bin/#{service_cmd} -d 'mydatabase' -c 'select extname from pg_extension' -A | grep -x myextension") - helper.extension_enabled?('myextension', 'mydatabase') - end - end - - describe '#database_exists?' do - it 'returns whether a database exists' do - expect(helper).to receive(:success?).with("/opt/gitlab/bin/#{service_cmd} -d 'template1' -c 'select datname from pg_database' -A | grep -x mydatabase") - helper.database_exists?('mydatabase') - end - end -end - -RSpec.describe PgHelper do - include_examples 'Postgres helpers', 'postgresql', 'gitlab-psql', 'gitlab' -end - -RSpec.describe GeoPgHelper do - before do - allow(Gitlab).to receive(:[]).and_call_original - stub_gitlab_rb( - 'geo_postgresql' => { - 'dir' => '/fakedir' - } - ) - end - - include_examples 'Postgres helpers', 'geo-postgresql', 'gitlab-geo-psql', 'gitlab-ee' -end - -RSpec.describe OmnibusHelper do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - let(:node) { chef_run.node } - let(:services) do - %w( - puma - sidekiq - gitlab-workhorse - postgresql - redis - nginx - logrotate - prometheus - alertmanager - grafana - node-exporter - redis-exporter - postgres-exporter - gitlab-exporter - gitlab-pages - gitaly - ).freeze - end - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(File).to receive(:symlink?).with(any_args).and_call_original - end - - context 'service is currently enabled, bootstrapped and is running' do - before do - services.each do |service| - stub_should_notify?(service, true) - end - stub_gitlab_rb(nginx: { enable: true }) - end - - it 'notifies the service' do - expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'disabling a service that was bootstrapped and is currently running' do - before do - services.each do |service| - stub_should_notify?(service, true) - end - stub_gitlab_rb(nginx: { enable: false }) - end - - it 'does not notify the service' do - expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'enabling a service that was bootstrapped but not currently running' do - before do - services.each do |service| - allow(File).to receive(:symlink?).with("/opt/gitlab/service/#{service}").and_return(true) - allow_any_instance_of(OmnibusHelper).to receive(:success?).with("/opt/gitlab/init/#{service} status").and_return(false) - end - stub_gitlab_rb(nginx: { enable: true }) - end - - it 'does not notify the service' do - expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'disabling a service that was bootstrapped but not currently running' do - before do - services.each do |service| - allow(File).to receive(:symlink?).with("/opt/gitlab/service/#{service}").and_return(true) - allow_any_instance_of(OmnibusHelper).to receive(:success?).with("/opt/gitlab/init/#{service} status").and_return(false) - end - stub_gitlab_rb(nginx: { enable: false }) - end - - it 'does not notify the service' do - expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'enabling a service that was disabled but currently running' do - before do - services.each do |service| - allow(File).to receive(:symlink?).with("/opt/gitlab/service/#{service}").and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:success?).with("/opt/gitlab/init/#{service} status").and_return(true) - end - stub_gitlab_rb(nginx: { enable: true }) - end - - it 'does not notify the service' do - expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'disabling a service that was disabled but currently running' do - before do - services.each do |service| - allow(File).to receive(:symlink?).with("/opt/gitlab/service/#{service}").and_return(false) - allow_any_instance_of(OmnibusHelper).to receive(:success?).with("/opt/gitlab/init/#{service} status").and_return(true) - end - stub_gitlab_rb(nginx: { enable: false }) - end - - it 'does not notify the service' do - expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'enabling a service that was disabled and not currently running' do - before do - services.each do |service| - stub_should_notify?(service, false) - end - stub_gitlab_rb(nginx: { enable: true }) - end - - it 'does not notify the service' do - expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'disabling a service that was disabled and not currently running' do - before do - services.each do |service| - stub_should_notify?(service, false) - end - stub_gitlab_rb(nginx: { enable: false }) - end - - it 'does not notify the service' do - expect(chef_run.template('/var/opt/gitlab/nginx/conf/gitlab-http.conf')).not_to notify('runit_service[nginx]').to(:restart).delayed - end - end - - context 'expected_owner?' do - let(:oh) { OmnibusHelper.new(chef_run.node) } - before do - allow_any_instance_of(OmnibusHelper).to receive(:expected_owner?) - .and_call_original - allow_any_instance_of(OmnibusHelper).to receive( - :expected_user?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive( - :expected_user? - ).with('/tmp/fakefile', 'fakeuser').and_return(true) - allow_any_instance_of(OmnibusHelper).to receive( - :expected_group?).and_return(false) - allow_any_instance_of(OmnibusHelper).to receive( - :expected_group? - ).with('/tmp/fakefile', 'fakegroup').and_return(true) - end - - it 'should return false if the group is wrong' do - expect(oh.expected_owner?('/tmp/fakefile', 'fakeuser', 'wronggroup')) - .to be false - end - - it 'should return false if the user is wrong' do - expect(oh.expected_owner?('/tmp/fakefile', 'wronguser', 'fakegroup')) - .to be false - end - - it 'should return true if user and group is correct' do - expect(oh.expected_owner?('/tmp/fakefile', 'fakeuser', 'fakegroup')) - .to be true - end - end -end diff --git a/spec/libraries/helpers/base_pg_helper_spec.rb b/spec/libraries/helpers/base_pg_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvaGVscGVycy9iYXNlX3BnX2hlbHBlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/libraries/helpers/base_pg_helper_spec.rb +++ /dev/null @@ -1,336 +0,0 @@ -require 'chef_helper' - -RSpec.describe BasePgHelper do - cached(:chef_run) { converge_config } - let(:node) { chef_run.node } - subject { described_class.new(node) } - - before do - allow(subject).to receive(:service_name) { 'postgresql' } - allow(subject).to receive(:service_cmd) { 'gitlab-psql' } - end - - context 'when handling ssl configuration' do - after do - chef_run.node.rm_normal('postgresql', 'dir') - chef_run.node.rm_normal('postgresql', 'ssl_cert_file') - end - - describe 'ssl_cert_file' do - it 'is configured with an absolute path' do - absolute_path = "/my/absolute/path" - config_path = "MrBoots" - chef_run.node.normal['postgresql']['ssl_cert_file'] = absolute_path - chef_run.node.normal['postgresql']['dir'] = config_path - expect(subject.ssl_cert_file).to eq(absolute_path) - end - - it 'is configured with a relative path' do - relative_path = "my/relative/path" - config_path = "/MrBoots" - chef_run.node.normal['postgresql']['ssl_cert_file'] = relative_path - chef_run.node.normal['postgresql']['dir'] = config_path - expect(subject.ssl_cert_file).to eq(File.join(config_path, 'data', relative_path)) - end - end - - describe 'ssl_key_file' do - it 'is configured with an absolute path' do - absolute_path = "/my/absolute/path" - config_path = "MrBoots" - chef_run.node.normal['postgresql']['ssl_key_file'] = absolute_path - chef_run.node.normal['postgresql']['dir'] = config_path - expect(subject.ssl_key_file).to eq(absolute_path) - end - - it 'is configured with a relative path' do - relative_path = "my/relative/path" - config_path = "/MrBoots" - chef_run.node.normal['postgresql']['ssl_key_file'] = relative_path - chef_run.node.normal['postgresql']['dir'] = config_path - expect(subject.ssl_key_file).to eq(File.join(config_path, 'data', relative_path)) - end - end - end - - describe '#database_exists?' do - it 'calls out to psql_cmd' do - expect(subject).to receive(:psql_cmd).with( - [ - "-d 'template1'", - "-c 'select datname from pg_database' -A", - '| grep -x example' - ]) - subject.database_exists?('example') - end - - context 'database present' do - it 'truthy' do - allow(subject).to receive(:psql_cmd) { true } - expect(subject.database_exists?('example')).to be_truthy - end - - it 'falsey' do - allow(subject).to receive(:psql_cmd) { false } - expect(subject.database_exists?('example')).to be_falsey - end - end - end - - describe '#extension_enabled?' do - it 'will check with psql_cmd when database present' do - expect(subject).to receive(:psql_cmd).with( - [ - "-d 'database'", - "-c 'select extname from pg_extension' -A", - '| grep -x extension' - ]) - subject.extension_enabled?('extension', 'database') - end - - context 'extension present' do - it 'truthy' do - allow(subject).to receive(:psql_cmd) { true } - expect(subject.extension_enabled?('extension', 'database')).to be_truthy - end - - it 'falsey' do - allow(subject).to receive(:psql_cmd) { false } - expect(subject.extension_enabled?('extension', 'database')).to be_falsey - end - end - end - - describe '#extension_can_be_enabled?' do - before do - allow(subject).to receive(:is_running?).and_return(true) - allow(subject).to receive(:is_standby?).and_return(false) - allow(subject).to receive(:extension_exists?).and_return(true) - allow(subject).to receive(:database_exists?).and_return(true) - allow(subject).to receive(:extension_enabled?).and_return(false) - end - - it 'can be enabled' do - expect(subject.extension_can_be_enabled?('extension', 'db')).to be_truthy - end - - it 'needs to be running' do - allow(subject).to receive(:is_running?).and_return(false) - expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey - end - - it 'cannot be done on a slave' do - allow(subject).to receive(:is_standby?).and_return(true) - expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey - end - - it 'needs to have the extension available' do - allow(subject).to receive(:extension_exists?).and_return(false) - expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey - end - - it 'needs the database to load the extension into' do - allow(subject).to receive(:database_exists?).and_return(false) - expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey - end - - it 'should not enable twice' do - allow(subject).to receive(:extension_enabled?).and_return(true) - expect(subject.extension_can_be_enabled?('extension', 'db')).to be_falsey - end - end - - describe '#user_options' do - before do - result = spy('shellout') - allow(result).to receive(:stdout).and_return("f|f|t|f\n") - allow(subject).to receive(:do_shell_out).and_return(result) - end - - it 'returns hash from query' do - expect(subject.user_options('')).to eq( - { - 'SUPERUSER' => false, - 'CREATEDB' => false, - 'REPLICATION' => true, - 'BYPASSRLS' => false - } - ) - end - end - - describe '#user_options_set?' do - let(:default_options) do - { - 'SUPERUSER' => false, - 'CREATEDB' => false, - 'REPLICATION' => true, - 'BYPASSRLS' => false - } - end - - context 'default user options' do - before do - allow(subject).to receive(:user_options).and_return(default_options) - end - - it 'returns true when no options are asked about' do - expect(subject.user_options_set?('', [])).to be_truthy - end - - it 'returns true when options are set to their defaults' do - expect(subject.user_options_set?('', ['NOSUPERUSER'])).to be_truthy - end - - it 'returns false when options are set away from their defaults' do - expect(subject.user_options_set?('', ['SUPERUSER'])).to be_falsey - end - end - - context 'modified user' do - before do - allow(subject).to receive(:user_options).and_return(default_options.merge({ 'SUPERUSER' => true })) - end - - it 'returns false when options is not what we expect' do - expect(subject.user_options_set?('', ['NOSUPERUSER'])).to be_falsey - end - end - end - - describe '#user_password_match?' do - before do - # user: gitlab pass: test123 - allow(subject).to receive(:user_hashed_password) { 'md5b56573ef0d94cff111898c63ec259f3f' } - end - - it 'returns true when same password is in plain-text' do - expect(subject.user_password_match?('gitlab', 'test123')).to be_truthy - end - - it 'returns true when same password is in MD5 format' do - expect(subject.user_password_match?('gitlab', 'md5b56573ef0d94cff111898c63ec259f3f')).to be_truthy - end - - it 'returns false when wrong password is in plain-text' do - expect(subject.user_password_match?('gitlab', 'wrong')).to be_falsey - end - - it 'returns false when wrong password is in MD5 format' do - expect(subject.user_password_match?('gitlab', 'md5b599de4332636c03a60fca13be1edb5f')).to be_falsey - end - - it 'returns false when password is not supplied' do - expect(subject.user_password_match?('gitlab', nil)).to be_falsey - end - - context 'nil password' do - before do - # user: gitlab pass: unset - allow(subject).to receive(:user_hashed_password) { '' } - end - - it 'returns true when the password is nil' do - expect(subject.user_password_match?('gitlab', nil)).to be_truthy - end - end - end - - describe '#parse_pghash' do - let(:payload) { '{host=127.0.0.1,dbname=gitlabhq_production,port=5432,user=gitlab,"password=foo}bar\"zoo\\cat"}' } - - it 'returns a hash' do - expect(subject.parse_pghash(payload)).to be_a(Hash) - end - - it 'when content is empty still return a hash' do - expect(subject.parse_pghash('')).to be_a(Hash) - expect(subject.parse_pghash('{}')).to be_a(Hash) - end - - it 'returns hash with expected keys' do - hash = subject.parse_pghash(payload) - - expect(hash.keys).to contain_exactly(:host, :dbname, :port, :user, :password) - end - - it 'returns hash with expected values' do - hash = subject.parse_pghash(payload) - - expect(hash.values).to contain_exactly('127.0.0.1', 'gitlabhq_production', '5432', 'gitlab', 'foo}bar"zoo\cat') - end - end - - describe '#is_running?' do - it 'returns true when postgres is running' do - stub_service_success_status('postgresql', true) - - expect(subject.is_running?).to be_truthy - end - - it 'returns false when postgres is not running' do - stub_service_success_status('postgresql', false) - - expect(subject.is_running?).to be_falsey - end - end - - describe '#is_managed_and_offline?' do - it 'returns true when conditions are met' do - chef_run.node.normal['postgresql']['enable'] = true - stub_service_failure_status('postgresql', true) - - expect(subject.is_managed_and_offline?).to be_truthy - end - - it 'returns false when conditions are not met' do - chef_run.node.normal['postgresql']['enable'] = true - stub_service_failure_status('postgresql', false) - - expect(subject.is_managed_and_offline?).to be_falsey - - chef_run.node.normal['postgresql']['enable'] = false - stub_service_failure_status('postgresql', false) - - expect(subject.is_managed_and_offline?).to be_falsey - - stub_service_failure_status('postgresql', true) - expect(subject.is_managed_and_offline?).to be_falsey - end - end - - describe '#is_standby?' do - let(:recovery_files) { %w(recovery.conf recovery.signal standby.signal) } - - it 'returns true for a standby instance' do - recovery_files.each do |f| - allow(File).to receive(:exist?) - .with("/var/opt/gitlab/postgresql/data/#{f}").and_return(true) - end - - expect(subject.is_standby?).to be true - end - - it 'returns false for a primary instance' do - recovery_files.each do |f| - allow(File).to receive(:exist?) - .with("/var/opt/gitlab/postgresql/data/#{f}").and_return(false) - end - expect(subject.is_standby?).to be false - end - end - - context 'when handling connection information' do - describe '#connection_info' do - it 'raises a not implemented warning' do - expect { subject.connection_info }.to raise_error(NotImplementedError) - end - end - - describe '#build_connection_info' do - it 'returns a ConnectionInfo object' do - expect(subject.build_connection_info('dbname', 'dbhost', 'port', 'pguser')).to be_a(described_class::ConnectionInfo) - end - end - end -end diff --git a/spec/libraries/helpers/gitlab_cluster_spec.rb b/spec/libraries/helpers/gitlab_cluster_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvaGVscGVycy9naXRsYWJfY2x1c3Rlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/libraries/helpers/gitlab_cluster_spec.rb +++ /dev/null @@ -1,164 +0,0 @@ -require 'spec_helper' -require_relative '../../../files/gitlab-cookbooks/package/libraries/gitlab_cluster' - -RSpec.describe GitlabCluster, :cluster_config do - let(:gitlab_cluster_config_path) { described_class::JSON_FILE } - subject { described_class.config } - - describe '#all' do - context 'when the cluster configuration file does not exist' do - it 'returns an empty hash' do - expect(subject.all).to be_empty - end - end - - context 'when the cluster configuration file exists' do - it 'parses the file content' do - stub_file_content(gitlab_cluster_config_path, foo: 'bar') - - expect(subject.all).to eq('foo' => 'bar') - end - end - end - - describe '#set' do - it 'set the value of a single config option' do - subject.set('primary', true) - - expect(subject.all).to eq('primary' => true) - end - - it 'sets the value of a nested config option' do - subject.set('patroni', 'standby_cluster', 'enable', true) - - expect(subject.all).to eq('patroni' => { 'standby_cluster' => { 'enable' => true } }) - end - - it 'overrides the key value if it already set' do - stub_file_content(gitlab_cluster_config_path, patroni: { standby_cluster: { enable: false } }) - - subject.set('patroni', 'standby_cluster', 'enable', true) - - expect(subject.all).to eq('patroni' => { 'standby_cluster' => { 'enable' => true } }) - end - end - - describe '#get' do - before do - stub_file_content(gitlab_cluster_config_path, primary: false, patroni: { standby_cluster: { enable: true } }) - end - - it 'get the value of a single config option if the key exists' do - expect(subject.get('primary')).to eq(false) - end - - it 'get the value of a nested config option if the key exists' do - expect(subject.get('patroni', 'standby_cluster', 'enable')).to eq(true) - end - - it 'returns nil if the key does not exist' do - expect(subject.get('foo', 'bar')).to be_nil - end - end - - describe '#load_roles!' do - before do - stub_gitlab_rb(application_role: { enable: true }, geo_primary_role: { enable: nil }, geo_secondary_role: { enable: true }) - end - - it 'overrides roles defined in the configuration file' do - stub_file_content(gitlab_cluster_config_path, secondary: false) - - subject.load_roles! - - expect(Gitlab['application_role']['enable']).to eq(true) - expect(Gitlab['geo_secondary_role']['enable']).to eq(false) - end - - it 'does not override roles not defined in the configuration file' do - stub_file_content(gitlab_cluster_config_path, {}) - - subject.load_roles! - - expect(Gitlab['application_role']['enable']).to eq(true) - expect(Gitlab['geo_secondary_role']['enable']).to eq(true) - end - - it 'prints a warning message for each enabled role defined in the configuration file' do - stub_file_content(gitlab_cluster_config_path, primary: true, secondary: false) - - expect(LoggingHelper) - .to receive(:warning) - .with("The 'geo_primary_role' is defined in #{gitlab_cluster_config_path} as 'true' and overrides the setting in the /etc/gitlab/gitlab.rb") - - expect(LoggingHelper) - .to receive(:warning) - .with("The 'geo_secondary_role' is defined in #{gitlab_cluster_config_path} as 'false' and overrides the setting in the /etc/gitlab/gitlab.rb") - .once - - subject.load_roles! - end - end - - describe '#write_to_file!' do - let(:config_path) { Dir.mktmpdir } - let(:gitlab_cluster_config_path) { File.join(config_path, 'gitlab-cluster.json') } - - before do - stub_const('GitlabCluster::CONFIG_PATH', config_path) - stub_const('GitlabCluster::JSON_FILE', gitlab_cluster_config_path) - end - - after do - FileUtils.rm_rf(config_path) - end - - context 'when the config directory does not exist' do - it 'does not create the configuration file' do - FileUtils.rm_rf(config_path) - - subject.save - - expect(File.exist?(gitlab_cluster_config_path)).to eq(false) - end - end - - context 'when the cluster configuration file does not exist' do - it 'creates the configuration file' do - FileUtils.rm_rf(gitlab_cluster_config_path) - - subject.save - - expect(File.exist?(gitlab_cluster_config_path)).to eq(true) - expect(read_file_content(gitlab_cluster_config_path)).to be_empty - end - end - - context 'when the cluster configuration file exists' do - it 'overrides previous settings' do - write_file_content(gitlab_cluster_config_path, foo: 'bar', zoo: true) - subject.set('zoo', false) - - subject.save - - expect(read_file_content(gitlab_cluster_config_path)).to eq("foo" => "bar", "zoo" => false) - end - end - end - - def stub_file_content(fullpath, content) - allow(File).to receive(:exist?).with(fullpath).and_return(true) - allow(IO).to receive(:read).with(fullpath).and_return(content.to_json) - end - - def read_file_content(fullpath) - JSON.parse(File.read(fullpath)) - end - - def write_file_content(fullpath, content) - File.open(fullpath, 'w') do |f| - f.write(content.to_json) - f.chmod(0600) - end - end -end diff --git a/spec/libraries/helpers/gitlab_workhorse_helper_spec.rb b/spec/libraries/helpers/gitlab_workhorse_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvaGVscGVycy9naXRsYWJfd29ya2hvcnNlX2hlbHBlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/libraries/helpers/gitlab_workhorse_helper_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'chef_helper' - -RSpec.describe GitlabWorkhorseHelper do - let(:node) { chef_run.node } - subject { described_class.new(node) } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'workhorse is listening on a tcp socket' do - cached(:chef_run) { converge_config } - let(:tcp_address) { '1.9.8.4' } - - before do - stub_gitlab_rb( - gitlab_workhorse: { - listen_network: 'http', - listen_addr: tcp_address - } - ) - end - - describe '#unix_socket?' do - it 'returns false' do - expect(subject.unix_socket?).to be false - end - end - end - - context 'workhorse is listening on a unix socket' do - cached(:chef_run) { converge_config } - before do - stub_gitlab_rb( - gitlab_workhorse: { - listen_network: 'unix' - } - ) - end - - describe '#unix_socket?' do - it 'returns true' do - expect(subject.unix_socket?).to be true - end - end - end -end diff --git a/spec/libraries/helpers/pg_status_helper_spec.rb b/spec/libraries/helpers/pg_status_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvaGVscGVycy9wZ19zdGF0dXNfaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/helpers/pg_status_helper_spec.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'chef_helper' - -RSpec.describe PgStatusHelper do - cached(:chef_run) { converge_config } - let!(:node) { chef_run.node } - let!(:helper) { PgHelper.new(node) } - let(:connection_info) { helper.build_connection_info('dbname', 'dbhost', 'port', 'pguser') } - - context 'when checking status' do - subject { described_class.new(connection_info, node) } - - describe '#service_checks_exhausted?' do - it 'returns true when there are zero remaining service checks' do - allow(subject).to receive(:remaining_service_checks).and_return(0) - expect(subject.service_checks_exhausted?).to be true - end - - it 'returns false when more service checks are allowed' do - allow(subject).to receive(:remaining_service_checks).and_return(1) - expect(subject.service_checks_exhausted?).to be false - end - end - - describe '#ready?' do - it 'raises no warnings when Postgres is accepting connections' do - allow(subject).to receive(:accepting_connections?).and_return(true) - expect(subject.ready?).to be true - end - - it 'raises a warning when Postgres is not responding' do - allow(subject).to receive(:not_responding?).and_return(true) - allow(subject).to receive(:remaining_service_checks).and_return(0) - expect { subject.ready? }.to raise_error(RuntimeError, 'PostgreSQL did not respond before service checks were exhausted') - end - - it 'raises a warning when Postgres gets invalid connection parameters' do - allow(subject).to receive(:invalid_connection_parameters?).and_return(true) - expect { subject.ready? }.to raise_error(RuntimeError, 'PostgreSQL is not receiving the correct connection parameters') - end - - it 'raises a warning when all service checks are exhausted' do - allow(subject).to receive(:service_checks_exhausted?).and_return(true) - expect { subject.ready? }.to raise_error(RuntimeError, 'Exhausted service checks and database is still not available') - end - end - - context 'when invoking pg_isready' do - using RSpec::Parameterized::TableSyntax - - where(:exit_code, :accepting, :rejecting, :not_responding, :invalid_parameters) do - 0 | true | false | false | false - 1 | false | true | false | false - 2 | false | false | true | false - 3 | false | false | false | true - end - - with_them do - before do - result = spy('shellout') - allow(subject).to receive(:do_shell_out).and_return(result) - allow(result).to receive(:exitstatus).and_return(exit_code) - end - - describe '#service_state' do - it 'returns the expected exit code' do - expect(subject.service_state).to eq(exit_code) - end - end - - describe '#accepting_connections?' do - it 'returns the expected boolean' do - expect(subject.accepting_connections?).to be accepting - end - end - - describe '#rejecting_connections?' do - it 'returns the expected boolean' do - expect(subject.rejecting_connections?).to be rejecting - end - end - - describe '#not_responding?' do - it 'returns the expected boolean' do - expect(subject.not_responding?).to be not_responding - end - end - - describe '#invalid_connection_parameters?' do - it 'returns the expected boolean' do - expect(subject.invalid_connection_parameters?).to be invalid_parameters - end - end - end - end - end - - context 'when all checks use configured defaults' do - subject { described_class.new(connection_info, node) } - - describe '#maximum_service_checks' do - it 'will check 20 times' do - expect(subject.maximum_service_checks).to eq(20) - end - end - - describe '#service_check_interval' do - it 'will check every 5 seconds' do - expect(subject.service_check_interval).to eq(5) - end - end - end - - context 'when checks are are customized' do - let(:the_answer) { 424242 } - before do - chef_run.node.normal['postgresql']['max_service_checks'] = the_answer - chef_run.node.normal['postgresql']['service_check_interval'] = the_answer - @subject = described_class.new(connection_info, node) - end - - describe '#maximum_service_checks' do - it 'will check the configured number of times' do - expect(@subject.maximum_service_checks).to eq(the_answer) - end - end - - describe '#service_check_interval' do - it 'will check at a configured interval' do - expect(@subject.service_check_interval).to eq(the_answer) - end - end - end -end diff --git a/spec/libraries/lets_encrypt_spec.rb b/spec/libraries/lets_encrypt_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvbGV0c19lbmNyeXB0X3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/lets_encrypt_spec.rb +++ /dev/null @@ -1,179 +0,0 @@ -require 'chef_helper' - -RSpec.describe LetsEncrypt do - subject { ::LetsEncrypt } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context '.parse_variables' do - it 'calls to parse_enable' do - expect(subject).to receive(:parse_enable) - - subject.parse_variables - end - end - - context '.parse_enable' do - context 'when specifying letsencrypt enabled' do - context 'true' do - before { stub_gitlab_rb(letsencrypt: { enable: true }) } - - it 'should not call should_auto_enable?' do - expect(subject).not_to receive(:should_auto_enable?) - - subject.parse_enable - end - end - - context 'false' do - before { stub_gitlab_rb(letsencrypt: { enable: false }) } - - it 'should not call should_auto_enable?' do - expect(subject).not_to receive(:should_auto_enable?) - - subject.parse_enable - end - end - - context 'unspecified' do - it 'should use the value of should_auto_enable' do - allow(subject).to receive(:should_auto_enable?).and_return('bananas') - subject.parse_enable - - expect(Gitlab['letsencrypt']['enable']).to eq('bananas') - end - end - end - end - - context '.should_auto_enable?' do - let(:node) { Mash.new(gitlab: { nginx: {} }) } - - before do - stub_gitlab_rb( - gitlab_rails: { - gitlab_https: true - }, - nginx: { - ssl_certificate_key: 'example.key', - ssl_certificate: 'example.crt' - } - ) - - allow(Gitlab).to receive(:[]).with(:node).and_return(node) - allow(File).to receive(:exist?).with('example.key').and_return(false) - allow(File).to receive(:exist?).with('example.crt').and_return(false) - end - - it 'is true' do - expect(subject.should_auto_enable?).to be_truthy - end - - it 'is false when not using a https url' do - stub_gitlab_rb(gitlab_rails: { gitlab_https: false }) - - expect(subject.should_auto_enable?).to be_falsey - end - - it 'is false when nginx is not enabled' do - stub_gitlab_rb(nginx: { enable: false }) - - expect(subject.should_auto_enable?).to be_falsey - end - - it 'is false when nginx is disabled by roles' do - allow(node['gitlab']['nginx']).to receive(:[]).with('enable').and_return(false) - - expect(subject.should_auto_enable?).to be_falsey - end - - it 'is false with explicit nginx.listen_https = false' do - stub_gitlab_rb(nginx: { listen_https: false }) - - expect(subject.should_auto_enable?).to be_falsey - end - - it 'is false with the key present' do - allow(File).to receive(:exist?).with('example.key').and_return(true) - - expect(subject.should_auto_enable?).to be_falsey - end - - it 'is false with the cert present' do - mock_cert = OpenSSL::X509::Certificate.new - allow(mock_cert).to receive(:not_after).and_return(Time.now + 600) - allow(File).to receive(:exist?).with('example.crt').and_return(true) - allow(File).to receive(:read).with('example.crt').and_return(nil) - allow(OpenSSL::X509::Certificate).to receive(:new).and_return(mock_cert) - - expect(subject.should_auto_enable?).to be_falsey - end - - it 'is true when files present, but we provisioned them before' do - stub_gitlab_rb(letsencrypt: { auto_enabled: true }) - - allow(File).to receive(:exist?).with('example.key').and_return(true) - allow(File).to receive(:exist?).with('example.crt').and_return(true) - - expect(subject.should_auto_enable?).to be_truthy - end - - it 'is true when files present, but LE certificate is expired' do - mock_cert = OpenSSL::X509::Certificate.new - allow(mock_cert).to receive(:not_after).and_return(Time.now - 1) - allow(mock_cert).to receive(:issuer).and_return( - OpenSSL::X509::Name.parse(%(/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3))) - allow(File).to receive(:exist?).with('example.key').and_return(true) - allow(File).to receive(:exist?).with('example.crt').and_return(true) - allow(File).to receive(:read).with('example.crt').and_return(nil) - allow(OpenSSL::X509::Certificate).to receive(:new).and_return(mock_cert) - - expect(subject.should_auto_enable?).to be_truthy - end - - it 'is false when files present, but non-LE certificate is expired' do - mock_cert = OpenSSL::X509::Certificate.new - allow(mock_cert).to receive(:not_after).and_return(Time.now - 1) - allow(mock_cert).to receive(:issuer).and_return( - OpenSSL::X509::Name.parse('/C=US/O=Example Corporation/CN=Example')) - allow(File).to receive(:exist?).with('example.key').and_return(true) - allow(File).to receive(:exist?).with('example.crt').and_return(true) - allow(File).to receive(:read).with('example.crt').and_return(nil) - allow(OpenSSL::X509::Certificate).to receive(:new).and_return(mock_cert) - - expect(subject.should_auto_enable?).to be_falsey - end - end - - context '.save_auto_enabled' do - it 'does nothing if not auto_enabled' do - expect(SecretsHelper).not_to receive(:load_gitlab_secrets) - - subject.save_auto_enabled - end - - context 'auto_enabled' do - before do - stub_gitlab_rb(letsencrypt: { auto_enabled: true }) - allow(SecretsHelper).to receive(:load_gitlab_secrets).and_return({}) - allow(SecretsHelper).to receive(:write_to_gitlab_secrets) - end - - it 'writes when secret is absent' do - expect(SecretsHelper).to receive(:write_to_gitlab_secrets) - - subject.save_auto_enabled - end - - it 'does not write if secret is already true' do - allow(SecretsHelper).to receive(:load_gitlab_secrets) - .and_return('letsencrypt' => { 'auto_enabled' => true }) - expect(SecretsHelper).not_to receive(:write_to_gitlab_secrets) - - subject.save_auto_enabled - end - end - end -end diff --git a/spec/libraries/logging_helper_spec.rb b/spec/libraries/logging_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvbG9nZ2luZ19oZWxwZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/libraries/logging_helper_spec.rb +++ /dev/null @@ -1,172 +0,0 @@ -require 'chef_helper' - -RSpec.describe LoggingHelper do - let(:log) { spy('Chef::Log') } - - before do - subject.reset - stub_const('Chef::Log', log) - end - - context '.messages' do - it 'starts with no messages' do - expect(subject.messages).to eq([]) - end - end - - context '.log' do - it 'records into .messages' do - subject.log('one') - expect(subject.messages).to contain_exactly(Hash) - end - - it 'records message' do - subject.log('two') - expect(subject.messages).to contain_exactly(hash_including(message: 'two')) - end - - it 'records two messages' do - subject.log('one') - subject.log('two') - expect(subject.messages).to contain_exactly( - hash_including(message: 'one'), - hash_including(message: 'two') - ) - end - - it 'optionally records kind' do - subject.log('three', kind: :fake) - expect(subject.messages).to contain_exactly(hash_including(kind: :fake)) - end - end - - context '.deprecation' do - it 'calls Chef::Log.warn' do - subject.deprecation('hello') - expect(log).to have_received(:warn).with('hello') - end - - it 'adds the kind :deprecation' do - subject.deprecation('basic') - expect(subject.messages).to contain_exactly(hash_including(kind: :deprecation)) - end - end - - context '.warning' do - it 'calls Chef::Log.warn' do - subject.warning('hello') - expect(log).to have_received(:warn).with('hello') - end - - it 'adds the kind :warning' do - subject.warning('basic') - expect(subject.messages).to contain_exactly(hash_including(kind: :warning)) - end - end - - context '.debug' do - it 'calls Chef::Log.debug' do - subject.debug('hello') - expect(log).to have_received(:debug).with('hello') - end - - it 'adds the kind :debug' do - subject.debug('basic') - expect(subject.messages).to contain_exactly(hash_including(kind: :debug)) - end - end - - context '.note' do - it 'adds the kind :note' do - subject.note('basic') - expect(subject.messages).to contain_exactly(hash_including(kind: :note)) - end - end - - context '.report' do - it 'prints nothing if nothing happened' do - expect { subject.report }.not_to output.to_stdout - end - - it 'prints a removal header, then removal' do - subject.removal('one') - expect { subject.report }.to output(/\nRemovals:\none/).to_stdout - end - - it 'prints a removal header, then removals' do - subject.removal('one') - subject.removal('two') - expect { subject.report }.to output(/\nRemovals:\none\ntwo/).to_stdout - end - - it 'prints a deprecation header, then deprecation' do - subject.deprecation('one') - expect { subject.report }.to output(/\nDeprecations:\none/).to_stdout - end - - it 'prints a deprecation header, then deprecations' do - subject.deprecation('one') - subject.deprecation('two') - expect { subject.report }.to output(/\nDeprecations:\none\ntwo/).to_stdout - end - - it 'prints a warning header, then warning' do - subject.warning('one') - expect { subject.report }. to output(/\nWarnings:\none/).to_stdout - end - - it 'prints a warning header, then warnings' do - subject.warning('one') - subject.warning('two') - expect { subject.report }.to output(/\nWarnings:\none\ntwo/).to_stdout - end - - it 'prints a note header, then note' do - subject.note('one') - expect { subject.report }. to output(/\nNotes:\none/).to_stdout - end - - it 'prints a note header, then notes' do - subject.note('one') - subject.note('two') - expect { subject.report }.to output(/\nNotes:\none\ntwo/).to_stdout - end - - it 'prints a removal header, removals, deprecation header, deprecations, warning header, warnings, note header then notes' do - subject.removal('one') - subject.removal('two') - subject.deprecation('three') - subject.deprecation('four') - subject.warning('five') - subject.warning('six') - subject.note('seven') - subject.note('eight') - - output = <<~EOS - - Removals: - one - two - - - Deprecations: - three - four - - - Warnings: - five - six - - - Notes: - seven - eight - - EOS - - expect { subject.report } - .to output(output).to_stdout - end - end -end diff --git a/spec/libraries/output_helper_spec.rb b/spec/libraries/output_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvb3V0cHV0X2hlbHBlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/libraries/output_helper_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'spec_helper' -require_relative '../../files/gitlab-cookbooks/package/libraries/helpers/output_helper' - -RSpec.describe OutputHelper do - include OutputHelper - - describe "#quote" do - context 'handling nil' do - it 'should return nil' do - expect(quote(nil)).to eq(nil) - end - end - - context 'handling numbers' do - it 'should cooerce numbers to strings' do - result = quote(42) - expect(result).to be_instance_of(String) - expect(result).to eq('"42"') - end - end - - context 'roundtripping values via YAML' do - values = [ - "foo", - # single quotes - "foo'", - "'foo", - "fo'o", - "'foo'", - # double quotes - 'foo"', - '"foo', - 'fo"o', - '"foo"', - # newlines - "foo\n", - "\nfoo", - "fo\no", - "\nfoo\n", - # tabs - "foo\t", - "\tfoo", - "fo\to", - "\tfoo\t", - # spaces - "foo ", - " foo", - "fo o", - " foo ", - # unicode snowman - "foo☃", - "☃foo", - "fo☃o", - "☃foo☃", - ] - values.each do |value| - it "should YAML roundtrip #{value.inspect}" do - # create a document with structure { 'value' : quote(value) } - yaml_document = "---\nvalue: #{quote(value)}" - document = YAML.safe_load(yaml_document) - expect(document['value']).to eq(value) - end - end - end - end -end diff --git a/spec/libraries/pg_version_spec.rb b/spec/libraries/pg_version_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvcGdfdmVyc2lvbl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/libraries/pg_version_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'chef_helper' - -RSpec.describe PGVersion do - context '.parse' do - it 'returns a PGVersion class when provided a value' do - expect(PGVersion.parse('string')).to be_a(described_class) - end - - it 'returns nil when not passed a value' do - expect(PGVersion.parse(nil)).to be_nil - end - end - - context 'postgres 9.6' do - it '.valid? validates version strings' do - expect(PGVersion.new('9.6.0').valid?).to be true - expect(PGVersion.new('9.6').valid?).to be true - expect(PGVersion.new('9').valid?).to be false - expect(PGVersion.new('9.6.0.0').valid?).to be false - expect(PGVersion.new('i9').valid?).to be false - expect(PGVersion.new('i9.6').valid?).to be false - expect(PGVersion.new('9.6.0i').valid?).to be false - expect(PGVersion.new('i9.6.0').valid?).to be false - end - - it 'parses the correct version parts from a MAJOR only version' do - version = PGVersion.new('9.6') - - expect(version.major).to eq '9.6' - expect(version.minor).to be_nil - end - - it 'parses the correct major version from a full version' do - version = PGVersion.new('9.6.10') - - expect(version.major).to eq '9.6' - expect(version.minor).to eq '10' - end - end - - context 'postgres 10+' do - it '.valid? validates version strings' do - expect(PGVersion.new('10.0.0').valid?).to be true - expect(PGVersion.new('11.0').valid?).to be true - expect(PGVersion.new('10').valid?).to be true - expect(PGVersion.new('10.0.0.0').valid?).to be false - expect(PGVersion.new('i10').valid?).to be false - expect(PGVersion.new('i11.0').valid?).to be false - expect(PGVersion.new('10.0.0i').valid?).to be false - expect(PGVersion.new('i10.0.0').valid?).to be false - end - - it 'parses the correct version parts from a MAJOR only version' do - version = PGVersion.new('10') - version_new = PGVersion.new('12') - - expect(version.major).to eq '10' - expect(version.minor).to be_nil - expect(version_new.major).to eq '12' - expect(version_new.minor).to be_nil - end - - it 'parses the correct major version from a full version' do - version = PGVersion.new('10.5') - version_new = PGVersion.new('12.122') - - expect(version.major).to eq '10' - expect(version.minor).to eq '5' - expect(version_new.major).to eq '12' - expect(version_new.minor).to eq '122' - end - - it 'ignores patch version' do - version = PGVersion.new('10.5.3') - version_new = PGVersion.new('12.122.3') - - expect(version.major).to eq '10' - expect(version.minor).to eq '5' - expect(version_new.major).to eq '12' - expect(version_new.minor).to eq '122' - end - end -end diff --git a/spec/libraries/redis_helper_spec.rb b/spec/libraries/redis_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvcmVkaXNfaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/redis_helper_spec.rb +++ /dev/null @@ -1,251 +0,0 @@ -require 'chef_helper' - -RSpec.describe RedisHelper do - let(:chef_run) { converge_config } - subject { described_class.new(chef_run.node) } - - context '#redis_params' do - context 'without sentinels' do - before { allow(Gitlab).to receive(:[]).and_call_original } - - it 'returns correct parameters' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_port: 8888, - redis_password: 'mypass' - } - ) - expect(subject.redis_params).to eq(['redis.example.com', 8888, 'mypass']) - expect(subject.redis_params(support_sentinel_groupname: false)).to eq(['redis.example.com', 8888, 'mypass']) - end - end - - context 'with sentinels' do - before { allow(Gitlab).to receive(:[]).and_call_original } - - it 'returns correct parameters' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_port: 8888, - redis_password: 'mypass', - redis_sentinels: [ - { 'host' => 'sentinel1.example.com', 'port' => '12345' }, - { 'host' => 'sentinel2.example.com', 'port' => '12345' } - ] - }, - redis: { - master_name: 'master_from.redis.master_name', - master_password: 'password_from.redis.master_password' - } - ) - expect(subject.redis_params).to eq(['master_from.redis.master_name', 6379, 'password_from.redis.master_password']) - expect(subject.redis_params(support_sentinel_groupname: false)).to eq(['redis.example.com', 8888, 'mypass']) - end - end - end - - context '#redis_url' do - context 'with default configuration' do - it 'returns a unix socket' do - expect(subject.redis_url.to_s).to eq('unix:/var/opt/gitlab/redis/redis.socket') - end - end - - context 'with custom configuration' do - before { allow(Gitlab).to receive(:[]).and_call_original } - - it 'returns a Redis URL when redis_host is defined' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com' - } - ) - - expect(subject.redis_url.to_s).to eq('redis://redis.example.com/') - end - - it 'returns a Redis URL with port when a non default port is defined' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_port: 8888 - } - ) - - expect(subject.redis_url.to_s).to eq('redis://redis.example.com:8888/') - end - - it 'returns a Redis URL with database when specified' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_database: 0 - } - ) - - expect(subject.redis_url.to_s).to eq('redis://redis.example.com/0') - end - - it 'returns a Redis URL with password when specified' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_password: 'mypass' - } - ) - - expect(subject.redis_url.to_s).to eq('redis://:mypass@redis.example.com/') - end - - it 'returns a Redis URL with an encoded password' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_password: '#223' - } - ) - - expect(subject.redis_url.to_s).to eq('redis://:%23223@redis.example.com/') - end - - it 'returns a Redis URL with password, port and database when all specified' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_password: 'mypass', - redis_database: 0, - redis_port: 8888 - } - ) - - expect(subject.redis_url.to_s).to eq('redis://:mypass@redis.example.com:8888/0') - end - - it 'returns an SSL Redis URL with password, port and database when all specified' do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'redis.example.com', - redis_password: 'mypass', - redis_database: 0, - redis_port: 8888, - redis_ssl: true - } - ) - - expect(subject.redis_url.to_s).to eq('rediss://:mypass@redis.example.com:8888/0') - end - end - end - - describe '#running_version' do - let(:redis_cli_output) do - <<~MSG - # Server - redis_version:3.2.12 - redis_git_sha1:00000000 - redis_git_dirty:0 - redis_build_id:e16da30f4a0a7845 - redis_mode:standalone - os:Linux 4.15.0-58-generic x86_64 - MSG - end - - before do - # Un-doing the stub added in chef_helper - allow_any_instance_of(described_class).to receive(:running_version).and_call_original - allow(Gitlab).to receive(:[]).and_call_original - allow(VersionHelper).to receive(:version).with(/redis-cli.*INFO/).and_return(redis_cli_output) - end - - context 'when redis is not running' do - it 'returns nil' do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(false) - - expect(subject.running_version).to be_nil - end - end - - context 'when redis is running' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(true) - end - context 'over socket' do - it 'calls VersionHelper.version with correct arguments' do - expect(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket INFO') - - subject.running_version - end - end - - context 'over TCP' do - before do - stub_gitlab_rb( - redis: { - bind: '0.0.0.0', - port: 6379 - } - ) - end - - it 'calls VersionHelper.version with correct arguments' do - expect(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 6379 INFO') - - subject.running_version - end - - context 'with a Redis password specified' do - before do - stub_gitlab_rb( - redis: { - bind: '0.0.0.0', - port: 6379, - password: 'toomanysecrets' - } - ) - end - - it 'it passes password to the command' do - expect(VersionHelper).to receive(:version).with("/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 6379 -a 'toomanysecrets' INFO") - - subject.running_version - end - end - end - - it 'parses version from redis-cli output properly' do - expect(subject.running_version).to eq('3.2.12') - end - end - end - - describe '#installed_version' do - let(:redis_server_output) { 'Redis server v=3.2.12 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=e16da30f4a0a7845' } - - before do - # Un-doing the stub added in chef_helper - allow_any_instance_of(described_class).to receive(:installed_version).and_call_original - allow(Gitlab).to receive(:[]).and_call_original - allow(VersionHelper).to receive(:version).with(/redis-server --version/).and_return(redis_server_output) - end - - context 'when redis is not running' do - it 'returns nil' do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(false) - - expect(subject.installed_version).to be_nil - end - end - - context 'when redis is running' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(true) - end - - it 'parses redis-server output properly' do - expect(subject.installed_version).to eq('3.2.12') - end - end - end -end diff --git a/spec/libraries/redis_spec.rb b/spec/libraries/redis_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvcmVkaXNfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/libraries/redis_spec.rb +++ /dev/null @@ -1,210 +0,0 @@ -# This spec is to test the Redis helper and whether the values parsed -# are the ones we expect -require 'chef_helper' - -RSpec.describe 'Redis' do - let(:chef_run) { converge_config(is_ee: true) } - let(:node) { chef_run.node } - subject { ::Redis } - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context '.parse_variables' do - it 'delegates to parse_redis_settings' do - expect(subject).to receive(:parse_redis_settings) - - subject.parse_variables - end - end - - context '.parse_redis_settings' do - context 'when no customization is made' do - it 'keeps unixsocket' do - expect(node['gitlab']['gitlab-rails']['unixsocket']).not_to eq false - - subject.parse_redis_settings - end - end - - context 'within redis host and port synchronization with gitlab_rails' do - let(:redis_host) { '1.2.3.4' } - let(:redis_port) { 6370 } - - context 'when not using sentinels' do - before do - stub_gitlab_rb( - redis: { - bind: redis_host, - port: redis_port - } - ) - end - - it 'disables unix socket when redis tcp params are defined' do - expect(node['redis']['unixsocket']).to eq false - - subject.parse_redis_settings - end - - it 'expects redis_host to match bind value from redis' do - expect(node['gitlab']['gitlab-rails']['redis_host']).to eq redis_host - - subject.parse_redis_settings - end - - it 'expects redis_port to match port value from redis' do - expect(node['gitlab']['gitlab-rails']['redis_port']).to eq redis_port - - subject.parse_redis_settings - end - end - - context 'when using sentinels' do - let(:master_name) { 'gitlabredis' } - let(:master_pass) { 'PASSWORD' } - - before do - stub_gitlab_rb( - redis: { - bind: redis_host, - port: redis_port, - master_name: master_name, - master_password: master_pass - }, - gitlab_rails: { - redis_sentinels: [ - { host: '1.2.3.4', port: '26379' } - ] - } - ) - end - - it 'disables unix socket when sentinel params are defined' do - expect(node['redis']['unixsocket']).to eq false - - subject.parse_redis_settings - end - end - - context 'when with redis_replica_role enabled' do - before do - stub_gitlab_rb( - redis_replica_role: { - enable: true - }, - redis: { - master_ip: '10.0.0.0', - master_port: 6379, - master_password: 'PASSWORD' - } - ) - end - - it 'defined redis master as false' do - expect(node['redis']['master']).to eq false - end - end - end - - context 'within redis password and master_password' do - let(:redis_password) { 'PASSWORD' } - - context 'when master_role is enabled' do - before do - stub_gitlab_rb( - redis_master_role: { - enable: true - }, - redis: { - password: redis_password, - master_ip: '10.0.0.0' - } - ) - end - - it 'master_password is autofilled based on redis current password' do - expect(node['redis']['master_password']).to eq redis_password - end - end - - context 'when redis is a replica' do - before do - stub_gitlab_rb( - redis: { - master: false, - password: redis_password, - master_ip: '10.0.0.0' - } - ) - end - - it 'master_password is autofilled based on redis current password' do - expect(node['redis']['master_password']).to eq redis_password - end - end - - context 'when sentinel is enabled' do - before do - stub_gitlab_rb( - sentinel: { - enable: true - }, - redis: { - password: redis_password, - master_ip: '10.0.0.0' - } - ) - end - - it 'master_password is autofilled based on redis current password' do - expect(node['redis']['master_password']).to eq redis_password - end - - context 'announce_ip is defined' do - let(:redis_port) { 6379 } - let(:redis_announce_ip) { '10.10.10.10' } - before do - stub_gitlab_rb( - redis_sentinel_role: { - enable: true, - }, - redis: { - master_password: redis_password, - master_ip: '10.0.0.0', - port: redis_port, - announce_ip: redis_announce_ip - } - ) - end - - it 'Redis announce_port is autofilled based on redis current port' do - expect(node['redis']['announce_port']).to eq redis_port - - subject.parse_redis_settings - end - end - end - - context 'when both password and master_password are present' do - let(:master_password) { 'anotherPASSWORD' } - before do - stub_gitlab_rb( - redis_replica_role: { - enable: true - }, - redis: { - password: redis_password, - master_ip: '10.0.0.0', - master_password: master_password - } - ) - end - - it 'keeps user specified master_password' do - expect(node['redis']['master_password']).to eq master_password - end - end - end - end -end diff --git a/spec/libraries/redis_uri_spec.rb b/spec/libraries/redis_uri_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvcmVkaXNfdXJpX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/redis_uri_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -# This spec is to test the Redis helper and whether the values parsed -# are the ones we expect - -require 'spec_helper' -require_relative '../../files/gitlab-cookbooks/gitlab/libraries/redis_uri.rb' - -RSpec.describe URI::Redis do - subject { URI('redis://localhost') } - - it { is_expected.to be_a(described_class) } - - context '.parse' do - it 'delegates to URI.parse' do - expect(URI).to receive(:parse).with('redis://localhost') - - described_class.parse('redis://localhost') - end - end - - context 'port' do - it 'has a default port' do - expect(subject.default_port).to eq 6379 - end - - it 'outputs default port when none is defined' do - expect(subject.port).to eq subject.default_port - end - - it 'does not include port when it is default port' do - expect(subject.to_s).to eq 'redis://localhost' - end - - it 'includes port when it is different than default' do - subject.port = 6378 - expect(subject.to_s).to eq 'redis://localhost:6378' - end - end - - context 'with password' do - before { subject.password = 'password' } - - it 'allows password to be defined' do - expect(subject.password).to eq 'password' - end - - it 'renders url with password' do - expect(subject.to_s).to eq 'redis://:password@localhost' - end - end - - context 'with non-alphanumeric password' do - let(:password) { "&onBsidv6#XeKFd}=BDDyRrv" } - let(:escaped) { CGI.escape(password) } - - it 'rejects unencoded passwords' do - expect { subject.password = password }.to raise_error(URI::InvalidComponentError) - end - - it 'allows encoded passwords' do - subject.password = escaped - - expect(subject.password).to eq(escaped) - end - - it 'renders url with escaped password' do - subject.password = escaped - - expect(subject.to_s).to eq "redis://:#{escaped}@localhost" - end - end - - context 'without password' do - it 'renders url without authentication division characters' do - expect(subject.to_s).to eq 'redis://localhost' - end - end -end diff --git a/spec/libraries/registry_spec.rb b/spec/libraries/registry_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvcmVnaXN0cnlfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/libraries/registry_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'chef_helper' -require_relative '../../files/gitlab-cookbooks/gitlab/libraries/registry' - -RSpec.describe Registry do - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context 'registry is disabled' do - before do - stub_gitlab_rb( - registry: { - enabled: false - } - ) - end - - it 'should return false' do - expect(described_class.auto_enable).to be_falsey - end - end - - context 'registry_external_url is set' do - before do - stub_gitlab_rb( - registry_external_url: 'https://registry.example.com' - ) - end - - it 'should return false' do - expect(described_class.auto_enable).to be_falsey - end - end - - context 'lets encrypt is not enabled' do - before do - stub_gitlab_rb( - letsencrypt: { - enable: false - } - ) - end - - it 'should return false' do - expect(described_class.auto_enable).to be_falsey - end - end - - context 'external_url is a relative url' do - before do - stub_gitlab_rb( - external_url: 'https://registry.example.com/path' - ) - end - - it 'should return false' do - expect(described_class.auto_enable).to be_falsey - end - end -end diff --git a/spec/libraries/roles_spec.rb b/spec/libraries/roles_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvcm9sZXNfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/libraries/roles_spec.rb +++ /dev/null @@ -1,116 +0,0 @@ -require 'chef_helper' - -RSpec.describe 'GitLabRoles' do - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(Services).to receive(:enable_group).and_call_original - end - - after do - # Disable all roles after each test to clean the environment - Gitlab.available_roles.each { |name, _value| Gitlab["#{name}_role"]['enable'] = false } - end - - describe 'roles config array' do - it 'enables roles listed in the roles array' do - stub_gitlab_rb(roles: %w(application_role geo_primary_role)) - - Gitlab.load_roles - - expect(Gitlab['application_role']['enable']).to be true - expect(Gitlab['geo_primary_role']['enable']).to be true - end - - it 'supports providing a single role as a string' do - stub_gitlab_rb(roles: 'application_role') - - Gitlab.load_roles - - expect(Gitlab['application_role']['enable']).to be true - end - - it 'handles users specifying hyphens instead of underscores' do - stub_gitlab_rb(roles: ['geo-primary-role']) - - Gitlab.load_roles - - expect(Gitlab['geo_primary_role']['enable']).to be true - end - - it 'throws errors when an invalid role is used' do - stub_gitlab_rb(roles: ['some_invalid_role']) - - expect { Gitlab.load_roles }.to raise_error(RuntimeError, /invalid roles have been set/) - end - end - - describe 'DefaultRole' do - before do - allow(DefaultRole).to receive(:load_role).and_call_original - allow(GeoPrimaryRole).to receive(:load_role).and_call_original - end - - it 'enables the default services when no other roles are active' do - Gitlab.load_roles - - expect(Services).to have_received(:enable_group).with(Services::DEFAULT_GROUP, anything).once - end - - it 'enables the default services when no "service managed" roles are active' do - stub_gitlab_rb(geo_primary_role: { enable: true }) - - Gitlab.load_roles - - expect(Services).to have_received(:enable_group).with(Services::DEFAULT_GROUP, anything).once - expect(GeoPrimaryRole).to have_received(:load_role) - end - - it 'leaves the default services disabled when another role is active' do - stub_gitlab_rb(application_role: { enable: true }) - - Gitlab.load_roles - - expect(DefaultRole).to have_received(:load_role) - expect(Services).not_to have_received(:enable_group).with(Services::DEFAULT_GROUP, anything) - end - - it 'leaves rails roles disabled when requested to maintain backward compat' do - stub_gitlab_rb(gitlab_rails: { enable: false }) - - Gitlab.load_roles - - expect(Services).to have_received(:enable_group).with(Services::DEFAULT_GROUP, hash_including(except: ['rails'])).once - end - end - - describe 'ApplicationRole' do - before do - allow(ApplicationRole).to receive(:load_role).and_call_original - end - - it 'enables the rails services' do - stub_gitlab_rb(application_role: { enable: true }) - - Gitlab.load_roles - - expect(ApplicationRole).to have_received(:load_role) - expect(Gitlab['gitlab_rails']['enable']).to eq true - expect(Services).to have_received(:enable_group).with('rails').once - end - end - - describe 'MonitoringRole' do - before do - allow(MonitoringRole).to receive(:load_role).and_call_original - end - - it 'enables the monitoring services' do - stub_gitlab_rb(roles: %w(monitoring_role)) - - Gitlab.load_roles - - expect(MonitoringRole).to have_received(:load_role) - expect(Services).to have_received(:enable_group).with('monitoring_role').once - end - end -end diff --git a/spec/libraries/sentinel_helper_spec.rb b/spec/libraries/sentinel_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvc2VudGluZWxfaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/sentinel_helper_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'chef_helper' - -RSpec.describe SentinelHelper do - let(:chef_run) { converge_config(is_ee: true) } - subject { described_class.new(chef_run.node) } - before { allow(Gitlab).to receive(:[]).and_call_original } - - context '#myid' do - context 'when retrieving from config' do - it 'fails when myid is not 40 hex-characters long' do - stub_gitlab_rb( - sentinel: { - myid: 'wrongid' - } - ) - - expect { subject.myid }.to raise_error RuntimeError - end - - it 'works when myid is 40 hex-characters long' do - stub_gitlab_rb( - sentinel: { - myid: '1234567890abcdef1234567890abcdef12345678' - } - ) - - expect { subject.myid }.not_to raise_error - end - end - - context 'when no config is defined' do - let(:myid) { 'abcdef1234567890abcdef1234567890abcdef1' } - - it 'generates a random myid' do - expect(subject.myid).not_to be_empty - end - - it 'persist generated value into JSON file' do - allow(subject).to receive(:generate_myid).at_least(:once) { myid } - - expect(subject).to receive(:save_to_file).with({ 'myid' => myid }) - subject.myid - end - end - end -end diff --git a/spec/libraries/sentinel_spec.rb b/spec/libraries/sentinel_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvc2VudGluZWxfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/libraries/sentinel_spec.rb +++ /dev/null @@ -1,68 +0,0 @@ -# This spec is to test the Sentinel helper and whether the values parsed -# are the ones we expect -require 'chef_helper' - -RSpec.describe 'Sentinel' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config') } - let(:node) { chef_run.node } - subject { ::Sentinel } - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context '.parse_variables' do - context 'When sentinel is enabled' do - before do - stub_gitlab_rb( - redis_sentinel_role: { - enable: true, - } - ) - end - it 'delegates to parse_sentinel_settings' do - expect(subject).to receive(:parse_sentinel_settings) - - subject.parse_variables - end - - context 'when redis announce_ip is defined' do - let(:redis_master_ip) { '1.1.1.1' } - let(:redis_announce_ip) { '10.10.10.10' } - before do - stub_gitlab_rb( - redis: { - master_ip: redis_master_ip, - announce_ip: redis_announce_ip - } - ) - end - - it 'Sentinel announce_ip is autofilled based on redis announce_ip' do - expect(node['gitlab']['sentinel']['announce_ip']).to eq redis_announce_ip - - subject.parse_sentinel_settings - end - end - - context 'when redis announce_port is defined' do - let(:redis_announce_port) { 6370 } - let(:sentinel_port) { 26370 } - before do - stub_gitlab_rb( - sentinel: { - port: sentinel_port - }, - redis: { - announce_port: redis_announce_port - } - ) - end - it 'Sentinel announce_port is autofilled based on sentinel port' do - expect(node['gitlab']['sentinel']['announce_port']).to eq sentinel_port - - subject.parse_sentinel_settings - end - end - end - end -end diff --git a/spec/libraries/services_spec.rb b/spec/libraries/services_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvc2VydmljZXNfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/libraries/services_spec.rb +++ /dev/null @@ -1,316 +0,0 @@ -require 'chef_helper' - -RSpec.describe Services do - before { allow(Gitlab).to receive(:[]).and_call_original } - - describe 'when using the gitlab cookbook' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') } - - it 'returns the gitlab service list' do - chef_run - expect(Services.service_list).to have_key('puma') - expect(Services.service_list).not_to have_key('sentinel') - end - end - - describe 'when using the gitlab-ee cookbook' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - it 'returns the gitlab service list including gitlab-ee items' do - chef_run - expect(Services.service_list).to have_key('puma') - expect(Services.service_list).to have_key('sentinel') - end - end - - it 'uses the default template when populating service information' do - expect(Services::Config.send(:service, ['test_service'])).to eq({ groups: [] }) - end - - describe 'service' do - let(:runner) { ChefSpec::SoloRunner.new } - let(:chef_run) { runner.converge('gitlab::config') } - let(:node) { runner.node } - - before { Services.add_services('gitlab', Services::BaseServices.list) } - - context 'when using user values that conflict with service settings' do - let!(:runner) { ChefSpec::SoloRunner.new { |node| Gitlab[:node] = node } } - - it 'node service settings are overridden by gitlab.rb changes' do - stub_gitlab_rb(redis: { enable: true }, mattermost: { enable: false }) - Services.disable('redis') - Services.enable('mattermost') - runner.converge('gitlab::config') - - expect(node['redis']['enable']).to be true - expect(node['mattermost']['enable']).to be false - end - end - - context 'when enable/disable is passed a single service' do - it 'sets the correct values' do - chef_run - Services.disable('redis') - expect(node['redis']['enable']).to be false - - Services.enable('mattermost') - expect(node['mattermost']['enable']).to be true - end - - it 'supports exceptions' do - chef_run - Services.disable('mattermost') - Services.enable('mattermost', except: 'mattermost') - expect(node['mattermost']['enable']).to be false - - Services.enable('redis') - Services.disable('redis', except: 'redis') - expect(node['redis']['enable']).to be true - end - end - - context 'when enable/disable is passed multiple services' do - before { chef_run } - - it 'sets the correct values' do - Services.disable('redis', 'postgresql', 'gitaly') - expect(node['redis']['enable']).to be false - expect(node['postgresql']['enable']).to be false - expect(node['gitaly']['enable']).to be false - - Services.enable('mattermost', 'registry', 'mailroom') - expect(node['mattermost']['enable']).to be true - expect(node['registry']['enable']).to be true - expect(node['gitlab']['mailroom']['enable']).to be true - end - - it 'supports single exceptions' do - Services.disable('registry') - Services.enable('mattermost', 'registry', 'mailroom', except: 'registry') - expect(node['mattermost']['enable']).to be true - expect(node['registry']['enable']).to be false - expect(node['gitlab']['mailroom']['enable']).to be true - - Services.enable('postgresql') - Services.disable('redis', 'postgresql', 'gitaly', except: 'postgresql') - expect(node['redis']['enable']).to be false - expect(node['postgresql']['enable']).to be true - expect(node['gitaly']['enable']).to be false - end - - it 'supports multiple exceptions' do - Services.disable('registry', 'mailroom') - Services.enable('mattermost', 'registry', 'mailroom', except: %w(registry mailroom)) - expect(node['mattermost']['enable']).to be true - expect(node['registry']['enable']).to be false - expect(node['gitlab']['mailroom']['enable']).to be false - - Services.enable('postgresql', 'gitaly') - Services.disable('redis', 'postgresql', 'gitaly', except: %w(postgresql gitaly)) - expect(node['redis']['enable']).to be false - expect(node['postgresql']['enable']).to be true - expect(node['gitaly']['enable']).to be true - end - - it 'ignores disable on system services' do - Services.disable('node_exporter') - expect(node['monitoring']['node-exporter']['enable']).to be true - end - - it 'allows forced disable on system services' do - Services.disable('node_exporter', include_system: true) - expect(node['monitoring']['node-exporter']['enable']).to be false - end - end - - context 'when passed single exception' do - before { chef_run } - - it 'enables all others' do - Services.disable('registry') - Services.enable(Services::ALL_SERVICES, except: 'registry') - expect(node['redis']['enable']).to be true - expect(node['postgresql']['enable']).to be true - expect(node['mattermost']['enable']).to be true - expect(node['registry']['enable']).to be false - end - - it 'disables all others' do - Services.enable('redis') - Services.disable(Services::ALL_SERVICES, except: 'redis') - expect(node['redis']['enable']).to be true - expect(node['postgresql']['enable']).to be false - expect(node['mattermost']['enable']).to be false - expect(node['registry']['enable']).to be false - end - end - - context 'when passed multiple exceptions' do - before { chef_run } - - it 'enables all others' do - Services.disable('registry', 'mailroom') - Services.enable(Services::ALL_SERVICES, except: %w(registry mailroom)) - expect(node['redis']['enable']).to be true - expect(node['postgresql']['enable']).to be true - expect(node['gitaly']['enable']).to be true - expect(node['mattermost']['enable']).to be true - expect(node['gitlab']['mailroom']['enable']).to be false - expect(node['registry']['enable']).to be false - end - - it 'disables all others' do - Services.enable('postgresql', 'gitaly') - Services.disable(Services::ALL_SERVICES, except: %w(postgresql gitaly)) - expect(node['redis']['enable']).to be false - expect(node['postgresql']['enable']).to be true - expect(node['gitaly']['enable']).to be true - expect(node['mattermost']['enable']).to be false - expect(node['gitlab']['mailroom']['enable']).to be false - expect(node['registry']['enable']).to be false - end - end - end - - describe 'group' do - let(:runner) { ChefSpec::SoloRunner.new } - let(:chef_run) { runner.converge('gitlab::config') } - let(:node) { runner.node } - - before { Services.add_services('gitlab', Services::BaseServices.list) } - - context 'when using user values that conflict with service settings' do - cached!(:runner) { ChefSpec::SoloRunner.new { |node| Gitlab[:node] = node } } - - it 'node service settings are overridden by gitlab.rb changes' do - stub_gitlab_rb(redis: { enable: true }, postgresql: { enable: false }) - Services.disable_group('redis') - Services.enable_group('postgres') - runner.converge('gitlab::config') - - expect(node['redis']['enable']).to be true - expect(node['mattermost']['enable']).to be false - end - end - - context 'when enable_group/disable_group is passed a single group' do - before { chef_run } - - it 'sets the correct values' do - Services.disable_group('redis') - expect(node['redis']['enable']).to be false - expect(node['monitoring']['redis-exporter']['enable']).to be false - - Services.enable_group('rails') - expect(node['gitlab']['puma']['enable']).to be true - expect(node['monitoring']['gitlab-exporter']['enable']).to be true - end - - it 'supports exceptions' do - Services.disable_group('monitoring') - Services.enable_group('rails', except: 'monitoring') - expect(node['monitoring']['gitlab-exporter']['enable']).to be false - expect(node['gitlab']['puma']['enable']).to be true - - Services.enable_group('monitoring') - Services.disable_group('redis', except: 'monitoring') - expect(node['redis']['enable']).to be false - expect(node['monitoring']['redis-exporter']['enable']).to be true - end - end - - context 'when enable/disable is passed multiple groups' do - before { chef_run } - it 'sets the correct values' do - Services.disable_group('redis', 'postgres') - expect(node['redis']['enable']).to be false - expect(node['postgresql']['enable']).to be false - - Services.enable_group('rails', 'monitoring') - expect(node['monitoring']['redis-exporter']['enable']).to be true - expect(node['gitlab']['puma']['enable']).to be true - end - - it 'supports single exceptions' do - Services.disable_group('monitoring') - Services.enable_group('redis', 'rails', except: 'monitoring') - expect(node['redis']['enable']).to be true - expect(node['gitlab']['puma']['enable']).to be true - expect(node['monitoring']['gitlab-exporter']['enable']).to be false - expect(node['monitoring']['redis-exporter']['enable']).to be false - - Services.enable_group('postgres') - Services.disable_group('redis', 'monitoring', except: 'postgres') - expect(node['redis']['enable']).to be false - expect(node['monitoring']['postgres-exporter']['enable']).to be true - expect(node['monitoring']['prometheus']['enable']).to be false - end - - it 'supports multiple exceptions' do - Services.disable_group('redis', Services::SYSTEM_GROUP, include_system: true) - Services.enable_group('rails', 'monitoring', except: ['redis', Services::SYSTEM_GROUP]) - expect(node['monitoring']['redis-exporter']['enable']).to be false - expect(node['monitoring']['node-exporter']['enable']).to be false - expect(node['gitlab']['puma']['enable']).to be true - - Services.enable_group('sidekiq', 'monitoring') - Services.disable_group('rails', 'postgres', except: %w(sidekiq monitoring)) - expect(node['gitlab']['gitlab-workhorse']['enable']).to be false - expect(node['gitlab']['sidekiq']['enable']).to be true - expect(node['postgresql']['enable']).to be false - expect(node['monitoring']['postgres-exporter']['enable']).to be true - end - - it 'ignores disable on system services' do - Services.enable_group(Services::SYSTEM_GROUP) - Services.disable_group(Services::SYSTEM_GROUP) - expect(node['logrotate']['enable']).to be true - end - - it 'allows forced disable on system services' do - Services.enable_group(Services::SYSTEM_GROUP) - Services.disable_group(Services::SYSTEM_GROUP, include_system: true) - expect(node['logrotate']['enable']).to be false - end - end - - context 'when passed single exception' do - before { chef_run } - - it 'enables all others' do - Services.disable_group('monitoring') - Services.enable_group(Services::ALL_GROUPS, except: 'monitoring') - expect(node['gitlab']['puma']['enable']).to be true - expect(node['monitoring']['gitlab-exporter']['enable']).to be false - end - - it 'disables all others' do - Services.enable_group('monitoring') - Services.disable_group(Services::ALL_GROUPS, except: 'monitoring') - expect(node['monitoring']['postgres-exporter']['enable']).to be true - expect(node['postgresql']['enable']).to be false - end - end - - context 'when passed multiple exceptions' do - before { chef_run } - - it 'enables all others' do - Services.disable_group('redis', 'rails') - Services.enable_group(Services::ALL_GROUPS, except: %w(redis rails)) - expect(node['gitlab']['puma']['enable']).to be false - expect(node['monitoring']['node-exporter']['enable']).to be true - expect(node['monitoring']['redis-exporter']['enable']).to be false - end - - it 'disables all others' do - Services.enable_group('redis', 'rails') - Services.disable_group(Services::ALL_GROUPS, except: %w(redis rails)) - expect(node['monitoring']['prometheus']['enable']).to be false - expect(node['redis']['enable']).to be true - expect(node['gitlab']['sidekiq']['enable']).to be true - end - end - end -end diff --git a/spec/libraries/storage_directory_helper_spec.rb b/spec/libraries/storage_directory_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvc3RvcmFnZV9kaXJlY3RvcnlfaGVscGVyX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/storage_directory_helper_spec.rb +++ /dev/null @@ -1,109 +0,0 @@ -require 'chef_helper' - -RSpec.describe StorageDirectoryHelper do - let(:success_shell) do - shell = instance_double(Mixlib::ShellOut) - allow(shell).to receive(:exitstatus).and_return(0) - shell - end - - let(:fail_shell) do - shell = instance_double(Mixlib::ShellOut) - allow(shell).to receive(:exitstatus).and_return(1) - shell - end - - before { allow(Gitlab).to receive(:[]).and_call_original } - - describe :validate do - context 'owner provided' do - subject { ::StorageDirectoryHelper.new('git', nil, nil) } - - it 'checks directory and owner and succeeds' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(success_shell) - expect(subject.validate('/tmp/validate')).to eq(true) - end - - it 'fails when path is not a directory' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(fail_shell) - expect(subject).not_to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args) - expect(subject.validate('/tmp/validate')).to eq(false) - end - - it 'fails when owner does not match' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(fail_shell) - expect(subject.validate('/tmp/validate')).to eq(false) - end - end - - context 'owner and group provided' do - subject { ::StorageDirectoryHelper.new('git', 'root', nil) } - - it 'checks directory, owner and group and succeeds' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U:%G' $(readlink -f /tmp/validate))\" = 'git:root' ]", any_args).and_return(success_shell) - expect(subject.validate('/tmp/validate')).to eq(true) - end - - it 'fails when path is not a directory' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(fail_shell) - expect(subject).not_to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U:%G' $(readlink -f /tmp/validate))\" = 'git:root' ]", any_args) - expect(subject.validate('/tmp/validate')).to eq(false) - end - - it 'fails when group does not match' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U:%G' $(readlink -f /tmp/validate))\" = 'git:root' ]", any_args).and_return(fail_shell) - expect(subject.validate('/tmp/validate')).to eq(false) - end - end - - context 'owner and permission mode provided' do - subject { ::StorageDirectoryHelper.new('git', nil, '700') } - - it 'checks directory, owner and permissions and succeeds' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%04a' $(readlink -f /tmp/validate) | grep -o '...$')\" = '700' ]", any_args).and_return(success_shell) - expect(subject.validate('/tmp/validate')).to eq(true) - end - - it 'fails when path is not a directory' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(fail_shell) - expect(subject).not_to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U:%G' $(readlink -f /tmp/validate))\" = 'git:root' ]", any_args) - expect(subject).not_to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%04a' $(readlink -f /tmp/validate) | grep -o '...$')\" = '700' ]", any_args) - expect(subject.validate('/tmp/validate')).to eq(false) - end - - it 'fails when owner does not match' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(fail_shell) - expect(subject).not_to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%04a' $(readlink -f /tmp/validate) | grep -o '...$')\" = '700' ]", any_args) - expect(subject.validate('/tmp/validate')).to eq(false) - end - - it 'fails when permissions do not match' do - expect(subject).to receive(:run_command).with("set -x && [ -d \"/tmp/validate\" ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%U' $(readlink -f /tmp/validate))\" = 'git' ]", any_args).and_return(success_shell) - expect(subject).to receive(:run_command) - .with("set -x && [ \"$(stat --printf='%04a' $(readlink -f /tmp/validate) | grep -o '...$')\" = '700' ]", any_args).and_return(fail_shell) - expect(subject.validate('/tmp/validate')).to eq(false) - end - end - end -end diff --git a/spec/libraries/systemd_helper_spec.rb b/spec/libraries/systemd_helper_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvc3lzdGVtZF9oZWxwZXJfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/libraries/systemd_helper_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'spec_helper' -require_relative '../../files/gitlab-cookbooks/package/libraries/helpers/systemd_helper' - -RSpec.describe SystemdHelper do - describe '#systemd_version' do - context 'when systemctl returns valid output' do - before do - valid_output = <<~OUTPUT - systemd 242 (242) - +PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT - OUTPUT - - allow(IO).to receive(:popen).with(%w[systemctl --version]).and_return(valid_output) - end - - it 'returns correct version' do - expect(described_class.systemd_version).to eq(242) - end - end - - context 'when systemctl returns invalid output' do - before do - invalid_output = <<~OUTPUT - some invalid output - OUTPUT - - allow(IO).to receive(:popen).with(%w[systemctl --version]).and_return(invalid_output) - end - - it 'returns an insanely low version' do - expect(described_class.systemd_version).to eq(-999) - end - end - end -end diff --git a/spec/libraries/workhorse_spec.rb b/spec/libraries/workhorse_spec.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9saWJyYXJpZXMvd29ya2hvcnNlX3NwZWMucmI=..0000000000000000000000000000000000000000 --- a/spec/libraries/workhorse_spec.rb +++ /dev/null @@ -1,125 +0,0 @@ -# This spec is to test the Workhorse library and whether the values parsed -# are the ones we expect -require 'chef_helper' - -RSpec.describe 'GitlabWorkhorse' do - let(:node) { chef_run.node } - let(:user_socket) { '/where/is/my/ten/mm/socket_now' } - let(:user_sockets_directory) { '/where/is/my/ten/mm/sockets' } - let(:default_sockets_directory) { '/var/opt/gitlab/gitlab-workhorse/sockets' } - let(:default_socket) { '/var/opt/gitlab/gitlab-workhorse/sockets/socket' } - let(:tcp_listen_address) { '1.9.8.4' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context '.parse_variables' do - context 'listening on a tcp socket' do - let(:chef_run) { converge_config } - - before do - stub_gitlab_rb( - gitlab_workhorse: { - listen_network: 'http', - listen_addr: tcp_listen_address - } - ) - end - - it 'uses the user configured TCP listen address' do - expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq(tcp_listen_address) - end - - it 'keeps the sockets_directory as nil' do - expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(nil) - end - end - - context 'listening on a unix socket' do - context 'using default configuration' do - let(:chef_run) { converge_config } - - before do - stub_gitlab_rb( - gitlab_workhorse: { - listen_network: 'unix' - } - ) - end - - it 'uses the default sockets directory' do - expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(default_sockets_directory) - end - - it 'uses the default socket file path' do - expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq(default_socket) - end - end - - context 'only listen_addr is set' do - let(:chef_run) { converge_config } - - before do - stub_gitlab_rb( - gitlab_workhorse: { - listen_network: 'unix', - listen_addr: user_socket - } - ) - end - - it 'uses the user configured listen address' do - expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq(user_socket) - end - - it 'keeps the sockets_directory as nil' do - expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(nil) - end - end - - context 'only sockets_directory is set' do - let(:chef_run) { converge_config } - - before do - stub_gitlab_rb( - gitlab_workhorse: { - listen_network: 'unix', - sockets_directory: user_sockets_directory - } - ) - end - - it 'uses the user configured sockets directory' do - expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(user_sockets_directory) - end - - it 'creates a socket named socket in the user configured sockets directory' do - expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq("#{user_sockets_directory}/socket") - end - end - - context 'listen_addr and sockets_directory are both set' do - let(:chef_run) { converge_config } - - before do - stub_gitlab_rb( - gitlab_workhorse: { - listen_network: 'unix', - listen_addr: user_socket, - sockets_directory: user_sockets_directory - } - ) - end - - it 'uses the user configured sockets directory' do - expect(node['gitlab']['gitlab-workhorse']['sockets_directory']).to eq(user_sockets_directory) - end - - it 'creates a socket matching the configured listen_addr' do - expect(node['gitlab']['gitlab-workhorse']['listen_addr']).to eq(user_socket) - end - end - end - end -end diff --git a/spec/support/macros.rb b/spec/support/macros.rb index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L21hY3Jvcy5yYg==..120b23950e6893931eef61fecada8c9387ee8623_c3BlYy9zdXBwb3J0L21hY3Jvcy5yYg== 100644 --- a/spec/support/macros.rb +++ b/spec/support/macros.rb @@ -99,7 +99,7 @@ # Return the full path for the spec fixtures folder # @return [String] full path def fixture_path - File.join(__dir__, '../fixtures') + File.join(__dir__, '../chef/fixtures') end def get_rendered_toml(chef_run, path) diff --git a/spec/support/shared_context/ctl_shared_context.rb b/spec/support/shared_context/ctl_shared_context.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9jb250ZXh0L2N0bF9zaGFyZWRfY29udGV4dC5yYg==..0000000000000000000000000000000000000000 --- a/spec/support/shared_context/ctl_shared_context.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'spec_helper' -require 'omnibus-ctl' - -RSpec.shared_context 'ctl' do - let(:ctl) { Omnibus::Ctl.new('testing-ctl') } - - before do - allow_any_instance_of(Omnibus::Ctl).to receive(:require).and_call_original - - allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( - "/opt/testing-ctl/embedded/cookbooks/package/libraries/gitlab_cluster" - ) do - require_relative("../../../files/gitlab-cookbooks/package/libraries/gitlab_cluster") - end - - allow_any_instance_of(Omnibus::Ctl).to receive(:require).with( - "/opt/testing-ctl/embedded/service/omnibus-ctl-ee/lib/geo/#{command_script}" - ) do - require_relative("../../../files/gitlab-ctl-commands-ee/lib/geo/#{command_script}") - end - - ctl.load_file("files/gitlab-ctl-commands-ee/#{command_script}.rb") - end -end diff --git a/spec/support/shared_context/gitlab_rails_shared_context.rb b/spec/support/shared_context/gitlab_rails_shared_context.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9jb250ZXh0L2dpdGxhYl9yYWlsc19zaGFyZWRfY29udGV4dC5yYg==..0000000000000000000000000000000000000000 --- a/spec/support/shared_context/gitlab_rails_shared_context.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'chef_helper' - -RSpec.shared_context 'gitlab-rails' do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: 'templatesymlink').converge('gitlab::default') } - let(:gitlab_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/gitlab.yml') } - let(:gitlab_yml_file_content) { ChefSpec::Renderer.new(chef_run, gitlab_yml_template).content } - let(:gitlab_yml) { YAML.safe_load(gitlab_yml_file_content, [], [], true, symbolize_names: true) } - let(:config_dir) { '/var/opt/gitlab/gitlab-rails/etc/' } - - before do - allow(Gitlab).to receive(:[]).and_call_original - allow(File).to receive(:symlink?).and_call_original - end -end diff --git a/spec/support/shared_context/object_store_shared_context.rb b/spec/support/shared_context/object_store_shared_context.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9jb250ZXh0L29iamVjdF9zdG9yZV9zaGFyZWRfY29udGV4dC5yYg==..0000000000000000000000000000000000000000 --- a/spec/support/shared_context/object_store_shared_context.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'spec_helper' -require 'omnibus-ctl' - -RSpec.shared_context 'object storage config' do - let(:object_config) do - { - artifacts: { bucket: 'artifacts' }, - lfs: { bucket: 'lfs-objects' }, - dependency_proxy: { bucket: 'dependency_proxy' }, - external_diffs: { bucket: 'external_diffs' }, - packages: { bucket: 'packages' }, - terraform_state: { enabled: false, bucket: 'terraform' }, - ci_secure_files: { bucket: 'ci_secure_files' }, - uploads: { bucket: 'uploads' }, - pages: { bucket: 'pages' } - } - end - let(:aws_connection_hash) do - { - 'provider' => 'AWS', - 'region' => 'eu-west-1', - 'aws_access_key_id' => 'AKIAKIAKI', - 'aws_secret_access_key' => 'secret123' - } - end - let(:aws_storage_options_hash) do - { - 'server_side_encryption' => 'AES256', - 'server_side_encryption_kms_key_id' => 'arn:aws:12345' - } - end - let(:azure_connection_hash) do - { - 'provider' => 'AzureRM', - 'azure_storage_account_name' => 'testaccount', - 'azure_storage_access_key' => '1234abcd' - } - end - - let(:aws_connection_data) { JSON.parse(aws_connection_hash.to_json, symbolize_names: true) } - let(:aws_storage_options) { JSON.parse(aws_storage_options_hash.to_json, symbolize_names: true) } -end diff --git a/spec/support/shared_examples/consul_service_name.rb b/spec/support/shared_examples/consul_service_name.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9leGFtcGxlcy9jb25zdWxfc2VydmljZV9uYW1lLnJi..0000000000000000000000000000000000000000 --- a/spec/support/shared_examples/consul_service_name.rb +++ /dev/null @@ -1,44 +0,0 @@ -shared_examples 'consul service discovery' do |gitlab_rb_key, service_name| - let(:gitlab_rb_setting) do - { - consul: { - enable: true, - monitoring_service_discovery: true - } - } - end - - describe 'consul service discovery' do - let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') } - - context 'by default' do - it 'is not registered as consul service' do - expect(chef_run).not_to create_consul_service(service_name) - end - end - - context 'when enabled' do - before do - stub_gitlab_rb(gitlab_rb_setting) - end - - it 'is registered as a consul service with default name' do - expect(chef_run).to create_consul_service(service_name) - end - - context 'with user specified service name' do - before do - gitlab_rb_setting[gitlab_rb_key] = { - enable: true, - consul_service_name: "#{service_name}-foobar" - } - stub_gitlab_rb(gitlab_rb_setting) - end - - it 'is registered as a consul service with user specified name' do - expect(chef_run).to create_consul_service("#{service_name}-foobar") - end - end - end - end -end diff --git a/spec/support/shared_examples/geo_promotion_command_accepts_option_shared_examples.rb b/spec/support/shared_examples/geo_promotion_command_accepts_option_shared_examples.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9leGFtcGxlcy9nZW9fcHJvbW90aW9uX2NvbW1hbmRfYWNjZXB0c19vcHRpb25fc2hhcmVkX2V4YW1wbGVzLnJi..0000000000000000000000000000000000000000 --- a/spec/support/shared_examples/geo_promotion_command_accepts_option_shared_examples.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -RSpec.shared_examples 'geo promotion command accepts option' do |passed_option, expected_option| - it 'accepts given option' do - allow_any_instance_of(klass).to receive(:execute).and_return(true) - - # ARGV contains the commands that were passed to rspec, which are - # invalid for the omnibus-ctl commands - oldargv = ARGV - ARGV = [passed_option] # rubocop:disable Style/MutableConstant - - expect(klass).to receive(:new).with( - anything, expected_option).and_call_original - - ctl.send(command_script, ARGV) - - ARGV = oldargv - end -end diff --git a/spec/support/shared_examples/gitlab_geo_promotion_commands_shared_examples.rb b/spec/support/shared_examples/gitlab_geo_promotion_commands_shared_examples.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9leGFtcGxlcy9naXRsYWJfZ2VvX3Byb21vdGlvbl9jb21tYW5kc19zaGFyZWRfZXhhbXBsZXMucmI=..0000000000000000000000000000000000000000 --- a/spec/support/shared_examples/gitlab_geo_promotion_commands_shared_examples.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -RSpec.shared_examples 'gitlab geo promotion commands' do - it 'appends a geo replication command' do - expect(ctl.get_all_commands_hash).to include(command_name) - end - - it 'executes the command when called' do - # ARGV contains the commands that were passed to rspec, which are - # invalid for the omnibus-ctl commands - oldargv = ARGV - ARGV = [] # rubocop:disable Style/MutableConstant - - expect_any_instance_of(klass).to receive(:execute) - - ctl.send(command_script) - - ARGV = oldargv - end -end diff --git a/spec/support/shared_examples/gitlab_yml_object_storage_shared_examples.rb b/spec/support/shared_examples/gitlab_yml_object_storage_shared_examples.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9leGFtcGxlcy9naXRsYWJfeW1sX29iamVjdF9zdG9yYWdlX3NoYXJlZF9leGFtcGxlcy5yYg==..0000000000000000000000000000000000000000 --- a/spec/support/shared_examples/gitlab_yml_object_storage_shared_examples.rb +++ /dev/null @@ -1,69 +0,0 @@ -shared_examples 'renders object storage settings in gitlab.yml' do |component, component_default = {}, workhorse_accelerated = true| - include_context 'gitlab-rails' - include_context 'object storage config' - - describe "for #{component}" do - context 'with default values' do - it 'renders gitlab.yml with object storage disabled and other default values' do - default_values = { - enabled: false, - remote_directory: component, - connection: {} - } - - if workhorse_accelerated - default_values.merge!( - direct_upload: false, - background_upload: true, - proxy_download: false - ) - end - - default_values.merge!(component_default) - - config = gitlab_yml[:production][component.to_sym][:object_store] - expect(config).to eq(default_values) - end - end - - context 'with user specified values' do - before do - gitlab_rails_config = { - "#{component}_object_store_enabled" => true, - "#{component}_object_store_remote_directory" => 'foobar', - "#{component}_object_store_connection" => aws_connection_hash - } - - if workhorse_accelerated - gitlab_rails_config.merge!( - "#{component}_object_store_direct_upload" => true, - "#{component}_object_store_background_upload" => false, - "#{component}_object_store_proxy_download" => true - ) - end - - stub_gitlab_rb( - gitlab_rails: gitlab_rails_config.transform_keys(&:to_sym) - ) - end - - it 'renders gitlab.yml with user specified values' do - expected_output = { - enabled: true, - connection: aws_connection_data, - remote_directory: 'foobar' - } - - if workhorse_accelerated - expected_output.merge!( - direct_upload: true, - background_upload: false, - proxy_download: true - ) - end - - expect(gitlab_yml[:production][component.to_sym][:object_store]).to eq(expected_output) - end - end - end -end diff --git a/spec/support/shared_examples/logrotate_config.rb b/spec/support/shared_examples/logrotate_config.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9leGFtcGxlcy9sb2dyb3RhdGVfY29uZmlnLnJi..0000000000000000000000000000000000000000 --- a/spec/support/shared_examples/logrotate_config.rb +++ /dev/null @@ -1,9 +0,0 @@ -shared_examples 'configured logrotate service' do |svc, username = 'git', group = 'git'| - it 'creates logrotate config file' do - expect(chef_run).to render_file("/var/opt/gitlab/logrotate/logrotate.d/#{svc}") - end - - it 'specifies su parameter in logrotate config' do - expect(chef_run).to render_file("/var/opt/gitlab/logrotate/logrotate.d/#{svc}").with_content(/su #{username} #{group}/) - end -end diff --git a/spec/support/shared_examples/registry_service.rb b/spec/support/shared_examples/registry_service.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9leGFtcGxlcy9yZWdpc3RyeV9zZXJ2aWNlLnJi..0000000000000000000000000000000000000000 --- a/spec/support/shared_examples/registry_service.rb +++ /dev/null @@ -1,102 +0,0 @@ -shared_examples 'enabled registry service' do - it 'creates default set of directories' do - expect(chef_run.node['registry']['dir']) - .to eql('/var/opt/gitlab/registry') - expect(chef_run.node['registry']['log_directory']) - .to eql('/var/log/gitlab/registry') - expect(chef_run.node['gitlab']['gitlab-rails']['registry_path']) - .to eql('/var/opt/gitlab/gitlab-rails/shared/registry') - - expect(chef_run).to create_directory('/var/opt/gitlab/registry') - expect(chef_run).to create_directory('/var/log/gitlab/registry').with( - owner: 'registry', - group: nil, - mode: '0700' - ) - expect(chef_run).to create_directory('/var/opt/gitlab/gitlab-rails/shared/registry').with( - owner: 'registry', - group: 'git', - mode: '0770' - ) - end - - it 'creates default user and group' do - expect(chef_run.node['registry']['username']) - .to eql('registry') - expect(chef_run.node['registry']['group']) - .to eql('registry') - - expect(chef_run).to create_account('Docker registry user and group').with( - username: 'registry', - groupname: 'registry', - uid: nil, - gid: nil, - system: true, - home: '/var/opt/gitlab/registry' - ) - end - - it 'creates default self signed key-certificate pair' do - expect(chef_run).to create_file('/var/opt/gitlab/registry/gitlab-registry.crt').with( - user: 'registry', - group: 'registry' - ) - expect(chef_run).to create_file('/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key').with( - user: 'git', - group: 'git' - ) - - expect(chef_run).to render_file('/var/opt/gitlab/registry/gitlab-registry.crt') - .with_content(/-----BEGIN CERTIFICATE-----/) - expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key') - .with_content(/-----BEGIN RSA PRIVATE KEY-----/) - end - - it 'creates registry config.yml template' do - expect(chef_run).to create_template('/var/opt/gitlab/registry/config.yml').with( - owner: 'registry', - group: nil, - mode: '0644' - ) - expect(chef_run).to( - render_file('/var/opt/gitlab/registry/config.yml').with_content do |content| - expect(content).to match(/version: 0.1/) - expect(content).to match(/realm: .*\/jwt\/auth/) - expect(content).to match(/addr: localhost:5000/) - expect(content).to match(%r(storage: {"filesystem":{"rootdirectory":"/var/opt/gitlab/gitlab-rails/shared/registry"})) - expect(content).to match(/health:\s*storagedriver:\s*enabled:\s*true/) - expect(content).to match(/log:\s*level: info\s*formatter:\s*text/) - expect(content).to match(/validation:\s*disabled: true$/) - expect(content).not_to match(/^compatibility:/) - expect(content).not_to match(/^middleware:/) - end - ) - end - - it 'populates default settings for svlogd' do - expect(chef_run).to render_file('/opt/gitlab/sv/registry/log/run') - .with_content(/exec svlogd -tt \/var\/log\/gitlab\/registry/) - end - - it 'creates a default VERSION file and restarts service' do - expect(chef_run).to create_version_file('Create version file for Registry').with( - version_file_path: '/var/opt/gitlab/registry/VERSION', - version_check_cmd: '/opt/gitlab/embedded/bin/registry --version' - ) - - expect(chef_run.version_file('Create version file for Registry')).to notify('runit_service[registry]').to(:restart) - end - - it 'creates gitlab-rails config with default values' do - expect(chef_run).to create_templatesymlink('Create a gitlab.yml and create a symlink to Rails root').with_variables(hash_including('registry_api_url' => 'http://localhost:5000')) - end - - it 'sets default storage options' do - expect(chef_run.node['registry']['storage']['filesystem']) - .to eql('rootdirectory' => '/var/opt/gitlab/gitlab-rails/shared/registry') - expect(chef_run.node['registry']['storage']['cache']) - .to eql('blobdescriptor' => 'inmemory') - expect(chef_run.node['registry']['storage']['delete']) - .to eql('enabled' => true) - end -end diff --git a/spec/support/shared_examples/runit_service.rb b/spec/support/shared_examples/runit_service.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9leGFtcGxlcy9ydW5pdF9zZXJ2aWNlLnJi..0000000000000000000000000000000000000000 --- a/spec/support/shared_examples/runit_service.rb +++ /dev/null @@ -1,120 +0,0 @@ -def get_env_string(username, groupname) - env_string = "" - env_string << username if username - env_string << ":#{groupname}" if groupname - - env_string -end - -shared_examples 'enabled runit service' do |svc_name, owner, group, username = nil, groupname = nil| - it 'creates directories' do - expect(chef_run).to create_directory("/opt/gitlab/sv/#{svc_name}").with( - owner: owner, - group: group, - mode: '0755' - ) - expect(chef_run).to create_directory("/opt/gitlab/sv/#{svc_name}/log").with( - owner: owner, - group: group, - mode: '0755' - ) - expect(chef_run).to create_directory("/opt/gitlab/sv/#{svc_name}/log/main").with( - owner: owner, - group: group, - mode: '0755' - ) - end - - it 'creates files' do - expect(chef_run).to create_template("/opt/gitlab/sv/#{svc_name}/run").with( - owner: owner, - group: group, - mode: '0755' - ) - expect(chef_run).to create_template("/opt/gitlab/sv/#{svc_name}/log/run").with( - owner: owner, - group: group, - mode: '0755' - ) - expect(chef_run).to create_template("/opt/gitlab/sv/#{svc_name}/log/config").with( - owner: owner, - group: group, - mode: '0644' - ) - - env_string = get_env_string(username, groupname) - - if env_string.empty? - expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/run").with_content(%r{.*}) - else - expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/run").with_content(/-u #{env_string}/) - expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/run").with_content(/-U #{env_string}/) - end - - expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/log/run").with_content(%r{.*}) - expect(chef_run).to render_file("/opt/gitlab/sv/#{svc_name}/log/config") - end - - it 'verifies that file ownership persists' do - expect(chef_run.template("/opt/gitlab/sv/#{svc_name}/log/config")).to notify("ruby_block[verify_chown_persisted_on_#{svc_name}]") - end - - it 'raises an error when file ownership does not persist' do - log_config_re = /\/opt\/gitlab\/sv\/.*\/log\/config/ - allow_any_instance_of(OmnibusHelper).to receive(:expected_user?).and_return(true) - allow_any_instance_of(OmnibusHelper).to receive(:expected_user?).with(log_config_re, anything).and_return(false) - - allow_any_instance_of(OmnibusHelper).to receive(:expected_group?).and_return(true) - allow_any_instance_of(OmnibusHelper).to receive(:expected_group?).with(log_config_re, anything).and_return(false) - - block = chef_run.find_resource('ruby_block', "verify_chown_persisted_on_#{svc_name}") - expect { block.block.call }.to raise_error("Unable to persist filesystem ownership changes of /opt/gitlab/sv/#{svc_name}/log/config. See https://docs.gitlab.com/ee/administration/high_availability/nfs.html#recommended-options for guidance.") - end - - it 'creates the symlink to the service directory' do - expect(chef_run).to create_link("/opt/gitlab/init/#{svc_name}").with(to: '/opt/gitlab/embedded/bin/sv') - end - - context 'gitlab customization' do - # These are specs related to changes we have made to the upstream runit cookbook - before do - %w(ok status control).each do |target| - file_name = ::File.join('/opt/gitlab/service', svc_name, 'log', 'supervise', target) - allow_any_instance_of(OmnibusHelper).to receive(:expected_owner?).with(file_name, username, groupname).and_return(false) - end - end - - it 'sets the supervise log files with correct permissions' do - # For some services, we set the ownership on the sv log files to allow supervisor_owner to run commands - unless username.nil? - expect(chef_run).to create_directory("/opt/gitlab/service/#{svc_name}/log/supervise").with(mode: '0755') - %w(ok status control).each do |target| - file_name = ::File.join('/opt/gitlab/service', svc_name, 'log', 'supervise', target) - expect(chef_run).to touch_file(file_name).with( - owner: username, - group: groupname - ) - end - end - end - end -end - -shared_examples 'disabled runit service' do |svc_name| - it 'does not create directories' do - expect(chef_run).not_to create_directory("/opt/gitlab/sv/#{svc_name}") - expect(chef_run).not_to create_directory("/opt/gitlab/sv/#{svc_name}/log") - expect(chef_run).not_to create_directory("/opt/gitlab/sv/#{svc_name}/log/main") - end - - it 'does not create files' do - expect(chef_run).not_to create_template("/opt/gitlab/sv/#{svc_name}/run") - expect(chef_run).not_to create_template("/opt/gitlab/sv/#{svc_name}/log/run") - expect(chef_run).not_to create_template("/var/log/gitlab/#{svc_name}/config") - end - - it 'runs the disable service ruby block' do - allow_any_instance_of(Chef::Provider::RunitService).to receive(:enabled?).and_return(true) - expect(chef_run).to run_ruby_block("disable #{svc_name}") - end -end diff --git a/spec/support/shared_examples/yaml_check.rb b/spec/support/shared_examples/yaml_check.rb deleted file mode 100644 index f8db0051e33f2ed7cec00f743c123af4a4185914_c3BlYy9zdXBwb3J0L3NoYXJlZF9leGFtcGxlcy95YW1sX2NoZWNrLnJi..0000000000000000000000000000000000000000 --- a/spec/support/shared_examples/yaml_check.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'yaml' - -shared_examples 'renders a valid YAML file' do |filename| - it 'parses YAML with no errors' do - expect(chef_run).to render_file(filename).with_content { |content| - expect { YAML.parse(content) }.not_to raise_error - } - end -end