Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions samcli/local/docker/platform_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,61 @@ def _read_config(self) -> Optional[str]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add a basic e2e test with rootless setup to see if it actually works.
Unit test i feel is not sufficient to check system related configs.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will also check the networking works as we probably will need to pull some container.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My one concern is that an added integration test for this situation will end up adding a lot of complexity to our testing structure, especially because this is a niche situation that requires a non-trivial amount of changes to the environment. I can discuss this further with the team.

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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs integ test to check builds work too in rootless mode?


On Linux, Finch can use either a Finch-specific socket or the underlying
containerd socket via nerdctl.

Priority order:
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 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 Finch-specific socket first
xdg_runtime_dir = os.environ.get("XDG_RUNTIME_DIR")
if xdg_runtime_dir:
# 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
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.warning("No Finch socket found in standard locations")
return None

def supports_finch(self) -> bool:
"""
Expand Down
122 changes: 108 additions & 14 deletions tests/unit/local/docker/test_platform_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,81 @@ 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.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)
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())
Expand Down Expand Up @@ -268,21 +338,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, expected_path)
mock_system.assert_called_once()
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.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):
Expand Down
Loading