initial import

This commit is contained in:
2009-12-10 20:45:56 +02:00
commit 14f518ce91
44 changed files with 1867 additions and 0 deletions

17
init/actions/activate.rb Normal file
View File

@@ -0,0 +1,17 @@
class ActivateAction < Action
def default
puts "please specify project"
end
def method_missing(project, *args)
Projects.send(project).activate(*args)
Action.perms :ensure
end
def all
Projects.activate
Action.perms :ensure
end
end

17
init/actions/active.rb Normal file
View File

@@ -0,0 +1,17 @@
class ActiveAction < Action
def default
puts "please specify project"
end
def method_missing(project, *args)
puts Projects.send(project).status["active"]
end
def all
Projects.list.each do |name, project|
puts "#{name.camelize}: #{project.status["active"]}"
end
end
end

15
init/actions/checkout.rb Normal file
View File

@@ -0,0 +1,15 @@
class CheckoutAction < Action
def default
puts "please specify project"
end
def method_missing(project, *args)
Projects.send(project).checkout(*args)
end
def all
Projects.checkout
end
end

View File

@@ -0,0 +1,11 @@
class HasLatestAction < Action
def default
Projects.has_latest?
end
def method_missing(*args)
Projects.has_latest?(*args)
end
end

7
init/actions/perms.rb Normal file
View File

@@ -0,0 +1,7 @@
class PermsAction < Action
def ensure
shell "chown -R www-data:www-data #{$skyhook_root}/www"
end
end

35
init/actions/update.rb Normal file
View File

@@ -0,0 +1,35 @@
class UpdateAction < Action
def default
skyhook
end
def all
skyhook
projects
end
def method_missing(project, *args)
Projects.send(project).checkout(*args)
Action.perms :ensure
end
def projects(*args)
Projects.checkout(*args)
Action.perms :ensure
end
def skyhook
SVN.up(nil, $skyhook_root)
shell "#{$skyhook_root}/init/rc.rb update.post_skyhook"
if $console
exec "irb -r #{$skyhook_root}/init/init.rb"
end
end
def post_skyhook
Action.perms :ensure
Projects.init
end
end

15
init/config.rb Normal file
View File

@@ -0,0 +1,15 @@
##
# Skyhook Services - Config
##
# config
$skyhook_root = File.dirname(File.dirname(__FILE__.gsub(/^.\/(.*)$/, "#{ENV["PWD"]}/\\1")))
$checkout_path = "/mnt/system"
$projects_path = "#{$skyhook_root}/www/projects"
$status_file = "#{$skyhook_root}/init/status.tmp"
$nginx_config_path = "#{$skyhook_root}/etc/nginx"
$nginx_projects_config_path = "#{$nginx_config_path}/projects"
$projects_config_path = "#{$skyhook_root}/init/projects"
$actions_config_path = "#{$skyhook_root}/init/actions"
$modes_config_path = "#{$skyhook_root}/init/modes"

9
init/init.rb Normal file
View File

@@ -0,0 +1,9 @@
if !$LOAD_PATH.include?(File.dirname(__FILE__))
$LOAD_PATH << File.dirname(__FILE__)
end
require "config"
require "lib/skyhook"
$console = true if $console.nil?
# $debug = 2

92
init/lib/skyhook.rb Normal file
View File

