ci: Prefix tracie artifacts with the device name
[mesa.git] / .gitlab-ci / tracie / tracie.py
1 import argparse
2 import base64
3 import datetime
4 import enum
5 import glob
6 import hashlib
7 import hmac
8 import json
9 import os
10 import requests
11 import sys
12 import tempfile
13 import time
14 import yaml
15 import shutil
16
17 from email.utils import formatdate
18 from pathlib import Path
19 from PIL import Image
20 from urllib import parse
21
22 import dump_trace_images
23
24 TRACES_DB_PATH = "./traces-db/"
25 RESULTS_PATH = "./results/"
26
27 def replay(trace_path, device_name):
28 success = dump_trace_images.dump_from_trace(trace_path, [], device_name)
29
30 if not success:
31 print("[check_image] Trace %s couldn't be replayed. See above logs for more information." % (str(trace_path)))
32 return None, None, None
33 else:
34 base_path = trace_path.parent
35 file_name = trace_path.name
36 files = glob.glob(str(base_path / "test" / device_name / (file_name + "-*" + ".png")))
37 assert(files)
38 image_file = files[0]
39 files = glob.glob(str(base_path / "test" / device_name / (file_name + ".log")))
40 assert(files)
41 log_file = files[0]
42 return hashlib.md5(Image.open(image_file).tobytes()).hexdigest(), image_file, log_file
43
44 def gitlab_ensure_trace(project_url, trace):
45 trace_path = TRACES_DB_PATH + trace['path']
46 if project_url is None:
47 if not os.path.exists(trace_path):
48 print("{} missing".format(trace_path))
49 sys.exit(1)
50 return
51
52 os.makedirs(os.path.dirname(trace_path), exist_ok=True)
53
54 if os.path.exists(trace_path):
55 return
56
57 print("[check_image] Downloading trace %s" % (trace['path']), end=" ", flush=True)
58 download_time = time.time()
59 r = requests.get(project_url + trace['path'])
60 open(trace_path, "wb").write(r.content)
61 print("took %ds." % (time.time() - download_time), flush=True)
62
63 def sign_with_hmac(key, message):
64 key = key.encode("UTF-8")
65 message = message.encode("UTF-8")
66
67 signature = hmac.new(key, message, hashlib.sha1).digest()
68
69 return base64.encodebytes(signature).strip().decode()
70
71 def upload_artifact(file_name, content_type, device_name):
72 with open('.minio_credentials', 'r') as f:
73 credentials = json.load(f)["minio-packet.freedesktop.org"]
74 minio_key = credentials["AccessKeyId"]
75 minio_secret = credentials["SecretAccessKey"]
76 minio_token = credentials["SessionToken"]
77
78 resource = '/artifacts/%s/%s/%s/%s' % (os.environ['CI_PROJECT_PATH'], os.environ['CI_PIPELINE_ID'], device_name, os.path.basename(file_name))
79 date = formatdate(timeval=None, localtime=False, usegmt=True)
80 url = 'https://minio-packet.freedesktop.org%s' % (resource)
81 to_sign = "PUT\n\n%s\n%s\nx-amz-security-token:%s\n%s" % (content_type, date, minio_token, resource)
82 signature = sign_with_hmac(minio_secret, to_sign)
83
84 with open(file_name, 'rb') as data:
85 headers = {'Host': 'minio-packet.freedesktop.org',
86 'Date': date,
87 'Content-Type': content_type,
88 'Authorization': 'AWS %s:%s' % (minio_key, signature),
89 'x-amz-security-token': minio_token}
90 print("Uploading artifact to %s" % url);
91 r = requests.put(url, headers=headers, data=data)
92 #print(r.text)
93 r.raise_for_status()
94
95 def gitlab_check_trace(project_url, device_name, trace, expectation):
96 gitlab_ensure_trace(project_url, trace)
97
98 result = {}
99 result[trace['path']] = {}
100 result[trace['path']]['expected'] = expectation['checksum']
101
102 trace_path = Path(TRACES_DB_PATH + trace['path'])
103 checksum, image_file, log_file = replay(trace_path, device_name)
104 if checksum is None:
105 result[trace['path']]['actual'] = 'error'
106 return False, result
107 elif checksum == expectation['checksum']:
108 print("[check_image] Images match for %s" % (trace['path']))
109 ok = True
110 else:
111 print("[check_image] Images differ for %s (expected: %s, actual: %s)" %
112 (trace['path'], expectation['checksum'], checksum))
113 print("[check_image] For more information see "
114 "https://gitlab.freedesktop.org/mesa/mesa/blob/master/.gitlab-ci/tracie/README.md")
115 ok = False
116
117 trace_dir = os.path.split(trace['path'])[0]
118 dir_in_results = os.path.join(trace_dir, "test", device_name)
119 results_path = os.path.join(RESULTS_PATH, dir_in_results)
120 os.makedirs(results_path, exist_ok=True)
121 shutil.move(log_file, os.path.join(results_path, os.path.split(log_file)[1]))
122 if not ok and os.environ.get('TRACIE_UPLOAD_TO_MINIO', '0') == '1':
123 upload_artifact(image_file, 'image/png', device_name)
124 if not ok or os.environ.get('TRACIE_STORE_IMAGES', '0') == '1':
125 image_name = os.path.split(image_file)[1]
126 shutil.move(image_file, os.path.join(results_path, image_name))
127 result[trace['path']]['image'] = os.path.join(dir_in_results, image_name)
128
129 result[trace['path']]['actual'] = checksum
130
131 return ok, result
132
133 def run(filename, device_name):
134
135 with open(filename, 'r') as f:
136 y = yaml.safe_load(f)
137
138 if "traces-db" in y:
139 project_url = y["traces-db"]["download-url"]
140 else:
141 project_url = None
142
143 traces = y['traces'] or []
144 all_ok = True
145 results = {}
146 for trace in traces:
147 for expectation in trace['expectations']:
148 if expectation['device'] == device_name:
149 ok, result = gitlab_check_trace(project_url,
150 device_name, trace,
151 expectation)
152 all_ok = all_ok and ok
153 results.update(result)
154
155 os.makedirs(RESULTS_PATH, exist_ok=True)
156 with open(os.path.join(RESULTS_PATH, 'results.yml'), 'w') as f:
157 yaml.safe_dump(results, f, default_flow_style=False)
158 if os.environ.get('TRACIE_UPLOAD_TO_MINIO', '0') == '1':
159 upload_artifact(os.path.join(RESULTS_PATH, 'results.yml'), 'text/yaml', device_name)
160
161 return all_ok
162
163 def main(args):
164 parser = argparse.ArgumentParser()
165 parser.add_argument('--file', required=True,
166 help='the name of the traces.yml file listing traces and their checksums for each device')
167 parser.add_argument('--device-name', required=True,
168 help="the name of the graphics device used to replay traces")
169
170 args = parser.parse_args(args)
171 return run(args.file, args.device_name)
172
173 if __name__ == "__main__":
174 all_ok = main(sys.argv[1:])
175 sys.exit(0 if all_ok else 1)