From 47fb3272fa455d2b0be734205217b6f658d1ce36 Mon Sep 17 00:00:00 2001 From: Urvashi Mohnani Date: Mon, 19 Feb 2024 10:11:24 -0500 Subject: [PATCH] Use new json connections file Podman has updated where it will store its system connection information to a new json format file. Add support to podman-py to read from both the new json file and old toml file giving preference to the new json file. Signed-off-by: Urvashi Mohnani --- podman/domain/config.py | 43 ++++++++++++++++++++++++------ podman/tests/unit/test_config.py | 45 +++++++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/podman/domain/config.py b/podman/domain/config.py index 173f3724..d5eeef55 100644 --- a/podman/domain/config.py +++ b/podman/domain/config.py @@ -4,6 +4,7 @@ import urllib from pathlib import Path from typing import Dict, Optional +import json import xdg.BaseDirectory @@ -48,12 +49,16 @@ def id(self): # pylint: disable=invalid-name @cached_property def url(self): """urllib.parse.ParseResult: Returns URL for service connection.""" - return urllib.parse.urlparse(self.attrs.get("uri")) + if self.attrs.get("uri"): + return urllib.parse.urlparse(self.attrs.get("uri")) + return urllib.parse.urlparse(self.attrs.get("URI")) @cached_property def identity(self): """Path: Returns Path to identity file for service connection.""" - return Path(self.attrs.get("identity")) + if self.attrs.get("identity"): + return Path(self.attrs.get("identity")) + return Path(self.attrs.get("Identity")) class PodmanConfig: @@ -64,15 +69,22 @@ def __init__(self, path: Optional[str] = None): if path is None: home = Path(xdg.BaseDirectory.xdg_config_home) - self.path = home / "containers" / "containers.conf" + self.path = home / "containers" / "podman-connections.json" + old_toml_file = home / "containers" / "containers.conf" else: self.path = Path(path) self.attrs = {} if self.path.exists(): - with self.path.open(encoding='utf-8') as file: + with open(self.path, encoding='utf-8') as file: + self.attrs = json.load(file) + + # Read the old toml file configuration + if old_toml_file.exists(): + with old_toml_file.open(encoding='utf-8') as file: buffer = file.read() - self.attrs = toml_loads(buffer) + loaded_toml = toml_loads(buffer) + self.attrs.update(loaded_toml) def __hash__(self) -> int: return hash(tuple(self.path.name)) @@ -105,17 +117,32 @@ def services(self): connection = ServiceConnection(key, attrs=destinations[key]) services[key] = connection + # read the new json file format + connection = self.attrs.get("Connection") + if connection: + destinations = connection.get("Connections") + for key in destinations: + connection = ServiceConnection(key, attrs=destinations[key]) + services[key] = connection + return services @cached_property def active_service(self): """Optional[ServiceConnection]: Returns active connection.""" + # read the new json file format + connection = self.attrs.get("Connection") + if connection: + active = connection.get("Default") + destinations = connection.get("Connections") + return ServiceConnection(active, attrs=destinations[active]) + + # if we are here, that means there was no default in the new json file engine = self.attrs.get("engine") if engine: active = engine.get("active_service") destinations = engine.get("service_destinations") - for key in destinations: - if key == active: - return ServiceConnection(key, attrs=destinations[key]) + return ServiceConnection(active, attrs=destinations[active]) + return None diff --git a/podman/tests/unit/test_config.py b/podman/tests/unit/test_config.py index 7ecb475a..ac57adb9 100644 --- a/podman/tests/unit/test_config.py +++ b/podman/tests/unit/test_config.py @@ -7,7 +7,7 @@ from podman.domain.config import PodmanConfig -class PodmanConfigTestCase(unittest.TestCase): +class PodmanConfigTestCaseTOML(unittest.TestCase): opener = mock.mock_open( read_data=""" [containers] @@ -35,7 +35,7 @@ def setUp(self) -> None: super().setUp() def mocked_open(self, *args, **kwargs): - return PodmanConfigTestCase.opener(self, *args, **kwargs) + return PodmanConfigTestCaseTOML.opener(self, *args, **kwargs) self.mocked_open = mocked_open @@ -49,10 +49,49 @@ def test_connections(self): self.assertEqual(config.active_service.url, expected) self.assertEqual(config.services["production"].identity, Path("/home/root/.ssh/id_rsa")) - PodmanConfigTestCase.opener.assert_called_with( + PodmanConfigTestCaseTOML.opener.assert_called_with( Path("/home/developer/containers.conf"), encoding='utf-8' ) +class PodmanConfigTestCaseJSON(unittest.TestCase): + opener = mock.mock_open( + read_data=""" +{ + "Connection": { + "Default": "testing", + "Connections": { + "testing": { + "URI": "ssh://qe@localhost:2222/run/podman/podman.sock", + "Identity": "/home/qe/.ssh/id_rsa" + } + "production": { + "URI": "ssh://root@localhost:22/run/podman/podman.sock", + "Identity": "/home/root/.ssh/id_rsa" + } + } + }, + "Farm": {} +} +""" + ) + + def setUp(self) -> None: + super().setUp() + + def mocked_open(self, *args, **kwargs): + return PodmanConfigTestCaseJSON.opener(self, *args, **kwargs) + + self.mocked_open = mocked_open + + def test_connections(self): + with mock.patch.multiple(Path, open=self.mocked_open, exists=MagicMock(return_value=True)): + config = PodmanConfig("/home/developer/podman-connections.json") + + self.assertEqual(config.active_service.id, "testing") + + expected = urllib.parse.urlparse("ssh://qe@localhost:2222/run/podman/podman.sock") + self.assertEqual(config.active_service.url, expected) + self.assertEqual(config.services["production"].identity, Path("/home/root/.ssh/id_rsa")) if __name__ == '__main__': unittest.main()