forked from GPUOpen-Drivers/llpc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgc-amdvlk-docker-images.py
executable file
·127 lines (104 loc) · 4.14 KB
/
gc-amdvlk-docker-images.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#! /usr/bin/env python3
"""Script to garbage collect old amdvlk docker images created by the public CI on GitHub.
Requires python 3.8 or later.
"""
import argparse
import json
import logging
import subprocess
import sys
from collections import defaultdict
from typing import Any, Dict, List, Optional, Tuple
def _run_cmd(cmd: List[str]) -> Tuple[bool, str]:
"""
Runs a shell command capturing its output.
Args:
cmd: List of strings to invoke as a subprocess
Returns:
Tuple: success, stdout. The first value is True on success.
"""
logging.info('Running command: %s', ' '.join(cmd))
result = subprocess.run(cmd, capture_output=True, check=False, text=True)
if result.returncode != 0:
logging.info('%s', result.stderr)
return False, ''
return True, result.stdout
def query_images(artifact_repository_url: str) -> Optional[List[Dict[str, Any]]]:
"""
Returns a list of JSON objects representing docker images found under
|artifact_repository_url|, or None on error.
Sample JSON object:
{
"createTime": "2022-07-11T20:20:23.577823Z",
"package": "us-docker.pkg.dev/stadia-open-source/amdvlk-public-ci/amdvlk_release_gcc_assertions",
"tags": "",
"updateTime": "2022-07-11T20:20:23.577823Z",
"version": "sha256:e101b6336fa78014e4008df59667dd84616dc8d1b60c2240f3246ab9a1ed6b20"
}
"""
ok, text = _run_cmd(['gcloud', 'artifacts', 'docker', 'images', 'list',
artifact_repository_url, '--format=json', '--quiet'])
if not ok:
return None
return list(json.loads(text))
def find_images_to_gc(images: List[Dict[str, Any]], num_last_to_keep) -> List[Dict[str, Any]]:
"""
Returns a subset of |images| that should be garbage collected. Preserves tagged
images and also the most recent |num_last_to_keep| for each package.
"""
package_to_images = defaultdict(list)
for image in images:
package_to_images[image['package']].append(image)
to_gc = []
for _, images in package_to_images.items():
# Because the time format is ISO 8601, the lexicographic order is also chronological.
images.sort(key=lambda x: x['createTime'])
for image in images[:-num_last_to_keep]:
if not image['tags']:
to_gc.append(image)
return to_gc
def delete_images(images: List[Dict[str, Any]], dry_run: bool) -> None:
"""
Deletes all |images| from the repository. When |dry_run| is True, synthesizes the delete
commands and logs but does not execute them.
"""
for image in images:
image_path = image['package'] + '@' + image['version']
cmd = ['gcloud', 'artifacts', 'docker', 'images', 'delete',
image_path, '--quiet']
if dry_run:
logging.info('Dry run: %s', ' '.join(cmd))
continue
ok, _ = _run_cmd(cmd)
if not ok:
logging.warning('Failed to delete image:\n%s', image)
def main() -> int:
logging.basicConfig(
format='%(levelname)s %(asctime)s %(filename)s:%(lineno)d %(message)s',
level=logging.INFO)
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'--dry-run',
action='store_true',
default=False,
help='Do not delete or modify any docker images (default: %(default)s).')
parser.add_argument(
'--keep-last',
default=8,
help='The number of the most recent images to keep for each package (default: %(default)s).')
parser.add_argument(
'--repository-url',
default='us-docker.pkg.dev/stadia-open-source/amdvlk-public-ci',
help='The repository with docker images to garbage collect (default: %(default)s).'
)
args = parser.parse_args()
all_images = query_images(args.repository_url)
if all_images is None:
logging.error('Failed to list docker images under \'%s\'', args.repository_url)
return 1
to_gc = find_images_to_gc(all_images, args.keep_last)
delete_images(to_gc, args.dry_run)
return 0
if __name__ == '__main__':
sys.exit(main())