Skip to content

Instantly share code, notes, and snippets.

@leehambley
Created April 29, 2011 18:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leehambley/d5a67911bf39c5b2ff2a to your computer and use it in GitHub Desktop.
Save leehambley/d5a67911bf39c5b2ff2a to your computer and use it in GitHub Desktop.
Proposed SSH DSL for Ruby
#
# Please read the examples inline thoroughly, these comprise some of the proposed
# DSLs for a better SSH Ruby SSH driver, upon which people can build decent tools
#
servers = %w( example.com beispeil.de ejemplo.es )
environment = {rails_env: production, rack_env: production}
LibSSH.configure do |config|
#
# Connection Timeout
#
config.connection_timeout = 10.seconds
#
# Connection "handler" - replacable
# if you don't need to raise an exception
# when a host times out
#
config.timeout_handler = lambda do
raise LibSSH::ConnectionTimeoutError
end
#
# Pool, and reuse connections to servers
# where possible
#
config.connection_pooling = true
#
# Send a default environment hash, where possible
#
config.default_environment = environment
#
# An interactive terminal (or PTY) is a connection
# that behaves like a user, sitting at a terminal
# programs running in a PTY expect to be able to
# prompt the user for feedback and input.
# Shells, for example typically use the presence
# of a PTY to justify loading a user's dotfiles
#
config.prefer_interative_terminal = true
#
# Default shell configutation
#
config.default_shell = 'sh'
end
#
# Runs "hostname" on one host, echos the result locally
#
on servers.first do
puts remote("hostname")
end
#
# Demonstrates connecting in parallel to two servers, and
# writing the result, IO#print is used because of thread
# safety
#
on servers[0..2] do
in "/etc/" do
remote("ls").each_line do |line|
print line + "\n"
end
end
end
#
# Change a user's password, includes some trimmings
#
responder = lambda do |prompt|
"MyAwesomePassword" if prompt =~ /Password:/
end
on servers.first do
as "root", :with => responder do
if run("passwd anotheruser", "User'sNewPassword")
puts "Changed 'anotheruser's Password Successfully"
else
puts "There was an error changing the other user's password #{exit_status}"
end
end
end
#
# Operates on all four servers, in sequence
# waiting 5 seconds between each operation
# does not have to concern itself with thread safety
#
on servers, :in => :sequence, :wait => 5 do
in "/var/www" do
with {queue: '*'} do
unless run("service resque restart")
# Break unless we can restart the server
break
end
end
end
end
@hedgehog
Copy link

Some quick opinions/thoughts - I have the blog post in mind, so I do not just refer to the DSL above:

  • Can't see the value-sdd of on. Rather servers {} should be sufficient. If name collisions are an issue use ssh_servers ?
  • Skip threading altogether - people interested in performance will be using EM or some such - spend time on EM instead. Threading sucks to debug and offers no-little performance advantage :)
  • ffi bindings might be the 'proper way to go? May give your cross Ruby-VM for less pain?
  • Skip RVM - spend time on rbenv+Bundler, easier and is much more sane.
  • If you're daemonizing your own ruby code, rather than starting an external exec, I wonder if Dante+Bluepill are worth looking into in place of DaemonController?

@leehambley
Copy link
Author

Can't see the value-sdd of on. Rather servers {} should be sufficient. If name collisions are an issue use ssh_servers ?

It's about the vocabulary on ____ in ____ run ____ it reads quite well, at least in my opinion, and there's also no reason that it couldn't be called servers

Skip threading altogether - people interested in performance will be using EM or some such - spend time on EM instead. Threading sucks to debug and offers no-little performance advantage :)

My thoughts exactly, write a good, single threaded, single-server version, and then invest in writing something to multiplex connections. (Even if I write it myself) - I expect to have to do that pre 1.0, as I expect the API of the single threaded version will need to be written with multithreading/em in mind.

ffi bindings might be the 'proper way to go? May give your cross Ruby-VM for less pain?

I'm not able to install FFI under Ruby 1.9.3, that's a problem - but I had thought that an FFI binding might be better, FFI has matured a lot since I wrote the blog post, maybe I'll go back to the drawing board!

Yep, rbenv wasn't even public when I wrote the post -- I agree on it being way more sane (although the fallout from the rbenv Vs. rvm "discussions" are interesting, clear that these people don't see eye to eye!, I was totally sold on rbenv, but sometimes I wonder if it's really the smartest!)

I'm actually daemonizing the openssh that's installed on the host machine… although libssh has support for running a server (and I believe it's quite mature) - I'm just spinning up an Openssh with a chroot jail to test in.

@hedgehog
Copy link

I agree that on, in and run do read well, it was more that I could not think of what function/behavior they provide/facilitate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment