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 +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +131 -36
- data/lib/procsd/cli.rb +117 -50
- data/lib/procsd/generator.rb +6 -1
- data/lib/procsd/templates/nginx.erb +4 -4
- data/lib/procsd/templates/service.erb +4 -0
- data/lib/procsd/version.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 595c0d49250c7eb5075f7c01b7e6f28814bdc319cca686ad0ee0b5a5cea09c28
|
4
|
+
data.tar.gz: de7f7d1df76a9ec2cfdb7c55a90ca87c89fcb387afb28addd52734f42bcf2193
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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$
|
196
|
+
deploy@server:~/sample_app$ procsd exec web
|
196
197
|
|
197
198
|
=> Booting Puma
|
198
|
-
=> Rails 5.2.1 application starting in
|
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:
|
204
|
-
* Listening on tcp://localhost:
|
204
|
+
* Environment: production
|
205
|
+
* Listening on tcp://localhost:2501
|
205
206
|
Use Ctrl-C to stop
|
206
207
|
```
|
207
208
|
|
208
|
-
|
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,
|
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
|
-
|
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
|
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 @
|
279
|
-
location @
|
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://
|
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
|
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
|
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
|
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
|
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:
|
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
|
-
|
22
|
-
|
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
|
-
|
109
|
-
|
110
|
-
say
|
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
|
-
|
120
|
-
|
121
|
-
say
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
141
|
-
|
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 :
|
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
|
-
|
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, "
|
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 :
|
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
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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
|
229
|
-
|
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)
|
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
|
-
|
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
|
-
|
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
|
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
|
data/lib/procsd/generator.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
19
|
-
location
|
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
|
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
|
data/lib/procsd/version.rb
CHANGED
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.
|
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:
|
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
|
-
|
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
|