File Coverage

blib/lib/Gruntmaster/Daemon.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Gruntmaster::Daemon;
2              
3 2     2   27091 use 5.014000;
  2         8  
  2         83  
4 2     2   11 use strict;
  2         4  
  2         70  
5 2     2   10 use warnings;
  2         7  
  2         101  
6              
7             our $VERSION = '5999.000_004';
8              
9 2     2   1151 use Gruntmaster::Daemon::Constants qw/ERR/;
  2         6  
  2         254  
10 2     2   1565 use Gruntmaster::Daemon::Format qw/prepare_files/;
  2         7  
  2         160  
11 2     2   148068 use Gruntmaster::Data;
  0            
  0            
12              
13             use File::Basename qw/fileparse/;
14             use File::Temp qw/tempdir/;
15             use JSON qw/decode_json encode_json/;
16             use Sys::Hostname qw/hostname/;
17             use Time::HiRes qw/time/;
18             use Try::Tiny;
19             use Log::Log4perl qw/get_logger/;
20             use LWP::UserAgent;
21              
22             use constant PAGE_SIZE => 10;
23             use constant FORMAT_EXTENSION => {
24             C => 'c',
25             CPP => 'cpp',
26             GCCGO => 'go',
27             GOLANG => 'go',
28             HASKELL => 'hs',
29             MONO => 'cs',
30             JAVA => 'java',
31             PASCAL => 'pas',
32             PERL => 'pl',
33             PYTHON => 'py',
34             };
35              
36             ##################################################
37              
38             my $db;
39             my $ua = LWP::UserAgent->new;
40             my @purge_hosts = exists $ENV{PURGE_HOSTS} ? split ' ', $ENV{PURGE_HOSTS} : ();
41              
42             sub safe_can_nodie {
43             my ($type, $sub, $name) = @_;
44              
45             return unless $name =~ /^\w+$/;
46             no strict 'refs';
47             my $pkg = __PACKAGE__ . "::${type}::${name}";
48             eval "require $pkg" or get_logger->warn("Error while requiring $pkg: $@");
49             $pkg->can($sub);
50             }
51              
52             sub safe_can {
53             my ($type, $sub, $name) = @_;
54              
55             safe_can_nodie @_ or get_logger->logdie("No such \l$type: '$name'");
56             }
57              
58             sub purge {
59             get_logger->trace("Purging $_[0]");
60             for my $host (@purge_hosts) {
61             my $req = HTTP::Request->new(PURGE => "http://$host$_[0]");
62             $ua->request($req)
63             }
64             }
65              
66             sub process{
67             my ($job, $jobr) = @_;
68              
69             my @results;
70             my @full_results = ();
71             my $meta;
72             our $errors = '';
73             try {
74             if (ref $job) {
75             $meta = $job;
76             } else {
77             $meta = {
78             problem => $jobr->problem->id,
79             files => {
80             prog => {
81             name => 'prog.' . $jobr->extension,
82             format => $jobr->format,
83             content => $jobr->source,
84             },
85             },
86             map { $_ => $jobr->problem->get_column($_) } qw/generator runner judge testcnt timeout olimit/
87             };
88             $meta->{tests} = decode_json $jobr->problem->tests if $meta->{runner} eq 'File';
89              
90             $meta->{files}{ver} = {
91             name => 'ver.' . FORMAT_EXTENSION->{$jobr->problem->verformat},
92             format => $jobr->problem->verformat,
93             content => $jobr->problem->versource,
94             } if $jobr->problem->verformat;
95             }
96              
97             prepare_files $meta;
98             chomp $errors;
99              
100             my ($files, $generator, $runner, $judge, $testcnt) = map { $meta->{$_} or die "Required parameter missing: $_"} qw/files generator runner judge testcnt/;
101              
102             $generator = safe_can Generator => generate => $generator;
103             $runner = safe_can Runner => run => $runner;
104             $judge = safe_can Judge => judge => $judge;
105              
106             for my $test (1 .. $testcnt) {
107             my $start_time = time;
108             my $result;
109             try {
110             $generator->($test, $meta);
111             $result = $runner->($test, $meta);
112             } catch {
113             $result = $_;
114             unless (ref $result) {
115             chomp $result;
116             $result = [ERR, $result];
117             }
118             };
119              
120             if (ref $result) {
121             get_logger->trace("Test $test result is " . $result->[1]);
122             push @full_results, {id => $test, result => $result->[0], result_text => $result->[1], time => time - $start_time}
123             } else {
124             get_logger->trace("Test $test result is $result");
125             push @full_results, {id => $test, result => 0, result_text => $result, time => time - $start_time}
126             }
127             push @results, $result;
128             last if $meta->{judge} eq 'Absolute' && ref $result
129             }
130              
131             my %results = $judge->(@results);
132             $meta->{$_} = $results{$_} for keys %results;
133             $meta->{results} = \@full_results
134             } catch {
135             s,(.*) at .*,$1,;
136             chomp;
137             $meta->{result} = -1;
138             $meta->{result_text} = $_;
139             };
140              
141             get_logger->info("Job result: " . $meta->{result_text});
142             return unless $jobr;
143             $jobr->update({
144             result => $meta->{result},
145             result_text => $meta->{result_text},
146             ($meta->{results} ? (results => encode_json $meta->{results}) : ()),
147             $errors ? (errors => $errors) : ()
148             });
149              
150             my $log = $jobr->contest ? 'ct/' . $jobr->contest->id . '/log' : 'log';
151             my $page = int (($job + PAGE_SIZE - 1) / PAGE_SIZE);
152              
153             purge "/$log/$job";
154             purge "/$log/";
155             purge "/$log/st";
156             purge "/$log/page/$_" for $page - 1, $page, $page + 1;
157             }
158              
159             sub got_job{
160             my $job = $_[0];
161             my $id = $job->id;
162             get_logger->debug("Taking job $id...");
163             my $daemon = hostname . ":$$";
164             $job->update({daemon => $daemon});
165             #if (set_job_daemon $job, hostname . ":$$") {
166             if (1) {
167             get_logger->debug("Succesfully taken job $id");
168             process $id, $job;
169             get_logger->debug("Job $id done");
170             } else {
171             get_logger->debug("Job $id already taken");
172             }
173             }
174              
175             sub run{
176             $db = Gruntmaster::Data->connect('dbi:Pg:');
177             Log::Log4perl->init('/etc/gruntmasterd/gruntmasterd-log.conf');
178             get_logger->info("gruntmasterd $VERSION started");
179             chdir tempdir 'gruntmasterd.XXXX', CLEANUP => 1, TMPDIR => 1;
180             while (1) {
181             my $job = $db->jobs->search({daemon => undef}, {rows => 1})->first;
182             got_job $job if defined $job;
183             sleep 2 unless defined $job;
184             }
185             }
186              
187             1;
188             __END__