Skip to content

Commit

Permalink
Add a CLI options to order tags by date
Browse files Browse the repository at this point in the history
  • Loading branch information
sgarcialaguna committed Jul 28, 2019
1 parent e61d48b commit 422ab54
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 4 deletions.
45 changes: 43 additions & 2 deletions registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,12 @@ def parse_args(args=None):
default='POST',
metavar="POST|GET"
)
parser.add_argument(
'--order-by-date',
help=('Orders images by date instead of by tag name.'
'Useful if your tag names are not in a fixed order.'),
action='store_true'
)
return parser.parse_args(args)


Expand Down Expand Up @@ -672,6 +678,29 @@ def newer(tag):
return result


def get_datetime_tags(registry, image_name, tags_list):
def newer(tag):
image_config = registry.get_tag_config(image_name, tag)
if image_config == []:
print("tag not found")
return None
image_age = registry.get_image_age(image_name, image_config)
if image_age == []:
print("timestamp not found")
return None
return {
"tag": tag,
"datetime": dt.strptime(image_age[:-4], "%Y-%m-%dT%H:%M:%S.%f")
}

print('---------------------------------')
p = ThreadPool(4)
result = list(x for x in p.map(newer, tags_list) if x)
p.close()
p.join()
return result


def keep_images_like(image_list, regexp_list):
if image_list is None or regexp_list is None:
return []
Expand All @@ -685,6 +714,18 @@ def keep_images_like(image_list, regexp_list):
return result


def get_ordered_tags(registry, image_name, tags_list, order_by_date=False):
if order_by_date:
tags_date = get_datetime_tags(registry, image_name, tags_list)
sorted_tags_by_date = sorted(
tags_date,
key=lambda x: x["datetime"]
)
return [x["tag"] for x in sorted_tags_by_date]

return sorted(tags_list, key=natural_keys)


def main_loop(args):
global DEBUG

Expand Down Expand Up @@ -778,8 +819,8 @@ def main_loop(args):
if args.delete_all:
tags_list_to_delete = list(tags_list)
else:
tags_list_to_delete = sorted(tags_list, key=natural_keys)[
:-keep_last_versions]
ordered_tags_list = get_ordered_tags(registry, image_name, tags_list, args.order_by_date)
tags_list_to_delete = ordered_tags_list[:-keep_last_versions]

# A manifest might be shared between different tags. Explicitly add those
# tags that we want to preserve to the keep_tags list, to prevent
Expand Down
76 changes: 74 additions & 2 deletions test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import unittest

from datetime import datetime

from registry import Registry, Requests, get_tags, parse_args, \
delete_tags, delete_tags_by_age, get_error_explanation, get_newer_tags, \
keep_images_like, main_loop
keep_images_like, main_loop, get_datetime_tags, get_ordered_tags
from mock import MagicMock, patch
import requests

Expand Down Expand Up @@ -715,6 +718,72 @@ def test_keep_tags_by_age_keep(self):
)


class TestGetDatetimeTags(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_get_datetime_tags(self):
self.assertEqual(
get_datetime_tags(self.registry, "imagename", ["latest"]),
[{"tag": "latest", "datetime": datetime(2017, 12, 27, 12, 47, 33, 511765)}]
)


class TestGetOrderedTags(unittest.TestCase):
def setUp(self):
self.tags = ["e61d48b", "ff24a83", "ddd514c", "f4ba381", "9d5fab2"]

def test_tags_are_ordered_by_name_by_default(self):
tags = ["v1", "v10", "v2"]
ordered_tags = get_ordered_tags(registry=None, image_name=None, tags_list=tags)
self.assertEqual(ordered_tags, ["v1", "v2", "v10"])

@patch('registry.get_datetime_tags')
def test_tags_are_ordered_ascending_by_date_if_the_option_is_given(self, get_datetime_tags_patched):
tags = ["e61d48b", "ff24a83", "ddd514c", "f4ba381", "9d5fab2"]
get_datetime_tags_patched.return_value = [
{
"tag": "e61d48b",
"datetime": datetime(2025, 1, 1)
},
{
"tag": "ff24a83",
"datetime": datetime(2024, 1, 1)
},
{
"tag": "ddd514c",
"datetime": datetime(2023, 1, 1)
},
{
"tag": "f4ba381",
"datetime": datetime(2022, 1, 1)
},
{
"tag": "9d5fab2",
"datetime": datetime(2021, 1, 1)
}
]
ordered_tags = get_ordered_tags(registry="registry", image_name="image", tags_list=tags, order_by_date=True)
get_datetime_tags_patched.assert_called_once_with("registry", "image", tags)
self.assertEqual(ordered_tags, ["9d5fab2", "f4ba381", "ddd514c", "ff24a83", "e61d48b"])


class TestKeepImagesLike(unittest.TestCase):

# tests the filtering works
Expand Down Expand Up @@ -776,13 +845,15 @@ def test_all_args(self):
"--layers",
"--delete-by-hours", "24",
"--keep-by-hours", "24",
"--digest-method", "GET"]
"--digest-method", "GET",
"--order-by-age"]
args = parse_args(args_list)
self.assertTrue(args.delete)
self.assertTrue(args.layers)
self.assertTrue(args.no_validate_ssl)
self.assertTrue(args.delete_all)
self.assertTrue(args.layers)
self.assertTrue(args.order_by_date)
self.assertEqual(args.image, ["imagename1", "imagename2"])
self.assertEqual(args.num, "15")
self.assertEqual(args.login, "loginstring")
Expand All @@ -798,6 +869,7 @@ def test_default_args(self):
"-l", "loginstring"]
args = parse_args(args_list)
self.assertEqual(args.digest_method, "HEAD")
self.assertFalse(args.order_by_date)


if __name__ == '__main__':
Expand Down

0 comments on commit 422ab54

Please sign in to comment.