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 2022-05-02 16:25:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-efs-utils (Old) and /work/SRC/openSUSE:Factory/.aws-efs-utils.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "aws-efs-utils" Mon May 2 16:25:39 2022 rev:8 rq:974305 version:1.32.1 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-efs-utils/aws-efs-utils.changes 2022-03-11 11:40:23.642548932 +0100 +++ /work/SRC/openSUSE:Factory/.aws-efs-utils.new.1538/aws-efs-utils.changes 2022-05-02 16:25:45.668850989 +0200 @@ -1,0 +2,9 @@ +Wed Apr 27 06:39:41 UTC 2022 - pgaj...@suse.com + +- version update to 1.32.1 + * Enable watchdog to check stunnel health periodically and restart + hanging stunnel process when necessary. +- do not require python-mock for build + https://trello.com/c/S6eADbii/64-remove-python-mock + +------------------------------------------------------------------- Old: ---- efs-utils-1.31.3.tar.gz New: ---- efs-utils-1.32.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-efs-utils.spec ++++++ --- /var/tmp/diff_new_pack.tfgdZv/_old 2022-05-02 16:25:46.544851963 +0200 +++ /var/tmp/diff_new_pack.tfgdZv/_new 2022-05-02 16:25:46.560851981 +0200 @@ -17,13 +17,13 @@ Name: aws-efs-utils -Version: 1.31.3 +Version: 1.32.1 Release: 0 Summary: Utilities for using the EFS file systems License: MIT Group: System/Management URL: https://github.com/aws/efs-utils -Source0: efs-utils-%{version}.tar.gz +Source0: https://github.com/aws/efs-utils/archive/refs/tags/v%{version}.tar.gz#/efs-utils-%{version}.tar.gz Patch0: disable_mount_efs_test.patch Patch1: harden_amazon-efs-mount-watchdog.service.patch Patch2: skip-styletest.patch @@ -34,7 +34,6 @@ #BuildRequires: python3-flake8 >= 3.7.9 BuildRequires: python3-flake8 BuildRequires: python3-mccabe >= 0.6.1 -BuildRequires: python3-mock >= 2.0.0 BuildRequires: python3-pbr >= 3.1.1 BuildRequires: python3-pluggy >= 0.13.0 BuildRequires: python3-py >= 1.10.0 @@ -66,6 +65,8 @@ # No build required %check +# https://github.com/aws/efs-utils/issues/131 +sed -i 's:from mock:from unittest.mock:' test/*/test_*.py make test %install ++++++ efs-utils-1.31.3.tar.gz -> efs-utils-1.32.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/.circleci/config.yml new/efs-utils-1.32.1/.circleci/config.yml --- old/efs-utils-1.31.3/.circleci/config.yml 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/.circleci/config.yml 2022-04-01 22:34:12.000000000 +0200 @@ -49,7 +49,7 @@ - run: name: Install package command: | - apt-get -y install ./build/amazon-efs-utils*deb + DEBIAN_FRONTEND=noninteractive apt-get -y install ./build/amazon-efs-utils*deb - run: name: Check installed successfully command: | @@ -105,6 +105,13 @@ name: Check changelog command: | rpm -q --changelog amazon-efs-utils + build-centos-repo: + steps: + - run: + name: change the mirrors to vault.centos.org + command: | + sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* + sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* jobs: test: parameters: @@ -142,6 +149,16 @@ image: << parameters.image >> steps: - build-suse-rpm + build-centos-rpm-package: + parameters: + image: + type: string + executor: + name: linux + image: << parameters.image >> + steps: + - build-centos-repo + - build-rpm workflows: workflow: jobs: @@ -164,12 +181,24 @@ name: python34 image: circleci/python:3.4.9 - build-deb-package: + name: ubuntu-latest + image: ubuntu:latest + - build-deb-package: name: ubuntu16 image: ubuntu:16.04 - build-deb-package: name: ubuntu18 image: ubuntu:18.04 - build-deb-package: + name: ubuntu20 + image: ubuntu:20.04 + - build-deb-package: + name: ubuntu21 + image: ubuntu:21.04 + - build-deb-package: + name: ubuntu22 + image: ubuntu:22.04 + - build-deb-package: name: debian9 image: debian:stretch - build-deb-package: @@ -178,19 +207,31 @@ - build-deb-package: name: debian11 image: debian:bullseye + - build-centos-rpm-package: + name: centos-latest + image: centos:latest - build-rpm-package: name: centos7 image: centos:centos7 - - build-rpm-package: + - build-centos-rpm-package: name: centos8 image: centos:centos8 - build-rpm-package: + name: rocky8 + image: rockylinux/rockylinux:8 + - build-rpm-package: + name: amazon-linux-latest + image: amazonlinux:latest + - build-rpm-package: name: amazon-linux-2 image: amazonlinux:2 - build-rpm-package: name: amazon-linux image: amazonlinux:1 - build-rpm-package: + name: fedora-latest + image: fedora:latest + - build-rpm-package: name: fedora28 image: fedora:28 - build-rpm-package: @@ -208,6 +249,15 @@ - build-rpm-package: name: fedora33 image: fedora:33 + - build-rpm-package: + name: fedora34 + image: fedora:34 + - build-rpm-package: + name: fedora35 + image: fedora:35 + - build-rpm-package: + name: fedora36 + image: fedora:36 - build-suse-rpm-package: name: opensuse-leap15.1 image: opensuse/leap:15.1 @@ -215,5 +265,11 @@ name: opensuse-leap15.2 image: opensuse/leap:15.2 - build-suse-rpm-package: + name: opensuse-leap15.3 + image: opensuse/leap:15.3 + - build-suse-rpm-package: + name: opensuse-leap15.4 + image: opensuse/leap:15.4 + - build-suse-rpm-package: name: 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-1.31.3/README.md new/efs-utils-1.32.1/README.md --- old/efs-utils-1.31.3/README.md 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/README.md 2022-04-01 22:34:12.000000000 +0200 @@ -31,9 +31,10 @@ The `efs-utils` package has been verified against the following MacOS distributions: -| Distribution | `init` System | -| ------------ | ------------- | -| MacOS Big Sur | `launchd` | +| Distribution | `init` System | +| -------------- | ------------- | +| MacOS Big Sur | `launchd` | +| MacOS Monterey | `launchd` | ## Prerequisites @@ -116,14 +117,16 @@ $ sudo apt-get -y install ./build/amazon-efs-utils*deb ``` -### On MacOS Big Sur distribution +### On MacOS Big Sur and macOS Monterey distribution -For EC2 Mac instances running macOS Big Sur, you can install amazon-efs-utils from the [homebrew-aws](https://github.com/aws/homebrew-aws) respository. +For EC2 Mac instances running macOS Big Sur and macOS Monterey, 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 and macOS Monterey, not local Mac computers.** ``` brew install amazon-efs-utils ``` -This will install amazon-efs-utils on your EC2 Mac Instance running macOS Big Sur in the directory `/usr/local/Cellar/amazon-efs-utils`. At the end of the installation, it will print a set of commands that must be executed in order to start using efs-utils. The instructions that are printed after amazon-efs-utils and must be executed are: +This will install amazon-efs-utils on your EC2 Mac Instance running macOS Big Sur and macOS Monterey in the directory `/usr/local/Cellar/amazon-efs-utils`. At the end of the installation, it will print a set of commands that must be executed in order to start using efs-utils. The instructions that are printed after amazon-efs-utils and must be executed are: ``` Perform below actions to start using efs: @@ -278,7 +281,9 @@ sudo yum -y install wget ``` ```bash -if [[ "$(python3 -V 2>&1)" =~ ^(Python 3.5.*) ]]; then +if [[ "$(python3 -V 2>&1)" =~ ^(Python 3.6.*) ]]; then + sudo wget https://bootstrap.pypa.io/3.6/get-pip.py -O /tmp/get-pip.py +elif [[ "$(python3 -V 2>&1)" =~ ^(Python 3.5.*) ]]; then sudo wget https://bootstrap.pypa.io/3.5/get-pip.py -O /tmp/get-pip.py elif [[ "$(python3 -V 2>&1)" =~ ^(Python 3.4.*) ]]; then sudo wget https://bootstrap.pypa.io/3.4/get-pip.py -O /tmp/get-pip.py @@ -292,7 +297,9 @@ sudo apt-get -y install wget ``` ```bash -if echo $(python3 -V 2>&1) | grep -e "Python 3.5"; then +if echo $(python3 -V 2>&1) | grep -e "Python 3.6"; then + sudo wget https://bootstrap.pypa.io/3.6/get-pip.py -O /tmp/get-pip.py +elif echo $(python3 -V 2>&1) | grep -e "Python 3.5"; then sudo wget https://bootstrap.pypa.io/3.5/get-pip.py -O /tmp/get-pip.py elif echo $(python3 -V 2>&1) | grep -e "Python 3.4"; then sudo wget https://bootstrap.pypa.io/3.4/get-pip.py -O /tmp/get-pip.py @@ -346,7 +353,7 @@ - For MacOS: ```bash -sudo sed -i -e '/\[cloudwatch-log\]/{N;s/# enabled = true/enabled = true/;}' /usr/local/Cellar/amazon-efs-utils/<version>/etc/amazon/efs/efs-utils.conf +sudo sed -i -e '/\[cloudwatch-log\]/{N;s/# enabled = true/enabled = true/;}' /usr/local/Cellar/amazon-efs-utils/<version>/libexec/etc/amazon/efs/efs-utils.conf ``` You can also configure CloudWatch log group name and log retention days in the config file. If you want to have separate log groups in Cloudwatch for every mounted file system, add `/{fs_id}` to the end of the `log_group_name` field in `efs-utils.conf` file. For example, the `log_group_name` in `efs-utils.conf` file would look something like: @@ -427,7 +434,7 @@ `awsprofile` option. These options require the `tls` option. ``` -$ sudo mount -t efs -o tls,iam,aws-profile=test-profile file-system-id efs-mount-point/ +$ sudo mount -t efs -o tls,iam,awsprofile=test-profile file-system-id efs-mount-point/ ``` To configure the named profile, see the [Named Profiles doc](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/amazon-efs-utils.spec new/efs-utils-1.32.1/amazon-efs-utils.spec --- old/efs-utils-1.31.3/amazon-efs-utils.spec 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/amazon-efs-utils.spec 2022-04-01 22:34:12.000000000 +0200 @@ -18,20 +18,24 @@ %global with_systemd 1 %endif -%if 0%{?is_opensuse} -%global platform .opensuse +%if 0%{?dist:1} +%global platform %{dist} %else - -%if 0%{?sle_version} +%if 0%{?suse_version} %global platform .suse %else -%global platform %{dist} +%global platform .unknown +%endif %endif +%if 0%{?amzn} > 2 +%global efs_bindir %{_sbindir} +%else +%global efs_bindir /sbin %endif Name : amazon-efs-utils -Version : 1.31.3 +Version : 1.32.1 Release : 1%{platform} Summary : This package provides utilities for simplifying the use of EFS file systems @@ -78,14 +82,14 @@ install -p -m 644 %{_builddir}/%{name}/dist/amazon-efs-mount-watchdog.conf %{buildroot}%{_sysconfdir}/init %endif -mkdir -p %{buildroot}/sbin +mkdir -p %{buildroot}%{efs_bindir} mkdir -p %{buildroot}%{_bindir} mkdir -p %{buildroot}%{_localstatedir}/log/amazon/efs mkdir -p %{buildroot}%{_mandir}/man8 install -p -m 644 %{_builddir}/%{name}/dist/efs-utils.conf %{buildroot}%{_sysconfdir}/amazon/efs install -p -m 444 %{_builddir}/%{name}/dist/efs-utils.crt %{buildroot}%{_sysconfdir}/amazon/efs -install -p -m 755 %{_builddir}/%{name}/src/mount_efs/__init__.py %{buildroot}/sbin/mount.efs +install -p -m 755 %{_builddir}/%{name}/src/mount_efs/__init__.py %{buildroot}%{efs_bindir}/mount.efs install -p -m 755 %{_builddir}/%{name}/src/watchdog/__init__.py %{buildroot}%{_bindir}/amazon-efs-mount-watchdog install -p -m 644 %{_builddir}/%{name}/man/mount.efs.8 %{buildroot}%{_mandir}/man8 @@ -97,7 +101,7 @@ %config(noreplace) %{_sysconfdir}/init/amazon-efs-mount-watchdog.conf %endif %{_sysconfdir}/amazon/efs/efs-utils.crt -/sbin/mount.efs +%{efs_bindir}/mount.efs %{_bindir}/amazon-efs-mount-watchdog /var/log/amazon %{_mandir}/man8/mount.efs.8.gz @@ -131,7 +135,12 @@ %clean %changelog -* Thu Nov 23 2021 Jigar Dedhia <dedhi...@amazon.com> - 1.31.3 +* Thu Mar 31 2022 Shivam Gupta <lshig...@amazon.com> - 1.32.1 +- Enable watchdog to check stunnel health periodically and restart hanging stunnel process when necessary. +- Fix potential race condition issue when removing lock files. +- Add efs-utils Support for MacOS Monterey EC2 instances. + +* Tue Nov 23 2021 Jigar Dedhia <dedhi...@amazon.com> - 1.31.3 - Add unmount_time and unmount_count to handle inconsistent mount reads - Allow specifying fs_id in cloudwatch log group name diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/build-deb.sh new/efs-utils-1.32.1/build-deb.sh --- old/efs-utils-1.31.3/build-deb.sh 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/build-deb.sh 2022-04-01 22:34:12.000000000 +0200 @@ -11,7 +11,7 @@ BASE_DIR=$(pwd) BUILD_ROOT=${BASE_DIR}/build/debbuild -VERSION=1.31.3 +VERSION=1.32.1 RELEASE=1 DEB_SYSTEM_RELEASE_PATH=/etc/os-release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/config.ini new/efs-utils-1.32.1/config.ini --- old/efs-utils-1.31.3/config.ini 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/config.ini 2022-04-01 22:34:12.000000000 +0200 @@ -7,5 +7,5 @@ # [global] -version=1.31.3 +version=1.32.1 release=1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/dist/amazon-efs-utils.control new/efs-utils-1.32.1/dist/amazon-efs-utils.control --- old/efs-utils-1.31.3/dist/amazon-efs-utils.control 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/dist/amazon-efs-utils.control 2022-04-01 22:34:12.000000000 +0200 @@ -1,6 +1,6 @@ Package: amazon-efs-utils Architecture: all -Version: 1.31.3 +Version: 1.32.1 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-1.31.3/dist/efs-utils.conf new/efs-utils-1.32.1/dist/efs-utils.conf --- old/efs-utils-1.31.3/dist/efs-utils.conf 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/dist/efs-utils.conf 2022-04-01 22:34:12.000000000 +0200 @@ -55,10 +55,6 @@ dns_name_suffix = c2s.ic.gov stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem -[mount.us-isob-east-1] -dns_name_suffix = sc2s.sgov.gov -stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem - [mount-watchdog] enabled = true poll_interval_sec = 1 @@ -68,6 +64,11 @@ # Set client auth/access point certificate renewal rate. Minimum value is 1 minute. tls_cert_renewal_interval_min = 60 +# Periodically check the health of stunnel to make sure the connection is fully established +stunnel_health_check_enabled = true +stunnel_health_check_interval_min = 5 +stunnel_health_check_command_timeout_sec = 30 + [cloudwatch-log] # enabled = true log_group_name = /aws/efs/utils diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/src/mount_efs/__init__.py new/efs-utils-1.32.1/src/mount_efs/__init__.py --- old/efs-utils-1.31.3/src/mount_efs/__init__.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/src/mount_efs/__init__.py 2022-04-01 22:34:12.000000000 +0200 @@ -84,7 +84,7 @@ BOTOCORE_PRESENT = False -VERSION = "1.31.3" +VERSION = "1.32.1" SERVICE = "elasticfilesystem" CLONE_NEWNET = 0x40000000 @@ -258,23 +258,8 @@ WATCHDOG_SERVICE_PLIST_PATH = "/Library/LaunchAgents/amazon-efs-mount-watchdog.plist" SYSTEM_RELEASE_PATH = "/etc/system-release" OS_RELEASE_PATH = "/etc/os-release" -RHEL8_RELEASE_NAME = "Red Hat Enterprise Linux release 8" -CENTOS8_RELEASE_NAME = "CentOS Linux release 8" -ORACLE_RELEASE_NAME = "Oracle Linux Server release 8" -FEDORA_RELEASE_NAME = "Fedora release" -OPEN_SUSE_LEAP_RELEASE_NAME = "openSUSE Leap" -SUSE_RELEASE_NAME = "SUSE Linux Enterprise Server" MACOS_BIG_SUR_RELEASE = "macOS-11" - -SKIP_NO_LIBWRAP_RELEASES = [ - RHEL8_RELEASE_NAME, - CENTOS8_RELEASE_NAME, - FEDORA_RELEASE_NAME, - OPEN_SUSE_LEAP_RELEASE_NAME, - SUSE_RELEASE_NAME, - MACOS_BIG_SUR_RELEASE, - ORACLE_RELEASE_NAME, -] +MACOS_MONTEREY_RELEASE = "macOS-12" # Multiplier for max read ahead buffer size # Set default as 15 aligning with prior linux kernel 5.4 @@ -283,11 +268,11 @@ NFS_READAHEAD_OPTIMIZE_LINUX_KERNEL_MIN_VERSION = [5, 4] # MacOS does not support the property of Socket SO_BINDTODEVICE in stunnel configuration -SKIP_NO_SO_BINDTODEVICE_RELEASES = [MACOS_BIG_SUR_RELEASE] +SKIP_NO_SO_BINDTODEVICE_RELEASES = [MACOS_BIG_SUR_RELEASE, MACOS_MONTEREY_RELEASE] MAC_OS_PLATFORM_LIST = ["darwin"] -# MacOS Versions : Big Sur - 20.*, Catalina - 19.*, Mojave - 18.*. Catalina and Mojave are not supported for now -MAC_OS_SUPPORTED_VERSION_LIST = ["20"] +# MacOS Versions : Monterey - 21.*, Big Sur - 20.*, Catalina - 19.*, Mojave - 18.*. Catalina and Mojave are not supported for now +MAC_OS_SUPPORTED_VERSION_LIST = ["20", "21"] def errcheck(ret, func, args): @@ -1073,7 +1058,7 @@ return supported -def get_version_specific_stunnel_options(): +def get_stunnel_options(): stunnel_command = [_stunnel_bin(), "-help"] proc = subprocess.Popen( stunnel_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True @@ -1081,12 +1066,7 @@ proc.wait() _, err = proc.communicate() - stunnel_output = err.splitlines() - - check_host_supported = is_stunnel_option_supported(stunnel_output, b"checkHost") - ocsp_aia_supported = is_stunnel_option_supported(stunnel_output, b"OCSPaia") - - return check_host_supported, ocsp_aia_supported + return err.splitlines() def _stunnel_bin(): @@ -1193,7 +1173,7 @@ efs_config["cert"] = cert_details["certificate"] efs_config["key"] = cert_details["privateKey"] - check_host_supported, ocsp_aia_supported = get_version_specific_stunnel_options() + stunnel_options = get_stunnel_options() tls_controls_message = ( "WARNING: Your client lacks sufficient controls to properly enforce TLS. Please upgrade stunnel, " @@ -1204,7 +1184,7 @@ if get_boolean_config_item_value( config, CONFIG_SECTION, "stunnel_check_cert_hostname", default_value=True ): - if check_host_supported: + if is_stunnel_option_supported(stunnel_options, b"checkHost"): # Stunnel checkHost option checks if the specified DNS host name or wildcard matches any of the provider in peer # certificate's CN fields, after introducing the AZ field in dns name, the host name in the stunnel config file # is not valid, remove the az info there @@ -1214,14 +1194,14 @@ # Only use the config setting if the override is not set if ocsp_enabled: - if ocsp_aia_supported: + if is_stunnel_option_supported(stunnel_options, b"OCSPaia"): efs_config["OCSPaia"] = "yes" else: fatal_error(tls_controls_message % "stunnel_check_cert_validity") - if not any( - release in system_release_version for release in SKIP_NO_LIBWRAP_RELEASES - ): + # If the stunnel libwrap option is supported, we disable the usage of /etc/hosts.allow and /etc/hosts.deny by + # setting the option to no + if is_stunnel_option_supported(stunnel_options, b"libwrap"): efs_config["libwrap"] = "no" stunnel_config = "\n".join( @@ -1261,6 +1241,7 @@ "cmd": command, "files": files, "mount_time": time.time(), + "mountpoint": mountpoint, } if cert_details: @@ -1562,8 +1543,8 @@ while not verify_tlsport_can_be_connected(tlsport) and retry_times > 0: logging.debug( "The tlsport %s cannot be connected yet, sleep %s(s), %s retry time(s) left", - DEFAULT_TIMEOUT, tlsport, + DEFAULT_TIMEOUT, retry_times, ) time.sleep(DEFAULT_TIMEOUT) @@ -1831,8 +1812,7 @@ os.write(f, lock_file_contents.encode("utf-8")) yield f finally: - os.close(f) - os.remove(lock_file) + check_and_remove_lock_file(lock_file, f) def do_with_lock(function): while True: @@ -1847,7 +1827,15 @@ ) time.sleep(DEFAULT_TIMEOUT) else: - raise + # errno.ENOENT: No such file or directory, errno.EBADF: Bad file descriptor + if e.errno == errno.ENOENT or e.errno == errno.EBADF: + logging.debug( + "lock file does not exist or Bad file descriptor, The file is already removed nothing to do." + ) + else: + raise Exception( + "Could not remove lock file unexpected exception: %s", e + ) def generate_key(): if os.path.isfile(key): @@ -2074,7 +2062,10 @@ os.path.join(log_dir, LOG_FILE), maxBytes=max_bytes, backupCount=file_count ) handler.setFormatter( - logging.Formatter(fmt="%(asctime)s - %(levelname)s - %(message)s") + logging.Formatter( + fmt="%(asctime)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S %Z", + ) ) logger = logging.getLogger() @@ -2221,6 +2212,24 @@ ) +def check_and_remove_lock_file(path, file): + """ + There is a possibility of having a race condition as the lock file is getting deleted in both mount_efs and watchdog, + so creating a function in order to check whether the path exist or not before removing the lock file. + """ + try: + os.close(file) + os.remove(path) + logging.debug("Removed %s successfully", path) + except OSError as e: + if not (e.errno == errno.ENOENT or e.errno == errno.EBADF): + raise Exception("Could not remove %s. Unexpected exception: %s", path, e) + else: + logging.debug( + "%s does not exist, The file is already removed nothing to do", path + ) + + def dns_name_can_be_resolved(dns_name): try: socket.gethostbyname(dns_name) @@ -3399,7 +3408,9 @@ bootstrap_logging(config) if check_if_platform_is_mac() and not check_if_mac_version_is_supported(): - fatal_error("We do not support EFS on MacOS " + platform.release()) + fatal_error( + "We do not support EFS on MacOS Kernel version " + platform.release() + ) fs_id, path, mountpoint, options = parse_arguments(config) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/src/watchdog/__init__.py new/efs-utils-1.32.1/src/watchdog/__init__.py --- old/efs-utils-1.31.3/src/watchdog/__init__.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/src/watchdog/__init__.py 2022-04-01 22:34:12.000000000 +0200 @@ -27,7 +27,7 @@ from contextlib import contextmanager from datetime import datetime, timedelta from logging.handlers import RotatingFileHandler -from signal import SIGTERM, SIGHUP +from signal import SIGHUP, SIGKILL, SIGTERM try: from configparser import ConfigParser, NoOptionError, NoSectionError @@ -49,7 +49,7 @@ from urllib import urlencode -VERSION = "1.31.3" +VERSION = "1.32.1" SERVICE = "elasticfilesystem" CONFIG_FILE = "/etc/amazon/efs/efs-utils.conf" @@ -71,6 +71,8 @@ DEFAULT_NFS_PORT = "2049" PRIVATE_KEY_FILE = "/etc/amazon/efs/privateKey.pem" DEFAULT_REFRESH_SELF_SIGNED_CERT_INTERVAL_MIN = 60 +DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN = 5 +DEFAULT_STUNNEL_HEALTH_CHECK_TIMEOUT_SEC = 30 NOT_BEFORE_MINS = 15 NOT_AFTER_HOURS = 3 DATE_ONLY_FORMAT = "%Y%m%d" @@ -585,7 +587,10 @@ os.path.join(log_dir, LOG_FILE), maxBytes=max_bytes, backupCount=file_count ) handler.setFormatter( - logging.Formatter(fmt="%(asctime)s - %(levelname)s - %(message)s") + logging.Formatter( + fmt="%(asctime)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S %Z", + ) ) logger = logging.getLogger() @@ -937,11 +942,165 @@ if is_running: logging.debug("TLS tunnel for %s is running", state_file) + # https://github.com/kubernetes-sigs/aws-efs-csi-driver/issues/616 We have seen EFS hanging issue caused + # by stuck stunnel (version: 4.56) process. Apart from checking whether stunnel is running or not, we + # need to check whether the stunnel connection established is healthy periodically. + # + # The way to check the stunnel health is by `df` the mountpoint, i.e. check the file system information, + # which will trigger a remote GETATTR on the root of the file system. Normally the command will finish + # in 10 milliseconds, thus if the command hang for certain period (defined as 30 sec as of now), the + # stunnel connection is likely to be unhealthy. Watchdog will kill the old stunnel process and restart + # a new one for the unhealthy mount. The health check will run every 5 min since mount. + # + # Both the command hang timeout and health check interval are configurable in efs-utils config file. + # + check_stunnel_health( + config, state, state_file_dir, state_file, child_procs, nfs_mounts + ) else: logging.warning("TLS tunnel for %s is not running", state_file) restart_tls_tunnel(child_procs, state, state_file_dir, state_file) +def check_stunnel_health( + config, state, state_file_dir, state_file, child_procs, nfs_mounts +): + if not get_boolean_config_item_value( + config, CONFIG_SECTION, "stunnel_health_check_enabled", default_value=True + ): + return + + check_interval_min = get_int_value_from_config_file( + config, + "stunnel_health_check_interval_min", + DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN, + ) + + current_time = time.time() + + # The mount_time info in the state file is added in version 1.31.3. It is possible for existing mounts, there are + # no mount_time in state file, which will cause watchdog to crash. If the information does not exist, we just take + # current time as the initial mount time of the mount. + # + if "mount_time" not in state: + state["mount_time"] = current_time + rewrite_state_file(state, state_file_dir, state_file) + return + + # Only start to perform the stunnel health check after the check interval passed. + if current_time - state["mount_time"] < check_interval_min * 60: + return + + last_stunnel_check_time = ( + state["last_stunnel_check_time"] if "last_stunnel_check_time" in state else 0 + ) + if ( + last_stunnel_check_time != 0 + and current_time - last_stunnel_check_time < check_interval_min * 60 + ): + return + + # We add this mountpoint info in the state file along with this change. It is possible for existing mounts, there + # are no mountpoint in state file, which will cause watchdog to crash. To handle that case, we need to extract the + # mountpoint from the state file name, and write that information to state file. + # + if "mountpoint" in state: + mountpoint = state["mountpoint"] + else: + mountpoint = get_mountpoint_from_nfs_mounts(state_file, nfs_mounts) + state["mountpoint"] = mountpoint + rewrite_state_file(state, state_file_dir, state_file) + + stunnel_pid = state["pid"] + process = subprocess.Popen( + ["df", mountpoint], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + close_fds=True, + ) + + command_timeout_sec = get_int_value_from_config_file( + config, + "stunnel_health_check_command_timeout_sec", + DEFAULT_STUNNEL_HEALTH_CHECK_TIMEOUT_SEC, + ) + try: + state["last_stunnel_check_time"] = current_time + process.communicate(timeout=command_timeout_sec) + logging.debug( + "Stunnel [PID: %d] running for tls mount on %s passed health check.", + stunnel_pid, + mountpoint, + ) + rewrite_state_file(state, state_file_dir, state_file) + except subprocess.TimeoutExpired: + if is_pid_running(stunnel_pid): + process_group = os.getpgid(stunnel_pid) + logging.warning( + "Connection timeout for %s after %d, the stunnel could be unhealthy. Sending SIGKILL to kill the " + "stunnel(PID: %d, group ID: %s) and starting a new stunnel process.", + mountpoint, + command_timeout_sec, + stunnel_pid, + process_group, + ) + os.killpg(process_group, SIGKILL) + restart_tls_tunnel(child_procs, state, state_file_dir, state_file) + else: + logging.warning( + "Stunnel health check timed out for %s, stunnel [PID: %d] is not running anymore.", + mountpoint, + stunnel_pid, + ) + # The child process is not killed if the timeout expires, so in order to cleanup properly, kill the child + # process after the timeout. + # + process.kill() + + +# Retrieve the nfs mountpoint with the port information in the mount option +def get_mountpoint_from_nfs_mounts(state_file, nfs_mounts): + search_pattern = "port={port}".format( + port=os.path.basename(state_file).split(".")[-1] + ) + for mount in nfs_mounts.values(): + if search_pattern in mount[3]: + return mount[1] + + +def get_int_value_from_config_file(config, config_name, default_config_value): + val = default_config_value + try: + value_from_config = config.get(CONFIG_SECTION, config_name) + try: + if int(value_from_config) > 0: + val = int(value_from_config) + else: + logging.warning( + '%s value in config file "%s" is lower than 1. Defaulting to %d.', + config_name, + CONFIG_FILE, + default_config_value, + ) + except ValueError: + logging.warning( + 'Bad %s, "%s", in config file "%s". Defaulting to %d.', + config_name, + value_from_config, + CONFIG_FILE, + default_config_value, + ) + except NoOptionError: + logging.warning( + 'No %s value in config file "%s". Defaulting to %d.', + config_name, + CONFIG_FILE, + default_config_value, + ) + + return val + + def check_child_procs(child_procs): for proc in child_procs: proc.poll() @@ -1177,8 +1336,7 @@ os.write(f, lock_file_contents.encode("utf-8")) yield f finally: - os.close(f) - os.remove(lock_file) + check_and_remove_lock_file(lock_file, f) def do_with_lock(function): while True: @@ -1193,7 +1351,15 @@ ) time.sleep(DEFAULT_TIMEOUT) else: - raise + # errno.ENOENT: No such file or directory, errno.EBADF: Bad file descriptor + if e.errno == errno.ENOENT or e.errno == errno.EBADF: + logging.debug( + "lock file does not exist or Bad file descriptor, The file is already removed nothing to do." + ) + else: + raise Exception( + "Could not remove lock file unexpected exception: %s", e + ) def generate_key(): if os.path.isfile(key): @@ -1630,6 +1796,24 @@ logging.debug("%s does not exist, nothing to do", path) +def check_and_remove_lock_file(path, file): + """ + There is a possibility of having a race condition as the lock file is getting deleted in both mount_efs and watchdog, + so creating a function in order to check whether the path exist or not before removing the lock file. + """ + try: + os.close(file) + os.remove(path) + logging.debug("Removed %s successfully", path) + except OSError as e: + if not (e.errno == errno.ENOENT or e.errno == errno.EBADF): + raise Exception("Could not remove %s. Unexpected exception: %s", path, e) + else: + logging.debug( + "%s does not exist, The file is already removed nothing to do", path + ) + + def clean_up_certificate_lock_file(state_file_dir=STATE_FILE_DIR): """ Cleans up private key lock file 'efs-utils-lock' left behind by a previous process attempting to create private key diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/mount_efs_test/test_add_stunnel_ca_options.py new/efs-utils-1.32.1/test/mount_efs_test/test_add_stunnel_ca_options.py --- old/efs-utils-1.31.3/test/mount_efs_test/test_add_stunnel_ca_options.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/mount_efs_test/test_add_stunnel_ca_options.py 2022-04-01 22:34:12.000000000 +0200 @@ -31,7 +31,7 @@ def _create_temp_file(tmpdir, content=""): - temp_file = tmpdir.join(tempfile.mktemp()) + temp_file = tmpdir.join(tempfile.mkstemp()[1]) temp_file.write(content, ensure=True) return temp_file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/mount_efs_test/test_bootstrap_tls.py new/efs-utils-1.32.1/test/mount_efs_test/test_bootstrap_tls.py --- old/efs-utils-1.31.3/test/mount_efs_test/test_bootstrap_tls.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/mount_efs_test/test_bootstrap_tls.py 2022-04-01 22:34:12.000000000 +0200 @@ -97,7 +97,7 @@ def test_bootstrap_tls_state_file_nonexistent_dir(mocker, tmpdir): popen_mock, _ = setup_mocks(mocker) - state_file_dir = str(tmpdir.join(tempfile.mktemp())) + state_file_dir = str(tmpdir.join(tempfile.mkdtemp()[1])) def config_get_side_effect(section, field): if section == mount_efs.CONFIG_SECTION and field == "state_file_dir_mode": diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/mount_efs_test/test_get_init_system.py new/efs-utils-1.32.1/test/mount_efs_test/test_get_init_system.py --- old/efs-utils-1.31.3/test/mount_efs_test/test_get_init_system.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/mount_efs_test/test_get_init_system.py 2022-04-01 22:34:12.000000000 +0200 @@ -12,7 +12,7 @@ def test_get_init_system_from_file(tmpdir): - temp_file = tmpdir.join(tempfile.mktemp()) + temp_file = tmpdir.join(tempfile.mkstemp()[1]) temp_file.write("systemd\n", ensure=True) init_system = mount_efs.get_init_system(str(temp_file)) @@ -21,7 +21,7 @@ def test_get_init_system_nonexistent_file(tmpdir): - temp_file = tmpdir.join(tempfile.mktemp()) + temp_file = tmpdir.join(tempfile.mkstemp()[1]) init_system = mount_efs.get_init_system(str(temp_file)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/mount_efs_test/test_write_stunnel_config_file.py new/efs-utils-1.32.1/test/mount_efs_test/test_write_stunnel_config_file.py --- old/efs-utils-1.31.3/test/mount_efs_test/test_write_stunnel_config_file.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/mount_efs_test/test_write_stunnel_config_file.py 2022-04-01 22:34:12.000000000 +0200 @@ -36,14 +36,24 @@ stunnel_check_cert_hostname=None, stunnel_check_cert_validity=False, stunnel_logs_file=None, + stunnel_libwrap_option_supported=True, ): + options = [] + if stunnel_check_cert_hostname_supported: + options.append(b"checkHost = peer certificate host name pattern") + if stunnel_check_cert_validity_supported: + options.append( + b"OCSPaia = yes|no check the AIA responders from certificates" + ) + if stunnel_libwrap_option_supported: + options.append( + b"libwrap = yes|no use /etc/hosts.allow and /etc/hosts.deny" + ) + mocker.patch( - "mount_efs.get_version_specific_stunnel_options", - return_value=( - stunnel_check_cert_hostname_supported, - stunnel_check_cert_validity_supported, - ), + "mount_efs.get_stunnel_options", + return_value=options, ) if stunnel_check_cert_hostname is None: @@ -274,8 +284,11 @@ ) -def _test_disable_libwrap( - mocker, tmpdir, system_release="unknown", disable_libwrap=True +def _test_enable_disable_libwrap( + mocker, + tmpdir, + system_release="unknown", + libwrap_supported=True, ): mocker.patch("mount_efs.add_stunnel_ca_options") ver_mocker = mocker.patch( @@ -283,7 +296,7 @@ ) config_file = mount_efs.write_stunnel_config_file( - _get_config(mocker), + _get_config(mocker, stunnel_libwrap_option_supported=libwrap_supported), str(tmpdir), FS_ID, MOUNT_POINT, @@ -299,7 +312,7 @@ _validate_config( config_file, mount_efs.STUNNEL_GLOBAL_CONFIG, - _get_expected_efs_config(disable_libwrap=disable_libwrap), + _get_expected_efs_config(disable_libwrap=libwrap_supported), ) @@ -539,28 +552,12 @@ ) -def test_write_stunnel_config_for_rhel8_disable_libwrap(mocker, tmpdir): - _test_disable_libwrap( - mocker, - tmpdir, - system_release="Red Hat Enterprise Linux release 8.0 (Ootpa)", - disable_libwrap=False, - ) - - -def test_write_stunnel_config_for_unknown_system_enable_libwrap(mocker, tmpdir): - _test_disable_libwrap( - mocker, tmpdir, system_release="unknown", disable_libwrap=True - ) +def test_write_stunnel_config_libwrap_not_supported(mocker, tmpdir): + _test_enable_disable_libwrap(mocker, tmpdir, libwrap_supported=False) -def test_write_stunnel_config_for_non_rhel8_enable_libwrap(mocker, tmpdir): - _test_disable_libwrap( - mocker, - tmpdir, - system_release="Amazon Linux release 2 (Karoo)", - disable_libwrap=True, - ) +def test_write_stunnel_config_libwrap_supported(mocker, tmpdir): + _test_enable_disable_libwrap(mocker, tmpdir, libwrap_supported=True) def test_write_stunnel_config_with_fall_back_ip_address(mocker, tmpdir): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/watchdog_test/test_check_efs_mounts.py new/efs-utils-1.32.1/test/watchdog_test/test_check_efs_mounts.py --- old/efs-utils-1.31.3/test/watchdog_test/test_check_efs_mounts.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/watchdog_test/test_check_efs_mounts.py 2022-04-01 22:34:12.000000000 +0200 @@ -45,6 +45,8 @@ config = ConfigParser() config.add_section(mount_efs.CONFIG_SECTION) config.set(mount_efs.CONFIG_SECTION, "state_file_dir_mode", "750") + config.add_section(watchdog.CONFIG_SECTION) + config.set(watchdog.CONFIG_SECTION, "stunnel_health_check_enabled", "false") return config @@ -75,7 +77,7 @@ def create_state_file(tmpdir, content=json.dumps(STATE)): - state_file = tmpdir.join(tempfile.mktemp()) + state_file = tmpdir.join(tempfile.mkstemp()[1]) state_file.write(content, ensure=True) return state_file.dirname, state_file.basename diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/watchdog_test/test_check_stunnel_health.py new/efs-utils-1.32.1/test/watchdog_test/test_check_stunnel_health.py --- old/efs-utils-1.31.3/test/watchdog_test/test_check_stunnel_health.py 1970-01-01 01:00:00.000000000 +0100 +++ new/efs-utils-1.32.1/test/watchdog_test/test_check_stunnel_health.py 2022-04-01 22:34:12.000000000 +0200 @@ -0,0 +1,286 @@ +# +# Copyright 2017-2018 Amazon.com, Inc. and its affiliates. All Rights Reserved. +# +# Licensed under the MIT License. See the LICENSE accompanying this file +# for the specific language governing permissions and limitations under +# the License. +# + +import json +import subprocess +import tempfile + +import watchdog +from mock import MagicMock + +from .. import utils + +try: + import ConfigParser +except ImportError: + from configparser import ConfigParser + + +DEFAULT_MOUNT_POINT = "/mnt" +FIXED_TIME = 1648627041.66836 +DEFAULT_MOUNT_TIME = ( + FIXED_TIME - watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN * 60 * 2 - 1 +) +DEFAULT_LAST_STUNNEL_CHECK_TIME = ( + FIXED_TIME - watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN * 60 - 1 +) +DEFAULT_MOUNTS = { + "mnt": watchdog.Mount( + "127.0.0.1", DEFAULT_MOUNT_POINT, "nfs4", "port=12345", "0", "0" + ), +} + + +def setup_mocks( + mocker, mock_subprocess_success=False, mock_subprocess_timeout_sec=None +): + check_time_mock = mocker.patch("time.time", return_value=FIXED_TIME) + popen_mock = None + if mock_subprocess_success: + process_mock = MagicMock() + process_mock.communicate.return_value = ( + "stdout", + "stderr", + ) + process_mock.returncode = 0 + popen_mock = mocker.patch("subprocess.Popen", return_value=process_mock) + elif mock_subprocess_timeout_sec: + process_mock = MagicMock() + process_mock.communicate.side_effect = subprocess.TimeoutExpired( + "cmd", timeout=mock_subprocess_timeout_sec + ) + popen_mock = mocker.patch("subprocess.Popen", return_value=process_mock) + return check_time_mock, popen_mock + + +def _get_config( + stunnel_health_check_enabled=True, + stunnel_health_check_interval_min=5, + stunnel_health_check_command_timeout_sec=30, +): + try: + config = ConfigParser.SafeConfigParser() + except AttributeError: + config = ConfigParser() + + config.add_section(watchdog.CONFIG_SECTION) + config.set( + watchdog.CONFIG_SECTION, + "stunnel_health_check_enabled", + "true" if stunnel_health_check_enabled else "false", + ) + config.set( + watchdog.CONFIG_SECTION, + "stunnel_health_check_interval_min", + str(stunnel_health_check_interval_min), + ) + config.set( + watchdog.CONFIG_SECTION, + "stunnel_health_check_command_timeout_sec", + str(stunnel_health_check_command_timeout_sec), + ) + return config + + +def test_stunnel_health_not_checked_when_feature_disabled(mocker, tmpdir): + check_time_mock, _ = setup_mocks(mocker) + config = _get_config(stunnel_health_check_enabled=False) + + state = {} + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(json.dumps(state), ensure=True) + watchdog.check_stunnel_health( + config, state, state_file.dirname, state_file.basename, [], DEFAULT_MOUNTS + ) + utils.assert_not_called(check_time_mock) + + +def test_stunnel_health_not_checked_mount_time_not_exist(mocker, tmpdir): + check_time_mock, subprocess_mock = setup_mocks(mocker, mock_subprocess_success=True) + config = _get_config(stunnel_health_check_enabled=True) + + state = {} + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(json.dumps(state), ensure=True) + watchdog.check_stunnel_health( + config, state, state_file.dirname, state_file.basename, [], DEFAULT_MOUNTS + ) + utils.assert_called_once(check_time_mock) + utils.assert_not_called(subprocess_mock) + with state_file.open() as f: + new_state = json.load(f) + assert FIXED_TIME == new_state["mount_time"] + + +def test_stunnel_health_not_checked_when_mount_passed_short_time(mocker, tmpdir): + check_time_mock, subprocess_mock = setup_mocks(mocker, mock_subprocess_success=True) + config = _get_config(stunnel_health_check_enabled=True) + + state = {"mount_time": FIXED_TIME - 1} + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(json.dumps(state), ensure=True) + watchdog.check_stunnel_health( + config, state, state_file.dirname, state_file.basename, [], DEFAULT_MOUNTS + ) + utils.assert_called_once(check_time_mock) + utils.assert_not_called(subprocess_mock) + + +def test_stunnel_health_not_checked_when_last_stunnel_check_passed_short_time( + mocker, tmpdir +): + check_time_mock, subprocess_mock = setup_mocks(mocker, mock_subprocess_success=True) + config = _get_config(stunnel_health_check_enabled=True) + + state = { + "mount_time": DEFAULT_MOUNT_TIME, + "last_stunnel_check_time": FIXED_TIME + - watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN * 60 + + 1, + } + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(json.dumps(state), ensure=True) + watchdog.check_stunnel_health( + config, state, state_file.dirname, state_file.basename, [], DEFAULT_MOUNTS + ) + utils.assert_called_once(check_time_mock) + utils.assert_not_called(subprocess_mock) + + +def test_stunnel_health_checked_passed_for_first_check(mocker, tmpdir): + check_time_mock, subprocess_mock = setup_mocks(mocker, mock_subprocess_success=True) + config = _get_config(stunnel_health_check_enabled=True) + + state = { + "mount_time": DEFAULT_MOUNT_TIME, + "mountpoint": "/mnt", + "pid": 9999, + } + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(json.dumps(state), ensure=True) + watchdog.check_stunnel_health( + config, state, state_file.dirname, state_file.basename, [], DEFAULT_MOUNTS + ) + + utils.assert_called_once(check_time_mock) + utils.assert_called_once(subprocess_mock) + with state_file.open() as f: + new_state = json.load(f) + assert FIXED_TIME == new_state["last_stunnel_check_time"] + + +def test_stunnel_health_checked_passed_for_non_first_check_default_interval( + mocker, tmpdir +): + config = _get_config(stunnel_health_check_enabled=True) + _test_stunnel_health_checked_passed_for_non_first_check_helper( + mocker, tmpdir, config + ) + + +def test_stunnel_health_checked_passed_for_non_first_check_short_interval( + mocker, tmpdir +): + config = _get_config( + stunnel_health_check_enabled=True, stunnel_health_check_interval_min=1 + ) + _test_stunnel_health_checked_passed_for_non_first_check_helper( + mocker, tmpdir, config, last_stunnel_check_time=FIXED_TIME - 61 + ) + + +def test_stunnel_health_failed_due_to_timeout_not_kill_stunnel(mocker, tmpdir): + _, subprocess_mock = setup_mocks( + mocker, + mock_subprocess_timeout_sec=watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_TIMEOUT_SEC, + ) + config = _get_config(stunnel_health_check_enabled=True) + + state = { + "mount_time": DEFAULT_MOUNT_TIME, + "mountpoint": "/mnt", + "pid": 9999, + "last_stunnel_check_time": DEFAULT_LAST_STUNNEL_CHECK_TIME, + } + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(json.dumps(state), ensure=True) + + mocker.patch("watchdog.is_pid_running", return_value=False) + watchdog.check_stunnel_health( + config, state, state_file.dirname, state_file.basename, [], DEFAULT_MOUNTS + ) + utils.assert_called_once(subprocess_mock) + + +def test_stunnel_health_failed_due_to_timeout_kill_stunnel(mocker, tmpdir): + _, subprocess_mock = setup_mocks( + mocker, + mock_subprocess_timeout_sec=watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_TIMEOUT_SEC, + ) + config = _get_config(stunnel_health_check_enabled=True) + + state = { + "mount_time": DEFAULT_MOUNT_TIME, + "mountpoint": "/mnt", + "pid": 9999, + "last_stunnel_check_time": DEFAULT_LAST_STUNNEL_CHECK_TIME, + } + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(json.dumps(state), ensure=True) + + mocker.patch("watchdog.is_pid_running", return_value=True) + mocker.patch("os.getpgid", return_value="fakepg") + kill_mock = mocker.patch("os.killpg") + restart_tls_tunnel_mock = mocker.patch("watchdog.restart_tls_tunnel") + watchdog.check_stunnel_health( + config, state, state_file.dirname, state_file.basename, [], DEFAULT_MOUNTS + ) + utils.assert_called_once(subprocess_mock) + utils.assert_called_once(kill_mock) + utils.assert_called_once(restart_tls_tunnel_mock) + + +def test_stunnel_health_checked_passed_for_non_first_check_no_mountpoint_info( + mocker, tmpdir +): + config = _get_config(stunnel_health_check_enabled=True) + _test_stunnel_health_checked_passed_for_non_first_check_helper( + mocker, tmpdir, config, mountpoint=None + ) + + +def _test_stunnel_health_checked_passed_for_non_first_check_helper( + mocker, + tmpdir, + config, + mount_time=DEFAULT_MOUNT_TIME, + last_stunnel_check_time=DEFAULT_LAST_STUNNEL_CHECK_TIME, + mountpoint=DEFAULT_MOUNT_POINT, +): + _, subprocess_mock = setup_mocks(mocker, mock_subprocess_success=True) + state = { + "mount_time": mount_time, + "pid": 9999, + "last_stunnel_check_time": last_stunnel_check_time, + } + + if mountpoint: + state["mountpoint"] = mountpoint + + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(json.dumps(state), ensure=True) + watchdog.check_stunnel_health( + config, state, state_file.dirname, state_file.basename, [], DEFAULT_MOUNTS + ) + + utils.assert_called_once(subprocess_mock) + with state_file.open() as f: + new_state = json.load(f) + assert FIXED_TIME == new_state["last_stunnel_check_time"] + if not mountpoint: + assert mountpoint == new_state["mountpoint"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/watchdog_test/test_clean_up_mount_state.py new/efs-utils-1.32.1/test/watchdog_test/test_clean_up_mount_state.py --- old/efs-utils-1.31.3/test/watchdog_test/test_clean_up_mount_state.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/watchdog_test/test_clean_up_mount_state.py 2022-04-01 22:34:12.000000000 +0200 @@ -18,7 +18,7 @@ def create_temp_file(tmpdir, content=""): - temp_file = tmpdir.join(tempfile.mktemp()) + temp_file = tmpdir.join(tempfile.mkstemp()[1]) temp_file.write(content, ensure=True) return temp_file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/watchdog_test/test_clean_up_previous_stunnel_pids.py new/efs-utils-1.32.1/test/watchdog_test/test_clean_up_previous_stunnel_pids.py --- old/efs-utils-1.31.3/test/watchdog_test/test_clean_up_previous_stunnel_pids.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/watchdog_test/test_clean_up_previous_stunnel_pids.py 2022-04-01 22:34:12.000000000 +0200 @@ -40,7 +40,8 @@ def create_state_file(tmpdir, content=json.dumps(STATE)): - state_file = tmpdir.join(tempfile.mktemp()) + state_file = tmpdir.join(tempfile.mkstemp()[1]) + state_file.write(content, ensure=True) return state_file.dirname, state_file.basename diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/watchdog_test/test_helper_function.py new/efs-utils-1.32.1/test/watchdog_test/test_helper_function.py --- old/efs-utils-1.31.3/test/watchdog_test/test_helper_function.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/watchdog_test/test_helper_function.py 2022-04-01 22:34:12.000000000 +0200 @@ -42,7 +42,7 @@ if config_section: config.add_section(config_section) if config_item and config_item_value is not None: - config.set(config_section, config_item, config_item_value) + config.set(config_section, config_item, str(config_item_value)) return config @@ -297,3 +297,87 @@ boto_session_mock.set_config_variable.assert_called_once() boto_session_mock.get_credentials.assert_called_once() + + +def test_get_int_value_from_config_file(): + config_section = watchdog.CONFIG_SECTION + config_item = "stunnel_health_check_interval_min" + config_value = watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN + 1 + config = get_config(config_section, config_item, config_value) + assert config_value == watchdog.get_int_value_from_config_file( + config, config_item, watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN + ) + + +def test_get_int_value_from_config_file_not_positive_value(): + config_section = watchdog.CONFIG_SECTION + config_item = "stunnel_health_check_interval_min" + config_value = 0 + config = get_config(config_section, config_item, config_value) + assert ( + watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN + == watchdog.get_int_value_from_config_file( + config, config_item, watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN + ) + ) + + +def test_get_int_value_from_config_file_no_config_value(): + config_item = "stunnel_health_check_interval_min" + config = get_config() + assert ( + watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN + == watchdog.get_int_value_from_config_file( + config, config_item, watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN + ) + ) + + +def test_get_int_value_from_config_file_wrong_type_value(): + config_section = watchdog.CONFIG_SECTION + config_item = "stunnel_health_check_interval_min" + config_value = "false" + config = get_config(config_section, config_item, config_value) + assert ( + watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN + == watchdog.get_int_value_from_config_file( + config, config_item, watchdog.DEFAULT_STUNNEL_HEALTH_CHECK_INTERVAL_MIN + ) + ) + + +def test_get_mountpoint_from_state_file_name(): + state_file_name = "/var/run/efs/fs-deadbeef.home.efs.12345" + nfs_mounts = { + "mnt": watchdog.Mount("127.0.0.1", "/mnt", "nfs4", "port=12343", "0", "0"), + "mnt1": watchdog.Mount("127.0.0.1", "/mnt1", "nfs4", "port=12342", "0", "0"), + "home.efs": watchdog.Mount( + "127.0.0.1", "/home/efs", "nfs4", "port=12345", "0", "0" + ), + } + assert "/home/efs" == watchdog.get_mountpoint_from_nfs_mounts( + state_file_name, nfs_mounts + ) + + state_file_name = ( + "/var/run/efs/fs-deadbeef.var.lib.kubelet.pods.2cbe4183-2c36-4a37-9f72-7e3c1a67538c.volumes" + ".kubernetes.io~csi.pvc-008bf5dc-5859-482e-b8b9-0e5e6887e411.mount.12345" + ) + nfs_mounts = { + "mnt": watchdog.Mount("127.0.0.1", "/mnt", "nfs4", "port=12343", "0", "0"), + "mnt1": watchdog.Mount("127.0.0.1", "/mnt1", "nfs4", "port=12342", "0", "0"), + "test": watchdog.Mount( + "127.0.0.1:/deadbeef/test-008bf5dc-5859-482e-b8b9-0e5e6887e411", + "/var/lib/kubelet/pods/1234583-2c36-4a37-9f72-7e3c1a67538c/volumes/" + "kubernetes.io~csi/pvc-008bf5dc-5859-482e-b8b9-0e5e6887e411/mount", + "nfs4", + "port=12345", + "0", + "0", + ), + } + assert ( + "/var/lib/kubelet/pods/1234583-2c36-4a37-9f72-7e3c1a67538c/volumes/kubernetes.io~csi/pvc-008bf5dc-5859" + "-482e-b8b9-0e5e6887e411/mount" + == watchdog.get_mountpoint_from_nfs_mounts(state_file_name, nfs_mounts) + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/efs-utils-1.31.3/test/watchdog_test/test_restart_tls_tunnel.py new/efs-utils-1.32.1/test/watchdog_test/test_restart_tls_tunnel.py --- old/efs-utils-1.31.3/test/watchdog_test/test_restart_tls_tunnel.py 2021-12-09 04:39:51.000000000 +0100 +++ new/efs-utils-1.32.1/test/watchdog_test/test_restart_tls_tunnel.py 2022-04-01 22:34:12.000000000 +0200 @@ -21,7 +21,7 @@ state = {"pid": 9999, "cmd": ""} - state_file = tmpdir.join(tempfile.mktemp()) + state_file = tmpdir.join(tempfile.mkstemp()[1]) state_file.write(json.dumps(state), ensure=True) watchdog.restart_tls_tunnel([], state, state_file.dirname, state_file.basename) @@ -39,7 +39,7 @@ state = {"pid": 9999, "cmd": "", "certificate": "foo/bar/certificate.pem"} - state_file = tmpdir.join(tempfile.mktemp()) + state_file = tmpdir.join(tempfile.mkstemp()[1]) state_file.write(json.dumps(state), ensure=True) watchdog.restart_tls_tunnel([], state, state_file.dirname, state_file.basename)