Merge pull request #27 from kentwills/remove_py26_support

Remove py26 support, code cleanup
This commit is contained in:
Kyle Anderson 2016-04-20 14:10:18 -07:00
commit 0749ece61a
9 changed files with 127 additions and 86 deletions

View File

@ -1,11 +1,9 @@
language: python language: python
env: env:
- TOX_ENV=py26 - TOXENV=py27
- TOX_ENV=py27 - TOXENV=py33
- TOX_ENV=py33 - TOXENV=py34
- TOX_ENV=py34
install: install:
- "pip install --use-mirrors tox" - pip install tox
script: script:
- tox -e $TOX_ENV - tox
sudo: false

2
debian/rules vendored
View File

@ -7,7 +7,7 @@ export DH_OPTIONS
dh $@ --with python-virtualenv dh $@ --with python-virtualenv
override_dh_virtualenv: override_dh_virtualenv:
dh_virtualenv dh_virtualenv --python python2.7
# do not call `make clean` as part of packaging # do not call `make clean` as part of packaging
override_dh_auto_clean: override_dh_auto_clean:

View File

@ -1,4 +1,4 @@
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
__version_info__ = (0, 5, 3) __version_info__ = (0, 6, 0)
__version__ = '%d.%d.%d' % __version_info__ __version__ = '%d.%d.%d' % __version_info__

View File

@ -7,7 +7,8 @@ from pytimeparse import timeparse
def timedelta_type(value): def timedelta_type(value):
"""Return the :class:`datetime.datetime.DateTime` for a time in the past. """Return the :class:`datetime.datetime.DateTime` for a time in the past.
:param value: a string containing a time format supported by :mod:`pytimeparse` :param value: a string containing a time format supported by
mod:`pytimeparse`
""" """
if value is None: if value is None:
return None return None

View File

@ -77,18 +77,22 @@ def get_opts(args=None):
parser.add_argument( parser.add_argument(
'--max-run-time', '--max-run-time',
type=timedelta_type, type=timedelta_type,
help="Maximum time a container is allows to run. Time may be specified " help="Maximum time a container is allows to run. Time may "
"in any pytimeparse supported format.") "be specified in any pytimeparse supported format."
)
parser.add_argument( parser.add_argument(
'--prefix', action="append", default=[], '--prefix', action="append", default=[],
help="Only stop containers which match one of the " help="Only stop containers which match one of the "
"prefix.") "prefix."
)
parser.add_argument( parser.add_argument(
'--dry-run', action="store_true", '--dry-run', action="store_true",
help="Only log actions, don't stop anything.") help="Only log actions, don't stop anything."
)
parser.add_argument( parser.add_argument(
'-t', '--timeout', type=int, default=60, '-t', '--timeout', type=int, default=60,
help="HTTP timeout in seconds for making docker API calls.") help="HTTP timeout in seconds for making docker API calls."
)
opts = parser.parse_args(args=args) opts = parser.parse_args(args=args)
if not opts.prefix: if not opts.prefix:

View File

