From 284503899ad76eba8083f6e1a268bcf635be90d6 Mon Sep 17 00:00:00 2001 From: Marc Lennox Date: Fri, 3 May 2013 10:29:34 -0400 Subject: [PATCH] Added start-stop-daemon support. --- data/export/daemon/master.conf.erb | 14 +++ data/export/daemon/process.conf.erb | 8 ++ data/export/daemon/process_master.conf.erb | 2 + lib/foreman/cli.rb | 1 + lib/foreman/export.rb | 1 + lib/foreman/export/base.rb | 6 ++ lib/foreman/export/daemon.rb | 28 ++++++ spec/foreman/export/daemon_spec.rb | 97 +++++++++++++++++++ spec/resources/export/daemon/app-alpha-1.conf | 7 ++ spec/resources/export/daemon/app-alpha-2.conf | 7 ++ spec/resources/export/daemon/app-alpha.conf | 2 + spec/resources/export/daemon/app-bravo-1.conf | 7 ++ spec/resources/export/daemon/app-bravo.conf | 2 + spec/resources/export/daemon/app.conf | 14 +++ 14 files changed, 196 insertions(+) create mode 100644 data/export/daemon/master.conf.erb create mode 100644 data/export/daemon/process.conf.erb create mode 100644 data/export/daemon/process_master.conf.erb create mode 100644 lib/foreman/export/daemon.rb create mode 100644 spec/foreman/export/daemon_spec.rb create mode 100644 spec/resources/export/daemon/app-alpha-1.conf create mode 100644 spec/resources/export/daemon/app-alpha-2.conf create mode 100644 spec/resources/export/daemon/app-alpha.conf create mode 100644 spec/resources/export/daemon/app-bravo-1.conf create mode 100644 spec/resources/export/daemon/app-bravo.conf create mode 100644 spec/resources/export/daemon/app.conf diff --git a/data/export/daemon/master.conf.erb b/data/export/daemon/master.conf.erb new file mode 100644 index 0000000..0b2d2f7 --- /dev/null +++ b/data/export/daemon/master.conf.erb @@ -0,0 +1,14 @@ +pre-start script + +bash << "EOF" +mkdir -p <%= log %> +chown -R <%= user %> <%= log %> +mkdir -p <%= run %> +chown -R <%= user %> <%= run %> +EOF + +end script + +start on runlevel [2345] + +stop on runlevel [016] diff --git a/data/export/daemon/process.conf.erb b/data/export/daemon/process.conf.erb new file mode 100644 index 0000000..b133bbe --- /dev/null +++ b/data/export/daemon/process.conf.erb @@ -0,0 +1,8 @@ +start on starting <%= app %>-<%= name.gsub('_', '-') %> +stop on stopping <%= app %>-<%= name.gsub('_', '-') %> +respawn + +env PORT=<%= port %><% engine.env.each_pair do |var, env| %> +env <%= var.upcase %>=<%= env %><% end %> + +exec start-stop-daemon --start --chuid <%= user %> --chdir <%= engine.root %> --make-pidfile --pidfile <%= run %>/<%= app %>-<%= name %>-<%= num %>.pid --exec <%= executable %><%= arguments %> >> <%= log %>/<%= app %>-<%= name %>-<%= num %>.log 2>&1 diff --git a/data/export/daemon/process_master.conf.erb b/data/export/daemon/process_master.conf.erb new file mode 100644 index 0000000..c9cc851 --- /dev/null +++ b/data/export/daemon/process_master.conf.erb @@ -0,0 +1,2 @@ +start on starting <%= app %> +stop on stopping <%= app %> diff --git a/lib/foreman/cli.rb b/lib/foreman/cli.rb index 8ee1d17..26f7582 100644 --- a/lib/foreman/cli.rb +++ b/lib/foreman/cli.rb @@ -45,6 +45,7 @@ class Foreman::CLI < Thor method_option :app, :type => :string, :aliases => "-a" method_option :log, :type => :string, :aliases => "-l" + method_option :run, :type => :string, :aliases => "-r", :desc => "Specify the pid file directory, defaults to /var/run/" method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env" method_option :port, :type => :numeric, :aliases => "-p" method_option :user, :type => :string, :aliases => "-u" diff --git a/lib/foreman/export.rb b/lib/foreman/export.rb index cdec407..04bf71c 100644 --- a/lib/foreman/export.rb +++ b/lib/foreman/export.rb @@ -28,6 +28,7 @@ end require "foreman/export/base" require "foreman/export/inittab" require "foreman/export/upstart" +require "foreman/export/daemon" require "foreman/export/bluepill" require "foreman/export/runit" require "foreman/export/supervisord" diff --git a/lib/foreman/export/base.rb b/lib/foreman/export/base.rb index 5541006..2131f26 100644 --- a/lib/foreman/export/base.rb +++ b/lib/foreman/export/base.rb @@ -47,7 +47,9 @@ class Foreman::Export::Base error("Must specify a location") unless location FileUtils.mkdir_p(location) rescue error("Could not create: #{location}") FileUtils.mkdir_p(log) rescue error("Could not create: #{log}") + FileUtils.mkdir_p(run) rescue error("Could not create: #{run}") FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}") + FileUtils.chown(user, nil, run) rescue error("Could not chown #{run} to #{user}") end def app @@ -58,6 +60,10 @@ class Foreman::Export::Base options[:log] || "/var/log/#{app}" end + def run + options[:run] || "/var/run/#{app}" + end + def user options[:user] || app end diff --git a/lib/foreman/export/daemon.rb b/lib/foreman/export/daemon.rb new file mode 100644 index 0000000..40f623c --- /dev/null +++ b/lib/foreman/export/daemon.rb @@ -0,0 +1,28 @@ +require "erb" +require "foreman/export" + +class Foreman::Export::Daemon < Foreman::Export::Base + + def export + super + + (Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file| + clean file + end + + write_template "daemon/master.conf.erb", "#{app}.conf", binding + + engine.each_process do |name, process| + next if engine.formation[name] < 1 + write_template "daemon/process_master.conf.erb", "#{app}-#{name}.conf", binding + + 1.upto(engine.formation[name]) do |num| + port = engine.port_for(process, num) + arguments = process.command.split(" ") + executable = arguments.slice!(0) + arguments = arguments.size > 0 ? " -- #{arguments.join(' ')}" : "" + write_template "daemon/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding + end + end + end +end diff --git a/spec/foreman/export/daemon_spec.rb b/spec/foreman/export/daemon_spec.rb new file mode 100644 index 0000000..454270b --- /dev/null +++ b/spec/foreman/export/daemon_spec.rb @@ -0,0 +1,97 @@ +require "spec_helper" +require "foreman/engine" +require "foreman/export/daemon" +require "tmpdir" + +describe Foreman::Export::Daemon, :fakefs do + let(:procfile) { write_procfile("/tmp/app/Procfile") } + let(:formation) { nil } + let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) } + let(:options) { Hash.new } + let(:daemon) { Foreman::Export::Daemon.new("/tmp/init", engine, options) } + + before(:each) { load_export_templates_into_fakefs("daemon") } + before(:each) { stub(daemon).say } + + it "exports to the filesystem" do + daemon.export + + File.read("/tmp/init/app.conf").should == example_export_file("daemon/app.conf") + File.read("/tmp/init/app-alpha.conf").should == example_export_file("daemon/app-alpha.conf") + File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("daemon/app-alpha-1.conf") + File.read("/tmp/init/app-bravo.conf").should == example_export_file("daemon/app-bravo.conf") + File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("daemon/app-bravo-1.conf") + end + + it "cleans up if exporting into an existing dir" do + mock(FileUtils).rm("/tmp/init/app.conf") + mock(FileUtils).rm("/tmp/init/app-alpha.conf") + mock(FileUtils).rm("/tmp/init/app-alpha-1.conf") + mock(FileUtils).rm("/tmp/init/app-bravo.conf") + mock(FileUtils).rm("/tmp/init/app-bravo-1.conf") + mock(FileUtils).rm("/tmp/init/app-foo-bar.conf") + mock(FileUtils).rm("/tmp/init/app-foo-bar-1.conf") + mock(FileUtils).rm("/tmp/init/app-foo_bar.conf") + mock(FileUtils).rm("/tmp/init/app-foo_bar-1.conf") + + daemon.export + daemon.export + end + + it "does not delete exported files for similarly named applications" do + FileUtils.mkdir_p "/tmp/init" + + ["app2", "app2-alpha", "app2-alpha-1"].each do |name| + path = "/tmp/init/#{name}.conf" + FileUtils.touch(path) + dont_allow(FileUtils).rm(path) + end + + daemon.export + end + + context "with a formation" do + let(:formation) { "alpha=2" } + + it "exports to the filesystem with concurrency" do + daemon.export + + File.read("/tmp/init/app.conf").should == example_export_file("daemon/app.conf") + File.read("/tmp/init/app-alpha.conf").should == example_export_file("daemon/app-alpha.conf") + File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("daemon/app-alpha-1.conf") + File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("daemon/app-alpha-2.conf") + File.exists?("/tmp/init/app-bravo-1.conf").should == false + end + end + + context "with alternate templates" do + let(:template) { "/tmp/alternate" } + let(:options) { { :app => "app", :template => template } } + + before do + FileUtils.mkdir_p template + File.open("#{template}/master.conf.erb", "w") { |f| f.puts "alternate_template" } + end + + it "can export with alternate template files" do + daemon.export + File.read("/tmp/init/app.conf").should == "alternate_template\n" + end + end + + context "with alternate templates from home dir" do + + before do + FileUtils.mkdir_p File.expand_path("~/.foreman/templates/daemon") + File.open(File.expand_path("~/.foreman/templates/daemon/master.conf.erb"), "w") do |file| + file.puts "default_alternate_template" + end + end + + it "can export with alternate template files" do + daemon.export + File.read("/tmp/init/app.conf").should == "default_alternate_template\n" + end + end + +end diff --git a/spec/resources/export/daemon/app-alpha-1.conf b/spec/resources/export/daemon/app-alpha-1.conf new file mode 100644 index 0000000..347fc3a --- /dev/null +++ b/spec/resources/export/daemon/app-alpha-1.conf @@ -0,0 +1,7 @@ +start on starting app-alpha +stop on stopping app-alpha +respawn + +env PORT=5000 + +exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-alpha-1.pid --exec ./alpha >> /var/log/app/app-alpha-1.log 2>&1 diff --git a/spec/resources/export/daemon/app-alpha-2.conf b/spec/resources/export/daemon/app-alpha-2.conf new file mode 100644 index 0000000..f72fe0c --- /dev/null +++ b/spec/resources/export/daemon/app-alpha-2.conf @@ -0,0 +1,7 @@ +start on starting app-alpha +stop on stopping app-alpha +respawn + +env PORT=5001 + +exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-alpha-2.pid --exec ./alpha >> /var/log/app/app-alpha-2.log 2>&1 diff --git a/spec/resources/export/daemon/app-alpha.conf b/spec/resources/export/daemon/app-alpha.conf new file mode 100644 index 0000000..848119e --- /dev/null +++ b/spec/resources/export/daemon/app-alpha.conf @@ -0,0 +1,2 @@ +start on starting app +stop on stopping app diff --git a/spec/resources/export/daemon/app-bravo-1.conf b/spec/resources/export/daemon/app-bravo-1.conf new file mode 100644 index 0000000..44c4c1a --- /dev/null +++ b/spec/resources/export/daemon/app-bravo-1.conf @@ -0,0 +1,7 @@ +start on starting app-bravo +stop on stopping app-bravo +respawn + +env PORT=5100 + +exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-bravo-1.pid --exec ./bravo >> /var/log/app/app-bravo-1.log 2>&1 diff --git a/spec/resources/export/daemon/app-bravo.conf b/spec/resources/export/daemon/app-bravo.conf new file mode 100644 index 0000000..848119e --- /dev/null +++ b/spec/resources/export/daemon/app-bravo.conf @@ -0,0 +1,2 @@ +start on starting app +stop on stopping app diff --git a/spec/resources/export/daemon/app.conf b/spec/resources/export/daemon/app.conf new file mode 100644 index 0000000..f213f0e --- /dev/null +++ b/spec/resources/export/daemon/app.conf @@ -0,0 +1,14 @@ +pre-start script + +bash << "EOF" +mkdir -p /var/log/app +chown -R app /var/log/app +mkdir -p /var/run/app +chown -R app /var/run/app +EOF + +end script + +start on runlevel [2345] + +stop on runlevel [016]