mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2025-12-06 09:12:45 +01:00
89 lines
3.1 KiB
Python
Executable File
89 lines
3.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# Copyright 2014,2015,2016,2017,2018,2019,2020,2021 Security Onion Solutions, LLC
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import sys, argparse, re, docker
|
|
from packaging.version import Version, InvalidVersion
|
|
from itertools import groupby, chain
|
|
|
|
|
|
def get_image_name(string) -> str:
|
|
return ':'.join(string.split(':')[:-1])
|
|
|
|
|
|
def get_so_image_basename(string) -> str:
|
|
return get_image_name(string).split('/so-')[-1]
|
|
|
|
|
|
def get_image_version(string) -> str:
|
|
ver = string.split(':')[-1]
|
|
if ver == 'latest':
|
|
# Version doesn't like "latest", so use a high semver
|
|
return '999999.9.9'
|
|
else:
|
|
try:
|
|
Version(ver)
|
|
except InvalidVersion:
|
|
# Strip the last substring following a hyphen for automated branches
|
|
ver = '-'.join(ver.split('-')[:-1])
|
|
return ver
|
|
|
|
|
|
def main(quiet):
|
|
client = docker.from_env()
|
|
|
|
image_list = client.images.list(filters={ 'dangling': False })
|
|
|
|
# Map list of image objects to flattened list of tags (format: "name:version")
|
|
tag_list = list(chain.from_iterable(list(map(lambda x: x.attrs.get('RepoTags'), image_list))))
|
|
|
|
# Filter to only SO images (base name begins with "so-")
|
|
tag_list = list(filter(lambda x: re.match(r'^.*\/so-[^\/]*$', get_image_name(x)), tag_list))
|
|
|
|
# Group tags into lists by base name (sort by same projection first)
|
|
tag_list.sort(key=lambda x: get_so_image_basename(x))
|
|
grouped_tag_lists = [ list(it) for _, it in groupby(tag_list, lambda x: get_so_image_basename(x)) ]
|
|
|
|
no_prunable = True
|
|
for t_list in grouped_tag_lists:
|
|
try:
|
|
# Group tags by version, in case multiple images exist with the same version string
|
|
t_list.sort(key=lambda x: Version(get_image_version(x)), reverse=True)
|
|
grouped_t_list = [ list(it) for _,it in groupby(t_list, lambda x: get_image_version(x)) ]
|
|
|
|
# Keep the 2 most current version groups
|
|
if len(grouped_t_list) <= 2:
|
|
continue
|
|
else:
|
|
no_prunable = False
|
|
for group in grouped_t_list[2:]:
|
|
for tag in group:
|
|
if not quiet: print(f'Removing image {tag}')
|
|
client.images.remove(tag)
|
|
except InvalidVersion as e:
|
|
print(f'so-{get_so_image_basename(t_list[0])}: {e.args[0]}', file=sys.stderr)
|
|
exit(1)
|
|
|
|
if no_prunable and not quiet:
|
|
print('No Security Onion images to prune')
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main_parser = argparse.ArgumentParser(add_help=False)
|
|
main_parser.add_argument('-q', '--quiet', action='store_const', const=True, required=False)
|
|
args = main_parser.parse_args(sys.argv[1:])
|
|
|
|
main(args.quiet) |