Skip to content

Commit 2cff752

Browse files
committed
Add new pipeline job buttons to better interact with cluster
1 parent 74dfd10 commit 2cff752

File tree

6 files changed

+211
-2
lines changed

6 files changed

+211
-2
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
Ext4.namespace('Cluster.Utils');
2+
3+
Cluster.Utils = new function() {
4+
return {
5+
buttonHandlerForLog: function (dataRegionName) {
6+
const checked = LABKEY.DataRegions[dataRegionName].getChecked();
7+
if (!checked.length){
8+
Ext4.Msg.alert('Error', 'No rows selected');
9+
return;
10+
}
11+
else if (checked.length > 1){
12+
Ext4.Msg.alert('Error', 'Can only select one row at a time');
13+
return;
14+
}
15+
16+
window.location = LABKEY.ActionURL.buildURL('cluster', 'viewJavaLog', null, {jobId: checked[0]});
17+
},
18+
19+
recoverCompletedJobs: function (dataRegionName) {
20+
const checked = LABKEY.DataRegions[dataRegionName].getChecked();
21+
if (!checked.length){
22+
Ext4.Msg.alert('Error', 'No rows selected');
23+
return;
24+
}
25+
26+
window.location = LABKEY.ActionURL.buildURL('cluster', 'recoverCompletedJobs', null, {jobIds: checked.join(',')});
27+
},
28+
29+
forcePipelineCancel: function (dataRegionName) {
30+
const checked = LABKEY.DataRegions[dataRegionName].getChecked();
31+
if (!checked.length){
32+
Ext4.Msg.alert('Error', 'No rows selected');
33+
return;
34+
}
35+
36+
window.location = LABKEY.ActionURL.buildURL('cluster', 'forcePipelineCancel', null, {jobIds: checked.join(',')});
37+
}
38+
};
39+
};

cluster/src/org/labkey/cluster/ClusterController.java

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616

1717
package org.labkey.cluster;
1818

19+
import org.apache.commons.io.FilenameUtils;
1920
import org.apache.commons.lang3.StringUtils;
2021
import org.apache.logging.log4j.LogManager;
2122
import org.apache.logging.log4j.Logger;
2223
import org.labkey.api.action.ConfirmAction;
24+
import org.labkey.api.action.SimpleRedirectAction;
2325
import org.labkey.api.action.SpringActionController;
2426
import org.labkey.api.data.DbSchema;
2527
import org.labkey.api.data.DbSchemaType;
2628
import org.labkey.api.data.Table;
2729
import org.labkey.api.data.TableInfo;
30+
import org.labkey.api.pipeline.PipeRoot;
2831
import org.labkey.api.pipeline.PipelineJob;
2932
import org.labkey.api.pipeline.PipelineJobException;
3033
import org.labkey.api.pipeline.PipelineJobService;
@@ -35,6 +38,9 @@
3538
import org.labkey.api.security.RequiresPermission;
3639
import org.labkey.api.security.RequiresSiteAdmin;
3740
import org.labkey.api.security.permissions.AdminPermission;
41+
import org.labkey.api.security.permissions.ReadPermission;
42+
import org.labkey.api.settings.AppProps;
43+
import org.labkey.api.settings.ResourceURL;
3844
import org.labkey.api.util.HtmlString;
3945
import org.labkey.api.util.PageFlowUtil;
4046
import org.labkey.api.util.URLHelper;
@@ -110,9 +116,10 @@ public URLHelper getSuccessURL(JobIdsForm form)
110116

111117
public ModelAndView getConfirmView(JobIdsForm form, BindException errors) throws Exception
112118
{
119+
113120
return new HtmlView(HtmlString.unsafe("This will change the status of the pipeline job with the provided ID to Cancelled. It is intended to help the situation when the normal UI leave a job in a perpetual 'Cancelling' state." +
114121
"To continue, enter a comma-delimited list of Job IDs and hit submit:<br><br>" +
115-
"<label>Enter Job ID(s): </label><input name=\"jobIds\"><br>"));
122+
"<label>Enter Job ID(s): </label><input name=\"jobIds\" value = \"" + form.getJobIds() + "\"><br>"));
116123
}
117124

