mirror of
https://github.com/certbot/certbot.git
synced 2026-06-08 00:02:14 -04:00
In Phase 1, download a new letsencrypt-auto script only if necessary.
* Temp dir creation is now always done in shell. * Split download_upgrade.py into 2 phases itself so we can have it report back the latest LE version and make a decision based on it before doing and major downloading. Rename it for clarity.
This commit is contained in:
parent
46779da3b5
commit
5cc69d92e7
2 changed files with 70 additions and 70 deletions
|
|
@ -13,6 +13,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
|
|||
VENV_NAME="letsencrypt"
|
||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN=${VENV_PATH}/bin
|
||||
LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}"
|
||||
|
||||
ExperimentalBootstrap() {
|
||||
# Arguments: Platform name, bootstrap function name
|
||||
|
|
@ -102,6 +103,10 @@ Bootstrap() {
|
|||
fi
|
||||
}
|
||||
|
||||
TempDir() {
|
||||
mktemp -d 2>/dev/null || mktemp -d -t 'le' # Linux || OS X
|
||||
}
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
# for experimental platforms)
|
||||
|
|
@ -165,7 +170,7 @@ elif [ "$1" = "--no-self-upgrade" ]; then
|
|||
else
|
||||
INSTALLED_VERSION="0.0.0"
|
||||
fi
|
||||
if [ "{{ LE_AUTO_VERSION }}" = $INSTALLED_VERSION ]; then
|
||||
if [ "$LE_AUTO_VERSION" = "$INSTALLED_VERSION" ]; then
|
||||
echo "Reusing old virtual environment."
|
||||
else
|
||||
echo "Creating virtual environment..."
|
||||
|
|
@ -177,9 +182,9 @@ elif [ "$1" = "--no-self-upgrade" ]; then
|
|||
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH > /dev/null
|
||||
fi
|
||||
|
||||
# Install Python dependencies with peep, then run letsencrypt.
|
||||
echo "Installing Python package dependencies..."
|
||||
TEMP_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'le'` # Linux || OS X
|
||||
echo "Installing Python packages..."
|
||||
TEMP_DIR=$(TempDir)
|
||||
# There is no $ interpolation due to quotes on heredoc delimiters.
|
||||
# ---------------------------------------------------------------------------
|
||||
cat << "UNLIKELY_EOF" > $TEMP_DIR/letsencrypt-auto-requirements.txt
|
||||
{{ letsencrypt-auto-requirements.txt }}
|
||||
|
|
@ -195,7 +200,7 @@ UNLIKELY_EOF
|
|||
set -e
|
||||
rm -rf $TEMP_DIR
|
||||
if [ "$PEEP_STATUS" != 0 ]; then
|
||||
# Report error:
|
||||
# Report error. (Otherwise, be quiet.)
|
||||
echo $PEEP_OUT
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -205,45 +210,37 @@ UNLIKELY_EOF
|
|||
$SUDO $VENV_BIN/letsencrypt "$@"
|
||||
else
|
||||
# Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke.
|
||||
|
||||
if [ ! -f $VENV_BIN/letsencrypt ]; then
|
||||
OLD_VERSION="0.0.0" # ($VENV_BIN/letsencrypt --version)
|
||||
else
|
||||
OLD_VERSION="0.0.0"
|
||||
fi
|
||||
|
||||
# TODO: Don't bother upgrading if we're already up to date.
|
||||
if [ "$OLD_VERSION" != "1.2.3" ]; then
|
||||
echo "Upgrading letsencrypt-auto..."
|
||||
DeterminePythonVersion
|
||||
#
|
||||
# Each phase checks the version of only the thing it is responsible for
|
||||
# upgrading. Phase 1 checks the version of the latest release of
|
||||
# letsencrypt-auto (which is always the same as that of the letsencrypt
|
||||
# package). Phase 2 checks the version of the locally installed letsencrypt.
|
||||
|
||||
echo "Checking for new version..."
|
||||
TEMP_DIR=$(TempDir)
|
||||
# -------------------------------------------------------------------------
|
||||
cat << "UNLIKELY_EOF" > $TEMP_DIR/fetch.py
|
||||
{{ fetch.py }}
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
DeterminePythonVersion
|
||||
REMOTE_VERSION=`$LE_PYTHON $TEMP_DIR/fetch.py --latest-version`
|
||||
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
# dependencies (curl, etc.), for better flow control, and for the option of
|
||||
# future Windows compatibility.
|
||||
#
|
||||
# There is no $ interpolation due to quotes on heredoc delimiters.
|
||||
set +e
|
||||
# -------------------------------------------------------------------------
|
||||
TEMP_DIR=`$LE_PYTHON - << "UNLIKELY_EOF"
|
||||
{{ download_upgrade.py }}
|
||||
UNLIKELY_EOF`
|
||||
# -------------------------------------------------------------------------
|
||||
DOWNLOAD_STATUS=$?
|
||||
set -e
|
||||
if [ "$DOWNLOAD_STATUS" = 0 ]; then
|
||||
# Install new copy of letsencrypt-auto. This preserves permissions and
|
||||
# ownership from the old copy.
|
||||
# TODO: Deal with quotes in pathnames.
|
||||
echo "Installing new version of letsencrypt-auto..."
|
||||
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
|
||||
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
|
||||
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
||||
rm -rf $TEMP_DIR
|
||||
"$0" --no-self-upgrade "$@"
|
||||
else
|
||||
# Report error:
|
||||
echo $TEMP_DIR
|
||||
exit 1
|
||||
fi
|
||||
$LE_PYTHON "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
|
||||
|
||||
# Install new copy of letsencrypt-auto. This preserves permissions and
|
||||
# ownership from the old copy.
|
||||
# TODO: Deal with quotes in pathnames.
|
||||
echo "Installing new version of letsencrypt-auto..."
|
||||
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
|
||||
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
|
||||
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
||||
rm -rf $TEMP_DIR
|
||||
fi # should upgrade
|
||||
"$0" --no-self-upgrade "$@"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
"""Print a path to a temp dir containing a new copy of letsencrypt-auto.
|
||||
"""Do downloading and JSON parsing without additional dependencies. ::
|
||||
|
||||
# Print latest released version of LE to stdout:
|
||||
python fetch.py --latest-version
|
||||
|
||||
# Download letsencrypt-auto script from git tag v1.2.3 into the folder I'm
|
||||
# in, and make sure its signature verifies:
|
||||
python fetch.py --le-auto-script v1.2.3
|
||||
|
||||
On failure, return non-zero.
|
||||
|
||||
|
|
@ -9,8 +16,7 @@ from os import devnull
|
|||
from os.path import dirname, join
|
||||
import re
|
||||
from subprocess import check_call, CalledProcessError
|
||||
from sys import exit
|
||||
from tempfile import mkdtemp
|
||||
from sys import argv, exit
|
||||
from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError
|
||||
|
||||
|
||||
|
|
@ -59,62 +65,59 @@ class HttpsGetter(object):
|
|||
raise ExpectedError("Couldn't download %s." % url, exc)
|
||||
|
||||
|
||||
class TempDir(object):
|
||||
def __init__(self):
|
||||
self.path = mkdtemp()
|
||||
|
||||
def write(self, contents, filename):
|
||||
"""Write something to a named file in me."""
|
||||
with open(join(self.path, filename), 'w') as file:
|
||||
file.write(contents)
|
||||
def write(contents, dir, filename):
|
||||
"""Write something to a file in a certain directory."""
|
||||
with open(join(dir, filename), 'w') as file:
|
||||
file.write(contents)
|
||||
|
||||
|
||||
def latest_stable_version(get, package):
|
||||
"""Apply a fairly safe heuristic to determine the latest stable release of
|
||||
a PyPI package."""
|
||||
def latest_stable_version(get):
|
||||
"""Return the latest stable release of letsencrypt."""
|
||||
metadata = loads(get('https://pypi.python.org/pypi/letsencrypt/json'))
|
||||
# metadata['info']['version'] actually returns the latest of any kind of
|
||||
# release release, contrary to https://wiki.python.org/moin/PyPIJSON.
|
||||
# The regex is a sufficient regex for picking out prereleases for most
|
||||
# packages, LE included.
|
||||
return str(max(LooseVersion(r) for r
|
||||
in metadata['releases'].iterkeys()
|
||||
if re.match('^[0-9.]+$', r)))
|
||||
|
||||
|
||||
def verified_new_le_auto(get, tag, temp):
|
||||
def verified_new_le_auto(get, tag, temp_dir):
|
||||
"""Return the path to a verified, up-to-date letsencrypt-auto script.
|
||||
|
||||
If the download's signature does not verify or something else goes wrong,
|
||||
raise ExpectedError.
|
||||
If the download's signature does not verify or something else goes wrong
|
||||
with the verification process, raise ExpectedError.
|
||||
|
||||
"""
|
||||
le_auto_dir = ('https://raw.githubusercontent.com/letsencrypt/letsencrypt/'
|
||||
'%s/letsencrypt-auto/' % tag)
|
||||
temp.write(get(le_auto_dir + 'letsencrypt-auto'), 'letsencrypt-auto')
|
||||
temp.write(get(le_auto_dir + 'letsencrypt-auto.sig'), 'letsencrypt-auto.sig')
|
||||
temp.write(PUBLIC_KEY, 'public_key.pem')
|
||||
le_auto_path = join(temp.path, 'letsencrypt-auto')
|
||||
write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')
|
||||
write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')
|
||||
write(PUBLIC_KEY, temp_dir, 'public_key.pem')
|
||||
try:
|
||||
with open(devnull, 'w') as dev_null:
|
||||
check_call(['openssl', 'dgst', '-sha256', '-verify',
|
||||
join(temp.path, 'public_key.pem'),
|
||||
join(temp_dir, 'public_key.pem'),
|
||||
'-signature',
|
||||
join(temp.path, 'letsencrypt-auto.sig'),
|
||||
le_auto_path],
|
||||
join(temp_dir, 'letsencrypt-auto.sig'),
|
||||
join(temp_dir, 'letsencrypt-auto')],
|
||||
stdout=dev_null,
|
||||
stderr=dev_null)
|
||||
except CalledProcessError as exc:
|
||||
raise ExpectedError("Couldn't verify signature of downloaded "
|
||||
"letsencrypt-auto.", exc)
|
||||
else: # belt & suspenders
|
||||
return le_auto_path
|
||||
|
||||
|
||||
def main():
|
||||
get = HttpsGetter().get
|
||||
temp = TempDir()
|
||||
flag = argv[1]
|
||||
try:
|
||||
stable_tag = 'v' + latest_stable_version(get)
|
||||
print dirname(verified_new_le_auto(get, stable_tag, temp))
|
||||
if flag == '--latest-version':
|
||||
print latest_stable_version(get)
|
||||
elif flag == '--le-auto-script':
|
||||
tag = argv[2]
|
||||
verified_new_le_auto(get, tag, dirname(argv[0]))
|
||||
except ExpectedError as exc:
|
||||
print exc.args[0], exc.args[1]
|
||||
return 1
|
||||
Loading…
Reference in a new issue