@@ -0,0 +1,92 @@
##
# Skyhook Services - Libraries
##
require "yaml"
require "fileutils"
require "lib/skyhook/utils"
require "lib/skyhook/utils/initd"
require "lib/skyhook/utils/monit"
require "lib/skyhook/utils/nginx"
require "lib/skyhook/utils/object_overloads"
require "lib/skyhook/utils/svn"
require "lib/skyhook/project"
require "lib/skyhook/projects"
require "lib/skyhook/action"
require "lib/skyhook/mode"
class Skyhook
@info = {}
class << self
attr_accessor :info
end
def self.start
save_status if !load_status
if !@info.nil? && (!@info.has_key?(:booted) || @info[:booted] != true)
save_status({:booted => true})
end
if !Nginx.restore_enabled
Nginx.ensite("projects")
Nginx.ensite("default")
Nginx.ensite("monit-status")
end
Projects.boot
Action.perms :ensure
puts "Starting Skyhook services."
Initd.monit :start
sleep 1
Monit.start :all
sleep 1
Projects.start
write_status(1)
end
def self.stop
puts "Stopping Skyhook services."
Projects.stop
Monit.stop :all
sleep 1
Initd.monit :stop
write_status(0)
end
def self.restart
puts "Restarting Skyhook services."
Projects.restart
Monit.restart :all
sleep 1
Initd.monit :restart
end
def self.status
if File.exist?($status_file)
puts "Skyhook services are running."
else
puts "Skyhook services are not running."
end
end
def self.method_missing(method, *args)
call = (args.size > 0) ? args.shift : nil
Action.send(method, call, *args)
end
def self.load_status
if File.exist?("#{$skyhook_root}/init/status.yml")
@info = YAML.load_file("#{$skyhook_root}/init/status.yml")
return true
end
return false
end
def self.save_status(input = {})
@info = @info.merge(input)
File.open("#{$skyhook_root}/init/status.yml", "w") do |f|
YAML.dump(@info, f)
end
end
end

View File

@@ -0,0 +1,32 @@
class Action
@list = {}
class << self
attr_accessor :list
end
def default
puts "no default action defined"
end
def self.method_missing(action, call = nil, *args)
if @list.has_key?(action)
call = :default if call.nil?
@list[action].send(call, *args) rescue return false
return true
end
return false
end
def self.load
Dir.glob("#{$actions_config_path}/*.rb").each do |item|
item = item.gsub(/^.*\/(.*?)\.rb/, "\\1")
require "actions/#{item}"
@list[item.to_sym] = "#{item}Action".camelize.constantize.new
end
end
end
Action.load

102
init/lib/skyhook/mode.rb Normal file
View File

@@ -0,0 +1,102 @@
class Mode
@list = {}
@status = {}
class << self
attr_accessor :list
attr_reader :status
end
def self.enable(*args)
self.switch(:enable, *args)
end
def self.disable(*args)
self.switch(:disable, *args)
end
def self.switch(state, *args)
if args.size > 0
ret = true
result = nil
args.each do |mode|
if @list.has_key?(mode.to_sym)
if (state == :enable && !@status[mode.to_s]) || (state == :disable && @status[mode.to_s])
self.display_switch(mode, state)
result = @list[mode.to_sym].send(state) if @list[mode.to_sym].respond_to?(state)
ret = false if result == false
@status[mode.to_s] = (state == :enable) ? true : false
else
self.display_no_switch(mode, state)
end
else
self.display_not_found(mode)
end
end
self.save_status
return ret
end
end
def self.do(options = nil)
if !options.nil?
if options.is_a?(String)
options = options.split(" ")
end
if options.is_a?(Array)
options.each do |option|
if option =~ /([\-\+])([a-z0-9]+)/
state = ($1 == "+") ? :enable : :disable
mode = $2.to_sym
self.switch(state, mode)
end
end
end
end
end
def self.load_status
if File.exist?("#{$modes_config_path}/status.yml")
@status = YAML.load_file("#{$modes_config_path}/status.yml")
return true
end
return false
end
def self.save_status(input = {})
@status = @status.merge(input)
File.open("#{$modes_config_path}/status.yml", "w") do |f|
YAML.dump(@status, f)
end
end
def self.display_switch(mode, state)
msg = (state == :enable) ? "Enabling" : "Disabling"
puts "#{msg} #{mode.to_s.camelize} mode"
end
def self.display_no_switch(mode, state)
msg = (state == :enable) ? "enabled" : "disabled"
puts "#{mode.to_s.camelize} mode is already #{msg}"
end
def self.display_not_found(mode)
puts "ERROR: #{mode.to_s.camelize} mode does not exist"
end
def self.load
self.save_status if !self.load_status
Dir.glob("#{$modes_config_path}/*.rb").each do |item|
item = item.gsub(/^.*\/(.*?)\.rb/, "\\1")
require "modes/#{item}"
@list[item.to_sym] = "#{item}Mode".camelize.constantize.new
@status[item.to_s] = false if !@status.has_key?(item.to_s)
end
self.save_status
end
end
Mode.load

