Rakefile - stop every tasks in a multitask - ruby

I have an application running with Flask, and use Compass as css preprocessor. Which means I need to start the python server and compass for development. I made what I thought was a clever Rakefile to start everything from one command and have everything run in only one terminal window.
Everything works, but the problem is when I try to stop everything (with cmd + c), it only kills the compass task and the Flask server keeps running. How can I make sure every tasks are stopped? Or is there an alternative to simultaneously launch several tasks without this issue?
Here is my rakefile, pretty simple:
# start compass
task :compass do
system "compass watch"
end
# start the flask server
task :python do
system "./server.py"
end
# open the browser once everything is ready
task :open do
`open "http://127.0.0.1:5000"`
end
# the command I run: `$ rake server`
multitask :server => ['compass', 'python', 'open']
EDIT
For the record, I was using a Makefile and everything worked perfectly. But I changed part of my workflow and started using a Rakefile, so I Rakefile'd everything and got rid of the Makefile for simplicity.

That is because system creates new processes for your commands. To make sure they are killed alongside your ruby process, you will need to kill them yourself. For this you need to know their process ids, which system does not provide, but spawn does. Then you can wait for them to exit, or kill the sub-processes when you hit ^C.
An example:
pids = []
task :foo do
pids << spawn("sleep 3; echo foo")
end
task :bar do
pids << spawn("sleep 3; echo bar")
end
desc "run"
multitask :run => [:foo, :bar] do
begin
puts "run"
pids.each { |pid| Process.waitpid(pid) }
rescue
pids.each { |pid| Process.kill("TERM", pid) }
exit
end
end
If you do a rake run on that, the commands get executed, but when you abort, the tasks are sent the TERM signal. There's still an exception that makes it to the top level, but I guess for a Rakefile that is not meant to be published that does not matter too much. Waiting for the processes is necessary or the ruby process will finish before the others and the pids are lost (or have to be dug out of ps).

Related

kill ruby daemon process within a rake task

