File: config.py

package info (click to toggle)
python-podman 5.4.0.1-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,140 kB
  • sloc: python: 7,532; makefile: 82; sh: 75
file content (173 lines) | stat: -rw-r--r-- 6,137 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
"""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


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"))


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