1import time
2import uuid
3
4import attr
5from saltfactories.daemons.container import SaltMinion
6from saltfactories.utils import ports
7from tests.support.runtests import RUNTIME_VARS
8
9
10@attr.s(kw_only=True, slots=True)
11class SaltVirtMinionContainerFactory(SaltMinion):
12
13    host_uuid = attr.ib(default=attr.Factory(uuid.uuid4))
14    ssh_port = attr.ib(
15        default=attr.Factory(ports.get_unused_localhost_port), repr=False
16    )
17    sshd_port = attr.ib(default=attr.Factory(ports.get_unused_localhost_port))
18    libvirt_tcp_port = attr.ib(
19        default=attr.Factory(ports.get_unused_localhost_port), repr=False
20    )
21    libvirt_tls_port = attr.ib(
22        default=attr.Factory(ports.get_unused_localhost_port), repr=False
23    )
24
25    uri = attr.ib(init=False)
26    ssh_uri = attr.ib(init=False)
27    tcp_uri = attr.ib(init=False)
28    tls_uri = attr.ib(init=False)
29
30    def __attrs_post_init__(self):
31        self.uri = "localhost:{}".format(self.sshd_port)
32        self.ssh_uri = "qemu+ssh://{}/system".format(self.uri)
33        self.tcp_uri = "qemu+tcp://localhost:{}/system".format(self.libvirt_tcp_port)
34        self.tls_uri = "qemu+tls://127.0.0.1:{}/system".format(self.libvirt_tls_port)
35
36        # pylint: disable=access-member-before-definition
37        if self.check_ports is None:
38            self.check_ports = []
39        # pylint: enable=access-member-before-definition
40        self.check_ports.extend(
41            [self.sshd_port, self.libvirt_tcp_port, self.libvirt_tls_port]
42        )
43        if "environment" not in self.container_run_kwargs:
44            self.container_run_kwargs["environment"] = {}
45        self.container_run_kwargs["environment"].update(
46            {
47                "SSH_PORT": str(self.ssh_port),
48                "SSHD_PORT": str(self.sshd_port),
49                "LIBVIRT_TCP_PORT": str(self.libvirt_tcp_port),
50                "LIBVIRT_TLS_PORT": str(self.libvirt_tls_port),
51                "NO_START_MINION": "1",
52                "HOST_UUID": self.host_uuid,
53                "PYTHONDONTWRITEBYTECODE": "1",
54            }
55        )
56        if "ports" not in self.container_run_kwargs:
57            self.container_run_kwargs["ports"] = {}
58        self.container_run_kwargs["ports"].update(
59            {
60                "{}/tcp".format(self.ssh_port): self.ssh_port,
61                "{}/tcp".format(self.sshd_port): self.sshd_port,
62                "{}/tcp".format(self.libvirt_tcp_port): self.libvirt_tcp_port,
63                "{}/tcp".format(self.libvirt_tls_port): self.libvirt_tls_port,
64            }
65        )
66        if "volumes" not in self.container_run_kwargs:
67            self.container_run_kwargs["volumes"] = {}
68        self.container_run_kwargs["volumes"].update(
69            {
70                RUNTIME_VARS.CODE_DIR: {"bind": "/salt", "mode": "z"},
71                RUNTIME_VARS.CODE_DIR: {"bind": RUNTIME_VARS.CODE_DIR, "mode": "z"},
72            }
73        )
74        self.container_run_kwargs["working_dir"] = RUNTIME_VARS.CODE_DIR
75        self.container_run_kwargs["network_mode"] = "host"
76        self.container_run_kwargs["cap_add"] = ["ALL"]
77        self.container_run_kwargs["privileged"] = True
78        super().__attrs_post_init__()
79        self.python_executable = "python3"
80
81    def _container_start_checks(self):
82        # Once we're able to ls the salt-minion script it means the container
83        # has salt installed
84        ret = self.run("ls", "-lah", self.get_script_path())
85        if ret.exitcode == 0:
86            return True
87        time.sleep(1)
88        return False
89