Skip to content

Commit

Permalink
Enable retries and connection timeouts when using curl (#1335)
Browse files Browse the repository at this point in the history
In the shimmed CNBs used in `heroku/builder` we have been seeing
quite a few transient errors related to buildpacks downloading from S3.

Adding appropriate retries and connection timeouts to all of our
buildpack's curl usages should help with these, as well as make builds
more reliable in general for users on Heroku, plus also anyone using a
shimmed CNB locally with Pack CLI (where the network connection may
be even less reliable).

The `--retry-connrefused` has been used since otherwise curl doesn't
retry cases where the connection was refused. Ideally we would use
`--retry-all-errors` which takes that one step further, however that
option was only added in curl 7.71, so is only supported by Heroku-22+.

I've intentionally not added `--max-time` to usages within buildpack
compile, since otherwise users running the buildpack locally on slow
connections may permanently hit timeouts (and timeouts mid-download
occur so rarely it's not worth the hassle of making this configurable).

For more on curl options, see:
https://curl.se/docs/manpage.html

GUS-W-11283397.
  • Loading branch information
edmorley authored Jun 13, 2022
1 parent 2b0805e commit c6ebbec
Show file tree
Hide file tree
Showing 4 changed files with 12 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- run:
name: Install shellcheck
command: |
curl -L --fail --retry 3 --retry-delay 1 --connect-timeout 3 --max-time 30 \
curl -L --fail --retry 3 --retry-connrefused --connect-timeout 5 --max-time 30 \
https://github.com/koalaman/shellcheck/releases/download/v0.8.0/shellcheck-v0.8.0.linux.x86_64.tar.xz \
| sudo tar -xJ --strip-components=1 -C /usr/local/bin
- run:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Enable retries and connection timeouts when using `curl` ([#1335](https://github.com/heroku/heroku-buildpack-python/pull/1335)).
- Correct the error message shown when downloading a valid Python version fails ([#1335](https://github.com/heroku/heroku-buildpack-python/pull/1335)).
- Switch to the recommended regional S3 domain instead of the global one ([#1334](https://github.com/heroku/heroku-buildpack-python/pull/1334)).

## v212 (2022-06-07)
Expand Down
13 changes: 7 additions & 6 deletions bin/steps/python
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ runtime-fixer runtime.txt
PYTHON_VERSION=$(cat runtime.txt)

# The location of the pre-compiled python binary.
VENDORED_PYTHON="${S3_BASE_URL}/${STACK}/runtimes/${PYTHON_VERSION}.tar.gz"
PYTHON_URL="${S3_BASE_URL}/${STACK}/runtimes/${PYTHON_VERSION}.tar.gz"

SECURITY_UPDATE="Python has released a security update! Please consider upgrading to"
SECURITY_UPDATE_PYPY="The PyPy project has released a security update! Please consider upgrading to"
Expand All @@ -15,7 +15,7 @@ ONLY_SUPPORTED_2_VERSION="Only the latest version of Python 2 is supported on th
PYTHON_2_EOL_UPDATE="Python 2 has reached its community EOL. Upgrade your Python runtime to maintain a secure application as soon as possible."

# check if runtime exists
if curl --output /dev/null --silent --head --fail "$VENDORED_PYTHON"; then
if curl --output /dev/null --silent --head --fail --retry 3 --retry-connrefused --connect-timeout 5 "${PYTHON_URL}"; then
if [[ "$PYTHON_VERSION" == $PY310* ]]; then
if [ "$PYTHON_VERSION" != "$LATEST_310" ]; then
puts-warn "$SECURITY_UPDATE" "$LATEST_310"
Expand Down Expand Up @@ -135,9 +135,10 @@ else
# Prepare destination directory.
mkdir -p .heroku/python

if ! curl "${VENDORED_PYTHON}" -s | tar zxv -C .heroku/python &> /dev/null; then
puts-warn "Requested runtime ($PYTHON_VERSION) is not available for this stack ($STACK)."
puts-warn "Aborting. More info: https://devcenter.heroku.com/articles/python-support"
if ! curl --silent --show-error --fail --retry 3 --retry-connrefused --connect-timeout 5 "${PYTHON_URL}" | tar -zxC .heroku/python; then
# The Python version was confirmed to exist previously, so any failure here is due to
# a networking issue or archive/buildpack bug rather than the runtime not existing.
puts-warn "Failed to download/install ${PYTHON_VERSION}"
exit 1
fi

Expand Down Expand Up @@ -193,7 +194,7 @@ fi
PIP_WHEEL_URL="${S3_BASE_URL}/common/${PIP_WHEEL_FILENAME}"
PIP_WHEEL="${TMPDIR:-/tmp}/${PIP_WHEEL_FILENAME}"

if ! curl -sSf "${PIP_WHEEL_URL}" -o "$PIP_WHEEL"; then
if ! curl --silent --show-error --fail --retry 3 --retry-connrefused --connect-timeout 5 "${PIP_WHEEL_URL}" -o "$PIP_WHEEL"; then
mcount "failure.python.download-pip"
puts-warn "Failed to download pip"
exit 1
Expand Down
4 changes: 2 additions & 2 deletions builds/runtimes/python
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ SIGNATURE_URL="${SOURCE_URL}.asc"

set -o xtrace

curl --fail --retry 3 --connect-timeout 5 --max-time 60 -o python.tgz "${SOURCE_URL}"
curl --fail --retry 3 --connect-timeout 5 --max-time 60 -o python.tgz.asc "${SIGNATURE_URL}"
curl --fail --retry 3 --retry-connrefused --connect-timeout 5 --max-time 60 -o python.tgz "${SOURCE_URL}"
curl --fail --retry 3 --retry-connrefused --connect-timeout 5 --max-time 60 -o python.tgz.asc "${SIGNATURE_URL}"

# Skip GPG verification on Heroku-18 since it fails to fetch keys:
# `gpg: keyserver receive failed: Server indicated a failure`
Expand Down

0 comments on commit c6ebbec

Please sign in to comment.