Manage a Thin server through a Ruby script - ruby

I have a project where I have a sinatra app and I want to launch it with thin through a admin ruby script file. I want to be able to start, stop and restart it, also being able to daemonize it if asked to. This is, I want to have something like this in my script:
bin/myscript
require 'MyCLI'
MyCLI.new(ARGV).run
lib/mycli.rb
class MyCLI
# instantiate and other methods (inspired by thin runner)
...
def run
# parse commands and options
...
# then process command
case #command
when 'start'
#server = Thin::Server.new(host, port, MyModule::MyAppClass)
#server.start
when 'stop'
# ?
when 'restart'
# ?
else
raise "Unknown command"
end
end
end
But I'm struggling with some problems,
I need to daemonize it or not, depending on some command option and cant find if this is possible to do passing some parameter to #new after reading docs and digging in some of the code.
Stopping would be as easy as #server.stop, but as my script instantiates a mycli object at each command line request, I do not have a single object so #server vanishes after the start request, so I think that the only solution would be to control the PID (right??), but cant find how thin manages that. Also, running it in foreground would not work with this pid approach I presume.
What would be the proper way to restart it?
Has anyone a best solution for this?

I end up using Rack::Server.start(app, host, port, env, daemonize(Y/N), pid_file).
It works great, and it will pick up the thin handler if available.

Related

Setting Connection to "close" in Sinatra

I have this simple sinatra web app:
require 'sinatra'
get '/' do
"Success."
end
get '/app' do
"done"
response["Connection"] = "Close"
`sudo pkill blink`
`gpio write 0 0`
`sudo ./blink #{params["func"]}`
end
./blink is a program that runs forever and does not terminate, so when I access http://127.0.0.1/app?func=2 in a browser, I just get a loading loop and "done" is not shown as the result, however the program I am trying to run in the /app block is running.
I thought maybe setting the Connection header to Close would solve the problem, but using the code above, which I thought would set the header, still has the Connection header to Keep-Alive
Any help? Thanks!
Referencing this SO question Spawn a background process in Ruby and with some help from #andrykonchin, I was able to resolve my issue using this:
pid = fork do
`sudo ./blink #{params["func"]}`
end
Process.detach(pid)

Run Tortoise SVN command line commands with a hidden window

I have Tortoise SVN with a command-line interface installed. The installation path is C:\Program Files\TortoiseSVN\bin where svn.exe is used whenever I use any SVN command.
I developed a Ruby Windows application which is run as a background process. This application runs the command like
svn info "#{path_to_repository}"
This command invokes svn.exe as I mentioned.
The problem is, svn.exe flashes a command prompt for a second and terminates, thus if I run svn info ten times for ten different repositories then the screen flickers ten times as this command is developed to run in a timely fashion, the screen flickers ten times regularly.
What I need is a way to run SVN commands through Tortoise SVN without the svn.exe popping up the screen and closing.
Ruby has numerous way of executing command in shell, however, with all the options a command line popup seem to appear when using in GUI App.
Depending on what details you are looking for in svn info, one option you can use something like WebSVN and see if you can want to scrape the GUI or get data from its RSS feed. Take a look at demo site of this product.
If you have very specific and minimal needs, then, you can also choose to build a small REST API which can query the subversion server using command line. You can in that case call that REST API to fetch the data and avoid popping up of command windows.
If you are really short on time or do not have server infrastructure to host REST API, then, you can think of creating a Ruby App that runs a socket server and can run the shell command on receiving commands from client. Then, you can make your GUI App connect to the socket server using socket client and ask the server app to execute svn info and return result. Go through the tutorial on building such interacting apps. You can then choose to run them side-by-side on same PC.
Another alternative is to use Ruby SVN bindings. It may require some digging around to get this to work.
Here is quick starter code:
server.rb - a ruby TCP server that accepts commands and executes them in shell
require 'socket'
server = TCPServer.open(2000) # Socket to listen on port 2000
puts "Listening now #{server.addr}"
loop {
Thread.start(server.accept) do |client|
cmd = client.gets
puts "Processing #{cmd} from #{client.peeraddr}"
IO.popen(cmd) { |s| result = [];
while (line = s.gets) do
client.puts line.chop
end;
}
client.close
end
}
app.rb A Shoes GUI app that issues svn info command to TCP server being run by server.rb
require 'socket'
Shoes.app {
stack do
#push = button "Get SVN Info"
#note = para ""
end
#push.click {
hostname = 'localhost'
port = 2000
result = []
s = TCPSocket.open(hostname, port)
s.puts "svn info trunk/backend"
while line = s.gets
result << line.chop
end
s.close
#note.replace result.join("\n")
}
}
app.rb should be launched using shoes app.rb command.
This behavior is not specific to Ruby but to the Windows command-line interpreter. There are several ways to work around it.
Try running the svn command prefixed by cmd.exe /C which should not flash the command prompt window. A variation of that is to use start /min as a prefix instead. This doesn't work under all circumstances and I don't have a Ruby on Windows handy to check.
Create a .vbs wrapper for your command. Since .vbs is not handled by the command-line interpreter, its window will not be created. See "How to run a batch file without launching a 'command window'?" for details.
The best option is to use a WinAPI wrapper gem to get access to ShellExecute function which is pretty flexible:
require 'win32ole'
# Create an instance of the Windows Shell object...
shell = WIN32OLE.new('Shell.Application')
# The shell object's ShellExecute method performs a specified operation on a specified file. The syntax is...
shell.ShellExecute(FILE, ARGUMENTS, DIRECTORY, OPERATION, SHOW)
This example is taken from "Launching Apps and Printing Docs with the Windows Shell" where you can find more details.
For your purpose it would be something like
shell.ShellExecute('svn.exe', 'info', path_to_repository, 'open', 0)
Learn more about ShellExecute usage.

