List of commits:
Subject Hash Author Date (UTC)
Worker and builder big changes 90f596e4fd8fa291b1da831bce609b486c7a5875 Catalin(ux) M. BOIE 2020-01-06 13:31:20
Samples update (switch to systemd services for builder and worker) 8bfde1fb79778d024350bf5d75f076fb5e178512 Catalin(ux) M. BOIE 2020-01-06 12:50:21
builder: show queue in the web page 6cab7820b0bd2622afb77391a9e9515e0e837eef Catalin(ux) M. BOIE 2020-01-06 12:49:26
totp: Allow also years for 'val' command d666df0cec948f938469b9eb12a1b981cb41cfa1 Catalin(ux) M. BOIE 2020-01-04 14:39:33
worker: change git clone parameters because we could not clone 8eb2727ac17e12e148f39c72e1f4bd1e31319b33 Catalin(ux) M. BOIE 2019-12-19 03:34:26
Added a TODO for worker 492fb4546829f53adacd74c4647c3634f7c37065 Catalin(ux) M. BOIE 2019-12-18 22:23:12
worker improvements 4387b00291b5848aa08c303f9d62a126a6806a35 Catalin(ux) M. BOIE 2019-12-18 22:17:23
.spec: require tidy for building the tests c0deab2ce24388c91d2bc2e96338a260c9657dd6 Catalin(ux) M. BOIE 2019-12-18 22:15:29
nginx configuration update 015c9404229f2ff7fb91cc57ab21289f01878334 Catalin(ux) M. BOIE 2019-12-18 21:55:13
Added Business model to comparison c62d466580aca0ff3371f3ba369959c7ef7631d0 Catalin(ux) M. BOIE 2019-11-21 20:44:16
CSS small correction ded9ef6bce5bebfa815bc7ae5733bc8e0810af3b Catalin(ux) M. BOIE 2019-11-21 18:20:41
Added LDAP/qrencode to features/thanks pages d269d59787f61045f5831c6985899c28da08ebeb Catalin(ux) M. BOIE 2019-11-21 18:20:22
Invalidate slave table cache when updating the db structure, else new slaves will not be created 8e1bf7feeb87818179e7152a356e0f408da706f2 Catalin(ux) M. BOIE 2019-11-19 19:33:15
docker: push also the latest label a72e2e74355d772ae45bb99798ad47082390943c Catalin(ux) M. BOIE 2019-11-19 19:32:11
More statistics changes (load, cpu, mem) e7f38e72405a227788a7011ea17dd57d0d4de67c Catalin(ux) M. BOIE 2019-11-19 19:31:24
Cosmetic 0a0b258e8993cb5035e018b049018ac41b99eb01 Catalin(ux) M. BOIE 2019-11-17 11:09:56
Insist on respecting the privacy b464bcc35476ffa23a83beaf956e8f7b15163d7c Catalin(ux) M. BOIE 2019-11-17 11:03:36
Remove PayPal donation because of legal uncertainly cd938af4c5cdbd81190e535973588dee6b697d34 Catalin(ux) M. BOIE 2019-11-17 11:03:07
nginx sample update 1f9eb2d293e91139f397cc45cb206d8994471134 Catalin(ux) M. BOIE 2019-11-17 09:10:02
Mostly cosmetic 3bf09056ede99df46c1b59802e6cf2c7bebf4f13 Catalin(ux) M. BOIE 2019-11-17 09:09:31
Commit 90f596e4fd8fa291b1da831bce609b486c7a5875 - Worker and builder big changes
Author: Catalin(ux) M. BOIE
Author date (UTC): 2020-01-06 13:31
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2020-01-06 13:31
Parent(s): 8bfde1fb79778d024350bf5d75f076fb5e178512
Signer:
Signing key:
Signing status: N
Tree: 3abbb5dbef49cb71cfff92cc5ffe8c54a76dddbb
File Lines added Lines deleted
Makefile.in 3 2
README 6 0
TODO 30 0
inc/builder.inc.php 3 0
inc/conn.inc.php 17 29
inc/log.inc.php 6 0
inc/repo.inc.php 1 1
inc/util.inc.php 1 1
rocketgit.spec.in 6 0
root/themes/default/builder/cmds/header.html 1 0
root/themes/default/builder/cmds/line.html 1 0
scripts/worker.php 378 150
scripts/worker.sh 2 2
File Makefile.in changed (mode: 100644) (index cf75b36..e96e806)
... ... install: all
41 41 cp -vd --no-clobber samples/config.php $(I_ETC)/$(PRJ)/ cp -vd --no-clobber samples/config.php $(I_ETC)/$(PRJ)/
42 42 cp -vd --no-clobber samples/php-fpm.conf $(I_ETC)/$(PRJ)/ cp -vd --no-clobber samples/php-fpm.conf $(I_ETC)/$(PRJ)/
43 43 cp -vd --no-clobber samples/pool.conf $(I_ETC)/$(PRJ)/ cp -vd --no-clobber samples/pool.conf $(I_ETC)/$(PRJ)/
44 @mkdir -pv $(I_USR)/lib/systemd/system/
45 cp -vd samples/rocketgit-fpm.service $(I_USR)/lib/systemd/system/rocketgit-fpm.service
44 cp -vd --no-clobber samples/worker-main.conf.sample $(I_ETC)/$(PRJ)/
45 @mkdir -pv $(I_USR)/lib/systemd/system
46 cp -vd samples/*.service $(I_USR)/lib/systemd/system/
46 47 @ @
47 48 @echo "Installing tools..." @echo "Installing tools..."
48 49 @mkdir -pv $(I_USR_SBIN) @mkdir -pv $(I_USR_SBIN)
File README changed (mode: 100644) (index 7602b16..6f0f5d6)
157 157 . As admin user, go to Admin -> Settings and check if any setting should be . As admin user, go to Admin -> Settings and check if any setting should be
158 158 tweaked. It is very important to set the 'Host name' value. tweaked. It is very important to set the 'Host name' value.
159 159
160 . Activate the builder service, if you want:
161 systemctl enable rocketgit-builder
162
163 . Activate the worker service, if you want:
164 systemctl enable rocketgit-worker@main
165
160 166
161 167 == Thanks == == Thanks ==
162 168 . Special thanks to my family that supports me in this project. . Special thanks to my family that supports me in this project.
File TODO changed (mode: 100644) (index 256d51d..1d838cd)
1 1 == Where I stopped last time == == Where I stopped last time ==
2 [ ] Add command to build_jobs.status to be displayed nicely on web.
3 [ ] Replace serialize with rg_serialize and unserialize with rg_unserialize.
4 We need to also convert the database.
5 [ ] Use users.last_seen to detect an active user.
6 It is updated by a fetch by ssh, for example?
2 7 [ ] docker [ ] docker
3 8 [ ] rg_admin_email should be extracted from database[users][admin][email]/state. [ ] rg_admin_email should be extracted from database[users][admin][email]/state.
4 9 [ ] docker: ssh and git clone URLs are using docker hostname which probably [ ] docker: ssh and git clone URLs are using docker hostname which probably
 
6 11 Or disable them if admin did not set the correct URL? Or disable them if admin did not set the correct URL?
7 12 Or use the IP address? Or use the IP address?
8 13 Search for php_uname? Search for php_uname?
14 [ ] Resync nginx conf on rg2.
15 [ ] worker: have a flag which allows intrnet access?
16 Better to let the owner provide a template which may have or
17 may have not Internet access. Think about private networks! Better
18 with template.
19 [ ] worker: allow ssh (root) login only by key, to not be able to become root.
20 [ ]
9 21
10 22 == BEFORE NEXT RELEASE == == BEFORE NEXT RELEASE ==
23 [ ] wh: are available for all users? Should we configure this?
24 Think about a user which has push rights but is not the owner of the project.
25 [ ] push/etc: show how what IPs are validated with TOTP?
26 [ ] func test: add /stats url
27 [ ] ci: show how much swap was used. Used a sparse file and do a 'stat' in
28 the end?
29 [ ] ci: show I/O disk/network stats. Show cpu stats.
30 [ ] When an access is allowed, list rule number which allowed the access.
31 [ ] Automatically create merge requests from a mail/patchwork/etc. feed?
32 [ ] Bytes/s on fetches/pushes (need exec support).
33 [ ] stats: add the number of events?
34 [ ] web analytics included in admin section (referer, URL, code).
35 We need it anyway for graphics.
36 [ ] stats: add size of the database.
37 [ ] Add (un)hexa/(un)base64 decoding of files (but marked as "nofollow"
38 to not be visited by engines? Maybe only 'un' operation to be allowed
39 to be visited? Maybe add generic chain of filters (or labels?).
40 [ ] Merge requests: show the base commit name and date.
11 41 [ ] Use HTTP_USER_AGENT for storing pulls in history. [ ] Use HTTP_USER_AGENT for storing pulls in history.
12 42 [ ] Seems I do not deal with renaming users, even if the function exists! [ ] Seems I do not deal with renaming users, even if the function exists!
13 43 [ ] Extra output when cloning: seems to be a bug in git. [ ] Extra output when cloning: seems to be a bug in git.
File inc/builder.inc.php changed (mode: 100644) (index 94c6c2e..d9cd509)
... ... function rg_builder_nice_status(&$a)
219 219 $a['done_nice'] = gmdate('Y-m-d H:i', intval($a['done'])); $a['done_nice'] = gmdate('Y-m-d H:i', intval($a['done']));
220 220
221 221 foreach ($a['cmds'] as &$i) { foreach ($a['cmds'] as &$i) {
222 if (empty($i['start']))
223 continue;
224
222 225 $i['start_nice'] = gmdate('Y-m-d H:i', intval($i['start'])); $i['start_nice'] = gmdate('Y-m-d H:i', intval($i['start']));
223 226 $i['done_nice'] = gmdate('Y-m-d H:i', intval($i['done'])); $i['done_nice'] = gmdate('Y-m-d H:i', intval($i['done']));
224 227 $i['elap'] = rg_human_time_interval($i['done'] - $i['start'] + 1); $i['elap'] = rg_human_time_interval($i['done'] - $i['start'] + 1);
File inc/conn.inc.php changed (mode: 100644) (index 0887a04..184d07a)
... ... function rg_conn_new($key, $socket)
85 85
86 86 $rg_events['r'][$key] = $socket; $rg_events['r'][$key] = $socket;
87 87 socket_set_nonblock($socket); socket_set_nonblock($socket);
88
89 if (strcmp($key, 'master') != 0)
90 rg_log($key . ': new connection from ' . $ip . '/' . $port);
88 91 } }
89 92
90 93 /* /*
 
... ... function rg_conn_send($key)
109 112 global $rg_events; global $rg_events;
110 113
111 114 $s = &$rg_conns[$key]; $s = &$rg_conns[$key];
112 rg_log('SEND: ' . $s['send']);
115 rg_log($key . ': SEND: ' . $s['send']);
113 116 $r = @socket_send($s['socket'], $s['send'], strlen($s['send']), 0); $r = @socket_send($s['socket'], $s['send'], strlen($s['send']), 0);
114 117 if ($r === FALSE) { if ($r === FALSE) {
115 rg_log('Cannot send: ' . socket_strerror(socket_last_error()));
118 rg_log($key . ': Cannot send: ' . socket_strerror(socket_last_error()));
116 119 $s['func_error']($key); $s['func_error']($key);
117 120 rg_conn_destroy($key); rg_conn_destroy($key);
118 121 return FALSE; return FALSE;
 
... ... function rg_conn_recv($key)
138 141 if (isset($s['func_new'])) { if (isset($s['func_new'])) {
139 142 $client = @socket_accept($s['socket']); $client = @socket_accept($s['socket']);
140 143 if ($client === FALSE) { if ($client === FALSE) {
141 rg_log($key . ':Cannot accept!');
144 rg_log($key . ': cannot accept: '
145 . socket_strerror(socket_last_error()));
142 146 return; return;
143 147 } }
144 148
 
... ... function rg_conn_recv($key)
156 160
157 161 $r = @socket_recv($s['socket'], $buf, 4096, 0); $r = @socket_recv($s['socket'], $buf, 4096, 0);
158 162 if ($r === FALSE) { if ($r === FALSE) {
159 rg_log($key . ':Cannot receive (err): ' . socket_strerror(socket_last_error()));
163 rg_log($key . ': cannot receive: '
164 . socket_strerror(socket_last_error()));
160 165 $s['func_error']($key); $s['func_error']($key);
161 166 rg_conn_destroy($key); rg_conn_destroy($key);
162 167 return FALSE; return FALSE;
163 168 } }
164 169 if ($r === 0) { if ($r === 0) {
165 rg_log($key . ':Cannot receive (0): ' . socket_strerror(socket_last_error()));
170 rg_log($key . ': cannot receive: remove closed');
166 171 $s['func_close']($key); $s['func_close']($key);
167 172 rg_conn_destroy($key); rg_conn_destroy($key);
168 173 return FALSE; return FALSE;
169 174 } }
170 rg_log('RECV: ' . $buf);
175 rg_log($key . ': RECV: ' . $buf);
171 176
172 177 $s['recv'] .= $buf; $s['recv'] .= $buf;
173 178
174 if (isset($s['func_data'])) {
175 $s['func_data']($key);
176 return;
177 }
178
179 if (!isset($s['func_cmd'])) {
180 rg_log_ml($key . ': s: ' . print_r($s, TRUE));
181 rg_log($key . ':Neither func_data nor func_cmd present!');
182 return;
183 }
184
185 while (1) {
186 $pos = strpos($s['recv'], "\n");
187 if ($pos === FALSE)
188 break;
189
190 $cmd = substr($s['recv'], 0, $pos);
191 $s['recv'] = substr($s['recv'], $pos + 1);
192 $s['func_cmd']($key, $cmd);
193 }
179 $used = $s['func_data']($key, $s['recv']);
180 $s['recv'] = substr($s['recv'], $used);
181 return;
194 182 } }
195 183
196 184 /* /*
 
... ... function rg_conn_wait($timeout)
214 202 return; return;
215 203
216 204 if (!empty($r2)) if (!empty($r2))
217 rg_log_ml('read events: ' . print_r($r2, TRUE));
205 rg_log_ml('read events: ' . rg_array2string($r2));
218 206 foreach ($r2 as $key => $sock) foreach ($r2 as $key => $sock)
219 207 rg_conn_recv($key); rg_conn_recv($key);
220 208
221 209 if (!empty($w2)) if (!empty($w2))
222 rg_log_ml('write events: ' . print_r($w2, TRUE));
210 rg_log_ml('write events: ' . rg_array2string($w2));
223 211 foreach ($w2 as $key => $sock) foreach ($w2 as $key => $sock)
224 212 rg_conn_send($key); rg_conn_send($key);
225 213
226 214 if (!empty($e2)) if (!empty($e2))
227 rg_log_ml('error events: ' . print_r($e2, TRUE));
215 rg_log_ml('error events: ' . rg_array2string($e2));
228 216 foreach ($e2 as $key => $sock) foreach ($e2 as $key => $sock)
229 217 rg_conn_destroy($key); rg_conn_destroy($key);
230 218 } }
File inc/log.inc.php changed (mode: 100644) (index cdbf680..4de5c52)
... ... function rg_log_set_sid($v)
26 26 function rg_log_set_file($file) function rg_log_set_file($file)
27 27 { {
28 28 global $rg_log_file; global $rg_log_file;
29 global $rg_log_fd;
29 30
30 31 $rg_log_file = $file; $rg_log_file = $file;
32
33 if ($rg_log_fd !== FALSE) {
34 fclose($rg_log_fd);
35 $rg_log_fd = FALSE;
36 }
31 37 } }
32 38
33 39 function rg_log($str) function rg_log($str)
File inc/repo.inc.php changed (mode: 100644) (index 48d7042..774c84f)
... ... function rg_repo_commit_labels($db, $repo_id, $head)
2244 2244 $ret['list'] = array(); $ret['list'] = array();
2245 2245 while (($row = rg_sql_fetch_array($res))) { while (($row = rg_sql_fetch_array($res))) {
2246 2246 // Cosmetic // Cosmetic
2247 $row['labels'] = @unserialize($row['labels']);
2247 $row['labels'] = rg_unserialize($row['labels']);
2248 2248 $row['itime_nice'] = gmdate('Y-m-d H:i', $row['itime']); $row['itime_nice'] = gmdate('Y-m-d H:i', $row['itime']);
2249 2249 $ret['list'][] = $row; $ret['list'][] = $row;
2250 2250 } }
File inc/util.inc.php changed (mode: 100644) (index 460a99a..b6e07f1)
... ... function rg_socket_recv_wait($socket, $wait, $timeout)
1983 1983
1984 1984 $r = @socket_recv($socket, $buf, 32 * 4096, 0); $r = @socket_recv($socket, $buf, 32 * 4096, 0);
1985 1985 if ($r === FALSE) { if ($r === FALSE) {
1986 rg_log('Cannot receive (' . socket_strerror(socket_last_error()) . ')!');
1986 rg_log('Cannot receive: ' . socket_strerror(socket_last_error()));
1987 1987 break; break;
1988 1988 } }
1989 1989 //rg_log("Received [$buf]"); //rg_log("Received [$buf]");
File rocketgit.spec.in changed (mode: 100644) (index 6527af9..97ab1b7)
... ... if [ $1 -ne 0 ]; then
56 56 /sbin/service xinetd reload &>/dev/null || : /sbin/service xinetd reload &>/dev/null || :
57 57 fi fi
58 58 %systemd_post @PRJ@-fpm.service %systemd_post @PRJ@-fpm.service
59 %systemd_post @PRJ@-builder.service
60 %systemd_post @PRJ@-worker.service
59 61
60 62 %preun %preun
61 63 %systemd_preun @PRJ@-fpm.service %systemd_preun @PRJ@-fpm.service
64 %systemd_preun @PRJ@-builder.service
65 %systemd_preun @PRJ@-worker.service
62 66
63 67 %postun %postun
64 68 if [ $1 = 0 ]; then if [ $1 = 0 ]; then
 
... ... if [ $1 = 0 ]; then
69 73 /sbin/fixfiles -R @PRJ@ restore || : /sbin/fixfiles -R @PRJ@ restore || :
70 74 fi fi
71 75 %systemd_postun @PRJ@-fpm.service %systemd_postun @PRJ@-fpm.service
76 %systemd_postun @PRJ@-builder.service
77 %systemd_postun @PRJ@-worker.service
72 78
73 79 %prep %prep
74 80 %setup -q %setup -q
File root/themes/default/builder/cmds/header.html changed (mode: 100644) (index dd2df1f..18d2269)
1 1 <table> <table>
2 2 <tr> <tr>
3 <th>Command</th>
3 4 <th>Date added (UTC)</th> <th>Date added (UTC)</th>
4 5 <th>Done (UTC)</th> <th>Done (UTC)</th>
5 6 <th>Elapsed time</th> <th>Elapsed time</th>
File root/themes/default/builder/cmds/line.html changed (mode: 100644) (index 5ba0273..9513029)
1 1 <tr> <tr>
2 <td>@@cmd@@</td>
2 3 <td>@@start_nice@@</td> <td>@@start_nice@@</td>
3 4 <td>@@done_nice@@</td> <td>@@done_nice@@</td>
4 5 <td>@@elap@@</td> <td>@@elap@@</td>
File scripts/worker.php changed (mode: 100644) (index 3f1f677..7238259)
... ... require_once($INC . "/conn.inc.php");
22 22
23 23 rg_prof_start('MAIN'); rg_prof_start('MAIN');
24 24
25 if (!isset($_SERVER['argv'][1]))
26 $name = 'main';
27 else
28 $name = $_SERVER['argv'][1];
25 if (!isset($_SERVER['argv'][1])) {
26 $id = 'main';
27 } else {
28 $id = $_SERVER['argv'][1];
29 }
29 30
30 if (!isset($_SERVER['argv'][2]))
31 if (!isset($_SERVER['argv'][2])) {
31 32 $conf_file = '/etc/rocketgit/worker.conf'; $conf_file = '/etc/rocketgit/worker.conf';
32 else
33 } else {
33 34 $conf_file = $_SERVER['argv'][2]; $conf_file = $_SERVER['argv'][2];
35 }
34 36
35 rg_log_set_file($rg_log_dir . '/worker-' . $name . '.log');
37 rg_log_set_file($rg_log_dir . '/worker-' . $id . '.log');
36 38 rg_log_set_sid("000000"); // to spread the logs rg_log_set_sid("000000"); // to spread the logs
37 39
38 rg_log('name=' . $name . ' conf_file=' . $conf_file);
39 40
40 41 /* /*
41 42 * Load configuration file * Load configuration file
 
... ... function reload_config()
95 96 } }
96 97 } }
97 98
99 if (!isset($conf['master'])) {
100 rg_log('master line not present in the conf file!');
101 sleep(60);
102 exit(1);
103 }
104
105 if (!strstr($conf['master'], '://')) {
106 $conf['master_proto'] = 'tcp';
107 $conf['master_host'] = $conf['master'];
108 $conf['master_port'] = isset($conf['port']) ? $conf['port'] : 65000;
109 $conf['master_url'] = '';
110 } else {
111 $_t = explode('://', $conf['master']);
112 $conf['master_proto'] = trim($_t[0]);
113 $_t = explode('/', $_t[1]); // _t[1]: host[:port][/url]
114 $_x = explode(':', $_t[0]); // _t[0]: host[:port]
115 $conf['master_host'] = $_x[0];
116 $conf['master_port'] = isset($_x[1]) ? $_x[1] : 443;
117 $conf['master_url'] = isset($_t[1]) ? $_t[1] : '';
118 }
119 unset($conf['master']);
120 unset($conf['port']);
121
98 122 if (!file_exists($conf['state'] . '/key.pub')) { if (!file_exists($conf['state'] . '/key.pub')) {
99 123 rg_log('Creating SSH key...'); rg_log('Creating SSH key...');
100 124 $cmd = 'ssh-keygen -t rsa -b 4096 -N \'\'' $cmd = 'ssh-keygen -t rsa -b 4096 -N \'\''
 
... ... function reload_config()
104 128 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
105 129 rg_log('Cannot create key: ' . $r['errmsg'] . '!'); rg_log('Cannot create key: ' . $r['errmsg'] . '!');
106 130 sleep(60); sleep(60);
107 exit(0);
131 exit(1);
108 132 } }
109 133 } }
110 134 $conf['ssh_key'] = @file_get_contents($conf['state'] . '/key.pub'); $conf['ssh_key'] = @file_get_contents($conf['state'] . '/key.pub');
111 135 if ($conf['ssh_key'] === FALSE) { if ($conf['ssh_key'] === FALSE) {
112 136 rg_log('Cannot load key!'); rg_log('Cannot load key!');
113 sleep(60);
137 sleep(1);
114 138 exit(0); exit(0);
115 139 } }
116 140
117 141 rg_log_ml('conf: ' . print_r($conf, TRUE)); rg_log_ml('conf: ' . print_r($conf, TRUE));
118 142 } }
119 143
144 /*
145 * Save a job
146 */
147 function save_job($job)
148 {
149 global $conf;
150
151 $ret = array('ok' => 0);
152 while (1) {
153 $j_job = @json_encode($job);
154 if ($j_job === FALSE) {
155 $ret['errstr'] = 'cannot encode json: ' . json_last_error();
156 break;
157 }
158
159 $f = $conf['state'] . '/job-' . $job['id'] . '.ser';
160 $r = @file_put_contents($f . '.tmp', $j_job);
161 if ($r === FALSE) {
162 $ret['errstr'] = 'cannot store job: ' . rg_php_err();
163 break;
164 }
165
166 $r = @rename($f . '.tmp', $f);
167 if ($r !== TRUE) {
168 $ret['errstr'] = 'cannot rename job file ['
169 . $f . ']: ' . rg_php_err();
170 break;
171 }
172
173 $ret['ok'] = 1;
174 break;
175 }
176
177 return $ret;
178 }
179
120 180 /* /*
121 181 * Starts an worker * Starts an worker
122 182 */ */
 
