mirror of
https://github.com/thegeeklab/docker-tidy.git
synced 2024-11-28 15:10:36 +00:00
Add --exclude-container-label argument to dcgc
Add an argument to dcgc that allows dcgc to ignore removing a container and its child images based on the container's label
This commit is contained in:
parent
091c6ea44b
commit
95ded14891
24
README.rst
24
README.rst
@ -92,6 +92,30 @@ You also can use basic pattern matching to exclude images with generic tags.
|
||||
user/repositoryB:?.?
|
||||
user/repositoryC-*:tag
|
||||
|
||||
|
||||
Prevent containers and associated images from being removed
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``dcgc`` also supports a container exclude list based on labels. If there are
|
||||
stopped containers that you'd like to keep, then you can check the labels to
|
||||
prevent them from being removed.
|
||||
|
||||
::
|
||||
|
||||
--exclude-container-label
|
||||
Never remove containers that have the label key=value. =value can be
|
||||
omitted and in that case only the key is checked. May be specified
|
||||
more than once.
|
||||
|
||||
You also can use basic pattern matching to exclude generic labels.
|
||||
|
||||
.. code::
|
||||
|
||||
foo*
|
||||
com.docker.compose.project=test*
|
||||
com.docker*=*bar*
|
||||
|
||||
|
||||
dcstop
|
||||
------
|
||||
|
||||
|
@ -23,14 +23,26 @@ log = logging.getLogger(__name__)
|
||||
YEAR_ZERO = "0001-01-01T00:00:00Z"
|
||||
|
||||
|
||||
def cleanup_containers(client, max_container_age, dry_run):
|
||||
def cleanup_containers(
|
||||
client,
|
||||
max_container_age,
|
||||
dry_run,
|
||||
exclude_container_labels,
|
||||
):
|
||||
all_containers = get_all_containers(client)
|
||||
|
||||
for container_summary in reversed(all_containers):
|
||||
container = api_call(client.inspect_container,
|
||||
container=container_summary['Id'])
|
||||
if not container or not should_remove_container(container,
|
||||
max_container_age):
|
||||
filtered_containers = filter_excluded_containers(
|
||||
all_containers,
|
||||
exclude_container_labels,
|
||||
)
|
||||
for container_summary in reversed(list(filtered_containers)):
|
||||
container = api_call(
|
||||
client.inspect_container,
|
||||
container=container_summary['Id'],
|
||||
)
|
||||
if not container or not should_remove_container(
|
||||
container,
|
||||
max_container_age,
|
||||
):
|
||||
continue
|
||||
|
||||
log.info("Removing container %s %s %s" % (
|
||||
@ -39,8 +51,43 @@ def cleanup_containers(client, max_container_age, dry_run):
|
||||
container['State']['FinishedAt']))
|
||||
|
||||
if not dry_run:
|
||||
api_call(client.remove_container, container=container['Id'],
|
||||
v=True)
|
||||
api_call(
|
||||
client.remove_container,
|
||||
container=container['Id'],
|
||||
v=True,
|
||||
)
|
||||
|
||||
|
||||
def filter_excluded_containers(containers, exclude_container_labels):
|
||||
def include_container(container):
|
||||
if exclude_container_labels and should_exclude_container_with_labels(
|
||||
container,
|
||||
exclude_container_labels,
|
||||
):
|
||||
return False
|
||||
return True
|
||||
return filter(include_container, containers)
|
||||
|
||||
|
||||
def should_exclude_container_with_labels(container, exclude_container_labels):
|
||||
for exclude_container_label in exclude_container_labels:
|
||||
split_exclude_label = exclude_container_label.split('=', 1)
|
||||
if len(split_exclude_label) == 2:
|
||||
exclude_key, exclude_value = split_exclude_label
|
||||
matching_keys = fnmatch.filter(
|
||||
container['Labels'].keys(), exclude_key
|
||||
)
|
||||
label_values_to_check = [
|
||||
container['Labels'][matching_key]
|
||||
for matching_key in matching_keys
|
||||
]
|
||||
if fnmatch.filter(label_values_to_check, exclude_value):
|
||||
return True
|
||||
else:
|
||||
exclude_key = split_exclude_label[0]
|
||||
if fnmatch.filter(container['Labels'].keys(), exclude_key):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def should_remove_container(container, min_date):
|
||||
@ -226,7 +273,12 @@ def main():
|
||||
**kwargs_from_env())
|
||||
|
||||
if args.max_container_age:
|
||||
cleanup_containers(client, args.max_container_age, args.dry_run)
|
||||
cleanup_containers(
|
||||
client,
|
||||
args.max_container_age,
|
||||
args.dry_run,
|
||||
args.exclude_container_label,
|
||||
)
|
||||
|
||||
if args.max_image_age:
|
||||
exclude_set = build_exclude_set(
|
||||
@ -271,6 +323,10 @@ def get_args(args=None):
|
||||
type=argparse.FileType('r'),
|
||||
help="Path to a file which contains a list of images to exclude, one "
|
||||
"image tag per line.")
|
||||
parser.add_argument(
|
||||
'--exclude-container-label',
|
||||
action='append',
|
||||
help="Never remove containers with this label key or label key=value")
|
||||
|
||||
return parser.parse_args(args=args)
|
||||
|
||||
|
@ -51,24 +51,47 @@ def test_cleanup_containers(mock_client, now):
|
||||
'Name': 'one',
|
||||
'State': {
|
||||
'Running': False,
|
||||
'FinishedAt': '2014-01-01T01:01:01Z'
|
||||
}
|
||||
'FinishedAt': '2014-01-01T01:01:01Z',
|
||||
},
|
||||
},
|
||||
{
|
||||
'Id': 'abbb',
|
||||
'Name': 'two',
|
||||
'State': {
|
||||
'Running': True,
|
||||
'FinishedAt': '2014-01-01T01:01:01Z'
|
||||
}
|
||||
}
|
||||
'FinishedAt': '2014-01-01T01:01:01Z',
|
||||
},
|
||||
},
|
||||
]
|
||||
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, None)
|
||||
mock_client.remove_container.assert_called_once_with(container='abcd',
|
||||
v=True)
|
||||
|
||||
|
||||
def test_filter_excluded_containers():
|
||||
mock_containers = [
|
||||
{'Labels': {'toot': ''}},
|
||||
{'Labels': {'too': 'lol'}},
|
||||
{'Labels': {'toots': 'lol'}},
|
||||
{'Labels': {'foo': 'bar'}},
|
||||
]
|
||||
result = docker_gc.filter_excluded_containers(mock_containers, None)
|
||||
assert mock_containers == list(result)
|
||||
exclude_labels = ['too', 'foo']
|
||||
result = docker_gc.filter_excluded_containers(
|
||||
mock_containers,
|
||||
exclude_labels,
|
||||
)
|
||||
assert [mock_containers[0], mock_containers[2]] == list(result)
|
||||
exclude_labels = ['too*=lol']
|
||||
result = docker_gc.filter_excluded_containers(
|
||||
mock_containers,
|
||||
exclude_labels,
|
||||
)
|
||||
assert [mock_containers[0], mock_containers[3]] == list(result)
|
||||
|
||||
|
||||
def test_cleanup_images(mock_client, now):
|
||||
max_image_age = now
|
||||
mock_client.images.return_value = images = [
|
||||
|
Loading…
Reference in New Issue
Block a user