Skip to content

Commit 0563cd9

Browse files
authored
Merge pull request #641 from Altinity/customizations/24.8.14
24.8.14 Altinity Stable Pre-release
2 parents 502d039 + 3be2ae7 commit 0563cd9

File tree

504 files changed

+11341
-3074
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

504 files changed

+11341
-3074
lines changed

.github/actionlint.yml

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ self-hosted-runner:
44
- func-tester
55
- func-tester-aarch64
66
- fuzzer-unit-tester
7+
- altinity-on-demand
8+
- altinity-type-cpx51
9+
- altinity-in-ash
10+
- altinity-image-x86-system-ubuntu-22.04
711
- style-checker
812
- style-checker-aarch64
913
- release-maker

.github/actions/common_setup/action.yml

+10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ runs:
2828
run: |
2929
# to remove every leftovers
3030
sudo rm -fr "$TEMP_PATH" && mkdir -p "$TEMP_PATH"
31+
- name: Setup zram
32+
shell: bash
33+
run: |
34+
sudo modprobe zram
35+
MemTotal=$(grep -Po "(?<=MemTotal:)\s+\d+" /proc/meminfo) # KiB
36+
Percent=200
37+
ZRAM_SIZE=$(($MemTotal / 1024 / 1024 * $Percent / 100)) # Convert to GiB
38+
.github/retry.sh 30 2 sudo zramctl --size ${ZRAM_SIZE}GiB --algorithm zstd /dev/zram0
39+
sudo mkswap /dev/zram0 && sudo swapon -p 100 /dev/zram0
40+
sudo sysctl vm.swappiness=200
3141
- name: Tune vm.mmap_rnd_bits for sanitizers
3242
shell: bash
3343
run: |
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Docker setup
2+
description: Setup docker
3+
inputs:
4+
test_name:
5+
description: name of the test, used in determining ipv6 configs.
6+
default: None
7+
type: string
8+
runs:
9+
using: "composite"
10+
steps:
11+
- name: Docker IPv6 configuration
12+
shell: bash
13+
env:
14+
ipv6_subnet: ${{ contains(inputs.test_name, 'Integration') && '2001:db8:1::/64' || '2001:3984:3989::/64' }}
15+
run: |
16+
# make sure docker uses proper IPv6 config
17+
sudo touch /etc/docker/daemon.json
18+
sudo chown ubuntu:ubuntu /etc/docker/daemon.json
19+
sudo cat <<EOT > /etc/docker/daemon.json
20+
{
21+
"ipv6": true,
22+
"fixed-cidr-v6": "${{ env.ipv6_subnet }}"
23+
}
24+
EOT
25+
sudo chown root:root /etc/docker/daemon.json
26+
sudo systemctl restart docker
27+
sudo systemctl status docker
28+
- name: Docker info
29+
shell: bash
30+
run: |
31+
docker info

.github/create_combined_ci_report.py

