Skip to content

Commit

Permalink
Re-worked script helper with support for cleaner DLS syntax for syste…
Browse files Browse the repository at this point in the history
…m commands
  • Loading branch information
delano committed Apr 26, 2009
1 parent 09ee7f8 commit 1389265
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 64 deletions.
2 changes: 1 addition & 1 deletion bin/rudy
Expand Up @@ -10,7 +10,7 @@

$:.unshift File.join(File.dirname(__FILE__), '..', 'lib') # Put our local lib in first place
$:.unshift File.join(File.dirname(__FILE__), '..', 'vendor', 'highline-1.5.1', 'lib')
%w{amazon-ec2 drydock rye}.each { |dir| $:.unshift File.join(File.dirname(__FILE__), '..', '..', dir, 'lib') }
%w{amazon-ec2 caesars drydock rye}.each { |dir| $:.unshift File.join(File.dirname(__FILE__), '..', '..', dir, 'lib') }
#require 'rubygems'

#$SAFE = 1 # require is unsafe in Ruby 1.9??
Expand Down
4 changes: 2 additions & 2 deletions lib/rudy/cli/routines.rb
Expand Up @@ -15,8 +15,8 @@ def startup

if @@global.environment == @@config.defaults.environment &&
@@global.role == @@config.defaults.role
puts
puts "Login with: rudy -u root ssh"
#puts
#puts "Login with: rudy -u root ssh"
end

end
Expand Down
1 change: 1 addition & 0 deletions lib/rudy/config.rb
Expand Up @@ -10,6 +10,7 @@ class Config < Caesars::Config
dsl Rudy::Config::Routines::DSL
dsl Rudy::Config::Machines::DSL
dsl Rudy::Config::Networks::DSL
dsl Rudy::Config::Controls::DSL

# TODO: auto-generate in caesars
def accounts?; self.respond_to?(:accounts) && !self[:accounts].nil?; end
Expand Down
18 changes: 17 additions & 1 deletion lib/rudy/config/objects.rb
Expand Up @@ -18,12 +18,28 @@ class Defaults < Caesars
class Networks < Caesars
end

class Controls < Caesars
end

class Routines < Caesars

forced_hash :create
forced_hash :destroy
forced_hash :restore
forced_hash :mount

forced_hash :before

# Add remote shell commands to the DSL as forced Arrays.
# Example:
# ls :a, :l, "/tmp" # => :ls => [[:a, :l, "/tmp"]]
# ls :o # => :ls => [[:a, :l, "/tmp"], [:o]]
# NOTE: Beware of namespace conflicts in other areas of the DSL,
# specifically shell commands that have the same name as a keyword
# we want to use in the DSL. This includes commands that were added
# to Rye::Cmd before Rudy is 'require'd.
Rye::Cmd.instance_methods.each do |cmd|
forced_array cmd
end

end
end
12 changes: 8 additions & 4 deletions lib/rudy/routines.rb
Expand Up @@ -54,7 +54,8 @@ def generic_machine_runner(machine_action, routine, &routine_action)
end

rmach.send(machine_action) do |machine|
puts machine.liner_note
puts machine_separator(machine.name, machine.awsid)

print "Waiting for instance..."
isup = Rudy::Utils.waiter(3, 120, STDOUT, "it's up!", 2) {
inst = machine.get_instance
Expand All @@ -66,6 +67,7 @@ def generic_machine_runner(machine_action, routine, &routine_action)
Rudy::Utils.service_available?(machine.dns_public, 22)
}


opts = { :keys => root_keypairpath, :user => 'root', :debug => nil }
rbox = Rye::Box.new(machine.dns_public, opts)

Expand Down Expand Up @@ -112,10 +114,12 @@ def task_separator(title)
("%s=== %s %s" % [$/, title, '='*dashes])
end

def machine_separator(title)
dashes = 52 - title.size #
def machine_separator(name, awsid)
dashes = 60 - name.size #
dashes = 0 if dashes < 1
("%s=== %s %s" % [$/, title.bright, '='*dashes]).bright.color(:blue)
puts $/, '='*60
puts 'MACHINE: %-40s (%s)' % [name.bright, awsid]
puts '='*60, $/
end

end
Expand Down
78 changes: 33 additions & 45 deletions lib/rudy/routines/helpers/scripthelper.rb
Expand Up @@ -42,6 +42,7 @@ def execute_command?(timing, routine)
(routine.is_a?(Caesars::Hash) && routine.has_key?(timing))
end

# Returns a formatted string for printing command info
def command_separator(cmd, user)
cmd ||= ""
spaces = 52 - cmd.size
Expand Down Expand Up @@ -69,68 +70,55 @@ def execute_command(timing, routine, sconf, hostname, rbox)
def rbox.rm(*args); cmd('rm', args); end

if execute_command?(timing, routine) # i.e. before_local?
#puts "Connecting to #{hostname}" # TODO: verbose mode
begin
rbox.connect
rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
STDERR.puts "Error connecting: #{ex.message}".color(:red)
STDERR.puts "Skipping scripts".color(:red)
end

