├── .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 | [](https://supermarket.chef.io/cookbooks/pm2)
4 | [](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 | | Attribute |
28 | Type |
29 | Description |
30 | Default |
31 |
32 |
33 | | ['pm2']['node_version'] |
34 | String |
35 | Node version to install |
36 | 4.5.0 |
37 |
38 |
39 | | ['pm2']['pm2_version'] |
40 | String |
41 | PM2 node module version to install |
42 | latest |
43 |
44 |
45 | | ['pm2']['npm_version'] |
46 | String |
47 | NPM node module version to install |
48 | latest |
49 |
50 |
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 | | Action |
89 | Description |
90 | Notes |
91 |
92 |
93 | | :deploy |
94 | Create a json configuration file for a node application |
95 | All configuration files are deployed in /etc/pm2/conf.d |
96 |
97 |
98 | | :start |
99 | Start an application defined as a json file |
100 | It does not change the state of a running application - use one of the start_or_restart/start_or_reload methods instead |
101 |
102 |
103 | | :stop |
104 | Stop an application |
105 | Invokes the PM2 CLI to stop an application by name |
106 |
107 |
108 | | :restart |
109 | Restart an application |
110 | Invokes the PM2 CLI to restart an application by name only if it is running |
111 |
112 |
113 | | :reload |
114 | Reload an application |
115 | Invokes the PM2 CLI to reload an application by name only if it is running |
116 |
117 |
118 | | :graceful_reload |
119 | Gracefully reload an application |
120 | Invokes the PM2 CLI to gracefully reload an application by name only if it is running |
121 |
122 |
123 | | :start_or_restart |
124 | Start or restart an application |
125 | Invokes the PM2 CLI to start or restart an application by name |
126 |
127 |
128 | | :start_or_reload |
129 | Start or reload an application |
130 | Invokes the PM2 CLI to start or reload an application by name |
131 |
132 |
133 | | :start_or_graceful_reload |
134 | Start or gracefully reload an application |
135 | Invokes the PM2 CLI to start or gracefully reload an application by name |
136 |
137 |
138 | | :delete |
139 | Stop and delete an application json file |
140 | Invokes the PM2 CLI to stop an application and deletes the json file from the filesystem |
141 |
142 |
143 | | :startup |
144 | Configures PM2 to start on boot |
145 | Invokes the PM2 CLI to configure the startup process for the running platform |
146 |
147 |
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 | | Attribute |
158 | Type |
159 | Description |
160 | Required |
161 |
162 |
163 | | name |
164 | String |
165 | Name of the application - See PM2 documentation for reference - Note: this is the resource name attribute |
166 | Yes |
167 |
168 |
169 | | script |
170 | String |
171 | Node script to execute - See PM2 documentation for reference |
172 | Yes |
173 |
174 |
175 | | user |
176 | String |
177 | User to execute the node process - defaults to `root` |
178 | No |
179 |
180 |
181 | | home |
182 | String |
183 | Value of the PM2_HOME environmental variable - Note: a .pm2 directory will be appended to the PM2_HOME value if missing |
184 | No |
185 |
186 |
187 | | args |
188 | Array |
189 | See PM2 documentation for reference |
190 | No |
191 |
192 |
193 | | node_args |
194 | Array |
195 | See PM2 documentation for reference |
196 | No |
197 |
198 |
199 | | max_memory_restart |
200 | String |
201 | See PM2 documentation for reference |
202 | No |
203 |
204 |
205 | | instances |
206 | Integer |
207 | See PM2 documentation for reference |
208 | No |
209 |
210 |
211 | | log_file |
212 | String |
213 | See PM2 documentation for reference |
214 | No |
215 |
216 |
217 | | error_file |
218 | String |
219 | See PM2 documentation for reference |
220 | No |
221 |
222 |
223 | | out_file |
224 | String |
225 | See PM2 documentation for reference |
226 | No |
227 |
228 |
229 | | pid_file |
230 | String |
231 | See PM2 documentation for reference |
232 | No |
233 |
234 |
235 | | cron_restart |
236 | String |
237 | See PM2 documentation for reference |
238 | No |
239 |
240 |
241 | | cwd |
242 | String |
243 | Location of the node script to execute - See PM2 documentation for reference |
244 | Yes |
245 |
246 |
247 | | merge_logs |
248 | TrueClass/FalseClass |
249 | See PM2 documentation for reference |
250 | No |
251 |
252 |
253 | | ignore_watch |
254 | Array |
255 | See PM2 documentation for reference |
256 | No |
257 |
258 |
259 | | watch_options |
260 | Hash |
261 | See PM2 documentation for reference |
262 | No |
263 |
264 |
265 | | env |
266 | Hash |
267 | See PM2 documentation for reference |
268 | No |
269 |
270 |
271 | | log_data_format |
272 | String |
273 | See PM2 documentation for reference |
274 | No |
275 |
276 |
277 | | min_uptime |
278 | String |
279 | See PM2 documentation for reference |
280 | No |
281 |
282 |
283 | | max_restart |
284 | Integer |
285 | See PM2 documentation for reference |
286 | No |
287 |
288 |
289 | | exec_mode |
290 | String |
291 | See PM2 documentation for reference |
292 | No |
293 |
294 |
295 | | exec_interpreter |
296 | String |
297 | See PM2 documentation for reference |
298 | No |
299 |
300 |
301 | | write |
302 | TrueClass/FalseClass |
303 | See PM2 documentation for reference |
304 | No |
305 |
306 |
307 | | force |
308 | TrueClass/FalseClass |
309 | See PM2 documentation for reference |
310 | No |
311 |
312 |
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 |
--------------------------------------------------------------------------------