118125
public boolean handlePost(JobIdsForm form, BindException errors) throws Exception
@@ -274,7 +281,7 @@ public ModelAndView getConfirmView(JobIdsForm form, BindException errors) throws
274281
{
275282
return new HtmlView(HtmlString.unsafe("This will attempt to re-queue existing pipeline jobs using their serialized JSON text files. It is intended as a workaround for the situation where a job has been marked complete." +
276283
"To continue, enter a comma-delimited list of Job IDs and hit submit:<br><br>" +
277-
"<label>Enter Job ID(s): </label><input name=\"jobIds\"><br>"));
284+
"<label>Enter Job ID(s): </label><input name=\"jobIds\" value=\"" + form.getJobIds() + "\"><br>"));
278285
}
279286

280287
public boolean handlePost(JobIdsForm form, BindException errors) throws Exception
@@ -330,4 +337,96 @@ public boolean handlePost(JobIdsForm form, BindException errors) throws Exceptio
330337
return true;
331338
}
332339
}
340+
341+
@RequiresPermission(ReadPermission.class)
342+
public static class ViewJavaLogAction extends SimpleRedirectAction<ViewJavaLogForm>
343+
{
344+
@Override
345+
public void validate(ViewJavaLogForm viewJavaLogForm, BindException errors)
346+
{
347+
super.validate(viewJavaLogForm, errors);
348+
349+
if (viewJavaLogForm.getJobId() == null)
350+
{
351+
errors.reject(ERROR_MSG, "Must provide JobId");
352+
}
353+
354+
PipelineStatusFile sf = PipelineService.get().getStatusFile(viewJavaLogForm.getJobId());
355+
if (sf == null)
356+
{
357+
errors.reject(ERROR_MSG, "Unknown job: " + viewJavaLogForm.getJobId());
358+
}
359+
else if (!sf.lookupContainer().hasPermission(getUser(), ReadPermission.class))
360+
{
361+
errors.reject(ERROR_MSG, "The current user does not have permission to view the folder: " + sf.lookupContainer().getPath());
362+
}
363+
}
364+
365+
@Override
366+
public URLHelper getRedirectURL(ViewJavaLogForm viewJavaLogForm) throws Exception
367+
{
368+
PipelineStatusFile sf = PipelineService.get().getStatusFile(viewJavaLogForm.getJobId());
369+
File parentDir = new File(sf.getFilePath()).getParentFile();
370+
if (!parentDir.exists())
371+
{
372+
throw new IllegalArgumentException("Log directory doesnt exist: " + parentDir.getPath());
373+
}
374+
375+
File[] javaLogs = parentDir.listFiles((dir, name) -> {
376+
return name.endsWith(".java.log");
377+
});
378+
379+
if (javaLogs == null || javaLogs.length == 0)
380+
{
381+
throw new IllegalArgumentException("No files ending with java.log found: " + parentDir.getPath());
382+
}
383+
384+
long lastModifiedTime = Long.MIN_VALUE;
385+
File chosenFile = null;
386+
for (File file : javaLogs)
387+
{
388+
if (file.lastModified() > lastModifiedTime)
389+
{
390+
chosenFile = file;
391+
lastModifiedTime = file.lastModified();
392+
}
393+
}
394+
395+
PipeRoot root = PipelineService.get().getPipelineRootSetting(sf.lookupContainer());
396+
if (root == null)
397+
{
398+
throw new IllegalArgumentException("Unable to find pipeline root for folder: " + sf.lookupContainer().getPath());
399+
}
400+
401+
if (!root.isUnderRoot(chosenFile))
402+
{
403+
throw new IllegalArgumentException("Log file is not under the pipeline root for folder: " + sf.lookupContainer().getPath());
404+
}
405+
406+
String relPath = root.relativePath(chosenFile);
407+
if (relPath == null)
408+
{
409+
throw new IllegalArgumentException("Unable to find log file path for folder: " + sf.lookupContainer().getPath());
410+
}
411+
412+
relPath = org.labkey.api.util.Path.parse(FilenameUtils.separatorsToUnix(relPath)).encode();
413+
414+
return new ResourceURL(AppProps.getInstance().getBaseServerUrl() + root.getWebdavURL() + relPath);
415+
}
416+
}
417+
418+
public static class ViewJavaLogForm
419+
{
420+
private Integer _jobId;
421+
422+
public Integer getJobId()
423+
{
424+
return _jobId;
425+
}
426+
427+
public void setJobId(Integer jobId)
428+
{
429+
_jobId = jobId;
430+
}
431+
}
333432
}

