Refactor container exclude labels

This commit is contained in:
Andy Tran 2018-03-20 15:30:38 -07:00
parent 95ded14891
commit b84b30052a
2 changed files with 58 additions and 13 deletions

View File

@ -13,6 +13,7 @@ import docker
import docker.errors import docker.errors
import requests.exceptions import requests.exceptions
from collections import namedtuple
from docker_custodian.args import timedelta_type from docker_custodian.args import timedelta_type
from docker.utils import kwargs_from_env from docker.utils import kwargs_from_env
@ -22,6 +23,8 @@ log = logging.getLogger(__name__)
# This seems to be something docker uses for a null/zero date # This seems to be something docker uses for a null/zero date
YEAR_ZERO = "0001-01-01T00:00:00Z" YEAR_ZERO = "0001-01-01T00:00:00Z"
ExcludeLabel = namedtuple('ExcludeLabel', ['key', 'value'])
def cleanup_containers( def cleanup_containers(
client, client,
@ -59,8 +62,11 @@ def cleanup_containers(
def filter_excluded_containers(containers, exclude_container_labels): def filter_excluded_containers(containers, exclude_container_labels):
if not exclude_container_labels:
return containers
def include_container(container): def include_container(container):
if exclude_container_labels and should_exclude_container_with_labels( if should_exclude_container_with_labels(
container, container,
exclude_container_labels, exclude_container_labels,
): ):
@ -70,22 +76,20 @@ def filter_excluded_containers(containers, exclude_container_labels):
def should_exclude_container_with_labels(container, exclude_container_labels): def should_exclude_container_with_labels(container, exclude_container_labels):
for exclude_container_label in exclude_container_labels: for exclude_label in exclude_container_labels:
split_exclude_label = exclude_container_label.split('=', 1) if exclude_label.value:
if len(split_exclude_label) == 2:
exclude_key, exclude_value = split_exclude_label
matching_keys = fnmatch.filter( matching_keys = fnmatch.filter(
container['Labels'].keys(), exclude_key container['Labels'].keys(),
exclude_label.key,
) )
label_values_to_check = [ label_values_to_check = [
container['Labels'][matching_key] container['Labels'][matching_key]
for matching_key in matching_keys for matching_key in matching_keys
] ]
if fnmatch.filter(label_values_to_check, exclude_value): if fnmatch.filter(label_values_to_check, exclude_label.value):
return True return True
else: else:
exclude_key = split_exclude_label[0] if fnmatch.filter(container['Labels'].keys(), exclude_label.key):
if fnmatch.filter(container['Labels'].keys(), exclude_key):
return True return True
return False return False
@ -261,6 +265,24 @@ def build_exclude_set(image_tags, exclude_file):
return exclude_set return exclude_set
def format_exclude_labels(exclude_label_args):
exclude_labels = []
for exclude_label_arg in exclude_label_args:
split_exclude_label = exclude_label_arg.split('=', 1)
exclude_label_key = split_exclude_label[0]
if len(split_exclude_label) == 2:
exclude_label_value = split_exclude_label[1]
else:
exclude_label_value = None
exclude_labels.append(
ExcludeLabel(
key=exclude_label_key,
value=exclude_label_value,
)
)
return exclude_labels
def main(): def main():
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
@ -272,12 +294,16 @@ def main():
timeout=args.timeout, timeout=args.timeout,
**kwargs_from_env()) **kwargs_from_env())
exclude_container_labels = format_exclude_labels(
args.exclude_container_label
)
if args.max_container_age: if args.max_container_age:
cleanup_containers( cleanup_containers(
client, client,
args.max_container_age, args.max_container_age,
args.dry_run, args.dry_run,
args.exclude_container_label, exclude_container_labels,
) )
if args.max_image_age: if args.max_image_age:
@ -325,7 +351,7 @@ def get_args(args=None):
"image tag per line.") "image tag per line.")
parser.add_argument( parser.add_argument(
'--exclude-container-label', '--exclude-container-label',
action='append', action='append', type=str, default=[],
help="Never remove containers with this label key or label key=value") help="Never remove containers with this label key or label key=value")
return parser.parse_args(args=args) return parser.parse_args(args=args)

View File

@ -78,13 +78,18 @@ def test_filter_excluded_containers():
] ]
result = docker_gc.filter_excluded_containers(mock_containers, None) result = docker_gc.filter_excluded_containers(mock_containers, None)
assert mock_containers == list(result) assert mock_containers == list(result)
exclude_labels = ['too', 'foo'] exclude_labels = [
docker_gc.ExcludeLabel(key='too', value=None),
docker_gc.ExcludeLabel(key='foo', value=None),
]
result = docker_gc.filter_excluded_containers( result = docker_gc.filter_excluded_containers(
mock_containers, mock_containers,
exclude_labels, exclude_labels,
) )
assert [mock_containers[0], mock_containers[2]] == list(result) assert [mock_containers[0], mock_containers[2]] == list(result)
exclude_labels = ['too*=lol'] exclude_labels = [
docker_gc.ExcludeLabel(key='too*', value='lol'),
]
result = docker_gc.filter_excluded_containers( result = docker_gc.filter_excluded_containers(
mock_containers, mock_containers,
exclude_labels, exclude_labels,
@ -482,6 +487,19 @@ def test_build_exclude_set():
assert exclude_set == expected assert exclude_set == expected
def test_format_exclude_labels():
exclude_label_args = [
'voo*',
'doo=poo',
]
expected = [
docker_gc.ExcludeLabel(key='voo*', value=None),
docker_gc.ExcludeLabel(key='doo', value='poo'),
]
exclude_labels = docker_gc.format_exclude_labels(exclude_label_args)
assert expected == exclude_labels
def test_build_exclude_set_empty(): def test_build_exclude_set_empty():
exclude_set = docker_gc.build_exclude_set(None, None) exclude_set = docker_gc.build_exclude_set(None, None)
assert exclude_set == set() assert exclude_set == set()
@ -500,5 +518,6 @@ def test_main(mock_client):
max_container_age=200, max_container_age=200,
exclude_image=[], exclude_image=[],
exclude_image_file=None, exclude_image_file=None,
exclude_container_label=[],
) )
docker_gc.main() docker_gc.main()