Conditionally use python instead of cURL

The healthcheck_curl has always been a bit bonky with the proxies
management, especially the "no_proxy" environment variable: since there
isn't any real RFC describing the content of this variable, there isn't
any unified handling.

This leads to situation where an Operator puts some CIDR notation inside
this variable, while curl doesn't handle it.

This change adds a new python script. Since it uses python-requests, it
will have a proper support for the CIDR notation[1].

It will be called if and only if either NO_PROXY or no_proxy environment
variables are set and non-empty.

One can also force the script usage by passing HEALTHCHECK_CURL_PY=1,
such as:
podman exec -e "HEALTHCHECK_CURL_PY=1" container /healthchecks/you-healthcheck

The output is 100% iso-compatible, and if someone wants to know what
healthcheck is used, they can check the User-Agent on the target logs.

This change is motivated by, at least, the following existing issues:
https://bugzilla.redhat.com/show_bug.cgi?id=1837458
https://bugzilla.redhat.com/show_bug.cgi?id=1883657

[1] 589c454733/requests/utils.py (L663-L684)

Change-Id: If56cd0fa986ef193d036b73e0ab84a88d59147e3
(cherry picked from commit 2185d9a859)
This commit is contained in:
Cédric Jeanneret 2021-01-19 11:41:30 +01:00 committed by Cedric Jeanneret
parent b7e584e4cb
commit 120a119279
2 changed files with 54 additions and 1 deletions

View File

@ -9,6 +9,7 @@ else
fi
: ${HEALTHCHECK_CURL_MAX_TIME:=10}
: ${HEALTHCHECK_CURL_USER_AGENT:=curl-healthcheck}
: ${HEALTHCHECK_CURL_PY_USER_AGENT:=pyrequests-healthcheck}
: ${HEALTHCHECK_CURL_WRITE_OUT:='\n%{http_code} %{remote_ip}:%{remote_port} %{time_total} seconds\n'}
: ${HEALTHCHECK_CURL_OUTPUT:='/dev/null'}
@ -30,11 +31,19 @@ healthcheck_curl () {
return 1
fi
export NSS_SDB_USE_CACHE=no
curl -g -k -q -s -S --fail -o "${HEALTHCHECK_CURL_OUTPUT}" \
if [ -n "${HEALTHCHECK_CURL_PY+x}" ] || [ -n "${no_proxy+x}" ] || [ -n "${NO_PROXY+x}" ]; then
${HEALTHCHECK_SCRIPTS:-/usr/share/openstack-tripleo-common/healthcheck}/http-healthcheck.py \
--max-time "${HEALTHCHECK_CURL_MAX_TIME}" \
--user-agent "${HEALTHCHECK_CURL_PY_USER_AGENT}" \
--write-out "${HEALTHCHECK_CURL_WRITE_OUT}" \
"$@" || return 1
else
curl -g -k -q -s -S --fail -o "${HEALTHCHECK_CURL_OUTPUT}" \
--max-time "${HEALTHCHECK_CURL_MAX_TIME}" \
--user-agent "${HEALTHCHECK_CURL_USER_AGENT}" \
--write-out "${HEALTHCHECK_CURL_WRITE_OUT}" \
"$@" || return 1
fi
}
healthcheck_port () {

44
healthcheck/http-healthcheck.py Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/python3
import argparse
import os
import requests
default_output = ("\n%(http_code)s %(remote_ip)s:%(remote_port)s "
"%(time_total)s seconds\n")
parser = argparse.ArgumentParser(description='Check remote HTTP')
parser.add_argument('uri', metavar='URI', type=str, nargs=1,
help='Remote URI to check')
parser.add_argument('--max-time', type=int, default=10,
help=('Maximum time in seconds that you allow the'
' whole operation to take.')
)
parser.add_argument('--user-agent', type=str, default='pyrequests-healthcheck',
help=('Specify the User-Agent string to send to the'
' HTTP server.')
)
parser.add_argument('--write-out', type=str, default=default_output,
help=('Display information on stdout after a completed'
' transfer.')
)
args = parser.parse_args()
uri = args.uri[0]
output = args.write_out.replace('%{', '%(').replace('}', ')s') \
.replace('\\n', os.linesep)
headers = {'User-Agent': args.user_agent}
with requests.get(uri, headers=headers, timeout=args.max_time,
allow_redirects=True, stream=True, verify=False) as req:
r_ip, r_port = req.raw._original_response.fp.raw._sock.getpeername()
resp = {'http_code': req.status_code,
'remote_ip': r_ip,
'remote_port': r_port,
'time_total': req.elapsed.total_seconds()
}
try:
print(output % resp)
except KeyError:
print(default_output % resp)
except ValueError:
print(default_output % resp)