136
init/lib/skyhook/project.rb Normal file
View File

@@ -0,0 +1,136 @@
class Project
attr_reader :name
attr_reader :path
attr_reader :repo
attr_reader :revision
attr_reader :status
def initialize
@status = {} if @status.nil?
if File.exist?(@path)
save_status if !load_status
end
end
def active
@status["active"] if @status.has_key?("active")
end
def boot
init
checkout
activate
end
def update(*args)
checkout(*args)
end
# def restart
# shell "touch #{@path}/r#{@status[:active]}/tmp/restart.txt"
# end
def init
if !File.exist?(@path)
shell "mkdir -p #{@path}"
end
if File.exist?("#{$projects_config_path}/#{@name}.conf")
shell "rm #{$nginx_projects_config_path}/#{@name}.conf" if File.exist?("#{$nginx_projects_config_path}/#{@name}.conf")
shell "cp #{$projects_config_path}/#{@name}.conf #{$nginx_projects_config_path}/#{@name}.conf"
end
end
def has_latest?
versions = get_available_versions
if !@revision.nil? && versions.include?(@revision)
return true
end
head = SVN.last_changed_rev(@repo)
if !@status.has_key?("head") || @status["head"].nil? || @status["head"] != head
save_status("head" => head)
end
if !versions.include?(head)
return false
end
return true
end
def checkout(rev = nil)
if File.exist?(@path)
has_latest?
rev = @revision if rev.nil? && !@revision.nil?
version = (rev.nil? || rev.to_i > @status["head"] || rev.to_s.downcase == "head") ? @status["head"] : rev.to_i
if !version.nil?
rev_path = "#{@path}/r#{version}"
if !File.exist?(rev_path)
SVN.export @repo, rev_path, version
return true
end
return nil
end
end
return false
end
def activate(rev = nil)
versions = get_available_versions
version = nil
if rev.nil? && !@revision.nil?
version = @revision
elsif rev.to_s.downcase == "head" || rev.nil?
version = versions.last
elsif versions.include?(rev.to_i)
version = rev.to_i
else
version = versions.last
end
active = (@status.has_key?("active")) ? @status["active"] : nil
return nil if version == active
if !version.nil?
shell "rm #{@path}/htdocs" if File.exist?("#{@path}/htdocs")
shell "ln -s r#{version} #{@path}/htdocs"
save_status("active" => version)
return true
end
return false
end
def clean
leave = 5 # number of version copies to keep
versions = get_available_versions
keep = []
keep << @revision if !@revision.nil?
keep << active if !active.nil? && !keep.include?(active)
(leave-keep.size).times { versions.pop }
versions.each do |version|
if !keep.include?(version)
shell "rm -rf #{@path}/r#{version}"
end
end
end
def load_status
if File.exist?("#{@path}/status.yml")
@status = YAML.load_file("#{@path}/status.yml")
return true
end
return false
end
def save_status(input = {})
@status = @status.merge(input)
File.open("#{@path}/status.yml", "w") do |f|
YAML.dump(@status, f)
end
end
def get_available_versions
dirs = []
Dir.glob("#{@path}/r*").each do |item|
dirs << $1.to_i if item =~ /^.*\/r(\d+)$/
end
return dirs.sort
end
end

View File

@@ -0,0 +1,86 @@
##
# Skyhook Services - Require projects
##
class Projects
@list = {}
class << self
attr_accessor :list
end
def self.method_missing(method, *args)
if @list.has_key?(method)
return @list[method]
else
return self.do(method, *args)
end
end
def self.checkout(*args)
if args.size > 0
return self.do_on_projects(*(args << :update))
else
return self.do(:update)
end
end
def self.activate(*args)
if args.size > 0
return self.do_on_projects(*(args << :activate))
else
return self.do(:activate)
end
end
def self.has_latest?(*args)
ret = self.do_on_projects(*(args << :has_latest?))
puts (ret) ? "yes" : "no"
return ret
end
def self.load
Dir.glob("#{$projects_config_path}/*.rb").each do |item|
item = item.gsub(/^.*\/(.*?)\.rb/, "\\1")
require "projects/#{item}"
@list[item.to_sym] = "#{item}Project".camelize.constantize.new
end
end
private
def self.do(what, *args)
ret = true
result = nil
@list.each do |name, project|
result = project.send(what, *args) if project.respond_to?(what)
ret = false if result == false
end
return ret
end
def self.do_on_projects(*args)
if args.size > 0
method = args.pop.to_sym
if args.size > 0
ret = true
result = nil
args.each do |item|
if @list.has_key?(item.to_sym)
result = @list[item.to_sym].send(method)
ret = false if result == false
end
end
return ret
else
return self.do(method)
end
end
end
end
Projects.load

