Dateparsing code crashes on non-UTC datetimes. Use python-dateutil for parsing dates, then convert them to UTC.

This commit is contained in:
Santiago Garcia
2019-08-06 11:29:36 +02:00
parent f99edd34b9
commit f99d1d246c
3 changed files with 40 additions and 10 deletions

View File

@@ -11,10 +11,11 @@ import sys
import os import os
import argparse import argparse
import www_authenticate import www_authenticate
import _strptime
from datetime import timedelta, datetime as dt from datetime import timedelta, datetime as dt
from getpass import getpass from getpass import getpass
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
from dateutil.parser import parse
from dateutil.tz import tzutc
# this is a registry manipulator, can do following: # this is a registry manipulator, can do following:
# - list all images (including layers) # - list all images (including layers)
@@ -642,7 +643,7 @@ def delete_tags_by_age(registry, image_name, dry_run, hours, tags_to_keep):
print("timestamp not found") print("timestamp not found")
continue continue
if dt.strptime(image_age[:-4], "%Y-%m-%dT%H:%M:%S.%f") < dt.now() - timedelta(hours=int(hours)): if parse(image_age).astimezone(tzutc()) < dt.now(tzutc()) - timedelta(hours=int(hours)):
print("will be deleted tag: {0} timestamp: {1}".format( print("will be deleted tag: {0} timestamp: {1}".format(
tag, image_age)) tag, image_age))
tags_to_delete.append(tag) tags_to_delete.append(tag)
@@ -661,7 +662,7 @@ def get_newer_tags(registry, image_name, hours, tags_list):
if image_age == []: if image_age == []:
print("timestamp not found") print("timestamp not found")
return None return None
if dt.strptime(image_age[:-4], "%Y-%m-%dT%H:%M:%S.%f") >= dt.now() - timedelta(hours=int(hours)): if parse(image_age).astimezone(tzutc()) >= dt.now(tzutc()) - timedelta(hours=int(hours)):
print("Keeping tag: {0} timestamp: {1}".format( print("Keeping tag: {0} timestamp: {1}".format(
tag, image_age)) tag, image_age))
return tag return tag
@@ -690,7 +691,7 @@ def get_datetime_tags(registry, image_name, tags_list):
return None return None
return { return {
"tag": tag, "tag": tag,
"datetime": dt.strptime(image_age[:-4], "%Y-%m-%dT%H:%M:%S.%f") "datetime": parse(image_age).astimezone(tzutc())
} }
print('---------------------------------') print('---------------------------------')

View File

@@ -1,6 +1,7 @@
certifi==2017.7.27.1 certifi==2017.7.27.1
chardet==3.0.4 chardet==3.0.4
idna>=2.5 idna>=2.5
python-dateutil==2.8.0
requests>=2.20.0 requests>=2.20.0
urllib3>=1.23 urllib3>=1.23
www-authenticate==0.9.2 www-authenticate==0.9.2

40
test.py
View File

@@ -2,6 +2,8 @@ import unittest
from datetime import datetime from datetime import datetime
from dateutil.tz import tzutc, tzoffset
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, get_newer_tags, \ delete_tags, delete_tags_by_age, get_error_explanation, get_newer_tags, \
keep_images_like, main_loop, get_datetime_tags, get_ordered_tags keep_images_like, main_loop, get_datetime_tags, get_ordered_tags
@@ -662,6 +664,18 @@ class TestDeleteTagsByAge(unittest.TestCase):
delete_tags_patched.assert_called_with( delete_tags_patched.assert_called_with(
self.registry, "imagename", False, ["image"], []) self.registry, "imagename", False, ["image"], [])
@patch('registry.delete_tags')
def test_delete_tags_by_age_no_keep_with_non_utc_value(self, delete_tags_patched):
self.registry.get_image_age.return_value = "2017-12-27T12:47:33.511765448+02:00"
delete_tags_by_age(self.registry, "imagename", False, 24, [])
self.list_tags_mock.assert_called_with(
"imagename"
)
self.list_tags_mock.assert_called_with("imagename")
self.get_tag_config_mock.assert_called_with("imagename", "image")
delete_tags_patched.assert_called_with(
self.registry, "imagename", False, ["image"], [])
@patch('registry.delete_tags') @patch('registry.delete_tags')
def test_delete_tags_by_age_keep_tags(self, delete_tags_patched): def test_delete_tags_by_age_keep_tags(self, delete_tags_patched):
delete_tags_by_age(self.registry, "imagename", False, 24, ["latest"]) delete_tags_by_age(self.registry, "imagename", False, 24, ["latest"])
@@ -717,6 +731,13 @@ class TestGetNewerTags(unittest.TestCase):
["latest"] ["latest"]
) )
def test_keep_tags_by_age_no_keep_non_utc_datetime(self):
self.registry.get_image_age.return_value = "2017-12-27T12:47:33.511765448+02:00"
self.assertEqual(
get_newer_tags(self.registry, "imagename", 23, ["latest"]),
[]
)
class TestGetDatetimeTags(unittest.TestCase): class TestGetDatetimeTags(unittest.TestCase):
@@ -741,7 +762,14 @@ class TestGetDatetimeTags(unittest.TestCase):
def test_get_datetime_tags(self): def test_get_datetime_tags(self):
self.assertEqual( self.assertEqual(
get_datetime_tags(self.registry, "imagename", ["latest"]), get_datetime_tags(self.registry, "imagename", ["latest"]),
[{"tag": "latest", "datetime": datetime(2017, 12, 27, 12, 47, 33, 511765)}] [{"tag": "latest", "datetime": datetime(2017, 12, 27, 12, 47, 33, 511765, tzinfo=tzutc())}]
)
def test_get_non_utc_datetime_tags(self):
self.registry.get_image_age.return_value = "2019-07-18T16:33:15.864962122+02:00"
self.assertEqual(
get_datetime_tags(self.registry, "imagename", ["latest"]),
[{"tag": "latest", "datetime": datetime(2019, 7, 18, 16, 33, 15, 864962, tzinfo=tzoffset(None, 7200))}]
) )
@@ -760,23 +788,23 @@ class TestGetOrderedTags(unittest.TestCase):
get_datetime_tags_patched.return_value = [ get_datetime_tags_patched.return_value = [
{ {
"tag": "e61d48b", "tag": "e61d48b",
"datetime": datetime(2025, 1, 1) "datetime": datetime(2025, 1, 1, tzinfo=tzutc())
}, },
{ {
"tag": "ff24a83", "tag": "ff24a83",
"datetime": datetime(2024, 1, 1) "datetime": datetime(2024, 1, 1, tzinfo=tzutc())
}, },
{ {
"tag": "ddd514c", "tag": "ddd514c",
"datetime": datetime(2023, 1, 1) "datetime": datetime(2023, 1, 1, tzinfo=tzutc())
}, },
{ {
"tag": "f4ba381", "tag": "f4ba381",
"datetime": datetime(2022, 1, 1) "datetime": datetime(2022, 1, 1, tzinfo=tzutc())
}, },
{ {
"tag": "9d5fab2", "tag": "9d5fab2",
"datetime": datetime(2021, 1, 1) "datetime": datetime(2021, 1, 1, tzinfo=tzutc())
} }
] ]
ordered_tags = get_ordered_tags(registry="registry", image_name="image", tags_list=tags, order_by_date=True) ordered_tags = get_ordered_tags(registry="registry", image_name="image", tags_list=tags, order_by_date=True)