... ... function start_worker($job)
126 186 global $php_errormsg; global $php_errormsg;
127 187
128 188 $env = $conf['env'][$job['env']]; $env = $conf['env'][$job['env']];
129 rg_log_ml('DEBUG: env: ' . print_r($env, TRUE));
189 rg_log_ml('DEBUG: start_worker: env: ' . print_r($env, TRUE));
130 190
131 191 $jid = $job['id']; $jid = $job['id'];
132 192 $emain = escapeshellarg($job['main']); $emain = escapeshellarg($job['main']);
 
... ... function start_worker($job)
134 194 $name = 'rg-worker-' . $job['id']; $name = 'rg-worker-' . $job['id'];
135 195 $ename = escapeshellarg($name); $ename = escapeshellarg($name);
136 196 $master = escapeshellarg($env['image']); $master = escapeshellarg($env['image']);
137 $img = escapeshellarg($job['main'] . '/image.qcow2');
138 $img2 = escapeshellarg($job['main'] . '/image2.raw');
197 $img = $job['main'] . '/image.qcow2';
198 $eimg = escapeshellarg($img);
199 $img2 = $job['main'] . '/image2.raw';
200 $eimg2 = escapeshellarg($img2);
139 201 // TODO: add bellow configuration to the web form // TODO: add bellow configuration to the web form
140 202 if (!isset($job['disk_size_gib'])) if (!isset($job['disk_size_gib']))
141 203 $job['disk_size_gib'] = '10'; $job['disk_size_gib'] = '10';
 
