debian-mirror-gitlab/app/services/projects/container_repository/delete_tags_service.rb

102 lines
3.9 KiB
Ruby
Raw Normal View History

2019-12-21 20:55:43 +05:30
# frozen_string_literal: true
module Projects
module ContainerRepository
class DeleteTagsService < BaseService
2020-07-28 23:09:34 +05:30
LOG_DATA_BASE = { service_class: self.to_s }.freeze
2019-12-21 20:55:43 +05:30
def execute(container_repository)
return error('access denied') unless can?(current_user, :destroy_container_image, project)
tag_names = params[:tags]
return error('not tags specified') if tag_names.blank?
2019-12-26 22:10:19 +05:30
smart_delete(container_repository, tag_names)
2019-12-21 20:55:43 +05:30
end
private
2020-03-13 15:44:24 +05:30
# Delete tags by name with a single DELETE request. This is only supported
# by the GitLab Container Registry fork. See
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23325 for details.
def fast_delete(container_repository, tag_names)
deleted_tags = tag_names.select do |name|
container_repository.delete_tag_by_name(name)
end
deleted_tags.any? ? success(deleted: deleted_tags) : error('could not delete tags')
end
2019-12-21 20:55:43 +05:30
# Replace a tag on the registry with a dummy tag.
# This is a hack as the registry doesn't support deleting individual
# tags. This code effectively pushes a dummy image and assigns the tag to it.
# This way when the tag is deleted only the dummy image is affected.
2020-03-13 15:44:24 +05:30
# This is used to preverse compatibility with third-party registries that
# don't support fast delete.
2019-12-21 20:55:43 +05:30
# See https://gitlab.com/gitlab-org/gitlab/issues/15737 for a discussion
2020-03-13 15:44:24 +05:30
def slow_delete(container_repository, tag_names)
2019-12-21 20:55:43 +05:30
# generates the blobs for the dummy image
dummy_manifest = container_repository.client.generate_empty_manifest(container_repository.path)
2019-12-26 22:10:19 +05:30
return error('could not generate manifest') if dummy_manifest.nil?
2019-12-21 20:55:43 +05:30
2020-01-01 13:55:28 +05:30
deleted_tags = replace_tag_manifests(container_repository, dummy_manifest, tag_names)
# Deletes the dummy image
# All created tag digests are the same since they all have the same dummy image.
# a single delete is sufficient to remove all tags with it
2020-03-13 15:44:24 +05:30
if deleted_tags.any? && container_repository.delete_tag_by_digest(deleted_tags.each_value.first)
2020-01-01 13:55:28 +05:30
success(deleted: deleted_tags.keys)
else
error('could not delete tags')
end
end
2020-03-13 15:44:24 +05:30
def smart_delete(container_repository, tag_names)
fast_delete_enabled = Feature.enabled?(:container_registry_fast_tag_delete, default_enabled: true)
2020-07-28 23:09:34 +05:30
response = if fast_delete_enabled && container_repository.client.supports_tag_delete?
fast_delete(container_repository, tag_names)
else
slow_delete(container_repository, tag_names)
end
response.tap { |r| log_response(r, container_repository) }
end
def log_response(response, container_repository)
log_data = LOG_DATA_BASE.merge(
container_repository_id: container_repository.id,
message: 'deleted tags'
)
if response[:status] == :success
log_data[:deleted_tags_count] = response[:deleted].size
log_info(log_data)
2020-03-13 15:44:24 +05:30
else
2020-07-28 23:09:34 +05:30
log_data[:message] = response[:message]
log_error(log_data)
2020-03-13 15:44:24 +05:30
end
end
2020-01-01 13:55:28 +05:30
# update the manifests of the tags with the new dummy image
def replace_tag_manifests(container_repository, dummy_manifest, tag_names)
deleted_tags = {}
2019-12-26 22:10:19 +05:30
tag_names.each do |name|
digest = container_repository.client.put_tag(container_repository.path, name, dummy_manifest)
next unless digest
2020-01-01 13:55:28 +05:30
deleted_tags[name] = digest
2019-12-21 20:55:43 +05:30
end
# make sure the digests are the same (it should always be)
2020-01-01 13:55:28 +05:30
digests = deleted_tags.values.uniq
2019-12-21 20:55:43 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2020-01-01 13:55:28 +05:30
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(ArgumentError.new('multiple tag digests')) if digests.many?
2019-12-21 20:55:43 +05:30
2020-01-01 13:55:28 +05:30
deleted_tags
2019-12-21 20:55:43 +05:30
end
end
end
end