foreman-systemd 0.78.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +64 -0
  3. data/bin/foreman +7 -0
  4. data/bin/foreman-runner +41 -0
  5. data/data/example/Procfile +4 -0
  6. data/data/example/Procfile.without_colon +2 -0
  7. data/data/example/error +7 -0
  8. data/data/example/log/neverdie.log +4 -0
  9. data/data/example/spawnee +14 -0
  10. data/data/example/spawner +7 -0
  11. data/data/example/ticker +14 -0
  12. data/data/example/utf8 +11 -0
  13. data/data/export/bluepill/master.pill.erb +28 -0
  14. data/data/export/daemon/master.conf.erb +14 -0
  15. data/data/export/daemon/process.conf.erb +8 -0
  16. data/data/export/daemon/process_master.conf.erb +2 -0
  17. data/data/export/launchd/launchd.plist.erb +33 -0
  18. data/data/export/runit/log/run.erb +7 -0
  19. data/data/export/runit/run.erb +4 -0
  20. data/data/export/supervisord/app.conf.erb +28 -0
  21. data/data/export/systemd/master.target.erb +6 -0
  22. data/data/export/systemd/process.service.erb +16 -0
  23. data/data/export/systemd/process_master.target.erb +4 -0
  24. data/data/export/upstart/master.conf.erb +2 -0
  25. data/data/export/upstart/process.conf.erb +14 -0
  26. data/data/export/upstart/process_master.conf.erb +2 -0
  27. data/lib/foreman.rb +17 -0
  28. data/lib/foreman/cli.rb +161 -0
  29. data/lib/foreman/distribution.rb +9 -0
  30. data/lib/foreman/engine.rb +441 -0
  31. data/lib/foreman/engine/cli.rb +104 -0
  32. data/lib/foreman/env.rb +29 -0
  33. data/lib/foreman/export.rb +36 -0
  34. data/lib/foreman/export/base.rb +156 -0
  35. data/lib/foreman/export/bluepill.rb +12 -0
  36. data/lib/foreman/export/daemon.rb +28 -0
  37. data/lib/foreman/export/inittab.rb +42 -0
  38. data/lib/foreman/export/launchd.rb +22 -0
  39. data/lib/foreman/export/runit.rb +34 -0
  40. data/lib/foreman/export/supervisord.rb +16 -0
  41. data/lib/foreman/export/systemd.rb +32 -0
  42. data/lib/foreman/export/upstart.rb +43 -0
  43. data/lib/foreman/helpers.rb +45 -0
  44. data/lib/foreman/process.rb +80 -0
  45. data/lib/foreman/procfile.rb +92 -0
  46. data/lib/foreman/version.rb +5 -0
  47. data/man/foreman.1 +278 -0
  48. data/spec/foreman/cli_spec.rb +107 -0
  49. data/spec/foreman/engine_spec.rb +112 -0
  50. data/spec/foreman/export/base_spec.rb +19 -0
  51. data/spec/foreman/export/bluepill_spec.rb +37 -0
  52. data/spec/foreman/export/daemon_spec.rb +97 -0
  53. data/spec/foreman/export/inittab_spec.rb +40 -0
  54. data/spec/foreman/export/launchd_spec.rb +31 -0
  55. data/spec/foreman/export/runit_spec.rb +36 -0
  56. data/spec/foreman/export/supervisord_spec.rb +36 -0
  57. data/spec/foreman/export/systemd_spec.rb +91 -0
  58. data/spec/foreman/export/upstart_spec.rb +118 -0
  59. data/spec/foreman/export_spec.rb +24 -0
  60. data/spec/foreman/helpers_spec.rb +26 -0
  61. data/spec/foreman/process_spec.rb +71 -0
  62. data/spec/foreman/procfile_spec.rb +43 -0
  63. data/spec/foreman_spec.rb +16 -0
  64. data/spec/helper_spec.rb +19 -0
  65. data/spec/resources/Procfile +5 -0
  66. data/spec/resources/bin/echo +2 -0
  67. data/spec/resources/bin/env +2 -0
  68. data/spec/resources/bin/test +2 -0
  69. data/spec/resources/bin/utf8 +2 -0
  70. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  71. data/spec/resources/export/bluepill/app.pill +81 -0
  72. data/spec/resources/export/daemon/app-alpha-1.conf +7 -0
  73. data/spec/resources/export/daemon/app-alpha-2.conf +7 -0
  74. data/spec/resources/export/daemon/app-alpha.conf +2 -0
  75. data/spec/resources/export/daemon/app-bravo-1.conf +7 -0
  76. data/spec/resources/export/daemon/app-bravo.conf +2 -0
  77. data/spec/resources/export/daemon/app.conf +14 -0
  78. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  79. data/spec/resources/export/inittab/inittab.default +6 -0
  80. data/spec/resources/export/launchd/launchd-a.default +29 -0
  81. data/spec/resources/export/launchd/launchd-b.default +29 -0
  82. data/spec/resources/export/launchd/launchd-c.default +30 -0
  83. data/spec/resources/export/runit/app-alpha-1/log/run +7 -0
  84. data/spec/resources/export/runit/app-alpha-1/run +4 -0
  85. data/spec/resources/export/runit/app-alpha-2/log/run +7 -0
  86. data/spec/resources/export/runit/app-alpha-2/run +4 -0
  87. data/spec/resources/export/runit/app-bravo-1/log/run +7 -0
  88. data/spec/resources/export/runit/app-bravo-1/run +4 -0
  89. data/spec/resources/export/supervisord/app-alpha-1.conf +46 -0
  90. data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
  91. data/spec/resources/export/systemd/concurrency/app-alpha-1.service +14 -0
  92. data/spec/resources/export/systemd/concurrency/app-alpha-2.service +14 -0
  93. data/spec/resources/export/systemd/concurrency/app-alpha.target +3 -0
  94. data/spec/resources/export/systemd/concurrency/app.target +6 -0
  95. data/spec/resources/export/systemd/standard/app-alpha-1.service +14 -0
  96. data/spec/resources/export/systemd/standard/app-alpha.target +3 -0
  97. data/spec/resources/export/systemd/standard/app-bravo-1.service +14 -0
  98. data/spec/resources/export/systemd/standard/app-bravo.target +3 -0
  99. data/spec/resources/export/systemd/standard/app.target +6 -0
  100. data/spec/resources/export/upstart/app-alpha-1.conf +11 -0
  101. data/spec/resources/export/upstart/app-alpha-2.conf +11 -0
  102. data/spec/resources/export/upstart/app-alpha.conf +2 -0
  103. data/spec/resources/export/upstart/app-bravo-1.conf +11 -0
  104. data/spec/resources/export/upstart/app-bravo.conf +2 -0
  105. data/spec/resources/export/upstart/app.conf +2 -0
  106. data/spec/spec_helper.rb +166 -0
  107. metadata +164 -0
