pleaserun 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +73 -0
  4. data/Guardfile +17 -0
  5. data/Makefile +50 -0
  6. data/README.md +98 -0
  7. data/bin/pleaserun +7 -0
  8. data/examples/runit.rb +16 -0
  9. data/lib/pleaserun/cli.rb +241 -0
  10. data/lib/pleaserun/configurable.rb +143 -0
  11. data/lib/pleaserun/detector.rb +58 -0
  12. data/lib/pleaserun/mustache_methods.rb +41 -0
  13. data/lib/pleaserun/namespace.rb +3 -0
  14. data/lib/pleaserun/platform/base.rb +144 -0
  15. data/lib/pleaserun/platform/launchd.rb +27 -0
  16. data/lib/pleaserun/platform/runit.rb +18 -0
  17. data/lib/pleaserun/platform/systemd.rb +24 -0
  18. data/lib/pleaserun/platform/sysv.rb +12 -0
  19. data/lib/pleaserun/platform/upstart.rb +11 -0
  20. data/pleaserun.gemspec +27 -0
  21. data/spec/pleaserun/configurable_spec.rb +215 -0
  22. data/spec/pleaserun/mustache_methods_spec.rb +46 -0
  23. data/spec/pleaserun/platform/base_spec.rb +27 -0
  24. data/spec/pleaserun/platform/launchd_spec.rb +93 -0
  25. data/spec/pleaserun/platform/systemd_spec.rb +119 -0
  26. data/spec/pleaserun/platform/sysv_spec.rb +133 -0
  27. data/spec/pleaserun/platform/upstart_spec.rb +117 -0
  28. data/spec/testenv.rb +69 -0
  29. data/templates/launchd/10.9/program.plist +47 -0
  30. data/templates/runit/log +4 -0
  31. data/templates/runit/run +17 -0
  32. data/templates/systemd/default/prestart.sh +2 -0
  33. data/templates/systemd/default/program.service +17 -0
  34. data/templates/sysv/lsb-3.1/default +0 -0
  35. data/templates/sysv/lsb-3.1/init.d +141 -0
  36. data/templates/upstart/1.5/init.conf +41 -0
  37. data/templates/upstart/1.5/init.d.sh +4 -0
  38. data/test.rb +33 -0
  39. data/test/helpers.rb +20 -0
  40. data/test/test.rb +60 -0
  41. data/test/vagrant/Vagrantfile +40 -0
  42. data/test/vagrant/fedora-18/Vagrantfile +28 -0
  43. data/test/vagrant/fedora-18/provision.sh +10 -0
  44. metadata +187 -0
