spec/chef/cookbooks/consul/recipes/consul_spec.rb (444 lines of code) (raw):
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' }
let(:consul_conf_chef_file) { chef_run.file(consul_conf) }
let(:consul_conf_file_content) { ChefSpec::Renderer.new(chef_run, consul_conf_chef_file).content }
let(:consul_conf_json) { JSON.parse(consul_conf_file_content) }
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', true
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 { |content|
expect(content).to match(%r{-config-dir /fake/config.d})
expect(content).to match(%r{-config-dir /custom/dir})
expect(content).to match(%r{-data-dir /fake/data})
}
end
it 'renders log run file with timestamp option' do
expect(chef_run).to render_file('/opt/gitlab/sv/consul/log/run').with_content { |content|
expect(content).to match(%r{svlogd -tt /var/log/gitlab/consul})
}
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":false})
expect(content).to match(%r{"enable_local_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',
configuration: {
log_json: true
},
logging_filters: {
label: 'filter'
}
}
)
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 'renders log run file without timestamp option' do
expect(chef_run).to render_file('/opt/gitlab/sv/consul/log/run').with_content { |content|
expect(content).to match(%r{svlogd /var/log/gitlab/consul})
}
end
it 'renders log config with logging_filters keys/values as comments/values' do
expect(chef_run).to render_file('/opt/gitlab/sv/consul/log/config').with_content { |content|
expect(content).to match(%r{# label\nfilter})
}
end
it_behaves_like 'enabled runit service', 'consul', 'foo', 'bar', 'foo', 'bar', true
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
end
describe 'TLS configuration' do
context 'in client mode' do
context 'by default' do
before do
stub_gitlab_rb(
consul: {
enable: true,
use_tls: true
}
)
end
it 'verifies outgoing connections only' do
expected_output = {
'defaults' => {
'verify_incoming' => false,
'verify_outgoing' => true
}
}
expect(consul_conf_json['tls']).to eq(expected_output)
end
end
context 'with user specified values' do
before 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
}
)
end
it 'verifies incoming and outgoing connections' do
expected_output = {
'defaults' => {
'verify_incoming' => true,
'verify_outgoing' => true,
'ca_file' => '/fake/ca.crt.pem',
'cert_file' => '/fake/server.crt.pem',
'key_file' => '/fake/server.key.pem',
}
}
expect(consul_conf_json['ports']).to eq({ 'https' => 8501 })
expect(consul_conf_json['tls']).to eq(expected_output)
end
end
end
context 'in server mode' do
context 'by default' do
before do
stub_gitlab_rb(
consul: {
enable: true,
use_tls: true,
configuration: {
server: true
}
}
)
end
it 'verifies outgoing and incoming connections' do
expected_output = {
'defaults' => {
'verify_incoming' => true,
'verify_outgoing' => true
}
}
expect(consul_conf_json['tls']).to eq(expected_output)
end
end
context 'with user specified values' do
before 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: false,
https_port: 8501
}
)
end
it 'verifies outgoing connection only' do
expected_output = {
'defaults' => {
'verify_incoming' => false,
'verify_outgoing' => true,
'ca_file' => '/fake/ca.crt.pem',
'cert_file' => '/fake/server.crt.pem',
'key_file' => '/fake/server.key.pem',
}
}
expect(consul_conf_json['ports']).to eq({ 'https' => 8501 })
expect(consul_conf_json['tls']).to eq(expected_output)
end
end
end
context 'using both dedicated consul TLS configuration settings and general configuration hash' do
before 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: false,
https_port: 8501,
configuration: {
tls: {
defaults: {
cert_file: '/foo/server.crt',
key_file: '/foo/server.key',
ca_file: '/foo/ca.crt'
}
}
}
}
)
end
it 'uses the settings given in general configuration hash' do
expected_output = {
'defaults' => {
'verify_incoming' => false,
'verify_outgoing' => true,
'ca_file' => '/foo/ca.crt',
'cert_file' => '/foo/server.crt',
'key_file' => '/foo/server.key',
}
}
expect(consul_conf_json['tls']).to eq(expected_output)
end
context 'using deprecated settings' do
before do
stub_gitlab_rb(
consul: {
enable: true,
use_tls: true,
configuration: {
cert_file: '/foo/server.crt',
key_file: '/foo/server.key',
ca_file: '/foo/ca.crt'
}
}
)
end
it 'generates the configuration file properly' do
expected_output = {
'defaults' => {
'verify_incoming' => false,
'verify_outgoing' => true,
'ca_file' => '/foo/ca.crt',
'cert_file' => '/foo/server.crt',
'key_file' => '/foo/server.key',
}
}
expect(consul_conf_json['tls']).to eq(expected_output)
end
it 'generates deprecation notices' do
chef_run
expect_logged_deprecation(%r{`consul\['configuration'\]\['ca_file'\]` has been deprecated.*`consul\['configuration'\]\['tls'\]\['defaults'\]\['ca_file'\]`})
expect_logged_deprecation(%r{`consul\['configuration'\]\['cert_file'\]` has been deprecated.*`consul\['configuration'\]\['tls'\]\['defaults'\]\['cert_file'\]`})
expect_logged_deprecation(%r{`consul\['configuration'\]\['key_file'\]` has been deprecated.*`consul\['configuration'\]\['tls'\]\['defaults'\]\['key_file'\]`})
end
end
end
end
describe 'using deprecated ACL token settings' do
before do
stub_gitlab_rb(
consul: {
enable: true,
configuration: {
acl: {
tokens: {
master: 'foo',
agent_master: 'bar'
}
}
}
}
)
end
it 'generates the configuration file properly' do
expected_output = {
'tokens' => {
'initial_management' => 'foo',
'agent_recovery' => 'bar'
}
}
expect(consul_conf_json['acl']).to eq(expected_output)
end
it 'generates deprecation notices' do
chef_run
expect_logged_deprecation(%r{`consul\['configuration'\]\['acl'\]\['tokens'\]\['master'\]` has been deprecated.*`consul\['configuration'\]\['acl'\]\['tokens'\]\['initial_management'\]`})
expect_logged_deprecation(%r{`consul\['configuration'\]\['acl'\]\['tokens'\]\['agent_master'\]` has been deprecated.*`consul\['configuration'\]\['acl'\]\['tokens'\]\['agent_recovery'\]`})
end
end
context 'log directory and runit group' do
context 'default values' do
before do
stub_gitlab_rb(
consul: {
enable: true,
}
)
end
it_behaves_like 'enabled logged service', 'consul', true, { log_directory_owner: 'gitlab-consul', log_directory_mode: '0755' }
end
context 'custom values' do
before do
stub_gitlab_rb(
consul: {
enable: true,
log_group: 'fugee'
}
)
end
it_behaves_like 'enabled logged service', 'consul', true, { log_directory_owner: 'gitlab-consul', log_group: 'fugee' }
end
end
end