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,200 @@
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 'chef/resource'
18
+ require 'chef/provider'
19
+ require 'poise'
20
+
21
+
22
+ module PoiseService
23
+ module Resources
24
+ # (see PoiseServiceTest::Resource)
25
+ module PoiseServiceTest
26
+ # A `poise_service_test` resource for integration testing service providers.
27
+ # This is used in Test-Kitchen tests to ensure all providers behave
28
+ # similarly.
29
+ #
30
+ # @since 1.0.0
31
+ # @provides poise_service_test
32
+ # @action run
33
+ # @example
34
+ # poise_service_test 'upstart' do
35
+ # service_provider :upstart
36
+ # base_port 5000
37
+ # end
38
+ class Resource < Chef::Resource
39
+ include Poise
40
+ provides(:poise_service_test)
41
+ actions(:run)
42
+
43
+ # @!attribute service_provider
44
+ # Service provider to set for the test group.
45
+ # @return [Symbol]
46
+ attribute(:service_provider, kind_of: Symbol)
47
+ # @!attribute base_port
48
+ # Port number to start from for the test group.
49
+ # @return [Integer]
50
+ attribute(:base_port, kind_of: Integer)
51
+ end
52
+
53
+ # Provider for `poise_service_test`.
54
+ #
55
+ # @see Resource
56
+ # @provides poise_service_test
57
+ class Provider < Chef::Provider
58
+ include Poise
59
+ provides(:poise_service_test)
60
+
61
+ SERVICE_SCRIPT = <<-EOH
62
+ require 'webrick'
63
+ require 'json'
64
+ require 'etc'
65
+ FILE_DATA = ''
66
+ server = WEBrick::HTTPServer.new(Port: ARGV[0].to_i)
67
+ server.mount_proc '/' do |req, res|
68
+ res.body = {
69
+ directory: Dir.getwd,
70
+ user: Etc.getpwuid(Process.uid).name,
71
+ group: Etc.getgrgid(Process.gid).name,
72
+ environment: ENV.to_hash,
73
+ file_data: FILE_DATA,
74
+ pid: Process.pid,
75
+ }.to_json
76
+ end
77
+ EOH
78
+
79
+ # `run` action for `poise_service_test`. Create all test services.
80
+ #
81
+ # @return [void]
82
+ def action_run
83
+ notifying_block do
84
+ create_script
85
+ create_noterm_script
86
+ create_user
87
+ create_tests
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def create_script
94
+ file '/usr/bin/poise_test' do
95
+ owner 'root'
96
+ group 'root'
97
+ mode '755'
98
+ content <<-EOH
99
+ #!/opt/chef/embedded/bin/ruby
100
+ #{SERVICE_SCRIPT}
101
+ def load_file
102
+ FILE_DATA.replace(IO.read(ARGV[1]))
103
+ end
104
+ if ARGV[1]
105
+ load_file
106
+ trap('HUP') do
107
+ load_file
108
+ end
109
+ end
110
+ server.start
111
+ EOH
112
+ end
113
+ end
114
+
115
+ def create_noterm_script
116
+ file '/usr/bin/poise_test_noterm' do
117
+ owner 'root'
118
+ group 'root'
119
+ mode '755'
120
+ content <<-EOH
121
+ #!/opt/chef/embedded/bin/ruby
122
+ trap('HUP', 'IGNORE')
123
+ trap('STOP', 'IGNORE')
124
+ trap('TERM', 'IGNORE')
125
+ #{SERVICE_SCRIPT}
126
+ while true
127
+ begin
128
+ server.start
129
+ rescue Exception
130
+ rescue StandardError
131
+ end
132
+ end
133
+ EOH
134
+ end
135
+ end
136
+
137
+ def create_user
138
+ poise_service_user 'poise' do
139
+ home '/tmp'
140
+ end
141
+ end
142
+
143
+ def create_tests
144
+ poise_service "poise_test_#{new_resource.name}" do
145
+ provider new_resource.service_provider if new_resource.service_provider
146
+ command "/usr/bin/poise_test #{new_resource.base_port}"
147
+ end
148
+
149
+ poise_service "poise_test_#{new_resource.name}_params" do
150
+ provider new_resource.service_provider if new_resource.service_provider
151
+ command "/usr/bin/poise_test #{new_resource.base_port + 1}"
152
+ environment POISE_ENV: new_resource.name
153
+ user 'poise'
154
+ end
155
+
156
+ poise_service "poise_test_#{new_resource.name}_noterm" do
157
+ provider new_resource.service_provider if new_resource.service_provider
158
+ action [:enable, :disable]
159
+ command "/usr/bin/poise_test_noterm #{new_resource.base_port + 2}"
160
+ stop_signal 'kill'
161
+ end
162
+
163
+ {'restart' => 3, 'reload' => 4}.each do |action, port|
164
+ # Stop it before writing the file so we always start with first.
165
+ poise_service "poise_test_#{new_resource.name}_#{action} stop" do
166
+ provider new_resource.service_provider if new_resource.service_provider
167
+ action(:disable)
168
+ service_name "poise_test_#{new_resource.name}_#{action}"
169
+ end
170
+
171
+ # Write the content to the read on service launch.
172
+ file "/etc/poise_test_#{new_resource.name}_#{action}" do
173
+ content 'first'
174
+ end
175
+
176
+ # Launch the service, reading in first.
177
+ poise_service "poise_test_#{new_resource.name}_#{action}" do
178
+ provider new_resource.service_provider if new_resource.service_provider
179
+ command "/usr/bin/poise_test #{new_resource.base_port + port} /etc/poise_test_#{new_resource.name}_#{action}"
180
+ end
181
+
182
+ # Rewrite the file to second, restart/reload to trigger an update.
183
+ file "/etc/poise_test_#{new_resource.name}_#{action} again" do
184
+ path "/etc/poise_test_#{new_resource.name}_#{action}"
185
+ content 'second'
186
+ notifies action.to_sym, "poise_service[poise_test_#{new_resource.name}_#{action}]"
187
+ end
188
+ end
189
+
190
+ ruby_block "/tmp/poise_test_#{new_resource.name}_pid" do
191
+ block do
192
+ pid = resources("poise_service[poise_test_#{new_resource.name}]").pid
193
+ IO.write("/tmp/poise_test_#{new_resource.name}_pid", pid.to_s)
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,136 @@
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 'chef/resource'
18
+ require 'chef/provider'
19
+ require 'poise'
20
+
21
+
22
+ module PoiseService
23
+ module Resources
24
+ # (see PoiseServiceUser::Resource)
25
+ module PoiseServiceUser
26
+ # A `poise_service_user` resource to create service users/groups.
27
+ #
28
+ # @since 1.0.0
29
+ # @provides poise_service_user
30
+ # @action create
31
+ # @action remove
32
+ # @example
33
+ # poise_service_user 'myapp' do
34
+ # home '/var/tmp'
35
+ # group 'nogroup'
36
+ # end
37
+ class Resource < Chef::Resource
38
+ include Poise
39
+ provides(:poise_service_user)
40
+ actions(:create, :remove)
41
+
42
+ # @!attribute user
43
+ # Name of the user to create. Defaults to the name of the resource.
44
+ # @return [String]
45
+ attribute(:user, kind_of: String, name_attribute: true)
46
+ # @!attribute group
47
+ # Name of the group to create. Defaults to the name of the resource.
48
+ # Set to false to disable group creation.
49
+ # @return [String, false]
50
+ attribute(:group, kind_of: [String, FalseClass], name_attribute: true)
51
+ # @!attribute uid
52
+ # UID of the user to create. Optional, if not set the UID will be
53
+ # allocated automatically.
54
+ # @return [Integer]
55
+ attribute(:uid, kind_of: Integer)
56
+ # @!attribute gid
57
+ # GID of the group to create. Optional, if not set the GID will be
58
+ # allocated automatically.
59
+ # @return [Integer]
60
+ attribute(:gid, kind_of: Integer)
61
+ # @!attribute home
62
+ # Home directory of the user. This directory will not be created if it
63
+ # does not exist. Optional.
64
+ # @return [String]
65
+ attribute(:home, kind_of: String)
66
+ end
67
+
68
+ # Provider for `poise_service_user`.
69
+ #
70
+ # @see Resource
71
+ # @provides poise_service_user
72
+ class Provider < Chef::Provider
73
+ include Poise
74
+ provides(:poise_service_user)
75
+
76
+ # `create` action for `poise_service_user`. Ensure the user and group (if
77
+ # enabled) exist.
78
+ #
79
+ # @return [void]
80
+ def action_create
81
+ notifying_block do
82
+ create_group if new_resource.group
83
+ create_user
84
+ end
85
+ end
86
+
87
+ # `remove` action for `poise_service_user`. Ensure the user and group (if
88
+ # enabled) are destroyed.
89
+ #
90
+ # @return [void]
91
+ def action_remove
92
+ notifying_block do
93
+ remove_user
94
+ remove_group if new_resource.group
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ # Create the system group.
101
+ def create_group
102
+ group new_resource.group do
103
+ gid new_resource.gid
104
+ system true
105
+ end
106
+ end
107
+
108
+ # Create the system user.
109
+ def create_user
110
+ user new_resource.user do
111
+ comment "Service user for #{new_resource.name}"
112
+ gid new_resource.group if new_resource.group
113
+ home new_resource.home
114
+ shell '/bin/false'
115
+ system true
116
+ uid new_resource.uid
117
+ end
118
+ end
119
+
120
+ # Remove the system group.
121
+ def remove_group
122
+ create_group.tap do |r|
123
+ r.action(:remove)
124
+ end
125
+ end
126
+
127
+ # Remove the system user.
128
+ def remove_user
129
+ create_user.tap do |r|
130
+ r.action(:remove)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,192 @@
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 'poise'
18
+
19
+ require 'poise_service/resources/poise_service'
20
+
21
+
22
+ module PoiseService
23
+ # Mixin for application services. This is any resource that will be part of
24
+ # an application deployment and involves running a persistent service.
25
+ #
26
+ # @since 1.0.0
27
+ # @example
28
+ # module MyApp
29
+ # class Resource < Chef::Resource
30
+ # include Poise
31
+ # provides(:my_app)
32
+ # include PoiseService::ServiceMixin
33
+ # end
34
+ #
35
+ # class Provider < Chef::Provider
36
+ # include Poise
37
+ # provides(:my_app)
38
+ # include PoiseService::ServiceMixin
39
+ #
40
+ # def action_enable
41
+ # notifying_block do
42
+ # template '/etc/myapp.conf' do
43
+ # # ...
44
+ # end
45
+ # end
46
+ # super
47
+ # end
48
+ #
49
+ # def service_options(r)
50
+ # r.command('myapp --serve')
51
+ # end
52
+ # end
53
+ # end
54
+ module ServiceMixin
55
+ include Poise::Utils::ResourceProviderMixin
56
+
57
+ # Mixin for service wrapper resources.
58
+ #
59
+ # @see ServiceMixin
60
+ module Resource
61
+ include Poise::Resource
62
+
63
+ module ClassMethods
64
+ # @api private
65
+ def included(klass)
66
+ super
67
+ klass.extend(ClassMethods)
68
+ klass.class_exec do
69
+ actions(:enable, :disable, :start, :stop, :restart, :reload)
70
+ attribute(:service_name, kind_of: String, name_attribute: true)
71
+ end
72
+ end
73
+ end
74
+
75
+ extend ClassMethods
76
+ end
77
+
78
+ # Mixin for service wrapper providers.
79
+ #
80
+ # @see ServiceMixin
81
+ module Provider
82
+ include Poise::Provider
83
+
84
+ # Default enable action for service wrappers.
85
+ #
86
+ # @return [void]
87
+ def action_enable
88
+ notify_if_service do
89
+ service_resource.run_action(:enable)
90
+ end
91
+ end
92
+
93
+ # Default disable action for service wrappers.
94
+ #
95
+ # @return [void]
96
+ def action_disable
97
+ notify_if_service do
98
+ service_resource.run_action(:disable)
99
+ end
100
+ end
101
+
102
+ # Default start action for service wrappers.
103
+ #
104
+ # @return [void]
105
+ def action_start
106
+ notify_if_service do
107
+ service_resource.run_action(:start)
108
+ end
109
+ end
110
+
111
+ # Default stop action for service wrappers.
112
+ #
113
+ # @return [void]
114
+ def action_stop
115
+ notify_if_service do
116
+ service_resource.run_action(:stop)
117
+ end
118
+ end
119
+
120
+ # Default restart action for service wrappers.
121
+ #
122
+ # @return [void]
123
+ def action_restart
124
+ notify_if_service do
125
+ service_resource.run_action(:restart)
126
+ end
127
+ end
128
+
129
+ # Default reload action for service wrappers.
130
+ #
131
+ # @return [void]
132
+ def action_reload
133
+ notify_if_service do
134
+ service_resource.run_action(:reload)
135
+ end
136
+ end
137
+
138
+ # @todo Add reload once poise-service supports it.
139
+
140
+ private
141
+
142
+ # Set the current resource as notified if the provided block updates the
143
+ # service resource.
144
+ #
145
+ # @api public
146
+ # @param block [Proc] Block to run.
147
+ # @return [void]
148
+ # @example
149
+ # notify_if_service do
150
+ # service_resource.run_action(:enable)
151
+ # end
152
+ def notify_if_service(&block)
153
+ service_resource.updated_by_last_action(false)
154
+ block.call if block
155
+ new_resource.updated_by_last_action(true) if service_resource.updated_by_last_action?
156
+ end
157
+
158
+ # Service resource for this service wrapper. This returns a
159
+ # poise_service resource that will not be added to the resource
160
+ # collection. Override {#service_options} to set service resource
161
+ # parameters.
162
+ #
163
+ # @api public
164
+ # @return [Chef::Resource]
165
+ # @example
166
+ # service_resource.run_action(:restart)
167
+ def service_resource
168
+ @service_resource ||= PoiseService::Resources::PoiseService::Resource.new(new_resource.name, run_context).tap do |r|
169
+ # Set some defaults.
170
+ r.enclosing_provider = self
171
+ r.source_line = new_resource.source_line
172
+ r.service_name(new_resource.service_name)
173
+ # Call the subclass hook for more specific settings.
174
+ service_options(r)
175
+ end
176
+ end
177
+
178
+ # Abstract hook to set parameters on {#service_resource} when it is
179
+ # created. This is required to set at least `resource.command`.
180
+ #
181
+ # @api public
182
+ # @param resource [Chef::Resource] Resource instance to set parameters on.
183
+ # @return [void]
184
+ # @example
185
+ # def service_options(resource)
186
+ # resource.command('myapp --serve')
187
+ # end
188
+ def service_options(resource)
189
+ end
190
+ end
191
+ end
192
+ end