rough draft for systemd export support
http://0pointer.de/blog/projects/systemd.html This adds support for exporting systemd targets and services. The structure is based on the existing upstart support. Quality is draft and expected to refine in the coming weeks. One Foremanism that is not respected by these export templates is the usual log output location, instead stdout and stderr go to syslog.
This commit is contained in:
1
data/export/systemd/master.target.erb
Normal file
1
data/export/systemd/master.target.erb
Normal file
@@ -0,0 +1 @@
|
||||
[Unit]
|
||||
17
data/export/systemd/process.service.erb
Normal file
17
data/export/systemd/process.service.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Service]
|
||||
User=<%= user %>
|
||||
WorkingDirectory=<%= engine.root %>
|
||||
Environment=PORT=<%= port %><% engine.env.each_pair do |var,env| %>
|
||||
Environment=<%= var.upcase %>=<%= env %><% end %>
|
||||
ExecStart=/bin/bash -lc '<%= process.command %>'
|
||||
Restart=always
|
||||
StandardInput=null
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=%n
|
||||
|
||||
[Install]
|
||||
WantedBy=<%= app %>-<%= name %>.target
|
||||
5
data/export/systemd/process_master.target.erb
Normal file
5
data/export/systemd/process_master.target.erb
Normal file
@@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Install]
|
||||
WantedBy=<%= app %>.target
|
||||
@@ -32,3 +32,4 @@ require "foreman/export/bluepill"
|
||||
require "foreman/export/runit"
|
||||
require "foreman/export/supervisord"
|
||||
require "foreman/export/launchd"
|
||||
require "foreman/export/systemd"
|
||||
|
||||
25
lib/foreman/export/systemd.rb
Normal file
25
lib/foreman/export/systemd.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
require "erb"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Systemd < Foreman::Export::Base
|
||||
|
||||
def export
|
||||
super
|
||||
|
||||
Dir["#{location}/#{app}*.target"].concat(Dir["#{location}/#{app}*.service"]).each do |file|
|
||||
clean file
|
||||
end
|
||||
|
||||
write_template "systemd/master.target.erb", "#{app}.target", binding
|
||||
|
||||
engine.each_process do |name, process|
|
||||
next if engine.formation[name] < 1
|
||||
write_template "systemd/process_master.target.erb", "#{app}-#{name}.target", binding
|
||||
|
||||
1.upto(engine.formation[name]) do |num|
|
||||
port = engine.port_for(process, num)
|
||||
write_template "systemd/process.service.erb", "#{app}-#{name}-#{num}.service", binding
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -103,6 +103,8 @@ foreman currently supports the following output formats:
|
||||
|
||||
* runit
|
||||
|
||||
* systemd
|
||||
|
||||
* upstart
|
||||
|
||||
## INITTAB EXPORT
|
||||
@@ -114,6 +116,17 @@ Will export a chunk of inittab-compatible configuration:
|
||||
EX02:4:respawn:/bin/su - example -c 'PORT=5100 bundle exec rake jobs:work >> /var/log/job-1.log 2>&1'
|
||||
# ----- end foreman example processes -----
|
||||
|
||||
## SYSTEMD EXPORT
|
||||
|
||||
Will create a series of systemd scripts in the location you specify. Scripts
|
||||
will be structured to make the following commands valid:
|
||||
|
||||
`systemctl start appname.target`
|
||||
|
||||
`systemctl stop appname-processname.target`
|
||||
|
||||
`systemctl restart appname-processname-3.service`
|
||||
|
||||
## UPSTART EXPORT
|
||||
|
||||
Will create a series of upstart scripts in the location you specify. Scripts
|
||||
|
||||
87
spec/foreman/export/systemd_spec.rb
Normal file
87
spec/foreman/export/systemd_spec.rb
Normal file
@@ -0,0 +1,87 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/systemd"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Systemd, :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(:systemd) { Foreman::Export::Systemd.new("/tmp/init", engine, options) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("systemd") }
|
||||
before(:each) { stub(systemd).say }
|
||||
|
||||
it "exports to the filesystem" do
|
||||
systemd.export
|
||||
|
||||
File.read("/tmp/init/app.target").should == example_export_file("systemd/app.target")
|
||||
File.read("/tmp/init/app-alpha.target").should == example_export_file("systemd/app-alpha.target")
|
||||
File.read("/tmp/init/app-alpha-1.service").should == example_export_file("systemd/app-alpha-1.service")
|
||||
File.read("/tmp/init/app-bravo.target").should == example_export_file("systemd/app-bravo.target")
|
||||
File.read("/tmp/init/app-bravo-1.service").should == example_export_file("systemd/app-bravo-1.service")
|
||||
end
|
||||
|
||||
it "cleans up if exporting into an existing dir" do
|
||||
mock(FileUtils).rm("/tmp/init/app.target")
|
||||
mock(FileUtils).rm("/tmp/init/app-alpha.target")
|
||||
mock(FileUtils).rm("/tmp/init/app-alpha-1.service")
|
||||
mock(FileUtils).rm("/tmp/init/app-bravo.target")
|
||||
mock(FileUtils).rm("/tmp/init/app-bravo-1.service")
|
||||
|
||||
systemd.export
|
||||
systemd.export
|
||||
end
|
||||
|
||||
it "includes environment variables" do
|
||||
engine.env['KEY'] = 'some "value"'
|
||||
systemd.export
|
||||
File.read("/tmp/init/app-alpha-1.service").should =~ /KEY=some "value"$/
|
||||
end
|
||||
|
||||
context "with a formation" do
|
||||
let(:formation) { "alpha=2" }
|
||||
|
||||
it "exports to the filesystem with concurrency" do
|
||||
systemd.export
|
||||
|
||||
File.read("/tmp/init/app.target").should == example_export_file("systemd/app.target")
|
||||
File.read("/tmp/init/app-alpha.target").should == example_export_file("systemd/app-alpha.target")
|
||||
File.read("/tmp/init/app-alpha-1.service").should == example_export_file("systemd/app-alpha-1.service")
|
||||
File.read("/tmp/init/app-alpha-2.service").should == example_export_file("systemd/app-alpha-2.service")
|
||||
File.exists?("/tmp/init/app-bravo-1.service").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.target.erb", "w") { |f| f.puts "alternate_template" }
|
||||
end
|
||||
|
||||
it "can export with alternate template files" do
|
||||
systemd.export
|
||||
File.read("/tmp/init/app.target").should == "alternate_template\n"
|
||||
end
|
||||
end
|
||||
|
||||
context "with alternate templates from home dir" do
|
||||
|
||||
before do
|
||||
FileUtils.mkdir_p File.expand_path("~/.foreman/templates/systemd")
|
||||
File.open(File.expand_path("~/.foreman/templates/systemd/master.target.erb"), "w") do |file|
|
||||
file.puts "default_alternate_template"
|
||||
end
|
||||
end
|
||||
|
||||
it "can export with alternate template files" do
|
||||
systemd.export
|
||||
File.read("/tmp/init/app.target").should == "default_alternate_template\n"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
16
spec/resources/export/systemd/app-alpha-1.service
Normal file
16
spec/resources/export/systemd/app-alpha-1.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Service]
|
||||
User=app
|
||||
WorkingDirectory=/tmp/app
|
||||
Environment=PORT=5000
|
||||
ExecStart=/bin/bash -lc './alpha'
|
||||
Restart=always
|
||||
StandardInput=null
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=%n
|
||||
|
||||
[Install]
|
||||
WantedBy=app-alpha.target
|
||||
16
spec/resources/export/systemd/app-alpha-2.service
Normal file
16
spec/resources/export/systemd/app-alpha-2.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Service]
|
||||
User=app
|
||||
WorkingDirectory=/tmp/app
|
||||
Environment=PORT=5001
|
||||
ExecStart=/bin/bash -lc './alpha'
|
||||
Restart=always
|
||||
StandardInput=null
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=%n
|
||||
|
||||
[Install]
|
||||
WantedBy=app-alpha.target
|
||||
5
spec/resources/export/systemd/app-alpha.target
Normal file
5
spec/resources/export/systemd/app-alpha.target
Normal file
@@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Install]
|
||||
WantedBy=app.target
|
||||
16
spec/resources/export/systemd/app-bravo-1.service
Normal file
16
spec/resources/export/systemd/app-bravo-1.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Service]
|
||||
User=app
|
||||
WorkingDirectory=/tmp/app
|
||||
Environment=PORT=5100
|
||||
ExecStart=/bin/bash -lc './bravo'
|
||||
Restart=always
|
||||
StandardInput=null
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=%n
|
||||
|
||||
[Install]
|
||||
WantedBy=app-bravo.target
|
||||
5
spec/resources/export/systemd/app-bravo.target
Normal file
5
spec/resources/export/systemd/app-bravo.target
Normal file
@@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
StopWhenUnneeded=true
|
||||
|
||||
[Install]
|
||||
WantedBy=app.target
|
||||
1
spec/resources/export/systemd/app.target
Normal file
1
spec/resources/export/systemd/app.target
Normal file
@@ -0,0 +1 @@
|
||||
[Unit]
|
||||
Reference in New Issue
Block a user