diff --git a/amazon-efs-utils.spec b/amazon-efs-utils.spec index 46fe2de6..d2c20c77 100644 --- a/amazon-efs-utils.spec +++ b/amazon-efs-utils.spec @@ -41,7 +41,7 @@ %{?!include_vendor_tarball:%define include_vendor_tarball true} Name : amazon-efs-utils -Version : 2.4.1 +Version : 2.4.2 Release : 1%{platform} Summary : This package provides utilities for simplifying the use of EFS file systems @@ -196,6 +196,13 @@ fi %clean %changelog +* Tue Dec 23 2025 Samuel Hale - 2.4.2 +- Skip stunnel binary invocation when efs-proxy mode is enabled +- Retry "access denied" only for access point mounting +- Fix issue for missing PATH in env when check stunnel lib +- Fix EFS_FQDN_RE to support ADC DNS suffixes with hyphens +- Fix IPv6-only mount target FQDN resolution in match_device + * Thu Nov 20 2025 Anthony Tse - 2.4.1 - Add cafile override for eusc-de-east-1 in efs-utils.conf diff --git a/build-deb.sh b/build-deb.sh index 93cdee72..a62a3944 100755 --- a/build-deb.sh +++ b/build-deb.sh @@ -11,7 +11,7 @@ set -ex BASE_DIR=$(pwd) BUILD_ROOT=${BASE_DIR}/build/debbuild -VERSION=2.4.1 +VERSION=2.4.2 RELEASE=1 ARCH=$(dpkg --print-architecture) DEB_SYSTEM_RELEASE_PATH=/etc/os-release diff --git a/config.ini b/config.ini index 90025d23..19ddb2e0 100644 --- a/config.ini +++ b/config.ini @@ -7,5 +7,5 @@ # [global] -version=2.4.1 +version=2.4.2 release=1 diff --git a/src/mount_efs/__init__.py b/src/mount_efs/__init__.py index 9b5fe51b..30aa5336 100755 --- a/src/mount_efs/__init__.py +++ b/src/mount_efs/__init__.py @@ -86,7 +86,7 @@ BOTOCORE_PRESENT = False -VERSION = "2.4.1" +VERSION = "2.4.2" SERVICE = "elasticfilesystem" AMAZON_LINUX_2_RELEASE_ID = "Amazon Linux release 2 (Karoo)" @@ -1425,10 +1425,15 @@ def find_command_path(command, install_method): # For more information, see https://brew.sh/2021/02/05/homebrew-3.0.0/ else: env_path = "/opt/homebrew/bin:/usr/local/bin" - os.putenv("PATH", env_path) + + existing_path = os.environ.get("PATH", "") + search_path = env_path + ":" + existing_path if existing_path else env_path + + env = os.environ.copy() + env["PATH"] = search_path try: - path = subprocess.check_output(["which", command]) + path = subprocess.check_output(["which", command], env=env) return path.strip().decode() except subprocess.CalledProcessError as e: fatal_error( @@ -1479,7 +1484,7 @@ def write_stunnel_config_file( hand-serialize it. """ - stunnel_options = get_stunnel_options() + stunnel_options = [] if efs_proxy_enabled else get_stunnel_options() mount_filename = get_mount_specific_filename(fs_id, mountpoint, tls_port) system_release_version = get_system_release_version() @@ -2223,9 +2228,15 @@ def backoff_function(i): out, err = proc.communicate(timeout=retry_nfs_mount_command_timeout_sec) rc = proc.poll() if rc != 0: + is_access_point_mount = "accesspoint" in options continue_retry = any( error_string in str(err) for error_string in RETRYABLE_ERRORS ) + + # Only retry "access denied" for access point mounts, handles race condition that can occur during AP backend provisioning + if not continue_retry and "access denied by server" in str(err): + continue_retry = is_access_point_mount + if continue_retry: logging.error( 'Mounting %s to %s failed, return code=%s, stdout="%s", stderr="%s", mount attempt %d/%d, ' @@ -3223,8 +3234,18 @@ def match_device(config, device, options): return remote, path, None try: - primary, secondaries, _ = socket.gethostbyname_ex(remote) - hostnames = list(filter(lambda e: e is not None, [primary] + secondaries)) + addrinfo = socket.getaddrinfo( + remote, None, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_CANONNAME + ) + hostnames = list( + set( + filter( + lambda e: e is not None and e != "", [info[3] for info in addrinfo] + ) + ) + ) + if not hostnames: + hostnames = [remote] except socket.gaierror: create_default_cloudwatchlog_agent_if_not_exist(config, options) fatal_error( diff --git a/src/proxy/Cargo.lock b/src/proxy/Cargo.lock deleted file mode 100644 index 66cf7425..00000000 --- a/src/proxy/Cargo.lock +++ /dev/null @@ -1,1923 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr 2.7.6", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "aws-lc-fips-sys" -version = "0.13.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede71ad84efb06d748d9af3bc500b14957a96282a69a6833b1420dcacb411cc3" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", - "regex", -] - -[[package]] -name = "aws-lc-rs" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" -dependencies = [ - "aws-lc-fips-sys", - "aws-lc-sys", - "untrusted", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "backtrace" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7815ea54e4d821e791162e078acbebfd6d8c8939cd559c9335dceb1c8ca7282" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.10.0", - "cexpr", - "clang-sys", - "itertools", - "log 0.4.28", - "prettyplease", - "proc-macro2", - "quote 1.0.41", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.108", -] - -[[package]] -name = "bitflags" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" - -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - -[[package]] -name = "cc" -version = "1.2.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom 7.1.3", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422592638015fe46332afb8fbf9361d9fa2d498d05c0c384e28710b4639e33a5" -dependencies = [ - "atty", - "bitflags 1.3.2", - "clap_derive", - "clap_lex", - "once_cell", - "strsim 0.10.0", - "termcolor", -] - -[[package]] -name = "clap_derive" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677ca5a153ca1804d4bf3e9d45f0f6b5ba4f950de155e373d457cd5f154cca9c" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote 1.0.41", - "syn 1.0.109", -] - -[[package]] -name = "clap_lex" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 1.0.109", -] - -[[package]] -name = "destructure_traitobject" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "efs-proxy" -version = "2.4.1" -dependencies = [ - "anyhow", - "async-trait", - "bytes", - "chrono", - "clap 4.0.0", - "fern", - "futures", - "log 0.4.28", - "log4rs", - "nix", - "onc-rpc", - "rand 0.8.5", - "s2n-tls", - "s2n-tls-tokio", - "serde", - "serde_ini", - "tempfile", - "test-case", - "thiserror", - "tokio", - "tokio-util", - "uuid", - "xdr-codec", - "xdrgen", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "humantime", - "is-terminal", - "log 0.4.28", - "regex", - "termcolor", -] - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "error-chain" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" -dependencies = [ - "backtrace", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fern" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" -dependencies = [ - "log 0.4.28", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr 2.7.6", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", -] - -[[package]] -name = "gimli" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "humantime" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log 0.4.28", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "is-terminal" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" -dependencies = [ - "hermit-abi 0.5.2", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" - -[[package]] -name = "libc" -version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -dependencies = [ - "log 0.4.28", -] - -[[package]] -name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" -dependencies = [ - "serde", -] - -[[package]] -name = "log-mdc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" - -[[package]] -name = "log4rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36ca1786d9e79b8193a68d480a0907b612f109537115c6ff655a3a1967533fd" -dependencies = [ - "anyhow", - "arc-swap", - "chrono", - "derivative", - "fnv", - "humantime", - "libc", - "log 0.4.28", - "log-mdc", - "parking_lot", - "serde", - "serde-value", - "serde_json", - "serde_yaml", - "thiserror", - "thread-id", - "typemap-ors", - "winapi", -] - -[[package]] -name = "memchr" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" -dependencies = [ - "libc", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset", - "pin-utils", -] - -[[package]] -name = "nom" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b" -dependencies = [ - "memchr 1.0.2", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr 2.7.6", - "minimal-lexical", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi 0.5.2", - "libc", -] - -[[package]] -name = "object" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7" -dependencies = [ - "memchr 2.7.6", -] - -[[package]] -name = "onc-rpc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869ad251376b969679e1cc00d989d59f577c05f33481a1b2e3273147c0f9d615" -dependencies = [ - "byteorder", - "bytes", - "smallvec", - "thiserror", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.108", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote 1.0.41", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" - -[[package]] -name = "quote" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.10.0", -] - -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr 2.7.6", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr 2.7.6", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "result" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560" - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustix" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" -dependencies = [ - "bitflags 2.10.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "s2n-tls" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "821c6c037686bbc60273f3c4af20012eecbe5e9b1c4ac3d7f766a1f2464681bf" -dependencies = [ - "errno", - "hex", - "libc", - "pin-project-lite", - "s2n-tls-sys", -] - -[[package]] -name = "s2n-tls-sys" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a755df740916e2fc0aaf99c6fc0e519028702a75bff018b6b55a735eada406a" -dependencies = [ - "aws-lc-rs", - "cc", - "libc", -] - -[[package]] -name = "s2n-tls-tokio" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e03f7869a3e06f9dcec01de224bd096320cf89ddf495a32547a0364cbf8de90" -dependencies = [ - "errno", - "libc", - "pin-project-lite", - "s2n-tls", - "tokio", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "serde_ini" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb236687e2bb073a7521c021949be944641e671b8505a94069ca37b656c81139" -dependencies = [ - "result", - "serde", - "void", -] - -[[package]] -name = "serde_json" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" -dependencies = [ - "itoa", - "memchr 2.7.6", - "ryu", - "serde", - "serde_core", -] - -[[package]] -name = "serde_yaml" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" -dependencies = [ - "indexmap", - "ryu", - "serde", - "yaml-rust", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "test-case" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" -dependencies = [ - "test-case-macros", -] - -[[package]] -name = "test-case-core" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "test-case-macros" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", - "test-case-core", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "thread-id" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe8f25bbdd100db7e1d34acf7fd2dc59c4bf8f7483f505eaa7d4f12f76cc0ea" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "tokio" -version = "1.38.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68722da18b0fc4a05fdc1120b302b82051265792a1e1b399086e9b204b10ad3d" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "tokio-util" -version = "0.7.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "typemap-ors" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" -dependencies = [ - "unsafe-any-ors", -] - -[[package]] -name = "unicode-ident" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unsafe-any-ors" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" -dependencies = [ - "destructure_traitobject", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "rand 0.9.2", - "uuid-macro-internal", - "wasm-bindgen", -] - -[[package]] -name = "uuid-macro-internal" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9384a660318abfbd7f8932c34d67e4d1ec511095f95972ddc01e19d7ba8413f" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" -dependencies = [ - "quote 1.0.41", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "xdr-codec" -version = "0.4.4" -dependencies = [ - "byteorder", - "error-chain", -] - -[[package]] -name = "xdrgen" -version = "0.4.4" -dependencies = [ - "bitflags 0.9.1", - "clap 2.34.0", - "env_logger", - "lazy_static", - "log 0.3.9", - "nom 3.2.1", - "quote 0.3.15", - "xdr-codec", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote 1.0.41", - "syn 2.0.108", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/src/proxy/Cargo.toml b/src/proxy/Cargo.toml index cb469919..b5c020f2 100644 --- a/src/proxy/Cargo.toml +++ b/src/proxy/Cargo.toml @@ -3,7 +3,7 @@ name = "efs-proxy" edition = "2021" build = "build.rs" # The version of efs-proxy is tied to efs-utils. -version = "2.4.1" +version = "2.4.2" publish = false license = "MIT" @@ -34,6 +34,7 @@ xdr-codec = { path = "rust-xdr/xdr-codec"} test-case = "*" tokio = { version = "1.29.0", features = ["test-util"] } tempfile = "3.10.1" +regex = "1.10.2" [build-dependencies] xdrgen = { path = "rust-xdr/xdrgen" } diff --git a/src/proxy/build.rs b/src/proxy/build.rs index dbc10216..81bcde27 100644 --- a/src/proxy/build.rs +++ b/src/proxy/build.rs @@ -1,5 +1,3 @@ -use xdrgen; - fn main() { xdrgen::compile("src/efs_prot.x").expect("xdrgen efs_prot.x failed"); } diff --git a/src/proxy/rust-xdr/xdrgen/src/spec/mod.rs b/src/proxy/rust-xdr/xdrgen/src/spec/mod.rs index e259ae08..15b6bb67 100644 --- a/src/proxy/rust-xdr/xdrgen/src/spec/mod.rs +++ b/src/proxy/rust-xdr/xdrgen/src/spec/mod.rs @@ -1073,15 +1073,15 @@ impl Symtab { } } - pub fn constants(&self) -> Iter)> { + pub fn constants(&self) -> Iter<'_, String, (i64, Option)> { self.consts.iter() } - pub fn typespecs(&self) -> Iter { + pub fn typespecs(&self) -> Iter<'_, String, Type> { self.typespecs.iter() } - pub fn typesyns(&self) -> Iter { + pub fn typesyns(&self) -> Iter<'_, String, Type> { self.typesyns.iter() } } diff --git a/src/proxy/src/config_parser.rs b/src/proxy/src/config_parser.rs index 0a49fb14..0a367a3a 100644 --- a/src/proxy/src/config_parser.rs +++ b/src/proxy/src/config_parser.rs @@ -20,6 +20,10 @@ where } } +fn default_log_format() -> Option { + Some("file".to_string()) +} + #[derive(Default, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct ProxyConfig { #[serde(alias = "fips", deserialize_with = "deserialize_bool")] @@ -33,6 +37,11 @@ pub struct ProxyConfig { #[serde(alias = "output")] pub output: Option, + /// The format to use for logging. Values can be "file", "stdout" + /// Default is "file" if not specified. + #[serde(alias = "log_format", default = "default_log_format")] + pub log_format: Option, + /// The proxy process is responsible for writing it's PID into this file so that the Watchdog /// process can monitor it #[serde(alias = "pid")] @@ -136,6 +145,7 @@ checkHost = fs-12341234.efs.us-east-1.amazonaws.com output: Some(String::from( "/var/log/amazon/efs/fs-12341234.home.ec2-user.efs.21036.efs-proxy.log", )), + log_format: Some(String::from("file")), nested_config: EfsConfig { listen_addr: String::from("127.0.0.1:21036"), mount_target_addr: String::from("fs-12341234.efs.us-east-1.amazonaws.com:2049"), @@ -162,6 +172,7 @@ socket = a:SO_BINDTODEVICE=lo pid = /var/run/efs/fs-12341234.home.ec2-user.efs.21036+/stunnel.pid port = 8081 initial_partition_ip = 127.0.0.1:2049 +log_format = stdout [efs] accept = 127.0.0.1:21036 @@ -187,6 +198,7 @@ checkHost = fs-12341234.efs.us-east-1.amazonaws.com ), debug: DEFAULT_LOG_LEVEL.to_string(), output: None, + log_format: Some(String::from("stdout")), nested_config: EfsConfig { listen_addr: String::from("127.0.0.1:21036"), mount_target_addr: String::from("fs-12341234.efs.us-east-1.amazonaws.com:2049"), diff --git a/src/proxy/src/efs_prot.x b/src/proxy/src/efs_prot.x index d0faeb4f..eac14ae9 100644 --- a/src/proxy/src/efs_prot.x +++ b/src/proxy/src/efs_prot.x @@ -48,10 +48,3 @@ struct BindClientResponse { BindResponse bind_response; ScaleUpConfig scale_up_config; }; - -union OperationResponse switch (OperationType operation_type) { - case OP_BIND_CLIENT_TO_PARTITION: - BindClientResponse response; - default: - void; -}; diff --git a/src/proxy/src/lib.rs b/src/proxy/src/lib.rs index 42111954..ee41164e 100644 --- a/src/proxy/src/lib.rs +++ b/src/proxy/src/lib.rs @@ -10,6 +10,7 @@ pub mod connections; pub mod controller; pub mod efs_rpc; pub mod error; +pub mod log_encoder; pub mod logger; pub mod proxy; pub mod proxy_identifier; diff --git a/src/proxy/src/log_encoder.rs b/src/proxy/src/log_encoder.rs new file mode 100644 index 00000000..e0529bf5 --- /dev/null +++ b/src/proxy/src/log_encoder.rs @@ -0,0 +1,132 @@ +use anyhow::Result; +use chrono::Utc; +use log4rs::encode::{Encode, Write}; +use std::fmt; + +/// Custom encoder that replaces newlines with spaces to keep multi-line logs on a single line +pub struct SingleLineEncoder; + +impl Encode for SingleLineEncoder { + fn encode(&self, w: &mut dyn Write, record: &log::Record<'_>) -> Result<()> { + let timestamp = Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ"); + let level = record.level(); + let module = record.module_path().unwrap_or("-"); + let message = format!("{}", record.args()); + let single_line_message = message.replace('\n', " "); + + writeln!( + w, + "{} {} {} {} {}", + timestamp, + std::process::id(), + level, + module, + single_line_message + ) + .map_err(|e| anyhow::anyhow!(e)) + } +} + +impl fmt::Debug for SingleLineEncoder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SingleLineEncoder").finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use log::{Level, Record}; + use regex::Regex; + use std::io; + + struct BufferWriter<'a>(&'a mut Vec); + + impl<'a> io::Write for BufferWriter<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + impl<'a> Write for BufferWriter<'a> { + // This trait is implemented automatically because BufferWriter implements io::Write + } + + #[test] + fn test_format_log_message() { + let encoder = SingleLineEncoder; + + let record = Record::builder() + .args(format_args!("Test message")) + .level(Level::Info) + .target("test_target") + .module_path(Some("test_module")) + .file(Some("test_file.rs")) + .line(Some(42)) + .build(); + + let mut buffer = Vec::new(); + + let mut writer = BufferWriter(&mut buffer); + encoder.encode(&mut writer, &record).unwrap(); + + let output = String::from_utf8_lossy(&buffer); + + let timestamp_regex = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z"; + let pid_regex = r"\d+"; + let level_regex = r"INFO"; + let module_regex = r"test_module"; + let message_regex = r"Test message"; + + let pattern = format!( + "^{} {} {} {} {}$", + timestamp_regex, pid_regex, level_regex, module_regex, message_regex + ); + + let regex = Regex::new(&pattern).unwrap(); + assert!( + regex.is_match(output.trim()), + "Output format doesn't match expected pattern. Got: {}", + output + ); + } + + #[test] + fn test_multiline_message() { + let encoder = SingleLineEncoder; + + let record = Record::builder() + .args(format_args!("Test\nmultiline\nmessage")) + .level(Level::Warn) + .target("test_target") + .module_path(Some("test_module")) + .file(Some("test_file.rs")) + .line(Some(42)) + .build(); + + let mut buffer = Vec::new(); + + let mut writer = BufferWriter(&mut buffer); + encoder.encode(&mut writer, &record).unwrap(); + + let output = String::from_utf8_lossy(&buffer); + + assert!( + output.contains("Test multiline message"), + "Multiline message not properly formatted. Got: {}", + output + ); + + let newline_count = output.chars().filter(|&c| c == '\n').count(); + assert_eq!( + newline_count, 1, + "Expected only one newline at the end. Got: {}", + output + ); + } +} diff --git a/src/proxy/src/logger.rs b/src/proxy/src/logger.rs index 2b7a3c70..a82b504a 100644 --- a/src/proxy/src/logger.rs +++ b/src/proxy/src/logger.rs @@ -16,50 +16,187 @@ use log4rs::{ use std::{path::Path, str::FromStr}; use crate::config_parser::ProxyConfig; +use crate::log_encoder::SingleLineEncoder; const LOG_FILE_MAX_BYTES: u64 = 1048576; const LOG_FILE_COUNT: u32 = 10; -pub fn init(config: &ProxyConfig) { - let log_file_path_string = config - .output - .clone() - .expect("config value `output` is not set"); - let log_file_path = Path::new(&log_file_path_string); +pub fn create_config(config: &ProxyConfig) -> Config { let level_filter = LevelFilter::from_str(&config.debug).expect("config value for `debug` is invalid"); - let stderr = ConsoleAppender::builder().target(Target::Stderr).build(); - - let trigger = SizeTrigger::new(LOG_FILE_MAX_BYTES); - let mut pattern = log_file_path_string.clone(); - pattern.push_str(".{}"); - let roller = FixedWindowRoller::builder() - .build(&pattern, LOG_FILE_COUNT) - .expect("Unable to create roller"); - let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller)); - - let log_file = RollingFileAppender::builder() - .encoder(Box::new(PatternEncoder::new( - "{d(%Y-%m-%dT%H:%M:%S%.3fZ)(utc)} {P} {l} {M} {m}{n}", - ))) - .build(log_file_path, Box::new(policy)) - .expect("Unable to create log file"); - - let config = Config::builder() - .appender(Appender::builder().build("logfile", Box::new(log_file))) - .appender( - Appender::builder() - .filter(Box::new(ThresholdFilter::new(LevelFilter::Error))) - .build("stderr", Box::new(stderr)), - ) - .build( - Root::builder() - .appender("logfile") - .appender("stderr") - .build(level_filter), - ) - .expect("Invalid logger config"); - - let _ = log4rs::init_config(config).expect("Unable to initialize logger"); + let log_format = config.log_format.as_deref().unwrap_or("file"); + + let mut config_builder = Config::builder(); + let mut root_builder = Root::builder(); + + match log_format { + "file" => { + let log_file_path_string = config + .output + .clone() + .expect("config value `output` is not set"); + + let log_file_path = Path::new(&log_file_path_string); + + let stderr = ConsoleAppender::builder().target(Target::Stderr).build(); + + config_builder = config_builder.appender( + Appender::builder() + .filter(Box::new(ThresholdFilter::new(LevelFilter::Error))) + .build("stderr", Box::new(stderr)), + ); + + let trigger = SizeTrigger::new(LOG_FILE_MAX_BYTES); + let mut pattern = log_file_path_string.clone(); + pattern.push_str(".{}"); + let roller = FixedWindowRoller::builder() + .build(&pattern, LOG_FILE_COUNT) + .expect("Unable to create roller"); + let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller)); + + let log_file = RollingFileAppender::builder() + .encoder(Box::new(PatternEncoder::new( + "{d(%Y-%m-%dT%H:%M:%S%.3fZ)(utc)} {P} {l} {M} {m}{n}", + ))) + .build(log_file_path, Box::new(policy)) + .expect("Unable to create log file"); + + config_builder = + config_builder.appender(Appender::builder().build("logfile", Box::new(log_file))); + + root_builder = root_builder.appender("logfile").appender("stderr"); + } + "stdout" => { + let stderr = ConsoleAppender::builder() + .target(Target::Stderr) + .encoder(Box::new(SingleLineEncoder)) + .build(); + + config_builder = config_builder.appender( + Appender::builder() + .filter(Box::new(ThresholdFilter::new(LevelFilter::Error))) + .build("stderr", Box::new(stderr)), + ); + + let stdout = ConsoleAppender::builder() + .target(Target::Stdout) + .encoder(Box::new(SingleLineEncoder)) + .build(); + + config_builder = + config_builder.appender(Appender::builder().build("stdout", Box::new(stdout))); + + root_builder = root_builder.appender("stderr").appender("stdout"); + } + _ => panic!("Invalid `log_format` value. Must be either 'file' or 'stdout'"), + } + + config_builder + .build(root_builder.build(level_filter)) + .expect("Invalid logger config") +} + +pub fn init(config: &ProxyConfig) { + let log_format = config.log_format.as_deref().unwrap_or("file"); + if log_format == "file" && config.output.is_none() { + return; + } + + let log_config = create_config(config); + let _ = log4rs::init_config(log_config).expect("Unable to initialize logger"); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::config_parser::ProxyConfig; + use std::panic; + use tempfile::tempdir; + + #[test] + fn test_logger_init_with_file() { + let temp_dir = tempdir().expect("Failed to create temporary directory"); + let log_path = temp_dir.path().join("test.log"); + let log_path_str = log_path.to_str().expect("Failed to convert path to string"); + + let config = ProxyConfig { + fips: false, + debug: "info".to_string(), + output: Some(log_path_str.to_string()), + log_format: Some("file".to_string()), + pid_file_path: "".to_string(), + nested_config: Default::default(), + }; + + let result = panic::catch_unwind(|| { + init(&config); + }); + + let _ = temp_dir.close(); + + assert!( + result.is_ok(), + "Logger initialization panicked with valid config" + ); + } + + #[test] + fn test_create_config_with_file() { + let temp_dir = tempdir().expect("Failed to create temporary directory"); + let log_path = temp_dir.path().join("test.log"); + let log_path_str = log_path.to_str().expect("Failed to convert path to string"); + + let config = ProxyConfig { + fips: false, + debug: "info".to_string(), + output: Some(log_path_str.to_string()), + log_format: Some("file".to_string()), + pid_file_path: "".to_string(), + nested_config: Default::default(), + }; + + let log_config = create_config(&config); + + assert_eq!(log_config.root().level(), LevelFilter::Info); + + let _ = temp_dir.close(); + } + + #[test] + fn test_create_config_with_stdout() { + let config = ProxyConfig { + fips: false, + debug: "debug".to_string(), + output: None, + log_format: Some("stdout".to_string()), + pid_file_path: "".to_string(), + nested_config: Default::default(), + }; + + let log_config = create_config(&config); + + assert_eq!(log_config.root().level(), LevelFilter::Debug); + } + + #[test] + fn test_init_skips_when_output_none() { + let config = ProxyConfig { + fips: false, + debug: "info".to_string(), + output: None, + log_format: Some("file".to_string()), + pid_file_path: "".to_string(), + nested_config: Default::default(), + }; + + let result = panic::catch_unwind(|| { + init(&config); + }); + + assert!( + result.is_ok(), + "Logger initialization should not panic when output is None" + ); + } } diff --git a/src/proxy/src/main.rs b/src/proxy/src/main.rs index 92d4d1e4..65ecd409 100644 --- a/src/proxy/src/main.rs +++ b/src/proxy/src/main.rs @@ -18,6 +18,7 @@ mod connections; mod controller; mod efs_rpc; mod error; +mod log_encoder; mod logger; mod proxy; mod proxy_identifier; @@ -46,9 +47,7 @@ async fn main() { Err(e) => panic!("Failed to read configuration. {}", e), }; - if let Some(_log_file_path) = &proxy_config.output { - logger::init(&proxy_config) - } + logger::init(&proxy_config); info!("Running with configuration: {:?}", proxy_config); diff --git a/src/proxy/src/proxy_identifier.rs b/src/proxy/src/proxy_identifier.rs index 0e986864..75c71f22 100644 --- a/src/proxy/src/proxy_identifier.rs +++ b/src/proxy/src/proxy_identifier.rs @@ -8,6 +8,12 @@ pub struct ProxyIdentifier { pub incarnation: i64, } +impl Default for ProxyIdentifier { + fn default() -> Self { + Self::new() + } +} + impl ProxyIdentifier { pub fn new() -> Self { ProxyIdentifier { @@ -32,7 +38,7 @@ mod tests { #[test] fn test_increment() { - let mut proxy_id = ProxyIdentifier::new(); + let mut proxy_id = ProxyIdentifier::default(); let proxy_id_original = proxy_id; for i in 0..5 { assert_eq!(i, proxy_id.incarnation); diff --git a/src/watchdog/__init__.py b/src/watchdog/__init__.py index f66e7fb4..3a196979 100755 --- a/src/watchdog/__init__.py +++ b/src/watchdog/__init__.py @@ -56,7 +56,7 @@ AMAZON_LINUX_2_RELEASE_ID, AMAZON_LINUX_2_PRETTY_NAME, ] -VERSION = "2.4.1" +VERSION = "2.4.2" SERVICE = "elasticfilesystem" CONFIG_FILE = "/etc/amazon/efs/efs-utils.conf" @@ -992,10 +992,15 @@ def find_command_path(command, install_method): # For more information, see https://brew.sh/2021/02/05/homebrew-3.0.0/ else: env_path = "/opt/homebrew/bin:/usr/local/bin" - os.putenv("PATH", env_path) + + existing_path = os.environ.get("PATH", "") + search_path = env_path + ":" + existing_path if existing_path else env_path + + env = os.environ.copy() + env["PATH"] = search_path try: - path = subprocess.check_output(["which", command]) + path = subprocess.check_output(["which", command], env=env) return path.strip().decode() except subprocess.CalledProcessError as e: fatal_error( diff --git a/test/common.py b/test/common.py index 474746dd..d0dd9820 100644 --- a/test/common.py +++ b/test/common.py @@ -47,6 +47,14 @@ def _create_mock(self): communicate_return_value=(b"", b"mount.nfs4: Connection reset by peer"), ) DEFAULT_NON_RETRYABLE_FAILURE_POPEN = PopenMock( + return_code=1, + poll_result=1, + communicate_return_value=( + b"", + b"mount.nfs4: Protocol not supported", + ), +) +ACCESS_DENIED_FAILURE_POPEN = PopenMock( return_code=1, poll_result=1, communicate_return_value=( diff --git a/test/mount_efs_test/test_match_device.py b/test/mount_efs_test/test_match_device.py index af06baf4..d74401f1 100644 --- a/test/mount_efs_test/test_match_device.py +++ b/test/mount_efs_test/test_match_device.py @@ -1,123 +1,131 @@ -# 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 socket - -import pytest - -import mount_efs - -from .. import utils - -try: - import ConfigParser -except ImportError: - from configparser import ConfigParser - -DEFAULT_AZ = "us-east-1a" -CORRECT_DEVICE_DESCRIPTORS_FS_ID = [ - ("fs-deadbeef", ("fs-deadbeef", "/", None)), - ("fs-deadbeef:/", ("fs-deadbeef", "/", None)), - ("fs-deadbeef:/some/subpath", ("fs-deadbeef", "/some/subpath", None)), - ( - "fs-deadbeef:/some/subpath/with:colons", - ("fs-deadbeef", "/some/subpath/with:colons", None), - ), -] -CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS = [ - ("custom-cname.example.com", ("fs-deadbeef", "/", None)), - ("custom-cname.example.com:/", ("fs-deadbeef", "/", None)), - ("custom-cname.example.com:/some/subpath", ("fs-deadbeef", "/some/subpath", None)), - ( - "custom-cname.example.com:/some/subpath/with:colons", - ("fs-deadbeef", "/some/subpath/with:colons", None), - ), -] -CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS_WITH_AZ = [ - ("custom-cname.example.com", ("fs-deadbeef", "/", DEFAULT_AZ)), - ("custom-cname.example.com:/", ("fs-deadbeef", "/", DEFAULT_AZ)), - ( - "custom-cname.example.com:/some/subpath", - ("fs-deadbeef", "/some/subpath", DEFAULT_AZ), - ), - ( - "custom-cname.example.com:/some/subpath/with:colons", - ("fs-deadbeef", "/some/subpath/with:colons", DEFAULT_AZ), - ), -] -DEFAULT_REGION = "us-east-1" -DEFAULT_NFS_OPTIONS = {} -FS_ID = "fs-deadbeef" -OPTIONS_WITH_AZ = {"az": DEFAULT_AZ} -TEST_SOCKET_GET_ADDR_INFO_RETURN = [ - (socket.AF_INET, socket.SOCK_STREAM, 6, "", ("93.184.216.34", 80)) -] - - -@pytest.fixture(autouse=True) -def setup(mocker): - mocker.patch("mount_efs.get_target_region", return_value=DEFAULT_REGION) - mocker.patch( - "socket.getaddrinfo", - return_value=TEST_SOCKET_GET_ADDR_INFO_RETURN, - ) - - -def _get_mock_config( - dns_name_format="{az}.{fs_id}.efs.{region}.{dns_name_suffix}", - dns_name_suffix="amazonaws.com", - cloudwatch_enabled="false", - has_fallback_to_mount_target_ip_address_item=True, - fallback_to_mount_target_ip_address=False, -): - try: - config = ConfigParser.SafeConfigParser() - except AttributeError: - config = ConfigParser() - config.add_section(mount_efs.CONFIG_SECTION) - config.add_section(mount_efs.CLOUDWATCH_LOG_SECTION) - config.set(mount_efs.CONFIG_SECTION, "dns_name_format", dns_name_format) - config.set(mount_efs.CONFIG_SECTION, "dns_name_suffix", dns_name_suffix) - config.set(mount_efs.CLOUDWATCH_LOG_SECTION, "enabled", cloudwatch_enabled) - if has_fallback_to_mount_target_ip_address_item: - config.set( - mount_efs.CONFIG_SECTION, - mount_efs.FALLBACK_TO_MOUNT_TARGET_IP_ADDRESS_ITEM, - str(fallback_to_mount_target_ip_address), - ) - - return config - - -def test_match_device_correct_descriptors_fs_id(mocker): - config = _get_mock_config() - for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_FS_ID: - assert (fs_id, path, az) == mount_efs.match_device( - config, device, DEFAULT_NFS_OPTIONS - ) - - -def test_match_device_correct_descriptors_cname_dns_suffix_override_region(mocker): - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=("fs-deadbeef.efs.cn-north-1.amazonaws.com.cn", None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", - return_value=("fs-deadbeef.efs.cn-north-1.amazonaws.com.cn", [], None), - ) - config = _get_mock_config() - for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: - assert (fs_id, path, az) == mount_efs.match_device( - config, device, DEFAULT_NFS_OPTIONS - ) - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - +# 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 socket + +import pytest + +import mount_efs + +from .. import utils + +try: + import ConfigParser +except ImportError: + from configparser import ConfigParser + +DEFAULT_AZ = "us-east-1a" +CORRECT_DEVICE_DESCRIPTORS_FS_ID = [ + ("fs-deadbeef", ("fs-deadbeef", "/", None)), + ("fs-deadbeef:/", ("fs-deadbeef", "/", None)), + ("fs-deadbeef:/some/subpath", ("fs-deadbeef", "/some/subpath", None)), + ( + "fs-deadbeef:/some/subpath/with:colons", + ("fs-deadbeef", "/some/subpath/with:colons", None), + ), +] +CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS = [ + ("custom-cname.example.com", ("fs-deadbeef", "/", None)), + ("custom-cname.example.com:/", ("fs-deadbeef", "/", None)), + ("custom-cname.example.com:/some/subpath", ("fs-deadbeef", "/some/subpath", None)), + ( + "custom-cname.example.com:/some/subpath/with:colons", + ("fs-deadbeef", "/some/subpath/with:colons", None), + ), +] +CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS_WITH_AZ = [ + ("custom-cname.example.com", ("fs-deadbeef", "/", DEFAULT_AZ)), + ("custom-cname.example.com:/", ("fs-deadbeef", "/", DEFAULT_AZ)), + ( + "custom-cname.example.com:/some/subpath", + ("fs-deadbeef", "/some/subpath", DEFAULT_AZ), + ), + ( + "custom-cname.example.com:/some/subpath/with:colons", + ("fs-deadbeef", "/some/subpath/with:colons", DEFAULT_AZ), + ), +] +DEFAULT_REGION = "us-east-1" +DEFAULT_NFS_OPTIONS = {} +FS_ID = "fs-deadbeef" +OPTIONS_WITH_AZ = {"az": DEFAULT_AZ} +TEST_SOCKET_GET_ADDR_INFO_RETURN = [ + (socket.AF_INET, socket.SOCK_STREAM, 6, "", ("93.184.216.34", 80)) +] + + +@pytest.fixture(autouse=True) +def setup(mocker): + mocker.patch("mount_efs.get_target_region", return_value=DEFAULT_REGION) + mocker.patch( + "socket.getaddrinfo", + return_value=TEST_SOCKET_GET_ADDR_INFO_RETURN, + ) + + +def _get_mock_config( + dns_name_format="{az}.{fs_id}.efs.{region}.{dns_name_suffix}", + dns_name_suffix="amazonaws.com", + cloudwatch_enabled="false", + has_fallback_to_mount_target_ip_address_item=True, + fallback_to_mount_target_ip_address=False, +): + try: + config = ConfigParser.SafeConfigParser() + except AttributeError: + config = ConfigParser() + config.add_section(mount_efs.CONFIG_SECTION) + config.add_section(mount_efs.CLOUDWATCH_LOG_SECTION) + config.set(mount_efs.CONFIG_SECTION, "dns_name_format", dns_name_format) + config.set(mount_efs.CONFIG_SECTION, "dns_name_suffix", dns_name_suffix) + config.set(mount_efs.CLOUDWATCH_LOG_SECTION, "enabled", cloudwatch_enabled) + if has_fallback_to_mount_target_ip_address_item: + config.set( + mount_efs.CONFIG_SECTION, + mount_efs.FALLBACK_TO_MOUNT_TARGET_IP_ADDRESS_ITEM, + str(fallback_to_mount_target_ip_address), + ) + + return config + + +def test_match_device_correct_descriptors_fs_id(mocker): + config = _get_mock_config() + for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_FS_ID: + assert (fs_id, path, az) == mount_efs.match_device( + config, device, DEFAULT_NFS_OPTIONS + ) + + +def _mock_getaddrinfo_return(canonname, family=socket.AF_INET, ip="93.184.216.34"): + """Helper to build a mock getaddrinfo return value with AI_CANONNAME.""" + sockaddr = (ip, 0) if family == socket.AF_INET else (ip, 0, 0, 0) + return [(family, socket.SOCK_STREAM, 6, canonname, sockaddr)] + + +def test_match_device_correct_descriptors_cname_dns_suffix_override_region(mocker): + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=("fs-deadbeef.efs.cn-north-1.amazonaws.com.cn", None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return( + "fs-deadbeef.efs.cn-north-1.amazonaws.com.cn" + ), + ) + config = _get_mock_config() + for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: + assert (fs_id, path, az) == mount_efs.match_device( + config, device, DEFAULT_NFS_OPTIONS + ) + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + def test_match_device_correct_descriptors_cname_dns_adc_suffix(mocker): """ADC regions use DNS suffixes with hyphens (e.g. cloud.adc-e.uk)""" adc_dns_name = "fs-deadbeef.efs.eu-isoe-west-1.cloud.adc-e.uk" @@ -125,9 +133,9 @@ def test_match_device_correct_descriptors_cname_dns_adc_suffix(mocker): "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", return_value=(adc_dns_name, None), ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", - return_value=(adc_dns_name, [], None), + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return(adc_dns_name), ) config = _get_mock_config(dns_name_suffix="cloud.adc-e.uk") for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: @@ -135,290 +143,367 @@ def test_match_device_correct_descriptors_cname_dns_adc_suffix(mocker): config, device, DEFAULT_NFS_OPTIONS ) utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_correct_descriptors_cname_dns_primary(mocker): - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", - return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", [], None), - ) - config = _get_mock_config() - for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: - assert (fs_id, path, az) == mount_efs.match_device( - config, device, DEFAULT_NFS_OPTIONS - ) - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_correct_descriptors_cname_dns_secondary(mocker): - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", - return_value=(None, ["fs-deadbeef.efs.us-east-1.amazonaws.com"], None), - ) - config = _get_mock_config() - for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: - assert (fs_id, path, az) == mount_efs.match_device( - config, device, DEFAULT_NFS_OPTIONS - ) - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_correct_descriptors_cname_dns_tertiary(mocker): - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", - return_value=(None, [None, "fs-deadbeef.efs.us-east-1.amazonaws.com"], None), - ) - config = _get_mock_config() - for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: - assert (fs_id, path, az) == mount_efs.match_device( - config, device, DEFAULT_NFS_OPTIONS - ) - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_correct_descriptors_cname_dns_amongst_invalid(mocker): - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", - return_value=( - "fs-deadbeef.efs.us-west-1.amazonaws.com", - ["fs-deadbeef.efs.us-east-1.amazonaws.com", "invalid-efs-name.example.com"], - None, - ), - ) - config = _get_mock_config() - for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: - assert (fs_id, path, az) == mount_efs.match_device( - config, device, DEFAULT_NFS_OPTIONS - ) - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_unresolvable_domain(mocker, capsys): - mocker.patch("socket.gethostbyname_ex", side_effect=socket.gaierror) - config = _get_mock_config() - with pytest.raises(SystemExit) as ex: - mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) - - assert 0 != ex.value.code - out, err = capsys.readouterr() - assert "Failed to resolve" in err - - -def test_match_device_no_hostnames(mocker, capsys): - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", return_value=(None, [], None) - ) - config = _get_mock_config() - with pytest.raises(SystemExit) as ex: - mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) - - assert 0 != ex.value.code - out, err = capsys.readouterr() - assert "did not resolve to an EFS mount target" in err - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_no_hostnames2(mocker, capsys): - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", return_value=(None, [None, None], None) - ) - config = _get_mock_config() - with pytest.raises(SystemExit) as ex: - mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) - - assert 0 != ex.value.code - out, err = capsys.readouterr() - assert "did not resolve to an EFS mount target" in err - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_resolve_to_invalid_efs_dns_name(mocker, capsys): - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", - return_value=("invalid-efs-name.example.com", [], None), - ) - config = _get_mock_config() - with pytest.raises(SystemExit) as ex: - mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) - - assert 0 != ex.value.code - out, err = capsys.readouterr() - assert "did not resolve to a valid DNS name" in err - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_resolve_to_unexpected_efs_dns_name(mocker, capsys): - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=("fs-deadbeef.efs.us-west-1.amazonaws.com", None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", - return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", [], None), - ) - config = _get_mock_config() - with pytest.raises(SystemExit) as ex: - mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) - - assert 0 != ex.value.code - out, err = capsys.readouterr() - assert "did not resolve to a valid DNS name" in err - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_fqdn_same_as_dns_name(mocker, capsys): - dns_name = "%s.efs.us-east-1.amazonaws.com" % FS_ID - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", return_value=(dns_name, [], None) - ) - efs_fqdn_match = mount_efs.EFS_FQDN_RE.match(dns_name) - assert efs_fqdn_match - assert FS_ID == efs_fqdn_match.group("fs_id") - - config = _get_mock_config() - ( - expected_dns_name, - ip_address, - ) = mount_efs.get_dns_name_and_fallback_mount_target_ip_address( - config, FS_ID, DEFAULT_NFS_OPTIONS - ) - assert dns_name == expected_dns_name - assert None == ip_address - - for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: - assert (fs_id, path, az) == mount_efs.match_device( - config, device, DEFAULT_NFS_OPTIONS - ) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_fqdn_same_as_dns_name_with_az(mocker, capsys): - dns_name = "%s.%s.efs.us-east-1.amazonaws.com" % (DEFAULT_AZ, FS_ID) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", return_value=(dns_name, [], None) - ) - efs_fqdn_match = mount_efs.EFS_FQDN_RE.match(dns_name) - assert efs_fqdn_match - assert FS_ID == efs_fqdn_match.group("fs_id") - - config = _get_mock_config() - ( - expected_dns_name, - ip_address, - ) = mount_efs.get_dns_name_and_fallback_mount_target_ip_address( - config, FS_ID, OPTIONS_WITH_AZ - ) - assert dns_name == expected_dns_name - assert None == ip_address - for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS_WITH_AZ: - assert (fs_id, path, az) == mount_efs.match_device( - config, device, OPTIONS_WITH_AZ - ) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_with_az_dns_name_mount_az_not_in_option(mocker): - # When dns_name is provided for mounting, if the az is not provided in the mount option, also dns_name contains az - # info, verify that the az info returned is equal to the az info in the dns name - dns_name = "us-east-1a.fs-deadbeef.efs.us-east-1.amazonaws.com" - config = _get_mock_config() - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=(dns_name, None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", return_value=(dns_name, [], None) - ) - fsid, path, az = mount_efs.match_device(config, dns_name, DEFAULT_NFS_OPTIONS) - - assert az == "us-east-1a" - - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_with_az_dns_name_mount_az_in_option(mocker): - # When dns_name is provided for mounting, if the az is provided in the mount option, also dns_name contains az - # info, verify that the az info returned is equal to the az info in the dns name - dns_name = "us-east-1a.fs-deadbeef.efs.us-east-1.amazonaws.com" - config = _get_mock_config() - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=(dns_name, None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", return_value=(dns_name, [], None) - ) - fsid, path, az = mount_efs.match_device(config, dns_name, OPTIONS_WITH_AZ) - - assert az == "us-east-1a" - - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_with_dns_name_mount_az_in_option(mocker): - # When dns_name is mapping to the az_dns_name, and the az field is provided to the option, verify that the az info returned is - # equal to the az info in the dns name - dns_name = "example.random.com" - az_dns_name = "us-east-1a.fs-deadbeef.efs.us-east-1.amazonaws.com" - config = _get_mock_config() - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=(az_dns_name, None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", return_value=(az_dns_name, [], None) - ) - fsid, path, az = mount_efs.match_device(config, dns_name, OPTIONS_WITH_AZ) - - assert az == "us-east-1a" - - utils.assert_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) - - -def test_match_device_with_dns_name_mount_az_in_option_not_match(mocker, capsys): - # When dns_name is mapping to the az_dns_name, and the az field is provided to the option, while the two az value is not - # the same, verify that exception is thrown - dns_name = "example.random.com" - az_dns_name = "us-east-1b.fs-deadbeef.efs.us-east-1.amazonaws.com" - config = _get_mock_config() - get_dns_name_mock = mocker.patch( - "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", - return_value=(az_dns_name, None), - ) - gethostbyname_ex_mock = mocker.patch( - "socket.gethostbyname_ex", return_value=(az_dns_name, [], None) - ) - - with pytest.raises(SystemExit) as ex: - mount_efs.match_device(config, dns_name, OPTIONS_WITH_AZ) - - assert 0 != ex.value.code - out, err = capsys.readouterr() - assert "does not match the az provided" in err - utils.assert_not_called(get_dns_name_mock) - utils.assert_called(gethostbyname_ex_mock) + utils.assert_called(getaddrinfo_mock) + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_correct_descriptors_cname_dns_primary(mocker): + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return( + "fs-deadbeef.efs.us-east-1.amazonaws.com" + ), + ) + config = _get_mock_config() + for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: + assert (fs_id, path, az) == mount_efs.match_device( + config, device, DEFAULT_NFS_OPTIONS + ) + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_correct_descriptors_cname_dns_secondary(mocker): + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return( + "fs-deadbeef.efs.us-east-1.amazonaws.com" + ), + ) + config = _get_mock_config() + for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: + assert (fs_id, path, az) == mount_efs.match_device( + config, device, DEFAULT_NFS_OPTIONS + ) + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_correct_descriptors_cname_dns_tertiary(mocker): + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return( + "fs-deadbeef.efs.us-east-1.amazonaws.com" + ), + ) + config = _get_mock_config() + for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: + assert (fs_id, path, az) == mount_efs.match_device( + config, device, DEFAULT_NFS_OPTIONS + ) + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_correct_descriptors_cname_dns_amongst_invalid(mocker): + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=("fs-deadbeef.efs.us-east-1.amazonaws.com", None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return( + "fs-deadbeef.efs.us-east-1.amazonaws.com" + ), + ) + config = _get_mock_config() + for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: + assert (fs_id, path, az) == mount_efs.match_device( + config, device, DEFAULT_NFS_OPTIONS + ) + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_unresolvable_domain(mocker, capsys): + mocker.patch("socket.getaddrinfo", side_effect=socket.gaierror) + config = _get_mock_config() + with pytest.raises(SystemExit) as ex: + mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) + + assert 0 != ex.value.code + out, err = capsys.readouterr() + assert "Failed to resolve" in err + + +def test_match_device_no_hostnames(mocker, capsys): + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return(""), + ) + config = _get_mock_config() + with pytest.raises(SystemExit) as ex: + mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) + + assert 0 != ex.value.code + out, err = capsys.readouterr() + assert "did not resolve" in err + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_no_hostnames2(mocker, capsys): + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=[ + (socket.AF_INET6, socket.SOCK_STREAM, 6, "", ("::1", 0, 0, 0)), + (socket.AF_INET6, socket.SOCK_STREAM, 6, "", ("::2", 0, 0, 0)), + ], + ) + config = _get_mock_config() + with pytest.raises(SystemExit) as ex: + mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) + + assert 0 != ex.value.code + out, err = capsys.readouterr() + assert "did not resolve" in err + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_resolve_to_invalid_efs_dns_name(mocker, capsys): + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return("invalid-efs-name.example.com"), + ) + config = _get_mock_config() + with pytest.raises(SystemExit) as ex: + mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) + + assert 0 != ex.value.code + out, err = capsys.readouterr() + assert "did not resolve to a valid DNS name" in err + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_resolve_to_unexpected_efs_dns_name(mocker, capsys): + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=("fs-deadbeef.efs.us-west-1.amazonaws.com", None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return( + "fs-deadbeef.efs.us-east-1.amazonaws.com" + ), + ) + config = _get_mock_config() + with pytest.raises(SystemExit) as ex: + mount_efs.match_device(config, "custom-cname.example.com", DEFAULT_NFS_OPTIONS) + + assert 0 != ex.value.code + out, err = capsys.readouterr() + assert "did not resolve to a valid DNS name" in err + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_fqdn_same_as_dns_name(mocker, capsys): + dns_name = "%s.efs.us-east-1.amazonaws.com" % FS_ID + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return(dns_name), + ) + efs_fqdn_match = mount_efs.EFS_FQDN_RE.match(dns_name) + assert efs_fqdn_match + assert FS_ID == efs_fqdn_match.group("fs_id") + + config = _get_mock_config() + ( + expected_dns_name, + ip_address, + ) = mount_efs.get_dns_name_and_fallback_mount_target_ip_address( + config, FS_ID, DEFAULT_NFS_OPTIONS + ) + assert dns_name == expected_dns_name + assert None == ip_address + + for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: + assert (fs_id, path, az) == mount_efs.match_device( + config, device, DEFAULT_NFS_OPTIONS + ) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_fqdn_same_as_dns_name_with_az(mocker, capsys): + dns_name = "%s.%s.efs.us-east-1.amazonaws.com" % (DEFAULT_AZ, FS_ID) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return(dns_name), + ) + efs_fqdn_match = mount_efs.EFS_FQDN_RE.match(dns_name) + assert efs_fqdn_match + assert FS_ID == efs_fqdn_match.group("fs_id") + + config = _get_mock_config() + ( + expected_dns_name, + ip_address, + ) = mount_efs.get_dns_name_and_fallback_mount_target_ip_address( + config, FS_ID, OPTIONS_WITH_AZ + ) + assert dns_name == expected_dns_name + assert None == ip_address + for device, (fs_id, path, az) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS_WITH_AZ: + assert (fs_id, path, az) == mount_efs.match_device( + config, device, OPTIONS_WITH_AZ + ) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_with_az_dns_name_mount_az_not_in_option(mocker): + # When dns_name is provided for mounting, if the az is not provided in the mount option, also dns_name contains az + # info, verify that the az info returned is equal to the az info in the dns name + dns_name = "us-east-1a.fs-deadbeef.efs.us-east-1.amazonaws.com" + config = _get_mock_config() + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=(dns_name, None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return(dns_name), + ) + fsid, path, az = mount_efs.match_device(config, dns_name, DEFAULT_NFS_OPTIONS) + + assert az == "us-east-1a" + + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_with_az_dns_name_mount_az_in_option(mocker): + # When dns_name is provided for mounting, if the az is provided in the mount option, also dns_name contains az + # info, verify that the az info returned is equal to the az info in the dns name + dns_name = "us-east-1a.fs-deadbeef.efs.us-east-1.amazonaws.com" + config = _get_mock_config() + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=(dns_name, None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return(dns_name), + ) + fsid, path, az = mount_efs.match_device(config, dns_name, OPTIONS_WITH_AZ) + + assert az == "us-east-1a" + + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_with_dns_name_mount_az_in_option(mocker): + # When dns_name is mapping to the az_dns_name, and the az field is provided to the option, verify that the az info returned is + # equal to the az info in the dns name + dns_name = "example.random.com" + az_dns_name = "us-east-1a.fs-deadbeef.efs.us-east-1.amazonaws.com" + config = _get_mock_config() + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=(az_dns_name, None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return(az_dns_name), + ) + fsid, path, az = mount_efs.match_device(config, dns_name, OPTIONS_WITH_AZ) + + assert az == "us-east-1a" + + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_with_dns_name_mount_az_in_option_not_match(mocker, capsys): + # When dns_name is mapping to the az_dns_name, and the az field is provided to the option, while the two az value is not + # the same, verify that exception is thrown + dns_name = "example.random.com" + az_dns_name = "us-east-1b.fs-deadbeef.efs.us-east-1.amazonaws.com" + config = _get_mock_config() + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=(az_dns_name, None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return(az_dns_name), + ) + + with pytest.raises(SystemExit) as ex: + mount_efs.match_device(config, dns_name, OPTIONS_WITH_AZ) + + assert 0 != ex.value.code + out, err = capsys.readouterr() + assert "does not match the az provided" in err + utils.assert_not_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_ipv6_only_mount_target_resolves_via_fqdn(mocker): + """When an FQDN resolves to an IPv6-only mount target, match_device should + succeed using getaddrinfo (AF_INET6) instead of failing like gethostbyname_ex would. + """ + dns_name = "fs-deadbeef.efs.us-east-1.amazonaws.com" + ipv6_addr = "2600:1f16:1090:8802:228c:6404:76f8:e3c5" + config = _get_mock_config() + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=(dns_name, None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return( + dns_name, family=socket.AF_INET6, ip=ipv6_addr + ), + ) + + for device, ( + expected_fs_id, + expected_path, + _, + ) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS: + assert (expected_fs_id, expected_path, None) == mount_efs.match_device( + config, device, DEFAULT_NFS_OPTIONS + ) + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) + + +def test_match_device_ipv6_only_mount_target_with_az(mocker): + """IPv6-only mount target with AZ in the resolved FQDN.""" + dns_name = "us-east-1a.fs-deadbeef.efs.us-east-1.amazonaws.com" + ipv6_addr = "2600:1f16:1090:8802:228c:6404:76f8:e3c5" + config = _get_mock_config() + get_dns_name_mock = mocker.patch( + "mount_efs.get_dns_name_and_fallback_mount_target_ip_address", + return_value=(dns_name, None), + ) + getaddrinfo_mock = mocker.patch( + "socket.getaddrinfo", + return_value=_mock_getaddrinfo_return( + dns_name, family=socket.AF_INET6, ip=ipv6_addr + ), + ) + + for device, ( + expected_fs_id, + expected_path, + _, + ) in CORRECT_DEVICE_DESCRIPTORS_CNAME_DNS_WITH_AZ: + assert (expected_fs_id, expected_path, DEFAULT_AZ) == mount_efs.match_device( + config, device, OPTIONS_WITH_AZ + ) + utils.assert_called(get_dns_name_mock) + utils.assert_called(getaddrinfo_mock) diff --git a/test/mount_efs_test/test_mount_nfs.py b/test/mount_efs_test/test_mount_nfs.py index d9070846..d3f06dd6 100644 --- a/test/mount_efs_test/test_mount_nfs.py +++ b/test/mount_efs_test/test_mount_nfs.py @@ -331,6 +331,50 @@ def test_mount_nfs_not_retry_on_non_retryable_failure(mocker): utils.assert_not_called(optimize_readahead_window_mock) +def test_mount_nfs_not_retry_access_denied_without_access_point(mocker): + optimize_readahead_window_mock = mocker.patch("mount_efs.optimize_readahead_window") + + mocker.patch( + "subprocess.Popen", side_effect=[common.ACCESS_DENIED_FAILURE_POPEN.mock] + ) + + with pytest.raises(SystemExit) as ex: + mount_efs.mount_nfs( + _get_config(), + DNS_NAME, + "/", + "/mnt", + DEFAULT_OPTIONS, + ) + + assert 0 != ex.value.code + utils.assert_not_called(optimize_readahead_window_mock) + + +def test_mount_nfs_retry_access_denied_with_access_point(mocker): + optimize_readahead_window_mock = mocker.patch("mount_efs.optimize_readahead_window") + + mocker.patch( + "subprocess.Popen", return_value=common.ACCESS_DENIED_FAILURE_POPEN.mock + ) + + options = dict(DEFAULT_OPTIONS) + options["accesspoint"] = "fsap-12345" + + with pytest.raises(SystemExit) as ex: + mount_efs.mount_nfs( + _get_config(), + DNS_NAME, + "/", + "/mnt", + options, + ) + + assert 0 != ex.value.code + assert subprocess.Popen.call_count > 1 + utils.assert_not_called(optimize_readahead_window_mock) + + def test_mount_nfs_failure_after_all_attempts_fail(mocker): optimize_readahead_window_mock = mocker.patch("mount_efs.optimize_readahead_window") mocker.patch( diff --git a/test/mount_efs_test/test_write_stunnel_config_file.py b/test/mount_efs_test/test_write_stunnel_config_file.py index feaaa83d..b8606d0c 100644 --- a/test/mount_efs_test/test_write_stunnel_config_file.py +++ b/test/mount_efs_test/test_write_stunnel_config_file.py @@ -850,3 +850,24 @@ def test_write_stunnel_config_with_ipv6_and_legacy_stunnel(mocker, tmpdir): efs_proxy_enabled=False, ), ) + + +def test_write_stunnel_config_efs_proxy_skips_stunnel_options(mocker, tmpdir): + get_stunnel_options_mock = mocker.patch("mount_efs.get_stunnel_options") + mocker.patch("mount_efs.add_tunnel_ca_options") + + mount_efs.write_stunnel_config_file( + _get_config(mocker), + str(tmpdir), + FS_ID, + MOUNT_POINT, + PORT, + DNS_NAME, + VERIFY_LEVEL, + OCSP_ENABLED, + _get_mount_options_tls(), + DEFAULT_REGION, + efs_proxy_enabled=True, + ) + + get_stunnel_options_mock.assert_not_called()