replace pytimeparse with dateparser

This commit is contained in:
Robert Kaussow 2020-03-15 14:50:58 +01:00
parent 178137d0f4
commit cc23232f32
8 changed files with 103 additions and 75 deletions

View File

@ -34,7 +34,6 @@ docker-pycreds = "*"
idna = "*"
ipaddress = "*"
python-dateutil = "*"
pytimeparse = "*"
requests = "*"
appdirs = "*"
colorama = "*"
@ -46,3 +45,4 @@ environs = "*"
nested-lookup = "*"
"ruamel.yaml" = "*"
websocket-client = "*"
dateparser = "*"

84
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "bf5cf1a93672e4870e15ed4800cde2558d3915c10d294353e6e4c28c4e14524e"
"sha256": "2655d80711d8731029d69fc90a9adf0fcad44197d18758eaffceb9a93ac38091"
},
"pipfile-spec": 6,
"requires": {},
@ -60,6 +60,14 @@
"index": "pypi",
"version": "==0.4.3"
},
"dateparser": {
"hashes": [
"sha256:1b1f0e3034f82d1f92b45fa445826da6a36d67af8a1169e04869685594276011",
"sha256:fb5bfde4795fa4b179fe05c2c25b3981f785de26bec37e247dee1079c63d5689"
],
"index": "pypi",
"version": "==0.7.4"
},
"docker": {
"hashes": [
"sha256:1c2ddb7a047b2599d1faec00889561316c674f7099427b9c51e8cb804114b553",
@ -167,13 +175,38 @@
"index": "pypi",
"version": "==0.1.11"
},
"pytimeparse": {
"pytz": {
"hashes": [
"sha256:04b7be6cc8bd9f5647a6325444926c3ac34ee6bc7e69da4367ba282f076036bd",
"sha256:e86136477be924d7e670646a98561957e8ca7308d44841e21f5ddea757556a0a"
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
],
"index": "pypi",
"version": "==1.1.8"
"version": "==2019.3"
},
"regex": {
"hashes": [
"sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431",
"sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242",
"sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1",
"sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d",
"sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045",
"sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b",
"sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400",
"sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa",
"sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0",
"sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69",
"sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74",
"sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb",
"sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26",
"sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5",
"sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2",
"sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce",
"sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab",
"sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e",
"sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70",
"sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc",
"sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"
],
"version": "==2020.2.20"
},
"requests": {
"hashes": [
@ -223,6 +256,13 @@
],
"version": "==1.14.0"
},
"tzlocal": {
"hashes": [
"sha256:11c9f16e0a633b4b60e1eede97d8a46340d042e67b670b290ca526576e039048",
"sha256:949b9dd5ba4be17190a80c0268167d7e6c92c62b30026cf9764caf3e308e5590"
],
"version": "==2.0.0"
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
@ -324,10 +364,10 @@
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
"sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc",
"sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"
],
"version": "==7.0"
"version": "==7.1.1"
},
"colorama": {
"hashes": [
@ -438,11 +478,11 @@
},
"flake8-builtins": {
"hashes": [
"sha256:29bc0f7e68af481d088f5c96f8aeb02520abdfc900500484e3af969f42a38a5f",
"sha256:c44415fb19162ef3737056e700d5b99d48c3612a533943b4e16419a5d3de3a64"
"sha256:5de3917b9b6d81e8b92d56ebc2873cc178978658848a7a16a638a6ea5842f70c",
"sha256:b4aaa42bf503ae287c436a822374996542003ddfd73a971988b4c383652c9c58"
],
"index": "pypi",
"version": "==1.4.2"
"version": "==1.5.0"
},
"flake8-colors": {
"hashes": [
@ -620,10 +660,10 @@
},
"pip-shims": {
"hashes": [
"sha256:1cc3e2e4e5d5863edd4760d2032b180a6ef81719277fe95404df1bb0e58b7261",
"sha256:b5bb01c4394a2e0260bddb4cfdc7e6fdd9d6e61c8febd18c3594e2ea2596c190"
"sha256:2b9a88ff0fd31e7d27a362d3e36e6e75d8fbc339c9c4367f4a97b72b22e6f4f4",
"sha256:5861da6f48e60b55d40b984795c63681e4db7ac576c1c3b05f4b54a9d508e3da"
],
"version": "==0.5.0"
"version": "==0.5.1"
},
"pipenv-setup": {
"hashes": [
@ -701,11 +741,11 @@
},
"pytest": {
"hashes": [
"sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d",
"sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6"
"sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172",
"sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970"
],
"index": "pypi",
"version": "==5.3.5"
"version": "==5.4.1"
},
"pytest-cov": {
"hashes": [
@ -745,14 +785,6 @@
"index": "pypi",
"version": "==0.1.11"
},
"pytimeparse": {
"hashes": [
"sha256:04b7be6cc8bd9f5647a6325444926c3ac34ee6bc7e69da4367ba282f076036bd",
"sha256:e86136477be924d7e670646a98561957e8ca7308d44841e21f5ddea757556a0a"
],
"index": "pypi",
"version": "==1.1.8"
},
"pyyaml": {
"hashes": [
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",

View File

@ -30,15 +30,13 @@ class DockerTidy:
:return: args objec
"""
parser = argparse.ArgumentParser(
description="Generate documentation from annotated Ansible roles using templates"
)
parser = argparse.ArgumentParser(description="keep docker hosts tidy")
parser.add_argument(
"--dry-run",
action="store_true",
default=None,
dest="dry_run",
help="Only log actions, don't stop anything."
help="only log actions, don't stop anything"
)
parser.add_argument(
"-t",
@ -46,7 +44,7 @@ class DockerTidy:
type=int,
dest="http_timeout",
metavar="HTTP_TIMEOUT",
help="HTTP timeout in seconds for making docker API calls."
help="HTTP timeout in seconds for making docker API calls"
)
parser.add_argument(
"-v", dest="logging.level", action="append_const", const=-1, help="increase log level"
@ -61,30 +59,28 @@ class DockerTidy:
subparsers = parser.add_subparsers(dest="command", help="sub-command help")
subparsers.required = True
parser_gc = subparsers.add_parser("gc", help="Run docker garbage collector.")
parser_gc = subparsers.add_parser("gc", help="run docker garbage collector")
parser_gc.add_argument(
"--max-container-age",
type=timedelta_validator,
dest="gc.max_container_age",
metavar="MAX_CONTAINER_AGE",
help="Maximum age for a container. Containers older than this age "
"will be removed. Age can be specified in any pytimeparse "
"supported format."
help="maximum age for a container, containers older than this age "
"will be removed (dateparser value)"
)
parser_gc.add_argument(
"--max-image-age",
type=timedelta_validator,
dest="gc.max_image_age",
metavar="MAX_IMAGE_AGE",
help="Maxium age for an image. Images older than this age will be "
"removed. Age can be specified in any pytimeparse supported "
"format."
help="maxium age for an image, images older than this age will be "
"removed (dateparser value)"
)
parser_gc.add_argument(
"--dangling-volumes",
action="store_true",
dest="gc.dangling_volumes",
help="Dangling volumes will be removed."
help="dangling volumes will be removed"
)
parser_gc.add_argument(
"--exclude-image",
@ -92,7 +88,7 @@ class DockerTidy:
type=str,
dest="gc.exclude_images",
metavar="EXCLUDE_IMAGE",
help="Never remove images with this tag."
help="never remove images with this tag"
)
parser_gc.add_argument(
"--exclude-container-label",
@ -100,20 +96,19 @@ class DockerTidy:
type=str,
dest="gc.exclude_container_labels",
metavar="EXCLUDE_CONTAINER_LABEL",
help="Never remove containers with this label key "
help="never remove containers with this label key "
"or label key=value"
)
parser_stop = subparsers.add_parser(
"stop", help="Stop containers that have been running for too long."
"stop", help="stop containers that have been running for too long"
)
parser_stop.add_argument(
"--max-run-time",
type=timedelta_validator,
dest="stop.max_run_time",
metavar="MAX_RUN_TIME",
help="Maximum time a container is allows to run. Time may "
"be specified in any pytimeparse supported format."
help="maximum time a container is allows to run (dateparser value)"
)
parser_stop.add_argument(
"--prefix",
@ -121,8 +116,7 @@ class DockerTidy:
type=str,
dest="stop.prefix",
metavar="PREFIX",
help="Only stop containers which match one of the "
"prefix."
help="only stop containers which match one of the prefix"
)
return parser.parse_args().__dict__

View File

@ -313,7 +313,7 @@ class GarbageCollector:
self.cleanup_volumes()
if (
not config["gc"]["max_container_age"] or not config["gc"]["max_image_age"]
or not config["gc"]["dangling_volumes"]
not config["gc"]["max_container_age"] and not config["gc"]["max_image_age"]
and not config["gc"]["dangling_volumes"]
):
self.logger.warn("Skipped, no arguments given")

View File

@ -1,52 +1,51 @@
#!/usr/bin/env python3
"""Custom input type parser."""
import datetime
from argparse import ArgumentTypeError
import dateparser
import environs
from dateutil import tz
from pytimeparse import timeparse
env = environs.Env()
def timedelta_validator(value):
"""Return the :class:`datetime.datetime.DateTime` for a time in the past.
"""Return the dateparser string for a time in the past.
:param value: a string containing a time format supported by
mod:`pytimeparse`
mod:`dateparser`
"""
if value is None:
return None
try:
_datetime_seconds_ago(timeparse.timeparse(value))
return value
except TypeError:
raise
if not dateparser.parse(value):
raise ArgumentTypeError("'{}' is not a valid timedelta string".format(value))
return value
def timedelta(value, dt_format=None):
"""Return the :class:`datetime.datetime.DateTime` for a time in the past.
:param value: a string containing a time format supported by
mod:`pytimeparse`
mod:`dateparser`
"""
if value is None:
return None
timedelta = _datetime_seconds_ago(timeparse.timeparse(value))
timedelta = dateparser.parse(
value, settings={
"TO_TIMEZONE": "UTC",
"RETURN_AS_TIMEZONE_AWARE": True
}
)
if dt_format:
timedelta = timedelta.strftime(dt_format)
return timedelta
def _datetime_seconds_ago(seconds):
now = datetime.datetime.now(tz.tzutc())
return now - datetime.timedelta(seconds=seconds)
@env.parser_for("timedelta_validator")
def timedelta_parser(value):
try:

View File

@ -74,18 +74,18 @@ $ docker-tidy --help
usage: docker-tidy [-h] [--dry-run] [-t HTTP_TIMEOUT] [-v] [-q] [--version]
{gc,stop} ...
Generate documentation from annotated Ansible roles using templates
keep docker hosts tidy
positional arguments:
{gc,stop} sub-command help
gc Run docker garbage collector.
stop Stop containers that have been running for too long.
gc run docker garbage collector
stop stop containers that have been running for too long
optional arguments:
-h, --help show this help message and exit
--dry-run Only log actions, don't stop anything.
--dry-run only log actions, don't stop anything
-t HTTP_TIMEOUT, --timeout HTTP_TIMEOUT
HTTP timeout in seconds for making docker API calls.
HTTP timeout in seconds for making docker API calls
-v increase log level
-q decrease log level
--version show program's version number and exit

View File

@ -13,12 +13,12 @@ than \"max age\". Running containers, and images which are used by a
container are never removed.
Maximum age can be specificied with any format supported by
[pytimeparse](https://github.com/wroberts/pytimeparse).
[dateparser](https://dateparser.readthedocs.io/en/latest/index.html#features).
__Example:__
```Shell
docker-tidy gc --max-container-age 3days --max-image-age 30days
docker-tidy gc --max-container-age "3 days ago" --max-image-age "30 days ago"
```
### Prevent images from being removed
@ -58,5 +58,5 @@ If no prefix is set, __all__ containers matching the `max-run-time` will be stop
__Example:__
```Shell
docker-tidy stop --max-run-time 2days --prefix "projectprefix_"
docker-tidy stop --max-run-time "2 days ago" --prefix "projectprefix_"
```

View File

@ -69,6 +69,7 @@ setup(
"certifi==2019.11.28",
"chardet==3.0.4",
"colorama==0.4.3",
"dateparser==0.7.4",
"docker==4.2.0",
"docker-pycreds==0.4.0",
"environs==7.3.0",
@ -83,11 +84,13 @@ setup(
"python-dateutil==2.8.1",
"python-dotenv==0.12.0",
"python-json-logger==0.1.11",
"pytimeparse==1.1.8",
"pytz==2019.3",
"regex==2020.2.20",
"requests==2.23.0",
"ruamel.yaml==0.16.10",
"ruamel.yaml.clib==0.2.0; platform_python_implementation == 'CPython' and python_version < '3.9'",
"six==1.14.0",
"tzlocal==2.0.0",
"urllib3==1.25.8",
"websocket-client==0.57.0",
"zipp==1.2.0",