├── .rspec ├── templates └── default │ └── application.json.erb ├── test ├── cookbooks │ └── pm2_application │ │ ├── metadata.rb │ │ ├── templates │ │ └── default │ │ │ └── test.js │ │ └── recipes │ │ └── default.rb └── integration │ └── default │ └── serverspec │ └── default_spec.rb ├── Berksfile ├── .travis.yml ├── attributes └── default.rb ├── .kitchen.yml ├── metadata.rb ├── .rubocop.yml ├── Gemfile ├── recipes └── default.rb ├── Guardfile ├── .gitignore ├── Rakefile ├── LICENSE ├── libraries └── matchers.rb ├── resources └── application.rb ├── providers └── application.rb └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --format progress 3 | --order rand 4 | -------------------------------------------------------------------------------- /templates/default/application.json.erb: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [<%= @config.to_json %>] 3 | } 4 | -------------------------------------------------------------------------------- /test/cookbooks/pm2_application/metadata.rb: -------------------------------------------------------------------------------- 1 | name 'pm2_application' 2 | version '0.0.0' 3 | 4 | depends 'pm2' 5 | -------------------------------------------------------------------------------- /Berksfile: -------------------------------------------------------------------------------- 1 | source 'https://supermarket.getchef.com' 2 | 3 | metadata 4 | 5 | cookbook 'pm2_application', path: './test/cookbooks/pm2_application' 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | cache: bundler 4 | bundler_args: --without development kitchen_vagrant 5 | rvm: 6 | - 2.3.0 7 | script: 8 | - bundle exec rake travis 9 | -------------------------------------------------------------------------------- /test/cookbooks/pm2_application/templates/default/test.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var server = http.createServer(function (request, response) { 3 | response.writeHead(200); 4 | }); 5 | server.listen(<%= @port %>); 6 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: pm2 3 | # Attributes:: default 4 | # 5 | # Copyright 2015, Mindera 6 | # 7 | 8 | default_unless['pm2']['pm2_version'] = 'latest' 9 | default_unless['pm2']['npm_version'] = 'latest' 10 | default_unless['pm2']['node_version'] = '4.5.0' 11 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | 5 | provisioner: 6 | name: chef_solo 7 | 8 | platforms: 9 | - name: centos-6.6 10 | - name: ubuntu-14.04 11 | - name: debian-7.8 12 | 13 | suites: 14 | - name: default 15 | run_list: 16 | - recipe[pm2_application::default] 17 | attributes: 18 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'pm2' 2 | maintainer 'Mindera' 3 | maintainer_email 'miguel.fonseca@mindera.com' 4 | license 'MIT' 5 | description 'Installs/Configures PM2' 6 | long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) 7 | version '0.7.4' 8 | 9 | depends 'poise-javascript', '~> 1.1.0' 10 | 11 | supports 'centos', '~> 6.0' 12 | supports 'redhat', '~> 6.0' 13 | supports 'ubuntu', '~> 14.0' 14 | supports 'debian', '~> 7.8' 15 | supports 'amazon' 16 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - Guardfile 4 | - 'vendor/**/*' 5 | - '.kitchen/**/*' 6 | LineLength: 7 | Max: 120 8 | HashSyntax: 9 | EnforcedStyle: hash_rockets 10 | Exclude: 11 | - Rakefile 12 | - Berksfile 13 | - 'spec/**/*' 14 | - 'test/**/*' 15 | Style/SpaceBeforeFirstArg: 16 | Enabled: false 17 | Style/WordArray: 18 | Exclude: 19 | - Rakefile 20 | Metrics/MethodLength: 21 | Enabled: false 22 | Lint/Eval: 23 | Enabled: false 24 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :development do 4 | gem 'rake', '< 11.0' 5 | gem 'rb-fsevent' 6 | gem 'guard', '~> 2.12.0' 7 | gem 'guard-rspec' 8 | gem 'guard-rubocop' 9 | gem 'berkshelf' 10 | end 11 | 12 | group :common do 13 | gem 'rspec', '~> 3.4' 14 | end 15 | 16 | group :lint do 17 | gem 'foodcritic', '~> 4.0' 18 | gem 'rubocop', '~> 0.29.0' 19 | end 20 | 21 | group :kitchen_common do 22 | gem 'test-kitchen', '~> 1.3.0' 23 | end 24 | 25 | group :kitchen_vagrant do 26 | gem 'kitchen-vagrant', '~> 0.16' 27 | end 28 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: pm2 3 | # Recipe:: default 4 | # 5 | # Copyright 2015, Mindera 6 | # 7 | 8 | # Constants 9 | NODE_VERSION = node['pm2']['node_version'] 10 | 11 | # Install node 12 | javascript_runtime 'node' do 13 | version NODE_VERSION 14 | end 15 | 16 | # Install node packages 17 | %w(pm2 npm).each do |pkg| 18 | node_package pkg do 19 | version node['pm2']["#{pkg}_version"] unless node['pm2']["#{pkg}_version"].nil? 20 | end 21 | end 22 | 23 | # Link executable into system path 24 | %w(node pm2 npm).each do |exe| 25 | link "/usr/bin/#{exe}" do 26 | to "/opt/nodejs-#{node['pm2']['node_version']}/bin/#{exe}" 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # Guardfile 2 | 3 | # TODO: commented because the guard-foodcritic plugin has deps issues 4 | # Foodcritic 5 | #guard :foodcritic, :cookbook_paths => '.', :all_on_start => false, :cli => '--epic-fail any' do 6 | # watch(%r{attributes/.+\.rb$}) 7 | # watch(%r{providers/.+\.rb$}) 8 | # watch(%r{recipes/.+\.rb$}) 9 | # watch(%r{resources/.+\.rb$}) 10 | # watch(%r{libraries/.+\.rb$}) 11 | # watch('metadata.rb') 12 | #end 13 | 14 | # ChefSpec 15 | guard :rspec, cmd: 'bundle exec rspec', all_on_start: true do 16 | watch(%r{^spec/.+_spec\.rb$}) 17 | watch(%r{^(recipes)/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 18 | watch('spec/spec_helper.rb') { 'spec' } 19 | end 20 | 21 | # RuboCop 22 | guard :rubocop do 23 | watch(%r{.+\.rb$}) 24 | watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) } 25 | end -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | metadata.json 2 | *.gem 3 | *.rbc 4 | .bundle 5 | .config 6 | coverage 7 | InstalledFiles 8 | lib/bundler/man 9 | pkg 10 | rdoc 11 | spec/reports 12 | test/tmp 13 | test/version_tmp 14 | tmp 15 | 16 | # Compiled Python files 17 | *.pyc 18 | 19 | # Folder view configuration files 20 | .DS_Store 21 | Desktop.ini 22 | 23 | # Thumbnail cache files 24 | ._* 25 | Thumbs.db 26 | 27 | # Files that might appear on external disks 28 | .Spotlight-V100 29 | .Trashes 30 | 31 | 32 | # YARD artifacts 33 | .yardoc 34 | _yardoc 35 | doc/ 36 | 37 | # Vagrant 38 | Vagrantfile 39 | .vagrant 40 | 41 | # Bundler 42 | Gemfile.lock 43 | bin/* 44 | .bundle/* 45 | 46 | .kitchen 47 | 48 | *~ 49 | *# 50 | \#*# 51 | .#* 52 | .*.sw[a-z] 53 | *.un~ 54 | /cookbooks 55 | .kitchen/ 56 | .kitchen.local.yml 57 | vendor/* 58 | 59 | # Berks 60 | Berksfile.lock 61 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | require 'rubocop/rake_task' 3 | require 'foodcritic' 4 | require 'kitchen' 5 | 6 | # RuboCop and Foodcritic 7 | namespace :lint do 8 | desc 'Run RuboCop checks' 9 | RuboCop::RakeTask.new(:rubocop) 10 | 11 | desc 'Run Foodcritic checks' 12 | FoodCritic::Rake::LintTask.new(:foodcritic) do |task| 13 | task.options = { fail_tags: ['any'], tags: [] } 14 | end 15 | end 16 | 17 | # Kitchen 18 | namespace :integration do 19 | namespace :kitchen do 20 | desc 'Run Kitchen with Vagrant' 21 | task :vagrant do 22 | Kitchen.logger = Kitchen.default_file_logger 23 | Kitchen::Config.new.instances.each do |instance| 24 | instance.test(:always) 25 | end 26 | end 27 | end 28 | end 29 | 30 | desc 'Run Lint tests' 31 | task lint: ['lint:rubocop', 'lint:foodcritic'] 32 | 33 | desc 'Run Integration tests' 34 | task integration: ['integration:kitchen:vagrant'] 35 | 36 | # Travis 37 | task travis: ['lint'] 38 | 39 | # Default 40 | task default: ['lint', 'integration'] 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mindera - Software Craft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/cookbooks/pm2_application/recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: pm2_application_cookbook-test 3 | # Recipe:: default 4 | # 5 | # Copyright 2015, Mindera 6 | # 7 | 8 | include_recipe 'pm2::default' 9 | 10 | nodejs_dir = '/tmp' 11 | 12 | %w( 13 | nodeuser 14 | nodeuserwithpm2home 15 | ).each do |u| 16 | user u do 17 | home "/home/#{u}" 18 | manage_home true 19 | action :create 20 | end 21 | end 22 | 23 | template "#{nodejs_dir}/test.js" do 24 | variables('port' => 8000) 25 | source 'test.js' 26 | notifies :graceful_reload, 'pm2_application[test]', :delayed 27 | end 28 | 29 | template "#{nodejs_dir}/test_w_user.js" do 30 | variables('port' => 8001) 31 | source 'test.js' 32 | end 33 | 34 | template "#{nodejs_dir}/test_w_user_w_pm2_home.js" do 35 | variables('port' => 8002) 36 | source 'test.js' 37 | end 38 | 39 | pm2_application 'test' do 40 | script 'test.js' 41 | cwd nodejs_dir 42 | action [:deploy, :start_or_reload, :startup] 43 | end 44 | 45 | pm2_application 'test_w_user_w_pm2_home' do 46 | script 'test_w_user_w_pm2_home.js' 47 | cwd nodejs_dir 48 | user 'nodeuserwithpm2home' 49 | home '/home/nodeuserwithpm2home/.pm2' 50 | action [:deploy, :start_or_reload, :startup] 51 | end 52 | 53 | pm2_application 'test_w_user' do 54 | script 'test_w_user.js' 55 | cwd nodejs_dir 56 | user 'nodeuser' 57 | home '/home/nodeuser' 58 | action [:deploy, :start_or_reload, :startup] 59 | end 60 | -------------------------------------------------------------------------------- /libraries/matchers.rb: -------------------------------------------------------------------------------- 1 | if defined?(ChefSpec) 2 | def start_pm2_application(name) 3 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :start, name) 4 | end 5 | 6 | def stop_pm2_application(name) 7 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :stop, name) 8 | end 9 | 10 | def restart_pm2_application(name) 11 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :restart, name) 12 | end 13 | 14 | def reload_pm2_application(name) 15 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :reload, name) 16 | end 17 | 18 | def graceful_reload_pm2_application(name) 19 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :graceful_reload, name) 20 | end 21 | 22 | def start_or_restart_pm2_application(name) 23 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :start_or_restart, name) 24 | end 25 | 26 | def start_or_reload_pm2_application(name) 27 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :start_or_reload, name) 28 | end 29 | 30 | def start_or_graceful_reload_pm2_application(name) 31 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :start_or_graceful_reload, name) 32 | end 33 | 34 | def deploy_pm2_application(name) 35 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :deploy, name) 36 | end 37 | 38 | def delete_pm2_application(name) 39 | ChefSpec::Matchers::ResourceMatcher.new(:pm2_application, :delete, name) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/integration/default/serverspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | require 'serverspec' 2 | 3 | # Required by serverspec 4 | set :backend, :exec 5 | 6 | RSpec.configure do |c| 7 | c.before :all do 8 | c.path = '/sbin:/usr/sbin:$PATH' 9 | end 10 | end 11 | 12 | describe command('node --version') do 13 | its(:exit_status) { should eq 0 } 14 | end 15 | 16 | describe command('npm --version') do 17 | its(:exit_status) { should eq 0 } 18 | end 19 | 20 | describe command('pm2 --version') do 21 | its(:exit_status) { should eq 0 } 22 | end 23 | 24 | describe file('/etc/pm2/conf.d/test.json') do 25 | it { should be_file } 26 | it { should contain 'test.js' } 27 | end 28 | 29 | describe file('/etc/pm2/conf.d/test_w_user.json') do 30 | it { should be_file } 31 | it { should contain 'test_w_user.js' } 32 | end 33 | 34 | describe file('/home/nodeuser/.pm2') do 35 | it { should be_directory } 36 | end 37 | 38 | describe file('/home/nodeuserwithpm2home/.pm2/pm2.pid') do 39 | it { should be_file } 40 | end 41 | 42 | describe command('su root -c "pm2 info test"') do 43 | its(:stdout) { should contain 'online' } 44 | end 45 | 46 | describe command('su nodeuser -c "pm2 info test_w_user"') do 47 | its(:stdout) { should contain 'online' } 48 | end 49 | 50 | describe command('su nodeuserwithpm2home -c "pm2 info test_w_user_w_pm2_home"') do 51 | its(:stdout) { should contain 'online' } 52 | end 53 | 54 | describe command('ls /etc/rc3.d/S*pm2-init.sh') do 55 | its(:exit_status) { should eq 0 } 56 | end 57 | 58 | describe file('/etc/init.d/pm2-init.sh') do 59 | it { should be_file } 60 | it { should contain 'export PM2_HOME="/home/nodeuser/.pm2"' } 61 | end 62 | 63 | describe file('/home/nodeuser/.pm2/dump.pm2') do 64 | it { should be_file } 65 | end 66 | -------------------------------------------------------------------------------- /resources/application.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: pm2 3 | # Resource:: application 4 | # 5 | # Copyright 2015, Mindera 6 | # 7 | 8 | actions :deploy, 9 | :start, 10 | :stop, 11 | :restart, 12 | :reload, 13 | :graceful_reload, 14 | :start_or_restart, 15 | :start_or_reload, 16 | :start_or_graceful_reload, 17 | :delete, 18 | :startup 19 | 20 | default_action [:deploy, :start_or_restart, :startup] 21 | 22 | attribute :name, :kind_of => String, :name_attribute => true 23 | attribute :script, :kind_of => String, :required => true 24 | attribute :user, :kind_of => String, :default => 'root' 25 | attribute :home, :kind_of => String 26 | # args can be an array or a string in pm2 so we map it to a ruby Array 27 | attribute :args, :kind_of => Array 28 | # node_args can be an array or a string in pm2 so we map it to a ruby Array 29 | attribute :node_args, :kind_of => Array 30 | attribute :max_memory_restart, :kind_of => String 31 | attribute :instances, :kind_of => Integer 32 | # log_file can be a bool or string in pm2 so we map it to a ruby Array 33 | attribute :log_file, :kind_of => String 34 | attribute :error_file, :kind_of => String 35 | attribute :out_file, :kind_of => String 36 | attribute :pid_file, :kind_of => String 37 | attribute :cron_restart, :kind_of => String 38 | attribute :cwd, :kind_of => String, :required => true 39 | attribute :merge_logs, :kind_of => [TrueClass, FalseClass] 40 | attribute :watch, :kind_of => [TrueClass, FalseClass] 41 | # ignore_watch can be an array or string in pm2 so we map it to a ruby Array 42 | attribute :ignore_watch, :kind_of => Array 43 | # watch_options a js object in pm2 so we map it to a ruby Hash 44 | attribute :watch_options, :kind_of => Hash 45 | # env is a js object or string in pm2 so we map it to a ruby Hash 46 | attribute :env, :kind_of => Hash 47 | attribute :log_date_format, :kind_of => String 48 | # min_uptime is a string or number in pm2 so we map it to a ruby String 49 | attribute :min_uptime, :kind_of => String 50 | attribute :max_restarts, :kind_of => Integer 51 | attribute :exec_mode, :kind_of => String 52 | attribute :exec_interpreter, :kind_of => String 53 | attribute :write, :kind_of => [TrueClass, FalseClass] 54 | attribute :force, :kind_of => [TrueClass, FalseClass] 55 | -------------------------------------------------------------------------------- /providers/application.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: pm2 3 | # Provider:: application 4 | # 5 | # Copyright 2015, Mindera 6 | # 7 | 8 | use_inline_resources 9 | 10 | action :deploy do 11 | Chef::Log.info "Deploying pm2 application #{new_resource.name}" 12 | 13 | # Save resource config 14 | config = {} 15 | resource_attrs.each do |key| 16 | config[key] = eval("new_resource.#{key}") unless eval("new_resource.#{key}.nil?") 17 | end 18 | 19 | # Make sure the pm2 config dir exist 20 | directory '/etc/pm2/conf.d' do 21 | recursive true 22 | owner 'root' 23 | group 'root' 24 | action :create 25 | end 26 | 27 | # Deploy pm2 application json config 28 | template pm2_config do 29 | source 'application.json.erb' 30 | variables(:config => config) 31 | cookbook 'pm2' 32 | mode '0644' 33 | backup false 34 | end 35 | end 36 | 37 | action :start do 38 | Chef::Log.info "Starting pm2 application #{new_resource.name}" 39 | 40 | # Start pm2 application pm2_config = "/etc/pm2/conf.d/#{resource.name}.json" 41 | pm2_command("start #{pm2_config}") unless pm2_app_online? 42 | end 43 | 44 | action :delete do 45 | Chef::Log.info "Deleting pm2 application #{new_resource.name}" 46 | 47 | # Stop pm2 application 48 | pm2_command("stop #{new_resource.name}") if pm2_app_online? 49 | 50 | # Remove pm2 application json config 51 | file pm2_config do 52 | action :delete 53 | only_if { ::File.exist?(pm2_config) } 54 | end 55 | end 56 | 57 | action :stop do 58 | Chef::Log.info "Stopping pm2 application #{new_resource.name}" 59 | 60 | # Stop pm2 application 61 | pm2_command("stop #{new_resource.name}") if pm2_app_online? 62 | end 63 | 64 | action :restart do 65 | Chef::Log.info "Restarting pm2 application #{new_resource.name}" 66 | 67 | # Restart pm2 application 68 | pm2_command("restart #{new_resource.name}") 69 | end 70 | 71 | action :reload do 72 | Chef::Log.info "Reloading pm2 application #{new_resource.name}" 73 | 74 | # Reload pm2 application 75 | pm2_command("reload #{new_resource.name}") 76 | end 77 | 78 | action :graceful_reload do 79 | Chef::Log.info "Gracefully reloading pm2 application #{new_resource.name}" 80 | 81 | # Gracefully reload pm2 application 82 | pm2_command("gracefulReload #{new_resource.name}") if pm2_app_online? 83 | end 84 | 85 | action :start_or_restart do 86 | Chef::Log.info "Start or restart pm2 application #{new_resource.name}" 87 | 88 | # Start or restart pm2 application 89 | pm2_command("startOrRestart #{pm2_config}") 90 | end 91 | 92 | action :start_or_reload do 93 | Chef::Log.info "Start or reload pm2 application #{new_resource.name}" 94 | 95 | # Start or reload pm2 application 96 | pm2_command("startOrReload #{pm2_config}") 97 | end 98 | 99 | action :start_or_graceful_reload do 100 | Chef::Log.info "Start or gracefully reload pm2 application #{new_resource.name}" 101 | 102 | # Start or gracefully reload pm2 application 103 | pm2_command("startOrGracefulReload #{pm2_config}") 104 | end 105 | 106 | action :startup do 107 | Chef::Log.info "Start or gracefully reload pm2 application #{new_resource.name}" 108 | 109 | # Set startup based on platform 110 | cmd = "pm2 startup #{node['platform']}" 111 | # Add the user option if doing it as a different user 112 | cmd << " -u #{new_resource.user}" 113 | execute cmd do 114 | environment pm2_environment 115 | command cmd 116 | end 117 | 118 | # Save running processes 119 | cmd = 'pm2 save' 120 | execute cmd do 121 | environment pm2_environment 122 | command cmd 123 | end 124 | end 125 | 126 | def pm2_config 127 | "/etc/pm2/conf.d/#{new_resource.name}.json" 128 | end 129 | 130 | def pm2_command(pm2_command) 131 | cmd = "pm2 #{pm2_command}" 132 | execute cmd do 133 | command cmd 134 | user new_resource.user 135 | environment pm2_environment 136 | end 137 | end 138 | 139 | def pm2_app_online? 140 | cmd = shell_out!('pm2 list', 141 | :user => new_resource.user, 142 | :returns => 0, 143 | :environment => pm2_environment) 144 | !cmd.stdout.match(new_resource.name).nil? 145 | end 146 | 147 | def pm2_home 148 | if new_resource.home.nil? 149 | "#{::Dir.home(new_resource.user)}/.pm2" 150 | elsif %r{/\.pm2/*$} =~ new_resource.home 151 | new_resource.home 152 | else 153 | "#{new_resource.home}/.pm2" 154 | end 155 | end 156 | 157 | def pm2_environment 158 | { 'PM2_HOME' => pm2_home } 159 | end 160 | 161 | def resource_attrs 162 | %w( 163 | name 164 | script 165 | args 166 | env 167 | node_args 168 | max_memory_restart 169 | instances 170 | log_file 171 | error_file 172 | out_file 173 | pid_file 174 | cron_restart 175 | cwd 176 | merge_logs 177 | watch 178 | ignore_watch 179 | watch_options 180 | log_date_format 181 | min_uptime 182 | max_restarts 183 | exec_mode 184 | exec_interpreter 185 | write 186 | force 187 | ) 188 | end 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PM2 Cookbook 2 | 3 | [![Cookbook](https://img.shields.io/cookbook/v/pm2.svg)](https://supermarket.chef.io/cookbooks/pm2) 4 | [![Build Status](https://travis-ci.org/Mindera/pm2-cookbook.svg?branch=master)](https://travis-ci.org/Mindera/pm2-cookbook) 5 | 6 | Chef coookbook to install and manage [PM2](https://github.com/Unitech/pm2). 7 | 8 | ## Requirements 9 | 10 | Depends on the cookbooks: 11 | 12 | * [poise-javascript](https://github.com/poise/poise-javascript) 13 | 14 | ## Platforms 15 | 16 | * Centos 6+ 17 | * Amazon Linux 18 | * Ubuntu 14+ 19 | * Debian 7+ 20 | 21 | ## Attributes 22 | 23 | ### default.rb 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
AttributeTypeDescriptionDefault
['pm2']['node_version']StringNode version to install4.5.0
['pm2']['pm2_version']StringPM2 node module version to installlatest
['pm2']['npm_version']StringNPM node module version to installlatest
51 | 52 | ## Recipes 53 | 54 | ### default.rb 55 | 56 | Installs PM2 as a global node module using a specific version if specified in the `default` attributes described above. 57 | 58 | ### Example 59 | 60 | To install PM2: 61 | 62 | Add the `pm2` cookbook as a dependency: 63 | 64 | ```ruby 65 | depends 'pm2' 66 | ``` 67 | 68 | Include the `pm2::default` recipe: 69 | 70 | ```ruby 71 | include_recipe 'pm2::default' 72 | ``` 73 | 74 | ## Providers 75 | 76 | ### pm2_application 77 | 78 | The `pm2_application` provider manages a json configuration file for a node application and controls it with PM2. 79 | 80 | It only start processes from a json configuration (located in `/etc/pm2/conf.d`) and it does not support starting processes by calling the PM2 CLI directly. 81 | 82 | #### Actions 83 | 84 | The available actions try to represent some of the PM2 CLI control [actions](https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#raw-examples). 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 |
ActionDescriptionNotes
:deployCreate a json configuration file for a node applicationAll configuration files are deployed in /etc/pm2/conf.d
:startStart an application defined as a json fileIt does not change the state of a running application - use one of the start_or_restart/start_or_reload methods instead
:stopStop an applicationInvokes the PM2 CLI to stop an application by name
:restartRestart an applicationInvokes the PM2 CLI to restart an application by name only if it is running
:reloadReload an applicationInvokes the PM2 CLI to reload an application by name only if it is running
:graceful_reloadGracefully reload an applicationInvokes the PM2 CLI to gracefully reload an application by name only if it is running
:start_or_restartStart or restart an applicationInvokes the PM2 CLI to start or restart an application by name
:start_or_reloadStart or reload an applicationInvokes the PM2 CLI to start or reload an application by name
:start_or_graceful_reloadStart or gracefully reload an applicationInvokes the PM2 CLI to start or gracefully reload an application by name
:deleteStop and delete an application json fileInvokes the PM2 CLI to stop an application and deletes the json file from the filesystem
:startupConfigures PM2 to start on bootInvokes the PM2 CLI to configure the startup process for the running platform
148 | 149 | If no action is specified then the default action `[:deploy, :start_or_restart, :startup]` will be used. 150 | 151 | #### Attributes 152 | 153 | The available attributes try to represent the PM2 json definition options [schema](https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#schema). 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 |
AttributeTypeDescriptionRequired
nameStringName of the application - See PM2 documentation for reference - Note: this is the resource name attributeYes
scriptStringNode script to execute - See PM2 documentation for referenceYes
userStringUser to execute the node process - defaults to `root`No
homeStringValue of the PM2_HOME environmental variable - Note: a .pm2 directory will be appended to the PM2_HOME value if missingNo
argsArraySee PM2 documentation for referenceNo
node_argsArraySee PM2 documentation for referenceNo
max_memory_restartStringSee PM2 documentation for referenceNo
instancesIntegerSee PM2 documentation for referenceNo
log_fileStringSee PM2 documentation for referenceNo
error_fileStringSee PM2 documentation for referenceNo
out_fileStringSee PM2 documentation for referenceNo
pid_fileStringSee PM2 documentation for referenceNo
cron_restartStringSee PM2 documentation for referenceNo
cwdStringLocation of the node script to execute - See PM2 documentation for referenceYes
merge_logsTrueClass/FalseClassSee PM2 documentation for referenceNo
ignore_watchArraySee PM2 documentation for referenceNo
watch_optionsHashSee PM2 documentation for referenceNo
envHashSee PM2 documentation for referenceNo
log_data_formatStringSee PM2 documentation for referenceNo
min_uptimeStringSee PM2 documentation for referenceNo
max_restartIntegerSee PM2 documentation for referenceNo
exec_modeStringSee PM2 documentation for referenceNo
exec_interpreterStringSee PM2 documentation for referenceNo
writeTrueClass/FalseClassSee PM2 documentation for referenceNo
forceTrueClass/FalseClassSee PM2 documentation for referenceNo
313 | 314 | #### Example 315 | 316 | To deploy and start (or restart) a `test.js` application that lives in `/tmp/test.js`: 317 | 318 | Install PM2 as described in the `Recipes` example above. 319 | 320 | Use the `pm2_application` provider - most basic example: 321 | 322 | ```ruby 323 | pm2_application 'test' do 324 | script 'test.js' 325 | cwd '/tmp' 326 | action [:deploy, :start_or_restart] 327 | end 328 | ``` 329 | 330 | This will deploy a `/etc/pm2/conf.d/test.json` configuration file and start/restart the application with PM2. 331 | 332 | #### References 333 | 334 | * PM2 advanced [documentation](https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md) 335 | 336 | ## Development / Contributing 337 | 338 | ### Dependencies 339 | 340 | * [Ruby](https://www.ruby-lang.org) 341 | * [Bundler](http://bundler.io) 342 | 343 | ### Installation 344 | 345 | ```bash 346 | $ git clone git@github.com:Mindera/pm2-cookbook.git 347 | $ cd pm2-cookbook 348 | $ bundle install 349 | ``` 350 | 351 | ### Tests 352 | 353 | To run lint tests (rubocop, foodcritic): 354 | ```bash 355 | $ bundle exec rake lint 356 | ``` 357 | 358 | To run integration tests (kitchen-ci, serverspec): 359 | ```bash 360 | $ bundle exec rake integration 361 | ``` 362 | --------------------------------------------------------------------------------