Merge pull request #48 from MightyNerdEric/keep_by_hours

Keep by hours
This commit is contained in:
Ivan Pavlushin
2018-06-11 19:59:28 +02:00
committed by GitHub
3 changed files with 96 additions and 21 deletions

View File

@@ -113,12 +113,12 @@ The following command would delete all tags containing "snapshot-" and beginning
As one manifest may be referenced by more than one tag, you may add tags, whose manifests should NOT be deleted. As one manifest may be referenced by more than one tag, you may add tags, whose manifests should NOT be deleted.
A tag that would otherwise be deleted, but whose manifest references one of those "kept" tags, is spared for deletion. A tag that would otherwise be deleted, but whose manifest references one of those "kept" tags, is spared for deletion.
In the following case, all tags beginning with "snapshot-" will be deleted, safe those whose manifest point to "stable" or "latest" In the following case, all tags beginning with "snapshot-" will be deleted, save those whose manifest point to "stable" or "latest":
``` ```
registry.py -l user:pass -r https://example.com:5000 --delete --tags-like "snapshot-" --keep-tags "stable" "latest" registry.py -l user:pass -r https://example.com:5000 --delete --tags-like "snapshot-" --keep-tags "stable" "latest"
``` ```
The last parameter is also available as regexp option with "--keep-tags-like". The last parameter is also available as regexp option with `--keep-tags-like`.
Delete all tags for particular image (e.g. delete all ubuntu tags): Delete all tags for particular image (e.g. delete all ubuntu tags):
@@ -131,10 +131,15 @@ Delete all tags for all images (do you really want to do it?):
registry.py -l user:pass -r https://example.com:5000 --delete-all --dry-run registry.py -l user:pass -r https://example.com:5000 --delete-all --dry-run
``` ```
Delete all tags by age in hours for the particular image (e.g. older than 24 hours, with --keep-tags and --keep-tags-like options, --dry-run for safe). Delete all tags by age in hours for the particular image (e.g. older than 24 hours, with `--keep-tags` and `--keep-tags-like` options, `--dry-run` for safe).
``` ```
registry.py -r https://example.com:5000 -i api-docs-origin/master --dry-run --delete-by-hours 24 --keep-tags c59c02c25f023263fd4b5d43fc1ff653f08b3d4x --keep-tags-like late registry.py -r https://example.com:5000 -i api-docs-origin/master --dry-run --delete-by-hours 24 --keep-tags c59c02c25f023263fd4b5d43fc1ff653f08b3d4x --keep-tags-like late
``` ```
Note that deleting by age will not prevent more recent tags from being deleted if there are more than 10 (or specified `--num` value). In order to keep all tags within a designated period, use the `--keep-by-hours` flag:
```
registry.py -r https://example.com:5000 --dry-run --delete --keep-by-hours 72 --keep-tags-like latest
```
## Disable ssl verification ## Disable ssl verification
If you are using docker registry with a self signed ssl certificate, you can disable ssl verification: If you are using docker registry with a self signed ssl certificate, you can disable ssl verification:
@@ -144,7 +149,7 @@ If you are using docker registry with a self signed ssl certificate, you can dis
## Nexus docker registry ## Nexus docker registry
Add --digest-method flag Add `--digest-method` flag
``` ```
registry.py -l user:pass -r https://example.com:5000 --digest-method GET registry.py -l user:pass -r https://example.com:5000 --digest-method GET

View File

@@ -501,7 +501,14 @@ for more detail on garbage collection read here:
parser.add_argument( parser.add_argument(
'--delete-by-hours', '--delete-by-hours',
help=('Will delete all tags that older than specified hours. Be careful!'), help=('Will delete all tags that are older than specified hours. Be careful!'),
default=False,
nargs='?',
metavar='Hours')
parser.add_argument(
'--keep-by-hours',
help=('Will keep all tags that are newer than specified hours.'),
default=False, default=False,
nargs='?', nargs='?',
metavar='Hours') metavar='Hours')
@@ -605,6 +612,30 @@ def delete_tags_by_age(registry, image_name, dry_run, hours, tags_to_keep):
delete_tags(registry, image_name, dry_run, tags_to_delete, tags_to_keep) delete_tags(registry, image_name, dry_run, tags_to_delete, tags_to_keep)
def get_newer_tags(registry, image_name, hours, tags_list):
newer_tags = []
print('---------------------------------')
for tag in tags_list:
image_config = registry.get_tag_config(image_name, tag)
if image_config == []:
print("tag not found")
continue
image_age = registry.get_image_age(image_name, image_config)
if image_age == []:
print("timestamp not found")
continue
if dt.strptime(image_age[:-4], "%Y-%m-%dT%H:%M:%S.%f") >= dt.now() - timedelta(hours=int(hours)):
print("Keeping tag: {0} timestamp: {1}".format(
tag, image_age))
newer_tags.append(tag)
return newer_tags
def main_loop(args): def main_loop(args):
global DEBUG global DEBUG
@@ -682,10 +713,14 @@ def main_loop(args):
layer['blobSum'])) layer['blobSum']))
# add tags to "tags_to_keep" list, if we have regexp "tags_to_keep" # add tags to "tags_to_keep" list, if we have regexp "tags_to_keep"
# entries: # entries or a number of hours for "keep_by_hours":
keep_tags = [] keep_tags = []
if args.keep_tags_like: if args.keep_tags_like:
keep_tags.extend(get_tags_like(args.keep_tags_like, tags_list)) keep_tags.extend(get_tags_like(args.keep_tags_like, tags_list))
if args.keep_by_hours:
keep_tags.extend(get_newer_tags(registry, image_name,
args.keep_by_hours, tags_list))
keep_tags = list(set(keep_tags)) # Eliminate duplicates
# delete tags if told so # delete tags if told so
if args.delete or args.delete_all: if args.delete or args.delete_all:

