debian-mirror-gitlab/spec/services/projects/container_repository/delete_tags_service_spec.rb
2020-03-13 15:44:24 +05:30

254 lines
9.2 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
describe Projects::ContainerRepository::DeleteTagsService do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :private) }
let_it_be(:repository) { create(:container_repository, :root, project: project) }
let(:params) { { tags: tags } }
let(:service) { described_class.new(project, user, params) }
before do
stub_container_registry_config(enabled: true,
api_url: 'http://registry.gitlab',
host_port: 'registry.gitlab')
stub_container_registry_tags(
repository: repository.path,
tags: %w(latest A Ba Bb C D E))
end
describe '#execute' do
let(:tags) { %w[A] }
subject { service.execute(repository) }
context 'without permissions' do
it { is_expected.to include(status: :error) }
end
context 'with permissions' do
before do
project.add_developer(user)
end
context 'when the registry supports fast delete' do
context 'and the feature is enabled' do
let_it_be(:project) { create(:project, :private) }
let_it_be(:repository) { create(:container_repository, :root, project: project) }
before do
allow(repository.client).to receive(:supports_tag_delete?).and_return(true)
end
context 'with tags to delete' do
let_it_be(:tags) { %w[A Ba] }
it 'deletes the tags by name' do
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/A")
.to_return(status: 200, body: "")
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/Ba")
.to_return(status: 200, body: "")
expect_delete_tag_by_name('A')
expect_delete_tag_by_name('Ba')
is_expected.to include(status: :success)
end
it 'succeeds when tag delete returns 404' do
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/A")
.to_return(status: 200, body: "")
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/Ba")
.to_return(status: 404, body: "")
is_expected.to include(status: :success)
end
context 'with failures' do
context 'when the delete request fails' do
before do
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/A")
.to_return(status: 500, body: "")
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/Ba")
.to_return(status: 500, body: "")
end
it { is_expected.to include(status: :error) }
end
end
end
context 'when no params are specified' do
let_it_be(:params) { {} }
it 'does not remove anything' do
expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_name)
is_expected.to include(status: :error)
end
end
context 'with empty tags' do
let_it_be(:tags) { [] }
it 'does not remove anything' do
expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_name)
is_expected.to include(status: :error)
end
end
end
context 'and the feature is disabled' do
before do
stub_feature_flags(container_registry_fast_tag_delete: false)
end
it 'fallbacks to slow delete' do
expect(service).not_to receive(:fast_delete)
expect(service).to receive(:slow_delete).with(repository, tags)
subject
end
end
end
context 'when the registry does not support fast delete' do
let_it_be(:project) { create(:project, :private) }
let_it_be(:repository) { create(:container_repository, :root, project: project) }
before do
stub_tag_digest('latest', 'sha256:configA')
stub_tag_digest('A', 'sha256:configA')
stub_tag_digest('Ba', 'sha256:configB')
allow(repository.client).to receive(:supports_tag_delete?).and_return(false)
end
context 'when no params are specified' do
let_it_be(:params) { {} }
it 'does not remove anything' do
expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_digest)
is_expected.to include(status: :error)
end
end
context 'with empty tags' do
let_it_be(:tags) { [] }
it 'does not remove anything' do
expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_digest)
is_expected.to include(status: :error)
end
end
context 'with tags to delete' do
let_it_be(:tags) { %w[A Ba] }
it 'deletes the tags using a dummy image' do
stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3')
stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A")
.to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' })
stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba")
.to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' })
expect_delete_tag_by_digest('sha256:dummy')
is_expected.to include(status: :success)
end
it 'succeeds when tag delete returns 404' do
stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3')
stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A")
.to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' })
stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba")
.to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' })
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/manifests/sha256:dummy")
.to_return(status: 404, body: "", headers: {})
is_expected.to include(status: :success)
end
context 'with failures' do
context 'when the dummy manifest generation fails' do
before do
stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3', success: false)
end
it { is_expected.to include(status: :error) }
end
context 'when updating the tags fails' do
before do
stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3')
stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A")
.to_return(status: 500, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' })
stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba")
.to_return(status: 500, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' })
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/manifests/sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3")
.to_return(status: 200, body: "", headers: {})
end
it { is_expected.to include(status: :error) }
end
end
end
end
end
end
private
def stub_tag_digest(tag, digest)
stub_request(:head, "http://registry.gitlab/v2/#{repository.path}/manifests/#{tag}")
.to_return(status: 200, body: "", headers: { 'docker-content-digest' => digest })
end
def stub_digest_config(digest, created_at)
allow_any_instance_of(ContainerRegistry::Client)
.to receive(:blob)
.with(repository.path, digest, nil) do
{ 'created' => created_at.to_datetime.rfc3339 }.to_json if created_at
end
end
def stub_upload(content, digest, success: true)
expect_any_instance_of(ContainerRegistry::Client)
.to receive(:upload_blob)
.with(repository.path, content, digest) { double(success?: success ) }
end
def expect_delete_tag_by_digest(digest)
expect_any_instance_of(ContainerRegistry::Client)
.to receive(:delete_repository_tag_by_digest)
.with(repository.path, digest) { true }
expect_any_instance_of(ContainerRegistry::Client)
.not_to receive(:delete_repository_tag_by_name)
end
def expect_delete_tag_by_name(name)
expect_any_instance_of(ContainerRegistry::Client)
.to receive(:delete_repository_tag_by_name)
.with(repository.path, name) { true }
expect_any_instance_of(ContainerRegistry::Client)
.not_to receive(:delete_repository_tag_by_digest)
end
end