original_user = rbox.user
scripts = [routine[timing]].flatten
scripts.each do |script|

users = routine[timing] || {}
users.each_pair do |user, commands|
begin
user, command, *args = script.to_a.flatten.compact

# TODO: Move the following to Rye.
# If there's a command with no args, we could have been given something
# like "ls -l /tmp". Safe-mode Rye requires the command to be sent
# separately so this is a quick-fix to do that.
if command && command.index(' ') && args.empty?
command, *args = command.strip.scan(/\A(.+?)\s(.+)/).flatten
end

rbox.switch_user user # does nothing if it's the same user

if command.nil? || command.empty?
puts command_separator("No command specified", user)
next
end

puts command_separator(rbox.preview_command(command, args), user)

# NOTE: can we put this only in verbose mode?
#puts " Creating #{@@script_config_file}"

rbox.connect(false) # does nothing if already connected
rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
STDERR.puts "Error connecting: #{ex.message}".color(:red)
STDERR.puts "Skipping user #{user}".color(:red)
next
end

begin
# We need to create the config file for every script,
# b/c the user may change and it would not be accessible.
# We turn off safe mode so we can write the config file via SSH.
# This will need to use SCP eventually; it is unsafe and error prone.
rbox.safe = false
rbox.umask = "0077" # Ensure script is not readable
conf_str = sconf.to_hash.to_yaml.tr("'", "''")
puts rbox.echo("'#{conf_str}' > #{@@script_config_file}")
rbox.safe = true
rbox.chmod(600, @@script_config_file)


ret = rbox.send(command, args)

puts ' ' << ret.stdout.join("#{$/} ") if !ret.stdout.empty?
puts " STDERR: #{ret.stderr.join("#{$/} ")}".color(:red) if !ret.stderr.empty?

rescue Rye::CommandError => ex
puts " Exit code: #{ex.exit_code}".color(:red)
puts " STDERR: #{ex.stderr.join("#{$/} ")}".color(:red)
puts " STDOUT: #{ex.stdout.join("#{$/} ")}".color(:red)
rescue Rye::CommandNotFound => ex
puts " CommandNotFound: #{ex.message}".color(:red)
rescue => ex
end


commands.each_pair do |command, calls|
calls.each do |args|
begin
puts command_separator(rbox.preview_command(command, args), user)
ret = rbox.send(command, args)
puts ' ' << ret.stdout.join("#{$/} ") if !ret.stdout.empty?
STDERR.puts " STDERR: #{ret.stderr.join("#{$/} ")}".color(:red) if !ret.stderr.empty?
rescue Rye::CommandError => ex
STDERR.puts " Exit code: #{ex.exit_code}".color(:red)
STDERR.puts " STDERR: #{ex.stderr.join("#{$/} ")}".color(:red)
STDERR.puts " STDOUT: #{ex.stdout.join("#{$/} ")}".color(:red)
rescue Rye::CommandNotFound => ex
STDERR.puts " CommandNotFound: #{ex.message}".color(:red)
STDERR.puts ex.backtrace
end
end
end

rbox.rm(@@script_config_file)
end

# Return the borrowed rbox instance to the user it was provided with
rbox.switch_user original_user
else
puts "Nothing to do"
Expand Down
6 changes: 4 additions & 2 deletions lib/rudy/routines/release.rb
Expand Up @@ -11,8 +11,10 @@ def execute
#puts rel

generic_machine_runner(:list, routine) do |machine,rbox|
puts task_separator("CREATING REMOTE CHECKOUT")
#scm.create_remote_checkout(rbox)
if scm
puts task_separator("CREATING REMOTE CHECKOUT")
scm.create_remote_checkout(rbox)
end
end

puts "Done"
Expand Down
121 changes: 118 additions & 3 deletions lib/rudy/scm/git.rb
@@ -1,14 +1,129 @@

require 'date'

require 'grit'

module Rudy
module SCM
class NotAWorkingCopy < Rudy::Error
def message
"You must be in the main directory of your working copy"
end
end
class RemoteError < Rudy::Error; end
class NoRemoteURI < Rudy::Error; end
class TooManyTags < Rudy::Error
def message; "Too many tag creation attempts!"; end
end
class NoRemotePath < Rudy::Error
def message
"Add a path for #{@obj} in your routines config"
end
end

class GIT
include Grit

attr_accessor :base_uri
attr_reader :repo, :rbox
attr_accessor :remote
attr_accessor :branch
attr_reader :rtag

# * +args+ a hash of params from the git block in the routines config
#
def initialize(args={})
args = {
:remote => :origin,
:branch => :master,
:path => nil
}.merge(args)
@remote, @branch, @path = args[:remote], args[:branch], args[:path]
raise NoRemotePath, :git if @path.nil?
@repo = Repo.new(Dir.pwd) if GIT.working_copy?
end

