Merge pull request #40 from ymilki/dangling_volumes

Remove dangling volumes
This commit is contained in:
Kyle Anderson 2016-12-01 09:21:42 -08:00 committed by GitHub
commit 4925cb0d9a
2 changed files with 71 additions and 0 deletions

View File

@ -74,6 +74,13 @@ def get_all_images(client):
return images
def get_dangling_volumes(client):
log.info("Getting dangling volumes")
volumes = client.volumes({'dangling': True})['Volumes']
log.info("Found %s dangling volumes", len(volumes))
return volumes
def cleanup_images(client, max_image_age, dry_run, exclude_set):
# re-fetch container list so that we don't include removed containers
image_tags_in_use = set(
@ -141,6 +148,25 @@ def remove_image(client, image_summary, min_date, dry_run):
api_call(client.remove_image, image=image_tag)
def remove_volume(client, volume, dry_run):
if not volume:
return
log.info("Removing volume %s" % volume['Name'])
if dry_run:
return
api_call(client.remove_volume, name=volume['Name'])
def cleanup_volumes(client, dry_run):
dangling_volumes = get_dangling_volumes(client)
for volume in reversed(dangling_volumes):
log.info("Removing dangling volume %s", volume['Name'])
remove_volume(client, volume, dry_run)
def api_call(func, **kwargs):
try:
return func(**kwargs)
@ -192,6 +218,9 @@ def main():
args.exclude_image_file)
cleanup_images(client, args.max_image_age, args.dry_run, exclude_set)
if args.dangling_volumes:
cleanup_volumes(client, args.dry_run)
def get_args(args=None):
parser = argparse.ArgumentParser()
@ -207,6 +236,10 @@ def get_args(args=None):
help="Maxium age for an image. Images older than this age will be "
"removed. Age can be specified in any pytimeparse supported "
"format.")
parser.add_argument(
'--dangling-volumes',
action="store_true",
help="Dangling volumes will be removed.")
parser.add_argument(
'--dry-run', action="store_true",
help="Only log actions, don't remove anything.")

View File

@ -93,6 +93,32 @@ def test_cleanup_images(mock_client, now):
]
def test_cleanup_volumes(mock_client):
mock_client.volumes.return_value = volumes = {
'Volumes': [
{
'Mountpoint': 'unused',
'Labels': None,
'Driver': 'unused',
'Name': u'one'
},
{
'Mountpoint': 'unused',
'Labels': None,
'Driver': 'unused',
'Name': u'two'
},
],
'Warnings': None,
}
docker_gc.cleanup_volumes(mock_client, False)
assert mock_client.remove_volume.mock_calls == [
mock.call(name=volume['Name'])
for volume in reversed(volumes['Volumes'])
]
def test_filter_images_in_use():
image_tags_in_use = set([
'user/one:latest',
@ -355,6 +381,18 @@ def test_get_all_images(mock_client):
mock_log.info.assert_called_with("Found %s images", count)
def test_get_dangling_volumes(mock_client):
count = 4
mock_client.volumes.return_value = {
'Volumes': [mock.Mock() for _ in range(count)]
}
with mock.patch('docker_custodian.docker_gc.log',
autospec=True) as mock_log:
volumes = docker_gc.get_dangling_volumes(mock_client)
assert volumes == mock_client.volumes.return_value['Volumes']
mock_log.info.assert_called_with("Found %s dangling volumes", count)
def test_build_exclude_set():
image_tags = [
'some_image:latest',