Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package aws-efs-utils for openSUSE:Factory checked in at 2025-04-02 17:11:47 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-efs-utils (Old) and /work/SRC/openSUSE:Factory/.aws-efs-utils.new.1907 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "aws-efs-utils" Wed Apr 2 17:11:47 2025 rev:19 rq:1265933 version:2.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-efs-utils/aws-efs-utils.changes 2024-08-01 22:04:40.821087651 +0200 +++ /work/SRC/openSUSE:Factory/.aws-efs-utils.new.1907/aws-efs-utils.changes 2025-04-02 17:12:57.248319147 +0200 @@ -1,0 +2,17 @@ +Mon Mar 31 09:42:29 UTC 2025 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to version 2.2.1 + * Readme Updates + * Update log4rs to mitigate CVE-2020-35881 +- from version 2.2.0 + * Use region-specific domain suffixes for dns endpoints where missing + * Merge PR #211 - Amend Debian control to use binary architecture +- from version 2.1.0 + * Add mount option for specifying region + * Add new ISO regions to config file +- from version 2.0.4 + * Add retry logic to and increase timeout for EC2 metadata token + retrieval requests +- Update BuildRequires from requirements.txt + +------------------------------------------------------------------- Old: ---- efs-utils-2.0.3.tar.gz New: ---- efs-utils-2.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-efs-utils.spec ++++++ --- /var/tmp/diff_new_pack.LfyBQ8/_old 2025-04-02 17:12:58.112355391 +0200 +++ /var/tmp/diff_new_pack.LfyBQ8/_new 2025-04-02 17:12:58.112355391 +0200 @@ -1,7 +1,7 @@ # # spec file for package aws-efs-utils # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -23,7 +23,7 @@ %endif %global _sitelibdir %{%{pythons}_sitelib} Name: aws-efs-utils -Version: 2.0.3 +Version: 2.2.1 Release: 0 Summary: Utilities for using the EFS file systems License: MIT @@ -34,23 +34,21 @@ Patch1: harden_amazon-efs-mount-watchdog.service.patch Patch2: skip-styletest.patch Patch3: use_mock_from_unittest.patch -BuildRequires: %{pythons}-attrs >= 17.4.0 -BuildRequires: %{pythons}-botocore >= 1.17.53 -BuildRequires: %{pythons}-coverage >= 4.5.4 -BuildRequires: openssl -#BuildRequires: %{pythons}-flake8 >= 3.7.9 -BuildRequires: %{pythons}-flake8 +BuildRequires: %{pythons}-botocore >= 1.34.140 +BuildRequires: %{pythons}-coverage >= 7.6.0 +BuildRequires: %{pythons}-flake8 >= 7.1.0 BuildRequires: %{pythons}-mccabe >= 0.6.1 BuildRequires: %{pythons}-pbr >= 3.1.1 BuildRequires: %{pythons}-pluggy >= 0.13.0 BuildRequires: %{pythons}-py >= 1.11.0 BuildRequires: %{pythons}-pycodestyle >= 2.5.0 BuildRequires: %{pythons}-pyflakes >= 2.1.1 -BuildRequires: %{pythons}-pytest >= 4.6.7 -BuildRequires: %{pythons}-pytest-cov >= 2.8.1 -BuildRequires: %{pythons}-pytest-html >= 1.19.0 -BuildRequires: %{pythons}-pytest-metadata >= 1.7.0 -BuildRequires: %{pythons}-pytest-mock >= 1.11.2 +BuildRequires: %{pythons}-pytest >= 8.2.2 +BuildRequires: %{pythons}-pytest-cov >= 5.0.0 +BuildRequires: %{pythons}-pytest-html >= 4.1.1 +BuildRequires: %{pythons}-pytest-metadata >= 3.1.1 +BuildRequires: %{pythons}-pytest-mock >= 3.14.0 +BuildRequires: openssl BuildRequires: systemd-rpm-macros BuildRequires: pkgconfig(systemd) Requires: nfs-utils ++++++ efs-utils-2.0.3.tar.gz -> efs-utils-2.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/.circleci/config.yml new/efs-utils-2.2.1/.circleci/config.yml --- old/efs-utils-2.0.3/.circleci/config.yml 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/.circleci/config.yml 2025-03-11 18:40:23.000000000 +0100 @@ -50,7 +50,7 @@ name: Install dependencies command: | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata - apt-get -y install binutils git rustc cargo pkg-config libssl-dev + apt-get -y install binutils git rustc cargo pkg-config libssl-dev gettext - run: name: Add local build repo as safe git directory command: | @@ -99,7 +99,7 @@ - run: name: Install dependencies command: | - yum install -y curl + yum install --skip-broken -y curl - run: name: Install latest Rust command: | @@ -233,11 +233,20 @@ workflow: jobs: - test: + name: python3_8 + image: python:3.8.13 + - test: name: python3_9 image: python:3.9.13 - test: - name: python3_8 - image: python:3.8.13 + name: python3_10 + image: python:3.10.13 + - test: + name: python3_11 + image: python:3.11.9 + - test: + name: python3_12 + image: python:3.12.4 - build-deb-package: name: ubuntu-latest image: ubuntu:latest @@ -265,15 +274,12 @@ - build-rpm-package: name: rocky8 image: rockylinux/rockylinux:8 - - build-rpm-package: + - build-rpm-package-rustup: name: amazon-linux-latest image: amazonlinux:latest - - build-rpm-package: + - build-rpm-package-rustup: name: amazon-linux-2 image: amazonlinux:2 - - build-rpm-package: - name: fedora-latest - image: fedora:latest - build-rpm-package-rustup: name: fedora29 image: fedora:29 @@ -292,10 +298,10 @@ - build-rpm-package-rustup: name: fedora34 image: fedora:34 - - build-rpm-package: + - build-rpm-package-rustup: name: fedora35 image: fedora:35 - - build-rpm-package: + - build-rpm-package-rustup: name: fedora36 image: fedora:36 - build-suse-rpm-package: @@ -312,4 +318,4 @@ image: opensuse/leap:15.4 - build-suse-rpm-package: name: opensuse-leap-latest - image: opensuse/leap:latest + image: opensuse/leap:latest \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/Makefile new/efs-utils-2.2.1/Makefile --- old/efs-utils-2.0.3/Makefile 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/Makefile 2025-03-11 18:40:23.000000000 +0100 @@ -11,6 +11,7 @@ SPECFILE = $(PACKAGE_NAME).spec BUILD_DIR = build/rpmbuild PROXY_VERSION = 2.0.0 +RPM_BUILD_FLAGS ?= --with system_rust export PYTHONPATH := $(shell pwd)/src .PHONY: clean @@ -48,12 +49,16 @@ cp $(SPECFILE) $(BUILD_DIR)/SPECS cp $(SOURCE_TARBALL) $(BUILD_DIR)/SOURCES cp config.toml $(BUILD_DIR)/SOURCES - rpmbuild -ba --define "_topdir `pwd`/$(BUILD_DIR)" --define "include_vendor_tarball false" $(BUILD_DIR)/SPECS/$(SPECFILE) + rpmbuild -ba --define "_topdir `pwd`/$(BUILD_DIR)" --define "include_vendor_tarball false" $(BUILD_DIR)/SPECS/$(SPECFILE) $(RPM_BUILD_FLAGS) cp $(BUILD_DIR)/RPMS/*/*rpm build .PHONY: rpm rpm: sources rpm-only +.PHONY: rpm-without-system-rust +rpm-without-system-rust: sources + $(MAKE) rpm-only RPM_BUILD_FLAGS="--without system_rust" + .PHONY: deb deb: ./build-deb.sh diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/README.md new/efs-utils-2.2.1/README.md --- old/efs-utils-2.0.3/README.md 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/README.md 2025-03-11 18:40:23.000000000 +0100 @@ -11,7 +11,6 @@ | Amazon Linux 2 | `rpm` | `systemd` | | Amazon Linux 2023 | `rpm` | `systemd` | | CentOS 8 | `rpm` | `systemd` | -| RHEL 7 | `rpm` | `systemd` | | RHEL 8 | `rpm` | `systemd` | | RHEL 9 | `rpm` | `systemd` | | Fedora 29 | `rpm` | `systemd` | @@ -37,6 +36,7 @@ | MacOS Monterey | `launchd` | | MacOS Ventura | `launchd` | | MacOS Sonoma | `launchd` | +| MacOS Sequoia | `launchd` | ## README contents - [Prerequisites](#prerequisites) @@ -82,7 +82,7 @@ * OpenSSL-devel 1.0.2+ * Python 3.7/3.8 * `stunnel` 4.56+ -- `rust` 1.68+ +- `rust` 1.70+ - `cargo` ## Optional @@ -117,7 +117,7 @@ Other distributions require building the package from source and installing it. If your distribution doesn't provide a rust or cargo package, or it provides versions -that are older than 1.68, then you can install rust and cargo through rustup: +that are older than 1.70, then you can install rust and cargo through rustup: ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh . "$HOME/.cargo/env" @@ -159,7 +159,7 @@ ```bash $ sudo apt-get update -$ sudo apt-get -y install git binutils rustc cargo pkg-config libssl-dev +$ sudo apt-get -y install git binutils rustc cargo pkg-config libssl-dev gettext $ git clone https://github.com/aws/efs-utils $ cd efs-utils $ ./build-deb.sh @@ -167,7 +167,7 @@ ``` If your Debian distribution doesn't provide a rust or cargo package, or your distribution provides versions -that are older than 1.68, then you can install rust and cargo through rustup: +that are older than 1.70, then you can install rust and cargo through rustup: ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh . "$HOME/.cargo/env" @@ -177,7 +177,7 @@ **`make rpm` fails due to "feature `edition2021` is required"**: Update to a version of rust and cargo -that is newer than 1.68. To install a new version of rust and cargo, run +that is newer than 1.70. To install a new version of rust and cargo, run ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh . "$HOME/.cargo/env" @@ -202,11 +202,23 @@ yum install gcc ``` -### On MacOS Big Sur, macOS Monterey, macOS Sonoma and macOS Ventura distribution +**Installation Issue - Failed Build Dependencies** -For EC2 Mac instances running macOS Big Sur, macOS Monterey, macOS Sonoma and macOS Ventura, you can install amazon-efs-utils from the +If rust dependencies was installed using rustup and the package manager does not have a rust and/or cargo package installed, you may see an error like this. + +``` +error: Failed build dependencies: + cargo is needed by amazon-efs-utils-2.1.0-1.el7_9.x86_64 + rust is needed by amazon-efs-utils-2.1.0-1.el7_9.x86_64 +``` + +In this case, the 'make rpm' command in the installation script above should be replaced by 'make rpm-without-system-rust' to remove the rpmbuild dependency check. + +### On macOS Sequoia, macOS Big Sur, macOS Monterey, macOS Sonoma and macOS Ventura distribution + +For EC2 Mac instances running macOS Sequoia, macOS Big Sur, macOS Monterey, macOS Sonoma and macOS Ventura, you can install amazon-efs-utils from the [homebrew-aws](https://github.com/aws/homebrew-aws) respository. **Note that this will ONLY work on EC2 instances -running macOS Big Sur, macOS Monterey, macOS Sonoma and macOS Ventura, not local Mac computers.** +running macOS Sequoia, macOS Big Sur, macOS Monterey, macOS Sonoma and macOS Ventura, not local Mac computers.** ```bash brew install amazon-efs-utils ``` @@ -260,12 +272,18 @@ $ sudo mount -t efs -o netns=netns-path file-system-id efs-mount-point/ ``` -To mount file system to the mount target in specific availability zone (e.g. us-east-1a), run: +To mount file system to the mount target in a specific availability zone (e.g. us-east-1a), run: ```bash $ sudo mount -t efs -o az=az-name file-system-id efs-mount-point/ ``` +To mount file system to the mount target in a specific region (e.g. us-east-1), run: + +```bash +$ sudo mount -t efs -o region=region-name file-system-id efs-mount-point/ +``` + **Note: The [prequisites in the crossaccount section below](#crossaccount-option-prerequisites) must be completed before using the crossaccount option.** To mount the filesystem mount target in the same physical availability zone ID (e.g. use1-az1) as the client instance over cross-AWS-account mounts, run: @@ -372,7 +390,7 @@ Efs-proxy is not compatible with OCSP or Mac clients. In these cases, efs-utils will automatically revert back to using stunnel. -If you are building efs-utils v2.0.0 from source, then you need Rust and Cargo >= 1.68. +If you are building efs-utils v2.0.0 from source, then you need Rust and Cargo >= 1.70. ## Upgrading stunnel for RHEL/CentOS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/amazon-efs-utils.spec new/efs-utils-2.2.1/amazon-efs-utils.spec --- old/efs-utils-2.0.3/amazon-efs-utils.spec 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/amazon-efs-utils.spec 2025-03-11 18:40:23.000000000 +0100 @@ -41,7 +41,7 @@ %{?!include_vendor_tarball:%define include_vendor_tarball true} Name : amazon-efs-utils -Version : 2.0.3 +Version : 2.2.1 Release : 1%{platform} Summary : This package provides utilities for simplifying the use of EFS file systems @@ -72,16 +72,21 @@ Requires(postun) : /sbin/service %endif -# RHEL 7 doesn't provide a Rust or Cargo package, -# so users are expected to install it through rustup. -%if ! 0%{?rhel} == 7 -BuildRequires : cargo rust +# Conditional to allow building without +# rust installed with yum +%bcond_without system_rust + +# If yum provides rust and cargo +# use rpmbuild --with system_rust +%if %{with system_rust} +BuildRequires: cargo rust %endif + BuildRequires: openssl-devel Source0 : %{name}.tar.gz %if "%{include_vendor_tarball}" == "true" -Source1 : %{proxy_name}-%{$Version}-vendor.tar.xz +Source1 : %{proxy_name}-%{version}-vendor.tar.xz Source2 : config.toml %endif @@ -91,6 +96,25 @@ %global debug_package %{nil} %prep + +# If yum doesn't provides rust and cargo +# use rpmbuild --without system_rust +%if %{without system_rust} +source $HOME/.cargo/env || true +%endif + +# Ensure cargo is installed +if ! command -v cargo &> /dev/null; then + echo "Error: cargo is not in PATH. Please install cargo." + exit 1 +fi + +# Ensure rustc is installed +if ! command -v rustc &> /dev/null; then + echo "Error: rustc is not PATH. Please install rustc." + exit 1 +fi + %setup -n %{name} mkdir -p %{_builddir}/%{name}/src/proxy/.cargo %if "%{include_vendor_tarball}" == "true" @@ -168,19 +192,34 @@ %clean %changelog +* Thu Mar 06 2025 Daniel Luthcke <dluth...@amazon.com> - 2.2.1 +- Readme Updates +- Update log4rs to mitigate CVE-2020-35881 + +* Wed Nov 13 2024 Anthony Tse <antho...@amazon.com> - 2.2.0 +- Use region-specific domain suffixes for dns endpoints where missing +- Merge PR #211 - Amend Debian control to use binary architecture + +* Wed Sep 18 2024 Julie Rakas <jra...@amazon.com> - 2.1.0 +- Add mount option for specifying region +- Add new ISO regions to config file + +* Tue Jun 25 2024 Anthony Tse <antho...@amazon.com> - 2.0.4 +- Add retry logic to and increase timeout for EC2 metadata token retrieval requests + * Tue Jun 18 2024 Arnav Gupta <arnav...@amazon.com> - 2.0.3 - Upgrade py version -- Replace deprecated usage of datetime +- Replace deprecated usage of datetime * Mon May 20 2024 Anthony Tse <antho...@amazon.com> - 2.0.2 - Check for efs-proxy PIDs when cleaning tunnel state files - Add PID to log entries -* Mon Apr 23 2024 Ryan Stankiewicz <rjst...@amazon.com> - 2.0.1 -- Disable Nagle's algorithm for efs-proxy TLS mounts to improve latencies +* Tue Apr 23 2024 Ryan Stankiewicz <rjst...@amazon.com> - 2.0.1 +- Disable Nagle's algorithm for efs-proxy TLS mounts to improve latencies * Mon Apr 08 2024 Ryan Stankiewicz <rjst...@amazon.com> - 2.0.0 -- Replace stunnel, which provides TLS encryptions for mounts, with efs-proxy, a component built in-house at AWS. Efs-proxy lays the foundation for upcoming feature launches at EFS. +- Replace stunnel, which provides TLS encryptions for mounts, with efs-proxy, a component built in-house at AWS. Efs-proxy lays the foundation for upcoming feature launches at EFS. * Mon Mar 18 2024 Sean Zatz <zatz...@amazon.com> - 1.36.0 - Support new mount option: crossaccount, conduct cross account mounts via ip address. Use client AZ-ID to choose mount target. @@ -200,7 +239,7 @@ - Add debug statement for size of state file write - Add parameters in mount options for assume web role with web identity -* Wed Jan 1 2023 Ryan Stankiewicz <rjst...@amazon.com> - 1.34.5 +* Wed Jan 4 2023 Ryan Stankiewicz <rjst...@amazon.com> - 1.34.5 - Watchdog detect empty private key and regenerate - Update man page - Avoid redundant get_target_region call diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/build-deb.sh new/efs-utils-2.2.1/build-deb.sh --- old/efs-utils-2.0.3/build-deb.sh 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/build-deb.sh 2025-03-11 18:40:23.000000000 +0100 @@ -11,9 +11,11 @@ BASE_DIR=$(pwd) BUILD_ROOT=${BASE_DIR}/build/debbuild -VERSION=2.0.3 +VERSION=2.2.1 RELEASE=1 +ARCH=$(dpkg --print-architecture) DEB_SYSTEM_RELEASE_PATH=/etc/os-release +export VERSION RELEASE ARCH echo 'Cleaning deb build workspace' rm -rf ${BUILD_ROOT} @@ -47,8 +49,9 @@ install -p -m 755 dist/scriptlets/before-remove ${BUILD_ROOT}/prerm install -p -m 755 dist/scriptlets/after-remove ${BUILD_ROOT}/postrm -echo 'Copying control file' -install -p -m 644 dist/amazon-efs-utils.control ${BUILD_ROOT}/control +echo 'Generating control file' +envsubst < dist/amazon-efs-utils.control > ${BUILD_ROOT}/control +chmod 644 ${BUILD_ROOT}/control echo 'Copying conffiles' install -p -m 644 dist/amazon-efs-utils.conffiles ${BUILD_ROOT}/conffiles @@ -69,7 +72,7 @@ cd ${BASE_DIR} echo 'Building deb' -DEB=${BUILD_ROOT}/amazon-efs-utils-${VERSION}-${RELEASE}_all.deb +DEB=${BUILD_ROOT}/amazon-efs-utils-${VERSION}-${RELEASE}_${ARCH}.deb ar r ${DEB} ${BUILD_ROOT}/debian-binary ar r ${DEB} ${BUILD_ROOT}/control.tar.gz ar r ${DEB} ${BUILD_ROOT}/data.tar.gz diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/config.ini new/efs-utils-2.2.1/config.ini --- old/efs-utils-2.0.3/config.ini 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/config.ini 2025-03-11 18:40:23.000000000 +0100 @@ -7,5 +7,5 @@ # [global] -version=2.0.3 +version=2.2.1 release=1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/dist/amazon-efs-utils.control new/efs-utils-2.2.1/dist/amazon-efs-utils.control --- old/efs-utils-2.0.3/dist/amazon-efs-utils.control 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/dist/amazon-efs-utils.control 2025-03-11 18:40:23.000000000 +0100 @@ -1,6 +1,6 @@ Package: amazon-efs-utils -Architecture: all -Version: 2.0.3 +Architecture: $ARCH +Version: $VERSION-$RELEASE Section: utils Depends: python3, nfs-common, stunnel4 (>= 4.56), openssl (>= 1.0.2), util-linux Priority: optional diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/dist/efs-utils.conf new/efs-utils-2.2.1/dist/efs-utils.conf --- old/efs-utils-2.0.3/dist/efs-utils.conf 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/dist/efs-utils.conf 2025-03-11 18:40:23.000000000 +0100 @@ -74,6 +74,22 @@ dns_name_suffix = sc2s.sgov.gov stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem +[mount.us-isob-west-1] +dns_name_suffix = sc2s.sgov.gov +stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + +[mount.us-isof-east-1] +dns_name_suffix = csp.hci.ic.gov +stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + +[mount.us-isof-south-1] +dns_name_suffix = csp.hci.ic.gov +stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + +[mount.eu-isoe-west-1] +dns_name_suffix = cloud.adc-e.uk +stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + [mount-watchdog] enabled = true poll_interval_sec = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/man/mount.efs.8 new/efs-utils-2.2.1/man/mount.efs.8 --- old/efs-utils-2.0.3/man/mount.efs.8 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/man/mount.efs.8 2025-03-11 18:40:23.000000000 +0100 @@ -79,6 +79,9 @@ \fBnotls\fR Mounts the EFS file system without TLS, applies for Mac distributions only\&. .TP +\fBregion\fR +Mounts the EFS file system from the specified region, overriding any config file value\&. +.TP \fBtlsport=\fR\fIn\fR Configures the proxy process to listen for connections from the NFS client on the specified port\&. This is applicable to both non-tls and tls mounts. By default, the \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/requirements.txt new/efs-utils-2.2.1/requirements.txt --- old/efs-utils-2.0.3/requirements.txt 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/requirements.txt 2025-03-11 18:40:23.000000000 +0100 @@ -1,20 +1,10 @@ -attrs==17.4.0 -botocore==1.17.53 -configparser==3.5.0 -coverage==4.5.4 -enum34==1.1.6 -flake8==3.7.9 -funcsigs==1.0.2 -mccabe==0.6.1 -mock==2.0.0 -pbr==3.1.1 -pluggy==0.13.0 -py==1.11.0 -pycodestyle==2.5.0 -pyflakes==2.1.1 -pytest==4.6.7 -pytest-cov==2.8.1 -pytest-html==1.19.0 -pytest-metadata==1.7.0 -pytest-mock==1.11.2 -six==1.11.0 +botocore == 1.34.140 +configparser == 7.0.0 +coverage == 7.6.0 +flake8 == 7.1.0 +pytest == 8.2.2 +pytest-cov == 5.0.0 +pytest-html == 4.1.1 +pytest-metadata == 3.1.1 +pytest-mock == 3.14.0 +mock == 5.1.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/mount_efs/__init__.py new/efs-utils-2.2.1/src/mount_efs/__init__.py --- old/efs-utils-2.0.3/src/mount_efs/__init__.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/mount_efs/__init__.py 2025-03-11 18:40:23.000000000 +0100 @@ -85,7 +85,7 @@ BOTOCORE_PRESENT = False -VERSION = "2.0.3" +VERSION = "2.2.1" SERVICE = "elasticfilesystem" AMAZON_LINUX_2_RELEASE_ID = "Amazon Linux release 2 (Karoo)" @@ -110,6 +110,7 @@ # 50ms DEFAULT_TIMEOUT = 0.05 DEFAULT_MACOS_VALUE = "macos" +DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT = 3 DEFAULT_NFS_MOUNT_COMMAND_RETRY_COUNT = 3 DEFAULT_NFS_MOUNT_COMMAND_TIMEOUT_SEC = 15 DISABLE_FETCH_EC2_METADATA_TOKEN_ITEM = "disable_fetch_ec2_metadata_token" @@ -187,14 +188,14 @@ FS_ID_RE = re.compile("^(?P<fs_id>fs-[0-9a-f]+)$") EFS_FQDN_RE = re.compile( - r"^((?P<az>[a-z0-9-]+)\.)?(?P<fs_id>fs-[0-9a-f]+)\.efs\." + r"^((?P<az>[a-z0-9-]+)\.)?(?P<fs_id>fs-[0-9a-f]+)\.(?:[a-z-]+\.)+" r"(?P<region>[a-z0-9-]+)\.(?P<dns_name_suffix>[a-z0-9.]+)$" ) AP_ID_RE = re.compile("^fsap-[0-9a-f]{17}$") CREDENTIALS_KEYS = ["AccessKeyId", "SecretAccessKey", "Token"] ECS_TASK_METADATA_API = "http://169.254.170.2" -STS_ENDPOINT_URL_FORMAT = "https://sts.{}.amazonaws.com/" +STS_ENDPOINT_URL_FORMAT = "https://sts.{}.{}/" INSTANCE_METADATA_TOKEN_URL = "http://169.254.169.254/latest/api/token" INSTANCE_METADATA_SERVICE_URL = ( "http://169.254.169.254/latest/dynamic/instance-identity/document/" @@ -241,6 +242,7 @@ "noocsp", "notls", "ocsp", + "region", "tls", "tlsport", "verify", @@ -283,6 +285,7 @@ MACOS_MONTEREY_RELEASE = "macOS-12" MACOS_VENTURA_RELEASE = "macOS-13" MACOS_SONOMA_RELEASE = "macOS-14" +MACOS_SEQUOIA_RELEASE = "macOS-15" # Multiplier for max read ahead buffer size @@ -297,11 +300,12 @@ MACOS_MONTEREY_RELEASE, MACOS_VENTURA_RELEASE, MACOS_SONOMA_RELEASE, + MACOS_SEQUOIA_RELEASE, ] MAC_OS_PLATFORM_LIST = ["darwin"] -# MacOS Versions : Sonoma - 23.*, Ventura - 22.*, Monterey - 21.*, Big Sur - 20.*, Catalina - 19.*, Mojave - 18.*. Catalina and Mojave are not supported for now -MAC_OS_SUPPORTED_VERSION_LIST = ["20", "21", "22", "23"] +# MacOS Versions : Sequoia - 24.*, Sonoma - 23.*, Ventura - 22.*, Monterey - 21.*, Big Sur - 20.*, Catalina - 19.*, Mojave - 18.*. Catalina and Mojave are not supported for now +MAC_OS_SUPPORTED_VERSION_LIST = ["20", "21", "22", "23", "24"] AWS_FIPS_ENDPOINT_CONFIG_ENV = "AWS_USE_FIPS_ENDPOINT" ECS_URI_ENV = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" @@ -369,14 +373,18 @@ sys.exit(exit_code) -def get_target_region(config): +def get_target_region(config, options): def _fatal_error(message): fatal_error( 'Error retrieving region. Please set the "region" parameter ' - "in the efs-utils configuration file.", + "in the efs-utils configuration file or specify it as a " + "mount option.", message, ) + if "region" in options: + return options.get("region") + try: return config.get(CONFIG_SECTION, "region") except NoOptionError: @@ -612,44 +620,73 @@ ) -def get_aws_ec2_metadata_token(timeout=DEFAULT_TIMEOUT): - # Normally the session token is fetched within 10ms, setting a timeout of 50ms here to abort the request - # and return None if the token has not returned within 50ms - try: - opener = build_opener(HTTPHandler) - request = Request(INSTANCE_METADATA_TOKEN_URL) +def get_aws_ec2_metadata_token( + request_timeout=0.5, + max_retries=DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT, + retry_delay=0.5, +): + """ + Retrieves the AWS EC2 metadata token. Typically, the token is fetched + within 10ms. We set a default timeout of 0.5 seconds to prevent mount + failures caused by slow requests. + + Args: + max_retries (int): The maximum number of retries. + retry_delay (int): The delay in seconds between retries. - request.add_header("X-aws-ec2-metadata-token-ttl-seconds", "21600") - request.get_method = lambda: "PUT" + Returns: + The AWS EC2 metadata token str or None if it cannot be retrieved. + """ + + def get_token(timeout): try: - res = opener.open(request, timeout=timeout) - return res.read() - except socket.timeout: - exception_message = "Timeout when getting the aws ec2 metadata token" - except HTTPError as e: - exception_message = "Failed to fetch token due to %s" % e - except Exception as e: - exception_message = ( - "Unknown error when fetching aws ec2 metadata token, %s" % e + opener = build_opener(HTTPHandler) + request = Request(INSTANCE_METADATA_TOKEN_URL) + request.add_header("X-aws-ec2-metadata-token-ttl-seconds", "21600") + request.get_method = lambda: "PUT" + try: + response = opener.open(request, timeout=timeout) + return response.read() + finally: + opener.close() + + except NameError: + headers = {"X-aws-ec2-metadata-token-ttl-seconds": "21600"} + request = Request( + INSTANCE_METADATA_TOKEN_URL, headers=headers, method="PUT" ) - logging.debug(exception_message) - return None - except NameError: - headers = {"X-aws-ec2-metadata-token-ttl-seconds": "21600"} - req = Request(INSTANCE_METADATA_TOKEN_URL, headers=headers, method="PUT") + response = urlopen(request, timeout=timeout) + return response.read() + + retries = 0 + while retries < max_retries: try: - res = urlopen(req, timeout=timeout) - return res.read() + return get_token(timeout=request_timeout) except socket.timeout: - exception_message = "Timeout when getting the aws ec2 metadata token" + logging.debug( + "Timeout when getting the aws ec2 metadata token. Attempt: %s/%s" + % (retries + 1, max_retries) + ) except HTTPError as e: - exception_message = "Failed to fetch token due to %s" % e + logging.debug( + "Failed to fetch token due to %s. Attempt: %s/%s" + % (e, retries + 1, max_retries) + ) except Exception as e: - exception_message = ( - "Unknown error when fetching aws ec2 metadata token, %s" % e + logging.debug( + "Unknown error when fetching aws ec2 metadata token, %s. Attempt: %s/%s" + % (e, retries + 1, max_retries) ) - logging.debug(exception_message) - return None + + retries += 1 + if retries < max_retries: + logging.debug("Retrying in %s seconds", retry_delay) + time.sleep(retry_delay) + else: + logging.debug( + "Unable to retrieve AWS EC2 metadata token. Maximum number of retries reached." + ) + return None def get_aws_security_credentials( @@ -799,9 +836,9 @@ else: return None, None - STS_ENDPOINT_URL = STS_ENDPOINT_URL_FORMAT.format(region) + sts_endpoint_url = get_sts_endpoint_url(config, region) webidentity_url = ( - STS_ENDPOINT_URL + sts_endpoint_url + "?" + urlencode( { @@ -815,11 +852,11 @@ ) unsuccessful_resp = ( - "Unsuccessful retrieval of AWS security credentials at %s." % STS_ENDPOINT_URL + "Unsuccessful retrieval of AWS security credentials at %s." % sts_endpoint_url ) url_error_msg = ( "Unable to reach %s to retrieve AWS security credentials. See %s for more info." - % (STS_ENDPOINT_URL, SECURITY_CREDS_WEBIDENTITY_HELP_URL) + % (sts_endpoint_url, SECURITY_CREDS_WEBIDENTITY_HELP_URL) ) resp = url_request_helper( config, @@ -849,6 +886,30 @@ return None, None +def get_sts_endpoint_url(config, region): + dns_name_suffix = get_dns_name_suffix(config, region) + return STS_ENDPOINT_URL_FORMAT.format(region, dns_name_suffix) + + +def get_dns_name_suffix(config, region): + return get_mount_config(config, region, "dns_name_suffix") + + +def get_mount_config(config, region, config_name): + try: + config_section = get_config_section(config, region) + return config.get(config_section, config_name) + except NoOptionError: + pass + + try: + return config.get(CONFIG_SECTION, config_name) + except NoOptionError: + fatal_error( + "Error retrieving config. Please set the {} configuration in efs-utils.conf".format(config_name) + ) + + def get_aws_security_credentials_from_instance_metadata(config, iam_role_name): security_creds_lookup_url = INSTANCE_IAM_URL + iam_role_name unsuccessful_resp = ( @@ -1717,7 +1778,7 @@ cert_details = None security_credentials = None client_info = get_client_info(config) - region = get_target_region(config) + region = get_target_region(config, options) if tls_enabled(options): cert_details = {} @@ -2632,8 +2693,9 @@ if options and "crossaccount" in options: try: az_id = get_az_id_from_instance_metadata(config, options) - region = get_target_region(config) - dns_name = "%s.%s.efs.%s.amazonaws.com" % (az_id, fs_id, region) + region = get_target_region(config, options) + dns_name_suffix = get_dns_name_suffix(config, region) + dns_name = "%s.%s.efs.%s.%s" % (az_id, fs_id, region, dns_name_suffix) except RuntimeError: err_msg = "Cannot retrieve AZ-ID from metadata service. This is required for the crossaccount mount option." fatal_error(err_msg) @@ -2655,27 +2717,18 @@ else: dns_name_format = dns_name_format.replace("{az}.", "") + region = None if "{region}" in dns_name_format: + region = get_target_region(config, options) expected_replacement_field_ct += 1 - format_args["region"] = get_target_region(config) + format_args["region"] = region if "{dns_name_suffix}" in dns_name_format: expected_replacement_field_ct += 1 - config_section = CONFIG_SECTION - region = format_args.get("region") - - if region: - config_section = get_config_section(config, region) - - format_args["dns_name_suffix"] = config.get( - config_section, "dns_name_suffix" - ) - - logging.debug( - "Using dns_name_suffix %s in config section [%s]", - format_args.get("dns_name_suffix"), - config_section, - ) + region = region or get_target_region(config, options) + dns_name_suffix = get_dns_name_suffix(config, region) + format_args["dns_name_suffix"] = dns_name_suffix + logging.debug("Using dns_name_suffix %s", dns_name_suffix) _validate_replacement_field_count( dns_name_format, expected_replacement_field_ct @@ -3350,7 +3403,7 @@ botocore_config = botocore.config.Config(use_fips_endpoint=True) session = botocore.session.get_session() - region = get_target_region(config) + region = get_target_region(config, options) if options and options.get("awsprofile"): profile = options.get("awsprofile") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/Cargo.toml new/efs-utils-2.2.1/src/proxy/Cargo.toml --- old/efs-utils-2.0.3/src/proxy/Cargo.toml 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/Cargo.toml 2025-03-11 18:40:23.000000000 +0100 @@ -3,8 +3,9 @@ edition = "2021" build = "build.rs" # The version of efs-proxy is tied to efs-utils. -version = "2.0.3" +version = "2.2.1" publish = false +license = "MIT" [dependencies] anyhow = "1.0.72" @@ -15,7 +16,7 @@ fern = "0.6" futures = "0.3" log = "0.4" -log4rs = { version = "0", features = ["rolling_file_appender", "compound_policy", "size_trigger", "fixed_window_roller"]} +log4rs = { version = "1.2.0", features = ["rolling_file_appender", "compound_policy", "size_trigger", "fixed_window_roller"]} nix = { version = "0.26.2", features = ["signal"]} onc-rpc = "0.2.3" rand = "0.8.5" @@ -25,7 +26,7 @@ serde = {version="1.0.175",features=["derive"]} serde_ini = "0.2.0" thiserror = "1.0.44" -tokio = { version = "1.29.0", features = ["full"] } +tokio = { version = "1.29.0, <1.39", features = ["full"] } tokio-util = "0.7.8" uuid = { version = "1.4.1", features = ["v4", "fast-rng", "macro-diagnostics"]} xdr-codec = "0.4.4" @@ -36,4 +37,4 @@ tempfile = "3.10.1" [build-dependencies] -xdrgen = "0.4.4" \ No newline at end of file +xdrgen = "0.4.4" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/src/config_parser.rs new/efs-utils-2.2.1/src/proxy/src/config_parser.rs --- old/efs-utils-2.0.3/src/proxy/src/config_parser.rs 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/src/config_parser.rs 2025-03-11 18:40:23.000000000 +0100 @@ -95,12 +95,12 @@ pub static TEST_CONFIG_PATH: &str = "tests/certs/test_config.ini"; pub fn get_test_config() -> ProxyConfig { - ProxyConfig::from_path(&Path::new(TEST_CONFIG_PATH)).expect("Could not parse test config.") + ProxyConfig::from_path(Path::new(TEST_CONFIG_PATH)).expect("Could not parse test config.") } #[test] fn test_read_config_from_file() { - assert!(ProxyConfig::from_path(&Path::new(TEST_CONFIG_PATH)).is_ok()); + assert!(ProxyConfig::from_path(Path::new(TEST_CONFIG_PATH)).is_ok()); } #[test] @@ -131,7 +131,7 @@ checkHost = fs-12341234.efs.us-east-1.amazonaws.com "#; - let result_config = ProxyConfig::from_str(&config_string).unwrap(); + let result_config = ProxyConfig::from_str(config_string).unwrap(); let expected_proxy_config = ProxyConfig { fips: true, pid_file_path: String::from( @@ -184,7 +184,7 @@ checkHost = fs-12341234.efs.us-east-1.amazonaws.com "#; - let result_config = ProxyConfig::from_str(&config_string).unwrap(); + let result_config = ProxyConfig::from_str(config_string).unwrap(); let expected_proxy_config = ProxyConfig { fips: false, pid_file_path: String::from( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/src/connections.rs new/efs-utils-2.2.1/src/proxy/src/connections.rs --- old/efs-utils-2.0.3/src/proxy/src/connections.rs 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/src/connections.rs 2025-03-11 18:40:23.000000000 +0100 @@ -405,7 +405,7 @@ use uuid::Uuid; const PROXY_ID: ProxyIdentifier = ProxyIdentifier { - uuid: Uuid::from_u128(1 as u128), + uuid: Uuid::from_u128(1_u128), incarnation: 0, }; @@ -430,7 +430,7 @@ let partition_finder = TlsPartitionFinder::new(Arc::new(Mutex::new(tls_config))); let (_s, id, _) = partition_finder - .establish_connection(PROXY_ID.clone()) + .establish_connection(PROXY_ID) .await .expect("Failed to connect to server"); @@ -440,7 +440,7 @@ MultiplexTest { service, - partition_finder: partition_finder, + partition_finder, initial_partition_id, } } @@ -455,8 +455,8 @@ let (new_connnection_id, connections, _) = test .partition_finder .inner_establish_multiplex_connection( - PROXY_ID.clone(), - Some(test.initial_partition_id.clone()), + PROXY_ID, + Some(test.initial_partition_id), shutdown_handle, ) .await @@ -479,15 +479,15 @@ test.service .post_action(ServiceAction::StopPartitionAcceptor( - test.initial_partition_id.clone(), + test.initial_partition_id, )) .await; let (new_connnection_id, connections, _) = test .partition_finder .inner_establish_multiplex_connection( - PROXY_ID.clone(), - Some(test.initial_partition_id.clone()), + PROXY_ID, + Some(test.initial_partition_id), shutdown_handle, ) .await @@ -510,7 +510,7 @@ let (new_connnection_id, connections, _) = test .partition_finder - .inner_establish_multiplex_connection(PROXY_ID.clone(), None, shutdown_handle) + .inner_establish_multiplex_connection(PROXY_ID, None, shutdown_handle) .await .expect("Could not establish a multiplex connection"); @@ -531,9 +531,7 @@ let partition_finder = PlainTextPartitionFinder { mount_target_addr: format!("127.0.0.1:{}", port.clone()), }; - partition_finder - .establish_connection(PROXY_ID.clone()) - .await + partition_finder.establish_connection(PROXY_ID).await }) .await .expect("join err"); @@ -552,7 +550,7 @@ mount_target_addr: format!("127.0.0.1:{}", port.clone()), }; partition_finder - .inner_establish_multiplex_connection(PROXY_ID.clone(), None, shutdown_handle) + .inner_establish_multiplex_connection(PROXY_ID, None, shutdown_handle) .await }) .await @@ -573,7 +571,7 @@ mount_target_addr: format!("127.0.0.1:{}", port.clone()), }; partition_finder - .inner_establish_multiplex_connection(PROXY_ID.clone(), None, shutdown_handle_clone) + .inner_establish_multiplex_connection(PROXY_ID, None, shutdown_handle_clone) .await }); @@ -598,8 +596,8 @@ let error = test .partition_finder .inner_establish_multiplex_connection( - PROXY_ID.clone(), - Some(test.initial_partition_id.clone()), + PROXY_ID, + Some(test.initial_partition_id), shutdown_handle.clone(), ) .await; @@ -610,6 +608,7 @@ )); } + #[allow(clippy::enum_variant_names)] enum BrokenPartitionFinderType { _ConnectIoError, _RpcIoError, @@ -666,7 +665,7 @@ let (shutdown_handle, _waiter) = ShutdownHandle::new(CancellationToken::new()); let error = partition_finder - .inner_establish_multiplex_connection(PROXY_ID.clone(), None, shutdown_handle.clone()) + .inner_establish_multiplex_connection(PROXY_ID, None, shutdown_handle.clone()) .await; assert!(matches!(error, Err((ConnectError::MultiplexFailure, None)))); @@ -678,7 +677,7 @@ let mut sigs_hangup_listener = signal::unix::signal(signal::unix::SignalKind::hangup()).unwrap(); let config_file_path = Path::new("tests/certs/test_config.ini"); - let config_contents = std::fs::read_to_string(&config_file_path).unwrap(); + let config_contents = std::fs::read_to_string(config_file_path).unwrap(); let proxy_config = ProxyConfig::from_str(&config_contents).unwrap(); let mut tls_config = TlsConfig::new_from_config(&proxy_config).await.unwrap(); tls_config.client_cert = vec![1, 2]; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/src/controller.rs new/efs-utils-2.2.1/src/proxy/src/controller.rs --- old/efs-utils-2.0.3/src/proxy/src/controller.rs 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/src/controller.rs 2025-03-11 18:40:23.000000000 +0100 @@ -439,7 +439,7 @@ } pub async fn new_with_throughput_scale_up_threshold(threshold: i32, tls: bool) -> Self { - let mut config = super::DEFAULT_SCALE_UP_CONFIG.clone(); + let mut config = super::DEFAULT_SCALE_UP_CONFIG; config.scale_up_bytes_per_sec_threshold = threshold; TestService::new_with_partition_count_and_scale_up_config(PARTITION_COUNT, config, tls) .await @@ -450,7 +450,7 @@ threshold: i32, tls: bool, ) -> Self { - let mut config = super::DEFAULT_SCALE_UP_CONFIG.clone(); + let mut config = super::DEFAULT_SCALE_UP_CONFIG; config.scale_up_bytes_per_sec_threshold = threshold; TestService::new_with_partition_count_and_scale_up_config(count, config, tls).await } @@ -472,7 +472,7 @@ let mut counter = HashMap::new(); for id in partition_ids.iter() { - counter.insert(id.clone(), Vec::new()); + counter.insert(*id, Vec::new()); } let request_counter = Arc::new(Mutex::new(counter)); @@ -531,6 +531,7 @@ *consumable_action = Some(new_action); } + #[allow(clippy::too_many_arguments)] fn run( listener: TcpListener, scale_up_config: ScaleUpConfig, @@ -575,7 +576,7 @@ stream: S, scale_up_config: ScaleUpConfig, partition_idx: &mut usize, - partition_ids: &Vec<PartitionId>, + partition_ids: &[PartitionId], stopped_partitions: Arc<Mutex<HashSet<PartitionId>>>, request_counter: Arc<Mutex<HashMap<PartitionId, Vec<Arc<AtomicU32>>>>>, posted_action: Arc<Mutex<Option<ServiceAction>>>, @@ -599,7 +600,7 @@ for i in 0..partition_ids.len() { *partition_idx = (*partition_idx + i + 1) % partition_ids.len(); if !stopped.contains(&partition_ids[*partition_idx]) { - next_id = Some(partition_ids[*partition_idx].clone()); + next_id = Some(partition_ids[*partition_idx]); break; } } @@ -725,6 +726,7 @@ } } + #[allow(clippy::type_complexity)] fn parse_bind_client_to_partition_request( request: &Vec<u8>, ) -> Result<RpcMessage<&[u8], &[u8]>, Box<dyn std::error::Error + Send + Sync>> { @@ -765,7 +767,7 @@ .expect("No message found") .expect("failed to parse"); - let rpc = payload_result.rpcs.get(0).expect("No RPCs found"); + let rpc = payload_result.rpcs.first().expect("No RPCs found"); assert_eq!(expected_data, rpc.to_vec()[RPC_HEADER_SIZE..]); Ok(()) } @@ -810,7 +812,7 @@ proxy_id: ProxyIdentifier::new(), scale_up_attempt_count: 0, restart_count: 0, - scale_up_config: scale_up_config, + scale_up_config, status_reporter, }; @@ -827,7 +829,7 @@ proxy_id: ProxyIdentifier::new(), scale_up_attempt_count: 0, restart_count: 0, - scale_up_config: scale_up_config, + scale_up_config, status_reporter, }; @@ -1601,7 +1603,7 @@ let mut proxy = ProxyUnderTest::new(tls_enabled, service.listen_port).await; let mut port_health_check = TestClient::new(proxy.listen_port).await; // Mimic efs-utils's port test which checks whether efs-proxy is alive. - let _ = port_health_check.stream.shutdown().await.unwrap(); + port_health_check.stream.shutdown().await.unwrap(); let mut client = TestClient::new(proxy.listen_port).await; client.send_message_with_size(10).await.unwrap(); client.send_message_with_size(1024).await.unwrap(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/src/efs_rpc.rs new/efs-utils-2.2.1/src/proxy/src/efs_rpc.rs --- old/efs-utils-2.0.3/src/proxy/src/efs_rpc.rs 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/src/efs_rpc.rs 2025-03-11 18:40:23.000000000 +0100 @@ -135,8 +135,8 @@ let mut payload_buf = Vec::new(); let response = BindClientResponse { - bind_response: bind_response, - scale_up_config: scale_up_config, + bind_response, + scale_up_config, }; xdr_codec::pack(&response, &mut payload_buf)?; @@ -224,7 +224,7 @@ #[test] fn test_response_serde() -> Result<(), RpcError> { let partition_id = generate_partition_id(); - let partition_id_copy = efs_prot::PartitionId(partition_id.0.clone()); + let partition_id_copy = efs_prot::PartitionId(partition_id.0); let response = create_bind_client_to_partition_response( XID, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/src/main.rs new/efs-utils-2.2.1/src/proxy/src/main.rs --- old/efs-utils-2.0.3/src/proxy/src/main.rs 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/src/main.rs 2025-03-11 18:40:23.000000000 +0100 @@ -49,7 +49,7 @@ info!("Running with configuration: {:?}", proxy_config); let pid_file_path = Path::new(&proxy_config.pid_file_path); - let _ = write_pid_file(&pid_file_path).await; + let _ = write_pid_file(pid_file_path).await; // This "status reporter" is currently only used in tests let (_status_requester, status_reporter) = status_reporter::create_status_channel(); @@ -111,7 +111,7 @@ .open(pid_file_path) .await?; pid_file - .write_all(&std::process::id().to_string().as_bytes()) + .write_all(std::process::id().to_string().as_bytes()) .await?; pid_file.write_u8(b'\x0A').await?; pid_file.flush().await?; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/src/proxy_identifier.rs new/efs-utils-2.2.1/src/proxy/src/proxy_identifier.rs --- old/efs-utils-2.0.3/src/proxy/src/proxy_identifier.rs 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/src/proxy_identifier.rs 2025-03-11 18:40:23.000000000 +0100 @@ -33,7 +33,7 @@ #[test] fn test_increment() { let mut proxy_id = ProxyIdentifier::new(); - let proxy_id_original = proxy_id.clone(); + let proxy_id_original = proxy_id; for i in 0..5 { assert_eq!(i, proxy_id.incarnation); proxy_id.increment(); @@ -45,7 +45,7 @@ #[test] fn test_wrap_around() { let mut proxy_id = ProxyIdentifier::new(); - let proxy_id_original = proxy_id.clone(); + let proxy_id_original = proxy_id; proxy_id.incarnation = i64::MAX; proxy_id.increment(); assert_eq!(proxy_id_original.uuid, proxy_id.uuid); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/src/status_reporter.rs new/efs-utils-2.2.1/src/proxy/src/status_reporter.rs --- old/efs-utils-2.0.3/src/proxy/src/status_reporter.rs 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/src/status_reporter.rs 2025-03-11 18:40:23.000000000 +0100 @@ -5,6 +5,7 @@ use tokio::sync::mpsc::{self, Receiver, Sender}; use tokio::time::Instant; +#[allow(dead_code)] pub struct Report { pub proxy_id: ProxyIdentifier, pub partition_id: Option<PartitionId>, @@ -88,7 +89,7 @@ .await .expect("Request channel closed"); let report = Report { - proxy_id: proxy_id.clone(), + proxy_id, partition_id: None, connection_state: ConnectionSearchState::Idle, num_connections: 1, @@ -101,9 +102,9 @@ let r = status_requester._request_status().await?; assert_eq!(proxy_id, r.proxy_id); - assert!(matches!(r.partition_id, None)); + assert!(r.partition_id.is_none()); assert_eq!(r.connection_state, ConnectionSearchState::Idle); - assert!(matches!(r.last_proxy_update, None)); + assert!(r.last_proxy_update.is_none()); assert_eq!(1, r.num_connections); Ok(()) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/proxy/src/tls.rs new/efs-utils-2.2.1/src/proxy/src/tls.rs --- old/efs-utils-2.0.3/src/proxy/src/tls.rs 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/proxy/src/tls.rs 2025-03-11 18:40:23.000000000 +0100 @@ -121,9 +121,9 @@ } TlsConfig::new( config.fips, - &ca_file, - &ca_cert_pem, - &private_key_pem, + ca_file, + ca_cert_pem, + private_key_pem, efs_config.mount_target_addr.as_str(), efs_config.expected_server_hostname_tls.as_str(), ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/src/watchdog/__init__.py new/efs-utils-2.2.1/src/watchdog/__init__.py --- old/efs-utils-2.0.3/src/watchdog/__init__.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/src/watchdog/__init__.py 2025-03-11 18:40:23.000000000 +0100 @@ -56,7 +56,7 @@ AMAZON_LINUX_2_RELEASE_ID, AMAZON_LINUX_2_PRETTY_NAME, ] -VERSION = "2.0.3" +VERSION = "2.2.1" SERVICE = "elasticfilesystem" CONFIG_FILE = "/etc/amazon/efs/efs-utils.conf" @@ -146,7 +146,7 @@ AP_ID_RE = re.compile("^fsap-[0-9a-f]{17}$") ECS_TASK_METADATA_API = "http://169.254.170.2" -STS_ENDPOINT_URL_FORMAT = "https://sts.{}.amazonaws.com/" +STS_ENDPOINT_URL_FORMAT = "https://sts.{}.{}/" INSTANCE_IAM_URL = "http://169.254.169.254/latest/meta-data/iam/security-credentials/" INSTANCE_METADATA_TOKEN_URL = "http://169.254.169.254/latest/api/token" SECURITY_CREDS_ECS_URI_HELP_URL = ( @@ -384,9 +384,9 @@ logging.error("Error reading token file %s: %s", token_file, e) return None - STS_ENDPOINT_URL = STS_ENDPOINT_URL_FORMAT.format(region) + sts_endpoint_url = get_sts_endpoint_url(config, region) webidentity_url = ( - STS_ENDPOINT_URL + sts_endpoint_url + "?" + urlencode( { @@ -400,11 +400,11 @@ ) unsuccessful_resp = ( - "Unsuccessful retrieval of AWS security credentials at %s." % STS_ENDPOINT_URL + "Unsuccessful retrieval of AWS security credentials at %s." % sts_endpoint_url ) url_error_msg = ( "Unable to reach %s to retrieve AWS security credentials. See %s for more info." - % (STS_ENDPOINT_URL, SECURITY_CREDS_WEBIDENTITY_HELP_URL) + % (sts_endpoint_url, SECURITY_CREDS_WEBIDENTITY_HELP_URL) ) resp = url_request_helper( config, @@ -430,6 +430,39 @@ return None +def get_sts_endpoint_url(config, region): + dns_name_suffix = get_dns_name_suffix(config, region) + return STS_ENDPOINT_URL_FORMAT.format(region, dns_name_suffix) + + +def get_dns_name_suffix(config, region): + return get_mount_config(config, region, "dns_name_suffix") + + +def get_mount_config(config, region, config_name): + try: + config_section = get_mount_config_section(config, region) + return config.get(config_section, config_name) + except NoOptionError: + pass + + try: + return config.get(MOUNT_CONFIG_SECTION, config_name) + except NoOptionError: + fatal_error( + "Error retrieving config. Please set the {} configuration in efs-utils.conf".format(config_name) + ) + + +def get_mount_config_section(config, region): + region_specific_config_section = "%s.%s" % (MOUNT_CONFIG_SECTION, region) + if config.has_section(region_specific_config_section): + config_section = region_specific_config_section + else: + config_section = MOUNT_CONFIG_SECTION + return config_section + + def get_aws_security_credentials_from_instance_metadata(config): # through IAM role name security credentials lookup uri (after lookup for IAM role name attached to instance) dict_keys = ["AccessKeyId", "SecretAccessKey", "Token"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/test/global_test/test_global_version_match.py new/efs-utils-2.2.1/test/global_test/test_global_version_match.py --- old/efs-utils-2.0.3/test/global_test/test_global_version_match.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/test/global_test/test_global_version_match.py 2025-03-11 18:40:23.000000000 +0100 @@ -20,7 +20,6 @@ "build-deb.sh", "src/watchdog/__init__.py", "src/mount_efs/__init__.py", - "dist/amazon-efs-utils.control", "build-deb.sh", "amazon-efs-utils.spec", ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/test/mount_efs_test/test_get_aws_security_credentials.py new/efs-utils-2.2.1/test/mount_efs_test/test_get_aws_security_credentials.py --- old/efs-utils-2.0.3/test/mount_efs_test/test_get_aws_security_credentials.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/test/mount_efs_test/test_get_aws_security_credentials.py 2025-03-11 18:40:23.000000000 +0100 @@ -211,7 +211,14 @@ Exception("Unknown Error"), ]: _test_get_aws_security_credentials_get_instance_metadata_role_name( - mocker, is_name_str=True, token_effects=[token_effect] + mocker, + is_name_str=True, + token_effects=[ + token_effect + for _ in range( + 0, mount_efs.DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT + ) + ], ) @@ -230,7 +237,14 @@ Exception("Unknown Error"), ]: _test_get_aws_security_credentials_get_instance_metadata_role_name( - mocker, is_name_str=False, token_effects=[token_effect] + mocker, + is_name_str=False, + token_effects=[ + token_effect + for _ in range( + 0, mount_efs.DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT + ) + ], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/test/mount_efs_test/test_get_dns_name_and_fallback_mount_target_ip_address.py new/efs-utils-2.2.1/test/mount_efs_test/test_get_dns_name_and_fallback_mount_target_ip_address.py --- old/efs-utils-2.0.3/test/mount_efs_test/test_get_dns_name_and_fallback_mount_target_ip_address.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/test/mount_efs_test/test_get_dns_name_and_fallback_mount_target_ip_address.py 2025-03-11 18:40:23.000000000 +0100 @@ -28,9 +28,14 @@ "cn-north-1": "amazonaws.com.cn", "cn-northwest-1": "amazonaws.com.cn", "us-iso-east-1": "c2s.ic.gov", + "us-iso-west-1": "c2s.ic.gov", "us-isob-east-1": "sc2s.sgov.gov", + "us-isob-west-1": "sc2s.sgov.gov", + "us-isof-south-1": "csp.hci.ic.gov", + "us-isof-east-1": "csp.hci.ic.gov", + "eu-isoe-west-1": "cloud.adc-e.uk", } -SPECIAL_REGIONS = ["cn-north-1", "cn-northwest-1", "us-iso-east-1", "us-isob-east-1"] +SPECIAL_REGIONS = SPECIAL_REGION_DNS_DICT.keys() DEFAULT_NFS_OPTIONS = {} OPTIONS_WITH_AZ = {"az": DEFAULT_AZ} OPTIONS_WITH_IP = {"mounttargetip": IP_ADDRESS} @@ -158,7 +163,8 @@ config, FS_ID, DEFAULT_NFS_OPTIONS ) - utils.assert_not_called(get_target_region_mock) + # get_target_region will be called 1 time to get dns_name_suffix + utils.assert_called_n_times(get_target_region_mock, 1) assert "%s.efs.%s.amazonaws.com" % (FS_ID, DEFAULT_REGION) == dns_name assert None == ip_address @@ -271,14 +277,15 @@ config, FS_ID, DEFAULT_NFS_OPTIONS ) - utils.assert_not_called(get_target_region_mock) - assert ( "%s.efs.%s.%s" % (FS_ID, special_region, special_dns_name_suffix) == dns_name ) assert None == ip_address + # get_target_region will be called 1 time for each region to get dns_name_suffix + utils.assert_called_n_times(get_target_region_mock, len(SPECIAL_REGIONS)) + def test_dns_name_can_be_resolved_dns_resolve_failure(mocker): dns_mock = mocker.patch("socket.gethostbyname", side_effect=socket.gaierror) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/test/mount_efs_test/test_get_fallback_mount_target_ip_address.py new/efs-utils-2.2.1/test/mount_efs_test/test_get_fallback_mount_target_ip_address.py --- old/efs-utils-2.0.3/test/mount_efs_test/test_get_fallback_mount_target_ip_address.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/test/mount_efs_test/test_get_fallback_mount_target_ip_address.py 2025-03-11 18:40:23.000000000 +0100 @@ -28,9 +28,14 @@ "cn-north-1": "amazonaws.com.cn", "cn-northwest-1": "amazonaws.com.cn", "us-iso-east-1": "c2s.ic.gov", + "us-iso-west-1": "c2s.ic.gov", "us-isob-east-1": "sc2s.sgov.gov", + "us-isob-west-1": "sc2s.sgov.gov", + "us-isof-south-1": "csp.hci.ic.gov", + "us-isof-east-1": "csp.hci.ic.gov", + "eu-isoe-west-1": "cloud.adc-e.uk", } -SPECIAL_REGIONS = ["cn-north-1", "cn-northwest-1", "us-iso-east-1", "us-isob-east-1"] +SPECIAL_REGIONS = SPECIAL_REGION_DNS_DICT.keys() DEFAULT_NFS_OPTIONS = {} OPTIONS_WITH_AZ = {"az": DEFAULT_AZ} OPTIONS_WITH_CROSSACCOUNT = {"crossaccount": None} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/test/mount_efs_test/test_get_instance_az_id.py new/efs-utils-2.2.1/test/mount_efs_test/test_get_instance_az_id.py --- old/efs-utils-2.0.3/test/mount_efs_test/test_get_instance_az_id.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/test/mount_efs_test/test_get_instance_az_id.py 2025-03-11 18:40:23.000000000 +0100 @@ -165,9 +165,12 @@ # Reproduce https://github.com/aws/efs-utils/issues/46 def test_get_instance_az_id_token_fetch_time_out(mocker): # get_aws_ec2_metadata_token timeout, fallback to call without session token - mocker.patch( - "mount_efs.urlopen", side_effect=[socket.timeout, MockUrlLibResponse()] - ) + side_effect = [ + socket.timeout + for _ in range(0, mount_efs.DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT) + ] + side_effect.append(MockUrlLibResponse()) + mocker.patch("mount_efs.urlopen", side_effect=side_effect) assert INSTANCE_AZ_ID == test_get_instance_az_id_helper() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/test/mount_efs_test/test_get_instance_id.py new/efs-utils-2.2.1/test/mount_efs_test/test_get_instance_id.py --- old/efs-utils-2.0.3/test/mount_efs_test/test_get_instance_id.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/test/mount_efs_test/test_get_instance_id.py 2025-03-11 18:40:23.000000000 +0100 @@ -99,9 +99,12 @@ # Reproduce https://github.com/aws/efs-utils/issues/46 def test_get_instance_id_token_fetch_time_out(mocker): # get_aws_ec2_metadata_token timeout, fallback to call without session token - mocker.patch( - "mount_efs.urlopen", side_effect=[socket.timeout, MockUrlLibResponse()] - ) + side_effect = [ + socket.timeout + for _ in range(0, mount_efs.DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT) + ] + side_effect.append(MockUrlLibResponse()) + mocker.patch("mount_efs.urlopen", side_effect=side_effect) assert INSTANCE_ID == test_get_instance_id_helper() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/test/mount_efs_test/test_get_target_instance_identity.py new/efs-utils-2.2.1/test/mount_efs_test/test_get_target_instance_identity.py --- old/efs-utils-2.0.3/test/mount_efs_test/test_get_target_instance_identity.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/test/mount_efs_test/test_get_target_instance_identity.py 2025-03-11 18:40:23.000000000 +0100 @@ -84,9 +84,9 @@ return config -def get_target_region_helper(): +def get_target_region_helper(options={}): config = get_config(DEFAULT_DNS_NAME_FORMAT) - return mount_efs.get_target_region(config) + return mount_efs.get_target_region(config, options) def get_target_az_helper(options={}): @@ -113,28 +113,32 @@ # Reproduce https://github.com/aws/efs-utils/issues/46 def test_get_target_region_token_endpoint_fetching_timeout(mocker): # get_aws_ec2_metadata_token timeout, fallback to call without session token - mocker.patch( - "mount_efs.urlopen", side_effect=[socket.timeout, MockUrlLibResponse()] - ) + side_effect = [ + socket.timeout + for _ in range(0, mount_efs.DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT) + ] + side_effect.append(MockUrlLibResponse()) + mocker.patch("mount_efs.urlopen", side_effect=side_effect) assert "us-east-1" == get_target_region_helper() def test_get_target_region_token_fetch_httperror(mocker): - mocker.patch( - "mount_efs.urlopen", - side_effect=[ - HTTPError("url", 405, "Now Allowed", None, None), - MockUrlLibResponse(), - ], - ) + side_effect = [ + HTTPError("url", 405, "Now Allowed", None, None) + for _ in range(0, mount_efs.DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT) + ] + side_effect.append(MockUrlLibResponse()) + mocker.patch("mount_efs.urlopen", side_effect=side_effect) assert "us-east-1" == get_target_region_helper() def test_get_target_region_token_fetch_unknownerror(mocker): - mocker.patch( - "mount_efs.urlopen", - side_effect=[Exception("Unknown Exception"), MockUrlLibResponse()], - ) + side_effect = [ + Exception("Unknown Exception") + for _ in range(0, mount_efs.DEFAULT_GET_AWS_EC2_METADATA_TOKEN_RETRY_COUNT) + ] + side_effect.append(MockUrlLibResponse()) + mocker.patch("mount_efs.urlopen", side_effect=side_effect) assert "us-east-1" == get_target_region_helper() @@ -162,7 +166,7 @@ mocker.patch("mount_efs.get_aws_ec2_metadata_token", return_value=None) mocker.patch("mount_efs.urlopen", return_value=MockUrlLibResponse()) config = get_config("{fs_id}.efs.{region}.{dns_name_suffix}", None) - assert TARGET_REGION == mount_efs.get_target_region(config) + assert TARGET_REGION == mount_efs.get_target_region(config, {}) def test_get_target_region_config_metadata_unavailable(mocker, capsys): @@ -170,7 +174,7 @@ mocker.patch("mount_efs.urlopen", side_effect=URLError("test error")) config = get_config("{fs_id}.efs.{region}.{dns_name_suffix}") with pytest.raises(SystemExit) as ex: - mount_efs.get_target_region(config) + mount_efs.get_target_region(config, {}) assert 0 != ex.value.code out, err = capsys.readouterr() @@ -228,13 +232,13 @@ def test_get_target_region_from_config_variable(mocker): config = get_config("{az}.{fs_id}.efs.us-east-2.{dns_name_suffix}", TARGET_REGION) - assert TARGET_REGION == mount_efs.get_target_region(config) + assert TARGET_REGION == mount_efs.get_target_region(config, {}) def _test_get_target_region_from_dns_format(mocker, config): mocker.patch("mount_efs.get_aws_ec2_metadata_token", return_value=None) mocker.patch("mount_efs.urlopen", side_effect=URLError("test error")) - assert TARGET_REGION == mount_efs.get_target_region(config) + assert TARGET_REGION == mount_efs.get_target_region(config, {}) def test_get_target_region_from_legacy_dns_name_format(mocker): @@ -273,3 +277,7 @@ def test_get_target_az_from_options(mocker): assert TARGET_AZ == get_target_az_helper(options={"az": TARGET_AZ}) + + +def test_get_target_region_from_options(mocker): + assert TARGET_REGION == get_target_region_helper(options={"region": TARGET_REGION}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-2.0.3/test/watchdog_test/test_start_tls_tunnel.py new/efs-utils-2.2.1/test/watchdog_test/test_start_tls_tunnel.py --- old/efs-utils-2.0.3/test/watchdog_test/test_start_tls_tunnel.py 2024-06-19 16:40:07.000000000 +0200 +++ new/efs-utils-2.2.1/test/watchdog_test/test_start_tls_tunnel.py 2025-03-11 18:40:23.000000000 +0100 @@ -36,12 +36,14 @@ state = { "pid": PID - 1, - "cmd": cmd - if cmd - else [ - tunnel_executable, - "/var/run/efs/stunnel-config.fs-deadbeef.mnt.21007", - ], + "cmd": ( + cmd + if cmd + else [ + tunnel_executable, + "/var/run/efs/stunnel-config.fs-deadbeef.mnt.21007", + ] + ), } state_file = tempfile.mkstemp(prefix="state", dir=str(tmpdir))[1] with open(state_file, "w") as f: