poise-service 1.0.0

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 (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