Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/projects/container_repository/third_party/delete_tags_service.rb')
-rw-r--r--app/services/projects/container_repository/third_party/delete_tags_service.rb64
1 files changed, 64 insertions, 0 deletions
diff --git a/app/services/projects/container_repository/third_party/delete_tags_service.rb b/app/services/projects/container_repository/third_party/delete_tags_service.rb
new file mode 100644
index 00000000000..6504172109e
--- /dev/null
+++ b/app/services/projects/container_repository/third_party/delete_tags_service.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module Projects
+ module ContainerRepository
+ module ThirdParty
+ class DeleteTagsService
+ include BaseServiceUtility
+
+ def initialize(container_repository, tag_names)
+ @container_repository = container_repository
+ @tag_names = tag_names
+ end
+
+ # 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.
+ # This is used to preverse compatibility with third-party registries that
+ # don't support fast delete.
+ # See https://gitlab.com/gitlab-org/gitlab/issues/15737 for a discussion
+ def execute
+ return success(deleted: []) if @tag_names.empty?
+
+ # generates the blobs for the dummy image
+ dummy_manifest = @container_repository.client.generate_empty_manifest(@container_repository.path)
+ return error('could not generate manifest') if dummy_manifest.nil?
+
+ deleted_tags = replace_tag_manifests(dummy_manifest)
+
+ # 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
+ if deleted_tags.any? && @container_repository.delete_tag_by_digest(deleted_tags.each_value.first)
+ success(deleted: deleted_tags.keys)
+ else
+ error('could not delete tags')
+ end
+ end
+
+ private
+
+ # update the manifests of the tags with the new dummy image
+ def replace_tag_manifests(dummy_manifest)
+ deleted_tags = {}
+
+ @tag_names.each do |name|
+ digest = @container_repository.client.put_tag(@container_repository.path, name, dummy_manifest)
+ next unless digest
+
+ deleted_tags[name] = digest
+ end
+
+ # make sure the digests are the same (it should always be)
+ digests = deleted_tags.values.uniq
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(ArgumentError.new('multiple tag digests')) if digests.many?
+
+ deleted_tags
+ end
+ end
+ end
+ end
+end