18
init/lib/skyhook/utils.rb Normal file
View File

@@ -0,0 +1,18 @@
# utility methods
def shell(*args)
if $debug.nil? || $debug > 1
send(:system, *args)
else
puts "DEBUG: " + args.join(" ")
end
end
def write_status(status)
case status
when 1
shell "echo '1' > #{$status_file}"
when 0
shell "rm #{$status_file}"
end
end

View File

@@ -0,0 +1,11 @@
class Initd
def self.method_missing(service, *args)
if $debug.nil? || $debug <= 1
shell "/etc/init.d/#{service.to_s} " + args.map { |i| i.to_s }.join(" ") if File.exist?("/etc/init.d/#{service.to_s}")
else
puts "DEBUG: /etc/init.d/#{service.to_s} " + args.map { |i| i.to_s }.join(" ")
end
end
end

View File

@@ -0,0 +1,11 @@
class Monit
def self.method_missing(cmd, *args)
if $debug.nil? || $debug <= 1
shell "monit #{cmd.to_s} " + args.map { |i| i.to_s }.join(" ")
else
puts "DEBUG: monit #{cmd.to_s} " + args.map { |i| i.to_s }.join(" ")
end
end
end

View File

@@ -0,0 +1,67 @@
class Nginx
@sites_enabled = []
@nginx_conf = "#{$nginx_config_path}"
@available_path = "#{@nginx_conf}/sites-available"
@enabled_path = "#{@nginx_conf}/sites-enabled"
class << self
attr_accessor :sites_enabled
attr_accessor :nginx_conf
attr_accessor :available
attr_accessor :enabled
end
def self.restore_enabled
if @sites_enabled.size > 0
@sites_enabled.each do |site|
self.ensite(site)
end
return true
end
return false
end
def self.ensite(site)
if File.exist?("#{@available_path}/#{site.to_s}")
self.dissite(site)
if $debug.nil? || $debug <= 1
shell "ln -s \"#{@available_path}/#{site.to_s}\" \"#{@enabled_path}/#{site.to_s}\""
@sites_enabled << site.to_s
self.save_status
else
puts "DEBUG: ln -s \"#{@available_path}/#{site.to_s}\" \"#{@enabled_path}/#{site.to_s}\""
end
end
end
def self.dissite(site)
if File.exist?("#{@enabled_path}/#{site.to_s}")
if $debug.nil? || $debug <= 1
shell "rm \"#{@enabled_path}/#{site.to_s}\""
@sites_enabled.delete(site.to_s)
self.save_status
else
puts "DEBUG: rm \"#{@enabled_path}/#{site.to_s}\""
end
end
end
def self.load_status
if File.exist?("#{$skyhook_root}/init/sites-enabled.yml")
@sites_enabled = YAML.load_file("#{$skyhook_root}/init/sites-enabled.yml")
return true
end
return false
end
def self.save_status(input = [])
@sites_enabled = @sites_enabled + input
File.open("#{$skyhook_root}/init/sites-enabled.yml", "w") do |f|
YAML.dump(@sites_enabled, f)
end
end
end
Nginx.save_status if !Nginx.load_status

View File

@@ -0,0 +1,26 @@
class Object
def constantize
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
raise NameError, "#{self.inspect} is not a valid constant name!"
end
Object.module_eval("::#{$1}", __FILE__, __LINE__)
end
def camelize(first_letter_in_uppercase = true)
if first_letter_in_uppercase
self.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
else
self.first + camelize(lower_case_and_underscored_word)[1..-1]
end
end
def underscore
self.to_s.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
end

