From 2a8375b4ef0e1979304806d85875f9e037526e77 Mon Sep 17 00:00:00 2001 From: Robert Bradley Date: Thu, 12 Feb 2026 20:43:45 +0000 Subject: [PATCH 1/8] fix: Improve Finch socket detection on Linux for rootless containerd Updated LinuxHandler.get_finch_socket_path() to check multiple standard socket locations instead of using a hardcoded path. --- samcli/local/docker/platform_config.py | 48 +++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/samcli/local/docker/platform_config.py b/samcli/local/docker/platform_config.py index 1d95b803b0..2cbf8081d5 100644 --- a/samcli/local/docker/platform_config.py +++ b/samcli/local/docker/platform_config.py @@ -123,11 +123,49 @@ def _read_config(self) -> Optional[str]: def get_finch_socket_path(self) -> Optional[str]: """ - Returns the socket path for Linux. - """ - - # Default fallback to system socket - return "unix:///var/run/finch.sock" + Returns the socket path for Linux, checking multiple locations. + + Priority order: + 1. XDG_RUNTIME_DIR/containerd/containerd.sock (rootless containerd) + 2. XDG_RUNTIME_DIR/finch.sock (Finch-specific) + 3. ~/.finch/finch.sock (user home directory) + 4. /var/run/finch.sock (system-wide) + + Returns: + Optional[str]: Socket path if found, None otherwise + """ + + # Check XDG_RUNTIME_DIR for rootless containerd + xdg_runtime_dir = os.environ.get("XDG_RUNTIME_DIR") + if xdg_runtime_dir: + # Rootless containerd socket (most common on Linux) + containerd_sock = os.path.join(xdg_runtime_dir, "containerd", "containerd.sock") + if os.path.exists(containerd_sock): + LOG.debug(f"Found Finch socket at XDG_RUNTIME_DIR: {containerd_sock}") + return f"unix://{containerd_sock}" + + # Finch-specific socket in XDG_RUNTIME_DIR + finch_sock = os.path.join(xdg_runtime_dir, "finch.sock") + if os.path.exists(finch_sock): + LOG.debug(f"Found Finch socket at XDG_RUNTIME_DIR: {finch_sock}") + return f"unix://{finch_sock}" + + # Check user home directory for Finch VM socket + home_dir = os.path.expanduser("~") + home_finch_sock = os.path.join(home_dir, ".finch", "finch.sock") + if os.path.exists(home_finch_sock): + LOG.debug(f"Found Finch socket in home directory: {home_finch_sock}") + return f"unix://{home_finch_sock}" + + # System-wide socket (fallback) + system_sock = "/var/run/finch.sock" + if os.path.exists(system_sock): + LOG.debug(f"Found Finch socket at system location: {system_sock}") + return f"unix://{system_sock}" + + # No socket found - return None to enable future CLI fallback + LOG.debug("No Finch socket found in standard locations") + return None def supports_finch(self) -> bool: """ From f8d0c45cb727a4d0fe85d3bfc7fff8f23bdb3979 Mon Sep 17 00:00:00 2001 From: Robert Bradley Date: Thu, 12 Feb 2026 20:46:28 +0000 Subject: [PATCH 2/8] test: Update tests for improved Finch socket detection Added comprehensive tests for all socket detection scenarios on Linux. --- .../unit/local/docker/test_platform_config.py | 105 +++++++++++++++--- 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/tests/unit/local/docker/test_platform_config.py b/tests/unit/local/docker/test_platform_config.py index c235c1c6ec..96e757cef1 100644 --- a/tests/unit/local/docker/test_platform_config.py +++ b/tests/unit/local/docker/test_platform_config.py @@ -159,11 +159,64 @@ def test_read_config_not_implemented(self): result = self.handler.read_config() self.assertIsNone(result) - def test_get_finch_socket_path(self): - """Test Linux Finch socket path""" + @patch("os.path.exists") + @patch.dict("os.environ", {"XDG_RUNTIME_DIR": "/run/user/1001"}) + def test_get_finch_socket_path_xdg_containerd(self, mock_exists): + """Test Linux Finch socket path with XDG_RUNTIME_DIR containerd socket""" + # Mock that containerd socket exists + def exists_side_effect(path): + return path == "/run/user/1001/containerd/containerd.sock" + + mock_exists.side_effect = exists_side_effect + result = self.handler.get_finch_socket_path() + self.assertEqual(result, "unix:///run/user/1001/containerd/containerd.sock") + + @patch("os.path.exists") + @patch.dict("os.environ", {"XDG_RUNTIME_DIR": "/run/user/1001"}) + def test_get_finch_socket_path_xdg_finch(self, mock_exists): + """Test Linux Finch socket path with XDG_RUNTIME_DIR finch socket""" + # Mock that finch socket exists (but not containerd) + def exists_side_effect(path): + return path == "/run/user/1001/finch.sock" + + mock_exists.side_effect = exists_side_effect + result = self.handler.get_finch_socket_path() + self.assertEqual(result, "unix:///run/user/1001/finch.sock") + + @patch("os.path.exists") + @patch("os.path.expanduser") + @patch.dict("os.environ", {}, clear=True) + def test_get_finch_socket_path_home_directory(self, mock_expanduser, mock_exists): + """Test Linux Finch socket path in home directory""" + mock_expanduser.return_value = "/home/testuser" + + # Mock that home finch socket exists + def exists_side_effect(path): + return path == "/home/testuser/.finch/finch.sock" + + mock_exists.side_effect = exists_side_effect + result = self.handler.get_finch_socket_path() + self.assertEqual(result, "unix:///home/testuser/.finch/finch.sock") + + @patch("os.path.exists") + @patch.dict("os.environ", {}, clear=True) + def test_get_finch_socket_path_system_socket(self, mock_exists): + """Test Linux Finch socket path at system location""" + # Mock that system socket exists + def exists_side_effect(path): + return path == "/var/run/finch.sock" + + mock_exists.side_effect = exists_side_effect result = self.handler.get_finch_socket_path() self.assertEqual(result, "unix:///var/run/finch.sock") + @patch("os.path.exists", return_value=False) + @patch.dict("os.environ", {}, clear=True) + def test_get_finch_socket_path_not_found(self, mock_exists): + """Test Linux Finch socket path when no socket exists""" + result = self.handler.get_finch_socket_path() + self.assertIsNone(result) + def test_supports_finch(self): """Test that Linux supports Finch""" self.assertTrue(self.handler.supports_finch()) @@ -268,21 +321,45 @@ def test_returns_none_for_unsupported_platform(self, mock_system): class TestGetFinchSocketPath(unittest.TestCase): """Tests for get_finch_socket_path utility function""" - @parameterized.expand( - [ - ("Linux", "unix:///var/run/finch.sock"), - ("Darwin", "unix:////Applications/Finch/lima/data/finch/sock/finch.sock"), - ("Windows", None), - ] - ) + @patch("os.path.exists") @patch("samcli.local.docker.platform_config.platform.system") - def test_get_finch_socket_path_returns_correct_path(self, platform_name, expected_path, mock_system): - """Test that get_finch_socket_path returns the correct path based on platform""" - mock_system.return_value = platform_name + def test_get_finch_socket_path_linux_with_system_socket(self, mock_system, mock_exists): + """Test that get_finch_socket_path returns system socket path on Linux when it exists""" + mock_system.return_value = "Linux" + + # Mock that system socket exists + def exists_side_effect(path): + return path == "/var/run/finch.sock" + + mock_exists.side_effect = exists_side_effect + + result = get_finch_socket_path() + self.assertEqual(result, "unix:///var/run/finch.sock") + @patch("os.path.exists", return_value=False) + @patch("samcli.local.docker.platform_config.platform.system") + def test_get_finch_socket_path_linux_no_socket(self, mock_system, mock_exists): + """Test that get_finch_socket_path returns None on Linux when no socket exists""" + mock_system.return_value = "Linux" + result = get_finch_socket_path() - self.assertEqual(result, expected_path) - mock_system.assert_called_once() + self.assertIsNone(result) + + @patch("samcli.local.docker.platform_config.platform.system") + def test_get_finch_socket_path_macos(self, mock_system): + """Test that get_finch_socket_path returns correct path on macOS""" + mock_system.return_value = "Darwin" + + result = get_finch_socket_path() + self.assertEqual(result, "unix:////Applications/Finch/lima/data/finch/sock/finch.sock") + + @patch("samcli.local.docker.platform_config.platform.system") + def test_get_finch_socket_path_windows(self, mock_system): + """Test that get_finch_socket_path returns None on Windows""" + mock_system.return_value = "Windows" + + result = get_finch_socket_path() + self.assertIsNone(result) @patch("samcli.local.docker.platform_config.get_platform_handler") def test_get_finch_socket_path_returns_none_when_no_handler(self, mock_get_handler): From 58660b5d23c662b30878def4abad8db6c9505ac9 Mon Sep 17 00:00:00 2001 From: Robert Bradley Date: Fri, 13 Feb 2026 08:10:16 +0000 Subject: [PATCH 3/8] fix: Black formatting --- samcli/local/docker/platform_config.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/samcli/local/docker/platform_config.py b/samcli/local/docker/platform_config.py index 2cbf8081d5..eea7fa346e 100644 --- a/samcli/local/docker/platform_config.py +++ b/samcli/local/docker/platform_config.py @@ -124,17 +124,17 @@ def _read_config(self) -> Optional[str]: def get_finch_socket_path(self) -> Optional[str]: """ Returns the socket path for Linux, checking multiple locations. - + Priority order: 1. XDG_RUNTIME_DIR/containerd/containerd.sock (rootless containerd) 2. XDG_RUNTIME_DIR/finch.sock (Finch-specific) 3. ~/.finch/finch.sock (user home directory) 4. /var/run/finch.sock (system-wide) - + Returns: Optional[str]: Socket path if found, None otherwise """ - + # Check XDG_RUNTIME_DIR for rootless containerd xdg_runtime_dir = os.environ.get("XDG_RUNTIME_DIR") if xdg_runtime_dir: @@ -143,26 +143,26 @@ def get_finch_socket_path(self) -> Optional[str]: if os.path.exists(containerd_sock): LOG.debug(f"Found Finch socket at XDG_RUNTIME_DIR: {containerd_sock}") return f"unix://{containerd_sock}" - + # Finch-specific socket in XDG_RUNTIME_DIR finch_sock = os.path.join(xdg_runtime_dir, "finch.sock") if os.path.exists(finch_sock): LOG.debug(f"Found Finch socket at XDG_RUNTIME_DIR: {finch_sock}") return f"unix://{finch_sock}" - + # Check user home directory for Finch VM socket home_dir = os.path.expanduser("~") home_finch_sock = os.path.join(home_dir, ".finch", "finch.sock") if os.path.exists(home_finch_sock): LOG.debug(f"Found Finch socket in home directory: {home_finch_sock}") return f"unix://{home_finch_sock}" - + # System-wide socket (fallback) system_sock = "/var/run/finch.sock" if os.path.exists(system_sock): LOG.debug(f"Found Finch socket at system location: {system_sock}") return f"unix://{system_sock}" - + # No socket found - return None to enable future CLI fallback LOG.debug("No Finch socket found in standard locations") return None From d75e7652c64c556c22296e1d1bda96be4c78150e Mon Sep 17 00:00:00 2001 From: Robert Bradley Date: Fri, 13 Feb 2026 08:11:08 +0000 Subject: [PATCH 4/8] fix: Black formatting --- .../unit/local/docker/test_platform_config.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/unit/local/docker/test_platform_config.py b/tests/unit/local/docker/test_platform_config.py index 96e757cef1..1151e24af9 100644 --- a/tests/unit/local/docker/test_platform_config.py +++ b/tests/unit/local/docker/test_platform_config.py @@ -163,10 +163,11 @@ def test_read_config_not_implemented(self): @patch.dict("os.environ", {"XDG_RUNTIME_DIR": "/run/user/1001"}) def test_get_finch_socket_path_xdg_containerd(self, mock_exists): """Test Linux Finch socket path with XDG_RUNTIME_DIR containerd socket""" + # Mock that containerd socket exists def exists_side_effect(path): return path == "/run/user/1001/containerd/containerd.sock" - + mock_exists.side_effect = exists_side_effect result = self.handler.get_finch_socket_path() self.assertEqual(result, "unix:///run/user/1001/containerd/containerd.sock") @@ -175,10 +176,11 @@ def exists_side_effect(path): @patch.dict("os.environ", {"XDG_RUNTIME_DIR": "/run/user/1001"}) def test_get_finch_socket_path_xdg_finch(self, mock_exists): """Test Linux Finch socket path with XDG_RUNTIME_DIR finch socket""" + # Mock that finch socket exists (but not containerd) def exists_side_effect(path): return path == "/run/user/1001/finch.sock" - + mock_exists.side_effect = exists_side_effect result = self.handler.get_finch_socket_path() self.assertEqual(result, "unix:///run/user/1001/finch.sock") @@ -189,11 +191,11 @@ def exists_side_effect(path): def test_get_finch_socket_path_home_directory(self, mock_expanduser, mock_exists): """Test Linux Finch socket path in home directory""" mock_expanduser.return_value = "/home/testuser" - + # Mock that home finch socket exists def exists_side_effect(path): return path == "/home/testuser/.finch/finch.sock" - + mock_exists.side_effect = exists_side_effect result = self.handler.get_finch_socket_path() self.assertEqual(result, "unix:///home/testuser/.finch/finch.sock") @@ -202,10 +204,11 @@ def exists_side_effect(path): @patch.dict("os.environ", {}, clear=True) def test_get_finch_socket_path_system_socket(self, mock_exists): """Test Linux Finch socket path at system location""" + # Mock that system socket exists def exists_side_effect(path): return path == "/var/run/finch.sock" - + mock_exists.side_effect = exists_side_effect result = self.handler.get_finch_socket_path() self.assertEqual(result, "unix:///var/run/finch.sock") @@ -326,13 +329,13 @@ class TestGetFinchSocketPath(unittest.TestCase): def test_get_finch_socket_path_linux_with_system_socket(self, mock_system, mock_exists): """Test that get_finch_socket_path returns system socket path on Linux when it exists""" mock_system.return_value = "Linux" - + # Mock that system socket exists def exists_side_effect(path): return path == "/var/run/finch.sock" - + mock_exists.side_effect = exists_side_effect - + result = get_finch_socket_path() self.assertEqual(result, "unix:///var/run/finch.sock") @@ -341,7 +344,7 @@ def exists_side_effect(path): def test_get_finch_socket_path_linux_no_socket(self, mock_system, mock_exists): """Test that get_finch_socket_path returns None on Linux when no socket exists""" mock_system.return_value = "Linux" - + result = get_finch_socket_path() self.assertIsNone(result) @@ -349,7 +352,7 @@ def test_get_finch_socket_path_linux_no_socket(self, mock_system, mock_exists): def test_get_finch_socket_path_macos(self, mock_system): """Test that get_finch_socket_path returns correct path on macOS""" mock_system.return_value = "Darwin" - + result = get_finch_socket_path() self.assertEqual(result, "unix:////Applications/Finch/lima/data/finch/sock/finch.sock") @@ -357,7 +360,7 @@ def test_get_finch_socket_path_macos(self, mock_system): def test_get_finch_socket_path_windows(self, mock_system): """Test that get_finch_socket_path returns None on Windows""" mock_system.return_value = "Windows" - + result = get_finch_socket_path() self.assertIsNone(result) From f50eb0098e1e657a8da23e44fa059865efe24efe Mon Sep 17 00:00:00 2001 From: Robert Bradley Date: Fri, 13 Feb 2026 08:17:12 +0000 Subject: [PATCH 5/8] fix: Update socket path comments and logging in platform_config Update based on PR review comments --- samcli/local/docker/platform_config.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/samcli/local/docker/platform_config.py b/samcli/local/docker/platform_config.py index eea7fa346e..6fb22af5d1 100644 --- a/samcli/local/docker/platform_config.py +++ b/samcli/local/docker/platform_config.py @@ -125,12 +125,20 @@ def get_finch_socket_path(self) -> Optional[str]: """ Returns the socket path for Linux, checking multiple locations. + On Linux, Finch uses nerdctl + containerd as its container runtime. + This method checks for both Finch-specific sockets and the underlying + containerd socket that Finch may use. + Priority order: 1. XDG_RUNTIME_DIR/containerd/containerd.sock (rootless containerd) - 2. XDG_RUNTIME_DIR/finch.sock (Finch-specific) + 2. XDG_RUNTIME_DIR/finch.sock (Finch-specific socket) 3. ~/.finch/finch.sock (user home directory) 4. /var/run/finch.sock (system-wide) + Note: The containerd socket may be shared by multiple container tools + (Finch, nerdctl, etc.). The FinchContainerClient will validate + compatibility when attempting to connect. + Returns: Optional[str]: Socket path if found, None otherwise """ @@ -138,10 +146,11 @@ def get_finch_socket_path(self) -> Optional[str]: # Check XDG_RUNTIME_DIR for rootless containerd xdg_runtime_dir = os.environ.get("XDG_RUNTIME_DIR") if xdg_runtime_dir: - # Rootless containerd socket (most common on Linux) + # Rootless containerd socket - commonly used by Finch on Linux + # Note: This socket may be shared with other containerd-based tools containerd_sock = os.path.join(xdg_runtime_dir, "containerd", "containerd.sock") if os.path.exists(containerd_sock): - LOG.debug(f"Found Finch socket at XDG_RUNTIME_DIR: {containerd_sock}") + LOG.debug(f"Found containerd socket at XDG_RUNTIME_DIR: {containerd_sock}") return f"unix://{containerd_sock}" # Finch-specific socket in XDG_RUNTIME_DIR @@ -164,7 +173,7 @@ def get_finch_socket_path(self) -> Optional[str]: return f"unix://{system_sock}" # No socket found - return None to enable future CLI fallback - LOG.debug("No Finch socket found in standard locations") + LOG.warn("No Finch socket found in standard locations") return None def supports_finch(self) -> bool: From dfc913295f1d7627f5b712dec84b090ddd4ac81c Mon Sep 17 00:00:00 2001 From: Robert Bradley Date: Fri, 13 Feb 2026 12:56:09 +0000 Subject: [PATCH 6/8] fix: Refactor socket path retrieval logic in platform_config.py Rearranged the priority order for socket path checks and updated comments for clarity regarding Finch and containerd socket usage. --- samcli/local/docker/platform_config.py | 41 ++++++++++++++------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/samcli/local/docker/platform_config.py b/samcli/local/docker/platform_config.py index 6fb22af5d1..001ad3af3e 100644 --- a/samcli/local/docker/platform_config.py +++ b/samcli/local/docker/platform_config.py @@ -125,34 +125,26 @@ def get_finch_socket_path(self) -> Optional[str]: """ Returns the socket path for Linux, checking multiple locations. - On Linux, Finch uses nerdctl + containerd as its container runtime. - This method checks for both Finch-specific sockets and the underlying - containerd socket that Finch may use. + On Linux, Finch can use either a Finch-specific socket or the underlying + containerd socket via nerdctl. Priority order: - 1. XDG_RUNTIME_DIR/containerd/containerd.sock (rootless containerd) - 2. XDG_RUNTIME_DIR/finch.sock (Finch-specific socket) - 3. ~/.finch/finch.sock (user home directory) - 4. /var/run/finch.sock (system-wide) + 1. XDG_RUNTIME_DIR/finch.sock (Finch-specific socket) + 2. ~/.finch/finch.sock (user home directory) + 3. /var/run/finch.sock (system-wide) + 4. XDG_RUNTIME_DIR/containerd/containerd.sock (rootless containerd fallback) - Note: The containerd socket may be shared by multiple container tools - (Finch, nerdctl, etc.). The FinchContainerClient will validate - compatibility when attempting to connect. + Note: The containerd socket is checked last as a fallback since it may be + shared by multiple container tools. Finch-specific sockets are preferred + for accurate telemetry reporting. Returns: Optional[str]: Socket path if found, None otherwise """ - # Check XDG_RUNTIME_DIR for rootless containerd + # Check XDG_RUNTIME_DIR for Finch-specific socket first xdg_runtime_dir = os.environ.get("XDG_RUNTIME_DIR") if xdg_runtime_dir: - # Rootless containerd socket - commonly used by Finch on Linux - # Note: This socket may be shared with other containerd-based tools - containerd_sock = os.path.join(xdg_runtime_dir, "containerd", "containerd.sock") - if os.path.exists(containerd_sock): - LOG.debug(f"Found containerd socket at XDG_RUNTIME_DIR: {containerd_sock}") - return f"unix://{containerd_sock}" - # Finch-specific socket in XDG_RUNTIME_DIR finch_sock = os.path.join(xdg_runtime_dir, "finch.sock") if os.path.exists(finch_sock): @@ -166,12 +158,23 @@ def get_finch_socket_path(self) -> Optional[str]: LOG.debug(f"Found Finch socket in home directory: {home_finch_sock}") return f"unix://{home_finch_sock}" - # System-wide socket (fallback) + # System-wide socket system_sock = "/var/run/finch.sock" if os.path.exists(system_sock): LOG.debug(f"Found Finch socket at system location: {system_sock}") return f"unix://{system_sock}" + # Fallback: Check for rootless containerd socket + # This is checked last since containerd may be used by other tools + if xdg_runtime_dir: + containerd_sock = os.path.join(xdg_runtime_dir, "containerd", "containerd.sock") + if os.path.exists(containerd_sock): + LOG.debug( + f"Found containerd socket at XDG_RUNTIME_DIR (fallback): {containerd_sock}. " + "Note: This socket may be shared with other containerd-based tools." + ) + return f"unix://{containerd_sock}" + # No socket found - return None to enable future CLI fallback LOG.warn("No Finch socket found in standard locations") return None From a4e2969a1e30c799475579f1cb446aa82b03c0fb Mon Sep 17 00:00:00 2001 From: Robert Bradley Date: Fri, 13 Feb 2026 12:57:15 +0000 Subject: [PATCH 7/8] fix: Implement test for finch socket path priority Add test for finch.sock preference over containerd.sock --- tests/unit/local/docker/test_platform_config.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/unit/local/docker/test_platform_config.py b/tests/unit/local/docker/test_platform_config.py index 1151e24af9..a0ae21bfae 100644 --- a/tests/unit/local/docker/test_platform_config.py +++ b/tests/unit/local/docker/test_platform_config.py @@ -185,6 +185,20 @@ def exists_side_effect(path): result = self.handler.get_finch_socket_path() self.assertEqual(result, "unix:///run/user/1001/finch.sock") + @patch("os.path.exists") + @patch.dict("os.environ", {"XDG_RUNTIME_DIR": "/run/user/1001"}) + def test_get_finch_socket_path_priority_finch_over_containerd(self, mock_exists): + """Test that finch.sock is preferred over containerd.sock when both exist""" + + # Mock that BOTH sockets exist + def exists_side_effect(path): + return path in ["/run/user/1001/finch.sock", "/run/user/1001/containerd/containerd.sock"] + + mock_exists.side_effect = exists_side_effect + result = self.handler.get_finch_socket_path() + # Should prefer finch.sock for accurate telemetry + self.assertEqual(result, "unix:///run/user/1001/finch.sock") + @patch("os.path.exists") @patch("os.path.expanduser") @patch.dict("os.environ", {}, clear=True) From 123ef89650e5f4f37bb27032df28893fe4fa8f67 Mon Sep 17 00:00:00 2001 From: Robert Bradley Date: Mon, 16 Feb 2026 09:46:21 +0000 Subject: [PATCH 8/8] fix: Log warn deprecation --- samcli/local/docker/platform_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samcli/local/docker/platform_config.py b/samcli/local/docker/platform_config.py index 001ad3af3e..95fae24c45 100644 --- a/samcli/local/docker/platform_config.py +++ b/samcli/local/docker/platform_config.py @@ -176,7 +176,7 @@ def get_finch_socket_path(self) -> Optional[str]: return f"unix://{containerd_sock}" # No socket found - return None to enable future CLI fallback - LOG.warn("No Finch socket found in standard locations") + LOG.warning("No Finch socket found in standard locations") return None def supports_finch(self) -> bool: