poudriere-runner: add more options, use types hints and cleanup
[freebsd-maintainer-scripts/.git] / poudriere-runner.py
1 #!/usr/bin/env python3
2
3 """
4 sbz poudriere runner
5 """
6
7 import argparse
8 import configparser
9 import os
10 import shlex
11 import subprocess
12 import sys
13
14 from typing import List, Dict
15
16 import colored
17 from colored import stylize
18
19 LOCALBASE="/usr/local"
20 PORTSDIR="/usr/ports" or os.environ['PORTSDIR']
21
22 default_conf="""
23 [default]
24 debug=True
25 setup=False
26 disk_path=%s/zfsfs
27 jails=12amd64,13amd64
28 jails_disabled=14amd64
29 mdconfig=False
30 mdconfig_cmd=mdconfig -f
31 cpuset=False
32 cpuset_cmd=cpuset -c -l 0-1
33 sets_host=FREEBSD_HOST=https://download.FreeBSD.org
34 port_tree=portsdir
35
36 [12amd64]
37 name=12amd64
38 arch=amd64
39 version=12.2-RELEASE
40
41 [13amd64]
42 name=13amd64
43 arch=amd64
44 version=13.0-RC1
45
46 [14amd64]
47 name=14amd64
48 arch=amd64
49 version=14.0-CURRENT
50 """ % LOCALBASE
51
52 class Prunner(object):
53     def __init__(self):
54         loadDisk()
55         self._jails = loadJails()
56         self._port_tree = cfg.get("default", "port_tree")
57         self.is_setup = cfg.getboolean("default", "setup")
58         if self.is_setup:
59             self.setUp()
60
61     def setUp(self, **params: Dict):
62         if debug:
63             print("Creating jail setup...")
64         params['host'] = cfg.get("default", "sets_host")
65         for jail in self._jails:
66             cmd = "poudriere jail -c -j {0} -a {1} -v {2} {3}".format(
67                 jail['name'],
68                 jail['arch'],
69                 jail['version'],
70                 params['host'])
71             if debug:
72                 print("exec: {0}".format(cmd))
73             sudo(cmd)
74
75     def tearDown(self):
76         if debug:
77             print("Stoping jails teardown()...")
78         for jail in self._jails:
79             cmd = "poudriere jail -k -j {0}".format(jail['name'])
80             if debug:
81                 print("exec: {0}".format(cmd))
82             sudo(cmd)
83
84     @property
85     def jails(self):
86        return self._jails
87
88     @property
89     def port_tree(self):
90        return self._port_tree
91
92     def testPort(self, origin: str, port_tree: str):
93         for jail in self._jails:
94             if debug:
95                 print(stylize("testport o: {0} j: {1}".format(origin,
96                     jail['name']), colored.fg("magenta")))
97             cmd = "poudriere testport -o {0} -j {1} -p {2}".format(
98                     origin, jail['name'], port_tree)
99             out, err = sudo(cmd)
100             if debug:
101                 print("j: {0}\nout: {1}\nerr: {2}".format(jail['name'], out,
102                     err))
103
104     def testAll(self):
105         for o in self.sbzports():
106             self.test_port(o)
107
108     def bulk(self, jail):
109         raise NotImplemented("not available yet")
110
111     def sbzPorts(self):
112         #TODO: don't rely on sbzports file
113         ports = open('{0}/sbzports'.format(os.path.expanduser('~%s' %
114             os.environ['USER'])), 'r').read().strip().split('\n')
115         return ports
116
117     def __del__(self):
118         if hasattr(self, 'is_setup') and self.is_setup:
119             self.tearDown()
120
121 def run(command: str, use_sudo=False) -> str:
122     cpuset_cmd = cfg.get('default', 'cpuset_cmd')
123     use_cpuset = cfg.getboolean('default', 'cpuset')
124     if use_cpuset:
125         command = "%s %s" % (cpuset_cmd, command)
126
127     command = "sudo %s" % command if use_sudo else command
128     p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE,
129             universal_newlines=True)
130     stdout, stderr = p.communicate()
131     p.wait()
132
133     return stdout.strip(), stderr
134
135 def sudo(command: str):
136     return run(command, use_sudo=True)
137
138 def loadDisk():
139     if cfg.getboolean('default', 'mdconfig') is False:
140         print("Skip mdconfig...\n")
141         return
142
143     mdconfig_cmd = cfg.get('default', 'mdconfig_cmd')
144     disk_path = cfg.get('default', 'disk_path')
145     if not os.path.exists('/dev/md0'):
146         out, err = sudo("{0} {1}".format(mdconfig_cmd, disk_path))
147         if err is not None:
148             print("Error: {0}".format(out))
149             sys.exit(1)
150
151 def loadJails():
152     jails = []
153     existing_jails = cfg.get('default', 'jails').split(',')
154     try:
155         existing_jails.remove(cfg.get('default', 'jails_disabled'))
156     except:
157         pass
158
159     for jail in existing_jails:
160         name = cfg.get(jail, 'name')
161         arch = cfg.get(jail, 'arch')
162         version = cfg.get(jail, 'version')
163         jails.append({'name': name, 'arch': arch, 'version': version})
164
165     return jails
166
167 def isValidOrigin(origin: str):
168     abspath = "{0}/{1}".format(PORTSDIR, origin)
169
170     return os.path.isdir(abspath)
171
172 def main():
173     global cfg, debug
174     cfg = configparser.ConfigParser()
175     cfg.read_string(default_conf)
176     debug = cfg.getboolean('default', 'debug')
177     if debug:
178         print(cfg.get('default', 'disk_path'))
179
180     runner = Prunner()
181     if len(sys.argv) == 1:
182         cwd = os.getcwd()
183         origin = "{0}/{1}".format(cwd.split('/')[-2], cwd.split('/')[-1])
184     else:
185         origin = sys.argv[1]
186
187     if isValidOrigin(origin):
188         runner.testPort(origin, runner.port_tree)
189
190 if __name__ == '__main__':
191     main()