comparison sat/plugins/plugin_app_manager_docker/__init__.py @ 3373:f44402f8a81f

plugin app managed docker: handle Docker application with App Manager
author Goffi <goffi@goffi.org>
date Mon, 28 Sep 2020 21:10:33 +0200
parents
children 3b08caa805e7
comparison
equal deleted inserted replaced
3372:5d926c7b0d99 3373:f44402f8a81f
1 #!/usr/bin/env python3
2
3 # SàT plugin to manage Docker
4 # Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org)
5
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
15
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 from pathlib import Path
20 from twisted.python.procutils import which
21 from sat.core.i18n import _
22 from sat.core.constants import Const as C
23 from sat.core import exceptions
24 from sat.core.log import getLogger
25 from sat.tools.common import async_process
26
27 log = getLogger(__name__)
28
29
30 PLUGIN_INFO = {
31 C.PI_NAME: "Docker Applications Manager",
32 C.PI_IMPORT_NAME: "APP_MANAGER_DOCKER",
33 C.PI_TYPE: C.PLUG_TYPE_MISC,
34 C.PI_MODES: C.PLUG_MODE_BOTH,
35 C.PI_DEPENDENCIES: ["APP_MANAGER"],
36 C.PI_MAIN: "AppManagerDocker",
37 C.PI_HANDLER: "no",
38 C.PI_DESCRIPTION: _(
39 """Applications Manager for Docker"""),
40 }
41
42
43 class AppManagerDocker:
44 name = "docker-compose"
45 discover_path = Path(__file__).parent
46
47 def __init__(self, host):
48 log.info(_("Docker App Manager initialization"))
49 try:
50 self.docker_compose_path = which('docker-compose')[0]
51 except IndexError:
52 raise exceptions.NotFound(
53 '"docker-compose" executable not found, Docker can\'t used with '
54 'application manager')
55 self.host = host
56 self._am = host.plugins['APP_MANAGER']
57 self._am.register(self)
58
59 async def start(self, app_data: dict) -> None:
60 await self._am.startCommon(app_data)
61 working_dir = app_data['_instance_dir_path']
62 try:
63 override = app_data['override']
64 except KeyError:
65 pass
66 else:
67 log.debug("writting override file")
68 override_path = working_dir / "docker-compose.override.yml"
69 with override_path.open("w") as f:
70 self._am.dump(override, f)
71 await async_process.run(
72 self.docker_compose_path,
73 "up",
74 "--detach",
75 path=str(working_dir),
76 )
77
78 async def stop(self, app_data: dict) -> None:
79 working_dir = app_data['_instance_dir_path']
80 await async_process.run(
81 self.docker_compose_path,
82 "down",
83 path=str(working_dir),
84 )
85
86 async def computeExpose(self, app_data: dict) -> dict:
87 working_dir = app_data['_instance_dir_path']
88 expose = app_data['expose']
89 ports = expose.get('ports', {})
90 for name, port_data in list(ports.items()):
91 try:
92 service = port_data['service']
93 private = port_data['private']
94 int(private)
95 except (KeyError, ValueError):
96 log.warning(
97 f"invalid value found for {name!r} port in {app_data['_file_path']}")
98 continue
99 exposed_port = await async_process.run(
100 self.docker_compose_path,
101 "port",
102 service,
103 str(private),
104 path=str(working_dir),
105 )
106 exposed_port = exposed_port.decode().strip()
107 try:
108 addr, port = exposed_port.split(':')
109 int(port)
110 except ValueError:
111 log.warning(
112 f"invalid exposed port for {name}, ignoring: {exposed_port!r}")
113 del ports[name]
114 else:
115 ports[name] = exposed_port