Missing 'stdout' file when using Slurm and Singularity - slurm

I want to use Nextflow for creating pipelines of singularity components which will be executed by Slurm job scheduler.
I created a simple job to run just a single task:
#!/usr/bin/env nextflow
process createGroups {
executor = 'slurm'
queue = 'upgrade'
memory = '10 GB'
time = '30 min'
cpus = 8
clusterOptions = '--workdir /path/to/the/singularity/image'
"""
singularity run ...
"""
}
The job executes successfully and gives exit status 0, but Nextflow arises "Missing 'stdout' file" error. There is the full output:
Error executing process > 'createGroups'
Caused by:
Missing 'stdout' file: .../work/89/449f849ffc77a967be91ed994da860/.command.out for process > createGroups
Command executed:
executor > slurm (1)
[89/449f84] process > createGroups [100%] 1 of 1, failed: 1 ✘
Error executing process > 'createGroups'
Caused by:
Missing 'stdout' file: .../work/89/449f849ffc77a967be91ed994da860/.command.out for process > createGroups
Command executed:
singularity run ...
Command exit status:
0
Command output:
(empty)
Work dir:
.../work/89/449f849ffc77a967be91ed994da860
Tip: when you have fixed the problem you can continue the execution adding the option `-resume` to the run command line
I couldn't find any solution to fix this error :(
Update:
Using the slurm's --workdir parameter caused that error. I simply used "cd" command to change the path and it solved my problem.

Best to avoid calling Singularity manually, like in your script block. Assuming Singularity will be in your $PATH on every execution/compute node in your queue, then, all you'd need, for example, is:
In your nextflow.config:
process {
executor = 'slurm'
}
singularity {
enabled = true
cacheDir = '/path/to/containers'
}
Then, your nextflow script would look like:
process bwa_mem {
container = 'quay.io/biocontainers/bwa:0.7.17--h5bf99c6_8'
queue = 'upgrade'
cpus = 8
memory = '10 GB'
time = '30.m'
"""
bwa mem
"""
}
See also: https://www.nextflow.io/docs/latest/singularity.html

Related

Is it possible to have an output feedback to an input in a single nextflow process?

I am trying to make a simple feedback loop in my nextflow script. I am getting a weird error message that I do not know how to debug. My attempt is modeled after the NextFlow design pattern described here. I need a value to be calculated from a python3 script that operates on an image but pass that value on to subsequent executions of the script. At this stage I just want to get the structure right by adding numbers but I cannot get that to work yet.
my script
feedback_ch = Channel.create()
input_ch = Channel.from(feedback_ch)
process test {
echo true
input:
val chan_a from Channel.from(1,2,3)
val feedback_val from input_ch
output:
stdout output_val into feedback_ch
shell:
'''
#!/usr/bin/python3
new_val = !{chan_a} + !{feedback_val}
print(new_val)
'''
}
The error message I get
Error executing process > 'test (1)'
Caused by:
Process `test (1)` terminated with an error exit status (1)
Command executed:
#!/usr/bin/python3
new_val = 1 + DataflowQueue(queue=[])
print(new_val)
Command exit status:
1
Command output:
(empty)
Command error:
Traceback (most recent call last):
File ".command.sh", line 3, in <module>
new_val = 1 + DataflowQueue(queue=[])
NameError: name 'DataflowQueue' is not defined
Work dir:
executor > local (1)
[cd/67768e] process > test (1) [100%] 1 of 1, failed: 1 ✘
Error executing process > 'test (1)'
Caused by:
Process `test (1)` terminated with an error exit status (1)
Command executed:
#!/usr/bin/python3
new_val = 1 + DataflowQueue(queue=[])
print(new_val)
Command exit status:
1
Command output:
(empty)
Command error:
Traceback (most recent call last):
File ".command.sh", line 3, in <module>
new_val = 1 + DataflowQueue(queue=[])
NameError: name 'DataflowQueue' is not defined
Work dir:
/home/cv_proj/work/cd/67768e706f50d7675ae93645a0ce6e
Tip: you can replicate the issue by changing to the process work dir and entering the command `bash .command.run`
Anyone have any ideas?
The problem you have says, that you are passing empty DataflowQueue object with input_ch. Nextflow tries to execute it, so it substitutes your python code with variables, resulting in:
#!/usr/bin/python3
new_val = 1 + DataflowQueue(queue=[])
print(new_val)
What is nonsense (You want some number instead of DataflowQueue(queue=[]), don't you?).
Second problem is, that you don't have channels mixed, what seems to be important in this pattern. Anyway, I fixed it, to have proof of concept, working solution:
condition = { it.trim().toInteger() > 10 } // As your output is stdout, you need to trim() to get rid of newline. Then cast to Integer to compare.
feedback_ch = Channel.create()
input_ch = Channel.from(1,2,3).mix( feedback_ch.until(condition) ) // Mixing channel, so we have feedback
process test {
input:
val chan_a from input_ch
output:
stdout output_val into feedback_ch
shell:
var output_val_trimmed = chan_a.toString().trim()
// I am using double quotes, so nextflow interpolates variable above.
"""
#!/usr/bin/python3
new_val = ${output_val_trimmed} + ${output_val_trimmed}
print(new_val)
"""
}
I hope, that it at least set you on right track :)

