From 58557b9d66c407a911b2fd3d94fddacbc55cd0e7 Mon Sep 17 00:00:00 2001 From: Emile Cantin Date: Wed, 18 Apr 2012 00:03:28 -0400 Subject: [PATCH] Initial import. --- .gitignore | 4 + Gemfile | 6 + Rakefile | 1 + data/export/initscript/master.erb | 160 ++++++++++++++++++ foreman-export-initscript.gemspec | 26 +++ lib/foreman-export-initscript.rb | 2 + lib/foreman-export-initscript/version.rb | 0 lib/foreman/export/.initscript.rb.swp | Bin 0 -> 12288 bytes lib/foreman/export/initscript.rb | 28 +++ spec/foreman/export/initscript_spec.rb | 37 ++++ spec/resources/export/initscript/app | 160 ++++++++++++++++++ .../export/initscript/app-concurrency | 160 ++++++++++++++++++ spec/spec_helper.rb | 52 ++++++ 13 files changed, 636 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Rakefile create mode 100644 data/export/initscript/master.erb create mode 100644 foreman-export-initscript.gemspec create mode 100644 lib/foreman-export-initscript.rb create mode 100644 lib/foreman-export-initscript/version.rb create mode 100644 lib/foreman/export/.initscript.rb.swp create mode 100644 lib/foreman/export/initscript.rb create mode 100644 spec/foreman/export/initscript_spec.rb create mode 100644 spec/resources/export/initscript/app create mode 100644 spec/resources/export/initscript/app-concurrency create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4040c6c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.gem +.bundle +Gemfile.lock +pkg/* diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..2defcf7 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "http://rubygems.org" + +# Specify your gem's dependencies in foreman-export-initscript.gemspec +gemspec + +gem "fakefs", git: "git://github.com/defunkt/fakefs.git" diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..2995527 --- /dev/null +++ b/Rakefile @@ -0,0 +1 @@ +require "bundler/gem_tasks" diff --git a/data/export/initscript/master.erb b/data/export/initscript/master.erb new file mode 100644 index 0000000..edb9f0c --- /dev/null +++ b/data/export/initscript/master.erb @@ -0,0 +1,160 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: <%= app %> +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Generated initscript for <%= app %> +# Description: This file starts <%= app %>. It should be placed in /etc/init.d +### END INIT INFO + +# Author: Foreman generator +# + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Runs <%= app %>" +NAME=<%= app %> +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +### START EACH PROCESS +<% engine.procfile.entries.each do |process| %> + <% 1.upto(concurrency[process.name]) do |num| %> + do_start_<%= process.name %>_<%= num %>() + { + # This starts the process + } + <% end %> +<% end %> +### + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +### STOP EACH PROCESS +<% engine.procfile.entries.each do |process| %> + <% 1.upto(concurrency[process.name]) do |num| %> + do_stop_<%= process.name %>_<%= num %>() + { + # This starts the process + } + <% end %> +<% end %> +### + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/foreman-export-initscript.gemspec b/foreman-export-initscript.gemspec new file mode 100644 index 0000000..2f84532 --- /dev/null +++ b/foreman-export-initscript.gemspec @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +$:.push File.expand_path("../lib", __FILE__) + +Gem::Specification.new do |s| + s.name = "foreman-export-initscript" + s.version = "0.0.1" + s.authors = ["Emile Cantin"] + s.email = ["emile.cantin@gmail.com"] + s.homepage = "" + s.summary = %q{Export foreman Procfile to a init script} + s.description = %q{Export foreman Procfile to a init script} + + s.rubyforge_project = "foreman-export-initscript" + + s.files = `git ls-files`.split("\n") + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + s.require_paths = ["lib"] + + # specify any dependencies here; for example: + s.add_development_dependency "rspec" + # s.add_development_dependency "fakefs", git: "git://github.com/defunkt/fakefs.git" + s.add_development_dependency "rr" + s.add_dependency "foreman" + # s.add_runtime_dependency "rest-client" +end diff --git a/lib/foreman-export-initscript.rb b/lib/foreman-export-initscript.rb new file mode 100644 index 0000000..2bdad3f --- /dev/null +++ b/lib/foreman-export-initscript.rb @@ -0,0 +1,2 @@ +require "foreman/export/initscript" + diff --git a/lib/foreman-export-initscript/version.rb b/lib/foreman-export-initscript/version.rb new file mode 100644 index 0000000..e69de29 diff --git a/lib/foreman/export/.initscript.rb.swp b/lib/foreman/export/.initscript.rb.swp new file mode 100644 index 0000000000000000000000000000000000000000..b2f0d1518490e04760dcd931d907c8a0c47c08ef GIT binary patch literal 12288 zcmeI2&2H2%5XZfg6Hq>{98C7mL~4^l4^#^jscI`#qM{%bB&rg5v)-*sVyBKnDcyxP zK)e9F2=4$#;L3?3;tYs!K6YE6;=}=UBK_B~XPlY&#g}NOJ$-L^gIy2_OL^fCP{L55jkN^@u0!RP} zAOR$R1dzZ#Bfuqm{!mKmk?v?=c-V!$XX-TfY*VulN)CeN9!t}KGf5Mc8Lq=f$*$1l!Bj=CEFIol%oBB;b@! z^|@}ttab{uQpPK4DT=X)QMv?mtQiq!zD`_QR2h=ejO|hz2hGzKZSGn7z#(?u zg3#-KDu2W0EZU?UMf=vfU&cF-1LVKT%%)@sFBsSq{EpCe!41Qu;J3pJvuh;;dpD|n!t-I7$xfI;h$7Qi^@`FVY{ug*w4wKCzfYauG1CD|>K9iy z=NZ?vV{s3j@LCHSjJ3($f~Cm3%`#r3&Sg)?q7oh%rFSdV)~O_%Jc262l-6gadKX$N z?K&%YW@wi3NOX588~QjN&1KB9Y`6lsba=Nq0)#{{1LIQJ5#MSp7A^6u*7C3?o?idm aIuAJ$qWN|%G#ulqpT6ptcSZ(#g2+#ur$_1l literal 0 HcmV?d00001 diff --git a/lib/foreman/export/initscript.rb b/lib/foreman/export/initscript.rb new file mode 100644 index 0000000..231489b --- /dev/null +++ b/lib/foreman/export/initscript.rb @@ -0,0 +1,28 @@ +require "erb" +require "foreman/export" + +class Foreman::Export::Initscript < Foreman::Export::Base + + def export + error("Must specify a location") unless location + + FileUtils.mkdir_p location + + app = self.app || File.basename(engine.directory) + user = self.user || app + log_root = self.log || "/var/log/#{app}" + template_root = Pathname.new(File.dirname(__FILE__)).join('..', '..', '..', 'data', 'export', 'initscript').expand_path + + Dir["#{location}/#{app}"].each do |file| + say "cleaning up: #{file}" + FileUtils.rm(file) + end + + master_template = export_template("initscript", "master.erb", template_root) + master_config = ERB.new(master_template).result(binding) + write_file "#{location}/#{app}", master_config + + end + +end + diff --git a/spec/foreman/export/initscript_spec.rb b/spec/foreman/export/initscript_spec.rb new file mode 100644 index 0000000..f778fd6 --- /dev/null +++ b/spec/foreman/export/initscript_spec.rb @@ -0,0 +1,37 @@ +require "spec_helper" +require "foreman/engine" +require "foreman/export/initscript" +require "tmpdir" + +describe Foreman::Export::Initscript, :fakefs do + let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") } + let(:engine) { Foreman::Engine.new(procfile) } + let(:options) { Hash.new } + let(:initscript) { Foreman::Export::Initscript.new("/tmp/init", engine, options) } + + before(:each) { load_export_templates_into_fakefs("initscript") } + before(:each) { stub(initscript).say } + + it "exports to the filesystem" do + initscript.export + normalize_space(File.read("/tmp/init/app")).should == normalize_space(example_export_file("initscript/app")) + end + + it "cleans up if exporting into an existing dir" do + mock(FileUtils).rm("/tmp/init/app") + + initscript.export + require 'debug' + initscript.export + end + + context "with concurrency" do + let(:options) { Hash[:concurrency => "alpha=2"] } + + it "exports to the filesystem with concurrency" do + initscript.export + normalize_space(File.read("/tmp/init/app")).should == normalize_space(example_export_file("initscript/app-concurrency")) + end + end + +end diff --git a/spec/resources/export/initscript/app b/spec/resources/export/initscript/app new file mode 100644 index 0000000..15b3466 --- /dev/null +++ b/spec/resources/export/initscript/app @@ -0,0 +1,160 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: app +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Generated initscript for app +# Description: This file starts app. It should be placed in /etc/init.d +### END INIT INFO + +# Author: Foreman generator +# + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Runs app" +NAME=app +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +### START EACH PROCESS + do_start_alpha_1() + { + # This starts the process + } + do_start_bravo_1() + { + # This starts the process + } +### + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +### STOP EACH PROCESS + do_stop_alpha_1() + { + # This starts the process + } + do_stop_bravo_1() + { + # This starts the process + } +### + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/spec/resources/export/initscript/app-concurrency b/spec/resources/export/initscript/app-concurrency new file mode 100644 index 0000000..20a0a4f --- /dev/null +++ b/spec/resources/export/initscript/app-concurrency @@ -0,0 +1,160 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: app +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Generated initscript for app +# Description: This file starts app. It should be placed in /etc/init.d +### END INIT INFO + +# Author: Foreman generator +# + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Runs app" +NAME=app +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +### START EACH PROCESS + do_start_alpha_1() + { + # This starts the process + } + do_start_alpha_2() + { + # This starts the process + } +### + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +### STOP EACH PROCESS + do_stop_alpha_1() + { + # This starts the process + } + do_stop_alpha_2() + { + # This starts the process + } +### + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..b9ac105 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,52 @@ +require "rspec" +require "fakefs/safe" +require "fakefs/spec_helpers" + +require 'foreman-export-initscript' + +def resource_path(filename) + File.expand_path("../resources/#{filename}", __FILE__) +end + +def write_procfile(procfile="Procfile", alpha_env="") + File.open(procfile, "w") do |file| + file.puts "alpha: ./alpha" + " #{alpha_env}".rstrip + file.puts "\n" + file.puts "bravo:\t./bravo" + end + File.expand_path(procfile) +end + +def load_export_templates_into_fakefs(type) + FakeFS.deactivate! + files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file| + hash.update(file => File.read(file)) + end + FakeFS.activate! + files.each do |filename, contents| + #require "debug" + FileUtils.mkdir_p RealFile.dirname filename + File.open(filename, "w") do |f| + f.puts contents + end + end +end + +def example_export_file(filename) + FakeFS.deactivate! + data = File.read(File.expand_path(resource_path("export/#{filename}"), __FILE__)) + FakeFS.activate! + data +end + +def normalize_space(s) + s.gsub(/\n[\n\s]*/, "\n") +end + +RSpec.configure do |config| + config.treat_symbols_as_metadata_keys_with_true_values = true + config.color_enabled = true + config.order = 'rand' + config.include FakeFS::SpecHelpers, :fakefs + config.mock_with :rr +end