The relocatable Python is built from Fedora packages. Unfortunately TLS
certificates are in a different location on Debian variants, which
causes "node_exporter_install" to fail as follows:
Traceback (most recent call last):
File "/usr/lib/scylla/libexec/node_exporter_install", line 58, in <module>
data = curl('https://github.com/prometheus/node_exporter/releases/download/v{version}/node_exporter-{version}.linux-amd64.tar.gz'.format(version=VERSION), byte=True)
File "/usr/lib/scylla/scylla_util.py", line 40, in curl
with urllib.request.urlopen(req) as res:
File "/opt/scylladb/python3/lib64/python3.7/urllib/request.py", line 222, in urlopen
return opener.open(url, data, timeout)
File "/opt/scylladb/python3/lib64/python3.7/urllib/request.py", line 525, in open
response = self._open(req, data)
File "/opt/scylladb/python3/lib64/python3.7/urllib/request.py", line 543, in _open
'_open', req)
File "/opt/scylladb/python3/lib64/python3.7/urllib/request.py", line 503, in _call_chain
result = func(*args)
File "/opt/scylladb/python3/lib64/python3.7/urllib/request.py", line 1360, in https_open
context=self._context, check_hostname=self._check_hostname)
File "/opt/scylladb/python3/lib64/python3.7/urllib/request.py", line 1319, in do_open
raise URLError(err)
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)>
Unable to retrieve version information
node exporter setup failed.
Fix the problem by overriding the SSL_CERT_FILE environment variable to
point to the correct location of the TLS bundle.
Message-Id: <20190604175434.24534-1-penberg@scylladb.com>
(cherry picked from commit eb00095bca)
125 lines
4.6 KiB
Python
Executable File
125 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# Copyright (C) 2019 ScyllaDB
|
|
#
|
|
|
|
#
|
|
# This file is part of Scylla.
|
|
#
|
|
# Scylla is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Scylla is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import argparse
|
|
import pathlib
|
|
import os
|
|
import shutil
|
|
import functools
|
|
import tarfile
|
|
import io
|
|
|
|
class FilesystemFixup:
|
|
def __init__(self, python_path, installroot):
|
|
self.thunk='''\
|
|
#!/usr/bin/env bash
|
|
x="$(readlink -f "$0")"
|
|
b="$(basename "$x")"
|
|
d="$(dirname "$x")"
|
|
CENTOS_SSL_CERT_FILE="/etc/pki/tls/cert.pem"
|
|
if [ -f "${{CENTOS_SSL_CERT_FILE}}" ]; then
|
|
c=${{CENTOS_SSL_CERT_FILE}}
|
|
fi
|
|
DEBIAN_SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt"
|
|
if [ -f "${{DEBIAN_SSL_CERT_FILE}}" ]; then
|
|
c=${{DEBIAN_SSL_CERT_FILE}}
|
|
fi
|
|
PYTHONPATH="${{d}}:${{d}}/libexec:$PYTHONPATH" PATH="${{d}}/{pythonpath}:${{PATH}}" SSL_CERT_FILE="${{c}}" exec -a "$0" "${{d}}/libexec/${{b}}" "$@"
|
|
'''
|
|
self.python_path = python_path
|
|
self.installroot = installroot
|
|
pathlib.Path(self.installroot).mkdir(parents=False, exist_ok=True)
|
|
|
|
def relocated_file(self, filename):
|
|
basename = os.path.basename(filename)
|
|
return os.path.join("libexec", basename)
|
|
|
|
def gen_thunk_contents(self, filename):
|
|
base_dir = os.path.dirname(os.path.realpath(filename))
|
|
python_path = os.path.dirname(os.path.relpath(self.python_path, base_dir))
|
|
return self.thunk.format(pythonpath=python_path)
|
|
|
|
def fix_shebang(self, dest, original_stat, bytes_stream):
|
|
dest = os.path.join(self.installroot, self.relocated_file(dest))
|
|
pathlib.Path(os.path.dirname(dest)).mkdir(parents=True, exist_ok=True)
|
|
with open(dest, "wb") as out:
|
|
bytes_stream.seek(0)
|
|
os.chmod(dest, original_stat.st_mode)
|
|
out.write(bytes_stream.read())
|
|
|
|
def copy_as_is(self, orig, dest):
|
|
dest = os.path.join(self.installroot, os.path.basename(dest))
|
|
pathlib.Path(os.path.dirname(dest)).mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(orig, dest)
|
|
|
|
def generate_thunk(self, original_stat, out):
|
|
bash_thunk = os.path.join(self.installroot, os.path.basename(out))
|
|
with open(bash_thunk, "w") as f:
|
|
os.chmod(bash_thunk, original_stat.st_mode)
|
|
f.write(self.gen_thunk_contents(bash_thunk))
|
|
|
|
def fixup_script(output, script_name):
|
|
'''Given a script as a parameter, fixup the script so it can be transparently called with a non-standard python3 interpreter.
|
|
This will generate a copy of the script in the libexec/ inside the script's directory location with the shebang pointing to env
|
|
instead of a hardcoded python location, and will replace the main script with a thunk that calls into the right interpreter
|
|
'''
|
|
|
|
script = os.path.realpath(script_name)
|
|
newpath = "libexec"
|
|
orig_stat = os.stat(script)
|
|
|
|
if not os.access(script, os.X_OK):
|
|
output.copy_as_is(script, script_name)
|
|
return
|
|
|
|
with open(script, "r") as f:
|
|
firstline = f.readline()
|
|
if not firstline.startswith("#!") or not "python3" in firstline:
|
|
output.copy_as_is(script, script_name)
|
|
return
|
|
|
|
obj = io.BytesIO()
|
|
shebang = "#!/usr/bin/env python3\n"
|
|
|
|
obj.write(shebang.encode())
|
|
for l in f:
|
|
obj.write(l.encode())
|
|
|
|
output.fix_shebang(script_name, orig_stat, obj)
|
|
|
|
output.generate_thunk(orig_stat, script_name)
|
|
|
|
def fixup_scripts(output, scripts):
|
|
for script in scripts:
|
|
fixup_script(output, script)
|
|
|
|
if __name__ == "__main__":
|
|
ap = argparse.ArgumentParser(description='Modify a python3 script adding indirection to its execution so it can run with a non-standard interpreter.')
|
|
ap.add_argument('--with-python3', required=True,
|
|
help='path of the python3 interepreter')
|
|
ap.add_argument('--installroot', required=True,
|
|
help='directory where to copy the files')
|
|
ap.add_argument('scripts', nargs='+', help='list of python modules scripts to modify')
|
|
|
|
args = ap.parse_args()
|
|
archive = FilesystemFixup(args.with_python3, args.installroot)
|
|
fixup_scripts(archive, args.scripts)
|