... ... function start_worker($job)
148 210 $err = TRUE; $err = TRUE;
149 211 $reason = ''; $reason2 = ''; $reason = ''; $reason2 = '';
150 212 while (1) { while (1) {
151 rg_exec('virsh destroy ' . $ename, '', FALSE, FALSE, FALSE);
152 rg_exec('virsh undefine --nvram ' . $ename, '', FALSE, FALSE, FALSE);
153
154 213 $r = rg_del_tree($job['main']); $r = rg_del_tree($job['main']);
155 214 if ($r === FALSE) { if ($r === FALSE) {
156 rg_log('Cannot delete main dir (' . $job['main'] . ')!');
215 $reason = 'cannot delete main dir';
216 $reason2 = 'cannot delete main dir (' . $job['main'] . ')';
157 217 break; break;
158 218 } }
159 219
160 220 $r = @mkdir($job['main'], 0700); $r = @mkdir($job['main'], 0700);
161 221 if ($r === FALSE) { if ($r === FALSE) {
162 rg_log('Cannot create main dir (' . $job['main'] . '):'
163 . ' ' . $php_errormsg . '!');
222 $reason = 'cannot create main dir';
223 $reason2 = 'cannot create main dir (' . $job['main'] . '):'
224 . ' (' . rg_php_err() . ')';
225 break;
226 }
227
228 $r = save_job($job);
229 if ($r['ok'] !== 1) {
230 $reason = 'cannot save job';
231 $reason2 = 'cannot save job: ' . $r['errstr'];
164 232 break; break;
165 233 } }
166 234
167 235 // We need to allow libvirt access to the image // We need to allow libvirt access to the image
236 // TODO: 'qemu' must be read from the conf file (on Fedora it is qemu)
168 237 $r = rg_exec('chown qemu:qemu ' $r = rg_exec('chown qemu:qemu '
169 238 . escapeshellarg($job['main']), '', FALSE, FALSE, FALSE); . escapeshellarg($job['main']), '', FALSE, FALSE, FALSE);
170 239 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
171 $reason = 'cannot chown build dir to qemu user: ' . $r['errmsg'];
172 $reason2 = $r['stderr'];
240 $reason = 'cannot chown build dir to qemu user';
241 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
173 242 break; break;
174 243 } }
175 244
176 // TODO: This will be used to clean up on a restart
177 $r = @file_put_contents($job['main'] . '/job.ser.tmp',
178 serialize($job));
179 if ($r === FALSE) {
180 $reason = 'cannot store job';
181 break;
182 }
183 $r = @rename($job['main'] . '/job.ser.tmp', $job['main'] . '/job.ser');
184 if ($r === FALSE) {
185 $reason = 'cannot rename job file';
186 break;
187 }
245 rg_exec('virsh destroy ' . $ename, '', FALSE, FALSE, FALSE);
246 rg_exec('virsh undefine --nvram ' . $ename, '', FALSE, FALSE, FALSE);
188 247
189 248 $r = rg_exec('qemu-img create -o lazy_refcounts=on,cluster_size=256K' $r = rg_exec('qemu-img create -o lazy_refcounts=on,cluster_size=256K'
190 249 . ' -b ' . $master . ' -b ' . $master
191 . ' -f qcow2 ' . $img, '', FALSE, FALSE, FALSE);
250 . ' -f qcow2 ' . $eimg, '', FALSE, FALSE, FALSE);
192 251 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
193 $reason = 'cannot create image: ' . $r['errmsg'];
194 $reason2 = $r['stderr'];
252 $reason = 'cannot create VM image';
253 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
195 254 break; break;
196 255 } }
197 256
198 257 // TODO: let the user specify the maximum disk space? // TODO: let the user specify the maximum disk space?
199 $r = rg_exec('qemu-img create -f raw ' . $img2
258 $r = rg_exec('qemu-img create -f raw ' . $eimg2
200 259 . ' ' . escapeshellarg($job['disk_size_gib'] . 'G'), . ' ' . escapeshellarg($job['disk_size_gib'] . 'G'),
201 260 '', FALSE, FALSE, FALSE); '', FALSE, FALSE, FALSE);
202 261 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
203 $reason = 'cannot create image2: ' . $r['errmsg'];
204 $reason2 = $r['stderr'];
262 $reason = 'cannot create VM image2';
263 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
205 264 break; break;
206 265 } }
207 266
 
