From addb9645b4cc80c452dc2006fa71f3d55918d59d Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 29 Nov 2024 11:06:00 +0000 Subject: [PATCH 01/15] test: fix malformed f string in microvm_helpers.py My python version does not accept the precense of double quotes inside a double-quoted f-string. Use single quotes instead. Signed-off-by: Patrick Roy --- tests/framework/microvm_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/framework/microvm_helpers.py b/tests/framework/microvm_helpers.py index f29ae84a9a0..b34da3c447e 100644 --- a/tests/framework/microvm_helpers.py +++ b/tests/framework/microvm_helpers.py @@ -242,6 +242,6 @@ def trace_cmd_guest(self, fns, cmd, port=4321): print("guest> trace-cmd record") host_ip = self.vm.iface["eth0"]["iface"].host_ip _guest_ps = self.vm.ssh.run( - f"trace-cmd record -N {host_ip}:{port} -p function {" ".join(fns)} {cmd}" + f"trace-cmd record -N {host_ip}:{port} -p function {' '.join(fns)} {cmd}" ) return list(Path(".").glob("trace.*.dat")) From b3d202b1926fe31daf534c28e2699566c36eb5aa Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 11 Dec 2024 13:13:33 +0000 Subject: [PATCH 02/15] test: close stdout/stderr after timeout in utils.run_cmd If a command times out in utils.run_cmd, we kill the subprocess and try to get whatever partial output it wrote so far to generate an error message. We do this using `communicate`, which waits for stdout/stderr to close. Some processes pass their stdout and stderr to their children. In this case, killing the parent won't close the stdout/stderr, and so the proc.communicate() call in run_cmd will wait indefinitely (or until whatever is holding on the the other end of the pipe exits). Avoid this by explicitly closing our end of the stdout/stderr pipes. Signed-off-by: Patrick Roy --- tests/framework/utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/framework/utils.py b/tests/framework/utils.py index e71302545c9..8bb1d8c744e 100644 --- a/tests/framework/utils.py +++ b/tests/framework/utils.py @@ -397,6 +397,12 @@ def run_cmd(cmd, check=False, shell=True, cwd=None, timeout=None) -> CommandRetu stdout, stderr = proc.communicate(timeout=timeout) except subprocess.TimeoutExpired: proc.kill() + + # Sometimes stdout/stderr are passed on to children, in which case killing + # the parent won't close them and communicate will still hang. + proc.stdout.close() + proc.stderr.close() + stdout, stderr = proc.communicate() # Log the message with one call so that multiple statuses From fd77b79a4cc3cf69d8626138d8d5cd539c9e6170 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 11 Dec 2024 13:16:34 +0000 Subject: [PATCH 03/15] test: fix assertions in test_pause_resume We asserted twice that the guest is not responsive anymore after pausing, and zero times that it is responsive again after resuming. Fix this by doing each once. Signed-off-by: Patrick Roy --- tests/integration_tests/functional/test_pause_resume.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/integration_tests/functional/test_pause_resume.py b/tests/integration_tests/functional/test_pause_resume.py index 3d0ac124c11..4b15c86ddfc 100644 --- a/tests/integration_tests/functional/test_pause_resume.py +++ b/tests/integration_tests/functional/test_pause_resume.py @@ -59,10 +59,6 @@ def test_pause_resume(uvm_nano): # guest or host side were handled. verify_net_emulation_paused(microvm.flush_metrics()) - # Verify guest is no longer active. - with pytest.raises(ChildProcessError): - microvm.ssh.check_output("true") - # Pausing the microVM when it is already `Paused` is allowed # (microVM remains in `Paused` state). microvm.api.vm.patch(state="Paused") @@ -71,6 +67,7 @@ def test_pause_resume(uvm_nano): microvm.api.vm.patch(state="Resumed") # Verify guest is active again. + microvm.ssh.check_output("true") # Resuming the microVM when it is already `Resumed` is allowed # (microVM remains in the running state). From b99abe1bd7a9e8b9b80df0b9ea8197849c6dabf0 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 11 Dec 2024 14:03:13 +0000 Subject: [PATCH 04/15] test: remove timeout from test_high_ingress_traffic It's a fairly tight timeout, and I was hitting it quite frequently while running functional tests with high parallelism. I don't really see any reason why we should be concerned about this specific command simply hitting the pytest timeout when things go wrong, so let's just remove the timeout. Signed-off-by: Patrick Roy --- tests/integration_tests/functional/test_net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration_tests/functional/test_net.py b/tests/integration_tests/functional/test_net.py index 2072d015ca2..efccd946c61 100644 --- a/tests/integration_tests/functional/test_net.py +++ b/tests/integration_tests/functional/test_net.py @@ -41,9 +41,9 @@ def test_high_ingress_traffic(uvm_plain_any): time.sleep(1) # Start iperf3 client on the host. Send 1Gbps UDP traffic. - # If the net device breaks, iperf will freeze. We have to use a timeout. + # If the net device breaks, iperf will freeze, and we'll hit the pytest timeout utils.check_output( - "timeout 31 {} {} -c {} -u -V -b 1000000000 -t 30".format( + "{} {} -c {} -u -V -b 1000000000 -t 30".format( test_microvm.netns.cmd_prefix(), IPERF_BINARY_HOST, guest_ip, From 36448e94ebf43f04fc55dac5aa2fae6f9132bfea Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 11 Dec 2024 14:04:58 +0000 Subject: [PATCH 05/15] test: set default timeout of 100s for ssh commands Opportunistically set a 100s timeout. We don't have anything that should run this long, and by manually setting a timeout we avoid hitting the pytest timeout of 300s in case something _does_ go wrong (and hitting the pytest timeout gives significantly less debug information, so a manual timeout is preferred). Signed-off-by: Patrick Roy --- tests/host_tools/network.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/host_tools/network.py b/tests/host_tools/network.py index 7877b914d28..4a961e025df 100644 --- a/tests/host_tools/network.py +++ b/tests/host_tools/network.py @@ -109,9 +109,9 @@ def _init_connection(self): We'll keep trying to execute a remote command that can't fail (`/bin/true`), until we get a successful (0) exit code. """ - self.check_output("true", timeout=100, debug=True) + self.check_output("true", debug=True) - def run(self, cmd_string, timeout=None, *, check=False, debug=False): + def run(self, cmd_string, timeout=100, *, check=False, debug=False): """ Execute the command passed as a string in the ssh context. @@ -124,11 +124,11 @@ def run(self, cmd_string, timeout=None, *, check=False, debug=False): return self._exec(command, timeout, check=check) - def check_output(self, cmd_string, timeout=None, *, debug=False): + def check_output(self, cmd_string, timeout=100, *, debug=False): """Same as `run`, but raises an exception on non-zero return code of remote command""" return self.run(cmd_string, timeout, check=True, debug=debug) - def _exec(self, cmd, timeout=None, check=False): + def _exec(self, cmd, timeout=100, check=False): """Private function that handles the ssh client invocation.""" if self.netns is not None: cmd = ["ip", "netns", "exec", self.netns] + cmd From 030c2262c30a6e65a4fd9bc3fb42a6ae541853b7 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 11 Dec 2024 14:44:31 +0000 Subject: [PATCH 06/15] test: simplify make_guest_dirty_memory Remove the logging from this function, because SSHConnection._exec already does this. Signed-off-by: Patrick Roy --- tests/integration_tests/functional/test_balloon.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/integration_tests/functional/test_balloon.py b/tests/integration_tests/functional/test_balloon.py index 2c59fd2e814..41a790190de 100644 --- a/tests/integration_tests/functional/test_balloon.py +++ b/tests/integration_tests/functional/test_balloon.py @@ -62,18 +62,10 @@ def lower_ssh_oom_chance(ssh_connection): def make_guest_dirty_memory(ssh_connection, amount_mib=32): """Tell the guest, over ssh, to dirty `amount` pages of memory.""" - logger = logging.getLogger("make_guest_dirty_memory") - lower_ssh_oom_chance(ssh_connection) - cmd = f"/usr/local/bin/fillmem {amount_mib}" try: - exit_code, stdout, stderr = ssh_connection.run(cmd, timeout=1.0) - # add something to the logs for troubleshooting - if exit_code != 0: - logger.error("while running: %s", cmd) - logger.error("stdout: %s", stdout) - logger.error("stderr: %s", stderr) + _ = ssh_connection.run(f"/usr/local/bin/fillmem {amount_mib}", timeout=1.0) except TimeoutExpired: # It's ok if this expires. Sometimes the SSH connection # gets killed by the OOM killer *after* the fillmem program From 3b2c2d4d69f41c99aecb2de10d8c04d6efc26bc9 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 11 Dec 2024 10:57:35 +0000 Subject: [PATCH 07/15] test: use single SSH connection for lifetime of microvm Instead of creating new SSH connections every time we want to run a command inside the microvm, open a single daemonized ssh connection in the constructor of `SSHConnection`, and reuse it until we kill the microvm. Realize this using openssh's ControlMaster/ControlPersist functionality, which allows us to persist the first connection opened to a microvm and multiplex all subsequent commands over this one connection. We need some slight changes in test_pause_restore.py and test_net.py. In the former, since we no longer reconnect to the VM on every ssh command, we will now observe a timeout instead of a connection failure. For the latter, it turns out that @lru_cache treats .ssh_iface() and .ssh_iface(0) as distinct invokations, despire the iface_idx argument of ssh_iface defaulting to 0 (meaning they are actually the same function call). This caused a re-intialization of the SSHConnection object, which then triggered the assertion in _init_connection about the socket file not existing. Signed-off-by: Patrick Roy --- tests/framework/microvm.py | 11 +- tests/host_tools/network.py | 123 ++++++++++++------ .../integration_tests/functional/test_net.py | 7 +- .../functional/test_pause_resume.py | 5 +- 4 files changed, 96 insertions(+), 50 deletions(-) diff --git a/tests/framework/microvm.py b/tests/framework/microvm.py index eb755ffc5fa..11fecf720a7 100644 --- a/tests/framework/microvm.py +++ b/tests/framework/microvm.py @@ -247,6 +247,8 @@ def __init__( self.mem_size_bytes = None self.cpu_template_name = None + self._connections = [] + self._pre_cmd = [] if numa_node: node_str = str(numa_node) @@ -282,6 +284,10 @@ def kill(self): for monitor in self.monitors: monitor.stop() + # Kill all background SSH connections + for connection in self._connections: + connection.close() + # We start with vhost-user backends, # because if we stop Firecracker first, the backend will want # to exit as well and this will cause a race condition. @@ -1007,13 +1013,16 @@ def ssh_iface(self, iface_idx=0): """Return a cached SSH connection on a given interface id.""" guest_ip = list(self.iface.values())[iface_idx]["iface"].guest_ip self.ssh_key = Path(self.ssh_key) - return net_tools.SSHConnection( + connection = net_tools.SSHConnection( netns=self.netns.id, ssh_key=self.ssh_key, user="root", host=guest_ip, + control_path=Path(self.chroot()) / f"ssh-{iface_idx}.sock", on_error=self._dump_debug_information, ) + self._connections.append(connection) + return connection @property def ssh(self): diff --git a/tests/host_tools/network.py b/tests/host_tools/network.py index 4a961e025df..ff6e58be2b9 100644 --- a/tests/host_tools/network.py +++ b/tests/host_tools/network.py @@ -3,30 +3,35 @@ """Utilities for test host microVM network setup.""" import ipaddress +import os import random +import re +import signal import string -import subprocess from dataclasses import dataclass, field from pathlib import Path -from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed +from tenacity import retry, stop_after_attempt, wait_fixed from framework import utils +from framework.utils import Timeout class SSHConnection: """ SSHConnection encapsulates functionality for microVM SSH interaction. - This class should be instantiated as part of the ssh fixture with the + This class should be instantiated as part of the ssh fixture with the hostname obtained from the MAC address, the username for logging into the image and the path of the ssh key. - This translates into an SSH connection as follows: - ssh -i ssh_key_path username@hostname + Establishes a ControlMaster upon construction, which is then re-used + for all subsequent SSH interactions. """ - def __init__(self, netns, ssh_key: Path, host, user, *, on_error=None): + def __init__( + self, netns, ssh_key: Path, control_path: Path, host, user, *, on_error=None + ): """Instantiate a SSH client and connect to a microVM.""" self.netns = netns self.ssh_key = ssh_key @@ -37,22 +42,13 @@ def __init__(self, netns, ssh_key: Path, host, user, *, on_error=None): assert (ssh_key.stat().st_mode & 0o777) == 0o400 self.host = host self.user = user + self._control_path = control_path self._on_error = None self.options = [ "-o", - "LogLevel=ERROR", - "-o", - "ConnectTimeout=1", - "-o", - "StrictHostKeyChecking=no", - "-o", - "UserKnownHostsFile=/dev/null", - "-o", - "PreferredAuthentications=publickey", - "-i", - str(self.ssh_key), + f"ControlPath={self._control_path}", ] # _init_connection loops until it can connect to the guest @@ -96,20 +92,82 @@ def scp_get(self, remote_path, local_path, recursive=False): self._scp(self.remote_path(remote_path), local_path, opts) @retry( - retry=retry_if_exception_type(ChildProcessError), - wait=wait_fixed(0.5), + wait=wait_fixed(1), stop=stop_after_attempt(20), reraise=True, ) def _init_connection(self): - """Create an initial SSH client connection (retry until it works). + """Initialize the persistent background connection which will be used + to execute all commands sent via this `SSHConnection` object. Since we're connecting to a microVM we just started, we'll probably have to wait for it to boot up and start the SSH server. We'll keep trying to execute a remote command that can't fail (`/bin/true`), until we get a successful (0) exit code. """ - self.check_output("true", debug=True) + assert not self._control_path.exists() + + # Sadly, we cannot get debug output from this command (e.g. `-vvv`), + # because passing -vvv causes the daemonized ssh to hold on to stderr, + # and inside utils.run_cmd we're using subprocess.communicate, which + # only returns once stderr gets closed (which would thus result in an + # indefinite hang). + establish_cmd = [ + "ssh", + # Only need to pass the ssh key here, as all multiplexed + # connections won't have to re-authenticate + "-i", + str(self.ssh_key), + "-o", + "StrictHostKeyChecking=no", + "-o", + "ConnectTimeout=2", + # Set up a persistent background connection + "-o", + "ControlMaster=auto", + "-o", + "ControlPersist=yes", + *self.options, + self.user_host, + "/usr/bin/true", + ] + + # don't set a low timeout here, because otherwise we might get into a race condition + # where ssh already forked off the persisted connection daemon, but gets killed here + # before exiting itself. In that case, self._control_path will exist, and the retry + # will hit the assert at the start of this function. + self._exec(establish_cmd, check=True) + + def _check_liveness(self) -> int: + """Checks whether the ControlPersist connection is still alive""" + check_cmd = ["ssh", "-O", "check", *self.options, self.user_host] + + _, _, stderr = self._exec(check_cmd, check=True) + + pid_match = re.match(r"Master running \(pid=(\d+)\)", stderr) + + assert pid_match, f"SSH ControlMaster connection not alive anymore: {stderr}" + + return int(pid_match.group(1)) + + def close(self): + """Closes the ControlPersist connection""" + master_pid = self._check_liveness() + + stop_cmd = ["ssh", "-O", "stop", *self.options, self.user_host] + + _, _, stderr = self._exec(stop_cmd, check=True) + + assert "Stop listening request sent" in stderr + + try: + with Timeout(5): + utils.wait_process_termination(master_pid) + except TimeoutError: + # for some reason it won't exit, let's force it... + # if this also fails, when during teardown we'll get an error about + # "found a process with supposedly dead Firecracker's jailer ID" + os.kill(master_pid, signal.SIGKILL) def run(self, cmd_string, timeout=100, *, check=False, debug=False): """ @@ -117,6 +175,8 @@ def run(self, cmd_string, timeout=100, *, check=False, debug=False): If `debug` is set, pass `-vvv` to `ssh`. Note that this will clobber stderr. """ + self._check_liveness() + command = ["ssh", *self.options, self.user_host, cmd_string] if debug: @@ -141,27 +201,6 @@ def _exec(self, cmd, timeout=100, check=False): raise - # pylint:disable=invalid-name - def Popen( - self, - cmd: str, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - **kwargs, - ) -> subprocess.Popen: - """Execute the command in the guest and return a Popen object. - - pop = uvm.ssh.Popen("while true; do echo $(date -Is) $RANDOM; sleep 1; done") - pop.stdout.read(16) - """ - cmd = ["ssh", *self.options, self.user_host, cmd] - if self.netns is not None: - cmd = ["ip", "netns", "exec", self.netns] + cmd - return subprocess.Popen( - cmd, stdin=stdin, stdout=stdout, stderr=stderr, **kwargs - ) - def mac_from_ip(ip_address): """Create a MAC address based on the provided IP. diff --git a/tests/integration_tests/functional/test_net.py b/tests/integration_tests/functional/test_net.py index efccd946c61..7b784e453c5 100644 --- a/tests/integration_tests/functional/test_net.py +++ b/tests/integration_tests/functional/test_net.py @@ -112,11 +112,8 @@ def test_tap_offload(uvm_any): ) # Try to send a UDP message from host with UDP offload enabled - cmd = f"ip netns exec {vm.ssh_iface().netns} python3 ./host_tools/udp_offload.py {vm.ssh_iface().host} {port}" - ret = utils.run_cmd(cmd) - - # Check that the transmission was successful - assert ret.returncode == 0, f"{ret.stdout=} {ret.stderr=}" + cmd = f"ip netns exec {vm.ssh.netns} python3 ./host_tools/udp_offload.py {vm.ssh.host} {port}" + utils.check_output(cmd) # Check that the server received the message ret = vm.ssh.run(f"cat {out_filename}") diff --git a/tests/integration_tests/functional/test_pause_resume.py b/tests/integration_tests/functional/test_pause_resume.py index 4b15c86ddfc..34b9a9a7229 100644 --- a/tests/integration_tests/functional/test_pause_resume.py +++ b/tests/integration_tests/functional/test_pause_resume.py @@ -4,6 +4,7 @@ import platform import time +from subprocess import TimeoutExpired import pytest @@ -52,8 +53,8 @@ def test_pause_resume(uvm_nano): microvm.flush_metrics() # Verify guest is no longer active. - with pytest.raises(ChildProcessError): - microvm.ssh.check_output("true") + with pytest.raises(TimeoutExpired): + microvm.ssh.check_output("true", timeout=1) # Verify emulation was indeed paused and no events from either # guest or host side were handled. From 9455ed0420bcd4a7a9ea0f40662ee2d7c2195fb1 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 11 Dec 2024 15:51:02 +0000 Subject: [PATCH 08/15] poetry: remove fabric and netns dependencies With the ControlMaster/ControlPersist approach, these are no needed anymore. I'm not reverting the commit that added them because that also contained an update of poetry.lock. I'm also not rebuilding the devctr for this because the dependencies will simply be dropped the next time we're rebuilding it. Signed-off-by: Patrick Roy --- tools/devctr/poetry.lock | 360 +----------------------------------- tools/devctr/pyproject.toml | 2 - 2 files changed, 1 insertion(+), 361 deletions(-) diff --git a/tools/devctr/poetry.lock b/tools/devctr/poetry.lock index eb969e9eda2..b04d05efac9 100644 --- a/tools/devctr/poetry.lock +++ b/tools/devctr/poetry.lock @@ -210,44 +210,6 @@ files = [ [package.dependencies] aiohttp = "*" -[[package]] -name = "bcrypt" -version = "4.2.1" -description = "Modern password hashing for your software and your servers" -optional = false -python-versions = ">=3.7" -files = [ - {file = "bcrypt-4.2.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17"}, - {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f"}, - {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dbd0747208912b1e4ce730c6725cb56c07ac734b3629b60d4398f082ea718ad"}, - {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:aaa2e285be097050dba798d537b6efd9b698aa88eef52ec98d23dcd6d7cf6fea"}, - {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:76d3e352b32f4eeb34703370e370997065d28a561e4a18afe4fef07249cb4396"}, - {file = "bcrypt-4.2.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:b7703ede632dc945ed1172d6f24e9f30f27b1b1a067f32f68bf169c5f08d0425"}, - {file = "bcrypt-4.2.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89df2aea2c43be1e1fa066df5f86c8ce822ab70a30e4c210968669565c0f4685"}, - {file = "bcrypt-4.2.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04e56e3fe8308a88b77e0afd20bec516f74aecf391cdd6e374f15cbed32783d6"}, - {file = "bcrypt-4.2.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cfdf3d7530c790432046c40cda41dfee8c83e29482e6a604f8930b9930e94139"}, - {file = "bcrypt-4.2.1-cp37-abi3-win32.whl", hash = "sha256:adadd36274510a01f33e6dc08f5824b97c9580583bd4487c564fc4617b328005"}, - {file = "bcrypt-4.2.1-cp37-abi3-win_amd64.whl", hash = "sha256:8c458cd103e6c5d1d85cf600e546a639f234964d0228909d8f8dbeebff82d526"}, - {file = "bcrypt-4.2.1-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:8ad2f4528cbf0febe80e5a3a57d7a74e6635e41af1ea5675282a33d769fba413"}, - {file = "bcrypt-4.2.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:909faa1027900f2252a9ca5dfebd25fc0ef1417943824783d1c8418dd7d6df4a"}, - {file = "bcrypt-4.2.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cde78d385d5e93ece5479a0a87f73cd6fa26b171c786a884f955e165032b262c"}, - {file = "bcrypt-4.2.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:533e7f3bcf2f07caee7ad98124fab7499cb3333ba2274f7a36cf1daee7409d99"}, - {file = "bcrypt-4.2.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:687cf30e6681eeda39548a93ce9bfbb300e48b4d445a43db4298d2474d2a1e54"}, - {file = "bcrypt-4.2.1-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:041fa0155c9004eb98a232d54da05c0b41d4b8e66b6fc3cb71b4b3f6144ba837"}, - {file = "bcrypt-4.2.1-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f85b1ffa09240c89aa2e1ae9f3b1c687104f7b2b9d2098da4e923f1b7082d331"}, - {file = "bcrypt-4.2.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c6f5fa3775966cca251848d4d5393ab016b3afed251163c1436fefdec3b02c84"}, - {file = "bcrypt-4.2.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:807261df60a8b1ccd13e6599c779014a362ae4e795f5c59747f60208daddd96d"}, - {file = "bcrypt-4.2.1-cp39-abi3-win32.whl", hash = "sha256:b588af02b89d9fad33e5f98f7838bf590d6d692df7153647724a7f20c186f6bf"}, - {file = "bcrypt-4.2.1-cp39-abi3-win_amd64.whl", hash = "sha256:e84e0e6f8e40a242b11bce56c313edc2be121cec3e0ec2d76fce01f6af33c07c"}, - {file = "bcrypt-4.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:76132c176a6d9953cdc83c296aeaed65e1a708485fd55abf163e0d9f8f16ce0e"}, - {file = "bcrypt-4.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e158009a54c4c8bc91d5e0da80920d048f918c61a581f0a63e4e93bb556d362f"}, - {file = "bcrypt-4.2.1.tar.gz", hash = "sha256:6765386e3ab87f569b276988742039baab087b2cdb01e809d74e74503c2faafe"}, -] - -[package.extras] -tests = ["pytest (>=3.2.1,!=3.3.0)"] -typecheck = ["mypy"] - [[package]] name = "black" version = "24.10.0" @@ -305,85 +267,6 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] -[[package]] -name = "cffi" -version = "1.17.1" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, -] - -[package.dependencies] -pycparser = "*" - [[package]] name = "charset-normalizer" version = "3.4.0" @@ -523,57 +406,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "cryptography" -version = "44.0.0" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = "!=3.9.0,!=3.9.1,>=3.7" -files = [ - {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"}, - {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, - {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, - {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, - {file = "cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd"}, - {file = "cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"}, - {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, - {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, - {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, - {file = "cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c"}, - {file = "cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02"}, -] - -[package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] -docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] -sdist = ["build (>=1.0.0)"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] -test-randomorder = ["pytest-randomly"] - [[package]] name = "decorator" version = "5.1.1" @@ -585,23 +417,6 @@ files = [ {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] -[[package]] -name = "deprecated" -version = "1.2.15" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -files = [ - {file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"}, - {file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "jinja2 (>=3.0.3,<3.1.0)", "setuptools", "sphinx (<2)", "tox"] - [[package]] name = "dill" version = "0.3.9" @@ -659,26 +474,6 @@ files = [ [package.extras] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] -[[package]] -name = "fabric" -version = "3.2.2" -description = "High level SSH command execution" -optional = false -python-versions = "*" -files = [ - {file = "fabric-3.2.2-py3-none-any.whl", hash = "sha256:91c47c0be68b14936c88b34da8a1f55e5710fd28397dac5d4ff2e21558113a6f"}, - {file = "fabric-3.2.2.tar.gz", hash = "sha256:8783ca42e3b0076f08b26901aac6b9d9b1f19c410074e7accfab902c184ff4a3"}, -] - -[package.dependencies] -decorator = ">=5" -deprecated = ">=1.2" -invoke = ">=2.0" -paramiko = ">=2.4" - -[package.extras] -pytest = ["pytest (>=7)"] - [[package]] name = "filelock" version = "3.16.1" @@ -863,17 +658,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "invoke" -version = "2.2.0" -description = "Pythonic task execution" -optional = false -python-versions = ">=3.6" -files = [ - {file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"}, - {file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"}, -] - [[package]] name = "ipython" version = "8.30.0" @@ -1347,16 +1131,6 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] -[[package]] -name = "netns" -version = "1.0" -description = "Wrapper for the Linux setns() system call" -optional = false -python-versions = "*" -files = [ - {file = "netns-1.0.tar.gz", hash = "sha256:dc994c53e415baa739072784292630724d609566fb4a60ed8feef38dd9ecdca5"}, -] - [[package]] name = "numpy" version = "2.2.0" @@ -1465,27 +1239,6 @@ files = [ {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] -[[package]] -name = "paramiko" -version = "3.5.0" -description = "SSH2 protocol library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "paramiko-3.5.0-py3-none-any.whl", hash = "sha256:1fedf06b085359051cd7d0d270cebe19e755a8a921cc2ddbfa647fb0cd7d68f9"}, - {file = "paramiko-3.5.0.tar.gz", hash = "sha256:ad11e540da4f55cedda52931f1a3f812a8238a7af7f62a60de538cd80bb28124"}, -] - -[package.dependencies] -bcrypt = ">=3.2" -cryptography = ">=3.3" -pynacl = ">=1.5" - -[package.extras] -all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] -gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] -invoke = ["invoke (>=2.0)"] - [[package]] name = "parso" version = "0.8.4" @@ -1728,17 +1481,6 @@ files = [ [package.extras] tests = ["pytest"] -[[package]] -name = "pycparser" -version = "2.22" -description = "C parser in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - [[package]] name = "pygments" version = "2.18.0" @@ -1782,32 +1524,6 @@ tomlkit = ">=0.10.1" spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] -[[package]] -name = "pynacl" -version = "1.5.0" -description = "Python binding to the Networking and Cryptography (NaCl) library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, - {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, - {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, - {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, - {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, - {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, -] - -[package.dependencies] -cffi = ">=1.4.1" - -[package.extras] -docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] - [[package]] name = "pytest" version = "8.3.4" @@ -2565,80 +2281,6 @@ files = [ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] -[[package]] -name = "wrapt" -version = "1.17.0" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.8" -files = [ - {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, - {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, - {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, - {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, - {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, - {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, - {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, - {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, - {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, - {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, - {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, - {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, - {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, - {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, - {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, - {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, - {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, - {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, - {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, - {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, - {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, - {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, - {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, -] - [[package]] name = "yarl" version = "1.18.3" @@ -2738,4 +2380,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "^3.10.0" -content-hash = "c3b6e2c1d9a0a7926b61ca38f9e91fd1c1c0519a1703d6eaa0fd07fcf5898cbd" +content-hash = "e38bfcb8811fb9b774956ed4b0e82603dbe5b66d8cb5bc4af6ed86417a9d5d1d" diff --git a/tools/devctr/pyproject.toml b/tools/devctr/pyproject.toml index 188a3b315bd..a4f7178aa5d 100644 --- a/tools/devctr/pyproject.toml +++ b/tools/devctr/pyproject.toml @@ -10,7 +10,6 @@ aws-embedded-metrics = "^3.1.0" black = "^24.3.0" # Gitlint locks this to 8.1.3. Lock from our side too to prevent different versions click = "8.1.3" -fabric = "^3.2.2" filelock = "^3.13.4" gitlint = "^0.19.1" ipython = "^8.15.0" @@ -19,7 +18,6 @@ mdformat = "^0.7.17" mdformat-gfm = "^0.3.5" mdformat-footnote = "^0.1.1" mdformat-frontmatter = "^2.0.8" -netns = "^1.0.0" openapi-spec-validator = "^0.7.1" psutil = "^6.0.0" pylint = "^3" From 8c7ee82f9d603faa6776671024ca7258c8e28d19 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 11 Dec 2024 16:08:50 +0000 Subject: [PATCH 09/15] fix: properly clean up tmp dir after shared build Building the A/B binaries happens in a tmpdir, which we try to delete at the end. But we create files in the tmpdir from a privileged docker container, so to delete this tmpdir we also need elevated privileges Signed-off-by: Patrick Roy --- tools/devtool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/devtool b/tools/devtool index 3b4d89ec392..7d41229b2e7 100755 --- a/tools/devtool +++ b/tools/devtool @@ -525,7 +525,7 @@ cmd_build() { git branch -D $branch_name mkdir -p build/"$revision" cp $tmp_dir/build/cargo_target/$(uname -m)-unknown-linux-$libc/$profile/* build/"$revision" - rm -rf $tmp_dir + cmd_sh "rm -rf $tmp_dir" fi return $ret From 0b4266b0f3112e949bb4bf6eb5425c2d8ff097cb Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 13 Dec 2024 08:12:11 +0000 Subject: [PATCH 10/15] fix(test): remove timeouts from MSR integration tests These run in a nightly pipeline and apparently the read/write MSR scripts take longer than the 100s default timeout added in b99abe1. Fixes: 36448e94ebf4 ("test: set default timeout of 100s for ssh commands") Signed-off-by: Patrick Roy --- .../functional/test_cpu_features_x86_64.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/integration_tests/functional/test_cpu_features_x86_64.py b/tests/integration_tests/functional/test_cpu_features_x86_64.py index 1af6a39f83a..dce36254cb6 100644 --- a/tests/integration_tests/functional/test_cpu_features_x86_64.py +++ b/tests/integration_tests/functional/test_cpu_features_x86_64.py @@ -314,7 +314,7 @@ def test_cpu_rdmsr( ) vm.start() vm.ssh.scp_put(DATA_FILES / "msr_reader.sh", "/tmp/msr_reader.sh") - _, stdout, stderr = vm.ssh.run("/tmp/msr_reader.sh") + _, stdout, stderr = vm.ssh.run("/tmp/msr_reader.sh", timeout=None) assert stderr == "" # Load results read from the microvm @@ -362,7 +362,9 @@ def dump_msr_state_to_file(dump_fname, ssh_conn, shared_names): ssh_conn.scp_put( shared_names["msr_reader_host_fname"], shared_names["msr_reader_guest_fname"] ) - _, stdout, stderr = ssh_conn.run(shared_names["msr_reader_guest_fname"]) + _, stdout, stderr = ssh_conn.run( + shared_names["msr_reader_guest_fname"], timeout=None + ) assert stderr == "" with open(dump_fname, "w", encoding="UTF-8") as file: @@ -416,7 +418,9 @@ def test_cpu_wrmsr_snapshot(microvm_factory, guest_kernel, rootfs, msr_cpu_templ wrmsr_input_guest_fname = "/tmp/wrmsr_input.txt" vm.ssh.scp_put(wrmsr_input_host_fname, wrmsr_input_guest_fname) - _, _, stderr = vm.ssh.run(f"{msr_writer_guest_fname} {wrmsr_input_guest_fname}") + _, _, stderr = vm.ssh.run( + f"{msr_writer_guest_fname} {wrmsr_input_guest_fname}", timeout=None + ) assert stderr == "" # Dump MSR state to a file that will be published to S3 for the 2nd part of the test From e56b6a2f109e036d31966cde0f0f963e4ac5172c Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 13 Dec 2024 08:25:16 +0000 Subject: [PATCH 11/15] gitlint: ignore "Fixes" lines Since these repeat commit titles, they can get longer than the configured body line length. So just ignore them. Signed-off-by: Patrick Roy --- .gitlint | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlint b/.gitlint index 8840b19bfe8..3b0cc0e07a0 100644 --- a/.gitlint +++ b/.gitlint @@ -9,8 +9,8 @@ line-length=72 [ignore-body-lines] # Ignore HTTP reference links -# Ignore lines that start with 'Co-Authored-By' or with 'Signed-off-by' -regex=(^\[.+\]: http.+)|(^Co-Authored-By)|(^Signed-off-by) +# Ignore lines that start with 'Co-Authored-By', with 'Signed-off-by' or with 'Fixes' +regex=(^\[.+\]: http.+)|(^Co-Authored-By)|(^Signed-off-by)|(^Fixes:) [ignore-by-author-name] # Ignore certain rules for commits of which the author name matches a regex From e7ccf54e7ca8997b8d4be9f9f2240aea10046ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Barb=C3=A1chano?= Date: Thu, 12 Dec 2024 15:20:43 +0100 Subject: [PATCH 12/15] tests: compile test_syscall with musl-gcc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In addition do a few more cleanups to the test: - Use `atol` to read longs - musl makes an ioctl that is not permitted in seccomp. We didn't use the return code anyway, so remove it. Signed-off-by: Pablo Barbáchano --- tests/host_tools/test_syscalls.c | 13 ++++++------- .../security/test_seccomp_validate.py | 16 ++++++++-------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/host_tools/test_syscalls.c b/tests/host_tools/test_syscalls.c index 6a58edf0983..685e45acde0 100644 --- a/tests/host_tools/test_syscalls.c +++ b/tests/host_tools/test_syscalls.c @@ -28,7 +28,6 @@ void install_bpf_filter(char *bpf_file) { exit(EXIT_FAILURE); } size_t size = sb.st_size; - size_t insn_len = size / sizeof(struct sock_filter); struct sock_filter *filterbuf = (struct sock_filter*)malloc(size); if (read(fd, filterbuf, size) == -1) { perror("read"); @@ -36,6 +35,7 @@ void install_bpf_filter(char *bpf_file) { } /* Install seccomp filter */ + size_t insn_len = size / sizeof(struct sock_filter); struct sock_fprog prog = { .len = (unsigned short)(insn_len), .filter = filterbuf, @@ -60,11 +60,11 @@ int main(int argc, char **argv) { char *bpf_file = argv[1]; long syscall_id = atoi(argv[2]); long arg0, arg1, arg2, arg3; - arg0 = arg1 = arg2 = arg3 = 0; - if (argc > 3) arg0 = atoi(argv[3]); - if (argc > 4) arg1 = atoi(argv[4]); - if (argc > 5) arg2 = atoi(argv[5]); - if (argc > 6) arg3 = atoi(argv[6]); + arg0 = arg1 = arg2 = arg3 = 0L; + if (argc > 3) arg0 = atol(argv[3]); + if (argc > 4) arg1 = atol(argv[4]); + if (argc > 5) arg2 = atol(argv[5]); + if (argc > 6) arg3 = atol(argv[6]); /* read seccomp filter from file */ if (strcmp(bpf_file, "/dev/null") != 0) { @@ -72,6 +72,5 @@ int main(int argc, char **argv) { } long res = syscall(syscall_id, arg0, arg1, arg2, arg3); - printf("%ld\n", res); return EXIT_SUCCESS; } diff --git a/tests/integration_tests/security/test_seccomp_validate.py b/tests/integration_tests/security/test_seccomp_validate.py index 5a0be067899..b91c7590a2b 100644 --- a/tests/integration_tests/security/test_seccomp_validate.py +++ b/tests/integration_tests/security/test_seccomp_validate.py @@ -13,18 +13,18 @@ import seccomp from framework import utils -from host_tools import cargo_build ARCH = platform.machine() -@pytest.fixture(scope="session") -def bin_test_syscall(test_fc_session_root_path): +@pytest.fixture +def bin_test_syscall(tmp_path): """Build the test_syscall binary.""" - test_syscall_bin = Path(test_fc_session_root_path) / "test_syscall" - cargo_build.gcc_compile("host_tools/test_syscalls.c", test_syscall_bin) + test_syscall_bin = tmp_path / "test_syscall" + compile_cmd = f"musl-gcc -static host_tools/test_syscalls.c -o {test_syscall_bin}" + utils.check_output(compile_cmd) assert test_syscall_bin.exists() - yield test_syscall_bin + yield test_syscall_bin.resolve() class BpfMapReader: @@ -77,11 +77,11 @@ def split(self): for _ in range(map_len): # read key key_str_len = self.read_format(" Date: Fri, 13 Dec 2024 11:15:53 +0000 Subject: [PATCH 13/15] fix(test): ensure socat gets killed in test_5_snapshots Use a try-finally block inside check_guest_connection to make sure that socat gets killed even if something inside check_guest_connection fails and raises an exception. Signed-off-by: Patrick Roy --- tests/framework/utils_vsock.py | 96 +++++++++++++++++----------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/tests/framework/utils_vsock.py b/tests/framework/utils_vsock.py index 3f6885e3afd..fe192e92f06 100644 --- a/tests/framework/utils_vsock.py +++ b/tests/framework/utils_vsock.py @@ -142,53 +142,55 @@ def check_guest_connections(vm, server_port_path, blob_path, blob_hash): ["socat", f"UNIX-LISTEN:{server_port_path},fork,backlog=5", "exec:'/bin/cat'"] ) - # Link the listening Unix socket into the VM's jail, so that - # Firecracker can connect to it. - attempt = 0 - # But 1st, give socat a bit of time to create the socket - while not Path(server_port_path).exists() and attempt < 3: - time.sleep(0.2) - attempt += 1 - vm.create_jailed_resource(server_port_path) - - # Increase maximum process count for the ssh service. - # Avoids: "bash: fork: retry: Resource temporarily unavailable" - # Needed to execute the bash script that tests for concurrent - # vsock guest initiated connections. - pids_max_file = "/sys/fs/cgroup/system.slice/ssh.service/pids.max" - ecode, _, _ = vm.ssh.run(f"echo 1024 > {pids_max_file}") - assert ecode == 0, "Unable to set max process count for guest ssh service." - - # Build the guest worker sub-command. - # `vsock_helper` will read the blob file from STDIN and send the echo - # server response to STDOUT. This response is then hashed, and the - # hash is compared against `blob_hash` (computed on the host). This - # comparison sets the exit status of the worker command. - worker_cmd = "hash=$(" - worker_cmd += "cat {}".format(blob_path) - worker_cmd += " | /tmp/vsock_helper echo 2 {}".format(ECHO_SERVER_PORT) - worker_cmd += " | md5sum | cut -f1 -d\\ " - worker_cmd += ")" - worker_cmd += ' && [[ "$hash" = "{}" ]]'.format(blob_hash) - - # Run `TEST_CONNECTION_COUNT` concurrent workers, using the above - # worker sub-command. - # If any worker fails, this command will fail. If all worker sub-commands - # succeed, this will also succeed. - cmd = 'workers="";' - cmd += "for i in $(seq 1 {}); do".format(TEST_CONNECTION_COUNT) - cmd += " ({})& ".format(worker_cmd) - cmd += ' workers="$workers $!";' - cmd += "done;" - cmd += "for w in $workers; do wait $w || (wait; exit 1); done" - - ecode, _, stderr = vm.ssh.run(cmd) - echo_server.terminate() - rc = echo_server.wait() - # socat exits with 128 + 15 (SIGTERM) - assert rc == 143 - - assert ecode == 0, stderr + try: + # Link the listening Unix socket into the VM's jail, so that + # Firecracker can connect to it. + attempt = 0 + # But 1st, give socat a bit of time to create the socket + while not Path(server_port_path).exists() and attempt < 3: + time.sleep(0.2) + attempt += 1 + vm.create_jailed_resource(server_port_path) + + # Increase maximum process count for the ssh service. + # Avoids: "bash: fork: retry: Resource temporarily unavailable" + # Needed to execute the bash script that tests for concurrent + # vsock guest initiated connections. + pids_max_file = "/sys/fs/cgroup/system.slice/ssh.service/pids.max" + ecode, _, _ = vm.ssh.run(f"echo 1024 > {pids_max_file}") + assert ecode == 0, "Unable to set max process count for guest ssh service." + + # Build the guest worker sub-command. + # `vsock_helper` will read the blob file from STDIN and send the echo + # server response to STDOUT. This response is then hashed, and the + # hash is compared against `blob_hash` (computed on the host). This + # comparison sets the exit status of the worker command. + worker_cmd = "hash=$(" + worker_cmd += "cat {}".format(blob_path) + worker_cmd += " | /tmp/vsock_helper echo 2 {}".format(ECHO_SERVER_PORT) + worker_cmd += " | md5sum | cut -f1 -d\\ " + worker_cmd += ")" + worker_cmd += ' && [[ "$hash" = "{}" ]]'.format(blob_hash) + + # Run `TEST_CONNECTION_COUNT` concurrent workers, using the above + # worker sub-command. + # If any worker fails, this command will fail. If all worker sub-commands + # succeed, this will also succeed. + cmd = 'workers="";' + cmd += "for i in $(seq 1 {}); do".format(TEST_CONNECTION_COUNT) + cmd += " ({})& ".format(worker_cmd) + cmd += ' workers="$workers $!";' + cmd += "done;" + cmd += "for w in $workers; do wait $w || (wait; exit 1); done" + + ecode, _, stderr = vm.ssh.run(cmd) + + assert ecode == 0, stderr + finally: + echo_server.terminate() + rc = echo_server.wait() + # socat exits with 128 + 15 (SIGTERM) + assert rc == 143 def make_host_port_path(uds_path, port): From 130af91a280d6a26db16893e18106a215be8799e Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 13 Dec 2024 11:18:15 +0000 Subject: [PATCH 14/15] refactor(test): Use check_output in check_guest_connections Now that the assertion on the `run` return code is paired with the .run call itself, combine them into .check_output. Also use check_output further up. Signed-off-by: Patrick Roy --- tests/framework/utils_vsock.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/framework/utils_vsock.py b/tests/framework/utils_vsock.py index fe192e92f06..244850c3496 100644 --- a/tests/framework/utils_vsock.py +++ b/tests/framework/utils_vsock.py @@ -156,9 +156,9 @@ def check_guest_connections(vm, server_port_path, blob_path, blob_hash): # Avoids: "bash: fork: retry: Resource temporarily unavailable" # Needed to execute the bash script that tests for concurrent # vsock guest initiated connections. - pids_max_file = "/sys/fs/cgroup/system.slice/ssh.service/pids.max" - ecode, _, _ = vm.ssh.run(f"echo 1024 > {pids_max_file}") - assert ecode == 0, "Unable to set max process count for guest ssh service." + vm.ssh.check_output( + "echo 1024 > /sys/fs/cgroup/system.slice/ssh.service/pids.max" + ) # Build the guest worker sub-command. # `vsock_helper` will read the blob file from STDIN and send the echo @@ -183,9 +183,7 @@ def check_guest_connections(vm, server_port_path, blob_path, blob_hash): cmd += "done;" cmd += "for w in $workers; do wait $w || (wait; exit 1); done" - ecode, _, stderr = vm.ssh.run(cmd) - - assert ecode == 0, stderr + vm.ssh.check_output(cmd) finally: echo_server.terminate() rc = echo_server.wait() From 77d3704f847d3ef32a33a0c18b330fb5e2534cb1 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 13 Dec 2024 11:23:15 +0000 Subject: [PATCH 15/15] refactor(test): improve retry look in check_guest_connections Use `tenancy.Retrying` instead of the home-grown retry loop. This has the advantage that we don't simply give up after 3 attempts, but instead raise a meaningful error message (whereas before I guess we would just fail further down the function when trying to do something that assumes the file exists). Signed-off-by: Patrick Roy --- tests/framework/utils_vsock.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/framework/utils_vsock.py b/tests/framework/utils_vsock.py index 244850c3496..9561c1c26f2 100644 --- a/tests/framework/utils_vsock.py +++ b/tests/framework/utils_vsock.py @@ -11,6 +11,8 @@ from subprocess import Popen from threading import Thread +from tenacity import Retrying, stop_after_attempt, wait_fixed + ECHO_SERVER_PORT = 5252 SERVER_ACCEPT_BACKLOG = 128 TEST_CONNECTION_COUNT = 50 @@ -143,13 +145,17 @@ def check_guest_connections(vm, server_port_path, blob_path, blob_hash): ) try: + # Give socat a bit of time to create the socket + for attempt in Retrying( + wait=wait_fixed(0.2), + stop=stop_after_attempt(3), + reraise=True, + ): + with attempt: + assert Path(server_port_path).exists() + # Link the listening Unix socket into the VM's jail, so that # Firecracker can connect to it. - attempt = 0 - # But 1st, give socat a bit of time to create the socket - while not Path(server_port_path).exists() and attempt < 3: - time.sleep(0.2) - attempt += 1 vm.create_jailed_resource(server_port_path) # Increase maximum process count for the ssh service.