How do I run an external file in soapui and take the output and set it as header

I would like to run an external .bat file using groovy script in soapUI. also would like to use the output generated from the external file as the value for the header
here is the script that I am using to run the bat file
String line
def p = "cmd /c C:\\Script\\S1.bat".execute()
def bri = new BufferedReader (new InputStreamReader(p.getInputStream()))
while ((line = bri.readLine()) != null) {log.info line}
here is the content of the bat file
java -jar SignatureGen.jar -pRESOURCE -nRandomString -mGET -d/api/discussion-streams/metadata -teyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJjbGllbnQiOiIxIiwicm9sZSI6IllGQURNSU4iLCJleHAiOjI3NTgzMjU2MDIsInRpIjo3MjAwNiwiaWF0IjoxNTU4MzI1NjAyLCJwZXJzb24iOiI1In0.bbci7ZBWmPsANN34Ris9H0-mosKF2JLTZ-530Rex2ut1kjCwprZr_196N-K1alFBH_A9pbG0MPspaDOnvOKOjA
The following code:
def p = "ls -la".execute()
def err = new StringBuffer()
def out = new StringBuffer()
p.waitForProcessOutput(out, err)
p.waitForOrKill(5000)
int ret = p.exitValue()
// optionally check the exit value and err for errors
println "ERR: $err"
println "OUT: $out"
// if you want to do something line based with the output
out.readLines().each { line ->
println "LINE: $line"
}
is based on linux, but translates to windows by just replacing the ls -la with your bat file invocation cmd /c C:\\Script\\S1.bat.
This executes the process, calls waitForProcessOutput to make sure the process doesn't block and that we are saving away the stdout and stderr streams of the process, and then waits for the process to finish using waitForOrKill.
After the waitForOrKill the process has either been terminated because it took too long, or it has completed normally. Whatever the case, the out variable will contain the output of the command. To figure out whether or not there was an error during bat file execution, you can inspect the ret and err variables.
I chose the waitForOrKill timeout at random, adjust to fit your needs. You can also use waitFor without a timeout which will wait until the process completes, but it is generally better to set some timeout to make sure your command doesn't execute indefinitely.

Is there any way to clean up a Jenkins Worflowjob workspace with a groovy script via Jenkins script console?

