forked from justin8/docker-makepkg
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathcontainerBuilder.py
executable file
·192 lines (169 loc) · 6.78 KB
/
containerBuilder.py
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#! /bin/python3 -B
import atexit
import ipaddress
import os
import subprocess
import sys
import netifaces
def eprint(*args, **kwargs):
"""
Shorthand for using print(file=sys.stderr)
"""
print(*args, file=sys.stderr, **kwargs)
class DmakepkgBuilder:
"""
Dockerfile generator
"""
head = ("FROM archlinux/base:latest\nLABEL org.thermicorp.tool=docker-makepkg\nRUN echo -e "
"\"[multilib]\\nInclude = /etc/pacman.d/mirrorlist\" >> /etc/pacman.conf\n"
"RUN pacman --noconfirm -Sy archlinux-keyring && pacman-key --init && "
"pacman-key --populate archlinux\n"
"RUN systemd-machine-id-setup")
tail = ("ADD sudoers /etc/sudoers\n"
"""VOLUME "/src\"\n"""
"ADD run.py /run.py\n"
"""ENTRYPOINT ["/run.py"]\n""")
def __init__(self):
self.pacman_cache_dir = "/var/cache/pacman/pkg"
self.pacman_cache_ip = None
self.pacman_cache_port = "8990"
self.cache = True
self.darkhttpd_process = None
self.docker_build_process = None
@classmethod
def get_docker0_address(cls):
"""
Get the first valid non-link-local IP address on docker0
"""
try:
addresses = netifaces.ifaddresses('docker0')
except:
eprint("No docker0 interface exists. Looks like you don't run docker?")
# we could actually theoretically use an IP from any interface, but I want
# to make sure to not make any holes into existing rule sets that protect
# other interfaces.
sys.exit(1)
else:
# check for IPv4 and IPv6 addresses. We can't use link-local ones though,
# because the address specified for those needs to contain the name
# of the interface that needs to be used to reach the destination address
# and I don't know of a way to predict it in docker.
for (family, address_list) in addresses.items():
if family == netifaces.AF_INET:
for address_dict in address_list:
return ipaddress.ip_address(address_dict["addr"])
elif family == netifaces.AF_INET6:
for address_dict in address_list:
ipv6_address = ipaddress.ip_address(address_dict["addr"])
if not ipv6_address.is_link_local():
return ipaddress.ip_address(address_dict["addr"])
eprint("No suitable address found for the local cache. " +
"Therefore the local cache is disabled.")
return None
@classmethod
def start_docker_build(cls):
"""
Start docker build
"""
args = ["/bin/docker", "build", "--pull", "--no-cache", "--tag=makepkg", os.path.dirname(
os.path.realpath(__file__))]
docker_id=None
process=None
try:
process=subprocess.run(args, capture_output=True)
except:
pass
if process:
if process.returncode:
docker_id=process.stdout.decode("utf-8")
try:
subprocess.run(["/bin/docker", "container", "rm", docker_id])
except:
pass
def pacman_cache_exists(self):
"""
Check if the pacman cache directory exists
"""
return os.path.exists(self.pacman_cache_dir)
def create_dockerfile(self):
"""
Generate the Dockerfile
"""
if self.cache:
complete = self.head + (
"\nRUN /bin/bash -c 'cat <(echo Server = "
"http://{}:{}) /etc/pacman.d/mirrorlist > foobar && mv foobar "
"/etc/pacman.d/mirrorlist && pacman -Syuq --noconfirm --needed "
"procps-ng gcc base-devel ccache distcc python git mercurial bzr "
"subversion openssh && rm -rf /var/cache/pacman/pkg/* && "
"cp /etc/pacman.d/mirrorlist foo && tail -n +2 foo > "
"/etc/pacman.d/mirrorlist'\n").format(
self.pacman_cache_ip.compressed,
self.pacman_cache_port) + self.tail
else:
complete = self.head + (
"""\nRUN pacman -Syuq --noconfirm "
"--needed procps-ng gcc base-devel distcc ccache python git mercurial "
"bzr subversion openssh && rm -rf /var/cache/pacman/pkg/*\n"
"COPY pump /usr/bin/pump\n""") + self.tail
# write file
script_location = os.path.realpath(__file__)
with open(os.path.join(os.path.dirname(script_location), "Dockerfile"), "w") as docker_file:
docker_file.write(complete)
def start_local_cache(self):
"""
Start the pacman http cache
"""
# runs darkhttpd
args = ["/usr/bin/darkhttpd", self.pacman_cache_dir, "--port", self.pacman_cache_port]
self.darkhttpd_process = subprocess.Popen(args)
def stop_local_cache(self):
"""
Stop the darkhttpd process
"""
self.darkhttpd_process.terminate()
def insert_iptables_rules(self):
"""
Install the iptables rule for the pacman http cache
"""
comm = {
4 : "/bin/iptables",
6 : "/bin/ip6tables"
}[self.pacman_cache_ip.version]
args = "{} -w 5 -W 2000 -I INPUT -p tcp --dport 8990 -i docker0 -d {} -j ACCEPT".format(
comm, self.pacman_cache_ip.compressed).split()
subprocess.run(args)
def delete_iptables_rules(self):
"""
Remove the iptables rule for the pacman http cache
"""
comm = {
4 : "/bin/iptables",
6 : "/bin/ip6tables"
}[self.pacman_cache_ip.version]
args = "{} -w 5 -W 2000 -D INPUT -p tcp --dport 8990 -i docker0 -d {} -j ACCEPT".format(
comm, self.pacman_cache_ip.compressed).split()
subprocess.run(args)
def main(self):
"""
Main function for dmakepkg_builder. Runs the build process
"""
# check the docker0 address
ip_address = self.get_docker0_address()
if not ip_address or not self.pacman_cache_exists():
self.cache = False
self.pacman_cache_ip = ip_address
# create and write Dockerfile
self.create_dockerfile()
# start darkhttpd
self.start_local_cache()
# make sure it gets stopped if the script exits
atexit.register(self.stop_local_cache)
# insert iptables rule
self.insert_iptables_rules()
# make sure it gets cleaned up if the script exits
atexit.register(self.delete_iptables_rules)
sys.exit(self.start_docker_build())
if __name__ == "__main__":
BUILDER = DmakepkgBuilder()
BUILDER.main()