Update cookbooks

This commit is contained in:
Greg Karékinian
2016-04-16 00:15:56 +02:00
parent 3854ab7232
commit c50b096c37
127 changed files with 1792 additions and 7431 deletions

View File

@@ -1,35 +0,0 @@
# Poise-Service Changelog
## 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!

View File

@@ -1,410 +0,0 @@
# Poise-Service Cookbook
[![Build Status](https://img.shields.io/travis/poise/poise-service.svg)](https://travis-ci.org/poise/poise-service)
[![Gem Version](https://img.shields.io/gem/v/poise-service.svg)](https://rubygems.org/gems/poise-service)
[![Cookbook Version](https://img.shields.io/cookbook/v/poise-service.svg)](https://supermarket.chef.io/cookbooks/poise-service)
[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-service.svg)](https://codecov.io/github/poise/poise-service)
[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-service.svg)](https://gemnasium.com/poise/poise-service)
[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](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. If unspecified it will be automatically allocated.
* `gid` GID of the group. If unspecified it will be automatically allocated.
* `home` Home directory of the user.
## 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.
* `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_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_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_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)*
### `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_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.
## 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.

View File

@@ -1,19 +0,0 @@
#
# 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'] = {}

View File

@@ -1,25 +0,0 @@
#
# 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

View File

@@ -1,18 +0,0 @@
#
# 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'

View File

@@ -1,20 +0,0 @@
#
# 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

View File

@@ -1,27 +0,0 @@
#
# 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

View File

@@ -1,162 +0,0 @@
#
# 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
# For root we always want the system root path.
unless user == 'root'
# Force a reload in case any users were created earlier in the run.
Etc.endpwent
home = begin
Dir.home(user)
rescue ArgumentError
nil
end
end
# Better than nothing
home || case node['platform_family']
when 'windows'
ENV.fetch('SystemRoot', 'C:\\')
else
'/'
end
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

View File

@@ -1,241 +0,0 @@
#
# 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('STOP', '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

View File

@@ -1,138 +0,0 @@
#
# 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
# 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 resource.
# Set to false to disable group creation.
# @return [String, false]
attribute(:group, kind_of: [String, FalseClass], name_attribute: true)
# @!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 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)
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.
def create_group
group new_resource.group do
gid new_resource.gid
system true
end
end
# Create the system user.
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 '/bin/false'
system true
uid new_resource.uid
end
end
# Remove the system group.
def remove_group
create_group.tap do |r|
r.action(:remove)
end
end
# Remove the system user.
def remove_user
create_user.tap do |r|
r.action(:remove)
end
end
end
end
end
end

View File

@@ -1,192 +0,0 @@
#
# 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.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

View File

@@ -1,38 +0,0 @@
#
# 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

View File

@@ -1,193 +0,0 @@
#
# 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
notify_if_service do
service_resource.run_action(:start)
end
end
def action_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.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 'root'
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 'poise-service'
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

View File

@@ -1,156 +0,0 @@
#
# 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)
def action_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:
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")
IO.write(pid_file, Process.pid)
Chef::Log.debug("[#{new_resource}] 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
end
Chef::Log.debug("[#{new_resource}] Changed privs to #{new_resource.user} (#{ent.uid}:#{ent.gid})")
# Split the command so we don't get an extra sh -c.
Chef::Log.debug("[#{new_resource}] Execing #{new_resource.command}")
Kernel.exec(*Shellwords.split(new_resource.command))
# Just in case, bail out.
exit!
# :nocov:
end
Chef::Log.debug("[#{new_resource}] Started.")
end
def action_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
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
end
end
end

View File

@@ -1,150 +0,0 @@
#
# 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

View File

@@ -1,83 +0,0 @@
#
# 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)
# Don't allow systemd under docker, it won't work in most cases.
return false if node['virtualization'] && %w{docker lxc}.include?(node['virtualization']['system'])
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,
})
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'])
end
end
def destroy_service
file "/etc/systemd/system/#{new_resource.service_name}.service" do
action :delete
end
end
end
end
end

View File

@@ -1,91 +0,0 @@
#
# 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'
Chef::Provider::Service::Redhat
else
# This will explode later in the template, but better than nothing for later.
Chef::Provider::Service::Init
end)
r.init_command(script_path)
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'],
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

View File

@@ -1,128 +0,0 @@
#
# 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)
# Don't allow upstart under docker, it won't work.
return false if node['virtualization'] && %w{docker lxc}.include?(node['virtualization']['system'])
service_resource_hints.include?(:upstart)
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
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

View File

@@ -1,45 +0,0 @@
#
# 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

View File

@@ -1,20 +0,0 @@
#
# 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.1.1'
end

View File

@@ -1,19 +0,0 @@
#
# 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"

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +0,0 @@
<%= {command: @command,
directory: @directory,
environment: @environment,
name: @name,
reload_signal: @reload_signal,
stop_signal: @stop_signal,
user: @user}.to_json %>

View File

@@ -1,15 +0,0 @@
#!/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}\"] = \"#{value}\"" }.join("; ") %>
exec(*<%= Shellwords.split(@command).inspect %>)
EOH

View File

@@ -1,13 +0,0 @@
[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 %>
[Install]
WantedBy=multi-user.target

View File

@@ -1,190 +0,0 @@
#!/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

View File

@@ -1,49 +0,0 @@
# <%= @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 -%>