@@ -0,0 +1,133 @@
1
+ require "testenv"
2
+ require "pleaserun/platform/sysv"
3
+
4
+ describe PleaseRun::Platform::SYSV do
5
+ let(:start) { "/etc/init.d/#{subject.name} start" }
6
+ let(:stop) { "/etc/init.d/#{subject.name} stop" }
7
+ let(:status) { "/etc/init.d/#{subject.name} status" }
8
+ let(:restart) { "/etc/init.d/#{subject.name} restart" }
9
+
10
+ it "inherits correctly" do
11
+ insist { PleaseRun::Platform::SYSV.ancestors }.include?(PleaseRun::Platform::Base)
12
+ end
13
+
14
+ context "#files" do
15
+ subject do
16
+ runner = PleaseRun::Platform::SYSV.new("ubuntu-12.04")
17
+ runner.name = "fancypants"
18
+ next runner
19
+ end
20
+
21
+ let(:files) { subject.files.collect { |path, content| path } }
22
+
23
+ it "emits a file in /etc/init.d/" do
24
+ insist { files }.include?("/etc/init.d/fancypants")
25
+ end
26
+ it "emits a file in /etc/default/" do
27
+ insist { files }.include?("/etc/default/fancypants")
28
+ end
29
+ end
30
+
31
+ context "deployment" do
32
+ partytime = superuser? && File.directory?("/etc/init.d")
33
+ it "cannot be attempted", :if => !partytime do
34
+ pending("we are not the superuser") unless superuser?
35
+ pending("no /etc/init.d/ directory found") unless File.directory?("/etc/init.d")
36
+ end
37
+
38
+ context "as the super user", :if => partytime do
39
+ subject { PleaseRun::Platform::SYSV.new("ubuntu-12.04") }
40
+
41
+ before do
42
+ subject.name = "example"
43
+ subject.user = "root"
44
+ subject.program = "/bin/ping"
45
+ subject.args = [ "127.0.0.1" ]
46
+
47
+ activate(subject)
48
+ end
49
+
50
+ after do
51
+ system_quiet("/etc/init.d/#{subject.name} stop")
52
+ subject.files.each do |path, content|
53
+ File.unlink(path) if File.exist?(path)
54
+ end
55
+
56
+ # TODO(sissel): Remove the logs, too.
57
+ #log = "/var/log/example.log"
58
+ #File.unlink(log) if File.exist?(log)
59
+ end
60
+
61
+ it "should install" do
62
+ insist { File }.exist?("/etc/init.d/#{subject.name}")
63
+ end
64
+
65
+ it "should start" do
66
+ # Status should fail before starting
67
+ status_stopped
68
+ starts
69
+ end
70
+
71
+ it "should stop" do
72
+ status_stopped
73
+ starts
74
+ stops
75
+ end
76
+
77
+ context "with failing prestart" do
78
+ before do
79
+ subject.prestart = "false"
80
+ activate(subject)
81
+ end
82
+
83
+ it "should fail to start" do
84
+ insist { starts }.fails
85
+ end
86
+
87
+ context "with PRESTART=no" do
88
+ before { ENV["PRESTART"] = "no" }
89
+ after { ENV["PRESTART"] = nil }
90
+ it "should start" do
91
+ starts
92
+ end
93
+ end
94
+
95
+ it "should stop" do
96
+ # Force a start
97
+ ENV["PRESTART"] = "no"
98
+ starts
99
+ ENV["PRESTART"] = nil
100
+ stops
101
+ end
102
+
103
+ it "should fail to restart" do
104
+ ENV["PRESTART"] = "no"
105
+ starts
106
+ ENV["PRESTART"] = nil
107
+ insist { restarts }.fails
108
+ end
109
+ end
110
+
111
+ context "with a successful prestart" do
112
+ before do
113
+ subject.prestart = "echo hello world"
114
+ activate(subject)
115
+ end
116
+
117
+ it "should start" do
118
+ starts
119
+ end
120
+
121
+ it "should restart" do
122
+ # Force start
123
+ ENV["PRESTART"] = "no"
124
+ starts
125
+ ENV["PRESTART"] = nil
126
+
127
+ # Restart should succeed
128
+ restarts
129
+ end
130
+ end
131
+ end # as the super user
132
+ end # real tests
133
+ end
@@ -0,0 +1,117 @@
1
+ require "testenv"
2
+ require "pleaserun/platform/upstart"
3
+
4
+ describe PleaseRun::Platform::Upstart do
5
+ let(:start) { "initctl start #{subject.name}" }
6
+ let(:stop) { "initctl stop #{subject.name}" }
7
+ let(:status) { "initctl status #{subject.name} | egrep -v '#{subject.name} stop/'" }
8
+ let(:restart) { "initctl restart #{subject.name}" }
9
+
10
+ it "inherits correctly" do
11
+ insist { PleaseRun::Platform::Upstart.ancestors }.include?(PleaseRun::Platform::Base)
12
+ end
13
+
14
+ context "#files" do
15
+ subject do
16
+ runner = PleaseRun::Platform::Upstart.new("1.10")
17
+ runner.name = "fancypants"
18
+ next runner
19
+ end
20
+
21
+ let(:files) { subject.files.collect { |path, content| path } }
22
+
23
+ it "emits a file in /etc/init/" do
24
+ insist { files }.include?("/etc/init/fancypants.conf")
25
+ end
26
+
27
+ it "emits a file in /etc/init.d/" do
28
+ insist { files }.include?("/etc/init.d/fancypants")
29
+ end
30
+ end
31
+
32
+ context "#install_actions" do
33
+ subject do
34
+ runner = PleaseRun::Platform::Upstart.new("1.10")
35
+ runner.name = "fancypants"
36
+ next runner
37
+ end
38
+
39
+ it "has no install actions" do
40
+ insist { subject.install_actions }.empty?
41
+ end
42
+ end
43
+
44
+ context "deployment" do
45
+ partytime = (superuser? && platform?("linux") && program?("initctl") && File.directory?("/etc/init"))
46
+ it "cannot be attempted", :if => !partytime do
47
+ pending("we are not the superuser") unless superuser?
48
+ pending("platform is not linux") unless platform?("linux")
49
+ pending("no 'initctl' program found") unless program?("initctl")
50
+ pending("missing /etc/init/ directory") unless File.directory?("/etc/init")
51
+ end
52
+
53
+ context "as the super user", :if => partytime do
54
+ subject { PleaseRun::Platform::Upstart.new("1.10") }
55
+
56
+ before do
57
+ subject.name = "example"
58
+ subject.user = "root"
59
+ subject.program = "/bin/sh"
60
+ subject.args = [ "-c", "echo hello world; sleep 5" ]
61
+ activate(subject)
62
+ end
63
+
64
+ after do
65
+ system_quiet("initctl stop #{subject.name}")
66
+ subject.files.each do |path, content|
67
+ File.unlink(path) if File.exist?(path)
68
+ end
69
+
70
+ # Remove the logs, too.
71
+ log = "/var/log/upstart/example.log"
72
+ File.unlink(log) if File.exist?(log)
73
+ end
74
+
75
+ #it "should install" do
76
+ #system("initctl status #{subject.name}")
77
+ #status_stopped
78
+ #end
79
+
80
+ it "should start" do
81
+ starts
82
+
83
+ # Starting an already-started job will fail
84
+ insist { starts }.fails
85
+
86
+ status_running
87
+ end
88
+
89
+ it "should stop" do
90
+ starts
91
+ stops
92
+ end
93
+
94
+ context "with failing prestart" do
95
+ before do
96
+ subject.prestart = "false"
97
+ activate(subject)
98
+ end
99
+
100
+ it "should fail to start" do
101
+ insist { starts }.fails
102
+ end
103
+ end
104
+
105
+ context "with successful prestart" do
106
+ before do
107
+ subject.prestart = "true"
108
+ activate(subject)
109
+ end
110
+
111
+ it "should start" do
112
+ starts
113
+ end
114
+ end
115
+ end # as the super user
116
+ end # real tests
117
+ end
data/spec/testenv.rb ADDED
@@ -0,0 +1,69 @@
1
+ require "pleaserun/namespace"
2
+ require "insist"
3
+
4
+ def superuser?
5
+ return Process::UID.eid == 0
6
+ end
7
+
8
+ def platform?(name)
9
+ return RbConfig::CONFIG["host_os"] =~ /^#{name}/
10
+ end
11
+
12
+ def system_quiet(command)
13
+ system("#{command} > /dev/null 2>&1")
14
+ end
15
+
16
+ def sh(command)
17
+ system_quiet(command)
18
+ return $?
19
+ end
20
+
21
+ def program?(name)
22
+ ENV["PATH"].split(":").each do |path|
23
+ file = File.join(path, name)
24
+ return true if File.executable?(file)
25
+ end
26
+ return false
27
+ end
28
+
29
+ def activate(pleaserun)
30
+ pleaserun.files.each do |path, content, mode=nil|
31
+ File.write(path, content)
32
+ File.chmod(mode, path) if mode
33
+ end
34
+ pleaserun.install_actions.each do |command|
35
+ system(command)
36
+ raise "Command failed: #{command}" unless $?.success?
37
+ end
38
+ end
39
+
40
+ module Helpers
41
+ def starts
42
+ system_quiet(start); insist { $? }.success?
43
+ status_running
44
+ end
45
+
46
+ def stops
47
+ system_quiet(stop); insist { $? }.success?
48
+ status_stopped
49
+ end
50
+
51
+ def status_running
52
+ system_quiet(status); insist { $? }.success?
53
+ end
54
+
55
+ def status_stopped
56
+ system_quiet(status); reject { $? }.success?
57
+ end
58
+
59
+ def restarts
60
+ starts
61
+ system_quiet(restart)
62
+ insist { $? }.success?
63
+ status_running
64
+ end
65
+ end
66
+
67
+ RSpec.configure do |c|
68
+ c.include Helpers
69
+ end
@@ -0,0 +1,47 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Disabled</key><false/>
6
+ <key>Label</key><string>{{{ name }}}</string>
7
+ <key>Program</key><string>{{{ program }}}</string>
8
+ <key>ProgramArguments</key><array>
9
+ <string>{{{ program }}}</string>
10
+ {{{xml_args}}}
11
+ </array>
12
+ <key>KeepAlive</key> <true/>
13
+ <key>StandardOutPath</key> <string>/var/log/{{name}}.out</string>
14
+ <key>StandardErrorPath</key> <string>/var/log/{{name}}.err</string>
15
+ <!-- Things to implement eventually.
16
+ <key>UserName</key><string>{{{ user }}}</string>
17
+ <key>GroupName</key><string>{{{ group }}}</string>
18
+ <key>Umask</key><integer>{{{ umask }}}</integer>
19
+ <key>RootDirectory</key><string>{{{ chroot }}}</string>
20
+ <key>WorkingDirectory</key><string>{{{ chdir }}}</string>
21
+ <key>Nice</key><integer>0</integer>
22
+ <key>SoftResourceLimits</key><dict>
23
+ <key>Core</key><integer></integer>
24
+ <key>CPU</key><integer></integer>
25
+ <key>Data</key><integer></integer>
26
+ <key>FileSize</key><integer></integer>
27
+ <key>MemoryLock</key><integer></integer>
28
+ <key>NumberOfFiles</key><integer></integer>
29
+ <key>NumberOfProcesses</key><integer></integer>
30
+ <key>ResidentSetSize</key><integer></integer>
31
+ <key>Stack</key><integer></integer>
32
+ </dict>
33
+ <key>HardResourceLimits</key><dict>
34
+ <key>Core</key><integer></integer>
35
+ <key>CPU</key><integer></integer>
36
+ <key>Data</key><integer></integer>
37
+ <key>FileSize</key><integer></integer>
38
+ <key>MemoryLock</key><integer></integer>
39
+ <key>NumberOfFiles</key><integer></integer>
40
+ <key>NumberOfProcesses</key><integer></integer>
41
+ <key>ResidentSetSize</key><integer></integer>
42
+ <key>Stack</key><integer></integer>
43
+ </dict>
44
+ -->
45
+ </dict>
46
+ </plist>
47
+
@@ -0,0 +1,4 @@
1
+ #! /bin/sh
2
+
3
+ exec svlogd -tt /var/log/{{ name }}
4
+
@@ -0,0 +1,17 @@
1
+ #! /bin/sh
2
+
3
+ # Send stderr to stdout so svlogd catches all output.
4
+ exec 2>&1
5
+
6
+ PROGRAM={{#quoted}}{{{PROGRAM}}}{{/quoted}}
7
+ # Set up the arguments (for $@)
8
+ set -- {{{shell_args}}}
9
+
10
+ {{#shell_continuation}}
11
+ exec chpst
12
+ -u {{#quoted}}{{{user}}}:{{{group}}}{{/quoted}}
13
+ {{#chroot}} -/ {{#quoted}}{{{chroot}}}{{/quoted}} {{/chroot}}
14
+ {{#nice}} -n {{#quoted}}{{{nice}}}{{/quoted}} {{/nice}}
15
+ "$PROGRAM" "$@"
16
+ {{/shell_continuation}}
17
+
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ {{{ prestart }}}
@@ -0,0 +1,17 @@
1
+ [Unit]
2
+ {{#description}}
3
+ Description={{{ description }}}
4
+ {{/description}}
5
+
6
+ [Service]
7
+ Type=simple
8
+ User={{{ user }}}
9
+ Group={{{ group }}}
10
+ ExecStart={{{program}}} {{{shell_args}}}
11
+ {{#prestart}}
12
+ ExecStartPre=/lib/systemd/system/{{{name}}}-prestart.sh
13
+ {{/prestart}}
14
+ Restart=always
15
+
16
+ [Install]
17
+ WantedBy=multi-user.target
File without changes
@@ -0,0 +1,141 @@
1
+ #!/bin/sh
2
+ # Init script for {{{ name }}}
3
+ # Maintained by {{{ author }}}
4
+ # Generated by pleaserun.
5
+ # Implemented based on LSB Core 3.1:
6
+ # * Sections: 20.2, 20.3
7
+ #
8
+ ### BEGIN INIT INFO
9
+ # Provides: {{{ name }}}
10
+ # Required-Start: $remote_fs $syslog
11
+ # Required-Stop: $remote_fs $syslog
12
+ # Default-Start: 2 3 4 5
13
+ # Default-Stop: 0 1 6
14
+ # Short-Description: {{{ one_line_description }}}
15
+ # Description: {{{ description }}}
16
+ ### END INIT INFO
17
+
18
+ PATH=/sbin:/usr/sbin:/bin:/usr/bin
19
+ export PATH
20
+
21
+ name={{#escaped}}{{#safe_filename}}{{{ name }}}{{/safe_filename}}{{/escaped}}
22
+ program={{#escaped}}{{{ program }}}{{/escaped}}
23
+ args={{{ escaped_args }}}
24
+ pidfile="/var/run/$name.pid"
25
+
26
+ [ -r /etc/default/$name ] && . /etc/default/$name
27
+ [ -r /etc/sysconfig/$name ] && . /etc/sysconfig/$name
28
+
29
+ start() {
30
+ {{! I don't use 'su' here to run as a different user because the process 'su'
31
+ stays as the parent, causing our pidfile to contain the pid of 'su' not the
32
+ program we intended to run. Luckily, the 'chroot' program on OSX, FreeBSD, and Linux
33
+ all support switching users and it invokes execve immediately after chrooting.
34
+ }}
35
+
36
+ {{#prestart}}
37
+ if [ "$PRESTART" != "no" ] ; then
38
+ # If prestart fails, abort start.
39
+ prestart || return $?
40
+ fi
41
+ {{/prestart}}
42
+
43
+ # Run the program!
44
+ chroot --userspec {{{user}}}:{{{group}}} {{{chroot}}} sh -c "
45
+ {{#chdir}}cd {{{chdir}}}{{/chdir}}
46
+ {{#nice}}nice {{{nice}}}{{/nice}}
47
+ exec \"$program\" $args
48
+ " > /var/log/$name.log 2> /var/log/$name.err &
49
+
50
+ # Generate the pidfile from here. If we instead made the forked process
51
+ # generate it there will be a race condition between the pidfile writing
52
+ # and a process possibly asking for status.
53
+ echo $! > $pidfile
54
+
55
+ echo "$name started."
56
+ return 0
57
+ }
58
+
59
+ stop() {
60
+ # Try a few times to kill TERM the program
61
+ if status ; then
62
+ pid=`cat "$pidfile"`
63
+ echo "Killing $name (pid $pid) with SIGTERM"
64
+ kill -TERM $pid
65
+ # Wait for it to exit.
66
+ for i in 1 2 3 4 5 ; do
67
+ echo "Waiting $name (pid $pid) to die..."
68
+ status || break
69
+ sleep 1
70
+ done
71
+ if status ; then
72
+ echo "$name stop failed; still running."
73
+ else
74
+ echo "$name stopped."
75
+ fi
76
+ fi
77
+ }
78
+
79
+ status() {
80
+ if [ -f "$pidfile" ] ; then
81
+ pid=`cat "$pidfile"`
82
+ if kill -0 $pid > /dev/null 2> /dev/null ; then
83
+ # process by this pid is running.
84
+ # It may not be our pid, but that's what you get with just pidfiles.
85
+ # TODO(sissel): Check if this process seems to be the same as the one we
86
+ # expect. It'd be nice to use flock here, but flock uses fork, not exec,
87
+ # so it makes it quite awkward to use in this case.
88
+ return 0
89
+ else
90
+ return 2 # program is dead but pid file exists
91
+ fi
92
+ else
93
+ return 3 # program is not running
94
+ fi
95
+ }
96
+
97
+ force_stop() {
98
+ if status ; then
99
+ stop
100
+ status && kill -KILL `cat "$pidfile"`
101
+ fi
102
+ }
103
+
104
+ {{#prestart}}
105
+ prestart() {
106
+ {{{ prestart }}}
107
+
108
+ status=$?
109
+
110
+ if [ $status -gt 0 ] ; then
111
+ echo "Prestart command failed with code $status. If you wish to skip the prestart command, set PRESTART=no in your environment."
112
+ fi
113
+ return $status
114
+ }
115
+ {{/prestart}}
116
+
117
+ case "$1" in
118
+ start) status || start ;;
119
+ stop) stop ;;
120
+ force-stop) force_stop ;;
121
+ status)
122
+ status
123
+ code=$?
124
+ if [ $code -eq 0 ] ; then
125
+ echo "$name is running"
126
+ else
127
+ echo "$name is not running"
128
+ fi
129
+ exit $code
130
+ ;;
131
+ restart)
132
+ {{#prestart}}prestart || exit $?{{/prestart}}
133
+ stop && start
134
+ ;;
135
+ *)
136
+ echo "Usage: $SCRIPTNAME {start|stop|force-stop|status|restart}" >&2
137
+ exit 3
138
+ ;;
139
+ esac
140
+
141
+ exit $?