procsd 0.5.0 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da11e4c031739d5f93c9b4b4135da9f8589fbe04348ae9dad0a87b212e3de7ec
4
- data.tar.gz: cea50b63954f02b21f713e6658feec63017b5718024b0447be5975b628d1ada9
3
+ metadata.gz: 595c0d49250c7eb5075f7c01b7e6f28814bdc319cca686ad0ee0b5a5cea09c28
4
+ data.tar.gz: de7f7d1df76a9ec2cfdb7c55a90ca87c89fcb387afb28addd52734f42bcf2193
5
5
  SHA512:
6
- metadata.gz: b758ae08dbaa3682314f5e378e3b2db1396d73e47e7af096a3176dda7b00a59b040e028ef5af7712068dfcfb1e944367184f29606eb8d30d4af44a9198d49722
7
- data.tar.gz: 95ca90f04d4f7a4c2e4569f526920cf91dd18b6fd7a431f17b69bb6509d6c6fc0d210c3e9c8ae96411c1429a9533ad80009fa4f413acef34a57acd0112bf2536
6
+ metadata.gz: c410272886ffe376089154a6499ccb9da9634732b01b8c84c45627e6a3c5ff73b47106c6680095a3529f01d3f94ab928828c882a50b8f39e5164004063c14bae
7
+ data.tar.gz: 6d9d84f9fd041b28a543dad0d458a5feed9ac0867062a82cd2af7926adda5d2e91be0162f972154b87cbb51442f1c49aadfb76dee30932406e593648568e0207
data/CHANGELOG.md CHANGED
@@ -1,4 +1,24 @@
1
1
  # CHANGELOG
2
+ ## 0.5.5
3
+ * Add: Allow to start/stop/restart particular service in the app target (example: `$ procsd start web`)
4
+ * Add: RuntimeMaxSec option for process
5
+
6
+ ## 0.5.4
7
+ * Add: information how to use SSL integration with Cloudflare CDN enabled
8
+ * Add: procsd config certbot_command command
9
+
10
+ ## 0.5.3
11
+ * Fix: procsd config sudoers command
12
+ * Add: procsd config services command
13
+
14
+ ## 0.5.2
15
+ * Fix: use uniq app name in Nginx config files
16
+ * Add: custom option public_folder_path (Nginx)
17
+ * Add: --dev option for exec command (to require dev_environment in development mode)
18
+
19
+ ## 0.5.1
20
+ * Fix bug `uninitialized constant Procsd::Generator::Pathname`, thanks to @koppen
21
+
2
22
  ## 0.5.0
3
23
  * **Breaking change:** Changed the way how to define SSL option for Ngnix configuration in procsd.yml (and by default contact email is not required anymore)
4
24
 
data/README.md CHANGED
@@ -8,7 +8,7 @@ Can we have something similar on the cheap Ubuntu VPS from DigitalOcean? Yes we
8
8
 
9
9
  ## Getting started
10
10
 
11
- > **Note:** latest version of Procsd is `0.5.0`. Since version `0.4.0` there are some breaking changes. Check the [CHANGELOG.md](CHANGELOG.md). To update to the latest version, run `$ gem update procsd` or `$ bundle update procsd` (if you have already installed procsd).
11
+ > **Note:** latest version of Procsd is `0.5.3`. Since version `0.4.0` there are some breaking changes. Check the [CHANGELOG.md](CHANGELOG.md). To update to the latest version, run `$ gem update procsd` or `$ bundle update procsd` (if you have already installed procsd).
12
12
 
