Source code for podman.domain.config

"""Read containers.conf file."""

import sys
import urllib
from pathlib import Path
from typing import Optional
import json

from podman.api import cached_property
from podman.api.path_utils import get_xdg_config_home

if sys.version_info >= (3, 11):
    from tomllib import loads as toml_loads
else:
    try:
        from tomli import loads as toml_loads
    except ImportError:
        try:
            from toml import loads as toml_loads
        except ImportError:
            from pytoml import loads as toml_loads


[docs] class ServiceConnection: """ServiceConnection defines a connection to the Podman service.""" def __init__(self, name: str, attrs: dict[str, str]): """Create a Podman ServiceConnection.""" self.name = name self.attrs = attrs def __repr__(self) -> str: return f"""<{self.__class__.__name__}: '{self.id}'>""" def __hash__(self) -> int: return hash(tuple(self.name)) def __eq__(self, other) -> bool: if isinstance(other, ServiceConnection): return self.id == other.id and self.attrs == other.attrs return False @property def id(self): # pylint: disable=invalid-name """str: Returns identifier for service connection.""" return self.name @cached_property def url(self): """urllib.parse.ParseResult: Returns URL for service connection.""" 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.""" if self.attrs.get("identity"): return Path(self.attrs.get("identity")) return Path(self.attrs.get("Identity"))
[docs] class PodmanConfig: """PodmanConfig provides a representation of the containers.conf file.""" def __init__(self, path: Optional[str] = None): """Read Podman configuration from users XDG_CONFIG_HOME.""" self.is_default = False if path is None: home = Path(get_xdg_config_home()) self.path = home / "containers" / "podman-connections.json" old_toml_file = home / "containers" / "containers.conf" self.is_default = True # this elif is only for testing purposes elif "@@is_test@@" in path: test_path = path.replace("@@is_test@@", '') self.path = Path(test_path) / "podman-connections.json" old_toml_file = Path(test_path) / "containers.conf" self.is_default = True else: self.path = Path(path) old_toml_file = None self.attrs = {} if self.path.exists(): try: with open(self.path, encoding='utf-8') as file: self.attrs = json.load(file) except Exception: # if the user specifies a path, it can either be a JSON file # or a TOML file - so try TOML next try: with self.path.open(encoding='utf-8') as file: buffer = file.read() loaded_toml = toml_loads(buffer) self.attrs.update(loaded_toml) except Exception as e: raise AttributeError( "The path given is neither a JSON nor a TOML connections file" ) from e # Read the old toml file configuration if self.is_default and old_toml_file.exists(): with old_toml_file.open(encoding='utf-8') as file: buffer = file.read() loaded_toml = toml_loads(buffer) self.attrs.update(loaded_toml) def __hash__(self) -> int: return hash(tuple(self.path.name)) def __eq__(self, other) -> bool: if isinstance(other, PodmanConfig): return self.id == other.id and self.attrs == other.attrs return False @property def id(self): # pylint: disable=invalid-name """Path: Returns Path() of container.conf.""" return self.path @cached_property def services(self): """dict[str, ServiceConnection]: Returns list of service connections. Examples: podman_config = PodmanConfig() address = podman_config.services["testing"] print(f"Testing service address {address}") """ services: dict[str, ServiceConnection] = {} # read the keys of the toml file first engine = self.attrs.get("engine") if engine: destinations = engine.get("service_destinations") for key in destinations: connection = ServiceConnection(key, attrs=destinations[key]) services[key] = connection # read the keys of the json file next # this will ensure that if the new json file and the old toml file # has a connection with the same name defined, we always pick the # json one 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") return ServiceConnection(active, attrs=destinations[active]) return None