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,12 @@
1
+ require "pleaserun/platform/base"
2
+ require "pleaserun/namespace"
3
+
4
+ class PleaseRun::Platform::SYSV < PleaseRun::Platform::Base
5
+ def files
6
+ return Enumerator::Generator.new do |out|
7
+ out.yield [ safe_filename("/etc/init.d/{{ name }}"), render_template("init.d"), 0755 ]
8
+ out.yield [ safe_filename("/etc/default/{{ name }}"), render_template("default") ]
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,11 @@
1
+ require "pleaserun/platform/base"
2
+
3
+ class PleaseRun::Platform::Upstart < PleaseRun::Platform::Base
4
+ def files
5
+ return Enumerator::Generator.new do |out|
6
+ out.yield [ safe_filename("/etc/init/{{ name }}.conf"), render_template("init.conf") ]
7
+ out.yield [ safe_filename("/etc/init.d/{{ name }}"), render_template("init.d.sh"), 0755 ]
8
+ end
9
+ end
10
+ end
11
+
data/pleaserun.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |spec|
2
+ files = %x{git ls-files}.split("\n")
3
+
4
+ spec.name = "pleaserun"
5
+ spec.version = "0.0.1"
6
+ spec.summary = "pleaserun"
7
+ spec.description = "pleaserun"
8
+ spec.license = "Apache 2.0"
9
+
10
+ # Note: You should set the version explicitly.
11
+ spec.add_dependency "cabin", ">0" # for logging. apache 2 license
12
+ spec.add_dependency "clamp"
13
+ spec.add_dependency "cabin"
14
+ spec.add_dependency "stud"
15
+ spec.add_dependency "mustache"
16
+ spec.add_dependency "insist"
17
+
18
+ spec.files = files
19
+ spec.require_paths << "lib"
20
+ spec.bindir = "bin"
21
+ spec.executables = "pleaserun"
22
+
23
+ spec.authors = ["Jordan Sissel"]
24
+ spec.email = ["jls@semicomplete.com"]
25
+ #spec.homepage = "..."
26
+ end
27
+
@@ -0,0 +1,215 @@
1
+ require "testenv"
2
+ require "pleaserun/configurable"
3
+
4
+ describe PleaseRun::Configurable::Facet do
5
+ subject { PleaseRun::Configurable::Facet }
6
+
7
+ it "should have nil value by default" do
8
+ facet = subject.new(:name, "description")
9
+ insist { facet.value }.nil?
10
+ end
11
+
12
+ context "#name" do
13
+ subject { PleaseRun::Configurable::Facet.new(:hello, "world") }
14
+ it "returns the name" do
15
+ insist { subject.name } == :hello
16
+ end
17
+ end
18
+
19
+ context "#description" do
20
+ subject { PleaseRun::Configurable::Facet.new(:hello, "world") }
21
+ it "returns the description" do
22
+ insist { subject.description } == "world"
23
+ end
24
+ end
25
+
26
+ context "#value=" do
27
+ it "returns the given value" do
28
+ facet = subject.new(:name, "description")
29
+
30
+ # Check return value of assignment
31
+ insist { facet.value = 3 } == 3
32
+
33
+ # Check return value of read
34
+ insist { facet.value } == 3
35
+ end
36
+
37
+ it "invokes the validation block when value is set" do
38
+ count = 0
39
+ facet = subject.new(:name, "description") do
40
+ validate do |v|
41
+ count += 1
42
+ insist { v } == 3
43
+ end
44
+ end
45
+ insist { count } == 0
46
+ insist { facet.value }.nil?
47
+ facet.value = 3
48
+ insist { facet.value } == 3
49
+ insist { count } == 1
50
+ end
51
+
52
+ it "invokes the munger" do
53
+ count = 0
54
+ facet = subject.new(:name, "description") do
55
+ munge do |v|
56
+ count += 1
57
+ next v.to_s
58
+ end
59
+ end
60
+
61
+ insist { count } == 0
62
+ facet.value = 1000
63
+ insist { facet.value } == "1000"
64
+ facet.value = "hello"
65
+ insist { facet.value } == "hello"
66
+ facet.value = { "foo" => "bar" }
67
+ insist { facet.value } == '{"foo"=>"bar"}'
68
+ insist { count } == 3
69
+ end
70
+
71
+ it "invokes munger prior to validation" do
72
+ order = []
73
+ facet = subject.new(:name, "description") do
74
+ validate do |v|
75
+ order << :validate
76
+ insist { v.is_a?(Array) }
77
+ end
78
+ munge do |v|
79
+ order << :munge
80
+ if !v.is_a?(Array)
81
+ next [v]
82
+ else
83
+ v
84
+ end
85
+ end
86
+ end
87
+
88
+ facet.value = "hello"
89
+ insist { facet.value } == [ "hello" ]
90
+ insist { order } == [ :munge, :validate ]
91
+ end
92
+ end
93
+
94
+ context "#set?" do
95
+ it "returns false if value isn't set" do
96
+ facet = subject.new(:name, "description")
97
+ reject { facet }.set?
98
+ end
99
+ it "returns false if value isn't set and we have a default" do
100
+ facet = subject.new(:name, "description", :default => 100)
101
+ reject { facet }.set?
102
+ insist { facet.value } == 100
103
+ end
104
+ it "returns true if value is set" do
105
+ facet = subject.new(:name, "description")
106
+ reject { facet }.set?
107
+ facet.value = "hello"
108
+ insist { facet }.set?
109
+ end
110
+ end
111
+
112
+ context "with :default => ..." do
113
+ it "uses default when no value is set" do
114
+ facet = subject.new(:name, "description", :default => 4)
115
+ insist { facet.value } == 4
116
+ facet.value = 5
117
+ insist { facet.value } == 5
118
+ end
119
+
120
+ it "fails if default is invalid" do
121
+ insist do
122
+ facet = subject.new(:name, "description", :default => 4) do
123
+ validate do |v|
124
+ insist { v } != 4
125
+ end
126
+ end
127
+ end.raises(PleaseRun::Configurable::ValidationError)
128
+ end
129
+
130
+ it "succeeds if default is valid" do
131
+ facet = subject.new(:name, "description", :default => 4) do
132
+ validate do |v|
133
+ insist { v } == 4
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ context "attribute" do
140
+ subject do
141
+ next Class.new do
142
+ include PleaseRun::Configurable
143
+ attribute :whatever, "whatever"
144
+ end
145
+ end
146
+
147
+ it "creates accessor methods" do
148
+ foo = subject.new
149
+ insist { foo }.respond_to?(:whatever)
150
+ insist { foo }.respond_to?(:whatever=)
151
+ insist { foo }.respond_to?(:whatever?)
152
+ end
153
+
154
+ it "doesn't share values with other instances" do
155
+ foo1 = subject.new
156
+ foo2 = subject.new
157
+ foo1.whatever = "fancy"
158
+ insist { foo1.whatever } != foo2.whatever
159
+ end
160
+
161
+ context "#<attribute>?" do
162
+ it "returns false if the value isn't set" do
163
+ reject { subject.new }.whatever?
164
+ end
165
+ it "returns true if the value is set" do
166
+ foo = subject.new
167
+ reject { foo }.whatever?
168
+ foo.whatever = "pants"
169
+ insist { foo }.whatever?
170
+ end
171
+ end
172
+
173
+ context "via configurable_setup" do
174
+ subject do
175
+ next Class.new do
176
+ include PleaseRun::Configurable
177
+ attribute :whatever, "whatever", :default => "Hello"
178
+
179
+ def initialize(something)
180
+ @something = something
181
+ configurable_setup
182
+ end
183
+ end
184
+ end
185
+
186
+ it "creates accessor methods" do
187
+ foo = subject.new(1234)
188
+ insist { foo }.respond_to?(:whatever)
189
+ insist { foo }.respond_to?(:whatever=)
190
+ insist { foo.whatever } == "Hello"
191
+ end
192
+ end
193
+
194
+ context "validation" do
195
+ subject do
196
+ next Class.new do
197
+ include PleaseRun::Configurable
198
+ attribute :number, "something" do
199
+ validate do |v|
200
+ insist { v }.is_a?(Numeric)
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ it "fails if it raises an exception" do
207
+ insist { subject.new.number = "hello" }.raises(PleaseRun::Configurable::ValidationError)
208
+ end
209
+
210
+ it "succeeds if no exception is raised" do
211
+ subject.new.number = 33
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,46 @@
1
+ require "testenv"
2
+ require "pleaserun/mustache_methods"
3
+
4
+ class MustacheMethodTester
5
+ include PleaseRun::MustacheMethods
6
+
7
+ def whatever
8
+ return "hello world"
9
+ end
10
+
11
+ def args
12
+ [ "hello world", "fancy pants"]
13
+ end
14
+
15
+ def render(s)
16
+ return s
17
+ end
18
+ end
19
+
20
+ describe PleaseRun::MustacheMethods do
21
+ subject { MustacheMethodTester.new }
22
+
23
+ context "{{shell_args}}" do
24
+ it "should escape multiple arguments via quoting" do
25
+ insist { subject.shell_args } == "\"hello world\" \"fancy pants\""
26
+ end
27
+ end
28
+
29
+ context "{{shell_quote}}" do
30
+ it "quotes with spaces correctly" do
31
+ insist { subject.shell_quote("hello world") } == "\"hello world\""
32
+ end
33
+ it "escapes $" do
34
+ insist { subject.shell_quote("$") } == "\"\\$\""
35
+ end
36
+ end
37
+
38
+ context "{{shell_continuation}}" do
39
+ let(:input) { "hello\nworld\nfizzle" }
40
+
41
+ it "should render correctly" do
42
+ insist { subject.shell_continuation(input) } == "hello \\\nworld \\\nfizzle"
43
+ end
44
+ end
45
+ context "{{quoted}}"
46
+ end
@@ -0,0 +1,27 @@
1
+ require "testenv"
2
+ require "pleaserun/platform/base"
3
+
4
+ describe PleaseRun::Platform::Base do
5
+ context "default" do
6
+ subject { PleaseRun::Platform::Base.new("example") }
7
+ it "#name should be nil" do
8
+ insist { subject.name }.nil?
9
+ end
10
+
11
+ it "#args should be nil" do
12
+ insist { subject.args }.nil?
13
+ end
14
+
15
+ it "#program should be nil" do
16
+ insist { subject.program }.nil?
17
+ end
18
+
19
+ it "#user should be root" do
20
+ insist { subject.user } == "root"
21
+ end
22
+
23
+ it "#group should be root" do
24
+ insist { subject.group } == "root"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,93 @@
1
+ require "testenv"
2
+ require "pleaserun/platform/launchd"
3
+
4
+ describe PleaseRun::Platform::LaunchD do
5
+ it "inherits correctly" do
6
+ insist { PleaseRun::Platform::LaunchD.ancestors }.include?(PleaseRun::Platform::Base)
7
+ end
8
+
9
+ context "#files" do
10
+ subject do
11
+ runner = PleaseRun::Platform::LaunchD.new("10.9")
12
+ runner.name = "fancypants"
13
+ next runner
14
+ end
15
+
16
+ let(:files) { subject.files.collect { |path, content| path } }
17
+
18
+ it "emits a file in /Library/LaunchDaemons" do
19
+ insist { files }.include?("/Library/LaunchDaemons/fancypants.plist")
20
+ end
21
+ end
22
+
23
+ context "#install_actions" do
24
+ subject do
25
+ runner = PleaseRun::Platform::LaunchD.new("10.9")
26
+ runner.name = "fancypants"
27
+ next runner
28
+ end
29
+
30
+ it "runs 'launchctl load'" do
31
+ insist { subject.install_actions }.include?("launchctl load /Library/LaunchDaemons/fancypants.plist")
32
+ end
33
+ end
34
+
35
+ context "deployment" do
36
+ partytime = (superuser? && platform?("darwin"))
37
+ it "cannot be attempted", :if => !partytime do
38
+ pending("we are not the superuser") unless superuser?
39
+ pending("platform is not darwin") unless platform?("darwin")
40
+ end
41
+
42
+ context "as the super user", :if => partytime do
43
+ subject { PleaseRun::Platform::LaunchD.new("10.9") }
44
+
45
+ before do
46
+ subject.name = "example"
47
+ subject.user = "root"
48
+ subject.program = "/bin/sh"
49
+ subject.args = [ "-c", "echo hello world; sleep 5" ]
50
+
51
+ subject.files.each do |path, content|
52
+ File.write(path, content)
53
+ end
54
+ subject.install_actions.each do |command|
55
+ system(command)
56
+ raise "Command failed: #{command}" unless $?.success?
57
+ end
58
+ end
59
+
60
+ after do
61
+ system_quiet("launchctl stop #{subject.name}")
62
+ system_quiet("launchctl unload /Library/LaunchDaemons/#{subject.name}.plist")
63
+ subject.files.each do |path, content|
64
+ File.unlink(path) if File.exist?(path)
65
+ end
66
+
67
+ # Remove the logs, too.
68
+ [ "/var/log/#{subject.name}.out", "/var/log/#{subject.name}.err" ].each do |log|
69
+ File.unlink(log) if File.exist?(log)
70
+ end
71
+ end
72
+
73
+ it "should install" do
74
+ system_quiet("launchctl list #{subject.name}")
75
+ insist { $? }.success?
76
+ end
77
+
78
+ it "should start" do
79
+ system_quiet("launchctl start #{subject.name}")
80
+ insist { $? }.success?
81
+ system_quiet("launchctl list #{subject.name}")
82
+ insist { $? }.success?
83
+ end
84
+
85
+ it "should stop" do
86
+ system_quiet("launchctl start #{subject.name}")
87
+ insist { $? }.success?
88
+ system_quiet("launchctl stop #{subject.name}")
89
+ insist { $? }.success?
90
+ end
91
+ end
92
+ end # real tests
93
+ end
@@ -0,0 +1,119 @@
1
+ require "testenv"
2
+ require "pleaserun/platform/systemd"
3
+
4
+ describe PleaseRun::Platform::Systemd do
5
+ let(:start) { "systemctl start #{subject.name}.service" }
6
+ let(:stop) { "systemctl stop #{subject.name}.service" }
7
+ let(:status) { "systemctl show #{subject.name} | grep -q SubState=running" }
8
+ let(:restart) { "systemctl restart #{subject.name}.service" }
9
+
10
+ it "inherits correctly" do
11
+ insist { PleaseRun::Platform::Systemd.ancestors }.include?(PleaseRun::Platform::Base)
12
+ end
13
+
14
+ context "#files" do
15
+ subject do
16
+ runner = PleaseRun::Platform::Systemd.new("default")
17
+ runner.name = "fancypants"
18
+ runner.program = "/bin/true"
19
+ next runner
20
+ end
21
+
22
+ let(:files) { subject.files.collect { |path, content| path } }
23
+
24
+ it "emits a file in /lib/systemd/system" do
25
+ insist { files }.include?("/lib/systemd/system/fancypants.service")
26
+ end
27
+ end
28
+
29
+ context "#install_actions" do
30
+ subject do
31
+ runner = PleaseRun::Platform::Systemd.new("default")
32
+ runner.name = "fancypants"
33
+ next runner
34
+ end
35
+
36
+ it "invokes systemctl to reload" do
37
+ insist { subject.install_actions }.include?("systemctl --system daemon-reload")
38
+ end
39
+ end
40
+
41
+ context "deployment" do
42
+ partytime = (superuser? && platform?("linux") && program?("systemctl") && File.directory?("/lib/systemd"))
43
+ it "cannot be attempted", :if => !partytime do
44
+ pending("we are not the superuser") unless superuser?
45
+ pending("platform is not linux") unless platform?("linux")
46
+ pending("no 'systemctl' program found") unless program?("systemctl")
47
+ pending("missing /lib/systemd directory") unless File.directory?("/lib/systemd")
48
+ end
49
+
50
+ context "as the super user", :if => partytime do
51
+ subject { PleaseRun::Platform::Systemd.new("default") }
52
+
53
+ before do
54
+ subject.name = "hurray"
55
+ subject.user = "root"
56
+ subject.program = "/bin/sh"
57
+ subject.args = [ "-c", "echo hello world; sleep 5" ]
58
+ activate(subject)
59
+
60
+ # monkeypatch StartLimitInterval=0 into the .service file to avoid
61
+ # being throttled by systemd during these tests.
62
+ # Fixes https://github.com/jordansissel/pleaserun/issues/11
63
+ path = "/lib/systemd/system/#{subject.name}.service"
64
+ File.write(path, File.read(path).sub(/^\[Service\]$/, "[Service]\nStartLimitInterval=0"))
65
+ end
66
+
67
+ after do
68
+ system_quiet("systemctl stop #{subject.name}")
69
+ subject.files.each do |path, content|
70
+ File.unlink(path) if File.exist?(path)
71
+ end
72
+ end
73
+
74
+ #it "should install" do
75
+ #system("systemctl status #{subject.name}")
76
+ #status_stopped
77
+ #end
78
+
79
+ it "should start" do
80
+ starts
81
+ status_running
82
+ end
83
+
84
+ it "should stop" do
85
+ starts
86
+ stops
87
+ end
88
+
89
+ it "should start and stop" do
90
+ 5.times do
91
+ starts
92
+ stops
93
+ end
94
+ end
95
+
96
+ context "with failing prestart" do
97
+ before do
98
+ subject.prestart = "#!/bin/sh\nfalse\n"
99
+ activate(subject)
100
+ end
101
+
102
+ it "should fail to start" do
103
+ insist { starts }.fails
104
+ end
105
+ end
106
+
107
+ context "with successful prestart" do
108
+ before do
109
+ subject.prestart = "true"
110
+ activate(subject)
111
+ end
112
+
113
+ it "should start" do
114
+ starts
115
+ end
116
+ end
117
+ end # as the super user
118
+ end # real tests
119
+ end