... ... function start_worker($job)
209 268 $path = getenv('PATH'); $path = getenv('PATH');
210 269 putenv('PATH=' . $path . ':/usr/sbin'); putenv('PATH=' . $path . ':/usr/sbin');
211 270
212 $r = rg_exec('mkfs.ext4 -L RG ' . $img2, '', FALSE, FALSE, FALSE);
271 // TODO: let user choose fs type?
272 $r = rg_exec('mkfs.ext4 -L RG ' . $eimg2, '', FALSE, FALSE, FALSE);
213 273 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
214 $reason = 'cannot create fs: ' . $r['errmsg'];
215 $reason2 = $r['stderr'];
274 $reason = 'cannot create fs';
275 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
216 276 break; break;
217 277 } }
218 278
219 279 $r = @mkdir($job['main'] . '/root', 0700); $r = @mkdir($job['main'] . '/root', 0700);
220 280 if ($r === FALSE) { if ($r === FALSE) {
221 $reason = 'Cannot create root dir: ' . $php_errormsg . '!';
281 $reason = 'cannot create root dir';
282 $reason2 = 'cannot create root dir (' . rg_php_err() . ')';
222 283 break; break;
223 284 } }
224 285
225 $r = rg_exec('mount ' . $img2 . ' ' . $emain . '/root',
286 $r = rg_exec('mount ' . $eimg2 . ' ' . $emain . '/root',
226 287 '', FALSE, FALSE, FALSE); '', FALSE, FALSE, FALSE);
227 288 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
228 $reason = 'cannot mount fs: ' . $r['errmsg'];
229 $reason2 = $r['stderr'];
289 $reason = 'cannot mount fs';
290 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
230 291 break; break;
231 292 } }
232 293 $do_umount = TRUE; $do_umount = TRUE;
 
