mirror of
https://github.com/jupyterhub/the-littlest-jupyterhub.git
synced 2025-12-18 21:54:05 +08:00
traefik 2.9.9
- traefik releases are tarballs now - move traefik platform check to download function for easier unit testing on unsupported platforms
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
"""Traefik installation and setup"""
|
"""Traefik installation and setup"""
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import tarfile
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
from subprocess import run
|
||||||
|
|
||||||
import backoff
|
import backoff
|
||||||
import requests
|
import requests
|
||||||
@@ -13,28 +17,33 @@ from tljh.configurer import _merge_dictionaries, load_config
|
|||||||
|
|
||||||
from .config import CONFIG_DIR
|
from .config import CONFIG_DIR
|
||||||
|
|
||||||
# traefik 2.7.x is not supported yet, use v1.7.x for now
|
logger = logging.getLogger("tljh")
|
||||||
# see: https://github.com/jupyterhub/traefik-proxy/issues/97
|
|
||||||
machine = os.uname().machine
|
machine = os.uname().machine
|
||||||
if machine == "aarch64":
|
if machine == "aarch64":
|
||||||
plat = "linux-arm64"
|
plat = "linux_arm64"
|
||||||
elif machine == "x86_64":
|
elif machine == "x86_64":
|
||||||
plat = "linux-amd64"
|
plat = "linux_amd64"
|
||||||
else:
|
else:
|
||||||
raise OSError(f"Error. Platform: {os.uname().sysname} / {machine} Not supported.")
|
plat = None
|
||||||
traefik_version = "1.7.33"
|
|
||||||
|
traefik_version = "2.9.9"
|
||||||
|
|
||||||
# record sha256 hashes for supported platforms here
|
# record sha256 hashes for supported platforms here
|
||||||
checksums = {
|
checksums = {
|
||||||
"linux-amd64": "314ffeaa4cd8ed6ab7b779e9b6773987819f79b23c28d7ab60ace4d3683c5935",
|
"linux_amd64": "141db1434ae76890915486a4bc5ecf3dbafc8ece78984ce1a8db07737c42db88",
|
||||||
"linux-arm64": "0640fa665125efa6b598fc08c100178e24de66c5c6035ce5d75668d3dc3706e1",
|
"linux_arm64": "0a65ead411307669916ba629fa13f698acda0b2c5387abe0309b43e168e4e57f",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def checksum_file(path):
|
def checksum_file(path_or_file):
|
||||||
"""Compute the sha256 checksum of a path"""
|
"""Compute the sha256 checksum of a path"""
|
||||||
hasher = hashlib.sha256()
|
hasher = hashlib.sha256()
|
||||||
with open(path, "rb") as f:
|
if hasattr(path_or_file, "read"):
|
||||||
|
f = path_or_file
|
||||||
|
else:
|
||||||
|
f = open(path_or_file, "rb")
|
||||||
|
with f:
|
||||||
for chunk in iter(lambda: f.read(4096), b""):
|
for chunk in iter(lambda: f.read(4096), b""):
|
||||||
hasher.update(chunk)
|
hasher.update(chunk)
|
||||||
return hasher.hexdigest()
|
return hasher.hexdigest()
|
||||||
@@ -45,39 +54,68 @@ def fatal_error(e):
|
|||||||
return str(e) != "ContentTooShort" and not isinstance(e, ConnectionResetError)
|
return str(e) != "ContentTooShort" and not isinstance(e, ConnectionResetError)
|
||||||
|
|
||||||
|
|
||||||
|
def check_traefik_version(traefik_bin):
|
||||||
|
"""Check the traefik version from `traefik version` output"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
version_out = run([traefik_bin, "version"], capture=True, text=True)
|
||||||
|
except (FileNotFoundError, OSError) as e:
|
||||||
|
logger.debug(f"Failed to get traefik version: {e}")
|
||||||
|
return False
|
||||||
|
versions = {}
|
||||||
|
for line in version_out.splitlines():
|
||||||
|
before, _, after = line.partition(":")
|
||||||
|
key = before.strip()
|
||||||
|
if key.lower() == "version":
|
||||||
|
version = after.strip()
|
||||||
|
if version == traefik_version:
|
||||||
|
logger.debug(f"Found {traefik_bin} {version}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.info(
|
||||||
|
f"Found {traefik_bin} version {version} != {traefik_version}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.debug(f"Failed to extract traefik version from: {version_out}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@backoff.on_exception(backoff.expo, Exception, max_tries=2, giveup=fatal_error)
|
@backoff.on_exception(backoff.expo, Exception, max_tries=2, giveup=fatal_error)
|
||||||
def ensure_traefik_binary(prefix):
|
def ensure_traefik_binary(prefix):
|
||||||
"""Download and install the traefik binary to a location identified by a prefix path such as '/opt/tljh/hub/'"""
|
"""Download and install the traefik binary to a location identified by a prefix path such as '/opt/tljh/hub/'"""
|
||||||
|
if plat is None:
|
||||||
|
raise OSError(
|
||||||
|
f"Error. Platform: {os.uname().sysname} / {machine} Not supported."
|
||||||
|
)
|
||||||
|
traefik_bin_dir = os.path.join(prefix, "bin")
|
||||||
traefik_bin = os.path.join(prefix, "bin", "traefik")
|
traefik_bin = os.path.join(prefix, "bin", "traefik")
|
||||||
if os.path.exists(traefik_bin):
|
if os.path.exists(traefik_bin):
|
||||||
checksum = checksum_file(traefik_bin)
|
if check_traefik_version(traefik_bin):
|
||||||
if checksum == checksums[plat]:
|
|
||||||
# already have the right binary
|
|
||||||
# ensure permissions and we're done
|
|
||||||
os.chmod(traefik_bin, 0o755)
|
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
print(f"checksum mismatch on {traefik_bin}")
|
|
||||||
os.remove(traefik_bin)
|
os.remove(traefik_bin)
|
||||||
|
|
||||||
traefik_url = (
|
traefik_url = (
|
||||||
"https://github.com/containous/traefik/releases"
|
"https://github.com/containous/traefik/releases"
|
||||||
f"/download/v{traefik_version}/traefik_{plat}"
|
f"/download/v{traefik_version}/traefik_v{traefik_version}_{plat}.tar.gz"
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"Downloading traefik {traefik_version}...")
|
logger.info(f"Downloading traefik {traefik_version} from {traefik_url}...")
|
||||||
# download the file
|
# download the file
|
||||||
response = requests.get(traefik_url)
|
response = requests.get(traefik_url)
|
||||||
|
response.raise_for_status()
|
||||||
if response.status_code == 206:
|
if response.status_code == 206:
|
||||||
raise Exception("ContentTooShort")
|
raise Exception("ContentTooShort")
|
||||||
with open(traefik_bin, "wb") as f:
|
|
||||||
f.write(response.content)
|
|
||||||
os.chmod(traefik_bin, 0o755)
|
|
||||||
|
|
||||||
# verify that we got what we expected
|
# verify that we got what we expected
|
||||||
checksum = checksum_file(traefik_bin)
|
checksum = checksum_file(io.BytesIO(response.content))
|
||||||
if checksum != checksums[plat]:
|
if checksum != checksums[plat]:
|
||||||
raise OSError(f"Checksum failed {traefik_bin}: {checksum} != {checksums[plat]}")
|
raise OSError(f"Checksum failed {traefik_url}: {checksum} != {checksums[plat]}")
|
||||||
|
|
||||||
|
with tarfile.open(fileobj=io.BytesIO(response.content)) as tf:
|
||||||
|
tf.extract("traefik", path=traefik_bin_dir)
|
||||||
|
os.chmod(traefik_bin, 0o755)
|
||||||
|
|
||||||
|
|
||||||
def compute_basic_auth(username, password):
|
def compute_basic_auth(username, password):
|
||||||
|
|||||||
Reference in New Issue
Block a user