13
13
  > **Note:** Procsd works best with Capistrano integration: [vifreefly/capistrano-procsd](https://github.com/vifreefly/capistrano-procsd)
14
14
 
@@ -64,19 +64,20 @@ You can provide additional options for `create` command:
64
64
  * `--dir` - application's working directory, default is current _$PWD_ env variable
65
65
  * `--path` - $PATH to include to the each service. Default is current _$PATH_ env variable
66
66
  * `--add-to-sudoers` - if option present, procsd will create sudoers rule file `/etc/sudoers.d/app_name` which allow to start/stop/restart app services without a password prompt (passwordless sudo).
67
- * `--or-restart` - if option provided and services already created, procsd will skip creation and call instead `restart` command. Otherwise (if services are not present), they will be created and (in additional) started.
67
+ * `--or-restart` - if option provided and services already created, procsd will skip creation and call instead `restart` command. Otherwise (if services are not present), they will be created and (in additional) started. It's useful option for deployment tools like Capistrano, Mina, etc.
68
68
 
69
69
 
70
70
  ### Start application
71
71
  > Other control commands: `stop` and `restart`
72
72
 
73
+ > You can start/stop/restart a particular process by providing it's name, i.e.: `$ procsd restart worker`
74
+
73
75
  ```
74
76
  deploy@server:~/sample_app$ procsd start
75
77
 
76
78
  Started app services (sample_app.target)
77
79
  ```
78
80
 
79
-
80
81
  ### Check the status
81
82
  > You can filter processes, like `$ procsd status worker` (show status only for worker processes) or `$ procsd status worker.2` (show status only for worker.2 process)
82
83
 
@@ -192,25 +193,47 @@ Systemd provides [a lot of possibilities](https://www.digitalocean.com/community
192
193
  Currently, procsd can not run all processes in development like `foreman start` does. But you can run one single process using `procsd exec` command:
193
194
 
194
195
  ```
195
- deploy@server:~/sample_app$ PORT=3000 procsd exec web
196
+ deploy@server:~/sample_app$ procsd exec web
196
197
 
197
198
  => Booting Puma
198
- => Rails 5.2.1 application starting in development
199
+ => Rails 5.2.1 application starting in production
199
200
  => Run `rails server -h` for more startup options
200
201
  Puma starting in single mode...
201
202
  * Version 3.12.0 (ruby 2.3.0-p0), codename: Llamas in Pajamas
202
203
  * Min threads: 5, max threads: 5
203
- * Environment: development
204
- * Listening on tcp://localhost:3000
204
+ * Environment: production
205
+ * Listening on tcp://localhost:2501
205
206
  Use Ctrl-C to stop
206
207
  ```
207
208
 
208
- By default `procsd exec` skip environment variables defined in `procsd.yml`. To run process with production environment, provide `--env` option as well: `procsd exec web --env`.
209
+ `procsd exec` requres all the environment variables defined in `environment` section of `procsd.yml` config file.
210
+
211
+ Sometimes in development mode you need different environment configuration. For that you can add additional environment section `dev_environment` and require it as well using `--dev` flag, example:
212
+
213
+ ```yaml
214
+ app: sample_app
215
+ environment:
216
+ PORT: 2501
217
+ RAILS_ENV: production
218
+ RAILS_LOG_TO_STDOUT: true
219
+ dev_environment:
220
+ RAILS_ENV: development
221
+ SOME_OTHER_DEV_ENV_VARIABLE: value
222
+ ```
223
+
224
+ ```
225
+ # Run web process with all environment & dev_environment variables included:
226
+
227
+ deploy@server:~/sample_app$ procsd exec web --dev
228
+ ```
229
+
230
+ > In case if `dev_environment` has env variable with the same name like in `environment`, this variable will be rewritten with value from `dev_environment`.
231
+
209
232
 
210
233
  ### Nginx integration (with automatic HTTPS)
211
234
  > Before make sure that you have Nginx installed `sudo apt install nginx` and running `sudo systemctl status nginx`.
212
235
 
213
- If one of your application processes is a web process, you can automatically setup Nginx (reverse proxy) config for it. Why? For example to serve static files (assets, images, etc) directly using fast Nginx, rather than application server. Or to enable SSL support (see below).
236
+ If one of your application processes is a web process, you can automatically setup Nginx (reverse proxy) config for it. Why? For example to serve static files (assets, images, and all other files located in `public` folder or another customly defined folder) directly using fast Nginx, rather than application server. Or to enable SSL support (see below).
214
237
 
215
238
  Add to your procsd.yml `nginx` section with `server_name` option defined:
216
239
 
@@ -218,20 +241,20 @@ Add to your procsd.yml `nginx` section with `server_name` option defined:
218
241
 
219
242
  > If your application use multiple domains/subdomains, add all of them separated with space: `server_name: my-domain.com us.my-domain.com uk.my-domain.com`
220
243
 
221
- > It's required to provide `PORT` number environment variable in `procsd.yml`. Provided port number will be used to proxy requests from Nginx to application server.
222
-
223
- ```yml
244
+ ```yaml
224
245
  app: sample_app
225
246
  processes:
226
247
  web: bundle exec rails server -p $PORT
227
248
  worker: bundle exec sidekiq -e $RAILS_ENV
228
249
  formation: web=1,worker=2
229
250
  environment:
230
- PORT: 2501
251
+ PORT: 2501 # PORT will be used by Nginx to proxy requests from 0.0.0.0:80/443 to 127.0.0.1:PORT (required)
252
+ HOST: localhost # Make sure that your application server in production running on 127.0.0.1, not 0.0.0.0
231
253
  RAILS_ENV: production
232
254
  RAILS_LOG_TO_STDOUT: true
233
255
  nginx:
234
256
  server_name: my-domain.com
257
+ public_folder_path: public # path is relative to the main project directory, default value is `public`.
235
258
  ```
236
259
 
237
260
  Configuration is done! Run [procsd create](#create-an-application-export-to-systemd) to create app services with Nginx reverse proxy config:
@@ -258,7 +281,7 @@ Nginx config created and daemon reloaded
258
281
  <summary>/etc/nginx/sites-available/sample_app</summary>
259
282
 
260
283
  ```
261
- upstream app {
284
+ upstream sample_app {
262
285
  server 127.0.0.1:2501;
263
286
  }
264
287
 
@@ -275,14 +298,14 @@ server {
275
298
  add_header Cache-Control public;
276
299
  }
277
300
 
278
- try_files $uri/index.html $uri @app;
279
- location @app {
301
+ try_files $uri/index.html $uri @sample_app;
302
+ location @sample_app {
280
303
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
281
304
  proxy_set_header Host $http_host;
282
305
  proxy_set_header X-Forwarded-Proto $scheme;
283
306
  proxy_set_header X-Real-IP $remote_addr;
284
307
  proxy_redirect off;
285
- proxy_pass http://app;
308
+ proxy_pass http://sample_app;
286
309
  }
287
310
 
288
311
  client_max_body_size 256M;
@@ -298,13 +321,13 @@ Everything is done. Start app services (`procsd start`) and go to `http://my-dom
298
321
 
299
322
  #### Auto SSL using Certbot
300
323
 
301
- To generate Nginx config with free SSL certificate (from [Let’s Encrypt](https://letsencrypt.org/)) included, you need to install [Certbot](https://certbot.eff.org/) on the remote server first:
324
+ To generate Nginx config with free SSL certificate (from [Let’s Encrypt](https://letsencrypt.org/)) included, you need to install [Certbot](https://certbot.eff.org/) on the remote server first. For Ubuntu 18.04 (check here instructions for other versions https://certbot.eff.org/lets-encrypt/ubuntufocal-nginx):
302
325
 
303
- ```
304
- sudo apt install software-properties-common
305
- sudo add-apt-repository ppa:certbot/certbot
306
- sudo apt update
307
- sudo apt-get install python-certbot-nginx
326
+ ```bash
327
+ $ sudo apt install software-properties-common
328
+ $ sudo add-apt-repository ppa:certbot/certbot
329
+ $ sudo apt update
330
+ $ sudo apt install certbot python-certbot-nginx
308
331
  ```
309
332
 
310
333
  > When you install certbot, it automatically setup a cron job (twice per day) to renew expiring certificates ([Automated Renewals](https://certbot.eff.org/docs/using.html#automated-renewals)), so you don't have to worry about renewing certificates manually.
@@ -318,7 +341,7 @@ nginx:
318
341
  ssl: true # added
319
342
  ```
320
343
 
321
- Configuration is done. **Make sure that all domains defined in procsd (nginx.server_name) are pointing to the server IP** where application is hosted. Then run `procsd create` as usual:
344
+ Configuration is done. **Make sure that all domains defined in procsd (nginx.server_name) are pointing to the server IP** where application is hosted. Then run `procsd create` _(you will probably need first run `procsd destroy` if app services already exists)_ as usual:
322
345
 
323
346
  > By default Certbot obtaining certificate from _Let's Encrypt_ without a contact email. If you want to provide contact email, define env variable `CERTBOT_EMAIL` with your email in the `.env` file.
324
347
 
@@ -386,26 +409,82 @@ Successfully installed SSL cert using certbot
386
409
  That's it. Start app services (`procsd start`) and go to `https://my-domain.com` where you'll see your application proxying with Nginx and SSL enabled.
387
410
 
388
411
 
412
+ <details/>
413
+ <summary>Note about using Cloudflare CDN</summary><br>
414
+
415
+ If you use Cloudflare CDN, that means the process of obtaining Let's Encrypt SSL Certificate will fail. To fix it, install `python-certbot-dns-cloudflare` package:
416
+
417
+ ```bash
418
+ $ sudo apt install certbot python-certbot-dns-cloudflare
419
+ ```
420
+
421
+ Read instructions [here how to get Cloudflare API Token and obtain certificates](https://certbot-dns-cloudflare.readthedocs.io/en/stable/). In short,
422
+
423
+ **1)** Go to https://dash.cloudflare.com/profile/api-tokens and [get your API Token](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-).
424
+
425
+ **2)** Create on the server `~/.secrets/certbot/` directory with `cloudflare.ini` file inside:
426
+
427
+ ```bash
428
+ $ mkdir -p ~/.secrets/certbot/
429
+ $ chmod 0700 ~/.secrets/
430
+ $ touch ~/.secrets/certbot/cloudflare.ini
431
+ $ chmod 0400 ~/.secrets/certbot/cloudflare.ini
432
+ ```
433
+
434
+ **3)** Put inside `cloudflare.ini` file your Cloudflare token:
435
+
436
+ ```bash
437
+ $ sudo nano ~/.secrets/certbot/cloudflare.ini
438
+ ```
439
+
440
+ ```bash
441
+ # ~/.secrets/certbot/cloudflare.ini
442
+
443
+ # Cloudflare API token (example) used by Certbot:
444
+ dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567
445
+ ```
446
+
447
+ **4)** Obtain certificates for all your domains declared in `procsd.yml` using _certbot-dns-cloudflare_ plugin:
448
+
449
+ ```bash
450
+ # Example command for my-domain.com domain:
451
+
452
+ $ sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini -d my-domain.com
453
+ ```
454
+
455
+ **5)** If all went fine, update Nginx application config with new certificates (using certbot command). To get required certbot command type `$ procsd config certbot_command`, then execute it:
456
+
457
+ ```bash
458
+ # Example command for my-domain.com domain:
459
+
460
+ $ sudo certbot --agree-tos --no-eff-email --redirect --non-interactive --nginx -d my-domain.com --register-unsafely-without-email
461
+ ```
462
+
463
+ All is done!
464
+
465
+ </details><br>
466
+
467
+
389
468
  ## All available commands
390
469
 
391
470
  ```
392
471
  $ procsd --help
393
472
 
394
473
  Commands:
395
- procsd --version, -v # Print the version
396
- procsd config # Print config files based on current settings. Available types: sudoers
397
474
  procsd create # Create and enable app services
398
475
  procsd destroy # Stop, disable and remove app services
399
- procsd disable # Disable app target
476
+ procsd start # Start app services
477
+ procsd stop # Stop app services
478
+ procsd restart # Restart app services
400
479
  procsd enable # Enable app target
401
- procsd exec # Run app process
402
- procsd help [COMMAND] # Describe available commands or one specific command
403
- procsd list # List all app services
480
+ procsd disable # Disable app target
404
481
  procsd logs # Show app services logs
405
- procsd restart # Restart app services
406
- procsd start # Start app services
407
482
  procsd status # Show app services status
408
- procsd stop # Stop app services
483
+ procsd list # List all app services
484
+ procsd exec # Run single app process with environment
485
+ procsd config # Print config files based on current settings. Available types: sudoers, services, certbot_command
486
+ procsd help [COMMAND] # Describe available commands or one specific command
487
+ procsd --version, -v # Print the version
409
488
  ```
410
489
 
411
490
 
@@ -425,6 +504,7 @@ Commands:
425
504
  ## Notes
426
505
 
427
506
  * If you want to set environment variables per process, [use format](https://github.com/ddollar/foreman/wiki/Per-Process-Environment-Variables) like Foreman recommends.
507
+
428
508
  * To print commands before execution, provide env variable `VERBOSE=true` before procsd command. Example:
429
509
 
430
510
  ```
@@ -437,11 +517,12 @@ Execute: journalctl --no-pager --no-hostname --all --output short-iso -n 3 --uni
437
517
  2018-11-04T19:11:59+0400 sample_app-worker.2[29907]: 2018-11-04T15:11:59.597Z 29907 TID-gne5aeyuz INFO: Booting Sidekiq 5.2.2 with redis options {:id=>"Sidekiq-server-PID-29907", :url=>nil}
438
518
  2018-11-04T19:11:59+0400 sample_app-worker.2[29907]: 2018-11-04T15:11:59.601Z 29907 TID-gne5aeyuz INFO: Starting processing, hit Ctrl-C to stop
439
519
  ```
520
+
440
521
  * You can use extended format of processes commands inside `procsd.yml` to provide additional restart/stop commands for each process:
441
522
 
442
- > All possible options: `ExecStart`, `ExecReload` and `ExecStop`
523
+ > All possible options: [ExecStart](https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=) (default command to start a process), [ExecReload](https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecReload=), and [ExecStop](https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStop=).
443
524
 
444
- > If procsd.yml has `processes:` option defined, then content of Procfile will be ignored
525
+ > If procsd.yml has `processes:` option defined, then content of Procfile (if it exists) will be ignored.
445
526
 
446
527
  ```yml
447
528
  app: sample_app
@@ -454,6 +535,21 @@ processes:
454
535
 
455
536
  Why? For example default Ruby on Rails application server [Puma](http://puma.io/) supports [Phased or Rolling restart](https://github.com/puma/puma/blob/master/docs/restart.md#normal-vs-hot-vs-phased-restart) feature. If you provide separate `ExecReload`command for a process, then this command will be called while executing `$ procsd restart` by systemd instead of just killing and starting process again.
456
537
 
538
+ * Another option you can provide for each process is [RuntimeMaxSec](https://www.freedesktop.org/software/systemd/man/systemd.service.html#RuntimeMaxSec=). It is used to automatically restart a process every N period of time. Could be useful for worker types of processes (like Sidekiq) where process memory could increase while running:
539
+
540
+ > Example values for RuntimeMaxSec: `30s` (30 seconds), `5m` (5 minutes), `3h` (3 hours), `1d` (1 day).
541
+
542
+ ```yml
543
+ app: sample_app
544
+ processes:
545
+ web:
546
+ ExecStart: bundle exec rails server -p $PORT
547
+ RuntimeMaxSec: 12h
548
+ ```
549
+
550
+ * If you use Nginx integration but default Nginx requests timeout (60s) is too small for you, [you can set a custom timeout](https://serverfault.com/a/777753) in the global Nginx config.
551
+
552
+
457
553
  ## Capistrano integration
458
554
 
459
555
  https://github.com/vifreefly/capistrano-procsd
@@ -461,7 +557,6 @@ https://github.com/vifreefly/capistrano-procsd
461
557
 
462
558
  ## ToDo
463
559
  * Add `procsd update` command to quickly update changed configuration (application units, nginx config, etc), instead of calling two separate commands (`procsd destroy` and `procsd create`)
464
- * Add integration with [Inspeqtor](https://github.com/mperham/inspeqtor) to monitor application services and get alert notifications if something happened
465
560
 
466
561
 
467
562
  ## License
data/lib/procsd/cli.rb CHANGED
@@ -18,12 +18,16 @@ module Procsd
18
18
  preload!
19
19
  if @config[:nginx]
20
20
  raise ConfigurationError, "Can't find nginx executable available" unless in_path?("nginx")
21
- unless Dir.exist?(File.join options["dir"], "public")
22
- raise ConfigurationError, "Missing public/ folder to use with Nginx"
21
+
22
+ public_folder_path = @config[:nginx]["public_folder_path"] || "public"
23
+ unless Dir.exist?(File.join options["dir"], public_folder_path)
24
+ raise ConfigurationError, "Missing public folder path to use with Nginx"
23
25
  end
26
+
24
27
  unless @config.dig(:environment, "PORT")
25
28
  raise ConfigurationError, "Please provide PORT environment variable in procsd.yml to use with Nginx"
26
29
  end
30
+
27
31
  if @config[:nginx]["ssl"]
28
32
  raise ConfigurationError, "Can't find certbot executable available" unless in_path?("certbot")
29
33
  end
@@ -70,7 +74,7 @@ module Procsd
70
74
  execute %W(sudo rm #{path}) and say "Deleted: #{path}" if File.exist?(path)
71
75
  end
72
76
 
73
- execute %w(sudo systemctl restart nginx)
77
+ execute %w(sudo systemctl reload-or-restart nginx)
74
78
  say("Nginx config removed and daemon reloaded", :green)
75
79
  end
76
80
  else
@@ -101,44 +105,67 @@ module Procsd
101
105
  end
102
106
 
103
107
  desc "start", "Start app services"
104
- def start
108
+ def start(service_name = nil)
105
109
  preload!
106
110
  say_target_not_exists and return unless target_exist?
107
111
 
108
- say "Note: app target #{target_name} already started/active" if target_active?
109
- if execute %W(sudo systemctl start #{target_name})
110
- say("Started app services (#{target_name})", :green)
112
+ if service_name
113
+ full_name = to_full_name(service_name)
114
+ say "Note: app service #{full_name} already started/active" if service_active?(full_name)
115
+ if execute %W(sudo systemctl start #{full_name} --all)
116
+ say("Started app service (#{full_name})", :green)
117
+ end
118
+ else
119
+ say "Note: app target #{target_name} already started/active" if target_active?
120
+ if execute %W(sudo systemctl start #{target_name})
121
+ say("Started app services (#{target_name})", :green)
122
+ end
111
123
  end
112
124
  end
113
125
 
114
126
  desc "stop", "Stop app services"
115
- def stop
127
+ def stop(service_name = nil)
116
128
  preload!
117
129
  say_target_not_exists and return unless target_exist?
118
130
 
119
- say "Note: app target #{target_name} already stopped/inactive" if !target_active?
120
- if execute %W(sudo systemctl stop #{target_name})
121
- say("Stopped app services (#{target_name})", :green)
131
+ if service_name
132
+ full_name = to_full_name(service_name)
133
+ say "Note: app service #{full_name} already stopped/inactive" if !service_active?(full_name)
134
+ if execute %W(sudo systemctl stop #{full_name} --all)
135
+ say("Stopped app service (#{full_name})", :green)
136
+ end
137
+ else
138
+ say "Note: app target #{target_name} already stopped/inactive" if !target_active?
139
+ if execute %W(sudo systemctl stop #{target_name})
140
+ say("Stopped app services (#{target_name})", :green)
141
+ end
122
142
  end
123
143
  end
124
144
 
125
145
  desc "restart", "Restart app services"
126
- def restart
146
+ def restart(service_name = nil)
127
147
  preload!
128
148
  say_target_not_exists and return unless target_exist?
129
149
 
130
- # If one of the child services of a target has `ExecReload` and `ReloadPropagatedFrom`
131
- # options defined, then use `reload-or-restart` to call all services (not the main target)
132
- # because of systemd bug https://github.com/systemd/systemd/issues/10638
133
- success =
134
- if has_reload?
135
- execute %W(sudo systemctl reload-or-restart #{app_name}-* --all)
136
- else
137
- execute %W(sudo systemctl restart #{target_name})
150
+ if service_name
151
+ full_name = to_full_name(service_name)
152
+ if execute %W(sudo systemctl reload-or-restart #{full_name} --all)
153
+ say("Restarted app service (#{full_name})", :green)
138
154
  end
155
+ else
156
+ # If one of the child services of a target has `ExecReload` and `ReloadPropagatedFrom`
157
+ # options defined, then use `reload-or-restart` to call all services (not the main target)
158
+ # because of systemd bug https://github.com/systemd/systemd/issues/10638
159
+ success =
160
+ if has_reload?
161
+ execute %W(sudo systemctl reload-or-restart #{app_name}-* --all)
162
+ else
163
+ execute %W(sudo systemctl restart #{target_name})
164
+ end
139
165
 
140
- if success
141
- say("Restarted app services (#{target_name})", :green)
166
+ if success
167
+ say("Restarted app services (#{target_name})", :green)
168
+ end
142
169
  end
143
170
  end
144
171
 
@@ -155,7 +182,7 @@ module Procsd
155
182
  command = %w(systemctl status --no-pager --output short-iso --all)
156
183
  end
157
184
 
158
- command << (options["target"] ? target_name : "#{app_name}-#{service_name}*")
185
+ command << (options["target"] ? target_name : to_full_name(service_name))
159
186
  execute command, type: :exec
160
187
  end
161
188
 
@@ -188,33 +215,49 @@ module Procsd
188
215
  execute command, type: :exec
189
216
  end
190
217
 
191
- desc "config", "Print config files based on current settings. Available types: sudoers"
218
+ desc "config", "Print config files based on current settings. Available types: sudoers, services, certbot_command"
192
219
  def config(name)
193
220
  preload!
194
221
 
222
+ options = { "user" => ENV["USER"], "dir" => ENV["PWD"], "path" => `/bin/bash -ilc 'echo $PATH'`.strip }
223
+ generator = Generator.new(@config, options)
224
+
195
225
  case name
196
226
  when "sudoers"
197
- say generate_sudoers_rule(ENV["USER"])
227
+ puts generator.generate_sudoers(options["user"], has_reload: has_reload?)
228
+ when "services"
229
+ return unless valid_create_options?(options)
230
+
231
+ services = generator.generate_units
232
+ services.each do |service_name, service_data|
233
+ puts "Service: #{service_name} (size: #{service_data[:size]}):"
234
+ puts "---\n\n"
235
+ puts service_data[:content]
236
+ puts "---\n\n"
237
+ end
238
+ when "certbot_command"
239
+ puts get_certbot_command.join(' ')
198
240
  else
199
- raise ArgumentError, "Wring type of argument: #{name}"
241
+ raise ArgumentError, "Wrong type of argument: #{name}"
200
242
  end
201
243
  end
202
244
 
203
245
  map exec: :__exec
204
- desc "exec", "Run app process"
205
- option :env, type: :boolean, banner: "Require environment defined in procsd.yml"
246
+ desc "exec", "Run single app process with environment"
247
+ option :dev, type: :boolean, banner: "Require dev_environment (in additional to base env) defined in procsd.yml"
206
248
  def __exec(process_name)
207
249
  preload!
208
250
 
209
251
  start_cmd = @config[:processes].dig(process_name, "commands", "ExecStart")
210
252
  raise ArgumentError, "Process is not defined: #{process_name}" unless start_cmd
211
253
 
212
- if options["env"]
213
- @config[:environment].each { |k, v| @config[:environment][k] = v.to_s }
214
- exec @config[:environment], start_cmd
215
- else
216
- exec start_cmd
254
+ process_env = @config[:environment].each { |k, v| @config[:environment][k] = v.to_s }
255
+ if options["dev"]
256
+ dev_env = @config[:dev_environment].each { |k, v| @config[:dev_environment][k] = v.to_s }
257
+ process_env.merge!(dev_env)
217
258
  end
259
+
260
+ exec process_env, start_cmd
218
261
  end
219
262
 
220
263
  map %w[--version -v] => :__print_version
@@ -225,16 +268,23 @@ module Procsd
225
268
 
226
269
  private
227
270
 
228
- def perform_create
229
- options.each do |key, value|
271
+ def valid_create_options?(opts)
272
+ opts.each do |key, value|
230
273
  next unless %w(user dir path).include? key
231
274
  if value.nil? || value.empty?
232
- say("Can't fetch value for --#{key}, please provide it's as an argument", :red) and return
275
+ say("Can't fetch value for --#{key}, please provide it's as an argument", :red)
276
+ return false
233
277
  else
234
278
  say("Value of the --#{key} option: #{value}", :yellow)
235
279
  end
236
280
  end
237
281
 
282
+ true
283
+ end
284
+
285
+ def perform_create
286
+ return unless valid_create_options?(options)
287
+
238
288
  generator = Generator.new(@config, options)
239
289
  generator.generate_units(save: true)
240
290
 
@@ -267,31 +317,38 @@ module Procsd
267
317
 
268
318
  if nginx = @config[:nginx]
269
319
  generator.generate_nginx_conf(save: true)
270
- execute %w(sudo systemctl restart nginx)
271
- say("Nginx config created and daemon reloaded", :green)
320
+ say("Nginx config created", :green)
272
321
 
273
322
  # Reference: https://certbot.eff.org/docs/using.html#certbot-command-line-options
274
323
  # How it works in Caddy https://caddyserver.com/docs/automatic-https
275
324
  if nginx["ssl"]
276
- command = %w(sudo certbot --agree-tos --no-eff-email --redirect --non-interactive --nginx)
277
- nginx["server_name"].split(" ").map(&:strip).each do |domain|
278
- command.push("-d", domain)
279
- end
280
-
281
- if email = ENV["CERTBOT_EMAIL"]
282
- command.push("--email", email)
283
- else
284
- command << "--register-unsafely-without-email"
285
- end
286
-
325
+ certbot_command = get_certbot_command
287
326
  say "Trying to obtain SSL certificate for Nginx config using Certbot..."
288
- if execute command
327
+ if execute certbot_command
289
328
  say("Successfully installed SSL cert using Certbot", :green)
290
329
  else
291
330
  msg = "Failed to install SSL cert using Certbot. Make sure that all provided domains are pointing to this server IP."
292
331
  say(msg, :red)
293
332
  end
294
333
  end
334
+
335
+ if execute %w(sudo systemctl reload-or-restart nginx)
336
+ say("Nginx daemon reloaded", :green)
337
+ end
338
+ end
339
+ end
340
+
341
+ def get_certbot_command
342
+ command = %w(sudo certbot --agree-tos --no-eff-email --redirect --non-interactive --nginx)
343
+
344
+ @config[:nginx]["server_name"].split(" ").map(&:strip).each do |domain|
345
+ command.push("-d", domain)
346
+ end
347
+
348
+ if email = ENV["CERTBOT_EMAIL"]
349
+ command.push("--email", email)
350
+ else
351
+ command << "--register-unsafely-without-email"
295
352
  end
296
353
  end
297
354
 
@@ -342,10 +399,18 @@ module Procsd
342
399
  system "systemctl", "is-active", "--quiet", target_name
343
400
  end
344
401
 
402
+ def service_active?(service_name)
403
+ system "systemctl", "is-active", "--quiet", service_name
404
+ end
405
+
345
406
  def target_name
346
407
  "#{app_name}.target"
347
408
  end
348
409
 
410
+ def to_full_name(service_name)
411
+ "#{app_name}-#{service_name}*"
412
+ end
413
+
349
414
  def app_name
350
415
  @config[:app]
351
416
  end
@@ -394,6 +459,8 @@ module Procsd
394
459
  end
395
460
 
396
461
  @config[:environment] = procsd["environment"] || {}
462
+ @config[:dev_environment] = procsd["dev_environment"] || {}
463
+
397
464
  @config[:systemd_dir] = procsd["systemd_dir"] || DEFAULT_SYSTEMD_DIR
398
465
  @config[:nginx] = procsd["nginx"]
399
466
  end
@@ -1,3 +1,5 @@
1
+ require 'pathname'
2
+
1
3
  module Procsd
2
4
  class Generator
3
5
  attr_reader :app_name, :target_name
@@ -66,8 +68,11 @@ module Procsd
66
68
  end
67
69
 
68
70
  def generate_nginx_conf(save: false)
69
- root_path = File.join(@options["dir"], "public")
71
+ public_folder_path = @config[:nginx]["public_folder_path"] || "public"
72
+ root_path = File.join(@options["dir"], public_folder_path)
73
+
70
74
  content = generate_template("nginx", {
75
+ app_name: @config[:app],
71
76
  port: @config[:environment]["PORT"],
72
77
  server_name: @config[:nginx]["server_name"],
73
78
  root: root_path,
@@ -1,4 +1,4 @@
1
- upstream app {
1
+ upstream <%= config[:app_name] %> {
2
2
  server 127.0.0.1:<%= config[:port] %>;
3
3
  }
4
4
 
@@ -15,14 +15,14 @@ server {
15
15
  add_header Cache-Control public;
16
16
  }
17
17
 
18
- try_files $uri/index.html $uri @app;
19
- location @app {
18
+ try_files $uri/index.html $uri @<%= config[:app_name] %>;
19
+ location @<%= config[:app_name] %> {
20
20
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
21
21
  proxy_set_header Host $http_host;
22
22
  proxy_set_header X-Forwarded-Proto $scheme;
23
23
  proxy_set_header X-Real-IP $remote_addr;
24
24
  proxy_redirect off;
25
- proxy_pass http://app;
25
+ proxy_pass http://<%= config[:app_name] %>;
26
26
  }
27
27
 
28
28
  client_max_body_size 256M;
@@ -18,6 +18,10 @@ ExecStop=/bin/bash -lc '<%= stop %>'
18
18
  ExecReload=/bin/bash -lc '<%= reload %>'
19
19
  <% end -%>
20
20
 
21
+ <% if max_sec = config["commands"]["RuntimeMaxSec"] -%>
22
+ RuntimeMaxSec=<%= max_sec %>
23
+ <% end -%>
24
+
21
25
  Restart=always
22
26
  RestartSec=1
23
27
  TimeoutStopSec=30
@@ -1,3 +1,3 @@
1
1
  module Procsd
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.5"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: procsd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Afanasev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-20 00:00:00.000000000 Z
11
+ date: 2021-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -124,8 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
126
  requirements: []
127
- rubyforge_project:
128
- rubygems_version: 2.7.6
127
+ rubygems_version: 3.0.3
129
128
  signing_key:
130
129
  specification_version: 4
131
130
  summary: Manage your application processes in production hassle-free like Heroku CLI