aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Iurt/Ulri.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Iurt/Ulri.pm')
-rwxr-xr-xlib/Iurt/Ulri.pm185
1 files changed, 185 insertions, 0 deletions
diff --git a/lib/Iurt/Ulri.pm b/lib/Iurt/Ulri.pm
new file mode 100755
index 0000000..4140678
--- /dev/null
+++ b/lib/Iurt/Ulri.pm
@@ -0,0 +1,185 @@
+package Iurt::Ulri;
+
+use base qw(Exporter);
+use File::Path qw(make_path);
+use File::Temp qw(mktemp);
+use Iurt::Config qw(get_author_email);
+use Iurt::Util qw(plog ssh_setup ssh sput);
+use strict;
+
+our @EXPORT = qw(
+ build_package
+ warn_about_failure
+);
+
+sub build_package {
+ my ($config, $pkg_tree, $media, $prefix, $host, $arch, $bot) = @_;
+
+ plog('INFO', "building on $host/$arch ($bot)");
+
+ my $path = $pkg_tree->{$prefix}{media}{$media}{path};
+ my $todo_dir = "$config->{queue}/todo/$path";
+ my $target = $pkg_tree->{$prefix}{target};
+ my $srpms = $pkg_tree->{$prefix}{srpms};
+ my $user = get_author_email($pkg_tree->{$prefix}{user}) || $config->{packager};
+ $user =~ s/([<>])/\\$1/g;
+
+ my $bot_conf = $config->{bot}{$arch}{$host}{$bot};
+ my $remote = ssh_setup($config->{ssh_options},
+ $bot_conf->{user}, $host);
+
+ my $prefix_dir = "$bot_conf->{packages}/$path/$prefix-$arch/";
+ my $status_file = "$prefix_dir/log/status.log";
+
+ # Copy packages to build node
+ #
+ # create also the log dir for botcmd.log
+ if (ssh($remote, "mkdir -p $prefix_dir/log")) {
+ exclude_machine($config, $host);
+ next;
+ }
+ my $pkgs;
+ my $ok = 1;
+ foreach my $srpm (@$srpms) {
+ plog('NOTIFY', "Send to $host/$arch: $srpm");
+ $ok &&= !sput($remote, "$todo_dir/${prefix}_$srpm",
+ "$prefix_dir/$srpm");
+ $pkgs .= " $prefix_dir/$srpm";
+ }
+ if (!$ok) {
+ exclude_machine($config, $host);
+ return;
+ }
+
+ # spawn remote build bot and save output on local file
+ # (remove status.log before building, otherwise we can have
+ # a install_deps_failure and reschedule even if the package
+ # is currently building)
+ #
+ plog('DEBUG', "remove status file");
+ ssh($remote, "rm $status_file 2>/dev/null");
+
+ plog('INFO', "Execute build command on $host/$arch");
+
+ my $temp = mktemp("$config->{tmp}/ulri.tmp.$prefix.XXXXX");
+ my $cmd = $bot_conf->{command};
+ $cmd =~ s!__ARCH__!$arch!g;
+ $cmd =~ s!__DIR__!$path/$prefix-$arch!g;
+ $cmd =~ s!__TARGET__!$target!g;
+ $cmd =~ s!__PACKAGER__!$user!g;
+ my $section = $media;
+ $section =~ s!/.*$!!;
+ $cmd =~ s!__SECTION__!$section!g;
+
+ my $media_to_add;
+ my $medium = ref $config->{media}{$target}{$media} ? $target : 'default';
+ $media_to_add = join ' ', @{$config->{media}{$medium}{$media}};
+ plog('DEBUG', "Will compile only with media $media_to_add");
+ $cmd =~ s!__MEDIA__!$media_to_add!g;
+
+ #- allow x86_64 hosts to build i586 packages
+ if ($arch eq 'i586') {
+ $cmd = "setarch i586 $cmd";
+ }
+
+ plog('DEBUG', "Build $pkgs");
+ ssh($remote, "'echo PID=\$\$; exec $cmd $pkgs &>$prefix_dir/log/botcmd.\$(date +%s).\$(hostname -s).log' > $temp &");
+
+ # wait 10 seconds or until we have the log file
+ # plus 20 seconds if it timeouts.
+ #
+ if (check_file_timeout($temp, 10)) {
+ plog('WARN', "Timeout waiting for building start. Waiting more 20s.");
+ if (check_file_timeout($temp, 20)) {
+ plog('WARN', "Timeout! Abandoning the build.");
+ return;
+ }
+ }
+
+ # get remote PID from log file
+ #
+ my $pid = get_pid_from_file($temp);
+ unlink $temp;
+ plog('DEBUG', "remote pid $pid");
+ if (!$pid) {
+ plog('WARN', "pid is unknown, abandoning the build.");
+ return;
+ }
+
+ # Fork to wait for the build to finish
+ if (fork() == 0) {
+ local $SIG{ALRM} = sub {
+ # Run ourselves to kill the build
+ exec "ulri";
+ };
+ alarm $config->{faildelay};
+ # SSH to $host and wait up for $pid to exit
+ ssh($remote, "'while /bin/true; do ps $pid >/dev/null 2>&1 || exit; sleep 1; done'");
+ alarm 0;
+ # Fetch build results
+ exec "ulri";
+ }
+ return $pid;
+}
+
+sub check_file_timeout {
+ my ($file, $time) = @_;
+
+ my $i = 0;
+ while ($i < $time && (!-f $file || -z $file)) { sleep 1; $i++ }
+
+ $i == $time;
+}
+
+sub exclude_machine {
+ my ($config, $host) = @_;
+ plog('INFO', "Excluding build host $host");
+ foreach my $arch (keys %{$config->{bot}}) {
+ delete $config->{bot}{$arch}{$host};
+ }
+}
+
+sub get_pid_from_file {
+ my ($file) = @_;
+
+ my $pid;
+ open my $FILE, $file || die "FATAL: can't open $file";
+ local $_;
+ while (<$FILE>) { last if ($pid) = /^PID=(\d+)/ }
+
+ $pid;
+}
+
+sub warn_about_failure {
+ my ($config, $user, $ent, $arch, $fail_dir, $path, $prefix) = @_;
+ my $text = join("\n", "Build of the following packages failed:\n", map { "- $_" } @{$ent->{srpms}}) . "\n";
+ my $srpms = join(' ', @{$ent->{srpms}}, undef);
+
+ my $to = get_author_email($user) || "Unknown <$config->{admin}>";
+ my $cc;
+ my $fpath = "$config->{http_queue}/failure/$path/$prefix";
+ $fpath =~ tr!/!!s; # Squash double slashes ...
+ $fpath =~ s!/!//!; # ... except for http://
+
+ $text .= "\nFailure details available in $fpath/log\n";
+ $text .= "Reason:\n";
+ $text .= read_file("$fail_dir/$prefix/log/status.log");
+ $text .= "\nLog files generated:\n";
+
+ opendir my $DP1, "$fail_dir/$prefix/log/";
+ foreach my $f1 (sort(readdir($DP1))) {
+ next if ! -d "$fail_dir/$prefix/log/$f1" || $f1 =~ m/^\./;
+
+ opendir my $DP2, "$fail_dir/$prefix/log/$f1";
+ foreach my $f2 (readdir $DP2) {
+ next if $f2 =~ m/^\./;
+ $text .= "$fpath/log/$f1/$f2\n";
+ }
+ closedir $DP2;
+ }
+ closedir $DP1;
+
+ sendmail($to, $cc,
+ "Rebuild failed on $arch for $srpms", $text,
+ "Ulri the scheduler bot <$config->{admin}>", 0, $config);
+}