Ruby Process.daemon: turning on/off

I am trying to daemonize a Ruby script, running on 2.1.1.
My daemon part of the code is like this:
case ARGV[0]
when "-start"
puts "TweetSearcher started."
Process.daemon
when "-stop"
Process.kill(9,Process.pid)
else
puts "Lacks arguments. Use -start/-stop"
abort
end
However, it looks like that the Process.kill(9,Process.pid) is not killing what I wanted to. I want to kill a previous "ruby tweetsearcher.rb -start", already running in background.
How do I proceed?
Typically, the PID is stored in a file that is then read to stop it.
Calling Process.kill(9,Process.pid) kills the "stopper" process itself, rather than the one it's trying to stop.
Here's a guide to writing daemons in Ruby: http://codeincomplete.com/posts/2014/9/15/ruby_daemons/
As you can see, it's not a trivial process.
Here is another blog that suggests that you should not try to daemonize at all, but instead rely on a process monitoring system to take care of those concerns: https://www.mikeperham.com/2014/09/22/dont-daemonize-your-daemons/

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

Can a standalone ruby script (windows and mac) reload and restart itself?

I have a master-workers architecture where the number of workers is growing on a weekly basis. I can no longer be expected to ssh or remote console into each machine to kill the worker, do a source control sync, and restart. I would like to be able to have the master place a message out on the network that tells each machine to sync and restart.
That's where I hit a roadblock. If I were using any sane platform, I could just do:
exec('ruby', __FILE__)
...and be done. However, I did the following test:
p Process.pid
sleep 1
exec('ruby', __FILE__)
...and on Windows, I get one ruby instance for each call to exec. None of them die until I hit ^C on the window in question. On every platform I tried this on, it is executing the new version of the file each time, which I have verified this by making simple edits to the test script while the test marched along.
The reason I'm printing the pid is to double-check the behavior I'm seeing. On windows, I am getting a different pid with each execution - which I would expect, considering that I am seeing a new process in the task manager for each run. The mac is behaving correctly: the pid is the same for every system call and I have verified with dtrace that each run is trigging a call to the execve syscall.
So, in short, is there a way to get a windows ruby script to restart its execution so it will be running any code - including itself - that has changed during its execution? Please note that this is not a rails application, though it does use activerecord.
After trying a number of solutions (including the one submitted by Byron Whitlock, which ultimately put me onto the path to a satisfactory end) I settled upon:
IO.popen("start cmd /C ruby.exe #{$0} #{ARGV.join(' ')}")
sleep 5
I found that if I didn't sleep at all after the popen, and just exited, the spawn would frequently (>50% of the time) fail. This is not cross-platform obviously, so in order to have the same behavior on the mac:
IO.popen("xterm -e \"ruby blah blah blah\"&")
The classic way to restart a program is to write another one that does it for you. so you spawn a process to restart.exe <args>, then die or exit; restart.exe waits until the calling script is no longer running, then starts the script again.

Resources