Passed
Push — master ( 86fd7b...3fe522 )
by Vitalii
01:34 queued 30s
created

app_list()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 35
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 17
nc 12
nop 1
dl 0
loc 35
rs 9.3888
c 1
b 0
f 0
1
<?php
2
// This file is part of BOINC.
3
// https://boinc.berkeley.edu
4
// Copyright (C) 2024 University of California
5
//
6
// BOINC is free software; you can redistribute it and/or modify it
7
// under the terms of the GNU Lesser General Public License
8
// as published by the Free Software Foundation,
9
// either version 3 of the License, or (at your option) any later version.
10
//
11
// BOINC is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
// See the GNU Lesser General Public License for more details.
15
//
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
18
19
// web interface for managing BUDA science apps
20
//
21
// in the following, 'app' means BUDA science app
22
// and 'variant' means a variant of one of these (e.g. CPU, GPU)
23
24
require_once('../inc/util.inc');
25
require_once('../inc/sandbox.inc');
26
require_once('../inc/submit_util.inc');
27
28
display_errors();
29
30
$buda_root = "../../buda_apps";
31
32
// show list of BUDA apps and variants,
33
// w/ buttons for adding and deleting
34
//
35
function app_list($notice=null) {
36
    global $buda_root;
37
    if (!is_dir($buda_root)) {
38
        mkdir($buda_root);
39
    }
40
    page_head('BUDA science apps');
41
    if ($notice) {
42
        echo "$notice <p>\n";
43
    }
44
    text_start();
45
    echo "
46
        <p>BUDA (BOINC Universal Docker App)
47
        lets you submit Docker jobs through a web interface;
48
        you don't need to log into the BOINC server.
49
        <p>
50
        To use BUDA, you set up a 'science app' and one or more 'variants'.
51
        Each variant includes a Dockerfile,
52
        a main program to run within the container,
53
        and any other files that are needed.
54
        <p>
55
        Typically there is a variant named 'cpu' that uses one CPU.
56
        The names of other variants are plan class names;
57
        these can be versions that use a GPU or multiple CPUs.
58
    ";
59
60
    echo "<h2>Science apps</h2>";
61
    $dirs = scandir($buda_root);
62
    foreach ($dirs as $dir) {
63
        if ($dir[0] == '.') continue;
64
        show_app($dir);
65
    }
66
    echo '<hr>';
67
    show_button_small('buda.php?action=app_form', 'Add science app');
68
    text_end();
69
    page_tail();
70
}
71
72
function show_app($dir) {
73
    global $buda_root;
74
    echo "<hr><font size=+3>$dir</font>\n";
75
    start_table('table-striped');
76
    table_header('Variant name (click for details)', 'Submit jobs');
77
    $pcs = scandir("$buda_root/$dir");
78
    foreach ($pcs as $pc) {
79
        if ($pc[0] == '.') continue;
80
        table_row(
81
            "<a href=buda.php?action=variant_view&app=$dir&variant=$pc>$pc</href>",
82
            button_text(
83
                "buda_submit.php?app=$dir&variant=$pc", "Submit"
84
            )
85
        );
86
    }
87
    end_table();
88
    echo "<p>";
89
    show_button("buda.php?action=variant_form&app=$dir", 'Add variant');
90
    echo "<p>";
91
    show_button_small(
92
        "buda.php?action=app_delete&app=$dir", "Delete science app '$dir'"
93
    );
94
}
95
96
function variant_view() {
97
    global $buda_root;
98
    $app = get_str('app');
99
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
100
    $variant = get_str('variant');
101
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
102
    page_head("App $app variant $variant");
103
    $dir = "$buda_root/$app/$variant";
104
    start_table();
105
    table_header('name', 'size', 'md5');
106
    foreach(scandir($dir) as $f) {
107
        if ($f[0] == '.') continue;
108
        [$md5, $size] = parse_info_file("$dir/.md5/$f");
109
        table_row(
110
            "<a href=buda.php?action=view_file&app=$app&variant=$variant&fname=$f>$f</a>",
111
            $size,
112
            $md5
113
        );
114
    }
115
    end_table();
116
    show_button_small(
117
        "buda.php?action=variant_delete&app=$dir&variant=$variant",
118
        'Delete variant'
119
    );
120
    page_tail();
121
}
122
123
// form for creating app variant.
124
// Currently doesn't support indirect files.
125
// doing this would require checkboxes for indirect
126
//
127
// Could have other stuff like
128
//      - min_quorum, max_total_results
129
//      - rsc_disk_bound, rsc_memory_bound
130
// or does this belong in job submission?
131
//
132
function variant_form($user) {
133
    $sbitems = sandbox_select_items($user);
134
    $app = get_str('app');
135
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
136
137
    page_head("Create variant of Docker app $app");
138
    form_start('buda.php');
139
    form_input_hidden('app', $app);
140
    form_input_hidden('action', 'variant_action');
141
    form_input_text('Plan class', 'variant');
142
    form_select('Dockerfile', 'dockerfile', $sbitems);
143
    form_select_multiple('Application files', 'app_files', $sbitems);
144
    form_input_text('Input file names', 'input_file_names');
145
    form_input_text('Output file names', 'output_file_names');
146
    form_submit('OK');
147
    form_end();
148
    page_tail();
149
}
150
151
function buda_file_phys_name($app, $variant, $md5) {
152
    return sprintf('buda_%s_%s_%s', $app, $variant, $md5);
153
}
154
155
// copy file from sandbox to variant dir, and stage to download hier
156
//
157
function copy_and_stage_file($user, $fname, $dir, $app, $variant) {
158
    copy_sandbox_file($user, $fname, $dir);
159
    [$md5, $size] = parse_info_file("$dir/.md5/$fname");
160
    $phys_name = buda_file_phys_name($app, $variant, $md5);
161
    stage_file_aux("$dir/$fname", $md5, $size, $phys_name);
162
    return $phys_name;
163
}
164
165
// create variant
166
//
167
function variant_action($user) {
168
    global $buda_root;
169
    $variant = get_str('variant');
170
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
171
    $app = get_str('app');
172
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
173
    $dockerfile = get_str('dockerfile');
174
    if (!is_valid_filename($dockerfile)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
175
    $app_files = get_array('app_files');
176
    foreach ($app_files as $fname) {
177
        if (!is_valid_filename($fname)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
178
    }
179
    $input_file_names = explode(' ', get_str('input_file_names'));
180
    $output_file_names = explode(' ', get_str('output_file_names'));
181
    foreach ($input_file_names as $fname) {
182
        if (!is_valid_filename($fname)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
183
    }
184
    foreach ($output_file_names as $fname) {
185
        if (!is_valid_filename($fname)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
186
    }
187
188
    if (file_exists("$buda_root/$app/$variant")) {
189
        error_page("Variant '$variant' already exists.");
190
    }
191
    $dir = "$buda_root/$app/$variant";
192
    mkdir($dir);
193
    mkdir("$dir/.md5");
194
195
    // create variant description JSON file
196
    //
197
    $desc = new StdClass;
198
    $desc->dockerfile = $dockerfile;
199
    $desc->app_files = $app_files;
200
    $desc->input_file_names = $input_file_names;
201
    $desc->output_file_names = $output_file_names;
202
203
    // copy files from sandbox to variant dir
204
    //
205
    $pname = copy_and_stage_file($user, $dockerfile, $dir, $app, $variant);
206
    $desc->dockerfile_phys = $pname;
207
    $desc->app_files_phys = [];
208
    foreach ($app_files as $fname) {
209
        $pname = copy_and_stage_file($user, $fname, $dir, $app, $variant);
210
        $desc->app_files_phys[] = $pname;
211
    }
212
213
    file_put_contents(
214
        "$dir/variant.json",
215
        json_encode($desc, JSON_PRETTY_PRINT)
216
    );
217
218
    // Note: we don't currently allow indirect file access.
219
    // If we did, we'd need to create job.toml to mount project dir
220
221
    app_list("Variant $variant added for app $app.");
222
}
223
224
function variant_delete() {
225
    global $buda_root;
226
    $app = get_str('app');
227
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
228
    $variant = get_str('variant');
229
    if (!is_valid_filename($variant)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
230
    $confirmed = get_str('confirmed', true);
231
    if ($confirmed) {
232
        $dir = "$buda_root/$app/$variant";
233
        if (!file_exists($dir)) error_page('no such variant');
234
        // delete staged files
235
        //
236
        foreach (scandir("$dir/.md5") as $fname) {
237
            if ($fname[0] == '.') continue;
238
            [$md5, $size] = parse_info_file("$dir/.md5/$fname");
239
            $phys_name = buda_file_phys_name($app, $variant, $md5);
240
            $phys_path = download_hier_path($phys_name);
241
            unlink($phys_path);
242
            unlink("$phys_path.md5");
243
        }
244
        system("rm -r $buda_root/$app/$variant", $ret);
245
        if ($ret) {
246
            error_page("delete failed");
247
        }
248
        $notice = "Variant $variant of app $app removed.";
249
        app_list($notice);
250
    } else {
251
        page_head("Confirm");
252
        echo "Are you sure you want to delete variant $variant of app $app?  <p>";
253
        show_button(
254
            "buda.php?action=variant_delete&app=$app&variant=$variant&confirmed=yes",
255
            "Yes"
256
        );
257
        page_tail();
258
    }
259
}
260
261
function app_delete() {
262
    global $buda_root;
263
    $app = get_str('app');
264
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
265
    $confirmed = get_str('confirmed', true);
266
    if ($confirmed) {
267
        $dir = "$buda_root/$app";
268
        if (!file_exists($dir)) error_page('no such app');
269
        foreach (scandir($dir) as $fname) {
270
            if ($fname[0] == '.') continue;
271
            error_page("You must delete all variants first.");
272
        }
273
        system("rmdir $buda_root/$app", $ret);
274
        if ($ret) {
275
            error_page('delete failed');
276
        }
277
        $notice = "App $app removed.";
278
        app_list($notice);
279
    } else {
280
        page_head('Confirm');
281
        echo "Are you sure you want to delete app $app?  <p>";
282
        show_button(
283
            "buda.php?action=app_delete&app=$app&confirmed=yes",
284
            "Yes"
285
        );
286
        page_tail();
287
    }
288
}
289
290
function app_form() {
291
    page_head('Create Docker app');
292
    form_start();
0 ignored issues
show
Bug introduced by
The call to form_start() has too few arguments starting with action. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

292
    /** @scrutinizer ignore-call */ 
293
    form_start();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
293
    form_input_text('Name', 'name');
294
    form_submit('OK');
295
    form_end();
296
    page_tail();
297
}
298
299
function app_action() {
300
    global $buda_root;
301
    $name = get_str('name');
302
    if (!is_valid_filename($name)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
303
    $dir = "$buda_root/$name";
304
    if (file_exists($dir)) {
305
        error_page("App $name already exists.");
306
    }
307
    mkdir($dir);
308
    header("Location: buda.php");
309
}
310
311
function view_file() {
312
    global $buda_root;
313
    $app = get_str('app');
314
    if (!is_valid_filename($app)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
315
    $variant = get_str('variant');
316
    if (!is_valid_filename($arg)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Comprehensibility Best Practice introduced by
The variable $arg seems to be never defined.
Loading history...
317
    $fname = get_str('fname');
318
    if (!is_valid_filename($fname)) die('bad arg');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
319
    echo "<pre>\n";
320
    readfile("$buda_root/$app/$variant/$fname");
321
    echo "</pre>\n";
322
}
323
324
$user = get_logged_in_user();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $user is correct as get_logged_in_user() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
325
$action = get_str('action', true);
326
switch ($action) {
327
case 'app_form':
328
    app_form(); break;
329
case 'app_action':
330
    app_action(); break;
331
case 'app_delete':
332
    app_delete(); break;
333
case 'variant_view':
334
    variant_view($user); break;
0 ignored issues
show
Unused Code introduced by
The call to variant_view() has too many arguments starting with $user. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

334
    /** @scrutinizer ignore-call */ 
335
    variant_view($user); break;

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
335
case 'variant_form':
336
    variant_form($user); break;
337
case 'variant_action':
338
    variant_action($user); break;
339
case 'variant_delete':
340
    variant_delete(); break;
341
case 'view_file':
342
    view_file(); break;
343
case null:
344
    app_list(); break;
345
default:
0 ignored issues
show
Coding Style introduced by
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
Coding Style introduced by
DEFAULT case must have a breaking statement
Loading history...
346
    error_page("unknown action $action");
347
}
348
349
?>
350