Why again this type of question?
This question seems to have been asked multiple times, but all the answers are irelevant for Jenkins Pipeline jobs (plugin: workflow-job).
Situation
I am migrating a bunch of old freestyle jobs from old Jenkins standalone server to a distributed Jenkins env and I've decided to convert them to Jenkins Pipeline jobs (can't use Blue ocean for it as the SCM is SVN.
Anyway, for some of the jobs is not desired to clean up their workspaces as they are sort of sanity/verification jobs and because the size of SVN checkout and built stuff is large (2GB in 20K files, just deleting it is so slow).
However I do occasionally (ad-hoc) need to delete a workspace of such jobs.
And I don't want to do it by:
modifying a Jenkinsfile and committing it to SVN
"Replaying" a pipeline runs with modification
And I don't have r/w access to a FS on that slave node (which would be the easiest thing to do).
Googling
Quick search on the internet avalanched me with dozens of results [1,2, 3, 4, ...] how to cleanWS() from within a Groovy script ran from Jenkins script console.
Unfortunately, none of them tries to delete workspace of true org.jenkinsci.plugins.workflow.job.WorkflowJob item instance of a job.
My Groovy attempt to cleanWS
Based on the answers gathered from the internet I started my Groovy clean up script which can be executed from the Jenkins script console <Jenkins:port/script>
import hudson.model.*
import com.cloudbees.hudson.*
import com.cloudbees.hudson.plugins.*
import com.cloudbees.hudson.plugins.folder.*
import org.jenkinsci.plugins.workflow.job.*
//jobsToRetrieve = ["aFolder/aJobInFolder","topLevelJob"]
jobsToRetrieve = ["Sandbox/PipelineTests/SamplePipeline"]
enumerateItems(Hudson.instance.items)
def enumerateItems(items) {
items.each { item ->
println("===============::: GENERAL INFO::: =======================")
println(" item: " + item)
println(" item FN: " + item.fullName)
println(" item.getClass " + item.getClass())
println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
if ( !(item instanceof Folder)) {
jobName = item.getFullDisplayName()
println(" :::jobname::: " + jobName)
if (jobsToRetrieve.contains(item.getFullName())) {
if (item instanceof WorkflowJob) {
println("XXXXXXXXXXXXX--- THIS IS THE JOB --- XXXXXXXXXXXXXXXXXXXXX")
println(" item.workspace: " + item.WORKSPACE)
println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
println(" following methods ain't implemented for WorkflowJob type of Item\nand it will blow out.")
//see https://javadoc.jenkins.io/hudson/model/FreeStyleProject.html
println(" customWS: " + item.getCustomWorkspace())
println(" WS:" + item.getWorkspace())
item.doDoWipeOutWorkspace()
}
}
} else {
println(" :::foldername::: " + item.displayName)
enumerateItems(((Folder) item).getItems())
}
println("==========================================================")
}
}
Results (kinda expected, but disaponting)
as you can see, my script is going to explode on calls of:
item.getCustomWorkspace()
item.getWorkspace()
item.doDoWipeOutWorkspace()
with MissingMethodException
groovy.lang.MissingMethodException: No signature of method: org.jenkinsci.plugins.workflow.job.WorkflowJob.doDoWipeOutWorkspace() is applicable for argument types: () values: []
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:49)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
at Script1$_enumerateItems_closure1.doCall(Script1.groovy:33)
Simply because those methods ain't available for this type of item, but only for hudson.model.FreeStyleProject
Question: How then I can delete the Pipeline job's workspace?
There is another Jenkins plugin: Workspace Cleanup which is probably used within Jenkinsfile by calling cleanWs() inside a stage() {}, but I didn't figure out how to utilise it from the outside of Jenkinsfile (like from my groovy script ran Jenkins script console).
Is it a bug/request for enhancement of Jenkins Pipeline jobs plugin?
Or is there any other way how to cast the item to something from where I would have access to a desired functionality?
Alright, investigating this more and googling even more and listening to your ideas (this one particularly inspired me by Daniel Spilker) I have achieved what I wanted, which is:
Independently to CLEAN-UP Pipeline Job's WORKSPACE via Jenkins script console
(only using Jenkins available means and no messing up with Job configuration, nor updating Jenkinsfile, nor replaying)
The code is not surprisingly difficult and for manual demonstration it looks like this:
Jenkins jenkins = Jenkins.instance
Job item = jenkins.getItemByFullName('Sandbox/PipelineTests/SamplePipeline')
println("RootDir: " + item.getRootDir())
for (Node node in jenkins.nodes) {
// Make sure slave is online
if (!node.toComputer().online) {
println "Node '$node.nodeName' is currently offline - skipping workspace cleanup"
continue
}
println "Node '$node.nodeName' is online - performing cleanup:"
// Do some cleanup
FilePath wrksp = node.getWorkspaceFor(item)
println("WRKSP " + wrksp)
println("ls " + wrksp.list())
println("Free space " + wrksp.getFreeDiskSpace())
println("===== PERFORMING CLEAN UP!!! =====")
wrksp.deleteContents()
println("ls now " + wrksp.list())
println("Free space now " + wrksp.getFreeDiskSpace())
}
Its output, if your job is found, looks like:
Result
RootDir: /var/lib/jenkins/jobs/Sandbox/jobs/PipelineTests/jobs/SamplePipeline
....
.... other node's output noise
....
Node 'mcs-ubuntu-chch' is online - performing cleanup:
WRKSP /var/lib/jenkins/workspace/Sandbox/PipelineTests/SamplePipeline
ls [/var/lib/jenkins/workspace/Sandbox/PipelineTests/SamplePipeline/README.md, /var/lib/jenkins/workspace/Sandbox/PipelineTests/SamplePipeline/.git]
Free space 3494574714880
===== PERFORMING CLEAN UP!!! =====
ls now []
Free space now 3494574919680
Mission completed:)
References
Mainly Jenkins javadoc
https://javadoc.jenkins.io/hudson/model/Node.html
https://javadoc.jenkins.io/hudson/model/TopLevelItem.html
https://javadoc.jenkins.io/hudson/model/Item.html
https://javadoc.jenkins.io/hudson/FilePath.html
This is not the most beautiful way, but you could just execute the OS command:
def isWin = Jenkins.instance.windows
def cmd = isWin ? "rmdir /s /q $workspace" : "rm -rf $workspace"
cmd.execute()
If you are using your code just once or are not dealing with multiple OS, you can instead shorten the code to the respective command:
"rm -rf $workspace".execute()

docker api ContainerExecInspect cannot get correct exit code