View File

@@ -0,0 +1,52 @@
class SVN
def self.checkout(repo, target = "", revision = nil)
self.checkout_or_export(:checkout, repo, target, revision)
end
def self.co(repo, target = "", revision = nil)
self.checkout_or_export(:co, repo, target, revision)
end
def self.export(repo, target = "", revision = nil)
self.checkout_or_export(:export, repo, target, revision)
end
def self.up(revision = nil, target = "")
rev = parse_revision(revision)
shell "svn up #{rev} #{target}"
end
def self.revision(repo)
self.info(repo)["Revision"].to_i
end
def self.last_changed_rev(repo)
self.info(repo)["Last Changed Rev"].to_i
end
def self.info(path)
data = `svn info #{path}`.split(/\n\r|\n|\r/)
info = {}
data.each do |item|
info[$1.strip] = $2.strip if item =~ /(.+?)\:(.+)/
end
return info
end
private
def self.checkout_or_export(method, repo, target = "", revision = nil)
if ["export", "co", "checkout"].include?(method.to_s)
rev = parse_revision(revision)
shell "svn #{method.to_s} #{rev} #{repo} #{target}"
return true
end
return false
end
def self.parse_revision(revision)
(!revision.to_s.match(/^\d+$/).nil?) ? "-r#{revision.to_s.match(/^\d+$/).to_s}" : ""
end
end

19
init/modes/maintenance.rb Normal file
View File

@@ -0,0 +1,19 @@
class MaintenanceMode
def enable
Nginx.ensite :maintenance
Nginx.dissite :projects
Nginx.dissite :default
Initd.nginx :restart
Projects.stop
end
def disable
Nginx.ensite :projects
Nginx.ensite :default
Nginx.dissite :maintenance
Projects.start
Initd.nginx :restart
end
end

View File

@@ -0,0 +1,6 @@
server {
listen 80;
server_name skyhookdemo.jimeh.me;
passenger_enabled on;
root /var/www/projects/sinatra_demo/htdocs/public;
}

View File

@@ -0,0 +1,19 @@
class SinatraDemoProject < Project
def initialize
@name = "sinatra_demo" # Project name
@path = "#{$projects_path}/#{@name}" # Local checkout path
@revision = nil # Default revision to checkout
@repo = "https://heartbit.springloops.com/source/skyhookdemo/projects/sinatra_demo"
super
end
def start
file = "#{@path}/htdocs/config.ru"
configru = File.read(file)
File.open(file, "w+") do |f|
f.write(configru.gsub("development", "production"))
end
end
end

View File

@@ -0,0 +1,9 @@
server {
listen 80 default;
server_name _;
passenger_enabled on;
passenger_use_global_queue on;
rack_env production;
index index.html index.htm;
root /var/www/projects/skyhook_demo/htdocs/public;
}

View File

@@ -0,0 +1,24 @@
class SkyhookDemoProject < Project
def initialize
@name = "skyhook_demo" # Project name
@path = "#{$projects_path}/#{@name}" # Local checkout path
@revision = nil # Default revision to checkout
@repo = "https://heartbit.springloops.com/source/skyhookdemo/projects/skyhook_demo"
super
end
def start
# shell "export RAILS_ENV=production; #{@path}/htdocs/script/delayed_job start"
end
def stop
# shell "export RAILS_ENV=production; #{@path}/htdocs/script/delayed_job stop"
end
def restart
# stop
# start
end
end

33
init/rc.rb Executable file
View File

@@ -0,0 +1,33 @@
#! /usr/bin/env ruby
##
# Skyhook Services - Remote Init Script
##
$console = false
$LOAD_PATH << File.dirname(__FILE__)
require "init"
# parse input and run
case ARGV[0]
when "start"
Skyhook.start
when "stop"
Skyhook.stop
when "restart"
Skyhook.restart
when "status"
Skyhook.status
when "mode"
ARGV.shift
Mode.do(ARGV)
else
which = ARGV.shift.to_s.split(".")
action = Action.send(which[0].to_s.downcase.to_sym, which[1], *ARGV) rescue Process.exit(1)
if !action
Process.exit(1)
end
end
Process.exit(0)