From 7eea4c94bea3f269dbc71bc4c66334ae4b01b9bd Mon Sep 17 00:00:00 2001 From: benoit Date: Tue, 14 Mar 2023 18:02:53 +0100 Subject: [PATCH] Reorganise for the urllib3 > requests change --- README.md | 31 +++++-------------- check_patroni/cli.py | 17 ++++++++--- check_patroni/types.py | 69 +++++++++++++++--------------------------- docs/make_readme.sh | 23 ++------------ 4 files changed, 47 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index ca015c5..5f0be2e 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ Options: services check the status of the cluster, therefore it's better to give a list of all Patroni node addresses. [default: http://127.0.0.1:8008] - --cert_file TEXT File with the client certificate. - --key_file TEXT File with the client key. - --ca_file TEXT The CA certificate. + --cert_file PATH File with the client certificate. + --key_file PATH File with the client key. + --ca_file PATH The CA certificate. -v, --verbose Increase verbosity -v (info)/-vv (warning)/-vvv (debug) --version @@ -103,30 +103,13 @@ check_patroni -e https://10.20.199.3:8008 cluster_has_replica --warning 2: --cri ``` ## SSL -Several option are available: +Several options are available: -* you have a self-signed certificate: +* the server's CA certificate is not available or trusted by the client system: * `--ca_cert`: your certification chain `cat CA-certificate server-certificate > cabundle` -* you have a valid root certificate: +* you have a client certificate for authenticating with Patroni's REST API: * `--cert_file`: your certificate or the concatenation of your certificate and private key * `--key_file`: your private key (optional) - * `--ca_cert`: if your CA certificate is not installed on the server you can provide it here (optional) -* unsafe access: dont provide any info, you will get a warning as described below. - -If you configuration is unsafe you might get warning message such as: - -``` -$ check_patroni -e https://p1:8008 cluster_node_count -/home/vagrant/.local/lib/python3.9/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'p1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings - warnings.warn( -CLUSTERNODECOUNT OK - members is 2 | members=2 role_leader=1 role_replica=1 state_running=2 -``` - -After checking on the message, you can choose to ignore it by redirecting the -standart output to /dev/null: -``` -$ check_patroni -e https://p1:8008 cluster_node_count 2>/dev/null -CLUSTERNODECOUNT OK - members is 2 | members=2 role_leader=1 role_replica=1 state_running=2 ``` ## Cluster services @@ -230,7 +213,7 @@ Usage: check_patroni cluster_node_count [OPTIONS] Count the number of nodes in the cluster. The state refers to the state of PostgreSQL. Possible values are: - * initalizing new cluster, initdb failed + * initializing new cluster, initdb failed * running custom bootstrap script, custom bootstrap failed * starting, start failed * restarting, restart failed diff --git a/check_patroni/cli.py b/check_patroni/cli.py index 7fc1b8b..130be91 100644 --- a/check_patroni/cli.py +++ b/check_patroni/cli.py @@ -100,19 +100,22 @@ def configure(ctx: click.Context, param: str, filename: str) -> None: @click.option( "--cert_file", "cert_file", - type=str, + type=click.Path(exists=True), + default=None, help="File with the client certificate.", ) @click.option( "--key_file", "key_file", - type=str, + type=click.Path(exists=True), + default=None, help="File with the client key.", ) @click.option( "--ca_file", "ca_file", - type=str, + type=click.Path(exists=True), + default=None, help="The CA certificate.", ) @click.option( @@ -166,8 +169,14 @@ def main( logging.basicConfig(format="%(levelname)s - %(message)s", level=logging.DEBUG) logging.getLogger("urllib3").setLevel(logging.DEBUG) + connection_info: ConnectionInfo + if cert_file is None and key_file is None: + connection_info = ConnectionInfo(endpoints, None, ca_file) + else: + connection_info = ConnectionInfo(endpoints, (cert_file, key_file), ca_file) + ctx.obj = Parameters( - ConnectionInfo(endpoints, cert_file, key_file, ca_file), + connection_info, timeout, verbose, ) diff --git a/check_patroni/types.py b/check_patroni/types.py index fa89d31..fe36fa0 100644 --- a/check_patroni/types.py +++ b/check_patroni/types.py @@ -1,4 +1,5 @@ import logging +from urllib.parse import urlparse import attr import nagiosplugin @@ -17,8 +18,7 @@ class APIError(requests.exceptions.RequestException): @attr.s(auto_attribs=True, frozen=True, slots=True) class ConnectionInfo: endpoints: List[str] = ["http://127.0.0.1:8008"] - cert_file: Optional[str] = None - key_file: Optional[str] = None + cert: Optional[Union[str, Tuple[str, str]]] = None ca_cert: Optional[str] = None @@ -36,54 +36,33 @@ class PatroniResource(nagiosplugin.Resource): def rest_api(self: "PatroniResource", service: str) -> Any: """Try to connect to all the provided endpoints for the requested service""" for endpoint in self.conn_info.endpoints: + cert: Optional[Union[Tuple[str, str], str]] = None + verify: Optional[Union[str, bool]] = None + if urlparse(endpoint).scheme == "https": + if self.conn_info.cert is not None: + # we can have: a key + a cert or a single file with key and cert. + cert = self.conn_info.cert + if self.conn_info.ca_cert is not None: + verify = self.conn_info.ca_cert + + _log.debug( + f"Trying to connect to {endpoint}/{service} with cert: {cert} verify: {verify}" + ) + try: - cert: Optional[Union[Tuple[str, str], str]] = None - verify: Optional[Union[str, bool]] = None - if endpoint[:5] == "https": - if ( - self.conn_info.cert_file is not None - and self.conn_info.key_file is not None # noqa W503 - ): - # we provide a certificate and a private key - cert = (self.conn_info.cert_file, self.conn_info.key_file) - elif ( - self.conn_info.cert_file is not None - and self.conn_info.key_file is None # noqa W503 - ): - # we provide a pem file with the private key and the certificate - cert = self.conn_info.cert_file - - if self.conn_info.ca_cert is not None: - # if cert is not None: this is the CA certificate - # otherwise this is a ca bundle with root certificate - # then some optional intermediate certificate and finally - # the cerver certificate to validate the certification chain - verify = self.conn_info.ca_cert - else: - if cert is None: - # if cert is None we want to bypass https verification, - # this is in secure and should be avoided for production use - verify = False - - _log.debug( - f"Trying to connect to {endpoint}/{service} with cert: {cert} verify: {verify}" - ) - r = requests.get(f"{endpoint}/{service}", verify=verify, cert=cert) - # The status code is already handled by urllib3 - _log.debug(f"api call data: {r.text}") - - if r.status_code != 200: - raise APIError( - f"Failed to connect to {endpoint}/{service} status code {r.status_code}" - ) - - return r.json() - except nagiosplugin.Timeout as e: - raise e except Exception as e: _log.debug(e) continue + # The status code is already handled by urllib3 + _log.debug(f"api call data: {r.text}") + + if r.status_code != 200: + raise APIError( + f"Failed to connect to {endpoint}/{service} status code {r.status_code}" + ) + + return r.json() raise nagiosplugin.CheckError("Connection failed for all provided endpoints") diff --git a/docs/make_readme.sh b/docs/make_readme.sh index 5d12118..527d4e3 100755 --- a/docs/make_readme.sh +++ b/docs/make_readme.sh @@ -87,30 +87,13 @@ check_patroni -e https://10.20.199.3:8008 cluster_has_replica --warning 2: --cri ``` ## SSL -Several option are available: +Several options are available: -* you have a self-signed certificate: +* the server's CA certificate is not available or trusted by the client system: * `--ca_cert`: your certification chain `cat CA-certificate server-certificate > cabundle` -* you have a valid root certificate: +* you have a client certificate for authenticating with Patroni's REST API: * `--cert_file`: your certificate or the concatenation of your certificate and private key * `--key_file`: your private key (optional) - * `--ca_cert`: if your CA certificate is not installed on the server you can provide it here (optional) -* unsafe access: dont provide any info, you will get a warning as described below. - -If you configuration is unsafe you might get warning message such as: - -``` -$ check_patroni -e https://p1:8008 cluster_node_count -/home/vagrant/.local/lib/python3.9/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'p1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings - warnings.warn( -CLUSTERNODECOUNT OK - members is 2 | members=2 role_leader=1 role_replica=1 state_running=2 -``` - -After checking on the message, you can choose to ignore it by redirecting the -standart output to /dev/null: -``` -$ check_patroni -e https://p1:8008 cluster_node_count 2>/dev/null -CLUSTERNODECOUNT OK - members is 2 | members=2 role_leader=1 role_replica=1 state_running=2 ``` _EOF_ readme