diff --git a/CHANGELOG.md b/CHANGELOG.md index db9e0a1..42533b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ supervisor Cookbook CHANGELOG ============================= This file is used to list changes made in each version of the supervisor cookbook. +v0.4.13 +------- +- Compatibility with Chef 13 + v0.4.10 ------- diff --git a/README.md b/README.md index b617d7a..8db9e3c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ supervisor Cookbook =================== - -[![Build Status](https://travis-ci.org/poise/supervisor.svg?branch=master)](https://travis-ci.org/poise/supervisor) - Installs (Python) supervisor and provides resources to configure services @@ -12,7 +9,7 @@ Requirements Supports Debian and RHEL based systems. Tested on Ubuntu 12.04, 10.04, CentOS 6.5. ### Cookbooks -- python +- poise-python Attributes @@ -27,22 +24,8 @@ Attributes - `node['supervisor']['loglevel']` - the minimum severity for those log messages, default `'info'` - `node['supervisor']['minfds']` - The minimum number of file descriptors that must be available before supervisord will start successfully. - `node['supervisor']['minprocs']` - The minimum number of process descriptors that must be available before supervisord will start successfully. -- `node['supervisor']['nocleanup']` - If true, retain child log files at startup, the default is false -- `node['supervisor']['version']` - Sets the version of supervisor to install, must be 3.0+ to use minprocs, minfds and nocleanup. +- `node['supervisor']['version']` - Sets the version of supervisor to install, must be 3.0+ to use minprocs and minfds. - `node['supervisor']['socket_file']` - location of supervisor socket file. -- `node['supervisor']['ctlplugins']` - entries for `supervisorctl` plugins. - For instance, to install [serialrestart](https://pypi.python.org/pypi/supervisor-serialrestart), you'd manually add this to your config: - - ```text - [ctlplugin:serialrestart] - supervisor.ctl_factory = supervisorserialrestart.controllerplugin:make_serialrestart_controllerplugin - ``` - Which can be achieved using - ```ruby - node.default['supervisor']['ctlplugins'] = ({ - 'serialrestart'=> 'supervisorserialrestart.controllerplugin:make_serialrestart_controllerplugin' - }) - ``` Resources/Providers @@ -114,12 +97,15 @@ Includes the python recipe, installs the supervisor PIP package and sets up supe License & Authors ----------------- +- Author:: Nael Alolwani - Author:: Noah Kantrowitz - Author:: Gilles Devaux - Author:: Sam Clements - Author:: Chris Jerdonek ```text +Copyright:: 2017, Binary.com + Copyright:: 2011-2012, Opscode, Inc Copyright:: 2011, Formspring.me diff --git a/attributes/default.rb b/attributes/default.rb index 57947f1..9e30214 100644 --- a/attributes/default.rb +++ b/attributes/default.rb @@ -23,7 +23,7 @@ default['supervisor']['inet_username'] = nil default['supervisor']['inet_password'] = nil case node['platform_family'] -when "smartos" +when 'smartos' default['supervisor']['dir'] = '/opt/local/etc/supervisor.d' default['supervisor']['conffile'] = '/opt/local/etc/supervisord.conf' else @@ -36,6 +36,4 @@ default['supervisor']['loglevel'] = 'info' default['supervisor']['minfds'] = 1024 default['supervisor']['minprocs'] = 200 -default['supervisor']['nocleanup'] = false default['supervisor']['socket_file'] = '/var/run/supervisor.sock' -default['supervisor']['ctlplugins'] = {} diff --git a/libraries/matchers.rb b/libraries/matchers.rb index 153fb49..3652b46 100644 --- a/libraries/matchers.rb +++ b/libraries/matchers.rb @@ -19,51 +19,4 @@ def restart_supervisor_service(service_name) ChefSpec::Matchers::ResourceMatcher.new(:supervisor_service, :restart, service_name) end - def enable_supervisor_group(group_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_group, :enable, group_name) - end - - def disable_supervisor_group(group_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_group, :disable, group_name) - end - - def start_supervisor_group(group_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_group, :start, group_name) - end - - def stop_supervisor_group(group_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_group, :stop, group_name) - end - - def restart_supervisor_group(group_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_group, :restart, group_name) - end - - def reload_supervisor_group(group_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_group, :reload, group_name) - end - - def enable_supervisor_fcgi(fcgi_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_fcgi, :enable, fcgi_name) - end - - def disable_supervisor_fcgi(fcgi_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_fcgi, :disable, fcgi_name) - end - - def start_supervisor_fcgi(fcgi_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_fcgi, :start, fcgi_name) - end - - def stop_supervisor_fcgi(fcgi_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_fcgi, :stop, fcgi_name) - end - - def restart_supervisor_fcgi(fcgi_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_fcgi, :restart, fcgi_name) - end - - def reload_supervisor_fcgi(fcgi_name) - ChefSpec::Matchers::ResourceMatcher.new(:supervisor_fcgi, :reload, fcgi_name) - end end diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..f7c4244 --- /dev/null +++ b/metadata.json @@ -0,0 +1,38 @@ +{ + "name": "supervisor", + "version": "0.4.15", + "description": "Installs supervisor and provides resources to configure services", + "long_description": "", + "maintainer": "Noah Kantrowitz", + "maintainer_email": "noah@coderanger.net", + "license": "Apache 2.0", + "platforms": { + "ubuntu": ">= 0.0.0", + "debian": ">= 0.0.0", + "redhat": ">= 0.0.0", + "centos": ">= 0.0.0", + "fedora": ">= 0.0.0", + "amazon": ">= 0.0.0", + "smartos": ">= 0.0.0" + }, + "dependencies": { + "poise-python": ">= 0.0.0" + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "supervisor": "Installs and configures supervisord" + } +} diff --git a/metadata.rb b/metadata.rb deleted file mode 100644 index 7eee14e..0000000 --- a/metadata.rb +++ /dev/null @@ -1,14 +0,0 @@ -name "supervisor" -maintainer "Noah Kantrowitz" -maintainer_email "noah@coderanger.net" -license "Apache 2.0" -description "Installs supervisor and provides resources to configure services" -version "0.4.12" - -recipe "supervisor", "Installs and configures supervisord" - -depends "python" - -%w{ ubuntu debian redhat centos fedora amazon smartos raspbian }.each do |os| - supports os -end diff --git a/recipes/default.rb b/recipes/default.rb index b2df96f..4413aa8 100644 --- a/recipes/default.rb +++ b/recipes/default.rb @@ -17,103 +17,105 @@ # limitations under the License. # -include_recipe "python" +include_recipe 'poise-python' +python_runtime '2' # foodcritic FC023: we prefer not having the resource on non-smartos -if platform_family?("smartos") - package "py27-expat" do +if platform_family?('smartos') + package 'py27-expat' do action :install end end -python_pip "supervisor" do +python_package 'meld3' do action :upgrade - version node['supervisor']['version'] if node['supervisor']['version'] + version '1.0.2' +end + +python_package 'supervisor' do + action :upgrade + version '3.3.1' +end + +python_package 'setuptools' do + action :upgrade end directory node['supervisor']['dir'] do - owner "root" - group "root" - mode "755" + owner 'root' + group 'root' + mode '755' recursive true end template node['supervisor']['conffile'] do - source "supervisord.conf.erb" - owner "root" - group "root" - mode "644" - variables({ - :inet_port => node['supervisor']['inet_port'], - :inet_username => node['supervisor']['inet_username'], - :inet_password => node['supervisor']['inet_password'], - :supervisord_minfds => node['supervisor']['minfds'], - :supervisord_minprocs => node['supervisor']['minprocs'], - :supervisord_nocleanup => node['supervisor']['nocleanup'], - :supervisor_version => node['supervisor']['version'], - :socket_file => node['supervisor']['socket_file'], - }) + source 'supervisord.conf.erb' + owner 'root' + group 'root' + mode '644' + variables(inet_port: node['supervisor']['inet_port'], + inet_username: node['supervisor']['inet_username'], + inet_password: node['supervisor']['inet_password'], + supervisord_minfds: node['supervisor']['minfds'], + supervisord_minprocs: node['supervisor']['minprocs'], + supervisor_version: node['supervisor']['version'], + socket_file: node['supervisor']['socket_file']) end directory node['supervisor']['log_dir'] do - owner "root" - group "root" - mode "755" + owner 'root' + group 'root' + mode '755' recursive true end -template "/etc/default/supervisor" do - source "debian/supervisor.default.erb" - owner "root" - group "root" - mode "644" - only_if { platform_family?("debian") } +template '/etc/default/supervisor' do + source 'debian/supervisor.default.erb' + owner 'root' + group 'root' + mode '644' + only_if { platform_family?('debian') } end init_template_dir = value_for_platform_family( - ["rhel", "fedora", "centos", "amazon"] => "rhel", - "debian" => "debian" + %w(rhel fedora) => 'rhel', + 'debian' => 'debian' ) case node['platform'] -when "amazon", "centos", "debian", "fedora", "redhat", "ubuntu", "raspbian" - template "/etc/init.d/supervisor" do +when 'amazon', 'centos', 'debian', 'fedora', 'redhat', 'ubuntu' + template '/etc/init.d/supervisor' do source "#{init_template_dir}/supervisor.init.erb" - owner "root" - group "root" - mode "755" - variables({ - # TODO: use this variable in the debian platform-family template - # instead of altering the PATH and calling "which supervisord". - :supervisord => "#{node['python']['prefix_dir']}/bin/supervisord" - }) + owner 'root' + group 'root' + mode '755' end - service "supervisor" do - supports :status => true, :restart => true + service 'supervisor' do + supports status: true, restart: true, start: true, enable: true action [:enable, :start] end -when "smartos" - directory "/opt/local/share/smf/supervisord" do - owner "root" - group "root" - mode "755" +when 'smartos' + directory '/opt/local/share/smf/supervisord' do + owner 'root' + group 'root' + mode '755' end - template "/opt/local/share/smf/supervisord/manifest.xml" do - source "manifest.xml.erb" - owner "root" - group "root" - mode "644" - notifies :run, "execute[svccfg-import-supervisord]", :immediately + template '/opt/local/share/smf/supervisord/manifest.xml' do + source 'manifest.xml.erb' + owner 'root' + group 'root' + mode '644' + notifies :run, 'execute[svccfg-import-supervisord]', :immediately end - execute "svccfg-import-supervisord" do - command "svccfg import /opt/local/share/smf/supervisord/manifest.xml" + execute 'svccfg-import-supervisord' do + command 'svccfg import /opt/local/share/smf/supervisord/manifest.xml' action :nothing end - service "supervisord" do + service 'supervisord' do action [:enable] end end diff --git a/resources/service.rb b/resources/service.rb index 4e3f6ce..3cfb86c 100644 --- a/resources/service.rb +++ b/resources/service.rb @@ -18,43 +18,185 @@ # limitations under the License. # -actions :enable, :disable, :start, :stop, :restart -default_action :enable - -attribute :service_name, :kind_of => String, :name_attribute => true -attribute :command, :kind_of => String -attribute :process_name, :kind_of => String, :default => '%(program_name)s' -attribute :numprocs, :kind_of => Integer, :default => 1 -attribute :numprocs_start, :kind_of => Integer, :default => 0 -attribute :priority, :kind_of => Integer, :default => 999 -attribute :autostart, :kind_of => [TrueClass, FalseClass], :default => true -attribute :autorestart, :kind_of => [String, Symbol, TrueClass, FalseClass], :default => :unexpected -attribute :startsecs, :kind_of => Integer, :default => 1 -attribute :startretries, :kind_of => Integer, :default => 3 -attribute :exitcodes, :kind_of => Array, :default => [0, 2] -attribute :stopsignal, :kind_of => [String, Symbol], :default => :TERM -attribute :stopwaitsecs, :kind_of => Integer, :default => 10 -attribute :stopasgroup, :kind_of => [TrueClass,FalseClass], :default => nil -attribute :killasgroup, :kind_of => [TrueClass,FalseClass], :default => nil -attribute :user, :kind_of => [String, NilClass], :default => nil -attribute :redirect_stderr, :kind_of => [TrueClass, FalseClass], :default => false -attribute :stdout_logfile, :kind_of => String, :default => 'AUTO' -attribute :stdout_logfile_maxbytes, :kind_of => String, :default => '50MB' -attribute :stdout_logfile_backups, :kind_of => Integer, :default => 10 -attribute :stdout_capture_maxbytes, :kind_of => String, :default => '0' -attribute :stdout_events_enabled, :kind_of => [TrueClass, FalseClass], :default => false -attribute :stderr_logfile, :kind_of => String, :default => 'AUTO' -attribute :stderr_logfile_maxbytes, :kind_of => String, :default => '50MB' -attribute :stderr_logfile_backups, :kind_of => Integer, :default => 10 -attribute :stderr_capture_maxbytes, :kind_of => String, :default => '0' -attribute :stderr_events_enabled, :kind_of => [TrueClass, FalseClass], :default => false -attribute :environment, :kind_of => Hash, :default => {} -attribute :directory, :kind_of => [String, NilClass], :default => nil -attribute :umask, :kind_of => [NilClass, String], :default => nil -attribute :serverurl, :kind_of => String, :default => 'AUTO' - -attribute :eventlistener, :kind_of => [TrueClass,FalseClass], :default => false -attribute :eventlistener_buffer_size, :kind_of => Integer, :default => nil -attribute :eventlistener_events, :kind_of => Array, :default => nil +# property :name, name_property: true, kind_of: String, required: true, :name_property => true +# property :service_name, :kind_of => String +property :service_name, kind_of: String, name_property: true +property :command, kind_of: String +property :process_name, kind_of: String, default: '%(program_name)s' +property :numprocs, kind_of: Integer, default: 1 +property :numprocs_start, kind_of: Integer, default: 0 +property :priority, kind_of: Integer, default: 999 +property :autostart, kind_of: [TrueClass, FalseClass], default: true +property :autorestart, kind_of: [String, Symbol, TrueClass, FalseClass], default: :unexpected +property :startsecs, kind_of: Integer, default: 1 +property :startretries, kind_of: Integer, default: 3 +property :exitcodes, kind_of: Array, default: [0, 2] +property :stopsignal, kind_of: [String, Symbol], default: :TERM +property :stopwaitsecs, kind_of: Integer, default: 10 +property :stopasgroup, kind_of: [TrueClass, FalseClass], default: nil +property :killasgroup, kind_of: [TrueClass, FalseClass], default: nil +property :user, kind_of: [String, NilClass], default: nil +property :redirect_stderr, kind_of: [TrueClass, FalseClass], default: false +property :stdout_logfile, kind_of: String, default: 'AUTO' +property :stdout_logfile_maxbytes, kind_of: String, default: '50MB' +property :stdout_logfile_backups, kind_of: Integer, default: 10 +property :stdout_capture_maxbytes, kind_of: String, default: '0' +property :stdout_events_enabled, kind_of: [TrueClass, FalseClass], default: false +property :stderr_logfile, kind_of: String, default: 'AUTO' +property :stderr_logfile_maxbytes, kind_of: String, default: '50MB' +property :stderr_logfile_backups, kind_of: Integer, default: 10 +property :stderr_capture_maxbytes, kind_of: String, default: '0' +property :stderr_events_enabled, kind_of: [TrueClass, FalseClass], default: false +property :environment, kind_of: Hash, default: {} +property :directory, kind_of: [String, NilClass], default: nil +property :umask, kind_of: [NilClass, String], default: nil +property :serverurl, kind_of: String, default: 'AUTO' + +property :eventlistener, kind_of: [TrueClass, FalseClass], default: false +property :eventlistener_buffer_size, kind_of: Integer, default: nil +property :eventlistener_events, kind_of: Array, default: nil attr_accessor :state +attr_accessor :exists + +def load_current_value + state = get_current_state(name) +end + +action :enable do + converge_by("Enabling #{new_resource}") do + enable_service + end +end + +action :disable do + if state == 'UNAVAILABLE' + Chef::Log.info "#{new_resource} is already disabled." + else + converge_by("Disabling #{new_resource}") do + disable_service + end + end +end + +action :start do + case state + when 'UNAVAILABLE' + raise "Supervisor service #{name} cannot be started because it does not exist" + when 'RUNNING' + Chef::Log.debug "#{new_resource} is already started." + when 'STARTING' + Chef::Log.debug "#{new_resource} is already starting." + wait_til_state('RUNNING') + else + converge_by("Starting #{new_resource}") do + unless supervisorctl('start') + raise "Supervisor service #{name} was unable to be started" + end + end + end +end + +action :stop do + case state + when 'UNAVAILABLE' + raise "Supervisor service #{name} cannot be stopped because it does not exist" + when 'STOPPED' + Chef::Log.debug "#{new_resource} is already stopped." + when 'STOPPING' + Chef::Log.debug "#{new_resource} is already stopping." + wait_til_state('STOPPED') + else + converge_by("Stopping #{new_resource}") do + unless supervisorctl('stop') + raise "Supervisor service #{name} was unable to be stopped" + end + end + end +end + +action :restart do + case state + when 'UNAVAILABLE' + raise "Supervisor service #{name} cannot be restarted because it does not exist" + else + converge_by("Restarting #{new_resource}") do + unless supervisorctl('restart') + raise "Supervisor service #{name} was unable to be started" + end + end + end +end + +action_class.class_eval do + def enable_service + e = execute 'supervisorctl update' do + action :nothing + user 'root' + end + + t = template "#{node['supervisor']['dir']}/#{service_name}.conf" do + source 'program.conf.erb' + cookbook 'supervisor' + owner 'root' + group 'root' + mode '644' + variables prog: new_resource + notifies :run, 'execute[supervisorctl update]', :immediately + end + + t.run_action(:create) + e.run_action(:run) if t.updated? + end + + def disable_service + execute 'supervisorctl update' do + action :nothing + user 'root' + end + + file "#{node['supervisor']['dir']}/#{service_name}.conf" do + action :delete + notifies :run, 'execute[supervisorctl update]', :immediately + only_if { ::File.exist?("#{node['supervisor']['dir']}/#{service_name}.conf") } + end + end + + def supervisorctl(action) + cmd = "supervisorctl #{action} #{cmd_line_args} | grep -v ERROR" + result = Mixlib::ShellOut.new(cmd).run_command + # Since we append grep to the command + # The command will have an exit code of 1 upon failure + # So 0 here means it was successful + result.exitstatus == 0 + end + + def cmd_line_args + name = service_name + name += ':*' if process_name != '%(program_name)s' + name + end + + def get_current_state(service_name) + result = Mixlib::ShellOut.new('supervisorctl status').run_command + match = result.stdout.match("(^#{service_name}(\\:\\S+)?\\s*)([A-Z]+)(.+)") + if match.nil? + 'UNAVAILABLE' + else + match[3] + end + end + + def wait_til_state(state, max_tries = 20) + service = service_name + + max_tries.times do + return if get_current_state(service) == state + + Chef::Log.debug("Waiting for service #{service} to be in state #{state}") + sleep 1 + end + + raise "service #{service} not in state #{state} after #{max_tries} tries" + end +end diff --git a/templates/default/supervisord.conf.erb b/templates/default/supervisord.conf.erb index 80457b7..ca84f4e 100644 --- a/templates/default/supervisord.conf.erb +++ b/templates/default/supervisord.conf.erb @@ -29,10 +29,9 @@ pidfile=/var/run/supervisord.pid ; (supervisord p childlogdir=<%= @node['supervisor']['log_dir'] %> ; ('AUTO' child log dir, default $TEMP) <% if @supervisor_version && @supervisor_version.match(/^3/) %> -; minfds, minprocs & nocleanup first appeared in supervisord 3.0 +; minfds & minprocs first appeared in supervisord 3.0 minfds = <%= @supervisord_minfds %> minprocs = <%= @supervisord_minprocs %> -nocleanup=<%= @supervisord_nocleanup %> <% end %> ; the below section must remain in the config file for RPC @@ -50,13 +49,5 @@ serverurl=unix:///var/run//supervisor.sock ; use a unix:// URL for a unix socke ; interpreted as relative to this file. Included files *cannot* ; include files themselves. - -<% @node['supervisor']['ctlplugins'] && @node['supervisor']['ctlplugins'].each do |k,v| %> - -[ctlplugin:<%=k%>] -<%=v%> - -<%end%> - [include] files = <%= @node['supervisor']['dir'] %>/*.conf