+294
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import os
4+
from pathlib import Path
5+
from itertools import combinations
6+
import json
7+
8+
import requests
9+
from clickhouse_driver import Client
10+
import boto3
11+
from botocore.exceptions import NoCredentialsError
12+
13+
DATABASE_HOST_VAR = "CHECKS_DATABASE_HOST"
14+
DATABASE_USER_VAR = "CHECKS_DATABASE_USER"
15+
DATABASE_PASSWORD_VAR = "CHECKS_DATABASE_PASSWORD"
16+
S3_BUCKET = "altinity-build-artifacts"
17+
18+
19+
def get_checks_fails(client: Client, job_url: str):
20+
"""
21+
Get tests that did not succeed for the given job URL.
22+
Exclude checks that have status 'error' as they are counted in get_checks_errors.
23+
"""
24+
columns = (
25+
"check_status, check_name, test_status, test_name, report_url as results_link"
26+
)
27+
query = f"""SELECT {columns} FROM `gh-data`.checks
28+
WHERE task_url='{job_url}'
29+
AND test_status IN ('FAIL', 'ERROR')
30+
AND check_status!='error'
31+
ORDER BY check_name, test_name
32+
"""
33+
return client.query_dataframe(query)
34+
35+
36+
def get_checks_known_fails(client: Client, job_url: str, known_fails: dict):
37+
"""
38+
Get tests that are known to fail for the given job URL.
39+
"""
40+
assert len(known_fails) > 0, "cannot query the database with empty known fails"
41+
columns = (
42+
"check_status, check_name, test_status, test_name, report_url as results_link"
43+
)
44+
query = f"""SELECT {columns} FROM `gh-data`.checks
45+
WHERE task_url='{job_url}'
46+
AND test_status='BROKEN'
47+
AND test_name IN ({','.join(f"'{test}'" for test in known_fails.keys())})
48+
ORDER BY test_name, check_name
49+
"""
50+
51+
df = client.query_dataframe(query)
52+
53+
df.insert(
54+
len(df.columns) - 1,
55+
"reason",
56+
df["test_name"]
57+
.astype(str)
58+
.apply(
59+
lambda test_name: known_fails[test_name].get("reason", "No reason given")
60+
),
61+
)
62+
63+
return df
64+
65+
66+
def get_checks_errors(client: Client, job_url: str):
67+
"""
68+
Get checks that have status 'error' for the given job URL.
69+
"""
70+
columns = (
71+
"check_status, check_name, test_status, test_name, report_url as results_link"
72+
)
73+
query = f"""SELECT {columns} FROM `gh-data`.checks
74+
WHERE task_url='{job_url}'
75+
AND check_status=='error'
76+
ORDER BY check_name, test_name
77+
"""
78+
return client.query_dataframe(query)
79+
80+
81+
def drop_prefix_rows(df, column_to_clean):
82+
"""
83+
Drop rows from the dataframe if:
84+
- the row matches another row completely except for the specified column
85+
- the specified column of that row is a prefix of the same column in another row
86+
"""
87+
to_drop = set()
88+
reference_columns = [col for col in df.columns if col != column_to_clean]
89+
for (i, row_1), (j, row_2) in combinations(df.iterrows(), 2):
90+
if all(row_1[col] == row_2[col] for col in reference_columns):
91+
if row_2[column_to_clean].startswith(row_1[column_to_clean]):
92+
to_drop.add(i)
93+
elif row_1[column_to_clean].startswith(row_2[column_to_clean]):
94+
to_drop.add(j)
95+
return df.drop(to_drop)
96+
97+
98+
def get_regression_fails(client: Client, job_url: str):
99+
"""
100+
Get regression tests that did not succeed for the given job URL.
101+
"""
102+
# If you rename the alias for report_url, also update the formatters in format_results_as_html_table
103+
# Nested SELECT handles test reruns
104+
query = f"""SELECT arch, job_name, status, test_name, results_link
105+
FROM (
106+
SELECT
107+
architecture as arch,
108+
test_name,
109+
argMax(result, start_time) AS status,
110+
job_url,
111+
job_name,
112+
report_url as results_link
113+
FROM `gh-data`.clickhouse_regression_results
114+
GROUP BY architecture, test_name, job_url, job_name, report_url, start_time
115+
ORDER BY start_time DESC, length(test_name) DESC
116+
)
117+
WHERE job_url='{job_url}'
118+
AND status IN ('Fail', 'Error')
119+
"""
120+
df = client.query_dataframe(query)
121+
df = drop_prefix_rows(df, "test_name")
122+
df["job_name"] = df["job_name"].str.title()
123+
return df
124+
125+
126+
def url_to_html_link(url: str) -> str:
127+
if not url:
128+
return ""
129+
text = url.split("/")[-1]
130+
if not text:
131+
text = "results"
132+
return f'<a href="{url}">{text}</a>'
133+
134+
135+
def format_test_name_for_linewrap(text: str) -> str:
136+
"""Tweak the test name to improve line wrapping."""
137+
return text.replace(".py::", "/")
138+
139+
140+
def format_results_as_html_table(results) -> str:
141+
if len(results) == 0:
142+
return "<p>Nothing to report</p>"
143+
results.columns = [col.replace("_", " ").title() for col in results.columns]
144+
html = (
145+
results.to_html(
146+
index=False,
147+
formatters={
148+
"Results Link": url_to_html_link,
149+
"Test Name": format_test_name_for_linewrap,
150+
},
151+
escape=False,
152+
) # tbody/thead tags interfere with the table sorting script
153+
.replace("<tbody>\n", "")
154+
.replace("</tbody>\n", "")
155+
.replace("<thead>\n", "")
156+
.replace("</thead>\n", "")
157+
.replace('<table border="1"', '<table style="min-width: min(900px, 98vw);"')
158+
)
159+
return html
160+
161+
162+
def parse_args() -> argparse.Namespace:
163+
parser = argparse.ArgumentParser(description="Create a combined CI report.")
164+
parser.add_argument(
165+
"--actions-run-url", required=True, help="URL of the actions run"
166+
)
167+
parser.add_argument(
168+
"--pr-number", required=True, help="Pull request number for the S3 path"
169+
)
170+
parser.add_argument(
171+
"--commit-sha", required=True, help="Commit SHA for the S3 path"
172+
)
173+
parser.add_argument(
174+
"--no-upload", action="store_true", help="Do not upload the report"
175+
)
176+
parser.add_argument(
177+
"--known-fails", type=str, help="Path to the file with known fails"
178+
)
179+
parser.add_argument(
180+
"--mark-preview", action="store_true", help="Mark the report as a preview"
181+
)
182+
return parser.parse_args()
183+
184+
185+
def main():
186+
args = parse_args()
187+
188+
db_client = Client(
189+
host=os.getenv(DATABASE_HOST_VAR),
190+
user=os.getenv(DATABASE_USER_VAR),
191+
password=os.getenv(DATABASE_PASSWORD_VAR),
192+
port=9440,
193+
secure="y",
194+
verify=False,
195+
settings={"use_numpy": True},
196+
)
197+
198+
s3_path = (
199+
f"https://s3.amazonaws.com/{S3_BUCKET}/{args.pr_number}/{args.commit_sha}/"
200+
)
201+
report_destination_url = s3_path + "combined_report.html"
202+
ci_running_report_url = s3_path + "ci_running.html"
203+
204+
response = requests.get(ci_running_report_url)
205+
if response.status_code == 200:
206+
ci_running_report: str = response.text
207+
else:
208+
print(
209+
f"Failed to download CI running report. Status code: {response.status_code}, Response: {response.text}"
210+
)
211+
exit(1)
212+
213+
fail_results = {
214+
"checks_fails": get_checks_fails(db_client, args.actions_run_url),
215+
"checks_known_fails": [],
216+
"checks_errors": get_checks_errors(db_client, args.actions_run_url),
217+
"regression_fails": get_regression_fails(db_client, args.actions_run_url),
218+
}
219+
220+
if args.known_fails:
221+
if not os.path.exists(args.known_fails):
222+
print(f"Known fails file {args.known_fails} not found.")
223+
exit(1)
224+
225+
with open(args.known_fails) as f:
226+
known_fails = json.load(f)
227+
228+
if known_fails:
229+
fail_results["checks_known_fails"] = get_checks_known_fails(
230+
db_client, args.actions_run_url, known_fails
231+
)
232+
233+
combined_report = (
234+
ci_running_report.replace("ClickHouse CI running for", "Combined CI Report for")
235+
.replace(
236+
"<table>",
237+
f"""<h2>Table of Contents</h2>
238+
{'<p style="font-weight: bold;color: #F00;">This is a preview. FinishCheck has not completed.</p>' if args.mark_preview else ""}
239+
<ul>
240+
<li><a href="#ci-jobs-status">CI Jobs Status</a></li>
241+
<li><a href="#checks-errors">Checks Errors</a> ({len(fail_results['checks_errors'])})</li>
242+
<li><a href="#checks-fails">Checks New Fails</a> ({len(fail_results['checks_fails'])})</li>
243+
<li><a href="#regression-fails">Regression New Fails</a> ({len(fail_results['regression_fails'])})</li>
244+
<li><a href="#checks-known-fails">Checks Known Fails</a> ({len(fail_results['checks_known_fails'])})</li>
245+
</ul>
246+
247+
<h2 id="ci-jobs-status">CI Jobs Status</h2>
248+
<table>""",
249+
1,
250+
)
251+
.replace(
252+
"</table>",
253+
f"""</table>
254+
255+
<h2 id="checks-errors">Checks Errors</h2>
256+
{format_results_as_html_table(fail_results['checks_errors'])}
257+
258+
<h2 id="checks-fails">Checks New Fails</h2>
259+
{format_results_as_html_table(fail_results['checks_fails'])}
260+
261+
<h2 id="regression-fails">Regression New Fails</h2>
262+
{format_results_as_html_table(fail_results['regression_fails'])}
263+
264+
<h2 id="checks-known-fails">Checks Known Fails</h2>
265+
{format_results_as_html_table(fail_results['checks_known_fails'])}
266+
""",
267+
1,
268+
)
269+
)
270+
report_path = Path("combined_report.html")
271+
report_path.write_text(combined_report, encoding="utf-8")
272+
273+
if args.no_upload:
274+
print(f"Report saved to {report_path}")
275+
exit(0)
276+
277+
# Upload the report to S3
278+
s3_client = boto3.client("s3")
279+
280+
try:
281+
s3_client.put_object(
282+
Bucket=S3_BUCKET,
283+
Key=f"{args.pr_number}/{args.commit_sha}/combined_report.html",
284+
Body=combined_report,
285+
ContentType="text/html; charset=utf-8",
286+
)
287+
except NoCredentialsError:
288+
print("Credentials not available for S3 upload.")
289+
290+
print(report_destination_url)
291+
292+
293+
if __name__ == "__main__":
294+
main()

.github/retry.sh

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/bash
2+
# Execute command until exitcode is 0 or
3+
# maximum number of retries is reached
4+
# Example:
5+
# ./retry <retries> <delay> <command>
6+
retries=$1
7+
delay=$2
8+
command="${@:3}"
9+
exitcode=0
10+
try=0
11+
until [ "$try" -ge $retries ]
12+
do
13+
echo "$command"
14+
eval "$command"
15+
exitcode=$?
16+
if [ $exitcode -eq 0 ]; then
17+
break
18+
fi
19+
try=$((try+1))
20+
sleep $2
21+
done
22+
exit $exitcode

0 commit comments

Comments
 (0)