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