summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb176
-rw-r--r--spec/features/groups/dependency_proxy_for_containers_spec.rb108
-rw-r--r--spec/lib/gitlab/health_checks/probes/collection_spec.rb1
-rw-r--r--spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb8
-rw-r--r--spec/lib/gitlab/instrumentation/redis_spec.rb3
-rw-r--r--spec/lib/gitlab/middleware/multipart/handler_spec.rb1
-rw-r--r--spec/lib/gitlab/redis/rate_limiting_spec.rb50
-rw-r--r--spec/lib/gitlab/redis/sessions_spec.rb7
-rw-r--r--spec/lib/gitlab/redis/trace_chunks_spec.rb50
-rw-r--r--spec/support/redis.rb8
-rw-r--r--spec/support/redis/redis_helpers.rb5
-rw-r--r--spec/support/redis/redis_new_instance_shared_examples.rb55
12 files changed, 339 insertions, 133 deletions
diff --git a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
index 7415c2860c8..fa402d556c7 100644
--- a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
+++ b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Groups::DependencyProxyForContainersController do
include HttpBasicAuthHelpers
include DependencyProxyHelpers
+ include WorkhorseHelpers
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:group) { create(:group, :private) }
@@ -242,16 +243,9 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
describe 'GET #blob' do
- let_it_be(:blob) { create(:dependency_proxy_blob) }
+ let(:blob) { create(:dependency_proxy_blob, group: group) }
let(:blob_sha) { blob.file_name.sub('.gz', '') }
- let(:blob_response) { { status: :success, blob: blob, from_cache: false } }
-
- before do
- allow_next_instance_of(DependencyProxy::FindOrCreateBlobService) do |instance|
- allow(instance).to receive(:execute).and_return(blob_response)
- end
- end
subject { get_blob }
@@ -264,40 +258,31 @@ RSpec.describe Groups::DependencyProxyForContainersController do
it_behaves_like 'without permission'
it_behaves_like 'feature flag disabled with private group'
- context 'remote blob request fails' do
- let(:blob_response) do
- {
- status: :error,
- http_status: 400,
- message: ''
- }
- end
-
- before do
- group.add_guest(user)
- end
-
- it 'proxies status from the remote blob request', :aggregate_failures do
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(response.body).to be_empty
- end
- end
-
context 'a valid user' do
before do
group.add_guest(user)
end
it_behaves_like 'a successful blob pull'
- it_behaves_like 'a package tracking event', described_class.name, 'pull_blob'
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache'
- context 'with a cache entry' do
- let(:blob_response) { { status: :success, blob: blob, from_cache: true } }
+ context 'when cache entry does not exist' do
+ let(:blob_sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' }
- it_behaves_like 'returning response status', :success
- it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache'
+ it 'returns Workhorse send-dependency instructions' do
+ subject
+
+ send_data_type, send_data = workhorse_send_data
+ header, url = send_data.values_at('Header', 'Url')
+
+ expect(send_data_type).to eq('send-dependency')
+ expect(header).to eq("Authorization" => ["Bearer abcd1234"])
+ expect(url).to eq(DependencyProxy::Registry.blob_url('alpine', blob_sha))
+ expect(response.headers['Content-Type']).to eq('application/gzip')
+ expect(response.headers['Content-Disposition']).to eq(
+ ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: blob.file_name)
+ )
+ end
end
end
@@ -319,6 +304,74 @@ RSpec.describe Groups::DependencyProxyForContainersController do
it_behaves_like 'a successful blob pull'
end
end
+
+ context 'when dependency_proxy_workhorse disabled' do
+ let(:blob_response) { { status: :success, blob: blob, from_cache: false } }
+
+ before do
+ stub_feature_flags(dependency_proxy_workhorse: false)
+
+ allow_next_instance_of(DependencyProxy::FindOrCreateBlobService) do |instance|
+ allow(instance).to receive(:execute).and_return(blob_response)
+ end
+ end
+
+ context 'remote blob request fails' do
+ let(:blob_response) do
+ {
+ status: :error,
+ http_status: 400,
+ message: ''
+ }
+ end
+
+ before do
+ group.add_guest(user)
+ end
+
+ it 'proxies status from the remote blob request', :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(response.body).to be_empty
+ end
+ end
+
+ context 'a valid user' do
+ before do
+ group.add_guest(user)
+ end
+
+ it_behaves_like 'a successful blob pull'
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_blob'
+
+ context 'with a cache entry' do
+ let(:blob_response) { { status: :success, blob: blob, from_cache: true } }
+
+ it_behaves_like 'returning response status', :success
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache'
+ end
+ end
+
+ context 'a valid deploy token' do
+ let_it_be(:user) { create(:deploy_token, :group, :dependency_proxy_scopes) }
+ let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
+
+ it_behaves_like 'a successful blob pull'
+
+ context 'pulling from a subgroup' do
+ let_it_be_with_reload(:parent_group) { create(:group) }
+ let_it_be_with_reload(:group) { create(:group, parent: parent_group) }
+
+ before do
+ parent_group.create_dependency_proxy_setting!(enabled: true)
+ group_deploy_token.update_column(:group_id, parent_group.id)
+ end
+
+ it_behaves_like 'a successful blob pull'
+ end
+ end
+ end
end
it_behaves_like 'not found when disabled'
@@ -328,6 +381,61 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
end
+ describe 'GET #authorize_upload_blob' do
+ let(:blob_sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' }
+
+ subject(:authorize_upload_blob) do
+ request.headers.merge!(workhorse_internal_api_request_header)
+
+ get :authorize_upload_blob, params: { group_id: group.to_param, image: 'alpine', sha: blob_sha }
+ end
+
+ it_behaves_like 'without permission'
+
+ context 'with a valid user' do
+ before do
+ group.add_guest(user)
+ end
+
+ it 'sends Workhorse file upload instructions', :aggregate_failures do
+ authorize_upload_blob
+
+ expect(response.headers['Content-Type']).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(json_response['TempPath']).to eq(DependencyProxy::FileUploader.workhorse_local_upload_path)
+ end
+ end
+ end
+
+ describe 'GET #upload_blob' do
+ let(:blob_sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' }
+ let(:file) { fixture_file_upload("spec/fixtures/dependency_proxy/#{blob_sha}.gz", 'application/gzip') }
+
+ subject do
+ request.headers.merge!(workhorse_internal_api_request_header)
+
+ get :upload_blob, params: {
+ group_id: group.to_param,
+ image: 'alpine',
+ sha: blob_sha,
+ file: file
+ }
+ end
+
+ it_behaves_like 'without permission'
+
+ context 'with a valid user' do
+ before do
+ group.add_guest(user)
+
+ expect_next_found_instance_of(Group) do |instance|
+ expect(instance).to receive_message_chain(:dependency_proxy_blobs, :create!)
+ end
+ end
+
+ it_behaves_like 'a package tracking event', described_class.name, 'pull_blob'
+ end
+ end
+
def enable_dependency_proxy
group.create_dependency_proxy_setting!(enabled: true)
end
diff --git a/spec/features/groups/dependency_proxy_for_containers_spec.rb b/spec/features/groups/dependency_proxy_for_containers_spec.rb
new file mode 100644
index 00000000000..a4cd6d0f503
--- /dev/null
+++ b/spec/features/groups/dependency_proxy_for_containers_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Group Dependency Proxy for containers', :js do
+ include DependencyProxyHelpers
+
+ include_context 'file upload requests helpers'
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' }
+ let_it_be(:content) { fixture_file_upload("spec/fixtures/dependency_proxy/#{sha}.gz").read }
+
+ let(:image) { 'alpine' }
+ let(:url) { capybara_url("/v2/#{group.full_path}/dependency_proxy/containers/#{image}/blobs/sha256:#{sha}") }
+ let(:token) { 'token' }
+ let(:headers) { { 'Authorization' => "Bearer #{build_jwt(user).encoded}" } }
+
+ subject do
+ HTTParty.get(url, headers: headers)
+ end
+
+ def run_server(handler)
+ default_server = Capybara.server
+
+ Capybara.server = Capybara.servers[:puma]
+ server = Capybara::Server.new(handler)
+ server.boot
+ server
+ ensure
+ Capybara.server = default_server
+ end
+
+ let_it_be(:external_server) do
+ handler = lambda do |env|
+ if env['REQUEST_PATH'] == '/token'
+ [200, {}, [{ token: 'token' }.to_json]]
+ else
+ [200, {}, [content]]
+ end
+ end
+
+ run_server(handler)
+ end
+
+ before do
+ stub_application_setting(allow_local_requests_from_web_hooks_and_services: true)
+ stub_config(dependency_proxy: { enabled: true })
+ group.add_developer(user)
+
+ stub_const("DependencyProxy::Registry::AUTH_URL", external_server.base_url)
+ stub_const("DependencyProxy::Registry::LIBRARY_URL", external_server.base_url)
+ end
+
+ shared_examples 'responds with the file' do
+ it 'sends file' do
+ expect(subject.code).to eq(200)
+ expect(subject.body).to eq(content)
+ expect(subject.headers.to_h).to include(
+ "content-type" => ["application/gzip"],
+ "content-disposition" => ["attachment; filename=\"#{sha}.gz\"; filename*=UTF-8''#{sha}.gz"],
+ "content-length" => ["32"]
+ )
+ end
+ end
+
+ shared_examples 'caches the file' do
+ it 'caches the file' do
+ expect { subject }.to change {
+ group.dependency_proxy_blobs.count
+ }.from(0).to(1)
+
+ expect(subject.code).to eq(200)
+ expect(group.dependency_proxy_blobs.first.file.read).to eq(content)
+ end
+ end
+
+ context 'fetching a blob' do
+ context 'when the blob is cached for the group' do
+ let!(:dependency_proxy_blob) { create(:dependency_proxy_blob, group: group) }
+
+ it_behaves_like 'responds with the file'
+
+ context 'dependency_proxy_workhorse feature flag disabled' do
+ before do
+ stub_feature_flags({ dependency_proxy_workhorse: false })
+ end
+
+ it_behaves_like 'responds with the file'
+ end
+ end
+ end
+
+ context 'when the blob must be downloaded' do
+ it_behaves_like 'responds with the file'
+ it_behaves_like 'caches the file'
+
+ context 'dependency_proxy_workhorse feature flag disabled' do
+ before do
+ stub_feature_flags({ dependency_proxy_workhorse: false })
+ end
+
+ it_behaves_like 'responds with the file'
+ it_behaves_like 'caches the file'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/health_checks/probes/collection_spec.rb b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
index 401ffee9c28..741c45d953c 100644
--- a/spec/lib/gitlab/health_checks/probes/collection_spec.rb
+++ b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe Gitlab::HealthChecks::Probes::Collection do
Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::Redis::TraceChunksCheck,
Gitlab::HealthChecks::Redis::RateLimitingCheck,
+ Gitlab::HealthChecks::Redis::SessionsCheck,
Gitlab::HealthChecks::GitalyCheck
]
end
diff --git a/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb b/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb
new file mode 100644
index 00000000000..82b3b33ec0a
--- /dev/null
+++ b/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_relative '../simple_check_shared'
+
+RSpec.describe Gitlab::HealthChecks::Redis::SessionsCheck do
+ include_examples 'simple_check', 'redis_sessions_ping', 'RedisSessions', 'PONG'
+end
diff --git a/spec/lib/gitlab/instrumentation/redis_spec.rb b/spec/lib/gitlab/instrumentation/redis_spec.rb
index 0da44dfb8d8..900a079cdd2 100644
--- a/spec/lib/gitlab/instrumentation/redis_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_spec.rb
@@ -77,7 +77,8 @@ RSpec.describe Gitlab::Instrumentation::Redis do
details_row.merge(storage: 'Queues'),
details_row.merge(storage: 'SharedState'),
details_row.merge(storage: 'TraceChunks'),
- details_row.merge(storage: 'RateLimiting'))
+ details_row.merge(storage: 'RateLimiting'),
+ details_row.merge(storage: 'Sessions'))
end
end
end
diff --git a/spec/lib/gitlab/middleware/multipart/handler_spec.rb b/spec/lib/gitlab/middleware/multipart/handler_spec.rb
index aac3f00defe..53b59b042e2 100644
--- a/spec/lib/gitlab/middleware/multipart/handler_spec.rb
+++ b/spec/lib/gitlab/middleware/multipart/handler_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe Gitlab::Middleware::Multipart::Handler do
::Gitlab.config.uploads.storage_path,
::JobArtifactUploader.workhorse_upload_path,
::LfsObjectUploader.workhorse_upload_path,
+ ::DependencyProxy::FileUploader.workhorse_upload_path,
File.join(Rails.root, 'public/uploads/tmp')
]
end
diff --git a/spec/lib/gitlab/redis/rate_limiting_spec.rb b/spec/lib/gitlab/redis/rate_limiting_spec.rb
index f15aa71a52d..e79c070df93 100644
--- a/spec/lib/gitlab/redis/rate_limiting_spec.rb
+++ b/spec/lib/gitlab/redis/rate_limiting_spec.rb
@@ -3,53 +3,5 @@
require 'spec_helper'
RSpec.describe Gitlab::Redis::RateLimiting do
- let(:instance_specific_config_file) { "config/redis.rate_limiting.yml" }
- let(:environment_config_file_name) { "GITLAB_REDIS_RATE_LIMITING_CONFIG_FILE" }
- let(:cache_config_file) { nil }
-
- before do
- allow(Gitlab::Redis::Cache).to receive(:config_file_name).and_return(cache_config_file)
- end
-
- include_examples "redis_shared_examples"
-
- describe '.config_file_name' do
- subject { described_class.config_file_name }
-
- let(:rails_root) { Dir.mktmpdir('redis_shared_examples') }
-
- before do
- # Undo top-level stub of config_file_name because we are testing that method now.
- allow(described_class).to receive(:config_file_name).and_call_original
-
- allow(described_class).to receive(:rails_root).and_return(rails_root)
- FileUtils.mkdir_p(File.join(rails_root, 'config'))
- end
-
- after do
- FileUtils.rm_rf(rails_root)
- end
-
- context 'when there is only a resque.yml' do
- before do
- FileUtils.touch(File.join(rails_root, 'config/resque.yml'))
- end
-
- it { expect(subject).to eq("#{rails_root}/config/resque.yml") }
-
- context 'and there is a global env override' do
- before do
- stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
- end
-
- it { expect(subject).to eq('global override') }
-
- context 'and Cache has a different config file' do
- let(:cache_config_file) { 'cache config file' }
-
- it { expect(subject).to eq('cache config file') }
- end
- end
- end
- end
+ include_examples "redis_new_instance_shared_examples", 'rate_limiting', Gitlab::Redis::Cache
end
diff --git a/spec/lib/gitlab/redis/sessions_spec.rb b/spec/lib/gitlab/redis/sessions_spec.rb
new file mode 100644
index 00000000000..7e239c08e9f
--- /dev/null
+++ b/spec/lib/gitlab/redis/sessions_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Redis::Sessions do
+ include_examples "redis_new_instance_shared_examples", 'sessions', Gitlab::Redis::SharedState
+end
diff --git a/spec/lib/gitlab/redis/trace_chunks_spec.rb b/spec/lib/gitlab/redis/trace_chunks_spec.rb
index e974dc519d6..bb3c3089430 100644
--- a/spec/lib/gitlab/redis/trace_chunks_spec.rb
+++ b/spec/lib/gitlab/redis/trace_chunks_spec.rb
@@ -3,53 +3,5 @@
require 'spec_helper'
RSpec.describe Gitlab::Redis::TraceChunks do
- let(:instance_specific_config_file) { "config/redis.trace_chunks.yml" }
- let(:environment_config_file_name) { "GITLAB_REDIS_TRACE_CHUNKS_CONFIG_FILE" }
- let(:shared_state_config_file) { nil }
-
- before do
- allow(Gitlab::Redis::SharedState).to receive(:config_file_name).and_return(shared_state_config_file)
- end
-
- include_examples "redis_shared_examples"
-
- describe '.config_file_name' do
- subject { described_class.config_file_name }
-
- let(:rails_root) { Dir.mktmpdir('redis_shared_examples') }
-
- before do
- # Undo top-level stub of config_file_name because we are testing that method now.
- allow(described_class).to receive(:config_file_name).and_call_original
-
- allow(described_class).to receive(:rails_root).and_return(rails_root)
- FileUtils.mkdir_p(File.join(rails_root, 'config'))
- end
-
- after do
- FileUtils.rm_rf(rails_root)
- end
-
- context 'when there is only a resque.yml' do
- before do
- FileUtils.touch(File.join(rails_root, 'config/resque.yml'))
- end
-
- it { expect(subject).to eq("#{rails_root}/config/resque.yml") }
-
- context 'and there is a global env override' do
- before do
- stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
- end
-
- it { expect(subject).to eq('global override') }
-
- context 'and SharedState has a different config file' do
- let(:shared_state_config_file) { 'shared state config file' }
-
- it { expect(subject).to eq('shared state config file') }
- end
- end
- end
- end
+ include_examples "redis_new_instance_shared_examples", 'trace_chunks', Gitlab::Redis::SharedState
end
diff --git a/spec/support/redis.rb b/spec/support/redis.rb
index 946c8685741..421079af8e0 100644
--- a/spec/support/redis.rb
+++ b/spec/support/redis.rb
@@ -46,4 +46,12 @@ RSpec.configure do |config|
redis_rate_limiting_cleanup!
end
+
+ config.around(:each, :clean_gitlab_redis_sessions) do |example|
+ redis_sessions_cleanup!
+
+ example.run
+
+ redis_sessions_cleanup!
+ end
end
diff --git a/spec/support/redis/redis_helpers.rb b/spec/support/redis/redis_helpers.rb
index bf52da5d6f2..f27d873eb31 100644
--- a/spec/support/redis/redis_helpers.rb
+++ b/spec/support/redis/redis_helpers.rb
@@ -27,4 +27,9 @@ module RedisHelpers
def redis_rate_limiting_cleanup!
Gitlab::Redis::RateLimiting.with(&:flushdb)
end
+
+ # Usage: session state
+ def redis_sessions_cleanup!
+ Gitlab::Redis::Sessions.with(&:flushdb)
+ end
end
diff --git a/spec/support/redis/redis_new_instance_shared_examples.rb b/spec/support/redis/redis_new_instance_shared_examples.rb
new file mode 100644
index 00000000000..e9b1e3e4da1
--- /dev/null
+++ b/spec/support/redis/redis_new_instance_shared_examples.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples "redis_new_instance_shared_examples" do |name, fallback_class|
+ let(:instance_specific_config_file) { "config/redis.#{name}.yml" }
+ let(:environment_config_file_name) { "GITLAB_REDIS_#{name.upcase}_CONFIG_FILE" }
+ let(:fallback_config_file) { nil }
+
+ before do
+ allow(fallback_class).to receive(:config_file_name).and_return(fallback_config_file)
+ end
+
+ include_examples "redis_shared_examples"
+
+ describe '.config_file_name' do
+ subject { described_class.config_file_name }
+
+ let(:rails_root) { Dir.mktmpdir('redis_shared_examples') }
+
+ before do
+ # Undo top-level stub of config_file_name because we are testing that method now.
+ allow(described_class).to receive(:config_file_name).and_call_original
+
+ allow(described_class).to receive(:rails_root).and_return(rails_root)
+ FileUtils.mkdir_p(File.join(rails_root, 'config'))
+ end
+
+ after do
+ FileUtils.rm_rf(rails_root)
+ end
+
+ context 'when there is only a resque.yml' do
+ before do
+ FileUtils.touch(File.join(rails_root, 'config/resque.yml'))
+ end
+
+ it { expect(subject).to eq("#{rails_root}/config/resque.yml") }
+
+ context 'and there is a global env override' do
+ before do
+ stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
+ end
+
+ it { expect(subject).to eq('global override') }
+
+ context "and #{fallback_class.name.demodulize} has a different config file" do
+ let(:fallback_config_file) { 'fallback config file' }
+
+ it { expect(subject).to eq('fallback config file') }
+ end
+ end
+ end
+ end
+end