@ -1,20 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from setuptools import setup, find_packages from setuptools import setup, find_packages
import sys
from docker_custodian.__about__ import __version__ from docker_custodian.__about__ import __version__
install_requires = [
'python-dateutil',
'docker-py >= 0.5',
'pytimeparse',
]
if sys.version_info < (2, 7):
install_requires.append('argparse')
setup( setup(
name='docker_custodian', name='docker_custodian',
version=__version__, version=__version__,
@ -24,7 +12,11 @@ setup(
description='Keep docker hosts tidy.', description='Keep docker hosts tidy.',
packages=find_packages(exclude=['tests*']), packages=find_packages(exclude=['tests*']),
include_package_data=True, include_package_data=True,
install_requires=install_requires, install_requires=[
'python-dateutil',
'docker-py >= 0.5',
'pytimeparse',
],
license="Apache License 2.0", license="Apache License 2.0",
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [

View File

@ -54,7 +54,12 @@ def test_has_been_running_since_false(container, earlier_time):
@mock.patch('docker_custodian.docker_autostop.get_opts', @mock.patch('docker_custodian.docker_autostop.get_opts',
autospec=True) autospec=True)
@mock.patch('docker_custodian.docker_autostop.docker', autospec=True) @mock.patch('docker_custodian.docker_autostop.docker', autospec=True)
def test_main(mock_docker, mock_get_opts, mock_stop_containers, mock_build_matcher): def test_main(
mock_docker,
mock_get_opts,
mock_stop_containers,
mock_build_matcher
):
mock_get_opts.return_value.timeout = 30 mock_get_opts.return_value.timeout = 30
main() main()
mock_get_opts.assert_called_once_with() mock_get_opts.assert_called_once_with()

View File

@ -42,18 +42,26 @@ class TestShouldRemoveContainer(object):
def test_cleanup_containers(mock_client, now): def test_cleanup_containers(mock_client, now):
max_container_age = now max_container_age = now
mock_client.containers.return_value = [ mock_client.containers.return_value = [
dict(Id='abcd'), {'Id': 'abcd'},
dict(Id='abbb'), {'Id': 'abbb'},
] ]
mock_containers = [ mock_containers = [
dict( {
Id='abcd', 'Id': 'abcd',
Name='one', 'Name': 'one',
State=dict(Running=False, FinishedAt='2014-01-01T01:01:01Z')), 'State': {
dict( 'Running': False,
Id='abbb', 'FinishedAt': '2014-01-01T01:01:01Z'
Name='two', }
State=dict(Running=True, FinishedAt='2014-01-01T01:01:01Z')) },
{
'Id': 'abbb',
'Name': 'two',
'State': {
'Running': True,
'FinishedAt': '2014-01-01T01:01:01Z'
}
}
] ]
mock_client.inspect_container.side_effect = iter(mock_containers) mock_client.inspect_container.side_effect = iter(mock_containers)
docker_gc.cleanup_containers(mock_client, max_container_age, False) docker_gc.cleanup_containers(mock_client, max_container_age, False)
@ -63,12 +71,18 @@ def test_cleanup_containers(mock_client, now):
def test_cleanup_images(mock_client, now): def test_cleanup_images(mock_client, now):
max_image_age = now max_image_age = now
mock_client.images.return_value = images = [ mock_client.images.return_value = images = [
dict(Id='abcd'), {'Id': 'abcd'},
dict(Id='abbb'), {'Id': 'abbb'},
] ]
mock_images = [ mock_images = [
dict(Id='abcd', Created='2014-01-01T01:01:01Z'), {
dict(Id='abbb', Created='2014-01-01T01:01:01Z'), 'Id': 'abcd',
'Created': '2014-01-01T01:01:01Z'
},
{
'Id': 'abbb',
'Created': '2014-01-01T01:01:01Z'
},
] ]
mock_client.inspect_image.side_effect = iter(mock_images) mock_client.inspect_image.side_effect = iter(mock_images)
@ -86,17 +100,38 @@ def test_filter_images_in_use():
'2471708c19be:latest', '2471708c19be:latest',
]) ])
images = [ images = [
dict(RepoTags=['<none>:<none>'], Id='2471708c19beabababab'), {
dict(RepoTags=['<none>:<none>'], Id='babababababaabababab'), 'RepoTags': ['<none>:<none>'],
dict(RepoTags=['user/one:latest', 'user/one:abcd']), 'Id': '2471708c19beabababab'
dict(RepoTags=['other:abcda']), },
dict(RepoTags=['other:12345']), {
dict(RepoTags=['new_image:latest', 'new_image:123']), 'RepoTags': ['<none>:<none>'],
'Id': 'babababababaabababab'
},
{
'RepoTags': ['user/one:latest', 'user/one:abcd']
},
{
'RepoTags': ['other:abcda']
},
{
'RepoTags': ['other:12345']
},
{
'RepoTags': ['new_image:latest', 'new_image:123']
},
] ]
expected = [ expected = [
dict(RepoTags=['<none>:<none>'], Id='babababababaabababab'), {
dict(RepoTags=['other:abcda']), 'RepoTags': ['<none>:<none>'],
dict(RepoTags=['new_image:latest', 'new_image:123']), 'Id': 'babababababaabababab'
},
{
'RepoTags': ['other:abcda']
},
{
'RepoTags': ['new_image:latest', 'new_image:123']
},
] ]
actual = docker_gc.filter_images_in_use(images, image_tags_in_use) actual = docker_gc.filter_images_in_use(images, image_tags_in_use)
assert list(actual) == expected assert list(actual) == expected
@ -109,16 +144,34 @@ def test_filter_excluded_images():
'other:12345', 'other:12345',
]) ])
images = [ images = [
dict(RepoTags=['<none>:<none>'], Id='babababababaabababab'), {
dict(RepoTags=['user/one:latest', 'user/one:abcd']), 'RepoTags': ['<none>:<none>'],
dict(RepoTags=['other:abcda']), 'Id': 'babababababaabababab'
dict(RepoTags=['other:12345']), },
dict(RepoTags=['new_image:latest', 'new_image:123']), {
'RepoTags': ['user/one:latest', 'user/one:abcd']
},
{
'RepoTags': ['other:abcda']
},
{
'RepoTags': ['other:12345']
},
{
'RepoTags': ['new_image:latest', 'new_image:123']
},
] ]
expected = [ expected = [
dict(RepoTags=['<none>:<none>'], Id='babababababaabababab'), {
dict(RepoTags=['other:abcda']), 'RepoTags': ['<none>:<none>'],
dict(RepoTags=['new_image:latest', 'new_image:123']), 'Id': 'babababababaabababab'
},
{
'RepoTags': ['other:abcda']
},
{
'RepoTags': ['new_image:latest', 'new_image:123']
},
] ]
actual = docker_gc.filter_excluded_images(images, exclude_set) actual = docker_gc.filter_excluded_images(images, exclude_set)
assert list(actual) == expected assert list(actual) == expected
@ -134,7 +187,7 @@ def test_is_image_old_false(image, later_time):
def test_remove_image_no_tags(mock_client, image, now): def test_remove_image_no_tags(mock_client, image, now):
image_id = 'abcd' image_id = 'abcd'
image_summary = dict(Id=image_id) image_summary = {'Id': image_id}
mock_client.inspect_image.return_value = image mock_client.inspect_image.return_value = image
docker_gc.remove_image(mock_client, image_summary, now, False) docker_gc.remove_image(mock_client, image_summary, now, False)
@ -143,7 +196,7 @@ def test_remove_image_no_tags(mock_client, image, now):
def test_remove_image_new_image_not_removed(mock_client, image, later_time): def test_remove_image_new_image_not_removed(mock_client, image, later_time):
image_id = 'abcd' image_id = 'abcd'
image_summary = dict(Id=image_id) image_summary = {'Id': image_id}
mock_client.inspect_image.return_value = image mock_client.inspect_image.return_value = image
docker_gc.remove_image(mock_client, image_summary, later_time, False) docker_gc.remove_image(mock_client, image_summary, later_time, False)
@ -153,7 +206,10 @@ def test_remove_image_new_image_not_removed(mock_client, image, later_time):
def test_remove_image_with_tags(mock_client, image, now): def test_remove_image_with_tags(mock_client, image, now):
image_id = 'abcd' image_id = 'abcd'
repo_tags = ['user/one:latest', 'user/one:12345'] repo_tags = ['user/one:latest', 'user/one:12345']
image_summary = dict(Id=image_id, RepoTags=repo_tags) image_summary = {
'Id': image_id,
'RepoTags': repo_tags
}
mock_client.inspect_image.return_value = image mock_client.inspect_image.return_value = image
docker_gc.remove_image(mock_client, image_summary, now, False) docker_gc.remove_image(mock_client, image_summary, now, False)
@ -182,7 +238,9 @@ def test_api_call_with_timeout():
docker_gc.api_call(func, id) docker_gc.api_call(func, id)
func.assert_called_once_with(id) func.assert_called_once_with(id)
mock_log.warn.assert_called_once_with('Failed to call remove_image abcd msg') mock_log.warn.assert_called_once_with(
'Failed to call remove_image abcd msg'
)
def test_api_call_with_api_error(): def test_api_call_with_api_error():

27
tox.ini
View File

@ -1,30 +1,13 @@
[tox] [tox]
envlist = py26,py27,py33,py34 envlist = py27,py33,py34
[testenv] [testenv]
deps = deps =
-rrequirements.txt -rrequirements.txt
flake8
pytest
mock mock
pre-commit
pytest
commands = commands =
py.test {posargs:tests} py.test {posargs:tests}
flake8 . pre-commit install -f --install-hooks
pre-commit run --all-files
[testenv:py26]
deps =
-rrequirements.txt
flake8
pytest
mock < 1.1.0
[testenv:install-hooks]
deps =
pre-commit
commands =
pre-commit install
[flake8]
ignore =
exclude = .git/*,.tox/*,debian/*
max_line_length = 85