spec/chef/cookbooks/registry/recipes/registry_spec.rb (516 lines of code) (raw):

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 match_array(['gitlab.example.com', '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: match_array(['gitlab.example.com', '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(['gitlab.example.com']) end it 'is reflected in the acme_selfsigned' do expect(chef_run).to create_acme_selfsigned('gitlab.example.com').with( alt_names: ['gitlab.example.com'] ) 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 the registry user and group with the correct parameters' do expect(chef_run).to create_account('Docker registry user and group').with(username: 'registry', groupname: 'registry', shell: '/usr/sbin/nologin', home: '/var/opt/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 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 the registry metadata database is enabled' do let(:database_config) do { "enabled" => true, "host" => "localhost", "port" => 5432, "user" => "postgres", "password" => "postgres", "dbname" => "registry", "sslmode" => "verify-full", "sslcert" => "/path/to/client.crt", "sslkey" => "/path/to/client.key", "sslrootcert" => "/path/to/root.crt", "connecttimeout" => "5s", "draintimeout" => "2m", "preparedstatements" => false, "primary" => "primary.record.fqdn", "pool" => { "maxidle" => 25, "maxopen" => 25, "maxlifetime" => "5m" } } end before { stub_gitlab_rb(registry: { database: database_config }) } it 'creates registry config with database' do expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') .with_content(%r(^database: {"enabled":true)) end end context 'when the registry garbage collector configuration is present' do let(:gc_config) do { "disabled" => false, "maxbackoff" => "24h", "noidlebackoff" => false, "transactiontimeout" => "10s", "reviewafter" => "24h", "manifests" => { "disabled" => false, "interval" => "5s" }, "blobs" => { "disabled" => false, "interval" => "5s", "storagetimeout" => "5s" } } end before { stub_gitlab_rb(registry: { gc: gc_config }) } it 'creates registry config with gc' do expect(chef_run).to render_file('/var/opt/gitlab/registry/config.yml') .with_content(%r(^gc: {"disabled":false)) 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(/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_maxretries: 5, 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(/"maxretries":5/) 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 context 'log directory and runit group' do context 'default values' do before do stub_gitlab_rb({ registry_external_url: 'https://registry.example.com' }) end it_behaves_like 'enabled logged service', 'registry', true, { log_directory_owner: 'registry' } end context 'custom values' do before do stub_gitlab_rb( registry_external_url: 'https://registry.example.com', registry: { enabled: true, log_group: 'fugee' } ) end it_behaves_like 'enabled logged service', 'registry', true, { log_directory_owner: 'registry', log_group: 'fugee' } 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