I am using docker engine-api(github.com/docker/engine-api) to execute some command
I use client.ContainerExecCreate and then client.ContainerExecInspect to run my command and then get the command exit code(I run multiple commands in the same container so the exit code get from ContainerInspect is useless for me.)
This is my function use to execute command in container
http://pastebin.com/rTNVuv9T
but ContainerExecInspect return wrong values sometime, because sometimes ContainerExecInspect is called before the command exit and it said exit code is zero, which is wrong
And I wrote a testcase to test it
http://pastebin.com/PED1Rf4k
And the result will not be 233, it will be 0
I have set ExecConfig.Detach = true and ExecStartCheck.Detach = true, but no helps
Is there any way to wait until the command exit then get the exit code?
Addition:
For some of my command running is shell script not a executable, so I think I need to prefix /bin/bash, and wait the container exit, is not what I want, I want to wait the command exit, and the container is still running
I think now I can solve my problem
The main point is when using containerExecAttach it will exposed the hijacked connection and, I can judge whether the command exit by read from the connection until EOF
There are a few point to set then
Should set ExecConfig AttachStdout to true
Then read from hijacked conn
Here is a sample code
atinfo, err := cli.ContainerExecAttach(ctx, execID, ec)
// error handling
defer atinfo.Close()
c = atinfo.Conn
one := make([]byte, 1)
_, err = c.Read(one)
if err == io.EOF {
println("Connection closed")
}
This will wait until the command execute complete
ExecConfig is set to
ec.Detach = false
ec.Tty = false
ec.AttachStdout = true

Lua script unable to detect/catch error while executing invalid linux command

I have the following function that works fine as long as I give it a valid command to execute. As soon as I give it a non-existent command, the script is interrupted with an error message.
#!/usr/bin/lua
function exec_com(com)
local ok,res=pcall(function() return io.popen(com) end)
if ok then
local tmp=res:read('*a')
res:close()
return ok,tmp
else
return ok,res
end
end
local st,val=exec_com('uptime')
print('Executed "uptime" with status:'..tostring(st)..' and value:'..val)
st,val=exec_com('zzzz')
print('Executed "zzzz" with status:'..tostring(st)..' and value:'..val)
When I run the script above I get the following output:
Executed "uptime" with status:true and value: 18:07:38 up 1 day, 23:00, 3 users, load average: 0.37, 0.20, 0.20
sh: zzzz: command not found
Executed "zzzz" with status:true and value:
You can clearly see above that pcall() function still reported success when executing "zzzz" which is odd.
Can someone help me devise a way to catch an exception when executing a non-existent or ill-formed Linux command using Lua script? Thanks.
Edit: Restated my request after getting the clarification that pcall() works as expected, and the problem is due to popen() failing to throw an error.
I use a method which is similar to your "temporary workaround" but which gives you more information:
local cmd = "uptime"
local f = io.popen(cmd .. " 2>&1 || echo ::ERROR::", "r")
local text = f:read "*a"
if text:find "::ERROR::" then
-- something went wrong
print("error: " .. text)
else
-- all is fine!!
print(text)
end
If you look at io.popen(), you'll see that it'll always return a file handle.
Starts program prog in a separated process and returns a file handle
that you can use to read data from this program (if mode is "r", the
default) or to write data to this program (if mode is "w").
Since, a file handle returned is still a valid value for lua, the pcall(), your local function inside the pcall is returning a true value (and an error is not being propagated); thereby, giving you a true status and no output.
I have come up with my own temporary workaround that pipes the error to /dev/null and determines the success/failure of executed command based on the text received from io.popen():read('*a') command.
Here is my new code:
#!/usr/bin/lua
function exec_com(com)
local res=io.popen(com..' 2>/dev/null')
local tmp=res:read('*a')
res:close()
if string.len(tmp)>0 then
return true,tmp
else
return false,'Error executing command: '..com
end
end
local st,val=exec_com('uptime')
print('Executed "uptime" with status:'..tostring(st)..' and value:'..val)
st,val=exec_com('cat /etc/shadow')
print('Executed "cat /etc/shadow" with status:'..tostring(st)..' and value:'..val)
And the corresponding output is now correct:
Executed "uptime" with status:true and value: 00:10:11 up 2 days, 5:02, 3 users, load average: 0.01, 0.05, 0.19
Executed "cat /etc/shadow" with status:false and value:Error executing command: cat /etc/shadow
In my example above I am creating a "generic" error description. This is an intermediate fix and I am still interested in seeing alternative solutions that can return a more meaningful error message describing why the command failed to execute.
Rather than taking the time reading the whole file into a variable, why not just check if the file is empty with f:read(0)?
Local f = io.popen("NotExist")
if not f:read(0) Then
for l in st:lines() do
print(l)
end
else
error("Command Does Not Exist")
end
From the lua Manual:
As a special case, io.read(0) works as a test for end of file: It returns an empty string if there is more to be read or nil otherwise.

Resources