1 from typing
import Dict, Any, List, Tuple, Optional
2 from pathlib
import Path
8 from .process
import command, CommandException
9 from ..statistics
import getTotalSwapMemory
12 class DockerConfigurationException(Exception):
16 def isDockerAvailable() -> None:
19 command([
"docker",
"ps"], ignoreStdout =
True, ignoreStderr =
True)
20 except CommandException:
21 raise RuntimeError(
"Docker not available. Please check that it is properly installed and running on your system.")
24 def networkExists(name: str) -> bool:
31 command([
"docker",
"network",
"inspect", name], ignoreStdout =
True, ignoreStderr =
True)
37 def containerRunning(name: str) -> bool:
39 _, output, _ = command([
"docker",
"ps",
"--format",
"{{.Names}}"], ignoreStderr =
True, ignoreStdout =
True)
40 return name
in output.splitlines()
45 def containerExists(name: str) -> bool:
47 _, output, _ = command([
"docker",
"ps",
"-a",
"--format",
"{{.Names}}"], ignoreStderr =
True, ignoreStdout =
True)
48 return name
in output.splitlines()
53 def createNetwork(name: str) ->
None:
54 if networkExists(name):
57 command([
"docker",
"network",
"create",
"--driver",
"bridge", name], ignoreStdout =
True)
60 def removeNetwork(name: str) ->
None:
61 command([
"docker",
"network",
"rm", name], ignoreStdout =
True, ignoreStderr =
True)
64 def removeImage(image: str) ->
None:
65 command([
"docker",
"image",
"rm", image], ignoreStdout =
True, ignoreStderr =
True)
68 def removeDanglingImages(repository: str, tag: str) ->
None:
69 _, output, _ = command([
"docker",
"image",
"ls", repository,
"--format",
"json"], ignoreStdout =
True, ignoreStderr =
True)
70 images = output.strip().split(
"\n")
76 jsonImg = json.loads(image)
77 if jsonImg[
"Tag"] != tag:
78 removeImage(jsonImg[
"ID"])
81 def imagePull(image: str) ->
None:
82 command([
"docker",
"image",
"pull", image])
93 environ: Dict[str, str],
94 volumes: List[Tuple[str, str]]
101 "docker",
"run",
"-d",
102 "--restart",
"always",
104 "--cap-add",
"SYS_PTRACE",
106 "--memory", f
"{ram}G",
107 "--memory-swap", f
"{ram + swap}G",
108 "--shm-size", f
"{shm}G",
109 "--cpus", f
"{cpuCount}",
113 for key, value
in environ.items():
114 runCommand.extend([
"--env", f
"{key}={value}"])
116 for source, destination
in volumes:
117 runCommand.extend([
"-v", f
"{source}:{destination}"])
120 runCommand.extend([
"--gpus",
"all"])
123 runCommand.append(image)
124 command(runCommand, ignoreStdout =
True, ignoreStderr =
True)
127 def stopContainer(name: str) ->
None:
128 command([
"docker",
"stop", name], ignoreStdout =
True, ignoreStderr =
True)
131 def removeContainer(name: str) ->
None:
132 command([
"docker",
"rm", name], ignoreStdout =
True, ignoreStderr =
True)
135 def manifestInspect(image: str) -> Dict[str, Any]:
136 _, output, _ = command([
"docker",
"manifest",
"inspect", image,
"--verbose"], ignoreStdout =
True)
137 jsonOutput = json.loads(output)
138 if not isinstance(jsonOutput, dict):
139 raise TypeError(f
"Invalid function result type \"{type(jsonOutput)}\". Expected: \"dict\"")
144 def imageInspect(image: str) -> Dict[str, Any]:
145 _, output, _ = command([
"docker",
"image",
"inspect", image], ignoreStdout =
True, ignoreStderr =
True)
146 jsonOutput = json.loads(output)
147 if not isinstance(jsonOutput, list):
148 raise TypeError(f
"Invalid json.loads() result type \"{type(jsonOutput)}\". Expected: \"list\"")
150 if not isinstance(jsonOutput[0], dict):
151 raise TypeError(f
"Invalid function result type \"{type(jsonOutput[0])}\". Expected: \"dict\"")
156 def getResourceLimits() -> Tuple[int, int]:
157 _, output, _ = command([
"docker",
"info",
"--format",
"{{json .}}"], ignoreStdout =
True, ignoreStderr =
True)
158 jsonOutput = json.loads(output)
160 return jsonOutput[
"NCPU"], round(jsonOutput[
"MemTotal"] / (1024 ** 3))
163 def getDockerConfigPath() -> Optional[Path]:
164 if platform.system() ==
"Darwin":
165 return Path.home().joinpath(
"Library",
"Group Containers",
"group.com.docker",
"settings.json")
166 elif platform.system() ==
"Windows":
167 return Path.home().joinpath(
"AppData",
"Roaming",
"Docker",
"settings.json")
168 elif platform.system() ==
"Linux":
169 return Path.home().joinpath(
".docker",
"desktop",
"settings.json")
174 def getDockerSwapLimit() -> int:
175 configPath = getDockerConfigPath()
177 if configPath
is None or not configPath.exists():
178 return getTotalSwapMemory()
180 with configPath.open(
"r")
as configFile:
181 configJson = json.load(configFile)
183 swapLimit = configJson.get(
"swapMiB")
184 if not isinstance(swapLimit, int):
185 return getTotalSwapMemory()
187 return int(swapLimit / 1024)
190 def getContainerImageName(containerName: str) -> str:
191 _, output, _ = command([
"docker",
"inspect",
"--format",
"{{.Config.Image}}", containerName], ignoreStdout =
True, ignoreStderr =
True)
192 return output.strip()
195 def getLogs(name: str, tail: Optional[int], follow: bool, timestamps: bool) ->
None:
196 runCommand = [
"docker",
"logs", name]
197 if isinstance(tail, int):
198 runCommand.extend([
"--tail", str(tail)])
201 runCommand.append(
"-t")
204 runCommand.append(
"-f")
209 def isDockerDesktop() -> bool:
211 _, output, _ = command([
"docker",
"info",
"--format",
"{{json .}}"], ignoreStdout =
True, ignoreStderr =
True)
212 jsonOutput = json.loads(output)
214 clientInfo = jsonOutput.get(
"ClientInfo")
215 if not isinstance(clientInfo, dict):
218 pluginsInfo = clientInfo.get(
"Plugins")
219 if not isinstance(pluginsInfo, dict):
222 versionInfo = pluginsInfo.get(
"Version")
223 if not isinstance(versionInfo, str):
226 return "desktop" in versionInfo
231 def isDaemonFileUpdated() -> bool:
232 daemonFile = Path(
"/etc/docker/daemon.json")
233 cGroupFix =
"native.cgroupdriver=cgroupfs"
235 if not daemonFile.exists():
238 with daemonFile.open(
"r")
as file:
240 config = json.load(file)
241 execOpts = config.get(
"exec-opts", [])
242 return cGroupFix
in execOpts
243 except json.JSONDecodeError:
247 def updateDaemonFile() -> None:
248 daemonFile = Path(
"/etc/docker/daemon.json")
249 cGroupFix =
"native.cgroupdriver=cgroupfs"
250 config: Dict[str, Any] = {}
252 if not daemonFile.exists():
255 with daemonFile.open(
"r")
as file:
257 config = json.load(file)
258 except json.JSONDecodeError:
261 execOpts: List[str] = config.get(
"exec-opts", [])
262 execOpts.append(cGroupFix)
263 config[
"exec-opts"] = execOpts
265 with tempfile.NamedTemporaryFile(
"w", delete =
True)
as tempFile:
266 json.dump(config, tempFile, indent = 4)
267 tempFilePath = tempFile.name
270 command([
"sudo",
"mv", tempFilePath, str(daemonFile)], ignoreStderr =
True, ignoreStdout =
True)
273 def restartDocker() -> None:
274 command([
"sudo",
"systemctl",
"restart",
"docker"], ignoreStderr =
True, ignoreStdout =
True)