Skip to content

Commit 4fbfe7f

Browse files
committed
Add initial QA runner
1 parent 4c8039c commit 4fbfe7f

File tree

1 file changed

+272
-0
lines changed

1 file changed

+272
-0
lines changed

service-scripts/p3x-run-qa.pl

+272
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
#
2+
# Run one or more QA tests. Assume user is in an App-Appname directory
3+
#
4+
5+
use strict;
6+
use POSIX;
7+
use Data::Dumper;
8+
use Getopt::Long::Descriptive;
9+
use File::Path qw(make_path);
10+
use File::Basename;
11+
use JSON::XS;
12+
use IPC::Run qw(run);
13+
use Cwd qw(getcwd abs_path);
14+
use File::Slurp;
15+
use File::Temp;
16+
17+
my $json = JSON::XS->new->pretty(1)->canonical(1);
18+
19+
my($opt, $usage) = describe_options("%c %o [test-params.json]",
20+
["container|c=s" => "Container id to run with"],
21+
["submit|s" => "Submit the job to the scheduler"],
22+
["base|b=s" => "Use this directory as the deployment base for the run (not for use with --submit)"],
23+
["app|a=s" => "Application name"],
24+
['override=s@' => "Override other parameter settings in app parameter file", { default => [] }],
25+
["out|o=s" => "Use this workspace path as the output base",
26+
{ default => '/olson@patricbrc.org/PATRIC-QA/applications' }],
27+
["help|h" => "Show this help message"],
28+
);
29+
$usage->die() if @ARGV > 1;
30+
print($usage->text), exit 0 if $opt->help;
31+
32+
my $hostname = `hostname`;
33+
chomp $hostname;
34+
my $here = getcwd;
35+
my @container_paths = qw(/disks/patric-common/container-cache /vol/patric3/production/containers);
36+
37+
my $tmp = "$here/tmp";
38+
make_path($tmp);
39+
$ENV{TMPDIR} = $tmp;
40+
41+
#
42+
# Determine deployment base.
43+
#
44+
my $base = $opt->base;
45+
my @specs_dirs;
46+
if (!$base)
47+
{
48+
$base = $ENV{KB_TOP};
49+
#
50+
# Need specs dir.
51+
#
52+
my $specs = "$base/services/app_service/app_specs";
53+
if (-d $specs)
54+
{
55+
@specs_dirs = ($specs);
56+
}
57+
else
58+
{
59+
#
60+
# In a dev container. Enumerate them.
61+
#
62+
@specs_dirs = glob("$base/modules/*/app_specs");
63+
}
64+
}
65+
66+
67+
my $app = $opt->app;
68+
if (!defined($app))
69+
{
70+
if ($here =~ m,/App-(\S+)$,)
71+
{
72+
$app = $1;
73+
}
74+
else
75+
{
76+
die "Application not found for run in directory $here\n";
77+
}
78+
}
79+
80+
#
81+
# Find our app spec.
82+
#
83+
my $app_spec;
84+
if (!$opt->submit)
85+
{
86+
for my $path (@specs_dirs)
87+
{
88+
my $s = "$path/$app.json";
89+
if (-s $s)
90+
{
91+
$app_spec = $s;
92+
last;
93+
}
94+
}
95+
$app_spec or die "Could not find app spec for $app\n";
96+
}
97+
98+
my @bindings = ("/disks/tmp:/tmp",
99+
$here,
100+
"/opt/patric-data-2020-0914a:/opt/patric-common/data",
101+
);
102+
103+
my @input;
104+
if (@ARGV > 0)
105+
{
106+
my $inp = shift;
107+
$inp = abs_path($inp);
108+
push(@bindings, dirname($inp));
109+
110+
push(@input, $inp);
111+
}
112+
else
113+
{
114+
for my $inp (glob("tests/*.json"))
115+
{
116+
push(@input, abs_path($inp));
117+
}
118+
}
119+
120+
#
121+
# We set up an output directory based on the current date
122+
# and time and the name of the input file.
123+
#
124+
125+
my $app_name = "App-$app";
126+
127+
my $now = time;
128+
my $output_base = strftime("%Y/%m/%d/%H-%M-%S", localtime $now);
129+
my $output_path = $opt->out . "/$app_name/$output_base";
130+
my $output_file = strftime("out.%Y-%m-%d-%H-%M-%S", localtime $now);
131+
132+
#
133+
# For each of our input files, rewrite input json to have the changed output location.
134+
#
135+
136+
my @to_run;
137+
for my $input (@input)
138+
{
139+
push @to_run, rewrite_input($input, $opt, $output_path, $output_file);
140+
}
141+
142+
#
143+
# Now we may execute. If we are using cluster submission, use appserv-start-app. Otherwise
144+
# we will start at command line, optionally within a container.
145+
#
146+
for my $dat (@to_run)
147+
{
148+
my($params, $out_dir) = @$dat;
149+
if ($opt->submit)
150+
{
151+
submit_job($app, $params, $out_dir, $opt->container);
152+
}
153+
elsif ($opt->container)
154+
{
155+
run_in_container($app, $app_spec, $params, $out_dir, $opt->container);
156+
}
157+
else
158+
{
159+
run_locally($app, $app_spec, $params, $out_dir);
160+
}
161+
}
162+
163+
sub run_in_container
164+
{
165+
my($app, $spec, $params, $out_dir, $container_id) = @_;
166+
167+
#
168+
# Find our container.
169+
#
170+
my $container;
171+
for my $p (@container_paths)
172+
{
173+
my $c = "$p/$container_id.sif";
174+
if (-s $c)
175+
{
176+
$container = $c;
177+
}
178+
}
179+
$container or die "Cannot find container $container_id in @container_paths\n";
180+
181+
my $bindings = join(",", @bindings);
182+
183+
my @cmd = ("singularity", "exec", "--env", "KB_INTERACTIVE=1", "-B", $bindings, $container, "App-$app", "xx", $spec, $params);
184+
print "Run @cmd\n";
185+
my $ok = run(\@cmd,
186+
'>', "$out_dir/stdout.log",
187+
'2>', "$out_dir/stderr.log",
188+
);
189+
my $exitcode = $?;
190+
write_file("$out_dir/exitcode", "$exitcode\n");
191+
write_file("$out_dir/hostname", "$hostname\n");
192+
$ok or die "Error running @cmd\n";
193+
194+
}
195+
196+
sub run_locally
197+
{
198+
my($app, $spec, $params, $out_dir) = @_;
199+
200+
#
201+
# We need to submit the run with an environment configured
202+
# with the current properly if $base was set. Ignore this for now.
203+
#
204+
205+
my @cmd = ("App-$app", "xx", $spec, $params);
206+
print "Run @cmd\n";
207+
my $ok = run(\@cmd,
208+
init => sub { $ENV{KB_INTERACTIVE} = 1 },
209+
'>', "$out_dir/stdout.log",
210+
'2>', "$out_dir/stderr.log",
211+
);
212+
my $exitcode = $?;
213+
write_file("$out_dir/exitcode", "$exitcode\n");
214+
write_file("$out_dir/hostname", "$hostname\n");
215+
$ok or die "Error running @cmd\n";
216+
217+
}
218+
219+
sub submit_job
220+
{
221+
my($app, $params, $out_dir, $container) = @_;
222+
my @cmd = ('appserv-start-app');
223+
push(@cmd, '-c', $container) if $container;
224+
push(@cmd, $app, $params);
225+
226+
my $out;
227+
my $ok = run(\@cmd, ">", \$out);
228+
print $out;
229+
if ($out =~ /Started\s+task\s+(\d+)/)
230+
{
231+
write_file("$out_dir/task_id", "$1\n");
232+
}
233+
$ok or die "Error running @cmd\n";
234+
}
235+
236+
237+
sub rewrite_input
238+
{
239+
my($input, $opt, $output_path, $output_file) = @_;
240+
241+
my $params = $json->decode(scalar read_file($input));
242+
243+
my $this_base = join("/", $output_path, basename($input));
244+
245+
if ($params->{output_path} && $this_base)
246+
{
247+
# print STDERR "Change output path from $params->{output_path} to " . $this_base . "\n";
248+
$params->{output_path} = $this_base;
249+
}
250+
251+
if ($params->{output_file} && $output_file)
252+
{
253+
# print STDERR "Change output file from $params->{output_file} to " . $output_file . "\n";
254+
$params->{output_file} = $output_file;
255+
}
256+
257+
for my $ent (@{$opt->override})
258+
{
259+
my($k, $v) = split(/=/, $ent, 2);
260+
$params->{$k} = $v;
261+
}
262+
263+
#
264+
# if we are running locally or in a container, define output path
265+
#
266+
267+
my $out_dir = join("/", $here, strftime("%Y/%m/%d/%H-%M-%S", localtime $now), basename($input));
268+
make_path($out_dir);
269+
my $params_file = "$out_dir/" . basename($input);
270+
write_file($params_file, $json->encode($params));
271+
return [$params_file, $out_dir];
272+
}

0 commit comments

Comments
 (0)