39
test.py
View File

@@ -1,6 +1,6 @@
import unittest import unittest
from registry import Registry, Requests, get_tags, parse_args, \ from registry import Registry, Requests, get_tags, parse_args, \
delete_tags, delete_tags_by_age, get_error_explanation delete_tags, delete_tags_by_age, get_error_explanation, get_newer_tags
from mock import MagicMock, patch from mock import MagicMock, patch
import requests import requests
@@ -150,7 +150,7 @@ class TestRegistrySend(unittest.TestCase):
headers=self.registry.HEADERS, headers=self.registry.HEADERS,
verify=True) verify=True)
class TestGetrrorExplanation(unittest.TestCase): class TestGetErrorExplanation(unittest.TestCase):
def test_get_tag_digest_404(self): def test_get_tag_digest_404(self):
self.assertEqual(get_error_explanation("delete_tag", "405"), self.assertEqual(get_error_explanation("delete_tag", "405"),
'You might want to set REGISTRY_STORAGE_DELETE_ENABLED: "true" in your registry') 'You might want to set REGISTRY_STORAGE_DELETE_ENABLED: "true" in your registry')
@@ -681,6 +681,39 @@ class TestDeleteTagsByAge(unittest.TestCase):
self.registry, "imagename", True, ["image"], ["latest"]) self.registry, "imagename", True, ["image"], ["latest"])
class TestGetNewerTags(unittest.TestCase):
def setUp(self):
self.registry = Registry()
self.registry.http = MockRequests()
self.get_tag_config_mock = MagicMock(return_value={'mediaType': 'application/vnd.docker.container.image.v1+json', 'size': 12953,
'digest': 'sha256:8d71dfbf239c0015ad66993d55d3954cee2d52d86f829fdff9ccfb9f23b75aa8'})
self.registry.get_tag_config = self.get_tag_config_mock
self.get_image_age_mock = MagicMock(
return_value="2017-12-27T12:47:33.511765448Z")
self.registry.get_image_age = self.get_image_age_mock
self.list_tags_mock = MagicMock(return_value=["image"])
self.registry.list_tags = self.list_tags_mock
self.get_tag_digest_mock = MagicMock()
self.registry.get_tag_digest = self.get_tag_digest_mock
self.registry.http = MockRequests()
self.registry.hostname = "http://testdomain.com"
self.registry.http.reset_return_value(200, "MOCK_DIGEST")
def test_keep_tags_by_age_no_keep(self):
self.assertEqual(
get_newer_tags(self.registry, "imagename", 23, ["latest"]),
[]
)
def test_keep_tags_by_age_keep(self):
self.assertEqual(
get_newer_tags(self.registry, "imagename", 24, ["latest"]),
["latest"]
)
class TestArgParser(unittest.TestCase): class TestArgParser(unittest.TestCase):
def test_no_args(self): def test_no_args(self):
@@ -700,6 +733,7 @@ class TestArgParser(unittest.TestCase):
"--delete-all", "--delete-all",
"--layers", "--layers",
"--delete-by-hours", "24", "--delete-by-hours", "24",
"--keep-by-hours", "24",
"--digest-method", "GET"] "--digest-method", "GET"]
args = parse_args(args_list) args = parse_args(args_list)
self.assertTrue(args.delete) self.assertTrue(args.delete)
@@ -714,6 +748,7 @@ class TestArgParser(unittest.TestCase):
self.assertEqual(args.host, "hostname") self.assertEqual(args.host, "hostname")
self.assertEqual(args.keep_tags, ["keep1", "keep2"]) self.assertEqual(args.keep_tags, ["keep1", "keep2"])
self.assertEqual(args.delete_by_hours, "24") self.assertEqual(args.delete_by_hours, "24")
self.assertEqual(args.keep_by_hours, "24")
self.assertEqual(args.digest_method, "GET") self.assertEqual(args.digest_method, "GET")
def test_default_args(self): def test_default_args(self):