... ... function start_worker($job)
243 304 $cmd = ' git clone --depth 1' $cmd = ' git clone --depth 1'
244 305 . ' --recurse-submodules' . ' --recurse-submodules'
245 306 . ' --shallow-submodules' . ' --shallow-submodules'
307 . ' --no-checkout'
246 308 . ' ' . escapeshellarg($job['url']) . ' ' . escapeshellarg($job['url'])
247 309 . ' ' . $emain . '/root/git'; . ' ' . $emain . '/root/git';
248 310 $r = rg_exec($cmd, '', FALSE, FALSE, FALSE); $r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
249 311 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
250 $reason = 'cannot clone: ' . $r['errmsg'];
251 $reason2 = $r['stderr'];
312 $reason = 'git clone error';
313 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
252 314 break; break;
253 315 } }
254 316
255 317 // Build command list // Build command list
256 318 // TODO: document how a user can add labels in configure or make // TODO: document how a user can add labels in configure or make
257 $s = 'export RG_LABELS=/mnt/status/RG_LABELS' . "\n\n";
319 $s = '#!/bin/bash' . "\n";
320 $s .= 'export RG_LABELS=/mnt/status/RG_LABELS' . "\n\n";
258 321 $s .= 'cd /mnt/git' . "\n\n"; $s .= 'cd /mnt/git' . "\n\n";
259 $s .= 'git checkout ' . escapeshellarg($job['head']) . "\n";
260 foreach ($job['cmds'] as $name => $i) {
322 $s .= 'git checkout -b rgw ' . escapeshellarg($job['head']) . "\n";
323 foreach ($job['cmds'] as $_name => $i) {
261 324 if (empty($i['cmd'])) if (empty($i['cmd']))
262 325 continue; continue;
263 326
264 327 $prefix = '/mnt/status/' $prefix = '/mnt/status/'
265 . escapeshellarg($name);
328 . escapeshellarg($_name);
266 329
267 330 if (empty($i['label_ok'])) if (empty($i['label_ok']))
268 331 $lok = ''; $lok = '';
 
... ... function start_worker($job)
279 342 . ' >>/mnt/status/RG_LABELS' . "\n"; . ' >>/mnt/status/RG_LABELS' . "\n";
280 343
281 344 $s .= 'date +%s > ' . $prefix . '.start' . "\n" $s .= 'date +%s > ' . $prefix . '.start' . "\n"
282 . '(' . $i['cmd'] . ') 1>' . $prefix . '.log 2>&1' . "\n"
345 . 'echo "Executing [' . $i['cmd'] . ']..."' . "\n"
346 . '(' . $i['cmd'] . ') &>' . $prefix . '.log' . "\n"
283 347 . 'E=${?}' . "\n" . 'E=${?}' . "\n"
348 . 'echo ${E} > ' . $prefix . ".status\n"
284 349 . 'date +%s > ' . $prefix . '.done' . "\n" . 'date +%s > ' . $prefix . '.done' . "\n"
285 350 . 'if [ "${E}" != "0" ]; then' . "\n" . 'if [ "${E}" != "0" ]; then' . "\n"
286 . ' echo ${E} > ' . $prefix . ".status\n"
287 . $lnok
288 . ($i['abort'] ? ' exit 0' . "\n" : '')
351 . ' echo -n # just to not have an empty if branch' . "\n"
352 . $lnok
353 . ($i['abort'] ? ' exit 0' . "\n" : '')
289 354 . 'else' . "\n" . 'else' . "\n"
290 . ' echo 0 > ' . $prefix . ".status\n"
291 . $lok
355 . ' echo -n # just to not have an empty if branch' . "\n"
356 . $lok
292 357 . 'fi' . "\n\n"; . 'fi' . "\n\n";
293 358 } }
294 359 rg_log_ml('DEBUG: build.sh: ' . $s); rg_log_ml('DEBUG: build.sh: ' . $s);
295 360 $r = @file_put_contents($job['main'] . '/root/build.sh', $s); $r = @file_put_contents($job['main'] . '/root/build.sh', $s);
296 361 if ($r === FALSE) { if ($r === FALSE) {
297 $reason = 'cannot store build commands!';
362 $reason = 'cannot store build commands';
363 $reason2 = 'cannot store build commands (' . rg_php_err() . ')';
298 364 break; break;
299 365 } }
300 366 $r = @chmod($job['main'] . '/root/build.sh', 0755); $r = @chmod($job['main'] . '/root/build.sh', 0755);
301 367 if ($r === FALSE) { if ($r === FALSE) {
302 $reason = 'cannot chmod build.sh!';
368 $reason = 'cannot chmod build.sh';
369 $reason2 = 'cannot chmod build.sh (' . rg_php_err() . ')';
303 370 break; break;
304 371 } }
305 372
 
