2021-11-12 11:07:54 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import json
|
|
|
|
import logging
|
2022-09-07 17:20:22 +00:00
|
|
|
import os
|
2021-11-12 11:07:54 +00:00
|
|
|
import sys
|
|
|
|
import time
|
2023-01-27 15:10:10 +00:00
|
|
|
from pathlib import Path
|
2023-08-29 14:35:53 +00:00
|
|
|
from typing import Any, Callable, List, Union
|
2021-11-12 11:07:54 +00:00
|
|
|
|
2022-01-26 11:10:20 +00:00
|
|
|
import requests # type: ignore
|
2021-11-12 11:07:54 +00:00
|
|
|
|
2023-05-02 10:37:05 +00:00
|
|
|
import get_robot_token as grt # we need an updated ROBOT_TOKEN
|
2021-11-26 10:57:36 +00:00
|
|
|
from ci_config import CI_CONFIG
|
2021-11-12 11:07:54 +00:00
|
|
|
|
|
|
|
DOWNLOAD_RETRIES_COUNT = 5
|
|
|
|
|
2022-01-26 11:10:20 +00:00
|
|
|
|
2023-08-10 17:12:09 +00:00
|
|
|
class DownloadException(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2022-01-26 12:20:08 +00:00
|
|
|
def get_with_retries(
|
|
|
|
url: str,
|
|
|
|
retries: int = DOWNLOAD_RETRIES_COUNT,
|
|
|
|
sleep: int = 3,
|
2022-11-10 16:11:23 +00:00
|
|
|
**kwargs: Any,
|
2022-01-26 12:20:08 +00:00
|
|
|
) -> requests.Response:
|
2022-06-30 09:58:24 +00:00
|
|
|
logging.info(
|
|
|
|
"Getting URL with %i tries and sleep %i in between: %s", retries, sleep, url
|
|
|
|
)
|
2023-04-28 16:26:50 +00:00
|
|
|
exc = Exception("A placeholder to satisfy typing and avoid nesting")
|
2022-06-30 09:58:24 +00:00
|
|
|
for i in range(retries):
|
2022-01-26 12:20:08 +00:00
|
|
|
try:
|
|
|
|
response = requests.get(url, **kwargs)
|
|
|
|
response.raise_for_status()
|
2023-04-28 16:26:50 +00:00
|
|
|
return response
|
2022-01-26 12:20:08 +00:00
|
|
|
except Exception as e:
|
2022-06-30 09:58:24 +00:00
|
|
|
if i + 1 < retries:
|
2022-01-26 12:20:08 +00:00
|
|
|
logging.info("Exception '%s' while getting, retry %i", e, i + 1)
|
|
|
|
time.sleep(sleep)
|
|
|
|
|
|
|
|
exc = e
|
|
|
|
|
2023-04-28 16:26:50 +00:00
|
|
|
raise exc
|
|
|
|
|
|
|
|
|
|
|
|
def get_gh_api(
|
|
|
|
url: str,
|
|
|
|
retries: int = DOWNLOAD_RETRIES_COUNT,
|
|
|
|
sleep: int = 3,
|
|
|
|
**kwargs: Any,
|
|
|
|
) -> requests.Response:
|
2023-10-17 08:18:17 +00:00
|
|
|
"""
|
|
|
|
Request GH api w/o auth by default, and failover to the get_best_robot_token in case of receiving
|
|
|
|
"403 rate limit exceeded" or "404 not found" error
|
2023-10-11 17:25:36 +00:00
|
|
|
It sets auth automatically when ROBOT_TOKEN is already set by get_best_robot_token
|
2023-04-28 16:26:50 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
def set_auth_header():
|
|
|
|
if "headers" in kwargs:
|
|
|
|
if "Authorization" not in kwargs["headers"]:
|
2023-05-02 10:37:05 +00:00
|
|
|
kwargs["headers"][
|
|
|
|
"Authorization"
|
|
|
|
] = f"Bearer {grt.get_best_robot_token()}"
|
2023-04-28 16:26:50 +00:00
|
|
|
else:
|
2023-05-02 10:37:05 +00:00
|
|
|
kwargs["headers"] = {
|
|
|
|
"Authorization": f"Bearer {grt.get_best_robot_token()}"
|
|
|
|
}
|
2023-04-28 16:26:50 +00:00
|
|
|
|
2023-05-02 10:37:05 +00:00
|
|
|
if grt.ROBOT_TOKEN is not None:
|
2023-04-28 16:26:50 +00:00
|
|
|
set_auth_header()
|
|
|
|
|
2023-10-17 08:18:17 +00:00
|
|
|
token_is_set = "Authorization" in kwargs.get("headers", {})
|
|
|
|
exc = Exception("A placeholder to satisfy typing and avoid nesting")
|
|
|
|
try_cnt = 0
|
|
|
|
while try_cnt < retries:
|
|
|
|
try_cnt += 1
|
2023-04-28 16:26:50 +00:00
|
|
|
try:
|
2023-10-17 08:18:17 +00:00
|
|
|
response = requests.get(url, **kwargs)
|
2023-04-28 16:26:50 +00:00
|
|
|
response.raise_for_status()
|
|
|
|
return response
|
2023-10-17 08:18:17 +00:00
|
|
|
except requests.HTTPError as e:
|
|
|
|
exc = e
|
|
|
|
ratelimit_exceeded = (
|
|
|
|
e.response.status_code == 403
|
2023-10-12 17:32:11 +00:00
|
|
|
and b"rate limit exceeded"
|
2023-10-17 08:18:17 +00:00
|
|
|
in e.response._content # pylint:disable=protected-access
|
|
|
|
)
|
|
|
|
try_auth = e.response.status_code == 404
|
|
|
|
if (ratelimit_exceeded or try_auth) and not token_is_set:
|
2023-04-28 16:26:50 +00:00
|
|
|
logging.warning(
|
2023-10-12 17:32:11 +00:00
|
|
|
"Received rate limit exception, setting the auth header and retry"
|
2023-04-28 16:26:50 +00:00
|
|
|
)
|
|
|
|
set_auth_header()
|
2023-10-17 08:18:17 +00:00
|
|
|
token_is_set = True
|
|
|
|
try_cnt = 0
|
|
|
|
continue
|
|
|
|
except Exception as e:
|
|
|
|
exc = e
|
2023-10-11 16:00:48 +00:00
|
|
|
|
2023-10-17 08:18:17 +00:00
|
|
|
if try_cnt < retries:
|
|
|
|
logging.info("Exception '%s' while getting, retry %i", exc, try_cnt)
|
|
|
|
time.sleep(sleep)
|
|
|
|
|
|
|
|
raise exc
|
2022-01-26 12:20:08 +00:00
|
|
|
|
|
|
|
|
2022-11-10 16:11:23 +00:00
|
|
|
def get_build_name_for_check(check_name: str) -> str:
|
2023-08-02 16:27:14 +00:00
|
|
|
return CI_CONFIG.test_configs[check_name].required_build
|
2022-01-26 11:10:20 +00:00
|
|
|
|
2021-11-12 11:07:54 +00:00
|
|
|
|
2023-08-29 14:35:53 +00:00
|
|
|
def read_build_urls(build_name: str, reports_path: Union[Path, str]) -> List[str]:
|
2021-11-12 11:07:54 +00:00
|
|
|
for root, _, files in os.walk(reports_path):
|
2024-02-08 23:46:12 +00:00
|
|
|
for file in files:
|
|
|
|
if file.endswith(f"_{build_name}.json"):
|
|
|
|
logging.info("Found build report json %s", file)
|
|
|
|
with open(os.path.join(root, file), "r", encoding="utf-8") as file_handler:
|
2021-11-12 11:07:54 +00:00
|
|
|
build_report = json.load(file_handler)
|
2022-11-10 16:11:23 +00:00
|
|
|
return build_report["build_urls"] # type: ignore
|
2021-11-12 11:07:54 +00:00
|
|
|
return []
|
|
|
|
|
2022-01-26 11:10:20 +00:00
|
|
|
|
2023-01-27 15:10:10 +00:00
|
|
|
def download_build_with_progress(url: str, path: Path) -> None:
|
2021-11-12 11:07:54 +00:00
|
|
|
logging.info("Downloading from %s to temp path %s", url, path)
|
|
|
|
for i in range(DOWNLOAD_RETRIES_COUNT):
|
|
|
|
try:
|
2023-01-27 15:10:10 +00:00
|
|
|
response = get_with_retries(url, retries=1, stream=True)
|
|
|
|
total_length = int(response.headers.get("content-length", 0))
|
|
|
|
if path.is_file() and total_length and path.stat().st_size == total_length:
|
|
|
|
logging.info(
|
|
|
|
"The file %s already exists and have a proper size %s",
|
|
|
|
path,
|
|
|
|
total_length,
|
|
|
|
)
|
|
|
|
return
|
|
|
|
|
2022-01-26 11:10:20 +00:00
|
|
|
with open(path, "wb") as f:
|
2023-01-27 15:10:10 +00:00
|
|
|
if total_length == 0:
|
2022-01-26 11:10:20 +00:00
|
|
|
logging.info(
|
|
|
|
"No content-length, will download file without progress"
|
|
|
|
)
|
2021-11-12 11:07:54 +00:00
|
|
|
f.write(response.content)
|
|
|
|
else:
|
|
|
|
dl = 0
|
2023-01-27 15:10:10 +00:00
|
|
|
|
2021-11-12 11:07:54 +00:00
|
|
|
logging.info("Content length is %ld bytes", total_length)
|
|
|
|
for data in response.iter_content(chunk_size=4096):
|
|
|
|
dl += len(data)
|
|
|
|
f.write(data)
|
|
|
|
if sys.stdout.isatty():
|
|
|
|
done = int(50 * dl / total_length)
|
|
|
|
percent = int(100 * float(dl) / total_length)
|
2022-01-26 11:10:20 +00:00
|
|
|
eq_str = "=" * done
|
|
|
|
space_str = " " * (50 - done)
|
2021-11-12 11:07:54 +00:00
|
|
|
sys.stdout.write(f"\r[{eq_str}{space_str}] {percent}%")
|
|
|
|
sys.stdout.flush()
|
|
|
|
break
|
2023-09-27 16:24:56 +00:00
|
|
|
except Exception as e:
|
2022-01-26 12:20:08 +00:00
|
|
|
if sys.stdout.isatty():
|
|
|
|
sys.stdout.write("\n")
|
2021-11-12 11:07:54 +00:00
|
|
|
if os.path.exists(path):
|
|
|
|
os.remove(path)
|
2023-09-27 16:24:56 +00:00
|
|
|
|
|
|
|
if i + 1 < DOWNLOAD_RETRIES_COUNT:
|
|
|
|
time.sleep(3)
|
|
|
|
else:
|
|
|
|
raise DownloadException(
|
|
|
|
f"Cannot download dataset from {url}, all retries exceeded"
|
|
|
|
) from e
|
2021-11-12 11:07:54 +00:00
|
|
|
|
2022-01-26 12:20:08 +00:00
|
|
|
if sys.stdout.isatty():
|
|
|
|
sys.stdout.write("\n")
|
2021-11-12 11:07:54 +00:00
|
|
|
logging.info("Downloading finished")
|
|
|
|
|
|
|
|
|
2023-01-27 15:10:10 +00:00
|
|
|
def download_builds(
|
|
|
|
result_path: str, build_urls: List[str], filter_fn: Callable[[str], bool]
|
|
|
|
) -> None:
|
2021-11-12 11:07:54 +00:00
|
|
|
for url in build_urls:
|
|
|
|
if filter_fn(url):
|
2022-01-26 11:10:20 +00:00
|
|
|
fname = os.path.basename(url.replace("%2B", "+").replace("%20", " "))
|
2021-11-12 11:07:54 +00:00
|
|
|
logging.info("Will download %s to %s", fname, result_path)
|
2023-01-27 15:10:10 +00:00
|
|
|
download_build_with_progress(url, Path(result_path) / fname)
|
2021-11-12 11:07:54 +00:00
|
|
|
|
2022-01-26 11:10:20 +00:00
|
|
|
|
|
|
|
def download_builds_filter(
|
|
|
|
check_name, reports_path, result_path, filter_fn=lambda _: True
|
|
|
|
):
|
2021-11-26 10:57:36 +00:00
|
|
|
build_name = get_build_name_for_check(check_name)
|
2022-09-07 13:06:44 +00:00
|
|
|
urls = read_build_urls(build_name, reports_path)
|
2021-11-12 19:57:26 +00:00
|
|
|
print(urls)
|
2021-11-12 11:07:54 +00:00
|
|
|
|
|
|
|
if not urls:
|
2023-08-10 17:12:09 +00:00
|
|
|
raise DownloadException("No build URLs found")
|
2021-11-12 11:07:54 +00:00
|
|
|
|
|
|
|
download_builds(result_path, urls, filter_fn)
|
|
|
|
|
2022-01-26 11:10:20 +00:00
|
|
|
|
2021-11-12 11:07:54 +00:00
|
|
|
def download_all_deb_packages(check_name, reports_path, result_path):
|
2022-01-26 11:10:20 +00:00
|
|
|
download_builds_filter(
|
|
|
|
check_name, reports_path, result_path, lambda x: x.endswith("deb")
|
|
|
|
)
|
|
|
|
|
2021-11-12 11:07:54 +00:00
|
|
|
|
|
|
|
def download_unit_tests(check_name, reports_path, result_path):
|
2022-01-26 11:10:20 +00:00
|
|
|
download_builds_filter(
|
|
|
|
check_name, reports_path, result_path, lambda x: x.endswith("unit_tests_dbms")
|
|
|
|
)
|
|
|
|
|
2021-11-12 11:07:54 +00:00
|
|
|
|
|
|
|
def download_clickhouse_binary(check_name, reports_path, result_path):
|
2022-01-26 11:10:20 +00:00
|
|
|
download_builds_filter(
|
|
|
|
check_name, reports_path, result_path, lambda x: x.endswith("clickhouse")
|
|
|
|
)
|
|
|
|
|
2021-12-09 09:04:05 +00:00
|
|
|
|
|
|
|
def download_performance_build(check_name, reports_path, result_path):
|
2022-01-26 11:10:20 +00:00
|
|
|
download_builds_filter(
|
2023-01-09 01:08:38 +00:00
|
|
|
check_name,
|
|
|
|
reports_path,
|
|
|
|
result_path,
|
|
|
|
lambda x: x.endswith("performance.tar.zst"),
|
2022-01-26 11:10:20 +00:00
|
|
|
)
|
2023-09-10 17:07:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
def download_fuzzers(check_name, reports_path, result_path):
|
|
|
|
download_builds_filter(
|
2023-09-11 22:45:50 +00:00
|
|
|
check_name,
|
|
|
|
reports_path,
|
|
|
|
result_path,
|
|
|
|
lambda x: x.endswith(("_fuzzer", ".dict", ".options", "_seed_corpus.zip")),
|
2023-09-10 17:07:49 +00:00
|
|
|
)
|