Add dry-run option

This will help us validate and debug the prune command.

Change-Id: I54ba30e1963593762e1e1435bc7e67e7eb637d3e
This commit is contained in:
James E. Blair 2024-11-06 14:49:09 -08:00
parent 404b903c77
commit 66bf00a416
2 changed files with 16 additions and 11 deletions

View File

@ -1,5 +1,5 @@
# Copyright 2019 Red Hat, Inc. # Copyright 2019 Red Hat, Inc.
# Copyright 2021 Acme Gating, LLC # Copyright 2021, 2024 Acme Gating, LLC
# #
# This module is free software: you can redistribute it and/or modify # This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -553,8 +553,8 @@ class RegistryServer:
# same host/port settings. # same host/port settings.
cherrypy.server.httpserver = None cherrypy.server.httpserver = None
def prune(self): def prune(self, dry_run):
self.store.prune() self.store.prune(dry_run)
def main(): def main():
@ -566,6 +566,9 @@ def main():
parser.add_argument('-d', dest='debug', parser.add_argument('-d', dest='debug',
help='Debug log level', help='Debug log level',
action='store_true') action='store_true')
parser.add_argument('--dry-run', dest='dry_run',
help='Do not actually delete anything when pruning',
action='store_true')
parser.add_argument('command', parser.add_argument('command',
nargs='?', nargs='?',
help='Command: serve, prune', help='Command: serve, prune',
@ -591,7 +594,7 @@ def main():
s.start() s.start()
cherrypy.engine.block() cherrypy.engine.block()
elif args.command == 'prune': elif args.command == 'prune':
s.prune() s.prune(args.dry_run)
else: else:
print("Unknown command: %s", args.command) print("Unknown command: %s", args.command)
sys.exit(1) sys.exit(1)

View File

@ -258,7 +258,7 @@ class Storage:
path = os.path.join(namespace, 'repos', repo, 'manifests') path = os.path.join(namespace, 'repos', repo, 'manifests')
return self.backend.list_objects(path) return self.backend.list_objects(path)
def prune(self): def prune(self, dry_run):
"""Prune the registry """Prune the registry
Prune all namespaces in the registry according to configured Prune all namespaces in the registry according to configured
@ -273,13 +273,14 @@ class Storage:
for namespace in self.backend.list_objects(''): for namespace in self.backend.list_objects(''):
uploadpath = os.path.join(namespace.path, 'uploads/') uploadpath = os.path.join(namespace.path, 'uploads/')
for upload in self.backend.list_objects(uploadpath): for upload in self.backend.list_objects(uploadpath):
self._prune(upload, upload_target) self._prune(upload, upload_target, dry_run)
if not manifest_target: if not manifest_target:
continue continue
repopath = os.path.join(namespace.path, 'repos/') repopath = os.path.join(namespace.path, 'repos/')
kept_manifests = [] kept_manifests = []
for repo in self.backend.list_objects(repopath): for repo in self.backend.list_objects(repopath):
kept_manifests.extend(self._prune(repo, manifest_target)) kept_manifests.extend(
self._prune(repo, manifest_target, dry_run))
# mark/sweep manifest blobs # mark/sweep manifest blobs
layers = set() layers = set()
for manifest in kept_manifests: for manifest in kept_manifests:
@ -290,7 +291,7 @@ class Storage:
blobpath = os.path.join(namespace.path, 'blobs/') blobpath = os.path.join(namespace.path, 'blobs/')
for blob in self.backend.list_objects(blobpath): for blob in self.backend.list_objects(blobpath):
if blob.name not in layers: if blob.name not in layers:
self._prune(blob, upload_target) self._prune(blob, upload_target, dry_run)
def _get_layers_from_manifest(self, namespace, path): def _get_layers_from_manifest(self, namespace, path):
self.log.debug('Get layers %s', path) self.log.debug('Get layers %s', path)
@ -310,14 +311,15 @@ class Storage:
layers.append(layer['digest']) layers.append(layer['digest'])
return layers return layers
def _prune(self, root_obj, target): def _prune(self, root_obj, target, dry_run):
kept = [] kept = []
if root_obj.isdir: if root_obj.isdir:
for obj in self.backend.list_objects(root_obj.path): for obj in self.backend.list_objects(root_obj.path):
kept.extend(self._prune(obj, target)) kept.extend(self._prune(obj, target, dry_run))
if not kept and root_obj.ctime < target: if not kept and root_obj.ctime < target:
self.log.debug('Prune %s', root_obj.path) self.log.debug('Prune %s', root_obj.path)
self.backend.delete_object(root_obj.path) if not dry_run:
self.backend.delete_object(root_obj.path)
else: else:
self.log.debug('Keep %s', root_obj.path) self.log.debug('Keep %s', root_obj.path)
kept.append(root_obj) kept.append(root_obj)