summaryrefslogtreecommitdiff
path: root/spec/services/dependency_proxy
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 11:59:07 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 11:59:07 +0000
commit8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch)
tree544930fb309b30317ae9797a9683768705d664c4 /spec/services/dependency_proxy
parent4b1de649d0168371549608993deac953eb692019 (diff)
downloadgitlab-ce-8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca.tar.gz
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'spec/services/dependency_proxy')
-rw-r--r--spec/services/dependency_proxy/auth_token_service_spec.rb37
-rw-r--r--spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb83
-rw-r--r--spec/services/dependency_proxy/head_manifest_service_spec.rb44
-rw-r--r--spec/services/dependency_proxy/pull_manifest_service_spec.rb51
4 files changed, 204 insertions, 11 deletions
diff --git a/spec/services/dependency_proxy/auth_token_service_spec.rb b/spec/services/dependency_proxy/auth_token_service_spec.rb
new file mode 100644
index 00000000000..4b96f9d75a9
--- /dev/null
+++ b/spec/services/dependency_proxy/auth_token_service_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe DependencyProxy::AuthTokenService do
+ include DependencyProxyHelpers
+
+ describe '.decoded_token_payload' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:token) { build_jwt(user) }
+
+ subject { described_class.decoded_token_payload(token.encoded) }
+
+ it 'returns the user' do
+ result = subject
+
+ expect(result['user_id']).to eq(user.id)
+ end
+
+ it 'raises an error if the token is expired' do
+ travel_to(Time.zone.now + Auth::DependencyProxyAuthenticationService.token_expire_at + 1.minute) do
+ expect { subject }.to raise_error(JWT::ExpiredSignature)
+ end
+ end
+
+ it 'raises an error if decoding fails' do
+ allow(JWT).to receive(:decode).and_raise(JWT::DecodeError)
+
+ expect { subject }.to raise_error(JWT::DecodeError)
+ end
+
+ it 'raises an error if signature is immature' do
+ allow(JWT).to receive(:decode).and_raise(JWT::ImmatureSignature)
+
+ expect { subject }.to raise_error(JWT::ImmatureSignature)
+ end
+ end
+end
diff --git a/spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb b/spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb
new file mode 100644
index 00000000000..c375e5a2fa3
--- /dev/null
+++ b/spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe DependencyProxy::FindOrCreateManifestService do
+ include DependencyProxyHelpers
+
+ let_it_be(:image) { 'alpine' }
+ let_it_be(:tag) { 'latest' }
+ let_it_be(:dependency_proxy_manifest) { create(:dependency_proxy_manifest, file_name: "#{image}:#{tag}.json") }
+ let(:manifest) { dependency_proxy_manifest.file.read }
+ let(:group) { dependency_proxy_manifest.group }
+ let(:token) { Digest::SHA256.hexdigest('123') }
+ let(:headers) { { 'docker-content-digest' => dependency_proxy_manifest.digest } }
+
+ describe '#execute' do
+ subject { described_class.new(group, image, tag, token).execute }
+
+ context 'when no manifest exists' do
+ let_it_be(:image) { 'new-image' }
+
+ before do
+ stub_manifest_head(image, tag, digest: dependency_proxy_manifest.digest)
+ stub_manifest_download(image, tag, headers: headers)
+ end
+
+ it 'downloads manifest from remote registry if there is no cached one', :aggregate_failures do
+ expect { subject }.to change { group.dependency_proxy_manifests.count }.by(1)
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:manifest]).to be_a(DependencyProxy::Manifest)
+ expect(subject[:manifest]).to be_persisted
+ end
+ end
+
+ context 'when manifest exists' do
+ before do
+ stub_manifest_head(image, tag, digest: dependency_proxy_manifest.digest)
+ end
+
+ shared_examples 'using the cached manifest' do
+ it 'uses cached manifest instead of downloading one', :aggregate_failures do
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:manifest]).to be_a(DependencyProxy::Manifest)
+ expect(subject[:manifest]).to eq(dependency_proxy_manifest)
+ end
+ end
+
+ it_behaves_like 'using the cached manifest'
+
+ context 'when digest is stale' do
+ let(:digest) { 'new-digest' }
+
+ before do
+ stub_manifest_head(image, tag, digest: digest)
+ stub_manifest_download(image, tag, headers: { 'docker-content-digest' => digest })
+ end
+
+ it 'downloads the new manifest and updates the existing record', :aggregate_failures do
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:manifest]).to eq(dependency_proxy_manifest)
+ expect(subject[:manifest].digest).to eq(digest)
+ end
+ end
+
+ context 'failed connection' do
+ before do
+ expect(DependencyProxy::HeadManifestService).to receive(:new).and_raise(Net::OpenTimeout)
+ end
+
+ it_behaves_like 'using the cached manifest'
+
+ context 'and no manifest is cached' do
+ let_it_be(:image) { 'new-image' }
+
+ it 'returns an error', :aggregate_failures do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:http_status]).to eq(503)
+ expect(subject[:message]).to eq('Failed to download the manifest from the external registry')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/dependency_proxy/head_manifest_service_spec.rb b/spec/services/dependency_proxy/head_manifest_service_spec.rb
new file mode 100644
index 00000000000..7c7ebe4d181
--- /dev/null
+++ b/spec/services/dependency_proxy/head_manifest_service_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe DependencyProxy::HeadManifestService do
+ include DependencyProxyHelpers
+
+ let(:image) { 'alpine' }
+ let(:tag) { 'latest' }
+ let(:token) { Digest::SHA256.hexdigest('123') }
+ let(:digest) { '12345' }
+
+ subject { described_class.new(image, tag, token).execute }
+
+ context 'remote request is successful' do
+ before do
+ stub_manifest_head(image, tag, digest: digest)
+ end
+
+ it { expect(subject[:status]).to eq(:success) }
+ it { expect(subject[:digest]).to eq(digest) }
+ end
+
+ context 'remote request is not found' do
+ before do
+ stub_manifest_head(image, tag, status: 404, body: 'Not found')
+ end
+
+ it { expect(subject[:status]).to eq(:error) }
+ it { expect(subject[:http_status]).to eq(404) }
+ it { expect(subject[:message]).to eq('Not found') }
+ end
+
+ context 'net timeout exception' do
+ before do
+ manifest_link = DependencyProxy::Registry.manifest_url(image, tag)
+
+ stub_full_request(manifest_link, method: :head).to_timeout
+ end
+
+ it { expect(subject[:status]).to eq(:error) }
+ it { expect(subject[:http_status]).to eq(599) }
+ it { expect(subject[:message]).to eq('execution expired') }
+ end
+end
diff --git a/spec/services/dependency_proxy/pull_manifest_service_spec.rb b/spec/services/dependency_proxy/pull_manifest_service_spec.rb
index 030ed9c001b..b760839d1fb 100644
--- a/spec/services/dependency_proxy/pull_manifest_service_spec.rb
+++ b/spec/services/dependency_proxy/pull_manifest_service_spec.rb
@@ -8,26 +8,43 @@ RSpec.describe DependencyProxy::PullManifestService do
let(:tag) { '3.9' }
let(:token) { Digest::SHA256.hexdigest('123') }
let(:manifest) { { foo: 'bar' }.to_json }
+ let(:digest) { '12345' }
+ let(:headers) { { 'docker-content-digest' => digest } }
- subject { described_class.new(image, tag, token).execute }
+ subject { described_class.new(image, tag, token).execute_with_manifest(&method(:check_response)) }
context 'remote request is successful' do
before do
- stub_manifest_download(image, tag)
+ stub_manifest_download(image, tag, headers: headers)
end
- it { expect(subject[:status]).to eq(:success) }
- it { expect(subject[:manifest]).to eq(manifest) }
+ it 'successfully returns the manifest' do
+ def check_response(response)
+ response[:file].rewind
+
+ expect(response[:status]).to eq(:success)
+ expect(response[:file].read).to eq(manifest)
+ expect(response[:digest]).to eq(digest)
+ end
+
+ subject
+ end
end
context 'remote request is not found' do
before do
- stub_manifest_download(image, tag, 404, 'Not found')
+ stub_manifest_download(image, tag, status: 404, body: 'Not found')
end
- it { expect(subject[:status]).to eq(:error) }
- it { expect(subject[:http_status]).to eq(404) }
- it { expect(subject[:message]).to eq('Not found') }
+ it 'returns a 404 not found error' do
+ def check_response(response)
+ expect(response[:status]).to eq(:error)
+ expect(response[:http_status]).to eq(404)
+ expect(response[:message]).to eq('Not found')
+ end
+
+ subject
+ end
end
context 'net timeout exception' do
@@ -37,8 +54,20 @@ RSpec.describe DependencyProxy::PullManifestService do
stub_full_request(manifest_link).to_timeout
end
- it { expect(subject[:status]).to eq(:error) }
- it { expect(subject[:http_status]).to eq(599) }
- it { expect(subject[:message]).to eq('execution expired') }
+ it 'returns a 599 error' do
+ def check_response(response)
+ expect(response[:status]).to eq(:error)
+ expect(response[:http_status]).to eq(599)
+ expect(response[:message]).to eq('execution expired')
+ end
+
+ subject
+ end
+ end
+
+ context 'no block is given' do
+ subject { described_class.new(image, tag, token).execute_with_manifest }
+
+ it { expect { subject }.to raise_error(ArgumentError, 'Block must be provided') }
end
end