From e24ba47e0ad1c0f47de16281b077530f955ca906 Mon Sep 17 00:00:00 2001 From: celestialorb Date: Fri, 8 Sep 2023 15:24:56 -0700 Subject: [PATCH 1/9] stupid simple retry logic for download --- bazelisk.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/bazelisk.py b/bazelisk.py index 8a967c27..3c8bcde9 100755 --- a/bazelisk.py +++ b/bazelisk.py @@ -180,8 +180,7 @@ def get_version_history(bazelisk_directory): ), # This only handles versions with numeric components, but that is fine # since prerelease versions have been excluded. - key=lambda version: tuple(int(component) - for component in version.split('.')), + key=lambda version: tuple(int(component) for component in version.split(".")), reverse=True, ) @@ -339,21 +338,30 @@ def download_bazel_into_directory(version, is_commit, directory): return destination_path -def download(url, destination_path): - sys.stderr.write("Downloading {}...\n".format(url)) - request = Request(url) - if "BAZELISK_BASE_URL" in os.environ: - parts = urlparse(url) - creds = None +def download(url, destination_path, retries=5, wait_seconds=5): + while retries > 0: + retries -= 1 try: - creds = netrc.netrc().hosts.get(parts.netloc) + sys.stderr.write("Downloading {}...\n".format(url)) + request = Request(url) + if "BAZELISK_BASE_URL" in os.environ: + parts = urlparse(url) + creds = None + try: + creds = netrc.netrc().hosts.get(parts.netloc) + except Exception: + pass + if creds is not None: + auth = base64.b64encode(("%s:%s" % (creds[0], creds[2])).encode("ascii")) + request.add_header("Authorization", "Basic %s" % auth.decode("utf-8")) + + with closing(urlopen(request)) as response, open(destination_path, "wb") as file: + shutil.copyfileobj(response, file) + return except Exception: - pass - if creds is not None: - auth = base64.b64encode(("%s:%s" % (creds[0], creds[2])).encode("ascii")) - request.add_header("Authorization", "Basic %s" % auth.decode("utf-8")) - with closing(urlopen(request)) as response, open(destination_path, "wb") as file: - shutil.copyfileobj(response, file) + if retries <= 0: + raise + time.sleep(wait_seconds) def get_bazelisk_directory(): From c4a3c35dc510dd69941efbfd3dd20e7ea3d25759 Mon Sep 17 00:00:00 2001 From: celestialorb Date: Thu, 14 Sep 2023 10:22:43 -0700 Subject: [PATCH 2/9] reworking retry implementation --- bazelisk.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/bazelisk.py b/bazelisk.py index 3c8bcde9..27030c95 100755 --- a/bazelisk.py +++ b/bazelisk.py @@ -339,29 +339,32 @@ def download_bazel_into_directory(version, is_commit, directory): def download(url, destination_path, retries=5, wait_seconds=5): - while retries > 0: - retries -= 1 + request = Request(url) + if "BAZELISK_BASE_URL" in os.environ: + parts = urlparse(url) + + # Include credentials in the request if found. + creds = None try: - sys.stderr.write("Downloading {}...\n".format(url)) - request = Request(url) - if "BAZELISK_BASE_URL" in os.environ: - parts = urlparse(url) - creds = None - try: - creds = netrc.netrc().hosts.get(parts.netloc) - except Exception: - pass - if creds is not None: - auth = base64.b64encode(("%s:%s" % (creds[0], creds[2])).encode("ascii")) - request.add_header("Authorization", "Basic %s" % auth.decode("utf-8")) + creds = netrc.netrc().hosts.get(parts.netloc) + except Exception: + pass + if creds is not None: + auth = base64.b64encode(("%s:%s" % (creds[0], creds[2])).encode("ascii")) + request.add_header("Authorization", "Basic %s" % auth.decode("utf-8")) + # Attempt to download the Bazel binary with a rudimentary retry implementation. + for _ in range(retries): + try: + sys.stderr.write("Downloading {}...\n".format(url)) with closing(urlopen(request)) as response, open(destination_path, "wb") as file: shutil.copyfileobj(response, file) return - except Exception: - if retries <= 0: - raise + except Exception as ex: + print("failed to download Bazel binary: {}".format(ex)) time.sleep(wait_seconds) + else: + raise RuntimeError("all attempts to download Bazel binary failed") def get_bazelisk_directory(): From 3ae7607f5e6496217e2b0a8f8c5e11c84c1cea1b Mon Sep 17 00:00:00 2001 From: celestialorb Date: Thu, 14 Sep 2023 10:28:24 -0700 Subject: [PATCH 3/9] moving wait to finally clause --- bazelisk.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bazelisk.py b/bazelisk.py index 27030c95..b0fd52f0 100755 --- a/bazelisk.py +++ b/bazelisk.py @@ -362,6 +362,7 @@ def download(url, destination_path, retries=5, wait_seconds=5): return except Exception as ex: print("failed to download Bazel binary: {}".format(ex)) + finally: time.sleep(wait_seconds) else: raise RuntimeError("all attempts to download Bazel binary failed") From e3aee339422bd78ee18b51c62516285cd5d77e88 Mon Sep 17 00:00:00 2001 From: celestialorb Date: Thu, 14 Sep 2023 10:48:15 -0700 Subject: [PATCH 4/9] adding environmental configuration of retry logic --- bazelisk.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/bazelisk.py b/bazelisk.py index b0fd52f0..78307243 100755 --- a/bazelisk.py +++ b/bazelisk.py @@ -292,6 +292,10 @@ def trim_suffix(string, suffix): def download_bazel_into_directory(version, is_commit, directory): + # Obtain configuration settings to control download retry logic. + retry_attempts = int(os.getenv(key="BAZELISK_DOWNLOAD_RETRY_ATTEMPTS", default="5")) + retry_wait_secs = int(os.getenv(key="BAZELISK_DOWNLOAD_RETRY_WAIT_SECS", default="5")) + bazel_filename = determine_bazel_filename(version) bazel_url = determine_url(version, is_commit, bazel_filename) @@ -302,14 +306,24 @@ def download_bazel_into_directory(version, is_commit, directory): destination_path = os.path.join(destination_dir, "bazel" + filename_suffix) if not os.path.exists(destination_path): - download(bazel_url, destination_path) + download( + url=bazel_url, + destination_path=destination_path, + retries=retry_attempts, + wait_seconds=retry_wait_secs, + ) os.chmod(destination_path, 0o755) sha256_path = destination_path + ".sha256" expected_hash = "" if not os.path.exists(sha256_path): try: - download(bazel_url + ".sha256", sha256_path) + download( + url=bazel_url + ".sha256", + destination_path=sha256_path, + retries=retry_attempts, + wait_seconds=retry_wait_secs, + ) except HTTPError as e: if e.code == 404: sys.stderr.write( From 2cabd659e7b3a83074f93cf9574fd56c528d58be Mon Sep 17 00:00:00 2001 From: celestialorb Date: Thu, 14 Sep 2023 11:04:09 -0700 Subject: [PATCH 5/9] updating runtime error message --- bazelisk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazelisk.py b/bazelisk.py index 78307243..69075e70 100755 --- a/bazelisk.py +++ b/bazelisk.py @@ -379,7 +379,7 @@ def download(url, destination_path, retries=5, wait_seconds=5): finally: time.sleep(wait_seconds) else: - raise RuntimeError("all attempts to download Bazel binary failed") + raise RuntimeError("all attempts to download Bazel remote resource failed") def get_bazelisk_directory(): From adef377fd5874fde7eccbf6341db8f6b397b624c Mon Sep 17 00:00:00 2001 From: celestialorb Date: Thu, 14 Sep 2023 11:05:21 -0700 Subject: [PATCH 6/9] early exception raise on an HTTP 404 response --- bazelisk.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/bazelisk.py b/bazelisk.py index 69075e70..a42610a8 100755 --- a/bazelisk.py +++ b/bazelisk.py @@ -371,9 +371,16 @@ def download(url, destination_path, retries=5, wait_seconds=5): for _ in range(retries): try: sys.stderr.write("Downloading {}...\n".format(url)) - with closing(urlopen(request)) as response, open(destination_path, "wb") as file: - shutil.copyfileobj(response, file) - return + try: + response = urlopen(request) + with open(destination_path, "wb") as file: + shutil.copyfileobj(response, file) + return + except HTTPError as exception: + if exception.code == 404: + raise + finally: + response.close() except Exception as ex: print("failed to download Bazel binary: {}".format(ex)) finally: From b92db3808150f613b1743e51b39943b05e4c4ef3 Mon Sep 17 00:00:00 2001 From: celestialorb Date: Thu, 14 Sep 2023 11:22:51 -0700 Subject: [PATCH 7/9] updating exception message --- bazelisk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazelisk.py b/bazelisk.py index a42610a8..a6af8427 100755 --- a/bazelisk.py +++ b/bazelisk.py @@ -382,7 +382,7 @@ def download(url, destination_path, retries=5, wait_seconds=5): finally: response.close() except Exception as ex: - print("failed to download Bazel binary: {}".format(ex)) + print("failed to download Bazel resource: {}".format(ex)) finally: time.sleep(wait_seconds) else: From f8a7f119bb7c1ffcbefab7fa318b8270b8b42f40 Mon Sep 17 00:00:00 2001 From: celestialorb Date: Thu, 14 Sep 2023 11:25:07 -0700 Subject: [PATCH 8/9] raising 404 for outer logic control --- bazelisk.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bazelisk.py b/bazelisk.py index a6af8427..3b45863d 100755 --- a/bazelisk.py +++ b/bazelisk.py @@ -371,16 +371,18 @@ def download(url, destination_path, retries=5, wait_seconds=5): for _ in range(retries): try: sys.stderr.write("Downloading {}...\n".format(url)) + response = None try: response = urlopen(request) with open(destination_path, "wb") as file: shutil.copyfileobj(response, file) return - except HTTPError as exception: - if exception.code == 404: - raise finally: - response.close() + if response: + response.close() + except HTTPError as ex: + if ex.code == 404: + raise except Exception as ex: print("failed to download Bazel resource: {}".format(ex)) finally: From 6233ada7ad611eea85eb53df789c9cdb326ec0fd Mon Sep 17 00:00:00 2001 From: celestialorb Date: Sat, 21 Oct 2023 17:55:29 -0700 Subject: [PATCH 9/9] attempt to retrigger job