... ... function start_worker($job)
318 385 } }
319 386
320 387 // Store commands // Store commands
388 // TODO: set TMP somewhere on /mnt? (bind mount /mnt/tmp into /tmp)?
321 389 $r = @file_put_contents($job['main'] . '/root/rg.sh', $r = @file_put_contents($job['main'] . '/root/rg.sh',
322 'mkdir /mnt/status' . "\n"
390 '#!/bin/bash' . "\n"
391 . 'mkdir /mnt/status' . "\n"
323 392 . 'chown -R build:build /mnt/git /mnt/status' . "\n" . 'chown -R build:build /mnt/git /mnt/status' . "\n"
393 . 'echo "PATH=${PATH}"' . "\n"
394 . 'ERR=""' . "\n"
395 . 'id' . "\n"
324 396 . 'date +%s > /mnt/T_START' . "\n" . 'date +%s > /mnt/T_START' . "\n"
325 397 . '# Waiting for net...' . "\n" . '# Waiting for net...' . "\n"
326 398 . 'while [ "`ip ro li | grep ^default`" = "" ]; do' . "\n" . 'while [ "`ip ro li | grep ^default`" = "" ]; do' . "\n"
327 . ' (date; ip ro) &>>/mnt/status/wait_net.log' . "\n"
399 . ' (date; ip ro li) &>>/mnt/status/wait_net.log' . "\n"
328 400 . ' sleep 1' . "\n" . ' sleep 1' . "\n"
329 401 . 'done' . "\n" . 'done' . "\n"
330 . 'ip ro li >/mnt/status/iproli.log' . "\n"
331 402 . 'date +%s > /mnt/T_NET_OK' . "\n\n" . 'date +%s > /mnt/T_NET_OK' . "\n\n"
403 . '' . "\n"
332 404 . $p_i_cmd . $p_i_cmd
333 405 . 'date +%s > /mnt/T_PKGS_OK' . "\n\n" . 'date +%s > /mnt/T_PKGS_OK' . "\n\n"
334 . 'su - build -c /mnt/build.sh &>/mnt/status/build.log' . "\n\n"
335 . 'sync' . "\n"
406 . '' . "\n"
407 . '# Disabling further module loading.' . "\n"
408 . 'echo 1 > /proc/sys/kernel/modules_disabled' . "\n"
409 . '# Disabling root login' . "\n"
410 . 'chage -E 0 root' . "\n"
411 . 'if [ "${?}" != "0" ]; then' . "\n"
412 . ' ERR="cannot disable root account"' . "\n"
413 . 'fi' . "\n"
414 . 'if [ "${ERR}" = "" ]; then' . "\n"
415 . ' su - build -c "bash /mnt/build.sh" &>/mnt/status/build.log' . "\n"
416 . ' sync' . "\n"
417 . 'else' . "\n"
418 . ' echo "${ERR}" > /mnt/status/err' . "\n"
419 . 'fi' . "\n"
336 420 . 'date +%s > /mnt/T_DONE' . "\n\n" . 'date +%s > /mnt/T_DONE' . "\n\n"
337 . 'xxxshutdown -h now'
421 . 'shutdown -h now'
338 422 ); );
339 423 if ($r === FALSE) { if ($r === FALSE) {
340 $reason = 'cannot store commands!';
424 $reason = 'cannot store commands';
425 $reason2 = 'cannot store commands (' . rg_php_err() . ')';
341 426 break; break;
342 427 } }
343 428 $r = @chmod($job['main'] . '/root/rg.sh', 0700); $r = @chmod($job['main'] . '/root/rg.sh', 0700);
344 429 if ($r === FALSE) { if ($r === FALSE) {
345 $reason = 'cannot to chmod on rg.sh!';
430 $reason = 'cannot to chmod on rg.sh';
431 $reason2 = 'cannot to chmod on rg.sh (' . rg_php_err() . ')';
346 432 break; break;
347 433 } }
348 434
349 435 $r = rg_exec('umount ' . $emain . '/root', '', FALSE, FALSE, FALSE); $r = rg_exec('umount ' . $emain . '/root', '', FALSE, FALSE, FALSE);
350 436 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
351 $reason = 'cannot umount fs: ' . $r['errmsg'];
352 $reason2 = $r['stderr'];
437 $reason = 'cannot umount fs';
438 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
353 439 break; break;
354 440 } }
355 441 $do_umount = FALSE; $do_umount = FALSE;
356 442
357 443 // We need to allow libvirt access to the image // We need to allow libvirt access to the image
358 $r = rg_exec('chown qemu:qemu ' . $img2, '', FALSE, FALSE, FALSE);
444 $r = rg_exec('chown qemu:qemu ' . $eimg2, '', FALSE, FALSE, FALSE);
359 445 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
360 $reason = 'cannot chown image to qemu user: ' . $r['errmsg'];
361 $reason2 = $r['stderr'];
446 $reason = 'cannot chown image to qemu user';
447 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
362 448 break; break;
363 449 } }
364 450
365 451 // TODO: store the path to the templates in the configuration file. // TODO: store the path to the templates in the configuration file.
366 $template = @file_get_contents('/var/lib/libvirt/images/rgw/templates/'
367 . escapeshellarg($env['arch']) . '.xml');
452 $_f = '/var/lib/libvirt/images/rgw/templates/' . $env['arch'] . '.xml';
453 $template = @file_get_contents($_f);
368 454 if ($template === FALSE) { if ($template === FALSE) {
369 455 $reason = 'cannot load template'; $reason = 'cannot load template';
456 $reason2 = 'cannot load template from ' . $_f . ': ' . rg_php_err();
370 457 break; break;
371 458 } }
372 str_replace('@@name@@', $name, $template);
373 str_replace('@@mem@@', $job['mem_mib'], $template);
374 str_replace('@@cpus@@', $job['cpus'], $template);
375 str_replace('@@disk0@@', $img, $template);
376 str_replace('@@disk1@@', $img2, $template);
459 $template = str_replace('@@name@@', $name, $template);
460 $template = str_replace('@@mem@@', $job['mem_mib'], $template);
461 $template = str_replace('@@cpus@@', $job['cpus'], $template);
462 $template = str_replace('@@disk0@@', $img, $template);
463 $template = str_replace('@@disk0_type@@', 'qcow2', $template);
464 $template = str_replace('@@disk1@@', $img2, $template);
465 $template = str_replace('@@disk1_type@@', 'raw', $template);
377 466 // TODO: allow firewall specification // TODO: allow firewall specification
378 str_replace('@@net0@@', '<interface type=\'network\'><source network=\'default\'/><model type=\'virtio\'/></interface>', $template);
467 $template = str_replace('@@net0@@', '<interface type=\'network\'><source network=\'default\'/><model type=\'virtio\'/></interface>', $template);
379 468 // TODO: take care of XML injection? // TODO: take care of XML injection?
380 str_replace('@@chan1_path@@', $job['main'] . '/x.chan', $template);
381 $r = @file_put_contents($job['main'] . '/machine.xml');
469 $template = str_replace('@@chan1_path@@', $job['main'] . '/x.chan', $template);
470 $_xml = $job['main'] . '/machine.xml';
471 $r = @file_put_contents($_xml, $template);
382 472 if ($r === FALSE) { if ($r === FALSE) {
383 $reason = 'cannot store template file.';
384 break;
385 }
386
387 $r = rg_exec('virsh define ' . escapeshellarg($job['main'] . '/machine.xml'));
388 if ($r['ok'] !== 1) {
389 $reason = 'cannot define machine: ' . $r['errmsg'];
390 $reason2 = $r['stderr'];
473 $reason = 'cannot store template file';
474 $reason2 = 'cannot store template in ' . $_xml . ': ' . rg_php_err();
391 475 break; break;
392 476 } }
393 477
394 $r = rg_exec('virsh start ' . $ename);
478 $r = rg_exec('virsh create ' . escapeshellarg($_xml),
479 '', FALSE, FALSE, FALSE);
395 480 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
396 $reason = 'cannot start machine: ' . $r['errmsg'];
397 $reason2 = $r['stderr'];
481 $reason = 'cannot define VM';
482 $reason2 = $r['errmsg'] . ': ' . $r['stderr'];
398 483 break; break;
399 484 } }
400 485
 