cluster/src/org/labkey/cluster/ClusterModule.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.labkey.api.data.DbSchema;
2525
import org.labkey.api.data.DbSchemaType;
2626
import org.labkey.api.ldk.ExtendedSimpleModule;
27+
import org.labkey.api.ldk.LDKService;
2728
import org.labkey.api.module.Module;
2829
import org.labkey.api.module.ModuleContext;
2930
import org.labkey.api.module.ModuleLoader;
@@ -37,6 +38,9 @@
3738
import org.labkey.cluster.pipeline.ClusterPipelineJobNotificationProvider;
3839
import org.labkey.cluster.pipeline.ClusterPipelineProvider;
3940
import org.labkey.cluster.pipeline.TestCase;
41+
import org.labkey.cluster.query.ForceCancelJobsButton;
42+
import org.labkey.cluster.query.RecoverCompletedJobsButton;
43+
import org.labkey.cluster.query.ViewJavaLogButton;
4044

4145
import java.util.Arrays;
4246
import java.util.Collection;
@@ -92,6 +96,10 @@ public void doStartupAfterSpringConfig(ModuleContext moduleContext)
9296
PipelineService.get().registerPipelineProvider(new ClusterPipelineProvider(this));
9397

9498
PipelineService.get().registerPipelineJobNotificationProvider(new ClusterPipelineJobNotificationProvider());
99+
100+
LDKService.get().registerQueryButton(new ViewJavaLogButton(), "pipeline", "job");
101+
LDKService.get().registerQueryButton(new RecoverCompletedJobsButton(), "pipeline", "job");
102+
LDKService.get().registerQueryButton(new ForceCancelJobsButton(), "pipeline", "job");
95103
}
96104

97105
@Override
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.labkey.cluster.query;
2+
3+
import org.labkey.api.ldk.table.SimpleButtonConfigFactory;
4+
import org.labkey.api.module.ModuleLoader;
5+
import org.labkey.api.security.permissions.AdminPermission;
6+
import org.labkey.api.view.template.ClientDependency;
7+
import org.labkey.cluster.ClusterModule;
8+
9+
import java.util.Arrays;
10+
11+
public class ForceCancelJobsButton extends SimpleButtonConfigFactory
12+
{
13+
public ForceCancelJobsButton()
14+
{
15+
super(ModuleLoader.getInstance().getModule(ClusterModule.class), "Change Cancelling to Cancelled", "Cluster.Utils.forcePipelineCancel(dataRegionName);", Arrays.asList(
16+
ClientDependency.supplierFromPath("Ext4"),
17+
ClientDependency.supplierFromPath("cluster/window/Buttons.js")
18+
));
19+
20+
setPermission(AdminPermission.class);
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.labkey.cluster.query;
2+
3+
import org.labkey.api.ldk.table.SimpleButtonConfigFactory;
4+
import org.labkey.api.module.ModuleLoader;
5+
import org.labkey.api.security.permissions.AdminPermission;
6+
import org.labkey.api.view.template.ClientDependency;
7+
import org.labkey.cluster.ClusterModule;
8+
9+
import java.util.Arrays;
10+
11+
public class RecoverCompletedJobsButton extends SimpleButtonConfigFactory
12+
{
13+
public RecoverCompletedJobsButton()
14+
{
15+
super(ModuleLoader.getInstance().getModule(ClusterModule.class), "Requeue from Cluster JSON", "Cluster.Utils.recoverCompletedJobs(dataRegionName);", Arrays.asList(
16+
ClientDependency.supplierFromPath("Ext4"),
17+
ClientDependency.supplierFromPath("cluster/window/Buttons.js")
18+
));
19+
20+
setPermission(AdminPermission.class);
21+
}
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.labkey.cluster.query;
2+
3+
import org.labkey.api.ldk.table.SimpleButtonConfigFactory;
4+
import org.labkey.api.module.ModuleLoader;
5+
import org.labkey.api.view.template.ClientDependency;
6+
import org.labkey.cluster.ClusterModule;
7+
8+
import java.util.Arrays;
9+
10+
public class ViewJavaLogButton extends SimpleButtonConfigFactory
11+
{
12+
public ViewJavaLogButton()
13+
{
14+
super(ModuleLoader.getInstance().getModule(ClusterModule.class), "View Pipeline Java Log", "Cluster.Utils.buttonHandlerForLog(dataRegionName);", Arrays.asList(
15+
ClientDependency.supplierFromPath("Ext4"),
16+
ClientDependency.supplierFromPath("cluster/window/Buttons.js")
17+
));
18+
}
19+
}

0 commit comments

Comments
 (0)