procsd 0.5.0 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|