Vendor the external cookbooks
Knife-Zero doesn't include Berkshelf support, so vendoring everything in the repo is convenient again
This commit is contained in:
92
cookbooks/poise-service/CHANGELOG.md
Normal file
92
cookbooks/poise-service/CHANGELOG.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Poise-Service Changelog
|
||||
|
||||
## v1.5.2
|
||||
|
||||
* Set `declared_type` on the mixin-created `poise_service` resource so it works
|
||||
correctly with ChefSpec.
|
||||
|
||||
## v1.5.1
|
||||
|
||||
* Fix the `sysvinit` provider on Amazon Linux under Chef 13.
|
||||
|
||||
## v1.5.0
|
||||
|
||||
* Added `never_start` and `never_stop` provider options to prevent Chef from starting
|
||||
or stopping a service.
|
||||
* Automatically reload systemd when removing a service if auto_reload is enabled.
|
||||
* Improved dummy provider, records process output to `/var/run/service_name.out`
|
||||
and a `restart_delay` provider option to the dummy provider to wait between
|
||||
stopping and starting.
|
||||
|
||||
## v1.4.2
|
||||
|
||||
* Fix the `noterm` test service to work on Ruby 2.3.
|
||||
|
||||
## v1.4.1
|
||||
|
||||
* Fix `poise_service_user` on Solaris and make it closer to being usable on Windows.
|
||||
|
||||
## v1.4.0
|
||||
|
||||
* [#31](https://github.com/poise/poise-service/pull/31) Add `shell` property to
|
||||
`poise_service_user` resource.
|
||||
|
||||
## v1.3.1
|
||||
|
||||
* [#25](https://github.com/poise/poise-service/pull/25) Cope with a service user
|
||||
with an invalid home directory.
|
||||
* Use the correct default cookbook for `service_template` when used with additional plugins.
|
||||
|
||||
## v1.3.0
|
||||
|
||||
* Allow setting `pid_file_external false` as a provider option for the `sysvinit`
|
||||
provider to have non-standard path but keep the internal handling.
|
||||
* Improved quoting for environment variables in the `inittab` provider.
|
||||
|
||||
## v1.2.1
|
||||
|
||||
* [#23](https://github.com/poise/poise-service/pull/23) Fix service templates on AIX and FreeBSD to use the correct root group.
|
||||
|
||||
## v1.2.0
|
||||
|
||||
* The `Restart` mode for systemd services can now be controlled via provider
|
||||
option and defaults to `on-failure` to match other providers.
|
||||
|
||||
## v1.1.2
|
||||
|
||||
* [#22](https://github.com/poise/poise-service/pull/22) Set all script commands
|
||||
for the `sysvinit` provider. This should fix compatibility with EL5.
|
||||
|
||||
## v1.1.1
|
||||
|
||||
* Fix an incorrect value in `poise_service_test`. This is not relevant to
|
||||
end-users of `poise-service`.
|
||||
|
||||
## v1.1.0
|
||||
|
||||
* Added `inittab` provider to manage services using old-fashioned `/etc/inittab`.
|
||||
|
||||
## v1.0.4
|
||||
|
||||
* Set GID correctly in all service providers.
|
||||
* Allow overriding the path to the generated sysvinit script.
|
||||
|
||||
## v1.0.3
|
||||
|
||||
* [#10](https://github.com/poise/poise-service/pull/10) Fixes for ensuring services are restarted when their command or user changes.
|
||||
* [#11](https://github.com/poise/poise-service/pull/11) Revamp the `sysvinit` provider for non-Debian platforms to be more stable.
|
||||
* [#12](https://github.com/poise/poise-service/pull/12) Improve the `dummy` provider to handle dropping privs correctly.
|
||||
|
||||
## v1.0.2
|
||||
|
||||
* Fix a potential infinite loop when starting a service with the dummy provider.
|
||||
* [#2](https://github.com/poise/poise-service/pull/2) Remove usage of root
|
||||
default files so uploading with Berkshelf works (for now).
|
||||
|
||||
## v1.0.1
|
||||
|
||||
* Don't use a shared, mutable default value for `#environment`.
|
||||
|
||||
## v1.0.0
|
||||
|
||||
* Initial release!
|
||||
448
cookbooks/poise-service/README.md
Normal file
448
cookbooks/poise-service/README.md
Normal file
@@ -0,0 +1,448 @@
|
||||
# Poise-Service Cookbook
|
||||
|
||||
[](https://travis-ci.org/poise/poise-service)
|
||||
[](https://rubygems.org/gems/poise-service)
|
||||
[](https://supermarket.chef.io/cookbooks/poise-service)
|
||||
[](https://codecov.io/github/poise/poise-service)
|
||||
[](https://gemnasium.com/poise/poise-service)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
A [Chef](https://www.chef.io/) cookbook to provide a unified interface for
|
||||
services.
|
||||
|
||||
### What is poise-service?
|
||||
|
||||
Poise-service is a tool for developers of "library cookbooks" to define a
|
||||
service without forcing the end-user of the library to adhere to their choice of
|
||||
service management framework. The `poise_service` resource represents an
|
||||
abstract service to be run, which can then be customized by node attributes and
|
||||
the `poise_service_options` resource. This is a technique called [dependency
|
||||
injection](https://en.wikipedia.org/wiki/Dependency_injection), and allows a
|
||||
measure of decoupling between the library and application cookbooks.
|
||||
|
||||
### Why would I use poise-service?
|
||||
|
||||
Poise-service is most useful for authors of library-style cookbooks, for example
|
||||
the `apache2`, `mysql`, or `application` cookbooks. When using other service
|
||||
management options with Chef, the author of the library cookbook has to add
|
||||
specific code for each service management framework they want to support, often
|
||||
resulting in a cookbook only supporting the favorite framework of the author or
|
||||
depending on distribution packages for their init scripts. The `poise_service`
|
||||
resource allows library cookbook authors a way to write generic code for all
|
||||
service management frameworks while still allowing users of that cookbook to
|
||||
choose which service management framework best fits their needs.
|
||||
|
||||
### How is this different from the built-in service resource?
|
||||
|
||||
Chef includes a `service` resource which allows interacting with certain
|
||||
service management frameworks such as SysV, Upstart, and systemd.
|
||||
`poise-service` goes further in that it actually generates the configuration
|
||||
files needed for the requested service management framework, as well as offering
|
||||
a dependency injection system for application cookbooks to customize which
|
||||
framework is used.
|
||||
|
||||
### What service management frameworks are supported?
|
||||
|
||||
* [SysV (aka /etc/init.d)](#sysvinit)
|
||||
* [Upstart](#upstart)
|
||||
* [systemd](#systemd)
|
||||
* [Inittab](#inittab)
|
||||
* [Runit](https://github.com/poise/poise-service-runit)
|
||||
* [Monit](https://github.com/poise/poise-monit#service-provider)
|
||||
* [Solaris](https://github.com/sh9189/poise-service-solaris)
|
||||
* [AIX](https://github.com/johnbellone/poise-service-aix)
|
||||
* *Supervisor (coming soon!)*
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
To create a service user and a service to run Apache2:
|
||||
|
||||
```ruby
|
||||
poise_service_user 'www-data'
|
||||
|
||||
poise_service 'apache2' do
|
||||
command '/usr/sbin/apache2 -f /etc/apache2/apache2.conf -DFOREGROUND'
|
||||
stop_signal 'WINCH'
|
||||
reload_signal 'USR1'
|
||||
end
|
||||
```
|
||||
|
||||
or for a hypothetical Rails web application:
|
||||
|
||||
```ruby
|
||||
poise_service_user 'myapp'
|
||||
|
||||
poise_service 'myapp-web' do
|
||||
command 'bundle exec unicorn -p 8080'
|
||||
user 'myapp'
|
||||
directory '/srv/myapp'
|
||||
environment RAILS_ENV: 'production'
|
||||
end
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
### `poise_service`
|
||||
|
||||
The `poise_service` resource is the abstract definition of a service.
|
||||
|
||||
```ruby
|
||||
poise_service 'myapp' do
|
||||
command 'myapp --serve'
|
||||
environment RAILS_ENV: 'production'
|
||||
end
|
||||
```
|
||||
|
||||
#### Actions
|
||||
|
||||
* `:enable` – Create, enable and start the service. *(default)*
|
||||
* `:disable` – Stop, disable, and destroy the service.
|
||||
* `:start` – Start the service.
|
||||
* `:stop` – Stop the service.
|
||||
* `:restart` – Stop and then start the service.
|
||||
* `:reload` – Send the configured reload signal to the service.
|
||||
|
||||
#### Attributes
|
||||
|
||||
* `service_name` – Name of the service. *(name attribute)*
|
||||
* `command` – Command to run for the service. This command must stay in the
|
||||
foreground and not daemonize itself. *(required)*
|
||||
* `user` – User to run the service as. See
|
||||
[`poise_service_user`](#poise_service_user) for any easy way to create service
|
||||
users. *(default: root)*
|
||||
* `directory` – Working directory for the service. *(default: home directory for
|
||||
user, or / if not found)*
|
||||
* `environment` – Environment variables for the service.
|
||||
* `stop_signal` – Signal to use to stop the service. Some systems will fall back
|
||||
to SIGKILL if this signal fails to stop the process. *(default: TERM)*
|
||||
* `reload_signal` – Signal to use to reload the service. *(default: HUP)*
|
||||
* `restart_on_update` – If true, the service will be restarted if the service
|
||||
definition or configuration changes. If `'immediately'`, the notification will
|
||||
happen in immediate mode. *(default: true)*
|
||||
|
||||
#### Service Options
|
||||
|
||||
The `poise-service` library offers an additional way to pass configuration
|
||||
information to the final service called "options". Options are key/value pairs
|
||||
that are passed down to the service provider and can be used to control how it
|
||||
creates and manages the service. These can be set in the `poise_service`
|
||||
resource using the `options` method, in node attributes or via the
|
||||
`poise_service_options` resource. The options from all sources are merged
|
||||
together in to a single hash.
|
||||
|
||||
When setting options in the resource you can either set them for all providers:
|
||||
|
||||
```ruby
|
||||
poise_service 'myapp' do
|
||||
command 'myapp --serve'
|
||||
options status_port: 8000
|
||||
end
|
||||
```
|
||||
|
||||
or for a single provider:
|
||||
|
||||
```ruby
|
||||
poise_service 'myapp' do
|
||||
command 'myapp --serve'
|
||||
options :systemd, after_target: 'network'
|
||||
end
|
||||
```
|
||||
|
||||
Setting via node attributes is generally how an end-user or application cookbook
|
||||
will set options to customize services in the library cookbooks they are using.
|
||||
You can set options for all services or for a single service, by service name
|
||||
or by resource name:
|
||||
|
||||
```ruby
|
||||
# Global, for all services.
|
||||
override['poise-service']['options']['after_target'] = 'network'
|
||||
# Single service.
|
||||
override['poise-service']['myapp']['template'] = 'myapp.erb'
|
||||
```
|
||||
|
||||
The `poise_service_options` resource is also available to set node attributes
|
||||
for a specific service in a DSL-friendly way:
|
||||
|
||||
```ruby
|
||||
poise_service_options 'myapp' do
|
||||
template 'myapp.erb'
|
||||
restart_on_update false
|
||||
end
|
||||
```
|
||||
|
||||
Unlike resource attributes, service options can be different for each provider.
|
||||
Not all providers support the same options so make sure to check the
|
||||
documentation for each provider to see what options are available.
|
||||
|
||||
### `poise_service_options`
|
||||
|
||||
The `poise_service_options` resource allows setting per-service options in a
|
||||
DSL-friendly way. See [the Service Options](#service-options) section for more
|
||||
information about service options overall.
|
||||
|
||||
```ruby
|
||||
poise_service_options 'myapp' do
|
||||
template 'myapp.erb'
|
||||
restart_on_update false
|
||||
end
|
||||
```
|
||||
|
||||
#### Actions
|
||||
|
||||
* `:run` – Apply the service options. *(default)*
|
||||
|
||||
#### Attributes
|
||||
|
||||
* `resource` – Name of the service. *(name attribute)*
|
||||
* `for_provider` – Provider to set options for.
|
||||
|
||||
All other attribute keys will be used as options data.
|
||||
|
||||
### `poise_service_user`
|
||||
|
||||
The `poise_service_user` resource is an easy way to create service users. It is
|
||||
not required to use `poise_service`, it is only a helper.
|
||||
|
||||
```ruby
|
||||
poise_service_user 'myapp' do
|
||||
home '/srv/myapp'
|
||||
end
|
||||
```
|
||||
|
||||
#### Actions
|
||||
|
||||
* `:create` – Create the user and group. *(default)*
|
||||
* `:remove` – Remove the user and group.
|
||||
|
||||
#### Attributes
|
||||
|
||||
* `user` – Name of the user. *(name attribute)*
|
||||
* `group` – Name of the group. Set to `false` to disable group creation. *(name attribute)*
|
||||
* `uid` – UID of the user. *(default: automatic)*
|
||||
* `gid` – GID of the group. *(default: automatic)*
|
||||
* `home` – Home directory of the user.
|
||||
* `shell` – Shell of the user. *(default: /bin/nologin if present or /bin/false)*
|
||||
|
||||
## Providers
|
||||
|
||||
### `sysvinit`
|
||||
|
||||
The `sysvinit` provider supports SystemV-style init systems on Debian-family and
|
||||
RHEL-family platforms. It will create the `/etc/init.d/<service_name>` script
|
||||
and enable/disable the service using the platform-specific service resource.
|
||||
|
||||
```ruby
|
||||
poise_service 'myapp' do
|
||||
provider :sysvinit
|
||||
command 'myapp --serve'
|
||||
end
|
||||
```
|
||||
|
||||
By default a PID file will be created in `/var/run/service_name.pid`. You can
|
||||
use the `pid_file` option detailed below to override this and rely on your
|
||||
process creating a PID file in the given path.
|
||||
|
||||
#### Options
|
||||
|
||||
* `pid_file` – Path to PID file that the service command will create.
|
||||
* `pid_file_external` – If true, assume the service will create the PID file
|
||||
itself. *(default: true if `pid_file` option is set)*
|
||||
* `template` – Override the default script template. If you want to use a
|
||||
template in a different cookbook use `'cookbook:template'`.
|
||||
* `command` – Override the service command.
|
||||
* `directory` – Override the service directory.
|
||||
* `environment` – Override the service environment variables.
|
||||
* `reload_signal` – Override the service reload signal.
|
||||
* `stop_signal` – Override the service stop signal.
|
||||
* `user` – Override the service user.
|
||||
* `never_start` – Never try to start the service.
|
||||
* `never_stop` – Never try to stop the service.
|
||||
* `never_restart` – Never try to restart the service.
|
||||
* `never_reload` – Never try to reload the service.
|
||||
* `script_path` – Override the path to the generated service script.
|
||||
|
||||
### `upstart`
|
||||
|
||||
The `upstart` provider supports [Upstart](http://upstart.ubuntu.com/). It will
|
||||
create the `/etc/init/service_name.conf` configuration.
|
||||
|
||||
```ruby
|
||||
poise_service 'myapp' do
|
||||
provider :upstart
|
||||
command 'myapp --serve'
|
||||
end
|
||||
```
|
||||
|
||||
As a wide variety of versions of Upstart are in use in various Linux
|
||||
distributions, the provider does its best to identify which features are
|
||||
available and provide shims as appropriate. Most of these should be invisible
|
||||
however Upstart older than 1.10 does not support setting a `reload signal` so
|
||||
only SIGHUP can be used. You can set a `reload_shim` option to enable an
|
||||
internal implementaion of reloading to be used for signals other than SIGHUP,
|
||||
however as this is implemented inside Chef code, running `initctl reload` would
|
||||
still result in SIGHUP being sent. For this reason, the feature is disabled by
|
||||
default and will throw an error if a reload signal other than SIGHUP is used.
|
||||
|
||||
#### Options
|
||||
|
||||
* `reload_shim` – Enable the reload signal shim. See above for a warning about
|
||||
this feature.
|
||||
* `template` – Override the default configuration template. If you want to use a
|
||||
template in a different cookbook use `'cookbook:template'`.
|
||||
* `command` – Override the service command.
|
||||
* `directory` – Override the service directory.
|
||||
* `environment` – Override the service environment variables.
|
||||
* `reload_signal` – Override the service reload signal.
|
||||
* `stop_signal` – Override the service stop signal.
|
||||
* `user` – Override the service user.
|
||||
* `never_start` – Never try to start the service.
|
||||
* `never_stop` – Never try to stop the service.
|
||||
* `never_restart` – Never try to restart the service.
|
||||
* `never_reload` – Never try to reload the service.
|
||||
|
||||
### `systemd`
|
||||
|
||||
The `systemd` provider supports [systemd](http://www.freedesktop.org/wiki/Software/systemd/).
|
||||
It will create the `/etc/systemd/system/service_name.service` configuration.
|
||||
|
||||
|
||||
```ruby
|
||||
poise_service 'myapp' do
|
||||
provider :systemd
|
||||
command 'myapp --serve'
|
||||
end
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
* `template` – Override the default configuration template. If you want to use a
|
||||
template in a different cookbook use `'cookbook:template'`.
|
||||
* `command` – Override the service command.
|
||||
* `directory` – Override the service directory.
|
||||
* `environment` – Override the service environment variables.
|
||||
* `reload_signal` – Override the service reload signal.
|
||||
* `stop_signal` – Override the service stop signal.
|
||||
* `user` – Override the service user.
|
||||
* `never_start` – Never try to start the service.
|
||||
* `never_stop` – Never try to stop the service.
|
||||
* `never_restart` – Never try to restart the service.
|
||||
* `never_reload` – Never try to reload the service.
|
||||
* `auto_reload` – Run `systemctl daemon-reload` after changes to the unit file. *(default: true)*
|
||||
* `restart_mode` – Restart mode for the generated service unit. *(default: on-failure)*
|
||||
|
||||
### `inittab`
|
||||
|
||||
The `inittab` provider supports managing services via `/etc/inittab` using
|
||||
[SystemV Init](http://www.nongnu.org/sysvinit/). This can provide basic
|
||||
process supervision even on very old *nix machines.
|
||||
|
||||
```ruby
|
||||
poise_service 'myapp' do
|
||||
provider :inittab
|
||||
command 'myapp --serve'
|
||||
end
|
||||
```
|
||||
|
||||
**NOTE:** Inittab does not allow stopping services, and they are started as soon
|
||||
as they are enabled.
|
||||
|
||||
#### Options
|
||||
|
||||
* `never_start` – Never try to start the service.
|
||||
* `never_stop` – Never try to stop the service.
|
||||
* `never_restart` – Never try to restart the service.
|
||||
* `never_reload` – Never try to reload the service.
|
||||
* `pid_file` – Path to PID file that the service command will create.
|
||||
* `service_id` – Unique 1-4 character tag for the service. Defaults to an
|
||||
auto-generated hash based on the service name. If these collide, bad things
|
||||
happen. Don't do that.
|
||||
|
||||
### `dummy`
|
||||
|
||||
The `dummy` provider supports launching services directly from Chef itself.
|
||||
This is for testing purposes only and is entirely unsuitable for use in
|
||||
production. This is mostly useful when used alongside kitchen-docker.
|
||||
|
||||
```ruby
|
||||
poise_service 'myapp' do
|
||||
provider :dummy
|
||||
command 'myapp --serve'
|
||||
end
|
||||
```
|
||||
|
||||
The service information is written to `/var/run`. The PID file is `service_name.pid`,
|
||||
the command output is `service_name.out`, and the service parameters are in
|
||||
`service_name.json`.
|
||||
|
||||
#### Options
|
||||
|
||||
* `never_start` – Never try to start the service.
|
||||
* `never_stop` – Never try to stop the service.
|
||||
* `never_restart` – Never try to restart the service.
|
||||
* `never_reload` – Never try to reload the service.
|
||||
* `restart_delay` – Number of seconds to wait between stop and start when
|
||||
restarting. *(default: 1)*
|
||||
|
||||
## ServiceMixin
|
||||
|
||||
For the common case of a resource (LWRP or plain Ruby) that roughly maps to
|
||||
"some config files and a service" poise-service provides a mixin module,
|
||||
`PoiseService::ServiceMixin`. This mixin adds the standard service actions
|
||||
(`enable`, `disable`, `start`, `stop`, `restart`, and `reload`) with basic
|
||||
implementations that call those actions on a `poise_service` resource for you.
|
||||
You customize the service by defining a `service_options` method on your
|
||||
provider class:
|
||||
|
||||
```ruby
|
||||
def service_options(service)
|
||||
# service is the PoiseService::Resource object instance.
|
||||
service.command "/usr/sbin/#{new_resource.name} -f /etc/#{new_resource.name}/conf/httpd.conf -DFOREGROUND"
|
||||
service.stop_signal 'WINCH'
|
||||
service.reload_signal 'USR1'
|
||||
end
|
||||
```
|
||||
|
||||
You will generally want to override the `enable` action to install things
|
||||
related to the service like packages, users and configuration files:
|
||||
|
||||
```ruby
|
||||
def action_enable
|
||||
notifying_block do
|
||||
package 'apache2'
|
||||
poise_service_user 'www-data'
|
||||
template "/etc/#{new_resource.name}/conf/httpd.conf" do
|
||||
# ...
|
||||
end
|
||||
end
|
||||
# This super call will run the normal service enable,
|
||||
# creating the service and starting it.
|
||||
super
|
||||
end
|
||||
```
|
||||
|
||||
See [the poise_service_test_mixin resource](test/cookbooks/poise-service_test/resources/mixin.rb)
|
||||
and [provider](test/cookbooks/poise-service_test/providers/mixin.rb) for
|
||||
examples of using `ServiceMixin` in an LWRP.
|
||||
|
||||
## Sponsors
|
||||
|
||||
Development sponsored by [Bloomberg](http://www.bloomberg.com/company/technology/).
|
||||
|
||||
The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2015-2016, Noah Kantrowitz
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
19
cookbooks/poise-service/attributes/default.rb
Normal file
19
cookbooks/poise-service/attributes/default.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
default['poise-service']['provider'] = 'auto'
|
||||
|
||||
default['poise-service']['options'] = {}
|
||||
25
cookbooks/poise-service/files/halite_gem/poise_service.rb
Normal file
25
cookbooks/poise-service/files/halite_gem/poise_service.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
|
||||
module PoiseService
|
||||
autoload :Error, 'poise_service/error'
|
||||
autoload :Resources, 'poise_service/resources'
|
||||
autoload :ServiceMixin, 'poise_service/service_mixin'
|
||||
autoload :ServiceProviders, 'poise_service/service_providers'
|
||||
autoload :Utils, 'poise_service/utils'
|
||||
autoload :VERSION, 'poise_service/version'
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'poise_service/resources'
|
||||
require 'poise_service/service_providers'
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
module PoiseService
|
||||
class Error < ::Exception
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'poise_service/resources/poise_service'
|
||||
require 'poise_service/resources/poise_service_user'
|
||||
|
||||
|
||||
module PoiseService
|
||||
# Chef resources and providers for poise-service.
|
||||
#
|
||||
# @since 1.0.0
|
||||
module Resources
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,165 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'etc'
|
||||
|
||||
require 'chef/mash'
|
||||
require 'chef/resource'
|
||||
require 'poise'
|
||||
|
||||
require 'poise_service/error'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module Resources
|
||||
# (see PoiseService::Resource)
|
||||
module PoiseService
|
||||
# `poise_service` resource. Provides a unified service interface with a
|
||||
# dependency injection framework.
|
||||
#
|
||||
# @since 1.0.0
|
||||
# @provides poise_service
|
||||
# @action enable
|
||||
# @action disable
|
||||
# @action start
|
||||
# @action stop
|
||||
# @action restart
|
||||
# @action reload
|
||||
# @example
|
||||
# poise_service 'myapp' do
|
||||
# command 'myapp --serve'
|
||||
# user 'myuser'
|
||||
# directory '/home/myapp'
|
||||
# end
|
||||
class Resource < Chef::Resource
|
||||
include Poise(inversion: true)
|
||||
provides(:poise_service)
|
||||
actions(:enable, :disable, :start, :stop, :restart, :reload)
|
||||
|
||||
# @!attribute service_name
|
||||
# Name of the service to the underlying init system. Defaults to the name
|
||||
# of the resource.
|
||||
# @return [String]
|
||||
attribute(:service_name, kind_of: String, name_attribute: true)
|
||||
# @!attribute command
|
||||
# Command to run inside the service. This command must remain in the
|
||||
# foreground and not daemoinize itself.
|
||||
# @return [String]
|
||||
attribute(:command, kind_of: String, required: true)
|
||||
# @!attribute user
|
||||
# User to run the service as. See {UserResource} for an easy way to
|
||||
# create service users. Defaults to root.
|
||||
# @return [String]
|
||||
attribute(:user, kind_of: String, default: 'root')
|
||||
# @!attribute directory
|
||||
# Working directory for the service. Defaults to the home directory of
|
||||
# the configured user or / if not found.
|
||||
# @return [String]
|
||||
attribute(:directory, kind_of: String, default: lazy { default_directory })
|
||||
# @!attribute environment
|
||||
# Environment variables for the service.
|
||||
# @return [Hash]
|
||||
attribute(:environment, kind_of: Hash, default: lazy { Mash.new })
|
||||
# @!attribute stop_signal
|
||||
# Signal to use to stop the service. Some systems will fall back to
|
||||
# KILL if this signal fails to stop the process. Defaults to TERM.
|
||||
# @return [String, Symbol, Integer]
|
||||
attribute(:stop_signal, kind_of: [String, Symbol, Integer], default: 'TERM')
|
||||
# @!attribute reload_signal
|
||||
# Signal to use to reload the service. Defaults to HUP.
|
||||
# @return [String, Symbol, Integer]
|
||||
attribute(:reload_signal, kind_of: [String, Symbol, Integer], default: 'HUP')
|
||||
# @!attribute restart_on_update
|
||||
# If true, the service will be restarted if the service definition or
|
||||
# configuration changes. If 'immediately', the notification will happen
|
||||
# in immediate mode.
|
||||
# @return [Boolean, String]
|
||||
attribute(:restart_on_update, equal_to: [true, false, 'immediately', :immediately], default: true)
|
||||
|
||||
# Resource DSL callback.
|
||||
#
|
||||
# @api private
|
||||
def after_created
|
||||
# Set signals to clean values.
|
||||
stop_signal(clean_signal(stop_signal))
|
||||
reload_signal(clean_signal(reload_signal))
|
||||
end
|
||||
|
||||
# Return the PID of the main process for this service or nil if the service
|
||||
# isn't running or the PID cannot be found.
|
||||
#
|
||||
# @return [Integer, nil]
|
||||
# @example
|
||||
# execute "kill -WINCH #{resources('poise_test[myapp]').pid}"
|
||||
def pid
|
||||
# :pid isn't a real action, but this should still work.
|
||||
provider_for_action(:pid).pid
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Try to find the home diretory for the configured user. This will fail if
|
||||
# nsswitch.conf was changed during this run such as with LDAP. Defaults to
|
||||
# the system root directory.
|
||||
#
|
||||
# @see #directory
|
||||
# @return [String]
|
||||
def default_directory
|
||||
# Default fallback.
|
||||
sysroot = case node['platform_family']
|
||||
when 'windows'
|
||||
ENV.fetch('SystemRoot', 'C:\\')
|
||||
else
|
||||
'/'
|
||||
end
|
||||
# For root we always want the system root path.
|
||||
return sysroot if user == 'root'
|
||||
# Force a reload in case any users were created earlier in the run.
|
||||
Etc.endpwent
|
||||
# ArgumentError means we can't find the user, possibly nsswitch caching?
|
||||
home = begin
|
||||
Dir.home(user)
|
||||
rescue ArgumentError
|
||||
sysroot
|
||||
end
|
||||
# If the home doesn't exist or is empty, use sysroot.
|
||||
home = sysroot if home.empty? || !::File.directory?(home)
|
||||
home
|
||||
end
|
||||
|
||||
# Clean up a signal string/integer. Ints are mapped to the signal name,
|
||||
# and strings are reformatted to upper case and without the SIG.
|
||||
#
|
||||
# @see #stop_signal
|
||||
# @param signal [String, Symbol, Integer] Signal value to clean.
|
||||
# @return [String]
|
||||
def clean_signal(signal)
|
||||
if signal.is_a?(Integer)
|
||||
raise Error.new("Unknown signal #{signal}") unless (0..31).include?(signal)
|
||||
Signal.signame(signal)
|
||||
else
|
||||
short_sig = signal.to_s.upcase
|
||||
short_sig = short_sig[3..-1] if short_sig.start_with?('SIG')
|
||||
raise Error.new("Unknown signal #{signal}") unless Signal.list.include?(short_sig)
|
||||
short_sig
|
||||
end
|
||||
end
|
||||
|
||||
# Providers can be found under service_providers/.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,240 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/resource'
|
||||
require 'chef/provider'
|
||||
require 'poise'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module Resources
|
||||
# (see PoiseServiceTest::Resource)
|
||||
module PoiseServiceTest
|
||||
# A `poise_service_test` resource for integration testing service providers.
|
||||
# This is used in Test-Kitchen tests to ensure all providers behave
|
||||
# similarly.
|
||||
#
|
||||
# @since 1.0.0
|
||||
# @provides poise_service_test
|
||||
# @action run
|
||||
# @example
|
||||
# poise_service_test 'upstart' do
|
||||
# service_provider :upstart
|
||||
# base_port 5000
|
||||
# end
|
||||
class Resource < Chef::Resource
|
||||
include Poise
|
||||
provides(:poise_service_test)
|
||||
actions(:run)
|
||||
|
||||
# @!attribute service_provider
|
||||
# Service provider to set for the test group.
|
||||
# @return [Symbol]
|
||||
attribute(:service_provider, kind_of: Symbol)
|
||||
# @!attribute service_options
|
||||
# Service options to set for the test group.
|
||||
# @return [Hash, nil]
|
||||
attribute(:service_options, kind_of: [Hash, NilClass])
|
||||
# @!attribute base_port
|
||||
# Port number to start from for the test group.
|
||||
# @return [Integer]
|
||||
attribute(:base_port, kind_of: Integer)
|
||||
end
|
||||
|
||||
# Provider for `poise_service_test`.
|
||||
#
|
||||
# @see Resource
|
||||
# @provides poise_service_test
|
||||
class Provider < Chef::Provider
|
||||
include Poise
|
||||
provides(:poise_service_test)
|
||||
|
||||
SERVICE_SCRIPT = <<-EOH
|
||||
require 'webrick'
|
||||
require 'json'
|
||||
require 'etc'
|
||||
FILE_DATA = ''
|
||||
server = WEBrick::HTTPServer.new(Port: ARGV[0].to_i)
|
||||
server.mount_proc '/' do |req, res|
|
||||
res.body = {
|
||||
directory: Dir.getwd,
|
||||
user: Etc.getpwuid(Process.uid).name,
|
||||
euser: Etc.getpwuid(Process.euid).name,
|
||||
group: Etc.getgrgid(Process.gid).name,
|
||||
egroup: Etc.getgrgid(Process.egid).name,
|
||||
environment: ENV.to_hash,
|
||||
file_data: FILE_DATA,
|
||||
pid: Process.pid,
|
||||
}.to_json
|
||||
end
|
||||
EOH
|
||||
|
||||
# `run` action for `poise_service_test`. Create all test services.
|
||||
#
|
||||
# @return [void]
|
||||
def action_run
|
||||
notifying_block do
|
||||
create_script
|
||||
create_noterm_script
|
||||
create_user
|
||||
create_tests
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_script
|
||||
file '/usr/bin/poise_test' do
|
||||
owner 'root'
|
||||
group 'root'
|
||||
mode '755'
|
||||
content <<-EOH
|
||||
#!/opt/chef/embedded/bin/ruby
|
||||
#{SERVICE_SCRIPT}
|
||||
def load_file
|
||||
FILE_DATA.replace(IO.read(ARGV[1]))
|
||||
end
|
||||
if ARGV[1]
|
||||
load_file
|
||||
trap('HUP') do
|
||||
load_file
|
||||
end
|
||||
end
|
||||
server.start
|
||||
EOH
|
||||
end
|
||||
end
|
||||
|
||||
def create_noterm_script
|
||||
file '/usr/bin/poise_test_noterm' do
|
||||
owner 'root'
|
||||
group 'root'
|
||||
mode '755'
|
||||
content <<-EOH
|
||||
#!/opt/chef/embedded/bin/ruby
|
||||
trap('HUP', 'IGNORE')
|
||||
trap('TERM', 'IGNORE')
|
||||
#{SERVICE_SCRIPT}
|
||||
while true
|
||||
begin
|
||||
server.start
|
||||
rescue Exception
|
||||
rescue StandardError
|
||||
end
|
||||
end
|
||||
EOH
|
||||
end
|
||||
end
|
||||
|
||||
def create_user
|
||||
poise_service_user 'poise' do
|
||||
home '/tmp'
|
||||
end
|
||||
end
|
||||
|
||||
def create_tests
|
||||
poise_service "poise_test_#{new_resource.name}" do
|
||||
if new_resource.service_provider
|
||||
provider new_resource.service_provider
|
||||
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
||||
end
|
||||
command "/usr/bin/poise_test #{new_resource.base_port}"
|
||||
end
|
||||
|
||||
poise_service "poise_test_#{new_resource.name}_params" do
|
||||
if new_resource.service_provider
|
||||
provider new_resource.service_provider
|
||||
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
||||
end
|
||||
command "/usr/bin/poise_test #{new_resource.base_port + 1}"
|
||||
environment POISE_ENV: new_resource.name
|
||||
user 'poise'
|
||||
end
|
||||
|
||||
poise_service "poise_test_#{new_resource.name}_noterm" do
|
||||
if new_resource.service_provider
|
||||
provider new_resource.service_provider
|
||||
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
||||
end
|
||||
action [:enable, :disable]
|
||||
command "/usr/bin/poise_test_noterm #{new_resource.base_port + 2}"
|
||||
stop_signal 'kill'
|
||||
end
|
||||
|
||||
{'restart' => 3, 'reload' => 4}.each do |action, port|
|
||||
# Stop it before writing the file so we always start with first.
|
||||
poise_service "poise_test_#{new_resource.name}_#{action} stop" do
|
||||
if new_resource.service_provider
|
||||
provider new_resource.service_provider
|
||||
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
||||
end
|
||||
action(:disable)
|
||||
service_name "poise_test_#{new_resource.name}_#{action}"
|
||||
end
|
||||
|
||||
# Write the content to the read on service launch.
|
||||
file "/etc/poise_test_#{new_resource.name}_#{action}" do
|
||||
content 'first'
|
||||
end
|
||||
|
||||
# Launch the service, reading in first.
|
||||
poise_service "poise_test_#{new_resource.name}_#{action}" do
|
||||
if new_resource.service_provider
|
||||
provider new_resource.service_provider
|
||||
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
||||
end
|
||||
command "/usr/bin/poise_test #{new_resource.base_port + port} /etc/poise_test_#{new_resource.name}_#{action}"
|
||||
end
|
||||
|
||||
# Rewrite the file to second, restart/reload to trigger an update.
|
||||
file "/etc/poise_test_#{new_resource.name}_#{action} again" do
|
||||
path "/etc/poise_test_#{new_resource.name}_#{action}"
|
||||
content 'second'
|
||||
notifies action.to_sym, "poise_service[poise_test_#{new_resource.name}_#{action}]"
|
||||
end
|
||||
end
|
||||
|
||||
# Test the #pid accessor.
|
||||
ruby_block "/tmp/poise_test_#{new_resource.name}_pid" do
|
||||
block do
|
||||
pid = resources("poise_service[poise_test_#{new_resource.name}]").pid
|
||||
IO.write("/tmp/poise_test_#{new_resource.name}_pid", pid.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Test changing the service definition itself.
|
||||
poise_service "poise_test_#{new_resource.name}_change" do
|
||||
if new_resource.service_provider
|
||||
provider new_resource.service_provider
|
||||
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
||||
end
|
||||
command "/usr/bin/poise_test #{new_resource.base_port + 5}"
|
||||
end
|
||||
|
||||
poise_service "poise_test_#{new_resource.name}_change_second" do
|
||||
service_name "poise_test_#{new_resource.name}_change"
|
||||
if new_resource.service_provider
|
||||
provider new_resource.service_provider
|
||||
options new_resource.service_provider, new_resource.service_options if new_resource.service_options
|
||||
end
|
||||
command "/usr/bin/poise_test #{new_resource.base_port + 6}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,186 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/resource'
|
||||
require 'chef/provider'
|
||||
require 'poise'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module Resources
|
||||
# (see PoiseServiceUser::Resource)
|
||||
# @since 1.0.0
|
||||
module PoiseServiceUser
|
||||
# Shells to look for in order.
|
||||
# @api private
|
||||
DEFAULT_SHELLS = %w{/bin/nologin /usr/bin/nologin /bin/false}
|
||||
|
||||
# A `poise_service_user` resource to create service users/groups.
|
||||
#
|
||||
# @since 1.0.0
|
||||
# @provides poise_service_user
|
||||
# @action create
|
||||
# @action remove
|
||||
# @example
|
||||
# poise_service_user 'myapp' do
|
||||
# home '/var/tmp'
|
||||
# group 'nogroup'
|
||||
# end
|
||||
class Resource < Chef::Resource
|
||||
include Poise
|
||||
provides(:poise_service_user)
|
||||
actions(:create, :remove)
|
||||
|
||||
# @!attribute user
|
||||
# Name of the user to create. Defaults to the name of the resource.
|
||||
# @return [String]
|
||||
attribute(:user, kind_of: String, name_attribute: true)
|
||||
# @!attribute group
|
||||
# Name of the group to create. Defaults to the name of the user,
|
||||
# except on Windows where it defaults to false. Set to false to
|
||||
# disable group creation.
|
||||
# @return [String, false]
|
||||
attribute(:group, kind_of: [String, FalseClass], default: lazy { default_group })
|
||||
# @!attribute uid
|
||||
# UID of the user to create. Optional, if not set the UID will be
|
||||
# allocated automatically.
|
||||
# @return [Integer]
|
||||
attribute(:uid, kind_of: Integer)
|
||||
# @!attribute gid
|
||||
# GID of the group to create. Optional, if not set the GID will be
|
||||
# allocated automatically.
|
||||
# @return [Integer]
|
||||
attribute(:gid, kind_of: Integer)
|
||||
# @!attribute shell
|
||||
# Login shell for the user. Optional, if not set the shell will be
|
||||
# determined automatically.
|
||||
# @return [String]
|
||||
attribute(:shell, kind_of: String, default: lazy { default_shell })
|
||||
# @!attribute home
|
||||
# Home directory of the user. This directory will not be created if it
|
||||
# does not exist. Optional.
|
||||
# @return [String]
|
||||
attribute(:home, kind_of: String)
|
||||
|
||||
private
|
||||
|
||||
# Find a default shell for service users. Tries to use nologin, but fall
|
||||
# back on false.
|
||||
#
|
||||
# @api private
|
||||
# @return [String]
|
||||
def default_shell
|
||||
DEFAULT_SHELLS.find {|s| ::File.exist?(s) } || DEFAULT_SHELLS.last
|
||||
end
|
||||
|
||||
# Find the default group name. Returns false on Windows because service
|
||||
# groups aren't needed there. Otherwise use the name of the service user.
|
||||
#
|
||||
# @api private
|
||||
# @return [String, false]
|
||||
def default_group
|
||||
if node.platform_family?('windows')
|
||||
false
|
||||
else
|
||||
user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Provider for `poise_service_user`.
|
||||
#
|
||||
# @since 1.0.0
|
||||
# @see Resource
|
||||
# @provides poise_service_user
|
||||
class Provider < Chef::Provider
|
||||
include Poise
|
||||
provides(:poise_service_user)
|
||||
|
||||
# `create` action for `poise_service_user`. Ensure the user and group (if
|
||||
# enabled) exist.
|
||||
#
|
||||
# @return [void]
|
||||
def action_create
|
||||
notifying_block do
|
||||
create_group if new_resource.group
|
||||
create_user
|
||||
end
|
||||
end
|
||||
|
||||
# `remove` action for `poise_service_user`. Ensure the user and group (if
|
||||
# enabled) are destroyed.
|
||||
#
|
||||
# @return [void]
|
||||
def action_remove
|
||||
notifying_block do
|
||||
remove_user
|
||||
remove_group if new_resource.group
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Create the system group.
|
||||
#
|
||||
# @api private
|
||||
# @return [void]
|
||||
def create_group
|
||||
group new_resource.group do
|
||||
gid new_resource.gid
|
||||
# Solaris doesn't support the idea of system groups.
|
||||
system true unless node.platform_family?('solaris2')
|
||||
end
|
||||
end
|
||||
|
||||
# Create the system user.
|
||||
#
|
||||
# @api private
|
||||
# @return [void]
|
||||
def create_user
|
||||
user new_resource.user do
|
||||
comment "Service user for #{new_resource.name}"
|
||||
gid new_resource.group if new_resource.group
|
||||
home new_resource.home
|
||||
shell new_resource.shell
|
||||
# Solaris doesn't support the idea of system users.
|
||||
system true unless node.platform_family?('solaris2')
|
||||
uid new_resource.uid
|
||||
end
|
||||
end
|
||||
|
||||
# Remove the system group.
|
||||
#
|
||||
# @api private
|
||||
# @return [void]
|
||||
def remove_group
|
||||
create_group.tap do |r|
|
||||
r.action(:remove)
|
||||
end
|
||||
end
|
||||
|
||||
# Remove the system user.
|
||||
#
|
||||
# @api private
|
||||
# @return [void]
|
||||
def remove_user
|
||||
create_user.tap do |r|
|
||||
r.action(:remove)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,193 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'poise'
|
||||
|
||||
require 'poise_service/resources/poise_service'
|
||||
|
||||
|
||||
module PoiseService
|
||||
# Mixin for application services. This is any resource that will be part of
|
||||
# an application deployment and involves running a persistent service.
|
||||
#
|
||||
# @since 1.0.0
|
||||
# @example
|
||||
# module MyApp
|
||||
# class Resource < Chef::Resource
|
||||
# include Poise
|
||||
# provides(:my_app)
|
||||
# include PoiseService::ServiceMixin
|
||||
# end
|
||||
#
|
||||
# class Provider < Chef::Provider
|
||||
# include Poise
|
||||
# provides(:my_app)
|
||||
# include PoiseService::ServiceMixin
|
||||
#
|
||||
# def action_enable
|
||||
# notifying_block do
|
||||
# template '/etc/myapp.conf' do
|
||||
# # ...
|
||||
# end
|
||||
# end
|
||||
# super
|
||||
# end
|
||||
#
|
||||
# def service_options(r)
|
||||
# r.command('myapp --serve')
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
module ServiceMixin
|
||||
include Poise::Utils::ResourceProviderMixin
|
||||
|
||||
# Mixin for service wrapper resources.
|
||||
#
|
||||
# @see ServiceMixin
|
||||
module Resource
|
||||
include Poise::Resource
|
||||
|
||||
module ClassMethods
|
||||
# @api private
|
||||
def included(klass)
|
||||
super
|
||||
klass.extend(ClassMethods)
|
||||
klass.class_exec do
|
||||
actions(:enable, :disable, :start, :stop, :restart, :reload)
|
||||
attribute(:service_name, kind_of: String, name_attribute: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
extend ClassMethods
|
||||
end
|
||||
|
||||
# Mixin for service wrapper providers.
|
||||
#
|
||||
# @see ServiceMixin
|
||||
module Provider
|
||||
include Poise::Provider
|
||||
|
||||
# Default enable action for service wrappers.
|
||||
#
|
||||
# @return [void]
|
||||
def action_enable
|
||||
notify_if_service do
|
||||
service_resource.run_action(:enable)
|
||||
end
|
||||
end
|
||||
|
||||
# Default disable action for service wrappers.
|
||||
#
|
||||
# @return [void]
|
||||
def action_disable
|
||||
notify_if_service do
|
||||
service_resource.run_action(:disable)
|
||||
end
|
||||
end
|
||||
|
||||
# Default start action for service wrappers.
|
||||
#
|
||||
# @return [void]
|
||||
def action_start
|
||||
notify_if_service do
|
||||
service_resource.run_action(:start)
|
||||
end
|
||||
end
|
||||
|
||||
# Default stop action for service wrappers.
|
||||
#
|
||||
# @return [void]
|
||||
def action_stop
|
||||
notify_if_service do
|
||||
service_resource.run_action(:stop)
|
||||
end
|
||||
end
|
||||
|
||||
# Default restart action for service wrappers.
|
||||
#
|
||||
# @return [void]
|
||||
def action_restart
|
||||
notify_if_service do
|
||||
service_resource.run_action(:restart)
|
||||
end
|
||||
end
|
||||
|
||||
# Default reload action for service wrappers.
|
||||
#
|
||||
# @return [void]
|
||||
def action_reload
|
||||
notify_if_service do
|
||||
service_resource.run_action(:reload)
|
||||
end
|
||||
end
|
||||
|
||||
# @todo Add reload once poise-service supports it.
|
||||
|
||||
private
|
||||
|
||||
# Set the current resource as notified if the provided block updates the
|
||||
# service resource.
|
||||
#
|
||||
# @api public
|
||||
# @param block [Proc] Block to run.
|
||||
# @return [void]
|
||||
# @example
|
||||
# notify_if_service do
|
||||
# service_resource.run_action(:enable)
|
||||
# end
|
||||
def notify_if_service(&block)
|
||||
service_resource.updated_by_last_action(false)
|
||||
block.call if block
|
||||
new_resource.updated_by_last_action(true) if service_resource.updated_by_last_action?
|
||||
end
|
||||
|
||||
# Service resource for this service wrapper. This returns a
|
||||
# poise_service resource that will not be added to the resource
|
||||
# collection. Override {#service_options} to set service resource
|
||||
# parameters.
|
||||
#
|
||||
# @api public
|
||||
# @return [Chef::Resource]
|
||||
# @example
|
||||
# service_resource.run_action(:restart)
|
||||
def service_resource
|
||||
@service_resource ||= PoiseService::Resources::PoiseService::Resource.new(new_resource.name, run_context).tap do |r|
|
||||
# Set some defaults.
|
||||
r.declared_type = :poise_service
|
||||
r.enclosing_provider = self
|
||||
r.source_line = new_resource.source_line
|
||||
r.service_name(new_resource.service_name)
|
||||
# Call the subclass hook for more specific settings.
|
||||
service_options(r)
|
||||
end
|
||||
end
|
||||
|
||||
# Abstract hook to set parameters on {#service_resource} when it is
|
||||
# created. This is required to set at least `resource.command`.
|
||||
#
|
||||
# @api public
|
||||
# @param resource [Chef::Resource] Resource instance to set parameters on.
|
||||
# @return [void]
|
||||
# @example
|
||||
# def service_options(resource)
|
||||
# resource.command('myapp --serve')
|
||||
# end
|
||||
def service_options(resource)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/platform/provider_priority_map'
|
||||
|
||||
require 'poise_service/service_providers/dummy'
|
||||
require 'poise_service/service_providers/inittab'
|
||||
require 'poise_service/service_providers/systemd'
|
||||
require 'poise_service/service_providers/sysvinit'
|
||||
require 'poise_service/service_providers/upstart'
|
||||
|
||||
|
||||
module PoiseService
|
||||
# Inversion providers for the poise_service resource.
|
||||
#
|
||||
# @since 1.0.0
|
||||
module ServiceProviders
|
||||
# Set up priority maps
|
||||
Chef::Platform::ProviderPriorityMap.instance.priority(:poise_service, [
|
||||
PoiseService::ServiceProviders::Systemd,
|
||||
PoiseService::ServiceProviders::Upstart,
|
||||
PoiseService::ServiceProviders::Sysvinit,
|
||||
])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,196 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/provider'
|
||||
require 'poise'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module ServiceProviders
|
||||
class Base < Chef::Provider
|
||||
include Poise(inversion: :poise_service)
|
||||
|
||||
# Extend the default lookup behavior to check for service_name too.
|
||||
#
|
||||
# @api private
|
||||
def self.resolve_inversion_provider(node, resource)
|
||||
attrs = resolve_inversion_attribute(node)
|
||||
(attrs[resource.service_name] && attrs[resource.service_name]['provider']) || super
|
||||
end
|
||||
|
||||
# Extend the default options to check for service_name too.
|
||||
#
|
||||
# @api private
|
||||
def self.inversion_options(node, resource)
|
||||
super.tap do |opts|
|
||||
attrs = resolve_inversion_attribute(node)
|
||||
opts.update(attrs[resource.service_name]) if attrs[resource.service_name]
|
||||
run_state = Mash.new(node.run_state.fetch('poise_inversion', {}).fetch(inversion_resource, {}))[resource.service_name] || {}
|
||||
opts.update(run_state['*']) if run_state['*']
|
||||
opts.update(run_state[provides]) if run_state[provides]
|
||||
end
|
||||
end
|
||||
|
||||
# Cache the service hints to improve performance. This is called from the
|
||||
# provides_auto? on most service providers and hits the filesystem a lot.
|
||||
#
|
||||
# @return [Array<Symbol>]
|
||||
def self.service_resource_hints
|
||||
@@service_resource_hints ||= Chef::Platform::ServiceHelpers.service_resource_providers
|
||||
end
|
||||
|
||||
def action_enable
|
||||
include_recipe(*Array(recipes)) if recipes
|
||||
notifying_block do
|
||||
create_service
|
||||
end
|
||||
enable_service
|
||||
action_start
|
||||
end
|
||||
|
||||
def action_disable
|
||||
action_stop
|
||||
disable_service
|
||||
notifying_block do
|
||||
destroy_service
|
||||
end
|
||||
end
|
||||
|
||||
def action_start
|
||||
return if options['never_start']
|
||||
notify_if_service do
|
||||
service_resource.run_action(:start)
|
||||
end
|
||||
end
|
||||
|
||||
def action_stop
|
||||
return if options['never_stop']
|
||||
notify_if_service do
|
||||
service_resource.run_action(:stop)
|
||||
end
|
||||
end
|
||||
|
||||
def action_restart
|
||||
return if options['never_restart']
|
||||
notify_if_service do
|
||||
service_resource.run_action(:restart)
|
||||
end
|
||||
end
|
||||
|
||||
def action_reload
|
||||
return if options['never_reload']
|
||||
notify_if_service do
|
||||
service_resource.run_action(:reload)
|
||||
end
|
||||
end
|
||||
|
||||
def pid
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Recipes to include for this provider to work. Subclasses can override.
|
||||
#
|
||||
# @return [String, Array]
|
||||
def recipes
|
||||
end
|
||||
|
||||
# Subclass hook to create the required files et al for the service.
|
||||
def create_service
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# Subclass hook to remove the required files et al for the service.
|
||||
def destroy_service
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def enable_service
|
||||
notify_if_service do
|
||||
service_resource.run_action(:enable)
|
||||
end
|
||||
end
|
||||
|
||||
def disable_service
|
||||
notify_if_service do
|
||||
service_resource.run_action(:disable)
|
||||
end
|
||||
end
|
||||
|
||||
def notify_if_service(&block)
|
||||
service_resource.updated_by_last_action(false)
|
||||
block.call
|
||||
new_resource.updated_by_last_action(true) if service_resource.updated_by_last_action?
|
||||
end
|
||||
|
||||
# Subclass hook to create the resource used to delegate start, stop, and
|
||||
# restart actions.
|
||||
def service_resource
|
||||
@service_resource ||= Chef::Resource::Service.new(new_resource.service_name, run_context).tap do |r|
|
||||
r.declared_type = :service
|
||||
r.enclosing_provider = self
|
||||
r.source_line = new_resource.source_line
|
||||
r.supports(status: true, restart: true, reload: true)
|
||||
end
|
||||
end
|
||||
|
||||
def service_template(path, default_source, &block)
|
||||
# Sigh scoping.
|
||||
template path do
|
||||
owner 'root'
|
||||
group node['root_group']
|
||||
mode '644'
|
||||
if options['template']
|
||||
# If we have a template override, allow specifying a cookbook via
|
||||
# "cookbook:template".
|
||||
parts = options['template'].split(/:/, 2)
|
||||
if parts.length == 2
|
||||
source parts[1]
|
||||
cookbook parts[0]
|
||||
else
|
||||
source parts.first
|
||||
cookbook new_resource.cookbook_name.to_s
|
||||
end
|
||||
else
|
||||
source default_source
|
||||
cookbook self.poise_defined_in_cookbook
|
||||
end
|
||||
variables(
|
||||
command: options['command'] || new_resource.command,
|
||||
directory: options['directory'] || new_resource.directory,
|
||||
environment: options['environment'] || new_resource.environment,
|
||||
name: new_resource.service_name,
|
||||
new_resource: new_resource,
|
||||
options: options,
|
||||
reload_signal: options['reload_signal'] || new_resource.reload_signal,
|
||||
stop_signal: options['stop_signal'] || new_resource.stop_signal,
|
||||
user: options['user'] || new_resource.user,
|
||||
)
|
||||
# Don't trigger a restart if the template doesn't already exist, this
|
||||
# prevents restarting on the run that first creates the service.
|
||||
restart_on_update = options.fetch('restart_on_update', new_resource.restart_on_update)
|
||||
if restart_on_update && ::File.exist?(path)
|
||||
mode = restart_on_update.to_s == 'immediately' ? :immediately : :delayed
|
||||
notifies :restart, new_resource, mode
|
||||
end
|
||||
instance_exec(&block) if block
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,195 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'etc'
|
||||
require 'shellwords'
|
||||
|
||||
require 'poise_service/service_providers/base'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module ServiceProviders
|
||||
class Dummy < Base
|
||||
provides(:dummy)
|
||||
|
||||
# @api private
|
||||
def self.default_inversion_options(node, resource)
|
||||
super.merge({
|
||||
# Time to wait between stop and start.
|
||||
restart_delay: 1,
|
||||
})
|
||||
end
|
||||
|
||||
def action_start
|
||||
return if options['never_start']
|
||||
return if pid
|
||||
Chef::Log.debug("[#{new_resource}] Starting #{new_resource.command}")
|
||||
# Clear the pid file if it exists.
|
||||
::File.unlink(pid_file) if ::File.exist?(pid_file)
|
||||
if Process.fork
|
||||
# Parent, wait for the final child to write the pid file.
|
||||
now = Time.now
|
||||
until ::File.exist?(pid_file)
|
||||
sleep(1)
|
||||
# After 30 seconds, show output at a higher level to avoid too much
|
||||
# confusing on failed process launches.
|
||||
if Time.now - now <= 30
|
||||
Chef::Log.debug("[#{new_resource}] Waiting for PID file")
|
||||
else
|
||||
Chef::Log.warning("[#{new_resource}] Waiting for PID file at #{pid_file} to be created")
|
||||
end
|
||||
end
|
||||
else
|
||||
# :nocov:
|
||||
begin
|
||||
Chef::Log.debug("[#{new_resource}] Forked")
|
||||
# First child, daemonize and go to town. This handles multi-fork,
|
||||
# setsid, and shutting down stdin/out/err.
|
||||
Process.daemon(true)
|
||||
Chef::Log.debug("[#{new_resource}] Daemonized")
|
||||
# Daemonized, set up process environment.
|
||||
Dir.chdir(new_resource.directory)
|
||||
Chef::Log.debug("[#{new_resource}] Directory changed to #{new_resource.directory}")
|
||||
ENV['HOME'] = Dir.home(new_resource.user)
|
||||
new_resource.environment.each do |key, val|
|
||||
ENV[key.to_s] = val.to_s
|
||||
end
|
||||
Chef::Log.debug("[#{new_resource}] Process environment configured")
|
||||
# Make sure to open the output file and write the pid file before we
|
||||
# drop privs.
|
||||
output = ::File.open(output_file, 'ab')
|
||||
IO.write(pid_file, Process.pid)
|
||||
Chef::Log.debug("[#{new_resource}] PID #{Process.pid} written to #{pid_file}")
|
||||
ent = Etc.getpwnam(new_resource.user)
|
||||
if Process.euid != ent.uid || Process.egid != ent.gid
|
||||
Process.initgroups(ent.name, ent.gid)
|
||||
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
||||
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
||||
Chef::Log.debug("[#{new_resource}] Changed privs to #{new_resource.user} (#{ent.uid}:#{ent.gid})")
|
||||
end
|
||||
# Log the command. Happens before ouput redirect or this ends up in the file.
|
||||
Chef::Log.debug("[#{new_resource}] Execing #{new_resource.command}")
|
||||
# Set up output logging.
|
||||
Chef::Log.debug("[#{new_resource}] Logging output to #{output_file}")
|
||||
$stdout.reopen(output)
|
||||
$stdout.sync = true
|
||||
$stderr.reopen(output)
|
||||
$stderr.sync = true
|
||||
$stdout.write("#{Time.now} Starting #{new_resource.command}")
|
||||
# Split the command so we don't get an extra sh -c.
|
||||
Kernel.exec(*Shellwords.split(new_resource.command))
|
||||
# Just in case, bail out.
|
||||
$stdout.reopen(STDOUT)
|
||||
$stderr.reopen(STDERR)
|
||||
Chef::Log.debug("[#{new_resource}] Exec failed, bailing out.")
|
||||
exit!
|
||||
rescue Exception => e
|
||||
# Welp, we tried.
|
||||
$stdout.reopen(STDOUT)
|
||||
$stderr.reopen(STDERR)
|
||||
Chef::Log.error("[#{new_resource}] Error during process spawn: #{e}")
|
||||
exit!
|
||||
end
|
||||
# :nocov:
|
||||
end
|
||||
Chef::Log.debug("[#{new_resource}] Started.")
|
||||
end
|
||||
|
||||
def action_stop
|
||||
return if options['never_stop']
|
||||
return unless pid
|
||||
Chef::Log.debug("[#{new_resource}] Stopping with #{new_resource.stop_signal}. Current PID is #{pid.inspect}.")
|
||||
Process.kill(new_resource.stop_signal, pid)
|
||||
::File.unlink(pid_file)
|
||||
end
|
||||
|
||||
def action_restart
|
||||
return if options['never_restart']
|
||||
action_stop
|
||||
# Give things a moment to stop before we try starting again.
|
||||
sleep(options['restart_delay'])
|
||||
action_start
|
||||
end
|
||||
|
||||
def action_reload
|
||||
return if options['never_reload']
|
||||
return unless pid
|
||||
Chef::Log.debug("[#{new_resource}] Reloading with #{new_resource.reload_signal}. Current PID is #{pid.inspect}.")
|
||||
Process.kill(new_resource.reload_signal, pid)
|
||||
end
|
||||
|
||||
def pid
|
||||
return nil unless ::File.exist?(pid_file)
|
||||
pid = IO.read(pid_file).to_i
|
||||
begin
|
||||
# Check if the PID is running.
|
||||
Process.kill(0, pid)
|
||||
pid
|
||||
rescue Errno::ESRCH
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def service_resource
|
||||
# Intentionally not implemented.
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def enable_service
|
||||
end
|
||||
|
||||
# Write all major service parameters to a file so that if they change, we
|
||||
# can restart the service. This also makes debuggin a bit easier so you
|
||||
# can still see what it thinks it was starting without sifting through
|
||||
# piles of debug output.
|
||||
def create_service
|
||||
service_template(run_file, 'dummy.json.erb')
|
||||
end
|
||||
|
||||
def disable_service
|
||||
end
|
||||
|
||||
# Delete the tracking file.
|
||||
def destroy_service
|
||||
file run_file do
|
||||
action :delete
|
||||
end
|
||||
|
||||
file pid_file do
|
||||
action :delete
|
||||
end
|
||||
end
|
||||
|
||||
# Path to the run parameters tracking file.
|
||||
def run_file
|
||||
"/var/run/#{new_resource.service_name}.json"
|
||||
end
|
||||
|
||||
# Path to the PID file.
|
||||
def pid_file
|
||||
"/var/run/#{new_resource.service_name}.pid"
|
||||
end
|
||||
|
||||
# Path to the output file.
|
||||
def output_file
|
||||
"/var/run/#{new_resource.service_name}.out"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,150 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/util/file_edit'
|
||||
|
||||
require 'poise_service/service_providers/base'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module ServiceProviders
|
||||
class Inittab < Base
|
||||
provides(:inittab)
|
||||
|
||||
def self.provides_auto?(node, resource)
|
||||
::File.exist?('/etc/inittab')
|
||||
end
|
||||
|
||||
def pid
|
||||
IO.read(pid_file).to_i if ::File.exist?(pid_file)
|
||||
end
|
||||
|
||||
# Don't try to stop when disabling because we can't.
|
||||
def action_disable
|
||||
disable_service
|
||||
notifying_block do
|
||||
destroy_service
|
||||
end
|
||||
end
|
||||
|
||||
def action_start
|
||||
Chef::Log.debug("[#{new_resource}] Inittab services are always started.")
|
||||
end
|
||||
|
||||
def action_stop
|
||||
raise NotImplementedError.new("[#{new_resource}] Inittab services cannot be stopped")
|
||||
end
|
||||
|
||||
def action_restart
|
||||
return if options['never_restart']
|
||||
# Just kill it and let init restart it.
|
||||
Process.kill(new_resource.stop_signal, pid) if pid
|
||||
end
|
||||
|
||||
def action_reload
|
||||
return if options['never_reload']
|
||||
Process.kill(new_resource.reload_signal, pid) if pid
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def service_resource
|
||||
# Intentionally not implemented.
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def enable_service
|
||||
end
|
||||
|
||||
def disable_service
|
||||
end
|
||||
|
||||
def create_service
|
||||
# Sigh scoping.
|
||||
pid_file_ = pid_file
|
||||
# Inittab only allows 127 characters for the command, so cram stuff in
|
||||
# a file. Writing to a file is gross, but so is using inittab so ¯\_(ツ)_/¯.
|
||||
service_template("/sbin/poise_service_#{new_resource.service_name}", 'inittab.sh.erb') do
|
||||
mode '755'
|
||||
variables.update(
|
||||
pid_file: pid_file_,
|
||||
)
|
||||
end
|
||||
# Add to inittab.
|
||||
edit_inittab do |content|
|
||||
inittab_line = "#{service_id}:2345:respawn:/sbin/poise_service_#{new_resource.service_name}"
|
||||
if content =~ /^# #{Regexp.escape(service_tag)}$/
|
||||
# Existing line, update in place.
|
||||
content.gsub!(/^(# #{Regexp.escape(service_tag)}\n)(.*)$/, "\\1#{inittab_line}")
|
||||
else
|
||||
# Add to the end.
|
||||
content << "# #{service_tag}\n#{inittab_line}\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_service
|
||||
# Remove from inittab.
|
||||
edit_inittab do |content|
|
||||
content.gsub!(/^# #{Regexp.escape(service_tag)}\n.*?\n$/, '')
|
||||
end
|
||||
|
||||
file "/sbin/poise_service_#{new_resource.service_name}" do
|
||||
action :delete
|
||||
end
|
||||
|
||||
file pid_file do
|
||||
action :delete
|
||||
end
|
||||
end
|
||||
|
||||
# The shortened ID because sysvinit only allows 4 characters.
|
||||
def service_id
|
||||
# This is a terrible hash, but it should be good enough.
|
||||
options['service_id'] || begin
|
||||
sum = new_resource.service_name.sum(20).to_s(36)
|
||||
if sum.length < 4
|
||||
'p' + sum
|
||||
else
|
||||
sum
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Tag to put in a comment in inittab for tracking.
|
||||
def service_tag
|
||||
"poise_service(#{new_resource.service_name})"
|
||||
end
|
||||
|
||||
def pid_file
|
||||
options['pid_file'] || "/var/run/#{new_resource.service_name}.pid"
|
||||
end
|
||||
|
||||
def edit_inittab(&block)
|
||||
inittab = IO.read('/etc/inittab')
|
||||
original_inittab = inittab.dup
|
||||
block.call(inittab)
|
||||
if inittab != original_inittab
|
||||
file '/etc/inittab' do
|
||||
content inittab
|
||||
end
|
||||
|
||||
execute 'telinit q'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,85 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/mixin/shell_out'
|
||||
|
||||
require 'poise_service/service_providers/base'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module ServiceProviders
|
||||
class Systemd < Base
|
||||
include Chef::Mixin::ShellOut
|
||||
provides(:systemd)
|
||||
|
||||
# @api private
|
||||
def self.provides_auto?(node, resource)
|
||||
service_resource_hints.include?(:systemd)
|
||||
end
|
||||
|
||||
# @api private
|
||||
def self.default_inversion_options(node, resource)
|
||||
super.merge({
|
||||
# Automatically reload systemd on changes.
|
||||
auto_reload: true,
|
||||
# Service restart mode.
|
||||
restart_mode: 'on-failure',
|
||||
})
|
||||
end
|
||||
|
||||
def pid
|
||||
cmd = shell_out(%w{systemctl status} + [new_resource.service_name])
|
||||
if !cmd.error? && cmd.stdout.include?('Active: active (running)') && md = cmd.stdout.match(/Main PID: (\d+)/)
|
||||
md[1].to_i
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def service_resource
|
||||
super.tap do |r|
|
||||
r.provider(Chef::Provider::Service::Systemd)
|
||||
end
|
||||
end
|
||||
|
||||
def systemctl_daemon_reload
|
||||
execute 'systemctl daemon-reload' do
|
||||
action :nothing
|
||||
user 'root'
|
||||
end
|
||||
end
|
||||
|
||||
def create_service
|
||||
reloader = systemctl_daemon_reload
|
||||
service_template("/etc/systemd/system/#{new_resource.service_name}.service", 'systemd.service.erb') do
|
||||
notifies :run, reloader, :immediately if options['auto_reload']
|
||||
variables.update(auto_reload: options['auto_reload'], restart_mode: options['restart_mode'])
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_service
|
||||
reloader = systemctl_daemon_reload
|
||||
file "/etc/systemd/system/#{new_resource.service_name}.service" do
|
||||
action :delete
|
||||
notifies :run, reloader, :immediately if options['auto_reload']
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'poise_service/service_providers/base'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module ServiceProviders
|
||||
class Sysvinit < Base
|
||||
provides(:sysvinit)
|
||||
|
||||
def self.provides_auto?(node, resource)
|
||||
[:debian, :redhat, :invokercd].any? {|name| service_resource_hints.include?(name) }
|
||||
end
|
||||
|
||||
def pid
|
||||
IO.read(pid_file).to_i if ::File.exist?(pid_file)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def service_resource
|
||||
super.tap do |r|
|
||||
r.provider(case node['platform_family']
|
||||
when 'debian'
|
||||
Chef::Provider::Service::Debian
|
||||
when 'rhel', 'amazon'
|
||||
Chef::Provider::Service::Redhat
|
||||
else
|
||||
# Better than nothing I guess? Will fail on enable I think.
|
||||
Chef::Provider::Service::Init
|
||||
end)
|
||||
r.init_command(script_path)
|
||||
# Pending https://github.com/chef/chef/pull/4709.
|
||||
r.start_command("#{script_path} start")
|
||||
r.stop_command("#{script_path} stop")
|
||||
r.status_command("#{script_path} status")
|
||||
r.restart_command("#{script_path} restart")
|
||||
r.reload_command("#{script_path} reload")
|
||||
end
|
||||
end
|
||||
|
||||
def create_service
|
||||
# Split the command into the binary and its arguments. This is for
|
||||
# start-stop-daemon since it treats those differently.
|
||||
parts = new_resource.command.split(/ /, 2)
|
||||
daemon = ENV['PATH'].split(/:/)
|
||||
.map {|path| ::File.absolute_path(parts[0], path) }
|
||||
.find {|path| ::File.exist?(path) } || parts[0]
|
||||
# Sigh scoping.
|
||||
pid_file_ = pid_file
|
||||
# Render the service template
|
||||
service_template(script_path, 'sysvinit.sh.erb') do
|
||||
mode '755'
|
||||
variables.update(
|
||||
daemon: daemon,
|
||||
daemon_options: parts[1].to_s,
|
||||
pid_file: pid_file_,
|
||||
pid_file_external: options['pid_file_external'].nil? ? !!options['pid_file'] : options['pid_file_external'],
|
||||
platform_family: node['platform_family'],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_service
|
||||
file script_path do
|
||||
action :delete
|
||||
end
|
||||
|
||||
file pid_file do
|
||||
action :delete
|
||||
end
|
||||
end
|
||||
|
||||
def script_path
|
||||
options['script_path'] || "/etc/init.d/#{new_resource.service_name}"
|
||||
end
|
||||
|
||||
def pid_file
|
||||
options['pid_file'] || "/var/run/#{new_resource.service_name}.pid"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,136 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Used in the template.
|
||||
require 'shellwords'
|
||||
|
||||
require 'chef/mixin/shell_out'
|
||||
|
||||
require 'poise_service/error'
|
||||
require 'poise_service/service_providers/base'
|
||||
|
||||
|
||||
module PoiseService
|
||||
module ServiceProviders
|
||||
class Upstart < Base
|
||||
include Chef::Mixin::ShellOut
|
||||
provides(:upstart)
|
||||
|
||||
def self.provides_auto?(node, resource)
|
||||
service_resource_hints.include?(:upstart)
|
||||
end
|
||||
|
||||
# @api private
|
||||
def self.default_inversion_options(node, resource)
|
||||
super.merge({
|
||||
# Time to wait between stop and start.
|
||||
restart_delay: 1,
|
||||
})
|
||||
end
|
||||
|
||||
# True restart in Upstart preserves the original config data, we want the
|
||||
# more obvious behavior like everything else in the world that restart
|
||||
# would re-read the updated config file. Use stop+start to get this
|
||||
# behavior. http://manpages.ubuntu.com/manpages/raring/man8/initctl.8.html
|
||||
def action_restart
|
||||
return if options['never_restart']
|
||||
action_stop
|
||||
# Give things a moment to stop before we try starting again.
|
||||
sleep(options['restart_delay'])
|
||||
action_start
|
||||
end
|
||||
|
||||
# Shim out reload if we have a version that predates reload support.
|
||||
def action_reload
|
||||
return if options['never_reload']
|
||||
if !upstart_features[:reload_signal] && new_resource.reload_signal != 'HUP'
|
||||
if options[:reload_shim]
|
||||
Process.kill(new_resource.reload_signal, pid)
|
||||
else
|
||||
check_reload_signal!
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def pid
|
||||
cmd = shell_out(%w{initctl status} + [new_resource.service_name])
|
||||
if !cmd.error? && md = cmd.stdout.match(/process (\d+)/)
|
||||
md[1].to_i
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def service_resource
|
||||
super.tap do |r|
|
||||
r.provider(Chef::Provider::Service::Upstart)
|
||||
end
|
||||
end
|
||||
|
||||
def create_service
|
||||
check_reload_signal!
|
||||
# Set features so it will be a closure below.
|
||||
features = upstart_features
|
||||
service_template("/etc/init/#{new_resource.service_name}.conf", 'upstart.conf.erb') do
|
||||
variables.update(
|
||||
upstart_features: features,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_service
|
||||
file "/etc/init/#{new_resource.service_name}.conf" do
|
||||
action :delete
|
||||
end
|
||||
end
|
||||
|
||||
def upstart_version
|
||||
cmd = shell_out(%w{initctl --version})
|
||||
if !cmd.error? && md = cmd.stdout.match(/upstart ([^)]+)\)/)
|
||||
md[1]
|
||||
else
|
||||
'0'
|
||||
end
|
||||
end
|
||||
|
||||
def upstart_features
|
||||
@upstart_features ||= begin
|
||||
upstart_ver = Gem::Version.new(upstart_version)
|
||||
versions_added = {
|
||||
kill_signal: '1.3',
|
||||
reload_signal: '1.10',
|
||||
setuid: '1.4',
|
||||
}
|
||||
versions_added.inject({}) do |memo, (feature, version)|
|
||||
memo[feature] = Gem::Requirement.create(">= #{version}").satisfied_by?(upstart_ver)
|
||||
memo
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_reload_signal!
|
||||
if !options['reload_shim'] && !upstart_features[:reload_signal] && new_resource.reload_signal != 'HUP'
|
||||
raise Error.new("Upstart #{upstart_version} only supports HUP for reload, to use the shim please set the 'reload_shim' options for #{new_resource.to_s}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'pathname'
|
||||
|
||||
|
||||
module PoiseService
|
||||
# Utility methods for PoiseService.
|
||||
#
|
||||
# @api public
|
||||
# @since 1.0.0
|
||||
module Utils
|
||||
# Methods are also available as module-level methods as well as a mixin.
|
||||
extend self
|
||||
|
||||
# Common segments to ignore
|
||||
COMMON_SEGMENTS = %w{var www current etc}.inject({}) {|memo, seg| memo[seg] = true; memo }
|
||||
|
||||
# Parse the service name from a path. Look at the last component of the
|
||||
# path, ignoring some common names.
|
||||
#
|
||||
# @param path [String] Path to parse.
|
||||
# @return [String]
|
||||
# @example
|
||||
# attribute(:service_name, kind_of: String, default: lazy { PoiseService::Utils.parse_service_name(path) })
|
||||
def parse_service_name(path)
|
||||
parts = Pathname.new(path).each_filename.to_a.reverse!
|
||||
# Find the last segment not in common segments, fall back to the last segment.
|
||||
parts.find {|seg| !COMMON_SEGMENTS[seg] } || parts.first
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
|
||||
module PoiseService
|
||||
VERSION = '1.5.2'
|
||||
end
|
||||
19
cookbooks/poise-service/libraries/default.rb
Normal file
19
cookbooks/poise-service/libraries/default.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright 2015-2016, Noah Kantrowitz
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load]
|
||||
$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__)
|
||||
require "poise_service/cheftie"
|
||||
1
cookbooks/poise-service/metadata.json
Normal file
1
cookbooks/poise-service/metadata.json
Normal file
File diff suppressed because one or more lines are too long
7
cookbooks/poise-service/templates/default/dummy.json.erb
Normal file
7
cookbooks/poise-service/templates/default/dummy.json.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
<%= {command: @command,
|
||||
directory: @directory,
|
||||
environment: @environment,
|
||||
name: @name,
|
||||
reload_signal: @reload_signal,
|
||||
stop_signal: @stop_signal,
|
||||
user: @user}.to_json %>
|
||||
15
cookbooks/poise-service/templates/default/inittab.sh.erb
Normal file
15
cookbooks/poise-service/templates/default/inittab.sh.erb
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
exec /opt/chef/embedded/bin/ruby <<EOH
|
||||
require 'etc'
|
||||
IO.write("<%= @pid_file %>", Process.pid)
|
||||
Dir.chdir("<%= @directory %>")
|
||||
ent = Etc.getpwnam("<%= @user %>")
|
||||
if Process.euid != ent.uid || Process.egid != ent.gid
|
||||
Process.initgroups(ent.name, ent.gid)
|
||||
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
||||
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
||||
end
|
||||
(ENV["HOME"] = Dir.home("<%= @user %>")) rescue nil
|
||||
<%= @environment.map {|key, value| "ENV[#{key.to_s.inspect}] = #{value.to_s.inspect}" }.join("; ") %>
|
||||
exec(*<%= Shellwords.split(@command).inspect %>)
|
||||
EOH
|
||||
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=<%= @name %>
|
||||
|
||||
[Service]
|
||||
Environment=<%= @environment.map {|key, val| %Q{"#{key}=#{val}"} }.join(' ') %>
|
||||
ExecStart=<%= @command %>
|
||||
ExecReload=/bin/kill -<%= @reload_signal %> $MAINPID
|
||||
KillSignal=<%= @stop_signal %>
|
||||
User=<%= @user %>
|
||||
WorkingDirectory=<%= @directory %>
|
||||
Restart=<%= @restart_mode %>
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
190
cookbooks/poise-service/templates/default/sysvinit.sh.erb
Normal file
190
cookbooks/poise-service/templates/default/sysvinit.sh.erb
Normal file
@@ -0,0 +1,190 @@
|
||||
#!/bin/sh
|
||||
# Init script for <%= @name %> generated by poise-service
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: <%= @name %>
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Init script for <%= @name %>
|
||||
# Description: Init script for <%= @name %>
|
||||
### END INIT INFO
|
||||
|
||||
<%- if @platform_family == 'debian' -%>
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
_start() {
|
||||
start-stop-daemon --start --quiet --background \
|
||||
--pidfile "<%= @pid_file %>"<% unless @pid_file_external %> --make-pidfile<% end %> \
|
||||
--chuid "<%= @user %>" --chdir "<%= @directory %>" \
|
||||
--exec "<%= @daemon %>" -- <%= @daemon_options %>
|
||||
}
|
||||
|
||||
_stop() {
|
||||
start-stop-daemon --stop --quiet --pidfile "<%= @pid_file %>" --user "<%= @user %>" --signal "<%= @stop_signal %>"
|
||||
}
|
||||
|
||||
_status() {
|
||||
status_of_proc -p "<%= @pid_file %>" "<%= @daemon %>" "<%= @name %>"
|
||||
}
|
||||
|
||||
_reload() {
|
||||
start-stop-daemon --stop --quiet --pidfile "<%= @pid_file %>" --user "<%= @user %>" --signal "<%= @reload_signal %>"
|
||||
}
|
||||
|
||||
<%- else -%>
|
||||
_start() {
|
||||
<%# Implementing this using RedHat's bash helpers is too painful. Sorry. %>
|
||||
<%# See dummy.rb for a more commented version of this code. %>
|
||||
/opt/chef/embedded/bin/ruby <<EOH
|
||||
require 'etc'
|
||||
pid_file = <%= @pid_file.inspect %>
|
||||
File.unlink(pid_file) if File.exist?(pid_file)
|
||||
if Process.fork
|
||||
sleep(1) until File.exist?(pid_file)
|
||||
else
|
||||
Process.daemon(true)
|
||||
Dir.chdir(<%= @directory.inspect %>)
|
||||
<%- unless @pid_file_external -%>
|
||||
IO.write(pid_file, Process.pid)
|
||||
<%- end -%>
|
||||
ent = Etc.getpwnam(<%= @user.inspect %>)
|
||||
if Process.euid != ent.uid || Process.egid != ent.gid
|
||||
Process.initgroups(ent.name, ent.gid)
|
||||
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
||||
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
||||
end
|
||||
Kernel.exec(*<%= Shellwords.split(@command).inspect %>)
|
||||
exit!
|
||||
end
|
||||
EOH
|
||||
}
|
||||
|
||||
_stop() {
|
||||
if [ -r "<%= @pid_file %>" ]; then
|
||||
kill -<%= @stop_signal%> "$(cat "<%= @pid_file %>")"
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
_status() {
|
||||
if [ -r "<%= @pid_file %>" ]; then
|
||||
kill -0 "$(cat "<%= @pid_file %>")"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_reload() {
|
||||
if [ -r "<%= @pid_file %>" ]; then
|
||||
kill -<%= @reload_signal%> "$(cat "<%= @pid_file %>")"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
<%# Some functions to match LSB %>
|
||||
|
||||
log_daemon_msg() {
|
||||
echo -n "$1"
|
||||
}
|
||||
|
||||
log_progress_msg() {
|
||||
echo -n "$1"
|
||||
}
|
||||
|
||||
log_warning_msg() {
|
||||
echo -n "$1"
|
||||
}
|
||||
|
||||
log_failure_msg() {
|
||||
echo -n "$1"
|
||||
}
|
||||
|
||||
log_end_msg() {
|
||||
if [ "$1" = 0 ]; then
|
||||
echo " [ OK ]"
|
||||
else
|
||||
echo " [FAILED]"
|
||||
fi
|
||||
}
|
||||
<%- end -%>
|
||||
|
||||
set -e
|
||||
|
||||
start() {
|
||||
if _start
|
||||
then
|
||||
rc=0
|
||||
sleep 1
|
||||
if ! kill -0 "$(cat "<%= @pid_file %>")" >/dev/null 2>&1; then
|
||||
log_failure_msg "<%= @name %> failed to start"
|
||||
rc=1
|
||||
fi
|
||||
else
|
||||
rc=1
|
||||
fi
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
log_end_msg 0
|
||||
else
|
||||
log_end_msg 1
|
||||
rm -f "<%= @pid_file %>"
|
||||
fi
|
||||
}
|
||||
|
||||
<%- @environment.each do |key, val| -%>
|
||||
export <%= key %>="<%= val %>"
|
||||
<%- end -%>
|
||||
export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
log_daemon_msg "Starting <%= @name %>"
|
||||
if [ -s "<%= @pid_file %>" ] && kill -0 "$(cat "<%= @pid_file %>")" >/dev/null 2>&1; then
|
||||
log_progress_msg "apparently already running"
|
||||
log_end_msg 0
|
||||
exit 0
|
||||
fi
|
||||
start
|
||||
;;
|
||||
|
||||
stop)
|
||||
log_daemon_msg "Stopping <%= @name %>"
|
||||
_stop
|
||||
log_end_msg "$?"
|
||||
rm -f "<%= @pid_file %>"
|
||||
;;
|
||||
|
||||
reload|force-reload)
|
||||
log_daemon_msg "Reloading <%= @name %>"
|
||||
_reload
|
||||
log_end_msg "$?"
|
||||
;;
|
||||
|
||||
restart)
|
||||
set +e
|
||||
log_daemon_msg "Restarting <%= @name %>"
|
||||
if [ -s "<%= @pid_file %>" ] && kill -0 "$(cat "<%= @pid_file %>")" >/dev/null 2>&1; then
|
||||
_stop || true
|
||||
sleep 1
|
||||
else
|
||||
log_warning_msg "<%= @name %> not running, attempting to start."
|
||||
rm -f "<%= @pid_file %>"
|
||||
fi
|
||||
start
|
||||
;;
|
||||
|
||||
status)
|
||||
set +e
|
||||
_status
|
||||
exit $?
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: /etc/init.d/<%= @name %> {start|stop|reload|force-reload|restart|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
49
cookbooks/poise-service/templates/default/upstart.conf.erb
Normal file
49
cookbooks/poise-service/templates/default/upstart.conf.erb
Normal file
@@ -0,0 +1,49 @@
|
||||
# <%= @name %> generated by poise-service for <%= @new_resource.to_s %>
|
||||
|
||||
description "<%= @name %>"
|
||||
|
||||
start on runlevel [2345]
|
||||
stop on runlevel [!2345]
|
||||
|
||||
respawn
|
||||
respawn limit 10 5
|
||||
umask 022
|
||||
chdir <%= @directory %>
|
||||
<%- @environment.each do |key, val| -%>
|
||||
env <%= key %>="<%= val %>"
|
||||
<%- end -%>
|
||||
<%- if @upstart_features[:setuid] -%>
|
||||
setuid <%= @user %>
|
||||
<%- end -%>
|
||||
<%- if @upstart_features[:kill_signal] -%>
|
||||
kill signal <%= @stop_signal %>
|
||||
<%- end -%>
|
||||
<%- if @upstart_features[:reload_signal] -%>
|
||||
reload signal <%= @reload_signal %>
|
||||
<%- end -%>
|
||||
|
||||
<%- if @upstart_features[:setuid] -%>
|
||||
exec <%= @command %>
|
||||
<%- else -%>
|
||||
script
|
||||
exec /opt/chef/embedded/bin/ruby <<EOH
|
||||
require 'etc'
|
||||
ent = Etc.getpwnam(<%= @user.inspect %>)
|
||||
if Process.euid != ent.uid || Process.egid != ent.gid
|
||||
Process.initgroups(ent.name, ent.gid)
|
||||
Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid
|
||||
Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid
|
||||
end
|
||||
ENV["HOME"] = Dir.home(<%= @user.inspect %>) rescue nil
|
||||
exec(*<%= Shellwords.split(@command).inspect %>)
|
||||
EOH
|
||||
end script
|
||||
<%- end -%>
|
||||
<%- if !@upstart_features[:kill_signal] && @stop_signal != 'TERM' -%>
|
||||
pre-stop script
|
||||
PID=`initctl status <%= @name %> | sed 's/^.*process \([0-9]*\)$/\1/'`
|
||||
if [ -n "$PID" ]; then
|
||||
kill -<%= @stop_signal %> "$PID"
|
||||
fi
|
||||
end script
|
||||
<%- end -%>
|
||||
Reference in New Issue
Block a user