-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker.py
More file actions
109 lines (90 loc) · 3.46 KB
/
docker.py
File metadata and controls
109 lines (90 loc) · 3.46 KB
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
"""Docker client wrapper module."""
import os
import sys
from hashlib import sha256
from pathlib import Path
from python_on_whales import DockerClient
from python_on_whales.client_config import ClientNotFoundError
from codesectools.utils import PACKAGE_DIR, USER_DIR
UID = os.getuid()
GID = os.getgid()
class Docker(DockerClient):
"""Wrapper around DockerClient to handle initialization errors."""
def __init__(self) -> None:
"""Initialize the Docker client and verify availability."""
try:
super().__init__()
self.info()
except ClientNotFoundError as e:
print(e)
sys.exit(1)
class AnalysisEnvironment:
"""Manage the Docker environment for code analysis."""
build_args = {"UID": str(UID), "GID": str(GID)}
def __init__(self, isolation: bool) -> None:
"""Initialize the analysis environment."""
self.isolation = isolation
self.docker = Docker()
self.dockerfile = PACKAGE_DIR.parent / "Dockerfile"
self.name = "codesectools"
def build(self) -> None:
"""Build the Docker image if it does not exist or if the file hash changed."""
file_hash = sha256(self.dockerfile.read_bytes()).hexdigest()
if not self.docker.images(
all=True, filters={"label": f"file_hash={file_hash}"}
):
self.docker.image.build(
PACKAGE_DIR.parent,
tags=self.name,
labels={"file_hash": file_hash},
file=self.dockerfile,
build_args=self.build_args,
)
def start(self, target: Path) -> None:
"""Start the Docker container and attach to it.
Build the image if necessary, then create and start a container
with the target directory mounted. If a container for the target
already exists, it starts and attaches to it.
Args:
target: The path to the directory to mount in the container.
"""
self.build()
if self.isolation:
target_container_name = f"codesectools-{target.name}-isolated"
else:
target_container_name = f"codesectools-{target.name}"
target_container_home = Path("/home/codesectools")
target_container_workdir = target_container_home / target.name
if containers := self.docker.ps(
all=True,
filters=[
("name", "codesectools-*"),
("label", f"target={target.resolve()}"),
("label", f"isolation={self.isolation}"),
], # ty:ignore[invalid-argument-type]
):
container = containers[0]
if not container.state.running:
self.docker.start(container)
container.execute(
["/bin/bash"],
interactive=True,
tty=True,
)
else:
container = self.docker.run(
self.name,
name=target_container_name,
command=["/bin/bash"],
labels={
"target": str(target.resolve()),
"isolation": str(self.isolation),
},
networks=["none"] if self.isolation else [],
volumes=[
(target, target_container_workdir),
(USER_DIR, target_container_home / USER_DIR.name),
],
interactive=True,
tty=True,
)