mirror of
https://github.com/jimeh/skyline.git
synced 2026-02-19 02:56:39 +00:00
initial import
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
|
||||
168
lib/skyline/console.rb
Normal file
168
lib/skyline/console.rb
Normal file
@@ -0,0 +1,168 @@
|
||||
def load_config
|
||||
Skyline.load_config
|
||||
end
|
||||
|
||||
def init
|
||||
Skyline.init
|
||||
end
|
||||
|
||||
def reload
|
||||
Skyline.init(true)
|
||||
end
|
||||
|
||||
def list(what = nil)
|
||||
case what
|
||||
when :all
|
||||
Skyline.as_groups.each do |name, group|
|
||||
puts name.to_s
|
||||
group[:instances].each do |instance|
|
||||
puts " #{instance[:id].ljust(10)} " +
|
||||
"#{instance[:public_ip].ljust(15)} " +
|
||||
"#{instance[:private_ip].ljust(15)} " +
|
||||
"#{instance[:ami].ljust(10)} " +
|
||||
"#{instance[:type].ljust(10)} " +
|
||||
"#{instance[:state].ljust(15)} " +
|
||||
"#{instance[:launch_date].ljust(15)}"
|
||||
end
|
||||
end
|
||||
when :instances
|
||||
Skyline.instances.each do |i, instance|
|
||||
puts " #{instance[:id].ljust(10)} " +
|
||||
"#{instance[:public_ip].ljust(15)} " +
|
||||
"#{instance[:private_ip].ljust(15)} " +
|
||||
"#{instance[:ami].ljust(10)} " +
|
||||
"#{instance[:type].ljust(10)} " +
|
||||
"#{instance[:status].ljust(15)} " +
|
||||
"#{instance[:launch_date].ljust(15)}"
|
||||
end
|
||||
else
|
||||
Skyline.as_groups.each { |name, group| puts name.to_s }
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
def selected
|
||||
puts Skyline.selected
|
||||
end
|
||||
|
||||
def cmd(command)
|
||||
Skyline.cmd(command)
|
||||
end
|
||||
|
||||
def icmd
|
||||
if Skyline.selected.nil?
|
||||
puts "ERROR: No AS Group selected. Please use"
|
||||
puts "the \"use\" command to select one."
|
||||
return nil
|
||||
end
|
||||
exit = false
|
||||
started = false
|
||||
while !exit
|
||||
if !started
|
||||
puts "Entering interactive remote-terminal mode."
|
||||
puts ""
|
||||
puts "All entered commands are executed on all remote"
|
||||
puts "instances within selected auto-scaling group."
|
||||
puts ""
|
||||
puts "Type \"exit\"\" or \"halt\"\" to exit IRT mode."
|
||||
puts ""
|
||||
started = true
|
||||
end
|
||||
print "irt$ "
|
||||
input = STDIN.readline.strip
|
||||
if !input.nil? && input != ""
|
||||
if input =~ /^exit|halt$/
|
||||
exit = true
|
||||
else
|
||||
cmd(input)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def use
|
||||
groups = ["All"] + Skyline.as_groups.map { |name, group| name }
|
||||
valid_choice = false
|
||||
while !valid_choice
|
||||
puts "Please choose a group to use:"
|
||||
groups.each_with_index { |group, i| puts "#{i}. #{group}" }
|
||||
print "Enter your choice (0-9) or press Ctrl-C to abort: "
|
||||
s = STDIN.readline.strip
|
||||
s = (s =~ /^[\d]+$/) ? s.to_i : nil
|
||||
if !s.nil? && !groups[s].nil?
|
||||
valid_choice = true
|
||||
groups[0] = :all if s == 0
|
||||
puts groups[s].inspect
|
||||
return Skyline.use(groups[s])
|
||||
else
|
||||
puts "\nERROR: Not a valid choice\n\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def help_me
|
||||
cmds = [
|
||||
[:reload, "Force-reload instance and AutoScaling Group info."],
|
||||
[:list, <<-hd
|
||||
List loaded information. Options are:
|
||||
:all List AS Groups with instances.
|
||||
:instances List all instances in your EC2 account.
|
||||
hd
|
||||
],
|
||||
[:use, "Call without any arguments to choose which AS Group to work with."],
|
||||
[:cmd, "Run a command on all selected remote machines."],
|
||||
[:icmd, "Enter an interactive shell which runs entered commands on selected remote machines."]
|
||||
]
|
||||
puts "Available Skyline commands:"
|
||||
cmds.each do |cmd, desc|
|
||||
print " #{cmd.to_s.ljust(10)} "
|
||||
desc = desc.split("\n")
|
||||
if desc.size == 1
|
||||
puts desc[0]
|
||||
else
|
||||
puts desc.shift
|
||||
desc.each do |line|
|
||||
puts " #{line}"
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Skyhook related commands
|
||||
#
|
||||
|
||||
def skyhook(command)
|
||||
cmd("skyhook #{command}")
|
||||
end
|
||||
|
||||
def update(project = nil, rev = nil)
|
||||
if project.nil?
|
||||
puts "Please specify a project."
|
||||
return
|
||||
end
|
||||
rev = rev.gsub(/[^0-9]/, "") if rev.is_a?(String)
|
||||
commands = [
|
||||
[ "skyhook update",
|
||||
"Updating Skyhook on remote machines..." ],
|
||||
[ "skyhook update.#{project} #{rev}",
|
||||
"Fetching latest files for #{project} project on remote machines..." ],
|
||||
[ "skyhook mode +maintenance",
|
||||
"Putting remote machines in maintenance mode..." ],
|
||||
[ "skyhook activate.#{project} #{rev}",
|
||||
"Activating newly fetched version of #{project} on remote machines..." ],
|
||||
[ "skyhook mode -maintenance",
|
||||
"Disabling maintenance mode on remote machines" ]
|
||||
]
|
||||
commands.each do |command|
|
||||
puts command[1] if !command[1].nil?
|
||||
if !command[0].nil?
|
||||
puts " Executing: #{command[0]}"
|
||||
cmd command[0]
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
182
lib/skyline/skyline.rb
Normal file
182
lib/skyline/skyline.rb
Normal file
@@ -0,0 +1,182 @@
|
||||
require "rubygems"
|
||||
require "yaml"
|
||||
require "scanf"
|
||||
|
||||
class Skyline
|
||||
|
||||
@as_groups = {}
|
||||
@instances = {}
|
||||
@selected = nil
|
||||
@config = {}
|
||||
|
||||
class << self
|
||||
attr_accessor :as_groups
|
||||
attr_accessor :instances
|
||||
attr_accessor :selected
|
||||
attr_accessor :config
|
||||
end
|
||||
|
||||
def self.load_config
|
||||
if File.exist?("skyline_config.yml")
|
||||
@config = YAML.load_file("skyline_config.yml")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def self.init(force = false)
|
||||
get_instances if @instances.size == 0 || force
|
||||
get_as_groups if @as_groups.size == 0 || force
|
||||
return
|
||||
end
|
||||
|
||||
def self.use(group = nil)
|
||||
if !group.nil?
|
||||
if @as_groups.has_key?(group.to_s)
|
||||
@selected = group.to_s
|
||||
puts "Selected AutoScaling group: #{group}"
|
||||
return true
|
||||
elsif group == :all
|
||||
@selected = :all
|
||||
puts "NOTICE: You have selected all AutoScaling groups"
|
||||
return true
|
||||
else
|
||||
puts "ERROR: AutoScaling group #{group} doesn't seem to exist"
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.cmd(command)
|
||||
ssh_key = ""
|
||||
ssh_key = "-i " + @config["ssh_key"] if @config.has_key?("ssh_key") && !@config["ssh_key"].nil?
|
||||
template = "ssh #{ssh_key} root@{REMOTE_IP} " +
|
||||
"\"export PATH=\\\"/opt/ruby-enterprise/bin:$PATH\\\"; {CMD}\""
|
||||
cmds = []
|
||||
if @selected.nil?
|
||||
puts "ERROR: No AS Group selected."
|
||||
return
|
||||
end
|
||||
@as_groups.each do |name, group|
|
||||
if @selected.to_s == name.to_s || @selected == :all
|
||||
group[:instances].each do |instance|
|
||||
if instance[:state] == "InService" && !instance[:public_ip].nil?
|
||||
cmds << template.gsub("{REMOTE_IP}", instance[:public_ip]).gsub("{CMD}", command.gsub(/(")/, "\\\\\\1"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if cmds.size > 0
|
||||
puts "\nRunning command on all machines"
|
||||
self.call_parallel(cmds) do |results|
|
||||
results.each do |result|
|
||||
puts "------------------------------------------"
|
||||
puts " Command: #{result[0]}"
|
||||
puts " Exit code: #{result[2].to_i}"
|
||||
puts " Output: "
|
||||
puts "#{result[1]}"
|
||||
end
|
||||
puts "------------------------------------------"
|
||||
end
|
||||
else
|
||||
puts "No machines available under current AS Group."
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
def self.call_parallel(cmds = [], check_interval = nil)
|
||||
if cmds.size > 0
|
||||
threads, results = [], []
|
||||
cmds.each do |cmd|
|
||||
threads << Thread.new { results << [cmd, `#{cmd}`, $?] }
|
||||
end
|
||||
is_done = false
|
||||
check_interval = 0.2 if check_interval.nil?
|
||||
while is_done == false do
|
||||
sleep check_interval
|
||||
done = true
|
||||
threads.each { |thread| done = false if thread.status != false }
|
||||
is_done = true if done
|
||||
end
|
||||
yield(results)
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_instances
|
||||
print "Loading EC2 instance information. "
|
||||
raw = `ec2-describe-instances`
|
||||
if raw =~ /service error/i
|
||||
puts "ERROR: EC2 Service error."
|
||||
return false
|
||||
end
|
||||
lines = raw.split("\n")
|
||||
lines.each do |line|
|
||||
line = line.strip.split(/\s/)
|
||||
if line[0].downcase == "instance" && !line[5].nil? && line[5].downcase == "running"
|
||||
@instances[line[1]] = {
|
||||
:id => line[1], :ami => line[2], :public_dns => line[3], :private_dns => line[4],
|
||||
:status => line[5], :ssh_key => line[6], :idx => line[7], :type => line[9],
|
||||
:launch_date => line[10], :zone => line[11], :aki => line[12], :ari => line[13],
|
||||
:monitoring => (line[15] == "monitoring-enabled") ? true : false,
|
||||
:public_ip => line[16], :private_ip => line[17],
|
||||
}
|
||||
end
|
||||
end
|
||||
puts "DONE."
|
||||
return
|
||||
end
|
||||
|
||||
def self.get_as_groups
|
||||
print "Loading EC2 AutoScaling Groups information. "
|
||||
if @instances.size == 0
|
||||
puts "ERROR: Instances not loaded, or no running instances."
|
||||
return false
|
||||
end
|
||||
raw = `as-describe-auto-scaling-groups`
|
||||
if raw =~ /service error/i
|
||||
puts "ERROR: EC2 Service error"
|
||||
return false
|
||||
end
|
||||
raw = raw.strip.split("AUTO-SCALING-GROUP")
|
||||
raw.delete("")
|
||||
raw.each do |group|
|
||||
lines = group.split("\n")
|
||||
head = lines.shift.strip.split(/\s+/)
|
||||
instances = []
|
||||
lines.each do |line|
|
||||
line = line.split(/\s+/)
|
||||
instance = {
|
||||
:id => line[1],
|
||||
:group => line[2],
|
||||
:zone => line[3],
|
||||
:state => line[4],
|
||||
:public_ip => nil, :private_ip => nil, :ami => nil, :type => nil,
|
||||
:status => nil, :ssh_key => nil, :public_dns => nil,
|
||||
:private_dns => nil, :launch_date => nil
|
||||
}
|
||||
if !@instances[line[1]].nil?
|
||||
instance = instance.merge({
|
||||
:public_ip => @instances[line[1]][:public_ip],
|
||||
:private_ip => @instances[line[1]][:private_ip],
|
||||
:ami => @instances[line[1]][:ami],
|
||||
:type => @instances[line[1]][:type],
|
||||
:status => @instances[line[1]][:status],
|
||||
:ssh_key => @instances[line[1]][:ssh_key],
|
||||
:public_dns => @instances[line[1]][:public_dns],
|
||||
:private_dns => @instances[line[1]][:private_dns],
|
||||
:launch_date => @instances[line[1]][:launch_date]
|
||||
})
|
||||
end
|
||||
instances << instance
|
||||
end
|
||||
@as_groups[head[0]] = {
|
||||
:name => head[0], :launch_config => head[1], :zone => head[2], :load_balancer => head[3],
|
||||
:min_size => head[4], :max_size => head[5], :current_size => head[6],
|
||||
:instances => instances
|
||||
}
|
||||
end
|
||||
puts "DONE."
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
14
lib/skyline_console.rb
Normal file
14
lib/skyline_console.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
require "skyline/skyline"
|
||||
require "skyline/console"
|
||||
|
||||
load_config
|
||||
# init
|
||||
|
||||
puts <<-hd
|
||||
|
||||
Welcome to the Skyline console.
|
||||
|
||||
Available commands are:
|
||||
list, use, cmd, icmd, reload and help_me.
|
||||
|
||||
hd
|
||||
2
skyline
Executable file
2
skyline
Executable file
@@ -0,0 +1,2 @@
|
||||
#! /usr/bin/env ruby
|
||||
exec "irb -rubygems -I lib --simple-prompt -r skyline_console.rb"
|
||||
1
skyline_config.yml
Normal file
1
skyline_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
ssh_key: ~/.ec2/kp_jimeh_default
|
||||
Reference in New Issue
Block a user