Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress most cli errors, but still warn if we get too many #416

Merged
merged 5 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cli/.pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ good-names = k, v
[MESSAGES CONTROL]
# We currently have some older systems running this which
# don't support f-strings yet
disable = C0209
# Disable too-many-lines check because pylint on github seems to be miscounting lines in __init__.py
disable = C0209,too-many-lines
18 changes: 14 additions & 4 deletions cli/testflinger_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ def __init__(self):
or self.config.get("secret_key")
or os.environ.get("TESTFLINGER_SECRET_KEY")
)
error_threshold = self.config.get(
"error_threshold"
or os.environ.get("TESTFLINGER_ERROR_THRESHOLD")
or 3
)

# Allow config subcommand without worrying about server or client
if (
Expand All @@ -168,7 +173,7 @@ def __init__(self):
'Server must start with "http://" or "https://" '
'- currently set to: "{}"'.format(server)
)
self.client = client.Client(server)
self.client = client.Client(server, error_threshold=error_threshold)

def run(self):
"""Run the subcommand specified in command line arguments"""
Expand Down Expand Up @@ -361,7 +366,12 @@ def status(self):
job_state = self.get_job_state(self.args.job_id)
if job_state != "unknown":
self.history.update(self.args.job_id, job_state)
print(job_state)
print(job_state)
else:
print(
"Unable to retrieve job state from the server, check your "
"connection or try again later."
)

def cancel(self, job_id=None):
"""Tell the server to cancel a specified JOB_ID"""
Expand Down Expand Up @@ -980,8 +990,8 @@ def get_job_state(self, job_id):
"Received 404 error from server. Are you "
"sure this is a testflinger server?"
)
except (IOError, ValueError):
except (IOError, ValueError) as exc:
# For other types of network errors, or JSONDecodeError if we got
# a bad return from get_status()
logger.warning("Unable to retrieve job state.")
logger.debug("Unable to retrieve job state: %s", exc)
return "unknown"
22 changes: 13 additions & 9 deletions cli/testflinger_cli/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ def __init__(self, status, msg=""):
class Client:
"""Testflinger connection client"""

def __init__(self, server):
def __init__(self, server, error_threshold=3):
self.server = server
self.error_count = 0
self.error_threshold = error_threshold

def get(self, uri_frag, timeout=15, headers=None):
"""Submit a GET request to the server
Expand All @@ -56,14 +58,16 @@ def get(self, uri_frag, timeout=15, headers=None):
uri = urllib.parse.urljoin(self.server, uri_frag)
try:
req = requests.get(uri, timeout=timeout, headers=headers)
except requests.exceptions.ConnectionError:
logger.error("Unable to communicate with specified server.")
raise
except IOError:
# This should catch all other timeout cases
logger.error(
"Timeout while trying to communicate with the server."
)
self.error_count = 0
except (IOError, requests.exceptions.ConnectionError) as exc:
self.error_count += 1
if self.error_count % self.error_threshold == 0:
logger.warning(
"Error communicating with the server for the past %s "
"requests, but will continue to retry. Last error: %s",
self.error_count,
exc,
)
raise
if req.status_code != 200:
raise HTTPError(req.status_code)
Expand Down
47 changes: 47 additions & 0 deletions cli/testflinger_cli/tests/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (C) 2024 Canonical
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#

"""
Unit tests for the Client class
"""

import logging
import pytest
import requests

from testflinger_cli.client import Client


def test_get_error_threshold(caplog, requests_mock):
"""Test that a warning is logged when error_threshold is reached"""
caplog.set_level(logging.WARNING)
requests_mock.get(
"http://testflinger/test", exc=requests.exceptions.ConnectionError
)
client = Client("http://testflinger", error_threshold=3)
for _ in range(2):
with pytest.raises(requests.exceptions.ConnectionError):
client.get("test")
assert (
"Error communicating with the server for the past"
not in caplog.text
)
with pytest.raises(requests.exceptions.ConnectionError):
client.get("test")
assert (
"Error communicating with the server for the past 3 requests"
in caplog.text
)
47 changes: 47 additions & 0 deletions docs/reference/cli-config.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
Command Line Interface Configuration
====================================

The Testflinger CLI can be configured on the command line using the parameters described in ``testflinger --help``. However, sometimes it also supports using environment variables, or reading values from a configuration file. This can be helpful for CI/CD environments, or for setting up a config with different default values read from a config file.

The configuration file is read from ``$XDG_CONFIG_HOME/testflinger-cli.conf`` by default, but this can be overridden with the ``--configfile`` parameter.
plars marked this conversation as resolved.
Show resolved Hide resolved

When a configuration variable is defined in multiple locations, Testflinger resolves the value by applying the following priority order, from highest to lowest:

1. command-line parameter (for example, ``--server <local_server>``)

2. configuration file

3. environment variable

You can show the current values stored in the config file by running ``testflinger config``. If no value is found on the command line, config file, or environment variable, then a safe default value will be used.

To set a configuration value in the config file, you can edit it directly or use ``testflinger config key=value``.
plars marked this conversation as resolved.
Show resolved Hide resolved

Testflinger Config Variables
----------------------------

The following config values are currently supported:

* server - Testflinger Server URL to use

* Config file key: ``server``
* Environment variable: ``TESTFLINGER_SERVER``
* Default: ``https://testflinger.canonical.com``

* error_threshold - warn the user of possible server/network problems after failing to read from the server after this many consecutive attempts

* Config file key: ``error_threshold``
* Environment variable: ``TESTFLINGER_ERROR_THRESHOLD``
* Default: ``3``

* client_id - Client ID to use for APIs that require authorisation

* Config file key: ``client_id``
* Environment variable: ``TESTFLINGER_CLIENT_ID``
* Default: ``None``

* secret_key - Secret key to use for APIs that require authorisation

* Config file key: ``secret_key``
* Environment variable: ``TESTFLINGER_SECRET_KEY``
* Default: ``None``
10 changes: 9 additions & 1 deletion docs/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@ Working with Testflinger servers and agents

testflinger-server-conf
testflinger-agent-conf
test-phases
test-phases

Using the Command Line Interface
--------------------------------

.. toctree::
:maxdepth: 1

cli-config
Loading