From 62e2432de3c2fb3bcd14faee86029c1c0eefdb23 Mon Sep 17 00:00:00 2001 From: Eudyptula Date: Fri, 19 Jun 2009 16:33:50 -0400 Subject: Finished infrastructure for 1st gen. build wizard, enhanced backend logging --- backend/backend.php | 6 ++- backend/functions/build.php | 2 +- backend/functions/execution.php | 12 ++++-- backend/functions/log.php | 2 +- frontend/index.php | 6 ++- frontend/pages/wizard.php | 83 ++++++++++++++++++++++++++++++--------- frontend/pages/wizard/step0.php | 1 + frontend/pages/wizard/step1.php | 11 ++++++ shared/classes/0sql_row_obj.php | 1 + shared/classes/build.php | 43 ++++++++++++++++++-- shared/classes/buildlog_entry.php | 2 +- shared/classes/buildopt.php | 3 +- shared/classes/session.php | 3 +- shared/classes/task.php | 25 +++++++++--- todo | 10 ++++- 15 files changed, 169 insertions(+), 41 deletions(-) create mode 120000 frontend/pages/wizard/step0.php create mode 100644 frontend/pages/wizard/step1.php diff --git a/backend/backend.php b/backend/backend.php index 1cb1cdb..0dbd8a9 100755 --- a/backend/backend.php +++ b/backend/backend.php @@ -19,15 +19,19 @@ if (isset($opts['f'])) { } while (true) { // TODO check first for builds that need to be resumed - $r=$pdo->query('SELECT * FROM `builds` WHERE `status`="build/ready" LIMIT 1'); // TODO ORDER BY `ctime` ASC + $r=$pdo->query('SELECT * FROM `builds` WHERE `status`="build/ready" ORDER BY `ctime` ASC LIMIT 1'); if ($r->rowCount()) { $build=new sql_build($r->fetch(PDO::FETCH_ASSOC)); + $build->start=time(); + $build->write(); echo 'Starting build id='.$build->id."\n"; try { build($build); } catch (Exception $e) { echo 'Caught exception: '.$e->getMessage()."\n"; } + $build->finish=time(); + $build->write(); echo 'Finished with build id='.$build->id."\n"; } echo 'Sleeping...'; diff --git a/backend/functions/build.php b/backend/functions/build.php index 3346218..84b2a77 100644 --- a/backend/functions/build.php +++ b/backend/functions/build.php @@ -32,7 +32,7 @@ function build(&$build) { $contents.=strtoupper($name).'="'.$val.'"'."\n"; } unset($makeconf); - fatal(log_status('Writing '.$C.'/make.conf:'."\n".indent($contents), file_put_contents($C.'/etc/make.conf', $contents))); + fatal(log_status('Writing '.$C.'/make.conf', file_put_contents($C.'/etc/make.conf', $contents))); unset($contents); fatal(log_status('Making make.profile symlink to '.$conf['portdir'].'/profiles/'.$headers['profile'], symlink($conf['portdir'].'/profiles/'.$headers['profile'], $C.'/etc/make.profile'))); $env=array( diff --git a/backend/functions/execution.php b/backend/functions/execution.php index c201adf..73cef31 100644 --- a/backend/functions/execution.php +++ b/backend/functions/execution.php @@ -1,11 +1,14 @@ array('pipe', 'r'), // STDIN 1 => array('pipe', 'w'), // STDOUT 2 => array('pipe', 'w') // STDERR ); $task=new sql_task(null, $build->id, $command, null); + $task->start=time(); $task->write(); $p=proc_open($command, $descriptorspec, $pipes, $path, $env); foreach ($pipes as $pipe) { @@ -20,19 +23,16 @@ function log_command(&$build, $command, $path=null, $env=null) { $s=stream_select($outs, $null, $null, 1); if ($s) { $c=stream_get_contents($pipes[2]); - // TODO this really needs to go to the DB and carry metadata if ($c) { // STDERR $entry=new sql_buildlog_entry($task->id, $msg++, time(), 'stderr', $c); $entry->write(); - //log_msg($c, false); } $c=stream_get_contents($pipes[1]); if ($c) { // STDOUT $entry=new sql_buildlog_entry($task->id, $msg++, time(), 'stdout', $c); $entry->write(); - //log_msg($c, false); } } if ($status['running'] === false) { @@ -40,6 +40,7 @@ function log_command(&$build, $command, $path=null, $env=null) { break; } } + $task->finish=time(); foreach ($pipes as $pipe) { fclose($pipe); } @@ -48,6 +49,11 @@ function log_command(&$build, $command, $path=null, $env=null) { } $task->exit=$exit_status; $task->write(); + if ($exit_status == 0) { + log_msg(color('[success]', 'green')); + } else { + log_msg(color('[exit code '.$exit_status.']', 'red')); + } // Handle end status return $exit_status; } diff --git a/backend/functions/log.php b/backend/functions/log.php index c07fd73..1a40a43 100644 --- a/backend/functions/log.php +++ b/backend/functions/log.php @@ -17,6 +17,6 @@ function color($msg, $color) { } } function indent($msg, $tabs=1) { - return str_replace("\n", "\n".str_repeat("\t", $tabs), $msg); + return str_repeat("\t", $tabs).str_replace("\n", "\n".str_repeat("\t", $tabs), trim($msg)); } ?> diff --git a/frontend/index.php b/frontend/index.php index 0677c58..b66257a 100644 --- a/frontend/index.php +++ b/frontend/index.php @@ -63,8 +63,10 @@ for ($line=fgets($routing, 32768); !feof($routing); $line=fgets($routing, 32768) $dest=$value; } } else { - require_once('include/header.php'); - die(print_error('Routing Failure', 'Init function undefined for '.$dest.'.')); + debug('routing','No init function for '.$dest.'... continuing to body'); + break; +// require_once('include/header.php'); +// die(print_error('Routing Failure', 'Init function undefined for '.$dest.'.')); } } if (!$S['notemplates']) { diff --git a/frontend/pages/wizard.php b/frontend/pages/wizard.php index dee3c57..e9af804 100644 --- a/frontend/pages/wizard.php +++ b/frontend/pages/wizard.php @@ -1,32 +1,79 @@ query('SELECT * FROM `builds` WHERE `owner`='.$S['user']->id.' AND `status`="config/step'.$step.'"'); + if ($r->rowCount() == 0) { + debug('wizard', 'Build not found!'); + // Build + unset($request['step']); + return 'wizard'; + } + $S['build']=new sql_build($r->fetch(PDO::FETCH_ASSOC)); + require_once(FRONTEND.'/pages/wizard/step'.$step.'.php'); + $proc='process_wizard_step'.$step; + if (function_exists($proc)) { + $proc(); } else { - debug('Not numeric, or file not existant for '.$keys[0]); + debug('wizard', 'No processing function for step '.$step); } + $step++; + if (is_file(FRONTEND.'/pages/wizard/step'.$step.'.php')) { + $S['build']->status='config/step'.$step; + $S['build']->write(); + $S['title']='Create - Step '.$step; + return 'wizard/step'.$step; + } else { + $S['build']->status='build/ready'; + $S['build']->ctime=time(); + $S['build']->write(); + return array('title' => 'Create - Finished'); + } + } else { + debug('wizard', 'step[0] not numeric, or step '.htmlentities($step).' not existant'); } - return array('title' => 'Create'); } else { - return 'login'; + $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `owner`='.$S['user']->id.' AND `status` LIKE "config/step0"'); + if ($r->rowCount()) { + $S['build']=new sql_build($r->fetch(PDO::FETCH_ASSOC)); + } else { + $S['build']=new sql_build(); + $S['build']->init(); + } } + return array('title' => 'Create'); } function body_wizard() { global $S; - echo '

Request an image built

Name of your build (optional):
Profile:
Profile:
'; + } else { + echo '

Config finished!

Check your build\'s status here

'; } - echo '
'; +} +function process_wizard_step0() { + global $S, $request; + $S['build']->name=$request['name']; + $profile=new sql_profile($request['pkgdir']); + $profileopt=new sql_buildopt($S['build']->id, 'profile', $profile->pkgdir); // TODO pkgdir -> id + $profileopt->write(); + $S['build']->write(); +} +// One day this is going to be necessary +function body_wizard_step0() { + body_wizard(); } ?> diff --git a/frontend/pages/wizard/step0.php b/frontend/pages/wizard/step0.php new file mode 120000 index 0000000..cfe4a1c --- /dev/null +++ b/frontend/pages/wizard/step0.php @@ -0,0 +1 @@ +../wizard.php \ No newline at end of file diff --git a/frontend/pages/wizard/step1.php b/frontend/pages/wizard/step1.php new file mode 100644 index 0000000..baef871 --- /dev/null +++ b/frontend/pages/wizard/step1.php @@ -0,0 +1,11 @@ + 'Create - Step 1'); +} +function body_wizard_step1() { + global $S; + $build=&$S['build']; + $opts=$build->get_buildopts(); + echo '

Step 1

'; +} +?> diff --git a/shared/classes/0sql_row_obj.php b/shared/classes/0sql_row_obj.php index cf82e4b..0d75d37 100644 --- a/shared/classes/0sql_row_obj.php +++ b/shared/classes/0sql_row_obj.php @@ -1,5 +1,6 @@ array ( 'type' => 'CHAR', 'length' => 6, 'not_null' => true, - 'unique' => true ), 'owner' => array ( 'type' => 'INT', 'length' => 10, 'unsigned' => true, - 'not_null' => true + 'not_null' => true, + 'refers to' => 'users.id' ), 'name' => array ( 'type' => 'VARCHAR', @@ -22,6 +22,11 @@ class sql_build extends sql_row_obj { 'length' => 255, 'not_null' => true ), + 'ctime' => array ( + 'type' => 'INT', + 'length' => 10, + 'unsigned' => true + ), 'start' => array ( 'type' => 'INT', 'length' => 10, @@ -32,7 +37,37 @@ class sql_build extends sql_row_obj { 'length' => 10, 'unsigned' => true ) - ); + // Generates a unique id and sets status to config/step0, writes self to db and returns id + public function init() { + global $S; + $this->owner=$S['user']->id; + $this->status='config/step0'; + $fails=0; + while (true) { + $id=randstring(6); + debug("Trying id=$id..."); + $r=$S['pdo']->query('SELECT `id` FROM `builds` WHERE `id`="'.$id.'"'); + if ($r->rowCount() == 0) { + break; + } + if (++$fails == 10) { + throw new Exception('Failed 10 times to find a unique build id... this shouldn\'t happen.'); + } + } + $this->id=$id; + $this->write(); + return $this->id; + } + // Fetches all available buildopts pertaining to this build in a nice array + function get_buildopts() { + global $S; + $r=$S['pdo']->query('SELECT * FROM `buildopts` WHERE `build`="'.$this->id.'"'); + $opts=array(); + while ($opt=$r->fetch(PDO::FETCH_ASSOC)) { + $opt=new sql_buildopt($opt); + $opts=&$opt; + } + } } ?> diff --git a/shared/classes/buildlog_entry.php b/shared/classes/buildlog_entry.php index f060e9e..69d689f 100644 --- a/shared/classes/buildlog_entry.php +++ b/shared/classes/buildlog_entry.php @@ -6,7 +6,7 @@ class sql_buildlog_entry extends sql_row_obj { 'length' => 10, 'unsigned' => true, 'not_null' => true, - 'default' => 0 + 'refers to' => 'tasks.id' ), 'order' => array ( 'type' => 'INT', diff --git a/shared/classes/buildopt.php b/shared/classes/buildopt.php index e05192b..4999585 100644 --- a/shared/classes/buildopt.php +++ b/shared/classes/buildopt.php @@ -4,7 +4,8 @@ class sql_buildopt extends sql_row_obj { 'build' => array ( 'type' => 'CHAR', 'length' => 6, - 'not_null' => true + 'not_null' => true, + 'refers to' => 'builds.id' ), 'name' => array ( 'type' => 'VARCHAR', diff --git a/shared/classes/session.php b/shared/classes/session.php index 9b8a0a2..2cab1cb 100644 --- a/shared/classes/session.php +++ b/shared/classes/session.php @@ -10,7 +10,8 @@ class sql_session extends sql_row_obj { 'type' => 'INT', 'length' => 10, 'unsigned' => true, - 'not_null' => true + 'not_null' => true, + 'refers to' => 'users.id' ), 'atime' => array ( 'type' => 'INT', diff --git a/shared/classes/task.php b/shared/classes/task.php index 3eecdeb..cdea343 100644 --- a/shared/classes/task.php +++ b/shared/classes/task.php @@ -5,23 +5,36 @@ class sql_task extends sql_row_obj { 'type' => 'INT', 'length' => 10, 'unsigned' => true, - 'not null' => true, - 'auto increment' => true, + 'not_null' => true, + 'auto_increment' => true ), - 'build' => array( + 'build' => array ( 'type' => 'CHAR', 'length' => 6, - 'not null' => true, + 'not_null' => true, + 'default' => '' ), 'command' => array ( 'type' => 'VARCHAR', 'length' => 255, - 'not null' => true, + 'not_null' => true, + 'default' => '' + ), + 'start' => array ( + 'type' => 'INT', + 'length' => 10, + 'unsigned' => true, + 'not_null' => true + ), + 'end' => array ( + 'type' => 'INT', + 'length' => 10, + 'unsigned' => true ), 'exit' => array ( 'type' => 'TINYINT', 'length' => 3, - 'unsigned' => true, + 'unsigned' => true ) ); diff --git a/todo b/todo index f032b6e..01d1423 100644 --- a/todo +++ b/todo @@ -4,6 +4,12 @@ Investigate using testing branch of portage (2.2) Add a builds table and class, integrate it in backend Add signal handling to the backend so when you hit Ctrl-C, it doesn't leave the logs saying "running" for the current task Find kernels -Add a 'ctime' column to builds so we know which ones to start first -Add status column of builds to log viewer +Add status column of builds to log viewer (and ctime, start, finish) Post first weekly report to gentoo-dev, gentoo-soc +Move more functions into corresponding classes +Give profiles an auto_increment ID so they can have their directories moved without breaking the db +Have backend handle builds that it finds to already be running +Add a PID file so backend can't start twice +Add pagination to log viewer +Move over extra packages selection from mock-up +Implement extra packages in backend -- cgit v1.2.3-65-gdbad