def create_release(username=nil, msg=nil)
@rtag = generate_rtag(username)
msg ||= 'Another Release by Rudy!'
msg.tr!("'", "''")
ret = Rye.shell(:git, "tag", @rtag) # Use annotated? -a -m '#{msg}'
raise ret.stderr.join($/) if ret.exit_code > 0
ret = Rye.shell(:git, "push #{@remote} #{rtag}") if @remote
raise ret.stderr.join($/) if ret.exit_code > 0
@rtag
end

# rel-2009-03-05-user-rev
def generate_rtag(username=nil)
now = Time.now
mon = now.mon.to_s.rjust(2, '0')
day = now.day.to_s.rjust(2, '0')
rev = "01"
criteria = ['rel', now.year, mon, day, rev]
criteria.insert(-2, username) if username
rev.succ! while valid_rtag?(criteria.join(Rudy::DELIM)) && rev.to_i < 100
raise TooManyTags if rev.to_i >= 100
criteria.join(Rudy::DELIM)
end

def initialize(args={:base => ''})
@base_uri = args[:base]
def delete_rtag(rtag=nil)
rtag ||= @rtag
ret = Rye.shell(:git, 'tag', :d, rtag)
raise ret.stderr.join($/) if ret.exit_code > 0 # TODO: retest
# "git push origin :tag-name" deletes a remote tag
ret = Rye.shell(:git, "push #{@remote} :#{rtag}") if @remote
raise ret.stderr.join($/) if ret.exit_code > 0
true
end

def create_remote_checkout(rbox)
raise RemoteError, "#{@path} exists" if rbox.file_exists?(@path)
begin
puts " "
rbox.git('clone', get_remote_uri, @path)
rbox.git('checkout', @rtag)
rescue Rye::CommandError => ex
puts ex.message
end

end


def get_remote_uri
ret = Rye.shell(:git, "config", "remote.#{@remote}.url")
unless ret.exit_code == 0 && !ret.stdout.empty?
raise NoRemoteURI, "remote.#{@remote}.url not set"
end
ret.stdout.first
end

#def has_remote?(remote)
# success = false
# (@repo.remotes || []).each do |r|
# end
# success
#end

def valid_rtag?(tag)
# git tag -l tagname returns a 0 exit code and stdout is empty
# when a tag does not exit. When it does exist, the exit code
# is 0 and stdout contains the tagname.
ret = Rye.shell(:git, 'tag', :l, tag)
# change :l to :d for quick deleting above and return true
# OR: just change to :d to always recreate the same tag
(ret.exit_code == 0 && ret.stdout.to_s == tag)
end

# Are all local changes committed?
def self.clean_working_copy?(path=Dir.pwd)
raise NotAWorkingCopy, path unless working_copy?(path)
Rye.shell(:git, 'diff').stdout == []
end

def self.working_copy?(path=Dir.pwd)
(File.exists?(File.join(path, '.git')))
end

end
end
end
13 changes: 7 additions & 6 deletions lib/rudy/scm/svn.rb
Expand Up @@ -12,7 +12,7 @@ def initialize(args={:base => ''})

def create_release(username=nil, msg=nil)
local_uri, local_revision = local_info
rtag = generate_release_tag_name(username)
rtag = generate_rtag(username)
release_uri = "#{@base_uri}/#{rtag}"
msg ||= 'Another Release by Rudy!'
msg.tr!("'", "\\'")
Expand All @@ -24,12 +24,12 @@ def create_release(username=nil, msg=nil)
end

def switch_working_copy(tag)
raise "Invalid release tag (#{tag})." unless valid_uri?(tag)
raise "Invalid release tag (#{tag})." unless valid_rtag?(tag)
`svn switch #{tag}`
end

# rel-2009-03-05-user-rev
def generate_release_tag_name(username=nil)
def generate_rtag(username=nil)
now = Time.now
mon = now.mon.to_s.rjust(2, '0')
day = now.day.to_s.rjust(2, '0')
Expand All @@ -38,7 +38,7 @@ def generate_release_tag_name(username=nil)
criteria.insert(-2, username) if username
tag = criteria.join(Rudy::DELIM)
# Keep incrementing the revision number until we find the next one.
tag.succ! while (valid_uri?("#{@base_uri}/#{tag}"))
tag.succ! while (valid_rtag?("#{@base_uri}/#{tag}"))
tag
end

Expand All @@ -55,12 +55,13 @@ def working_copy?(path)
(File.exists?(File.join(path, '.svn')))
end

def valid_uri?(uri)
def valid_rtag?(uri)
ret = `svn info #{uri} 2>&1` || '' # Valid SVN URIs will return some info
(ret =~ /Repository UUID/) ? true : false
end

def everything_checked_in?
# Are all local changes committed?
def clean_working_copy?
`svn diff . 2>&1` == '' # svn diff should return nothing
end
end
Expand Down

0 comments on commit 1389265

Please sign in to comment.