poise-service 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.kitchen.travis.yml +4 -0
  4. data/.kitchen.yml +9 -0
  5. data/.travis.yml +23 -0
  6. data/.yardopts +6 -0
  7. data/Berksfile +27 -0
  8. data/Gemfile +33 -0
  9. data/LICENSE +201 -0
  10. data/README.md +381 -0
  11. data/Rakefile +17 -0
  12. data/chef/attributes/default.rb +19 -0
  13. data/chef/templates/systemd.service.erb +13 -0
  14. data/chef/templates/sysvinit.sh.erb +177 -0
  15. data/chef/templates/upstart.conf.erb +37 -0
  16. data/lib/poise_service.rb +25 -0
  17. data/lib/poise_service/cheftie.rb +18 -0
  18. data/lib/poise_service/error.rb +20 -0
  19. data/lib/poise_service/resources.rb +28 -0
  20. data/lib/poise_service/resources/poise_service.rb +161 -0
  21. data/lib/poise_service/resources/poise_service_test.rb +200 -0
  22. data/lib/poise_service/resources/poise_service_user.rb +136 -0
  23. data/lib/poise_service/service_mixin.rb +192 -0
  24. data/lib/poise_service/service_providers.rb +37 -0
  25. data/lib/poise_service/service_providers/base.rb +193 -0
  26. data/lib/poise_service/service_providers/dummy.rb +100 -0
  27. data/lib/poise_service/service_providers/systemd.rb +63 -0
  28. data/lib/poise_service/service_providers/sysvinit.rb +86 -0
  29. data/lib/poise_service/service_providers/upstart.rb +117 -0
  30. data/lib/poise_service/utils.rb +45 -0
  31. data/lib/poise_service/version.rb +19 -0
  32. data/poise-service.gemspec +41 -0
  33. data/test/cookbooks/poise-service_test/metadata.rb +18 -0
  34. data/test/cookbooks/poise-service_test/providers/mixin.rb +43 -0
  35. data/test/cookbooks/poise-service_test/recipes/default.rb +47 -0
  36. data/test/cookbooks/poise-service_test/recipes/mixin.rb +34 -0
  37. data/test/cookbooks/poise-service_test/resources/mixin.rb +26 -0
  38. data/test/gemfiles/chef-12.gemfile +19 -0
  39. data/test/gemfiles/master.gemfile +22 -0
  40. data/test/integration/default/serverspec/Gemfile +19 -0
  41. data/test/integration/default/serverspec/default_spec.rb +45 -0
  42. data/test/integration/default/serverspec/mixin_spec.rb +37 -0
  43. data/test/spec/resources/poise_service_spec.rb +235 -0
  44. data/test/spec/resources/poise_service_user_spec.rb +119 -0
  45. data/test/spec/service_mixin_spec.rb +164 -0
  46. data/test/spec/service_providers/base_spec.rb +231 -0
  47. data/test/spec/service_providers/dummy_spec.rb +91 -0
  48. data/test/spec/service_providers/systemd_spec.rb +98 -0
  49. data/test/spec/service_providers/sysvinit_spec.rb +96 -0
  50. data/test/spec/service_providers/upstart_spec.rb +300 -0
  51. data/test/spec/spec_helper.rb +50 -0
  52. data/test/spec/utils_spec.rb +44 -0
  53. metadata +172 -0