... ... function start_worker($job)
404 489 if ($do_umount) if ($do_umount)
405 490 rg_exec('umount ' . $emain . '/root', '', FALSE, FALSE, FALSE); rg_exec('umount ' . $emain . '/root', '', FALSE, FALSE, FALSE);
406 491
407 // Seems that any error above must retrigger the build on other worker
492 // Any error above must retrigger the build on other worker
408 493 if ($err) { if ($err) {
494 rg_log('error: ' . $reason);
495 rg_log('error2: ' . $reason2);
409 496 @file_put_contents($job['main'] . '/error.log', $reason); @file_put_contents($job['main'] . '/error.log', $reason);
410 497 @file_put_contents($job['main'] . '/error2.log', $reason2); @file_put_contents($job['main'] . '/error2.log', $reason2);
411 498 } }
499
500 rg_log('Done');
412 501 } }
413 502
414 503 /* /*
415 * Handle received commands
504 * Handle received commands (one JSON)
416 505 */ */
417 function xhandle($key, $cmd0)
506 function xhandle_one($key, $data)
418 507 { {
508 global $rg_log_dir;
419 509 global $jobs; global $jobs;
420 510 global $conf; global $conf;
421 511 global $pid_to_jid; global $pid_to_jid;
422 512
423 $cmd = substr($cmd0, 0, 4);
424 $data = stripcslashes(trim(substr($cmd0, 4)));
425 $job = @unserialize($data);
426 if ($job === FALSE) {
427 rg_log('Cannot unserialize [' . $data . ']');
513 $job = @json_decode($data, TRUE);
514 if ($job === NULL) {
515 rg_log_ml('Cannot decode JSON: ' . json_last_error_msg());
516 $err = array('errstr' => 'cannot decode json');
517 rg_conn_enq('master', json_encode($err) . "\n");
428 518 rg_conn_destroy($key); rg_conn_destroy($key);
429 519 return; return;
430 520 } }
431 521
432 522 $jid = $job['id']; $jid = $job['id'];
433 523
434 if (strcmp($cmd, 'BLD ') == 0) {
524 if (strcmp($job['op'], 'BLD') == 0) {
435 525 // TODO: should we confirm quickly if the job is accepted, // TODO: should we confirm quickly if the job is accepted,
436 526 // even if we could not fork? // even if we could not fork?
527
437 528 if (isset($jobs[$jid])) { if (isset($jobs[$jid])) {
438 529 // TODO: this should not happen, right? // TODO: this should not happen, right?
439 530 rg_log('Job ' . $jid . ' already in queue!'); rg_log('Job ' . $jid . ' already in queue!');
 
... ... function xhandle($key, $cmd0)
454 545 break; break;
455 546 } }
456 547 if ($pid == 0) { // child if ($pid == 0) { // child
548 rg_log_set_file($rg_log_dir . '/worker-' . $conf['id']
549 . '-' . $jid . '.log');
457 550 start_worker($jobs[$jid]); start_worker($jobs[$jid]);
458 551 exit(0); exit(0);
459 552 } }
 
... ... function xhandle($key, $cmd0)
466 559 } }
467 560 $a = array('id' => $jid); $a = array('id' => $jid);
468 561 if ($err) { if ($err) {
562 $a['op'] = 'ABR';
469 563 $a['reason'] = $reason; $a['reason'] = $reason;
470 rg_conn_enq('master', 'ABR ' . rg_conn_prepare($a) . "\n");
564 rg_conn_enq('master', json_encode($a) . "\n");
471 565 unset($pid_to_jid[$pid]); unset($pid_to_jid[$pid]);
472 566 unset($jobs[$jid]); unset($jobs[$jid]);
473 567 return; return;
474 568 } }
475 rg_conn_enq('master', 'STA ' . rg_conn_prepare($a) . "\n");
476 } else if (strcmp($cmd, 'DRE ') == 0) { // DRE = done received
569 $a['op'] = 'STA';
570 rg_conn_enq('master', json_encode($a) . "\n");
571 } else if (strcmp($job['op'], 'DRE') == 0) { // DRE = done received
477 572 // So, we can clean up everything related to this job // So, we can clean up everything related to this job
478 573 // TODO: do we clear the state file? // TODO: do we clear the state file?
479 574 rg_log('DRE command'); rg_log('DRE command');
480 $job = &$jobs[$jid];
481 unset($pid_to_jid[$job['pid']]);
575 $_job = &$jobs[$jid];
576 unset($pid_to_jid[$_job['pid']]);
482 577 unset($jobs[$jid]); unset($jobs[$jid]);
483 @unlink($job['main'] . '/job.ser');
578 @unlink($conf['state'] . '/job-' . $jid . '.ser');
484 579 } else { } else {
485 rg_log('Cannot handle[' . $key . ']: ' . $cmd);
580 rg_log($key . ': cannot handle op: ' . $job['op']);
486 581 } }
487 582 } }
488 583
584 /*
585 * Handle received commands
586 */
587 function xhandle($key, $data)
588 {
589 $ret = 0;
590 while (1) {
591 $pos = strpos($data, "\n");
592 if ($pos === FALSE)
593 return $ret;
594
595 $one = substr($data, 0, $pos);
596 xhandle_one($key, $one);
597 $data = substr($data, $pos + 1);
598 $ret += $pos + 1;
599 }
600
601 return $ret;
602 }
603
489 604 /* /*
490 605 * Extracts info from the virtual disk * Extracts info from the virtual disk
491 606 * TODO: if something fails, we may keep the file mounted! * TODO: if something fails, we may keep the file mounted!
 
... ... function rg_job_extract_info(&$job)
512 627 break; break;
513 628 } }
514 629
630 // Extract how much disk space was used
631 $r = stat($job['main'] . '/image2.raw');
632 if ($r === FALSE) {
633 $job['error'] = 'Missing image2 file';
634 break;
635 }
636 $job['status']['disk_used_mib'] = intval($r['blocks'] / 2 / 1024);
637 // TODO - remove this
638 $cmd = 'ln -f ' . $emain . '/image2.raw ' . $emain . '/..';
639 $r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
640
515 641 $cmd = 'mount ' . $emain . '/image2.raw ' . $emain . '/root'; $cmd = 'mount ' . $emain . '/image2.raw ' . $emain . '/root';
516 642 $r = rg_exec($cmd, '', FALSE, FALSE, FALSE); $r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
517 643 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
 
... ... function rg_job_extract_info(&$job)
519 645 break; break;
520 646 } }
521 647
648 $r = @file_get_contents($job['main'] . '/root/status/err');
649 if ($r !== FALSE) {
650 $job['error'] = $r;
651 break;
652 }
653
522 654 $labels = @file($job['main'] . '/root/status/RG_LABELS'); $labels = @file($job['main'] . '/root/status/RG_LABELS');
523 655 if ($labels === FALSE) if ($labels === FALSE)
524 656 $labels = array(); $labels = array();
 
... ... function rg_job_extract_info(&$job)
543 675
544 676 $sd = $job['main'] . '/root/status/' . $cmd; $sd = $job['main'] . '/root/status/' . $cmd;
545 677 $job['status']['cmds'][$cmd] = array( $job['status']['cmds'][$cmd] = array(
546 'start' => @trim(file_get_contents($sd . '.start')),
547 'done' => @trim(file_get_contents($sd . '.done')),
548 'log' => @file_get_contents($sd . '.log', FALSE,
549 NULL, -1, 4 * 4096)
678 'cmd' => $i['cmd'],
679 'start' => trim(file_get_contents($sd . '.start')),
680 'done' => trim(file_get_contents($sd . '.done')),
681 'status' => trim(file_get_contents($sd . '.status')),
682 'log' => trim(rg_file_get_tail($sd . '.log', 4 * 4096))
550 683 ); );
551 684 } }
552 685 unset($job['cmds']); unset($job['cmds']);
553 686 unset($job['url']); unset($job['url']);
554 687 unset($job['head']); unset($job['head']);
555 688 unset($job['env']); unset($job['env']);
689 break;
690 }
691
692 $cmd = 'umount ' . $emain . '/root';
693 $r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
694 if ($r['ok'] != 1)
695 rg_log('Cannot unmount [' . $job['main'] . '/root]: ' . $r['errmsg'] . '!');
696
697 rg_del_tree($job['main']);
698
699 rg_log_ml('DEBUG: job: ' . print_r($job, TRUE));
700 return TRUE;
701 }
556 702
557 $cmd = 'umount ' . $emain . '/root';
703 /*
704 * Extract blk/net/cpu/mem info from a VM
705 */
706 function vm_extract_info($name)
707 {
708 $ret = array();
709 while (1) {
710 $cmd = 'virsh domstats --raw ' . escapeshellarg($name);
558 711 $r = rg_exec($cmd, '', FALSE, FALSE, FALSE); $r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
559 712 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
560 rg_log('Cannot unmount: ' . $r['errmsg'] . '!');
713 rg_log('Could not get dom stats: ' . $r['errmsg']);
561 714 break; break;
562 715 } }
563 716
564 rg_del_tree($job['main']);
717 //rg_log_ml('DEBUG: domstats: ' . print_r($r['data'], TRUE));
718 $data = array();
719 $t = explode("\n", $r['data']);
720 foreach ($t as $line) {
721 $line = trim($line);
722 $x = explode('=', $line, 2);
723 if (!isset($x[1]))
724 continue;
725 $data[$x[0]] = $x[1];
726 }
727
728 $ret['rx_bytes'] = $data['net.0.rx.bytes'];
729 $ret['rx_pkts'] = $data['net.0.rx.pkts'];
730 $ret['tx_bytes'] = $data['net.0.tx.bytes'];
731 $ret['tx_pkts'] = $data['net.0.tx.pkts'];
732
733 $ret['block_read_ops'] = $data['block.1.rd.reqs'];
734 $ret['block_read_bytes'] = $data['block.1.rd.bytes'];
735 $ret['block_write_ops'] = $data['block.1.wr.reqs'];
736 $ret['block_write_bytes'] = $data['block.1.wr.bytes'];
737 $ret['block_physical_bytes'] = $data['block.1.physical'];
738 $ret['block_allocation_bytes'] = $data['block.1.allocation'];
739
740 $ret['cpu_time_ns'] = $data['cpu.time'];
741 $ret['ballon_current_mib'] = intval($data['balloon.current'] / 1024);
742 $ret['ballon_rss_mib'] = intval($data['balloon.rss'] / 1024);
565 743 break; break;
566 744 } }
567 745
568 rg_log_ml('DEBUG: job: ' . print_r($job, TRUE));
569 return TRUE;
746 return $ret;
570 747 } }
571 748
572 749
573 750 reload_config(); reload_config();
751 $conf['id'] = $id;
752
753 rg_log('Connecting to ' . $conf['master_host'] . '/' . $conf['master_port']
754 . ' with proto ' . $conf['master_proto']
755 . ' with url [' . $conf['master_url'] . ']' . '...');
756 if (strcmp($conf['master_proto'], 'tcp') == 0) {
757 $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
758 if ($socket === FALSE) {
759 rg_log('Cannot create socket: ' . rg_php_err());
760 exit(1);
761 }
574 762
763 $r = @socket_connect($socket, $conf['master_host'], $conf['master_port']);
764 if ($r === FALSE) {
765 rg_log('Cannot connect: ' . rg_php_err());
766 exit(1);
767 }
768 } else if (strcmp($conf['master_proto'], 'proxy_tls') == 0) {
769 $context = stream_context_create();
770 // TODO: make timeout configurable
771 $socket = @stream_socket_client('tls://' . $conf['master_host']
772 . ':' . $conf['master_port'], $errno, $errstr, 30,
773 STREAM_CLIENT_CONNECT, $context);
774 if ($socket === FALSE) {
775 rg_log('Cannot connect: ' . $errstr);
776 exit(1);
777 }
575 778
576 $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
577 if ($socket === FALSE) {
578 rg_log('Cannot create socket!');
579 exit(1);
580 }
779 $s = "GET " . $conf['maser_url'] . " HTTP/1.1\r\n"
780 . "Host: " . $conf['master_host'] . ':' . $conf['master_port'] . "\r\n"
781 . "Connection: keep-alive, Upgrade\r\n"
782 . "Pragma: no-cache\r\n"
783 . "Cache-Control: no-cache\r\n"
784 . "Upgrade: websocket\r\n"
785 . "\r\n";
786 $r = @fwrite($socket, $s);
787 if ($r === FALSE) {
788 rg_log('Cannot write HTTP request: ' . rg_php_err());
789 exit(1);
790 }
581 791
582 $r = @socket_connect($socket, $conf['master'], $conf['port']);
583 if ($r === FALSE) {
584 rg_log('Cannot connect to ' . $conf['master'] . '/'
585 . $conf['port'] . '!');
792 $buf = '';
793 while (1) {
794 $r = @fread($socket, 4096);
795 if ($r === FALSE) {
796 rg_log('Cannot read HTTP answer: ' . rg_php_err());
797 exit(1);
798 }
799 $buf .= $r;
800 rg_log_ml('Answer: ' . $buf);
801 if (strstr($buf, "\r\n\r\n") || strstr($buf, "\n\n"))
802 break;
803 }
804 } else {
805 rg_log('Invalid master protocol: ' . $conf['master_proto'] . '!');
806 sleep(60);
586 807 exit(1); exit(1);
587 808 } }
588 809 rg_log('Connected.'); rg_log('Connected.');
589 810
590 811 rg_conn_new('master', $socket); rg_conn_new('master', $socket);
591 812 $rg_conns['master']['exit_on_close'] = 1; $rg_conns['master']['exit_on_close'] = 1;
592 $rg_conns['master']['func_cmd'] = 'xhandle';
813 $rg_conns['master']['func_data'] = 'xhandle';
593 814
594 815 // announce ourselves // announce ourselves
595 816 $key = $conf['key']; unset($conf['key']); $key = $conf['key']; unset($conf['key']);
596 817 $ann = $conf; $ann = $conf;
818 $ann['op'] = 'ANN';
597 819 $ann['uname'] = php_uname('a'); $ann['uname'] = php_uname('a');
598 820 $ann['host'] = php_uname('n'); $ann['host'] = php_uname('n');
599 821 $ann['arch'] = php_uname('m'); $ann['arch'] = php_uname('m');
600 822 $ann['boot_time'] = time(); $ann['boot_time'] = time();
601 823 $ann['sign'] = hash_hmac('sha512', $ann['boot_time'], $key); $ann['sign'] = hash_hmac('sha512', $ann['boot_time'], $key);
602 rg_conn_enq('master', 'ANN ' . rg_conn_prepare($ann) . "\n");
824 $j_ann = json_encode($ann);
825 if ($j_ann === FALSE) {
826 rg_log('Cannot encode json: ' . json_last_error_msg());
827 exit(1);
828 }
829 rg_conn_enq('master', $j_ann . "\n");
603 830
604 831 $jobs = array(); $jobs = array();
605 832 $pid_to_jid = array(); $pid_to_jid = array();
 