@@ -0,0 +1,29 @@
1
+ require "foreman"
2
+
3
+ class Foreman::Env
4
+
5
+ attr_reader :entries
6
+
7
+ def initialize(filename)
8
+ @entries = File.read(filename).gsub("\r\n","\n").split("\n").inject({}) do |ax, line|
9
+ if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
10
+ key = $1
11
+ case val = $2
12
+ # Remove single quotes
13
+ when /\A'(.*)'\z/ then ax[key] = $1
14
+ # Remove double quotes and unescape string preserving newline characters
15
+ when /\A"(.*)"\z/ then ax[key] = $1.gsub('\n', "\n").gsub(/\\(.)/, '\1')
16
+ else ax[key] = val
17
+ end
18
+ end
19
+ ax
20
+ end
21
+ end
22
+
23
+ def entries
24
+ @entries.each do |key, value|
25
+ yield key, value
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,36 @@
1
+ require "foreman"
2
+ require "foreman/helpers"
3
+ require "pathname"
4
+
5
+ module Foreman::Export
6
+ extend Foreman::Helpers
7
+
8
+ class Exception < ::Exception; end
9
+
10
+ def self.formatter(format)
11
+ begin
12
+ require "foreman/export/#{ format.tr('-', '_') }"
13
+ classy_format = classify(format)
14
+ formatter = constantize("Foreman::Export::#{ classy_format }")
15
+ rescue NameError => ex
16
+ error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
17
+ rescue LoadError => ex
18
+ error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
19
+ end
20
+ end
21
+
22
+ def self.error(message)
23
+ raise Foreman::Export::Exception.new(message)
24
+ end
25
+
26
+ end
27
+
28
+ require "foreman/export/base"
29
+ require "foreman/export/inittab"
30
+ require "foreman/export/upstart"
31
+ require "foreman/export/daemon"
32
+ require "foreman/export/bluepill"
33
+ require "foreman/export/runit"
34
+ require "foreman/export/supervisord"
35
+ require "foreman/export/launchd"
36
+ require "foreman/export/systemd"
@@ -0,0 +1,156 @@
1
+ require "foreman/export"
2
+ require "ostruct"
3
+ require "pathname"
4
+ require "shellwords"
5
+
6
+ class Foreman::Export::Base
7
+
8
+ attr_reader :location
9
+ attr_reader :engine
10
+ attr_reader :options
11
+ attr_reader :formation
12
+
13
+ # deprecated
14
+ attr_reader :port
15
+
16
+ def initialize(location, engine, options={})
17
+ @location = location
18
+ @engine = engine
19
+ @options = options.dup
20
+ @formation = engine.formation
21
+
22
+ # deprecated
23
+ def port
24
+ Foreman::Export::Base.warn_deprecation!
25
+ engine.base_port
26
+ end
27
+
28
+ # deprecated
29
+ def template
30
+ Foreman::Export::Base.warn_deprecation!
31
+ options[:template]
32
+ end
33
+
34
+ # deprecated
35
+ def @engine.procfile
36
+ Foreman::Export::Base.warn_deprecation!
37
+ @processes.map do |process|
38
+ OpenStruct.new(
39
+ :name => @names[process],
40
+ :process => process
41
+ )
42
+ end
43
+ end
44
+ end
45
+
46
+ def export
47
+ error("Must specify a location") unless location
48
+ FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
49
+ chown user, log
50
+ chown user, run
51
+ end
52
+
53
+ def app
54
+ options[:app] || "app"
55
+ end
56
+
57
+ def log
58
+ options[:log] || "/var/log/#{app}"
59
+ end
60
+
61
+ def run
62
+ options[:run] || "/var/run/#{app}"
63
+ end
64
+
65
+ def user
66
+ options[:user] || app
67
+ end
68
+
69
+ private ######################################################################
70
+
71
+ def self.warn_deprecation!
72
+ @@deprecation_warned ||= false
73
+ return if @@deprecation_warned
74
+ puts "WARNING: Using deprecated exporter interface. Please update your exporter"
75
+ puts "the interface shown in the upstart exporter:"
76
+ puts
77
+ puts "https://github.com/ddollar/foreman/blob/master/lib/foreman/export/upstart.rb"
78
+ puts "https://github.com/ddollar/foreman/blob/master/data/export/upstart/process.conf.erb"
79
+ puts
80
+ @@deprecation_warned = true
81
+ end
82
+
83
+ def chown user, dir
84
+ FileUtils.chown user, nil, dir
85
+ rescue
86
+ error("Could not chown #{dir} to #{user}") unless File.writable?(dir) || ! File.exists?(dir)
87
+ end
88
+
89
+ def error(message)
90
+ raise Foreman::Export::Exception.new(message)
91
+ end
92
+
93
+ def say(message)
94
+ puts "[foreman export] %s" % message
95
+ end
96
+
97
+ def clean(filename)
98
+ return unless File.exists?(filename)
99
+ say "cleaning up: #{filename}"
100
+ FileUtils.rm(filename)
101
+ end
102
+
103
+ def shell_quote(value)
104
+ Shellwords.escape(value)
105
+ end
106
+
107
+ # deprecated
108
+ def old_export_template(exporter, file, template_root)
109
+ if template_root && File.exist?(file_path = File.join(template_root, file))
110
+ File.read(file_path)
111
+ elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
112
+ File.read(file_path)
113
+ else
114
+ File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
115
+ end
116
+ end
117
+
118
+ def export_template(name, file=nil, template_root=nil)
119
+ if file && template_root
120
+ old_export_template name, file, template_root
121
+ else
122
+ name_without_first = name.split("/")[1..-1].join("/")
123
+ matchers = []
124
+ matchers << File.join(options[:template], name_without_first) if options[:template]
125
+ matchers << File.expand_path("~/.foreman/templates/#{name}")
126
+ matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
127
+ File.read(matchers.detect { |m| File.exists?(m) })
128
+ end
129
+ end
130
+
131
+ def write_template(name, target, binding)
132
+ compiled = ERB.new(export_template(name), nil, '-').result(binding)
133
+ write_file target, compiled
134
+ end
135
+
136
+ def chmod(mode, file)
137
+ say "setting #{file} to mode #{mode}"
138
+ FileUtils.chmod mode, File.join(location, file)
139
+ end
140
+
141
+ def create_directory(dir)
142
+ say "creating: #{dir}"
143
+ FileUtils.mkdir_p(File.join(location, dir))
144
+ end
145
+
146
+ def write_file(filename, contents)
147
+ say "writing: #{filename}"
148
+
149
+ filename = File.join(location, filename) unless Pathname.new(filename).absolute?
150
+
151
+ File.open(filename, "w") do |file|
152
+ file.puts contents
153
+ end
154
+ end
155
+
156
+ end
@@ -0,0 +1,12 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Bluepill < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+ clean "#{location}/#{app}.pill"
9
+ write_template "bluepill/master.pill.erb", "#{app}.pill", binding
10
+ end
11
+
12
+ end
@@ -0,0 +1,28 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Daemon < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ (Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file|
10
+ clean file
11
+ end
12
+
13
+ write_template "daemon/master.conf.erb", "#{app}.conf", binding
14
+
15
+ engine.each_process do |name, process|
16
+ next if engine.formation[name] < 1
17
+ write_template "daemon/process_master.conf.erb", "#{app}-#{name}.conf", binding
18
+
19
+ 1.upto(engine.formation[name]) do |num|
20
+ port = engine.port_for(process, num)
21
+ arguments = process.command.split(" ")
22
+ executable = arguments.slice!(0)
23
+ arguments = arguments.size > 0 ? " -- #{arguments.join(' ')}" : ""
24
+ write_template "daemon/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ require "foreman/export"
2
+
3
+ class Foreman::Export::Inittab < Foreman::Export::Base
4
+
5
+ def export
6
+ error("Must specify a location") unless location
7
+
8
+ inittab = []
9
+ inittab << "# ----- foreman #{app} processes -----"
10
+
11
+ index = 1
12
+ engine.each_process do |name, process|
13
+ 1.upto(engine.formation[name]) do |num|
14
+ id = app.slice(0, 2).upcase + sprintf("%02d", index)
15
+ port = engine.port_for(process, num)
16
+
17
+ commands = []
18
+ commands << "cd #{engine.root}"
19
+ commands << "export PORT=#{port}"
20
+ engine.env.each_pair do |var, env|
21
+ commands << "export #{var.upcase}=#{shell_quote(env)}"
22
+ end
23
+ commands << "#{process.command} >> #{log}/#{name}-#{num}.log 2>&1"
24
+
25
+ inittab << "#{id}:4:respawn:/bin/su - #{user} -c '#{commands.join(";")}'"
26
+ index += 1
27
+ end
28
+ end
29
+
30
+ inittab << "# ----- end foreman #{app} processes -----"
31
+
32
+ inittab = inittab.join("\n") + "\n"
33
+
34
+ if location == "-"
35
+ puts inittab
36
+ else
37
+ say "writing: #{location}"
38
+ File.open(location, "w") { |file| file.puts inittab }
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,22 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Launchd < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+ engine.each_process do |name, process|
9
+ 1.upto(engine.formation[name]) do |num|
10
+ port = engine.port_for(process, num)
11
+ command_args = process.command.split(/\s+/).map{|arg|
12
+ case arg
13
+ when "$PORT" then port
14
+ else arg
15
+ end
16
+ }
17
+ write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
18
+ end
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,34 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Runit < Foreman::Export::Base
5
+
6
+ ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
7
+
8
+ def export
9
+ super
10
+
11
+ engine.each_process do |name, process|
12
+ 1.upto(engine.formation[name]) do |num|
13
+ process_directory = "#{app}-#{name}-#{num}"
14
+
15
+ create_directory process_directory
16
+ create_directory "#{process_directory}/env"
17
+ create_directory "#{process_directory}/log"
18
+
19
+ write_template "runit/run.erb", "#{process_directory}/run", binding
20
+ chmod 0755, "#{process_directory}/run"
21
+
22
+ port = engine.port_for(process, num)
23
+ engine.env.merge("PORT" => port.to_s).each do |key, value|
24
+ write_file "#{process_directory}/env/#{key}", value
25
+ end
26
+
27
+ write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
28
+ chmod 0755, "#{process_directory}/log/run"
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,16 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Supervisord < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ Dir["#{location}/#{app}*.conf"].each do |file|
10
+ clean file
11
+ end
12
+
13
+ write_template "supervisord/app.conf.erb", "#{app}.conf", binding
14
+ end
15
+
16
+ end
@@ -0,0 +1,32 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Systemd < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ Dir["#{location}/#{app}*.target"].concat(Dir["#{location}/#{app}*.service"]).each do |file|
10
+ clean file
11
+ end
12
+
13
+ process_master_names = []
14
+
15
+ engine.each_process do |name, process|
16
+ next if engine.formation[name] < 1
17
+
18
+ process_names = []
19
+
20
+ 1.upto(engine.formation[name]) do |num|
21
+ port = engine.port_for(process, num)
22
+ write_template "systemd/process.service.erb", "#{app}-#{name}-#{num}.service", binding
23
+ process_names << "#{app}-#{name}-#{num}.service"
24
+ end
25
+
26
+ write_template "systemd/process_master.target.erb", "#{app}-#{name}.target", binding
27
+ process_master_names << "#{app}-#{name}.target"
28
+ end
29
+
30
+ write_template "systemd/master.target.erb", "#{app}.target", binding
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Upstart < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ master_file = "#{app}.conf"
10
+
11
+ clean File.join(location, master_file)
12
+ write_template master_template, master_file, binding
13
+
14
+ engine.each_process do |name, process|
15
+ process_master_file = "#{app}-#{name}.conf"
16
+ clean File.join(location, process_master_file)
17
+
18
+ next if engine.formation[name] < 1
19
+ write_template process_master_template, process_master_file, binding
20
+
21
+ 1.upto(engine.formation[name]) do |num|
22
+ port = engine.port_for(process, num)
23
+ process_file = "#{app}-#{name}-#{num}.conf"
24
+ clean File.join(location, process_file)
25
+ write_template process_template, process_file, binding
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def master_template
33
+ "upstart/master.conf.erb"
34
+ end
35
+
36
+ def process_master_template
37
+ "upstart/process_master.conf.erb"
38
+ end
39
+
40
+ def process_template
41
+ "upstart/process.conf.erb"
42
+ end
43
+ end