@@ -0,0 +1,164 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'spec_helper'
18
+
19
+ describe PoiseService::ServiceMixin do
20
+ context 'in a resource' do
21
+ resource(:poise_test) do
22
+ include PoiseService::ServiceMixin
23
+ end
24
+ subject { resource(:poise_test) }
25
+
26
+ it { is_expected.to include PoiseService::ServiceMixin }
27
+ it { is_expected.to include PoiseService::ServiceMixin::Resource }
28
+ end # /context in a resource
29
+
30
+ context 'in a provider' do
31
+ provider(:poise_test) do
32
+ include PoiseService::ServiceMixin
33
+ end
34
+ subject { provider(:poise_test) }
35
+
36
+ it { is_expected.to include PoiseService::ServiceMixin }
37
+ it { is_expected.to include PoiseService::ServiceMixin::Provider }
38
+ end # /context in a provider
39
+ end # /describe PoiseService::ServiceMixin
40
+
41
+ describe PoiseService::ServiceMixin::Resource do
42
+ subject { resource(:poise_test).new('test', nil) }
43
+
44
+ context 'with Poise already included' do
45
+ resource(:poise_test) do
46
+ include Poise
47
+ provides(:poise_test_other)
48
+ actions(:doit)
49
+ include PoiseService::ServiceMixin::Resource
50
+ end
51
+
52
+ its(:action) { is_expected.to eq :doit }
53
+ its(:allowed_actions) { is_expected.to eq %i{nothing doit enable disable start stop restart reload} }
54
+ its(:service_name) { is_expected.to eq 'test' }
55
+ end # /context with Poise already included
56
+
57
+ context 'without Poise already included' do
58
+ resource(:poise_test) do
59
+ include PoiseService::ServiceMixin::Resource
60
+ actions(:doit)
61
+ end
62
+
63
+ its(:action) { is_expected.to eq :enable }
64
+ its(:allowed_actions) { is_expected.to eq %i{nothing enable disable start stop restart reload doit} }
65
+ its(:service_name) { is_expected.to eq 'test' }
66
+ end # /context without Poise already included
67
+ end # /describe PoiseService::ServiceMixin::Resource
68
+
69
+ describe PoiseService::ServiceMixin::Provider do
70
+ let(:new_resource) { double('new_resource', name: 'test') }
71
+ let(:service_resource) { double('service_resource', updated_by_last_action: nil) }
72
+ provider(:poise_test) do
73
+ include PoiseService::ServiceMixin::Provider
74
+ end
75
+ subject { provider(:poise_test).new(new_resource, nil) }
76
+
77
+ describe 'actions' do
78
+ before do
79
+ allow(subject).to receive(:notify_if_service) {|&block| block.call }
80
+ allow(subject).to receive(:service_resource).and_return(service_resource)
81
+ end
82
+
83
+ describe '#action_enable' do
84
+ it do
85
+ expect(service_resource).to receive(:run_action).with(:enable)
86
+ subject.action_enable
87
+ end
88
+ end # /describe #action_enable
89
+
90
+ describe '#action_disable' do
91
+ it do
92
+ expect(service_resource).to receive(:run_action).with(:disable)
93
+ subject.action_disable
94
+ end
95
+ end # /describe #action_disable
96
+
97
+ describe '#action_start' do
98
+ it do
99
+ expect(service_resource).to receive(:run_action).with(:start)
100
+ subject.action_start
101
+ end
102
+ end # /describe #action_start
103
+
104
+ describe '#action_stop' do
105
+ it do
106
+ expect(service_resource).to receive(:run_action).with(:stop)
107
+ subject.action_stop
108
+ end
109
+ end # /describe #action_stop
110
+
111
+ describe '#action_restart' do
112
+ it do
113
+ expect(service_resource).to receive(:run_action).with(:restart)
114
+ subject.action_restart
115
+ end
116
+ end # /describe #action_restart
117
+
118
+ describe '#action_reload' do
119
+ it do
120
+ expect(service_resource).to receive(:run_action).with(:reload)
121
+ subject.action_reload
122
+ end
123
+ end # /describe #action_reload
124
+ end # /describe actions
125
+
126
+ describe '#notify_if_service' do
127
+ before do
128
+ allow(subject).to receive(:service_resource).and_return(service_resource)
129
+ end
130
+
131
+ context 'with an update' do
132
+ it do
133
+ expect(service_resource).to receive(:updated_by_last_action?).and_return(true)
134
+ expect(new_resource).to receive(:updated_by_last_action).with(true)
135
+ subject.send(:notify_if_service)
136
+ end
137
+ end # /context with an update
138
+
139
+ context 'with no update' do
140
+ it do
141
+ expect(service_resource).to receive(:updated_by_last_action?).and_return(false)
142
+ subject.send(:notify_if_service)
143
+ end
144
+ end # /context with no update
145
+ end # /describe #notify_if_service
146
+
147
+ describe '#service_resource' do
148
+ it do
149
+ allow(new_resource).to receive(:source_line).and_return('path.rb:1')
150
+ allow(new_resource).to receive(:service_name).and_return('test')
151
+ fake_poise_service = double('poise_service')
152
+ expect(PoiseService::Resources::PoiseService::Resource).to receive(:new).with('test', nil).and_return(fake_poise_service)
153
+ expect(fake_poise_service).to receive(:enclosing_provider=).with(subject)
154
+ expect(fake_poise_service).to receive(:source_line=).with('path.rb:1')
155
+ expect(fake_poise_service).to receive(:service_name).with('test')
156
+ expect(subject).to receive(:service_options).with(fake_poise_service)
157
+ subject.send(:service_resource)
158
+ end
159
+ end # /describe #service_resource
160
+
161
+ describe '#service_options' do
162
+ it { expect(subject.send(:service_options, nil)).to be_nil }
163
+ end # /describe #service_options
164
+ end # /describe PoiseService::ServiceMixin::Provider
@@ -0,0 +1,231 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'spec_helper'
18
+
19
+ describe PoiseService::ServiceProviders::Base do
20
+ let(:new_resource) { double('new_resource') }
21
+ let(:run_context) { double('run_context') }
22
+ let(:service_resource) do
23
+ double('service_resource').tap do |r|
24
+ allow(r).to receive(:updated_by_last_action).with(false)
25
+ allow(r).to receive(:updated_by_last_action?).and_return(false)
26
+ end
27
+ end
28
+ let(:options) { Hash.new }
29
+ subject(:provider) do
30
+ described_class.new(new_resource, run_context).tap do |provider|
31
+ allow(provider).to receive(:notifying_block) {|&block| block.call }
32
+ allow(provider).to receive(:service_resource).and_return(service_resource)
33
+ allow(provider).to receive(:options).and_return(options)
34
+ end
35
+ end
36
+
37
+ describe '#action_enable' do
38
+ it do
39
+ expect(subject).to receive(:create_service).ordered
40
+ expect(service_resource).to receive(:run_action).with(:enable).ordered
41
+ expect(service_resource).to receive(:run_action).with(:start).ordered
42
+ subject.action_enable
43
+ end
44
+ end # /describe #action_enable
45
+
46
+ describe '#action_disable' do
47
+ it do
48
+ expect(service_resource).to receive(:run_action).with(:stop).ordered
49
+ expect(service_resource).to receive(:run_action).with(:disable).ordered
50
+ expect(subject).to receive(:destroy_service).ordered
51
+ subject.action_disable
52
+ end
53
+ end # /describe #action_disable
54
+
55
+ describe '#action_start' do
56
+ it do
57
+ expect(service_resource).to receive(:run_action).with(:start).ordered
58
+ subject.action_start
59
+ end
60
+ end # /describe #action_start
61
+
62
+ describe '#action_stop' do
63
+ it do
64
+ expect(service_resource).to receive(:run_action).with(:stop).ordered
65
+ subject.action_stop
66
+ end
67
+ end # /describe #action_stop
68
+
69
+ describe '#action_restart' do
70
+ it do
71
+ expect(service_resource).to receive(:run_action).with(:restart).ordered
72
+ subject.action_restart
73
+ end
74
+
75
+ context 'with never_restart' do
76
+ before { options['never_restart'] = true }
77
+ it do
78
+ expect(service_resource).to_not receive(:run_action).with(:restart).ordered
79
+ subject.action_restart
80
+ end
81
+ end # /context with never_restart
82
+ end # /describe #action_restart
83
+
84
+ describe '#action_reload' do
85
+ it do
86
+ expect(service_resource).to receive(:run_action).with(:reload).ordered
87
+ subject.action_reload
88
+ end
89
+
90
+ context 'with never_reload' do
91
+ before { options['never_reload'] = true }
92
+ it do
93
+ expect(service_resource).to_not receive(:run_action).with(:reload).ordered
94
+ subject.action_reload
95
+ end
96
+ end # /context with never_reload
97
+ end # /describe #action_reload
98
+
99
+ describe '#pid' do
100
+ it do
101
+ expect { subject.send(:pid) }.to raise_error(NotImplementedError)
102
+ end
103
+ end # /describe #pid
104
+
105
+ describe '#create_service' do
106
+ it do
107
+ expect { subject.send(:create_service) }.to raise_error(NotImplementedError)
108
+ end
109
+ end # /describe #create_service
110
+
111
+ describe '#destroy_service' do
112
+ it do
113
+ expect { subject.send(:destroy_service) }.to raise_error(NotImplementedError)
114
+ end
115
+ end # /describe #destroy_service
116
+
117
+ describe '#service_template' do
118
+ let(:new_resource) do
119
+ double('new_resource',
120
+ command: 'myapp --serve',
121
+ cookbook_name: :test_cookbook,
122
+ directory: '/cwd',
123
+ environment: Hash.new,
124
+ reload_signal: 'HUP',
125
+ restart_on_update: true,
126
+ service_name: 'myapp',
127
+ stop_signal: 'TERM',
128
+ user: 'root',
129
+ )
130
+ end
131
+ let(:run_context) { chef_run.run_context }
132
+ let(:options) { Hash.new }
133
+ let(:block) { Proc.new { } }
134
+ before do
135
+ allow(provider).to receive(:options).and_return(options)
136
+ end
137
+ subject do
138
+ provider.send(:service_template, '/test', 'source.erb', &block)
139
+ end
140
+
141
+ context 'with no block' do
142
+ its(:owner) { is_expected.to eq 'root' }
143
+ its(:source) { is_expected.to eq 'source.erb'}
144
+ its(:cookbook) { is_expected.to eq 'poise-service'}
145
+ end # /context with no block
146
+
147
+ context 'with a block' do
148
+ let(:block) do
149
+ Proc.new do
150
+ owner('nobody')
151
+ variables.update(mykey: 'myvalue')
152
+ end
153
+ end
154
+ its(:owner) { is_expected.to eq 'nobody' }
155
+ its(:variables) { are_expected.to include({mykey: 'myvalue'}) }
156
+ its(:source) { is_expected.to eq 'source.erb'}
157
+ its(:cookbook) { is_expected.to eq 'poise-service'}
158
+ end # /context with a block
159
+
160
+ context 'with a template override' do
161
+ let(:options) { {'template' => 'override.erb'} }
162
+ its(:source) { is_expected.to eq 'override.erb'}
163
+ its(:cookbook) { is_expected.to eq 'test_cookbook'}
164
+ end # /context with a template override
165
+
166
+ context 'with a template and cookbook override' do
167
+ let(:options) { {'template' => 'other:override.erb'} }
168
+ its(:source) { is_expected.to eq 'override.erb'}
169
+ its(:cookbook) { is_expected.to eq 'other'}
170
+ end # /context with a template and cookbook override
171
+ end # /describe #service_template
172
+
173
+ describe '#options' do
174
+ service_provider('dummy')
175
+ subject { chef_run.poise_service('test').provider_for_action(:enable).options }
176
+
177
+ context 'with an options resource' do
178
+ recipe(subject: false) do
179
+ poise_service 'test' do
180
+ service_name 'other'
181
+ end
182
+
183
+ poise_service_options 'test' do
184
+ command 'myapp'
185
+ end
186
+ end
187
+
188
+ it { is_expected.to eq({'command' => 'myapp', 'provider' => 'dummy'}) }
189
+ end # /context with an options resource
190
+
191
+ context 'with an options resource using service_name' do
192
+ recipe(subject: false) do
193
+ poise_service 'test' do
194
+ service_name 'other'
195
+ end
196
+
197
+ poise_service_options 'other' do
198
+ command 'myapp'
199
+ end
200
+ end
201
+
202
+ it { is_expected.to eq({'command' => 'myapp', 'provider' => 'dummy'}) }
203
+ end # /context with an options resource using service_name
204
+
205
+ context 'with node attributes' do
206
+ before do
207
+ override_attributes['poise-service']['test'] = {command: 'myapp'}
208
+ end
209
+ recipe(subject: false) do
210
+ poise_service 'test' do
211
+ service_name 'other'
212
+ end
213
+ end
214
+
215
+ it { is_expected.to eq({'command' => 'myapp', 'provider' => 'dummy'}) }
216
+ end # /context with node attributes
217
+
218
+ context 'with node attributes using service_name' do
219
+ before do
220
+ override_attributes['poise-service']['other'] = {command: 'myapp'}
221
+ end
222
+ recipe(subject: false) do
223
+ poise_service 'test' do
224
+ service_name 'other'
225
+ end
226
+ end
227
+
228
+ it { is_expected.to eq({'command' => 'myapp', 'provider' => 'dummy'}) }
229
+ end # /context with node attributes using service_name
230
+ end # /describe #options
231
+ end
@@ -0,0 +1,91 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'spec_helper'
18
+
19
+ describe PoiseService::ServiceProviders::Dummy do
20
+ service_provider('dummy')
21
+ step_into(:poise_service)
22
+ before do
23
+ allow(Process).to receive(:fork).and_return(0)
24
+ allow(Process).to receive(:kill).with(0, 100)
25
+ allow(File).to receive(:exists?).and_call_original
26
+ allow(File).to receive(:exists?).with('/var/run/test.pid').and_return(true)
27
+ allow(IO).to receive(:read).and_call_original
28
+ allow(IO).to receive(:read).with('/var/run/test.pid').and_return('100')
29
+ end
30
+
31
+ describe '#action_enable' do
32
+ before do
33
+ allow(File).to receive(:exists?).with('/var/run/test.pid').and_return(false, false, true)
34
+ expect_any_instance_of(described_class).to receive(:sleep).with(1).once
35
+ end
36
+ recipe do
37
+ poise_service 'test' do
38
+ command 'myapp --serve'
39
+ end
40
+ end
41
+
42
+ it { run_chef }
43
+ end # /describe #action_enable
44
+
45
+ describe '#action_disable' do
46
+ before do
47
+ expect(Process).to receive(:kill).with('TERM', 100)
48
+ allow(File).to receive(:unlink).and_call_original
49
+ allow(File).to receive(:unlink).with('/var/run/test.pid')
50
+ end
51
+ recipe do
52
+ poise_service 'test' do
53
+ action :disable
54
+ end
55
+ end
56
+
57
+ it { run_chef }
58
+ end # /describe #action_disable
59
+
60
+ describe '#action_restart' do
61
+ before do
62
+ expect_any_instance_of(described_class).to receive(:action_start)
63
+ expect_any_instance_of(described_class).to receive(:action_stop)
64
+ end
65
+ recipe do
66
+ poise_service 'test' do
67
+ action :restart
68
+ end
69
+ end
70
+
71
+ it { run_chef }
72
+ end # /describe #action_restart
73
+
74
+ describe '#action_reload' do
75
+ before do
76
+ expect(Process).to receive(:kill).with('HUP', 100)
77
+ end
78
+ recipe do
79
+ poise_service 'test' do
80
+ action :reload
81
+ end
82
+ end
83
+
84
+ it { run_chef }
85
+ end # /describe #action_reload
86
+
87
+ describe '#service_resource' do
88
+ subject { described_class.new(nil, nil).send(:service_resource) }
89
+ it { expect { subject }.to raise_error NotImplementedError }
90
+ end # /describe #service_resource
91
+ end