... ... while(1) {
633 860 break; break;
634 861
635 862 $vms_loaded = TRUE; $vms_loaded = TRUE;
636 rg_log_ml('vms: ' . print_r($vms, TRUE));
863 //rg_log_ml('vms: ' . print_r($vms, TRUE));
637 864 } }
638 865
639 866 $name = 'rg-worker-' . $jid; $name = 'rg-worker-' . $jid;
 
... ... while(1) {
643 870 // TODO: if too much time, abort (kill // TODO: if too much time, abort (kill
644 871 // worker and destroy virtual machine) // worker and destroy virtual machine)
645 872 //TODO: $job['error'] = 'too much time'; //TODO: $job['error'] = 'too much time';
873 // TODO: Signal from inside VM that we finished and extracts stats at that time
874 $job['stats'] = vm_extract_info($name);
646 875 continue; continue;
647 876 } }
648 877
649 878 rg_log('VM ' . $jid . ' finished'); rg_log('VM ' . $jid . ' finished');
650 879
651 880 rg_job_extract_info($job); rg_job_extract_info($job);
881
652 882 if (isset($job['error'])) if (isset($job['error']))
653 883 $job['state'] = RG_JOB_ERROR; $job['state'] = RG_JOB_ERROR;
654 884 else else
 
... ... while(1) {
656 886
657 887 // TODO: store in fs to be able to still inform the // TODO: store in fs to be able to still inform the
658 888 // master if we are crashing. // master if we are crashing.
659 @file_put_contents($job['main'] . '/job.ser', serialize($job));
889 save_job($job);
660 890
661 891 $xjob = $job; $xjob = $job;
892 $xjob['op'] = 'DON';
662 893 unset($xjob['debug']); unset($xjob['debug']);
663 894 unset($xjob['packages']); unset($xjob['packages']);
664 895 unset($xjob['main']); unset($xjob['main']);
665 rg_conn_enq('master', 'DON ' . rg_conn_prepare($xjob) . "\n");
666
667 // // TODO: do we clean the machine in case of crash?
668 // $cmd = 'virsh undefine --nvram rg-worker-' . escapeshellarg($jid);
669 // $r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
670 // if ($r['ok'] != 1) {
671 // $job['error'] = 'Could not undefine machine: ' . $r['errmsg'];
672 // rg_log('Error: ' . $job['error']);
673 // //break; TODO
674 // }
675
676 // $cmd = 'virsh destroy rocketgit-j-' . $jid;
677 // $r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
678 // if ($r['ok'] != 1) {
679 // $job['error'] = 'Could not destroy: ' . $r['errmsg'];
680 // rg_log('Error: ' . $job['error']);
681 // }
896 $j_xjob = json_encode($xjob);
897 if ($j_xjob === FALSE) {
898 rg_log('Cannot encode json: ' . json_last_error_msg());
899 } else {
900 rg_conn_enq('master', $j_xjob . "\n");
901 }
902
903 $cmd = 'virsh destroy rocketgit-j-' . $jid;
904 $r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
905 if ($r['ok'] != 1) {
906 // If error, probably the machine was not running, so, this is just a warning
907 $job['error'] = 'Could not destroy: ' . $r['errmsg'];
908 rg_log('Error: ' . $job['error']);
909 }
682 910 } }
683 911 } }
684 912
685 rg_prof_end("MAIN");
913 rg_prof_end('MAIN');
686 914 rg_prof_log(); rg_prof_log();
687 915 ?> ?>
File scripts/worker.sh changed (mode: 100755) (index 99b77c7..a778feb)
7 7 # this is run in rocketgit_worker_t, so we cannot do it # this is run in rocketgit_worker_t, so we cannot do it
8 8 #check_context #check_context
9 9
10 name="${1}"
10 id="${1}"
11 11
12 exec 100<>/var/lib/rocketgit/locks/worker-${name}.sh.lock
12 exec 100<>/var/lib/rocketgit/locks/worker-${id}.sh.lock
13 13
14 14 flock --exclusive --nonblock 100 flock --exclusive --nonblock 100
15 15 if [ "${?}" != "0" ]; then if [ "${?}" != "0" ]; then
Date/time (UTC) Type Misc Labels
2020-01-06 13:32 build CentOS-8-x86_64 worker/r1 builder/color=fff worker_elap/8s wait_time/5s date/2020-01-06 time/13:31
2020-01-06 13:32 build fedora-31-x86_64 worker/r1 builder/color=fff worker_elap/16s wait_time/5s date/2020-01-06 time/13:31
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/catalinux/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/catalinux/rocketgit

Clone this repository using git:
git clone git://git.rocketgit.com/user/catalinux/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main