I am using needing to start and stop a server during some rake tasks. I am using the following code:
task :start_server do
job = fork do
system `http-server ./_site -p 4000`
end
Process.detach(job)
#pid = Process.pid
end
task :stop_server do
puts "stopping server"
system Process.kill('QUIT', #pid)
end
The start works fine but I cannot get it to stop.
I am calling these tasks within capistrano deploy e.g. after 'pdf:generate_pdf', 'pdf:stop_server'
I dont get an error but I can still see pages being served by the web server.
Is there a better way to end the process?
I have found that this command stops the http-server perfectly.
task :stop_server do
system("pkill -f http-server")
end

Rake task for running a server in an independent thread then killing the thread when the task is complete?

How do I launch a thread within a rake task then kill the tread when the task is complete.
Essentially I am writing a rake task to test a jekyll site. I would like be able to launch the server, do some other tasks and then destroy the thread when the task is complete. Here is what I have thus far:
task :test_site do
`ejekyll --server`
`git -Xdn`
if agree( "Clean all ignored files?")
git -Xdf
end
end
but unfortunately the only way I know of to stop the jekyll --server is to use ctrl c. I would be happy to hear of a way to stop a jekyll --server in a manor which does not exit the rake task but please just comment as the question is specifically asking about threading and rake tasks.
You want Process.spawn, not a thread. It's a new process, not a thread of execution within an existing process. You get the PID back, so just send Process.kill(:QUIT, pid) or whatever method you want to use to kill the spawned processed.
pid = Process.spawn(
"ejekyll", "--server",
out: "/dev/null",
err: "/dev/null"
)
# you may need to add a short sleep() here
# other stuff
Process.kill(:QUIT, pid) && Process.wait
If ejekyll has a command line option to run in the foreground, it would be better to use that, otherwise if it self-daemonizes you need to know where it stores its PID file, in order to identify and kill the daemon.

Kill process and sub-processes in Ruby on Windows

Currently I'm doing this in one command prompt
require 'win32/process'
p = Process.spawn("C:/ruby193/bin/bundle exec rails s")
puts p
Process.waitpid(p)
and then in another
require 'win32/process'
Process.kill(1,<p>)
The problem is that the process I spawn (the Rails server in this case) spawns a chain of sub-processes. The kill command doesn't kill them, it just leaves them orphaned with no parent.
Any ideas how can I kill the whole spawned process and all its children?
I eventually solved this in the following manner
First I installed the sys-proctable gem
gem install 'sys-proctable'
then used the originally posted code to spawn the process, and the following to kill it (error handling omitted for brevity)
require 'win32/process'
require 'sys/proctable'
include Win32
include Sys
to_kill = .. // PID of spawned process
ProcTable.ps do |proc|
to_kill << proc.pid if to_kill.include?(proc.ppid)
end
Process.kill(9, *to_kill)
to_kill.each do |pid|
Process.waitpid(pid) rescue nil
end
You could change the kill 9 to something a little less offensive of course, but this is the gist of the solution.
One-script solution without any gems. Run the script, CTRL-C to stop everything:
processes = []
processes << Process.spawn("<your process>")
loop do
trap("INT") do
processes.each do |p|
Process.kill("KILL", p) rescue nil
Process.wait(p) rescue nil
end
exit 0
end
sleep(1)
end

How can I create a daemon with Thor (ruby)?

I would like to use the popular Thor gem to create a daemonized task. My Thor class looks like this:
require 'rubygems'
require 'daemons'
require 'thor'
class CLI < Thor
desc "start", "Startup the App"
method_option :daemonize, :aliases => "-d", :default => false, :type => :boolean, :banner => "Run as daemon"
def start
run_app(options[:daemonize])
end
desc "stop", "Stop the daemon"
def stop
stop_app
end
no_tasks {
def run_app(run_as_daemon)
# Run the application code
Daemons.daemonize if run_as_daemon
# loop until stopped or interrupted
# ...
end
def stop_app
#stop the app
end
}
end
So here I've setup a basic thor class with two tasks, start and stop. I'm also, currently using the Daemons gem, but that isn't required. The part that I'm struggling with is that when this app runs as "run_thor_app.rb start" everything runs just fine. Obviously the stop task isn't needed in this instance. But when I run "run_thor_app.rb start -d" the app runs until Daemons.daemonize runs and then it quits. Checking the running processes shows that nothing is running in the background.
Even if something were running, I wouldn't know how to approach the stop task. For example, how do you detect that the app is running as a daemon and stop it. I've looked at Daemons::Monitor, but the documentation isn't clear on how that works and when I tried it, it didn't work.
It seems to me that this would be a good use case for something that is built into Thor, but searching through the code on github hasn't revealed anything to me. Maybe I just missed it somewhere. In any case, I think it would be good to document a best practice or a pattern for handling daemons with Thor for others to reference.
The way you usually manage daemon processes is by having them write their PID in a file. This makes it possible for another process to discover the daemon's PID, and kill it (or send some other signal).
Your code should work. I tried a bare bones script that used the deamons gem, and it took me a few tries until I found the deamonized process. I figured it would get the same name as the parent process, or something similar, but instead it's name was "self". Remember that the daemonized process will no longer write to STDOUT.
Anyway, try this:
# set up everything
# then daemonize
Daemons.daemonize
# and write a pid file
File.open('/tmp/mydaemon.pid', 'w') { |f| f.puts(Process.pid) }
loop do
# do something
# this loop is important, if the script ends the daemon dies
end
and check the /tmp/mydaemon.pid file for the PID. Then run ps ax | grep x where x is the PID. Run cat /tmp/mydaemon.pid | xargs kill` to kill the daemon.
I think the daemons' gem has some helpers for managing PidFiles, check out PidFile in http://rubydoc.info/gems/daemons/1.1.0/frames

how to controller (start/kill) a background process (server app) in ruby

i'm trying to set up a server for integration tests (specs actually) via ruby and can't figure out how to control the process.
so, what i'm trying to do is:
run a rake task for my gem that executes the integration specs
the task needs to first start a server (i use webrick) and then run the specs
after executing the specs it should kill the webrick so i'm not left with some unused background process
webrick is not a requirement, but it's included in the ruby standard library so being able to use it would be great.
hope anyone is able to help!
ps. i'm running on linux, so having this work for windows is not my main priority (right now).
The standard way is to use the system functions fork (to duplicate the current process), exec (to replace the current process by an executable file), and kill (to send a signal to a process to terminate it).
For example :
pid = fork do
# this code is run in the child process
# you can do anything here, like changing current directory or reopening STDOUT
exec "/path/to/executable"
end
# this code is run in the parent process
# do your stuffs
# kill it (other signals than TERM may be used, depending on the program you want
# to kill. The signal KILL will always work but the process won't be allowed
# to cleanup anything)
Process.kill "TERM", pid
# you have to wait for its termination, otherwise it will become a zombie process
# (or you can use Process.detach)
Process.wait pid
This should work on any Unix like system. Windows creates process in a different way.
I just had to do something similar and this is what I came up with. #Michael Witrant's answer got me started, but I changed some things like using Process.spawn instead of fork (newer and better).
# start spawns a process and returns the pid of the process
def start(exe)
puts "Starting #{exe}"
pid = spawn(exe)
# need to detach to avoid daemon processes: http://www.ruby-doc.org/core-2.1.3/Process.html#method-c-detach
Process.detach(pid)
return pid
end
# This will kill off all the programs we started
def killall(pids)
pids.each do |pid|
puts "Killing #{pid}"
# kill it (other signals than TERM may be used, depending on the program you want
# to kill. The signal KILL will always work but the process won't be allowed
# to cleanup anything)
begin
Process.kill "TERM", pid
# you have to wait for its termination, otherwise it will become a zombie process
# (or you can use Process.detach)
Process.wait pid
rescue => ex
puts "ERROR: Couldn't kill #{pid}. #{ex.class}=#{ex.message}"
end
end
end
# Now we can start processes and keep the pids for killing them later
pids = []
pids << start('./someprogram')
# Do whatever you want here, run your tests, etc.
# When you're done, be sure to kill of the processes you spawned
killall(pids)
That's about all she wrote, give it a try and let me know how it works.
I have tried fork, but it has kind of problems when ActiveRecord is involved in both the processes. I would suggest Spawn plugin (http://github.com/tra/spawn). It does fork only but takes care of ActiveRecord.

Resources