procsd 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +222 -26
- data/lib/procsd/cli.rb +142 -102
- data/lib/procsd/generator.rb +92 -29
- data/lib/procsd/templates/nginx.erb +39 -0
- data/lib/procsd/templates/service.erb +7 -7
- data/lib/procsd/templates/target.erb +1 -1
- data/lib/procsd/version.rb +1 -1
- data/lib/procsd.rb +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 862f5699d79e6ac90e5c946ba08d13b190bde81c1c2f015bd9aea7ca2b797d2e
|
4
|
+
data.tar.gz: 4713b4d70b167bb999727bb20869568a6e15e3c91d9197f4c062eb4e384b9c9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 222767562ebc5727f52d5c26b95dbfaf2de2dca0f0901e49f5ac8b35513a50052ab848fcc23f7bb69c2740789c101c5a6258de73d5651ec6a0a75e4f9c81f0f9
|
7
|
+
data.tar.gz: 93000122413fc6d307a7436a5f614c0188c6172e6d8f6330fe2a57c7bd472d1bb6e414cd9076e1d9f3a8cad3c0e1411c1a7103fcffd894f510d9aff67dad2602
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,26 @@
|
|
1
1
|
# CHANGELOG
|
2
|
+
## 0.4.0
|
3
|
+
* **Breaking change:** commands in extended processes syntax were renamed from start/restart/stop to ExecStart/ExecReload/ExecStop:
|
4
|
+
|
5
|
+
Was:
|
6
|
+
```yml
|
7
|
+
processes:
|
8
|
+
web:
|
9
|
+
start: bundle exec rails server -p $PORT
|
10
|
+
restart: bundle exec pumactl phased-restart
|
11
|
+
```
|
12
|
+
|
13
|
+
Now:
|
14
|
+
```yml
|
15
|
+
processes:
|
16
|
+
web:
|
17
|
+
ExecStart: bundle exec rails server -p $PORT
|
18
|
+
ExecReload: bundle exec pumactl phased-restart
|
19
|
+
```
|
20
|
+
|
21
|
+
* Added new command `exec` to run one of the defined processes (for development purposes). Example: `$ procsd exec web`.
|
22
|
+
* Added Nginx support with auto-ssl (using Certbot)
|
23
|
+
|
2
24
|
## 0.3.0
|
3
25
|
* **Breaking change:** `.procsd.yml` renamed to `procsd.yml` (without dot)
|
4
26
|
* **Breaking change:** `environment` option in the procsd.yml now has hash format, not array:
|
data/README.md
CHANGED
@@ -2,15 +2,17 @@
|
|
2
2
|
|
3
3
|
I do like the way how simple is managing of application processes in production on Heroku with [Procfile](https://devcenter.heroku.com/articles/procfile). How easily can be accessed application logs with [heroku logs](https://devcenter.heroku.com/articles/logging) command. Just type `heroku create` and you're good to go.
|
4
4
|
|
5
|
-
Can we have something similar on the cheap Ubuntu VPS from DigitalOcean? Yes we can, all we need is a
|
5
|
+
Can we have something similar on the cheap Ubuntu VPS from DigitalOcean? Yes we can, all we need is a **systemd wrapper** which allows to export application processes from Procfile to system services, and control them/check status/access logs using simple commands.
|
6
6
|
|
7
|
-
> These days most of Linux distributions (including Ubuntu) has
|
7
|
+
> These days most of Linux distributions (including Ubuntu) has systemd as a default system processes manager. That's why it is a good idea to use systemd for managing application processes in production (for simple cases).
|
8
8
|
|
9
9
|
## Getting started
|
10
10
|
|
11
|
-
**Note:** latest version of Procsd is `0.
|
11
|
+
> **Note:** latest version of Procsd is `0.4.0`. Since version `0.3.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
|
+
|
15
|
+
Install `procsd` first: `$ gem install procsd`. Required Ruby version is `>= 2.3.0`.
|
14
16
|
|
15
17
|
Let's say you have following application's Procfile:
|
16
18
|
|
@@ -36,23 +38,21 @@ Configuration is done.
|
|
36
38
|
### Create an application (export to Systemd)
|
37
39
|
> To disable and remove application from Systemd there is command `$ procsd destroy`.
|
38
40
|
|
39
|
-
> Note: `create` command needs to provide a few arguments: _--user_ (name of the current user), _--dir_ (application's working directory) and `--path` (user's $PATH). Usually it's fine to provide them like on example below:
|
40
|
-
|
41
41
|
```
|
42
42
|
deploy@server:~/sample_app$ procsd create
|
43
43
|
|
44
44
|
Value of the --user option: deploy
|
45
45
|
Value of the --dir option: /home/deploy/sample_app
|
46
46
|
Value of the --path option: /home/deploy/.rbenv/shims:/home/deploy/.rbenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
47
|
-
Systemd directory: /etc/systemd/system
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
Creating app units files in the systemd directory (/etc/systemd/system)...
|
49
|
+
Create: /etc/systemd/system/sample_app-web.1.service
|
50
|
+
Create: /etc/systemd/system/sample_app-worker.1.service
|
51
|
+
Create: /etc/systemd/system/sample_app-worker.2.service
|
52
|
+
Create: /etc/systemd/system/sample_app.target
|
53
|
+
Reloaded configuraion (daemon-reload)
|
53
54
|
Created symlink /etc/systemd/system/multi-user.target.wants/sample_app.target → /etc/systemd/system/sample_app.target.
|
54
55
|
Enabled app target sample_app.target
|
55
|
-
Reloaded configuraion (daemon-reload)
|
56
56
|
App services were created and enabled. Run `start` to start them
|
57
57
|
|
58
58
|
Note: add following line to the sudoers file (`$ sudo visudo`) if you don't want to type password each time for start/stop/restart commands:
|
@@ -63,8 +63,8 @@ You can provide additional options for `create` command:
|
|
63
63
|
* `--user` - name of the user, default is current _$USER_ env variable
|
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
|
-
* `--add-to-sudoers` - if option present, procsd will create sudoers rule `/etc/sudoers.d/app_name`
|
67
|
-
* `--or-restart` - if option
|
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.
|
68
68
|
|
69
69
|
|
70
70
|
### Start application
|
@@ -187,6 +187,195 @@ Systemd provides [a lot of possibilities](https://www.digitalocean.com/community
|
|
187
187
|
* `--priority` - Filter messages by a [particular log level.](https://www.digitalocean.com/community/tutorials/how-to-use-journalctl-to-view-and-manipulate-systemd-logs#by-priority) For example show only error messages: `procsd logs --priority err`
|
188
188
|
* `--grep` - [Filter output](https://www.freedesktop.org/software/systemd/man/journalctl.html#-g) to messages where message matches the provided query (may not work for [some](https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1751006) Linux distributions)
|
189
189
|
|
190
|
+
### Execute processes defined in Procfile
|
191
|
+
|
192
|
+
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
|
+
```
|
195
|
+
deploy@server:~/sample_app$ PORT=3000 procsd exec web
|
196
|
+
|
197
|
+
=> Booting Puma
|
198
|
+
=> Rails 5.2.1 application starting in development
|
199
|
+
=> Run `rails server -h` for more startup options
|
200
|
+
Puma starting in single mode...
|
201
|
+
* Version 3.12.0 (ruby 2.3.0-p0), codename: Llamas in Pajamas
|
202
|
+
* Min threads: 5, max threads: 5
|
203
|
+
* Environment: development
|
204
|
+
* Listening on tcp://localhost:3000
|
205
|
+
Use Ctrl-C to stop
|
206
|
+
```
|
207
|
+
|
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
|
+
|
210
|
+
### Nginx
|
211
|
+
> Before make sure that you have Nginx installed `sudo apt install nginx` and running `sudo systemctl status nginx`.
|
212
|
+
|
213
|
+
If one of your application processes is a web process, you can automatically setup Nginx 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).
|
214
|
+
|
215
|
+
Add to your procsd.yml `nginx` section with `server_name` option defined:
|
216
|
+
|
217
|
+
> If you don't have domain defined (or don't need it), you can add server IP instead: `server_name: 159.159.159.159`.
|
218
|
+
|
219
|
+
> 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
|
+
|
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
|
224
|
+
app: sample_app
|
225
|
+
formation: web=1,worker=2
|
226
|
+
environment:
|
227
|
+
PORT: 2501
|
228
|
+
RAILS_ENV: production
|
229
|
+
RAILS_LOG_TO_STDOUT: true
|
230
|
+
nginx:
|
231
|
+
server_name: my-domain.com
|
232
|
+
```
|
233
|
+
|
234
|
+
Configuration is done! Run [procsd create](#create-an-application-export-to-systemd) to create app services with Nginx config:
|
235
|
+
|
236
|
+
```
|
237
|
+
deploy@server:~/sample_app$ procsd create
|
238
|
+
|
239
|
+
Creating app units files in the systemd directory (/etc/systemd/system)...
|
240
|
+
Create: /etc/systemd/system/sample_app-web.1.service
|
241
|
+
Create: /etc/systemd/system/sample_app-worker.1.service
|
242
|
+
Create: /etc/systemd/system/sample_app-worker.2.service
|
243
|
+
Create: /etc/systemd/system/sample_app.target
|
244
|
+
Reloaded configuraion (daemon-reload)
|
245
|
+
Created symlink /etc/systemd/system/multi-user.target.wants/sample_app.target → /etc/systemd/system/sample_app.target.
|
246
|
+
Enabled app target sample_app.target
|
247
|
+
App services were created and enabled. Run `start` to start them
|
248
|
+
Creating Nginx config (/etc/nginx/sites-available/sample_app)...
|
249
|
+
Create: /etc/nginx/sites-available/sample_app
|
250
|
+
Link Nginx config file to the sites-enabled folder...
|
251
|
+
Nginx config created and daemon reloaded
|
252
|
+
```
|
253
|
+
|
254
|
+
<details/>
|
255
|
+
<summary><code>/etc/nginx/sites-available/sample_app</code>:</summary>
|
256
|
+
|
257
|
+
```
|
258
|
+
upstream app {
|
259
|
+
server 127.0.0.1:2501;
|
260
|
+
}
|
261
|
+
|
262
|
+
server {
|
263
|
+
listen 80;
|
264
|
+
listen [::]:80;
|
265
|
+
|
266
|
+
server_name my-domain.com;
|
267
|
+
root /home/deploy/sample_app/public;
|
268
|
+
|
269
|
+
location ^~ /assets/ {
|
270
|
+
gzip_static on;
|
271
|
+
expires max;
|
272
|
+
add_header Cache-Control public;
|
273
|
+
}
|
274
|
+
|
275
|
+
try_files $uri/index.html $uri @app;
|
276
|
+
location @app {
|
277
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
278
|
+
proxy_set_header Host $http_host;
|
279
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
280
|
+
proxy_set_header X-Real-IP $remote_addr;
|
281
|
+
proxy_redirect off;
|
282
|
+
proxy_pass http://app;
|
283
|
+
}
|
284
|
+
|
285
|
+
client_max_body_size 256M;
|
286
|
+
keepalive_timeout 60;
|
287
|
+
error_page 500 502 503 504 /500.html;
|
288
|
+
error_page 404 /404.html;
|
289
|
+
error_page 422 /422.html;
|
290
|
+
}
|
291
|
+
```
|
292
|
+
</details>
|
293
|
+
|
294
|
+
#### Auto SSL using Certbot
|
295
|
+
|
296
|
+
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:
|
297
|
+
|
298
|
+
```
|
299
|
+
sudo add-apt-repository ppa:certbot/certbot
|
300
|
+
sudo apt update
|
301
|
+
sudo apt-get install python-certbot-nginx
|
302
|
+
```
|
303
|
+
|
304
|
+
Then update procsd.yml:
|
305
|
+
|
306
|
+
> It's required to provide contact email to obtain free certificate from Let’s Encrypt
|
307
|
+
|
308
|
+
```yml
|
309
|
+
nginx:
|
310
|
+
server_name: my-domain.com
|
311
|
+
certbot:
|
312
|
+
email: my-contact-email@gmail.com
|
313
|
+
```
|
314
|
+
|
315
|
+
Configuration is done. **Make sure that all domains defined in procsd (nginx.server_name) are pointed to server IP** where the application is hosted. Now run `procsd create` as usual:
|
316
|
+
|
317
|
+
<details/>
|
318
|
+
<summary>Output</summary>
|
319
|
+
|
320
|
+
```
|
321
|
+
deploy@server:~/sample_app$ procsd create
|
322
|
+
|
323
|
+
Creating app units files in the systemd directory (/etc/systemd/system)...
|
324
|
+
Create: /etc/systemd/system/sample_app-web.1.service
|
325
|
+
Create: /etc/systemd/system/sample_app-worker.1.service
|
326
|
+
Create: /etc/systemd/system/sample_app-worker.2.service
|
327
|
+
Create: /etc/systemd/system/sample_app.target
|
328
|
+
Reloaded configuraion (daemon-reload)
|
329
|
+
Created symlink /etc/systemd/system/multi-user.target.wants/sample_app.target → /etc/systemd/system/sample_app.target.
|
330
|
+
Enabled app target sample_app.target
|
331
|
+
App services were created and enabled. Run `start` to start them
|
332
|
+
Creating Nginx config (/etc/nginx/sites-available/sample_app)...
|
333
|
+
Create: /etc/nginx/sites-available/sample_app
|
334
|
+
Link Nginx config file to the sites-enabled folder...
|
335
|
+
Nginx config created and daemon reloaded
|
336
|
+
|
337
|
+
Execute: sudo certbot --agree-tos --no-eff-email --non-interactive --nginx -d my-domain.com -m my-contact-email@gmail.com
|
338
|
+
Saving debug log to /var/log/letsencrypt/letsencrypt.log
|
339
|
+
Plugins selected: Authenticator nginx, Installer nginx
|
340
|
+
Obtaining a new certificate
|
341
|
+
Performing the following challenges:
|
342
|
+
http-01 challenge for my-domain.com
|
343
|
+
Waiting for verification...
|
344
|
+
Cleaning up challenges
|
345
|
+
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/sample_app
|
346
|
+
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/sample_app
|
347
|
+
|
348
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
349
|
+
Congratulations! You have successfully enabled https://my-domain.com
|
350
|
+
|
351
|
+
You should test your configuration at:
|
352
|
+
https://www.ssllabs.com/ssltest/analyze.html?d=my-domain.com
|
353
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
354
|
+
|
355
|
+
IMPORTANT NOTES:
|
356
|
+
- Congratulations! Your certificate and chain have been saved at:
|
357
|
+
/etc/letsencrypt/live/my-domain.com/fullchain.pem
|
358
|
+
Your key file has been saved at:
|
359
|
+
/etc/letsencrypt/live/my-domain.com/privkey.pem
|
360
|
+
Your cert will expire on 2019-02-17. To obtain a new or tweaked
|
361
|
+
version of this certificate in the future, simply run certbot again
|
362
|
+
with the "certonly" option. To non-interactively renew *all* of
|
363
|
+
your certificates, run "certbot renew"
|
364
|
+
- Your account credentials have been saved in your Certbot
|
365
|
+
configuration directory at /etc/letsencrypt. You should make a
|
366
|
+
secure backup of this folder now. This configuration directory will
|
367
|
+
also contain certificates and private keys obtained by Certbot so
|
368
|
+
making regular backups of this folder is ideal.
|
369
|
+
- If you like Certbot, please consider supporting our work by:
|
370
|
+
|
371
|
+
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
|
372
|
+
Donating to EFF: https://eff.org/donate-le
|
373
|
+
|
374
|
+
Successfully installed SSL cert using certbot
|
375
|
+
```
|
376
|
+
</details>
|
377
|
+
|
378
|
+
|
190
379
|
## All available commands
|
191
380
|
|
192
381
|
```
|
@@ -194,11 +383,12 @@ $ procsd --help
|
|
194
383
|
|
195
384
|
Commands:
|
196
385
|
procsd --version, -v # Print the version
|
197
|
-
procsd config #
|
386
|
+
procsd config # Print config files based on current settings. Available types: sudoers
|
198
387
|
procsd create # Create and enable app services
|
199
388
|
procsd destroy # Stop, disable and remove app services
|
200
389
|
procsd disable # Disable app target
|
201
390
|
procsd enable # Enable app target
|
391
|
+
procsd exec # Run app process
|
202
392
|
procsd help [COMMAND] # Describe available commands or one specific command
|
203
393
|
procsd list # List all app services
|
204
394
|
procsd logs # Show app services logs
|
@@ -213,11 +403,13 @@ Commands:
|
|
213
403
|
|
214
404
|
[Foreman](http://ddollar.github.io/foreman/) itself designed for _development_ (not production) usage only and doing it great. Yes, Foreman allows to [export](http://ddollar.github.io/foreman/#EXPORTING) Procfile to the Systemd, but that's all. After export you have to manually use `systemctl` and `journalctl` to manage/check exported services. Procsd not only exports application, but provides [simple commands](#all-available-commands) to manage exported target.
|
215
405
|
|
216
|
-
|
406
|
+
* Foreman systemd export uses [dymamic](https://fedoramagazine.org/systemd-template-unit-files/) services templates and as a result generates quite a lot of files/folders in the systemd directory even for a simple application.
|
217
407
|
|
218
|
-
Services
|
408
|
+
* Services generated using Foreman contain [$PORT variable](http://ddollar.github.io/foreman/#PROCFILE) in their names (and it's [undocumented](http://ddollar.github.io/foreman/#SYSTEMD-EXPORT) logic). For example for Procfile and formation `web=1,worker=2` (from example above), exported services with Foreman will be: `sample_app-web@2500.service`, `sample_app-worker@2600.service` and `sample_app-worker@2601.service`. My opinion about this approach: it's complicated. Why is there required PORT variable in the services names? Procsd following one rule: simplicity. For export it uses static service files (that means for each process will be generated it's own service file) and services names have predictable, Heroku-like names.
|
219
409
|
|
220
|
-
Procsd
|
410
|
+
* Procsd export can provide additional stop/restart commands for each service (see _Notes_ below).
|
411
|
+
|
412
|
+
* To delete existing app services from Systemd, there is `procsd destroy` command. It is doing the following: stop services if they are running, delete all required systemd files from systemd directory, and restart systemd (`daemon-reload`). This command especially useful while testing, when you need frequently create/update configuration.
|
221
413
|
|
222
414
|
|
223
415
|
## Notes
|
@@ -228,32 +420,36 @@ Procsd following one rule: simplicity. For export it uses static service files (
|
|
228
420
|
```
|
229
421
|
deploy@server:~/sample_app$ VERBOSE=true procsd logs -n 3
|
230
422
|
|
231
|
-
|
423
|
+
Execute: journalctl --no-pager --no-hostname --all --output short-iso -n 3 --unit sample_app-*
|
232
424
|
|
233
425
|
-- Logs begin at Sun 2018-10-21 00:38:42 +04, end at Sun 2018-11-04 19:17:01 +04. --
|
234
426
|
2018-11-04T19:11:59+0400 sample_app-worker.2[29907]: 2018-11-04T15:11:59.597Z 29907 TID-gne5aeyuz INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org
|
235
427
|
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}
|
236
428
|
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
|
237
429
|
```
|
238
|
-
* You can use extended format of processes commands inside `procsd.yml` to provide additional restart/stop commands for
|
430
|
+
* You can use extended format of processes commands inside `procsd.yml` to provide additional restart/stop commands for each process:
|
431
|
+
|
432
|
+
> All possible options: `ExecStart`, `ExecReload` and `ExecStop`
|
239
433
|
|
240
|
-
> All possible options: `start`, `restart` and `stop`
|
241
434
|
> If procsd.yml has `processes:` option defined, then content of Procfile will be ignored
|
242
435
|
|
243
436
|
```yml
|
437
|
+
app: sample_app
|
244
438
|
processes:
|
245
439
|
web:
|
246
|
-
|
247
|
-
|
440
|
+
ExecStart: bundle exec rails server -p $PORT
|
441
|
+
ExecReload: bundle exec pumactl phased-restart
|
248
442
|
worker: bundle exec sidekiq -e production
|
249
|
-
app: sample_app
|
250
443
|
```
|
251
444
|
|
252
|
-
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 `
|
445
|
+
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.
|
446
|
+
|
447
|
+
## Capistrano integration
|
448
|
+
|
449
|
+
https://github.com/vifreefly/capistrano-procsd
|
253
450
|
|
254
451
|
|
255
452
|
## ToDo
|
256
|
-
* Optional possibility to generate Ngnix config (with out-of-box SSL using [Certbot](https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx)) for an application to use Ngnix as a proxy and serve static files
|
257
453
|
* Add integration with [Inspeqtor](https://github.com/mperham/inspeqtor) to monitor application services and get alert notifications if something happened
|
258
454
|
|
259
455
|
|
data/lib/procsd/cli.rb
CHANGED
@@ -1,71 +1,45 @@
|
|
1
1
|
require 'yaml'
|
2
|
-
require 'erb'
|
3
2
|
require_relative 'generator'
|
4
3
|
|
5
4
|
module Procsd
|
6
5
|
class CLI < Thor
|
7
6
|
class ConfigurationError < StandardError; end
|
8
7
|
class ArgumentError < StandardError; end
|
9
|
-
map %w[--version -v] => :__print_version
|
10
8
|
|
11
9
|
desc "create", "Create and enable app services"
|
12
|
-
option :user, aliases: :u, type: :string, banner: "$USER"
|
13
|
-
option :dir, aliases: :d, type: :string, banner: "$PWD"
|
14
|
-
option :path, aliases: :p, type: :string, banner: "$PATH"
|
10
|
+
option :user, aliases: :u, type: :string, banner: "$USER", default: ENV["USER"]
|
11
|
+
option :dir, aliases: :d, type: :string, banner: "$PWD", default: ENV["PWD"]
|
12
|
+
option :path, aliases: :p, type: :string, banner: "$PATH", default: `/bin/bash -ilc 'echo $PATH'`.strip
|
15
13
|
option :'or-restart', type: :boolean, banner: "Create and start app services if not created yet, otherwise restart"
|
16
14
|
option :'add-to-sudoers', type: :boolean, banner: "Create sudoers rule at /etc/sudoers.d/app_name to allow manage app target without password prompt"
|
17
15
|
def create
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
}
|
26
|
-
|
27
|
-
opts.each do |key, value|
|
28
|
-
if value.nil? || value.empty?
|
29
|
-
say("Can't fetch value for --#{key}, please provide it as an argument", :red) and return
|
30
|
-
else
|
31
|
-
say "Value of the --#{key} option: #{value}"
|
32
|
-
end
|
16
|
+
raise ConfigurationError, "Can't find systemctl executable available" unless in_path?("systemctl")
|
17
|
+
options.each do |key, value|
|
18
|
+
next unless %w(user dir path).include? key
|
19
|
+
if value.nil? || value.empty?
|
20
|
+
say("Can't fetch value for --#{key}, please provide it's as an argument", :red) and return
|
21
|
+
else
|
22
|
+
say("Value of the --#{key} option: #{value}", :yellow)
|
33
23
|
end
|
24
|
+
end
|
34
25
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
say("Reloaded configuraion (daemon-reload)", :green)
|
26
|
+
preload!
|
27
|
+
if @config[:nginx]
|
28
|
+
raise ConfigurationError, "Can't find nginx executable available" unless in_path?("nginx")
|
29
|
+
unless Dir.exist?(File.join options["dir"], "public")
|
30
|
+
raise ConfigurationError, "Missing public/ folder to use with Nginx"
|
41
31
|
end
|
42
|
-
|
43
|
-
|
44
|
-
start
|
45
|
-
say("App services were created, enabled and started", :green)
|
46
|
-
else
|
47
|
-
say("App services were created and enabled. Run `start` to start them", :green)
|
32
|
+
unless @config.dig(:environment, "PORT")
|
33
|
+
raise ConfigurationError, "Please provide PORT environment variable in procsd.yml to use with Nginx"
|
48
34
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
sudoers_file_temp_path = "/tmp/#{app_name}"
|
53
|
-
sudoers_file_dest_path = "#{SUDOERS_DIR}/#{app_name}"
|
54
|
-
if Dir.exist?(SUDOERS_DIR)
|
55
|
-
File.open(sudoers_file_temp_path, "w") { |f| f.puts sudoers_rule_content }
|
56
|
-
execute %W(sudo chown root:root #{sudoers_file_temp_path})
|
57
|
-
execute %W(sudo chmod 0440 #{sudoers_file_temp_path})
|
58
|
-
if execute %W(sudo mv #{sudoers_file_temp_path} #{sudoers_file_dest_path})
|
59
|
-
say("Sudoers file #{sudoers_file_dest_path} was created", :green)
|
60
|
-
end
|
61
|
-
else
|
62
|
-
say "Directory #{SUDOERS_DIR} does not exist, sudoers file wan't created"
|
63
|
-
end
|
64
|
-
else
|
65
|
-
say "Note: add following line to the sudoers file (`$ sudo visudo`) if you don't " \
|
66
|
-
"want to type password each time for start/stop/restart commands:"
|
67
|
-
puts sudoers_rule_content
|
35
|
+
if certbot = @config[:nginx]["certbot"]
|
36
|
+
raise ConfigurationError, "Can't find certbot executable available" unless in_path?("certbot")
|
37
|
+
raise ConfigurationError, "Provide email to generate cert using certbot" unless certbot["email"]
|
68
38
|
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if !target_exist?
|
42
|
+
perform_create
|
69
43
|
else
|
70
44
|
if options["or-restart"]
|
71
45
|
restart
|
@@ -83,9 +57,9 @@ module Procsd
|
|
83
57
|
stop
|
84
58
|
disable
|
85
59
|
|
86
|
-
|
60
|
+
units.each do |filename|
|
87
61
|
path = File.join(systemd_dir, filename)
|
88
|
-
execute %W(sudo rm #{path}) and say "Deleted #{path}" if File.exist?
|
62
|
+
execute %W(sudo rm #{path}) and say "Deleted: #{path}" if File.exist?(path)
|
89
63
|
end
|
90
64
|
|
91
65
|
if execute %w(sudo systemctl daemon-reload)
|
@@ -94,10 +68,19 @@ module Procsd
|
|
94
68
|
say("App services were stopped, disabled and removed", :green)
|
95
69
|
|
96
70
|
sudoers_file_path = "#{SUDOERS_DIR}/#{app_name}"
|
97
|
-
if
|
98
|
-
if
|
99
|
-
|
71
|
+
if system "sudo", "test", "-e", sudoers_file_path
|
72
|
+
say("Sudoers file removed", :green) if execute %W(sudo rm #{sudoers_file_path})
|
73
|
+
end
|
74
|
+
|
75
|
+
if @config[:nginx]
|
76
|
+
enabled_path = File.join(NGINX_DIR, "sites-enabled", app_name)
|
77
|
+
available_path = File.join(NGINX_DIR, "sites-available", app_name)
|
78
|
+
[enabled_path, available_path].each do |path|
|
79
|
+
execute %W(sudo rm #{path}) and say "Deleted: #{path}" if File.exist?(path)
|
100
80
|
end
|
81
|
+
|
82
|
+
execute %w(sudo systemctl restart nginx)
|
83
|
+
say("Nginx config removed and daemon reloaded", :green)
|
101
84
|
end
|
102
85
|
else
|
103
86
|
say_target_not_exists
|
@@ -182,7 +165,7 @@ module Procsd
|
|
182
165
|
end
|
183
166
|
|
184
167
|
command << (options["target"] ? target_name : "#{app_name}-#{service_name}*")
|
185
|
-
execute command
|
168
|
+
execute command, type: :exec
|
186
169
|
end
|
187
170
|
|
188
171
|
desc "logs", "Show app services logs"
|
@@ -202,7 +185,7 @@ module Procsd
|
|
202
185
|
command.push("--grep", "'" + options["grep"] + "'") if options["grep"]
|
203
186
|
|
204
187
|
command.push("--unit", "#{app_name}-#{service_name}*")
|
205
|
-
execute command
|
188
|
+
execute command, type: :exec
|
206
189
|
end
|
207
190
|
|
208
191
|
desc "list", "List all app services"
|
@@ -210,15 +193,11 @@ module Procsd
|
|
210
193
|
preload!
|
211
194
|
say_target_not_exists and return unless target_exist?
|
212
195
|
|
213
|
-
|
196
|
+
command = %W(systemctl list-dependencies #{target_name})
|
197
|
+
execute command, type: :exec
|
214
198
|
end
|
215
199
|
|
216
|
-
desc "
|
217
|
-
def __print_version
|
218
|
-
puts VERSION
|
219
|
-
end
|
220
|
-
|
221
|
-
desc "config", "Show configuration. Available types: sudoers"
|
200
|
+
desc "config", "Print config files based on current settings. Available types: sudoers"
|
222
201
|
def config(name)
|
223
202
|
preload!
|
224
203
|
|
@@ -230,32 +209,109 @@ module Procsd
|
|
230
209
|
end
|
231
210
|
end
|
232
211
|
|
212
|
+
map exec: :__exec
|
213
|
+
desc "exec", "Run app process"
|
214
|
+
option :env, type: :boolean, banner: "Require environment defined in procsd.yml"
|
215
|
+
def __exec(process_name)
|
216
|
+
preload!
|
217
|
+
|
218
|
+
start_cmd = @config[:processes].dig(process_name, "commands", "ExecStart")
|
219
|
+
raise ArgumentError, "Process is not defined: #{process_name}" unless start_cmd
|
220
|
+
|
221
|
+
if options["env"]
|
222
|
+
@config[:environment].each { |k, v| @config[:environment][k] = v.to_s }
|
223
|
+
exec @config[:environment], start_cmd
|
224
|
+
else
|
225
|
+
exec start_cmd
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
map %w[--version -v] => :__print_version
|
230
|
+
desc "--version, -v", "Print the version"
|
231
|
+
def __print_version
|
232
|
+
puts VERSION
|
233
|
+
end
|
234
|
+
|
233
235
|
private
|
234
236
|
|
235
|
-
def
|
236
|
-
|
237
|
-
|
237
|
+
def perform_create
|
238
|
+
generator = Generator.new(@config, options)
|
239
|
+
generator.generate_units(save: true)
|
240
|
+
|
241
|
+
if execute %w(sudo systemctl daemon-reload)
|
242
|
+
say("Reloaded configuraion (daemon-reload)", :green)
|
243
|
+
end
|
244
|
+
|
245
|
+
enable
|
246
|
+
|
247
|
+
if options["or-restart"]
|
248
|
+
start
|
249
|
+
say("App services were created, enabled and started", :green)
|
250
|
+
else
|
251
|
+
say("App services were created and enabled. Run `start` to start them", :green)
|
252
|
+
end
|
253
|
+
|
254
|
+
if options["add-to-sudoers"]
|
255
|
+
if Dir.exist?(SUDOERS_DIR)
|
256
|
+
if generator.generate_sudoers(options["user"], has_reload: has_reload?, save: true)
|
257
|
+
say("Sudoers file #{SUDOERS_DIR}/#{app_name} was created", :green)
|
258
|
+
end
|
259
|
+
else
|
260
|
+
say("Directory #{SUDOERS_DIR} does not exists, sudoers file wasn't created", :red)
|
261
|
+
end
|
262
|
+
else
|
263
|
+
say "Note: add following line to the sudoers file (`$ sudo visudo`) if you don't " \
|
264
|
+
"want to type password each time for start/stop/restart commands:"
|
265
|
+
puts generator.generate_sudoers(options["user"], has_reload: has_reload?)
|
266
|
+
end
|
267
|
+
|
268
|
+
if nginx = @config[:nginx]
|
269
|
+
generator.generate_nginx_conf(save: true)
|
270
|
+
execute %w(sudo systemctl restart nginx)
|
271
|
+
say("Nginx config created and daemon reloaded", :green)
|
272
|
+
|
273
|
+
# Reference: https://certbot.eff.org/docs/using.html#certbot-command-line-options
|
274
|
+
if certbot = nginx["certbot"]
|
275
|
+
command = %w(sudo certbot --agree-tos --no-eff-email --redirect --non-interactive --nginx)
|
276
|
+
nginx["server_name"].split(" ").map(&:strip).each do |domain|
|
277
|
+
command.push("-d", domain)
|
278
|
+
end
|
238
279
|
|
239
|
-
|
240
|
-
|
280
|
+
command.push("-m", certbot["email"])
|
281
|
+
if execute command
|
282
|
+
say("Successfully installed SSL cert using certbot", :green)
|
283
|
+
else
|
284
|
+
say("Failed to install SSL cert using certbot", :red)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
241
289
|
|
242
|
-
|
290
|
+
def in_path?(name)
|
291
|
+
system("which", name, [:out, :err] => "/dev/null")
|
243
292
|
end
|
244
293
|
|
245
294
|
def has_reload?
|
246
|
-
|
295
|
+
@config[:processes].any? { |name, values| values.dig("commands", "ExecReload") }
|
247
296
|
end
|
248
297
|
|
249
|
-
def
|
250
|
-
|
251
|
-
|
252
|
-
|
298
|
+
def units
|
299
|
+
all = [target_name]
|
300
|
+
@config[:processes].each do |name, values|
|
301
|
+
values["size"].times { |i| all << "#{app_name}-#{name}.#{i + 1}.service" }
|
302
|
+
end
|
253
303
|
|
254
|
-
|
255
|
-
|
304
|
+
all
|
305
|
+
end
|
256
306
|
|
257
|
-
|
258
|
-
|
307
|
+
def execute(command, type: :system)
|
308
|
+
say("Execute: #{command.join(' ')}", :yellow) if ENV["VERBOSE"] == "true"
|
309
|
+
case type
|
310
|
+
when :system
|
311
|
+
system *command
|
312
|
+
when :exec
|
313
|
+
exec *command
|
314
|
+
end
|
259
315
|
end
|
260
316
|
|
261
317
|
def say_target_not_exists
|
@@ -286,24 +342,9 @@ module Procsd
|
|
286
342
|
@config[:app]
|
287
343
|
end
|
288
344
|
|
289
|
-
def services
|
290
|
-
all = {}
|
291
|
-
@config[:processes].each do |process_name, opts|
|
292
|
-
opts["count"].times do |i|
|
293
|
-
commands = { "start" => opts["start"], "stop" => opts["stop"], "restart" => opts["restart"] }
|
294
|
-
all["#{app_name}-#{process_name}.#{i + 1}.service"] = commands
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
all
|
299
|
-
end
|
300
|
-
|
301
345
|
def preload!
|
302
|
-
@config = {}
|
346
|
+
@config = { processes: {}}
|
303
347
|
|
304
|
-
unless system("which", "systemctl", [:out, :err]=>"/dev/null")
|
305
|
-
raise ConfigurationError, "Your OS doesn't has systemctl executable available"
|
306
|
-
end
|
307
348
|
raise ConfigurationError, "Config file procsd.yml doesn't exists" unless File.exist? "procsd.yml"
|
308
349
|
begin
|
309
350
|
procsd = YAML.load(ERB.new(File.read "procsd.yml").result)
|
@@ -335,19 +376,18 @@ module Procsd
|
|
335
376
|
processes = procsd["processes"] || procfile
|
336
377
|
processes.each do |process_name, opts|
|
337
378
|
if opts.kind_of?(Hash)
|
338
|
-
raise ConfigurationError, "Missing
|
379
|
+
raise ConfigurationError, "Missing ExecStart command for `#{process_name}` process" unless opts["ExecStart"]
|
380
|
+
@config[:processes][process_name] = { "commands" => opts }
|
339
381
|
else
|
340
|
-
processes[process_name] = { "
|
382
|
+
@config[:processes][process_name] = { "commands" => { "ExecStart" => opts }}
|
341
383
|
end
|
342
384
|
|
343
|
-
|
344
|
-
processes[process_name]["count"] = formation[process_name] || 1
|
345
|
-
end
|
385
|
+
@config[:processes][process_name]["size"] = formation[process_name] || 1
|
346
386
|
end
|
347
387
|
|
348
|
-
@config[:processes] = processes
|
349
388
|
@config[:environment] = procsd["environment"] || {}
|
350
389
|
@config[:systemd_dir] = procsd["systemd_dir"] || DEFAULT_SYSTEMD_DIR
|
390
|
+
@config[:nginx] = procsd["nginx"]
|
351
391
|
end
|
352
392
|
end
|
353
393
|
end
|
data/lib/procsd/generator.rb
CHANGED
@@ -1,47 +1,110 @@
|
|
1
1
|
module Procsd
|
2
|
-
class Generator
|
3
|
-
|
2
|
+
class Generator
|
3
|
+
attr_reader :app_name, :target_name
|
4
4
|
|
5
|
-
def
|
6
|
-
File.dirname(__FILE__)
|
7
|
-
end
|
8
|
-
|
9
|
-
def export!(services, config:, options:)
|
10
|
-
self.destination_root = "/tmp"
|
5
|
+
def initialize(config, options)
|
11
6
|
@config = config
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
@options = options
|
8
|
+
@app_name = @config[:app]
|
9
|
+
@target_name = "#{app_name}.target"
|
10
|
+
end
|
16
11
|
|
17
|
-
|
18
|
-
|
12
|
+
def generate_units(save: false)
|
13
|
+
services = {}
|
14
|
+
@config[:processes].each do |name, values|
|
15
|
+
commands = values["commands"]
|
16
|
+
size = values["size"]
|
17
|
+
content = generate_template("service", @options.merge(
|
19
18
|
"target_name" => target_name,
|
20
|
-
"
|
21
|
-
"command" => service_command,
|
19
|
+
"commands" => commands,
|
22
20
|
"environment" => @config[:environment]
|
23
|
-
)
|
21
|
+
))
|
24
22
|
|
25
|
-
|
23
|
+
services[name] = { content: content, size: size }
|
26
24
|
end
|
27
25
|
|
28
|
-
|
29
|
-
"app
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
if save
|
27
|
+
puts "Creating app units files in the systemd directory (#{DEFAULT_SYSTEMD_DIR})..."
|
28
|
+
wants = []
|
29
|
+
services.each do |service_name, values|
|
30
|
+
values[:size].times do |i|
|
31
|
+
unit_name = "#{app_name}-#{service_name}.#{i + 1}.service"
|
32
|
+
wants << unit_name
|
33
|
+
write_file!(File.join(@config[:systemd_dir], unit_name), values[:content])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
target_content = generate_template("target", {
|
38
|
+
"app" => app_name,
|
39
|
+
"wants" => wants.join(" ")
|
40
|
+
})
|
41
|
+
write_file!(File.join(@config[:systemd_dir], target_name), target_content)
|
42
|
+
else
|
43
|
+
services
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_sudoers(user, has_reload:, save: false)
|
48
|
+
systemctl_path = `which systemctl`.strip
|
49
|
+
commands = []
|
50
|
+
%w(start stop restart).each { |cmd| commands << "#{systemctl_path} #{cmd} #{target_name}" }
|
51
|
+
commands << "#{systemctl_path} reload-or-restart #{app_name}-\\* --all" if has_reload
|
52
|
+
content = "#{user} ALL=NOPASSWD: #{commands.join(', ')}"
|
53
|
+
|
54
|
+
if save
|
55
|
+
puts "Creating sudoers rule file in the sudoers.d directory (#{SUDOERS_DIR})..."
|
56
|
+
temp_path = "/tmp/#{app_name}"
|
57
|
+
dest_path = "#{SUDOERS_DIR}/#{app_name}"
|
58
|
+
|
59
|
+
File.open(temp_path, "w") { |f| f.puts content }
|
60
|
+
system "sudo", "chown", "root:root", temp_path
|
61
|
+
system "sudo", "chmod", "0440", temp_path
|
62
|
+
system "sudo", "mv", temp_path, dest_path
|
63
|
+
else
|
64
|
+
content
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_nginx_conf(save: false)
|
69
|
+
root_path = File.join(@options["dir"], "public")
|
70
|
+
content = generate_template("nginx", {
|
71
|
+
port: @config[:environment]["PORT"],
|
72
|
+
server_name: @config[:nginx]["server_name"],
|
73
|
+
root: root_path,
|
74
|
+
error_500: File.exist?(File.join root_path, "500.html"),
|
75
|
+
error_404: File.exist?(File.join root_path, "404.html"),
|
76
|
+
error_422: File.exist?(File.join root_path, "422.html")
|
77
|
+
})
|
78
|
+
|
79
|
+
if save
|
80
|
+
config_path = File.join(NGINX_DIR, "sites-available", app_name)
|
81
|
+
puts "Creating Nginx config (#{config_path})..."
|
82
|
+
write_file!(config_path, content)
|
83
|
+
puts "Link Nginx config file to the sites-enabled folder..."
|
84
|
+
system "sudo", "ln", "-nfs", config_path, File.join(NGINX_DIR, "sites-enabled")
|
85
|
+
else
|
86
|
+
content
|
87
|
+
end
|
33
88
|
end
|
34
89
|
|
35
90
|
private
|
36
91
|
|
37
|
-
def
|
38
|
-
|
92
|
+
def generate_template(template_name, conf)
|
93
|
+
b = binding
|
94
|
+
b.local_variable_set(:config, conf)
|
95
|
+
template_path = File.join(File.dirname(__FILE__), "templates/#{template_name}.erb")
|
96
|
+
content = File.read(template_path)
|
97
|
+
ERB.new(content, nil, "-").result(b)
|
98
|
+
end
|
39
99
|
|
40
|
-
|
41
|
-
|
42
|
-
|
100
|
+
def write_file!(dest_path, content)
|
101
|
+
temp_path = File.join("/tmp", Pathname.new(dest_path).basename.to_s)
|
102
|
+
File.write(temp_path, content)
|
103
|
+
if system "sudo", "mv", temp_path, dest_path
|
104
|
+
puts "Create: #{dest_path}"
|
105
|
+
end
|
43
106
|
ensure
|
44
|
-
File.delete(
|
107
|
+
File.delete(temp_path) if File.exist? temp_path
|
45
108
|
end
|
46
109
|
end
|
47
110
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
upstream app {
|
2
|
+
server 127.0.0.1:<%= config[:port] %>;
|
3
|
+
}
|
4
|
+
|
5
|
+
server {
|
6
|
+
listen 80;
|
7
|
+
listen [::]:80;
|
8
|
+
|
9
|
+
server_name <%= config[:server_name] %>;
|
10
|
+
root <%= config[:root] %>;
|
11
|
+
|
12
|
+
location ^~ /assets/ {
|
13
|
+
gzip_static on;
|
14
|
+
expires max;
|
15
|
+
add_header Cache-Control public;
|
16
|
+
}
|
17
|
+
|
18
|
+
try_files $uri/index.html $uri @app;
|
19
|
+
location @app {
|
20
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
21
|
+
proxy_set_header Host $http_host;
|
22
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
23
|
+
proxy_set_header X-Real-IP $remote_addr;
|
24
|
+
proxy_redirect off;
|
25
|
+
proxy_pass http://app;
|
26
|
+
}
|
27
|
+
|
28
|
+
client_max_body_size 256M;
|
29
|
+
keepalive_timeout 60;
|
30
|
+
<% if config[:error_500] -%>
|
31
|
+
error_page 500 502 503 504 /500.html;
|
32
|
+
<% end -%>
|
33
|
+
<% if config[:error_404] -%>
|
34
|
+
error_page 404 /404.html;
|
35
|
+
<% end -%>
|
36
|
+
<% if config[:error_422] -%>
|
37
|
+
error_page 422 /422.html;
|
38
|
+
<% end -%>
|
39
|
+
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
[Unit]
|
2
2
|
Requires=network.target
|
3
3
|
PartOf=<%= config["target_name"] %>
|
4
|
-
<% if config["
|
4
|
+
<% if config["commands"]["ExecReload"] -%>
|
5
5
|
ReloadPropagatedFrom=<%= config["target_name"] %>
|
6
6
|
<% end -%>
|
7
7
|
|
@@ -10,20 +10,20 @@ Type=simple
|
|
10
10
|
User=<%= config["user"] %>
|
11
11
|
WorkingDirectory=<%= config["dir"] %>
|
12
12
|
|
13
|
-
ExecStart=/bin/bash -lc '<%= config["
|
14
|
-
<% if stop = config["
|
13
|
+
ExecStart=/bin/bash -lc '<%= config["commands"]["ExecStart"] %>'
|
14
|
+
<% if stop = config["commands"]["ExecStop"] -%>
|
15
15
|
ExecStop=/bin/bash -lc '<%= stop %>'
|
16
16
|
<% end -%>
|
17
|
-
<% if config["
|
18
|
-
ExecReload=/bin/bash -lc '<%=
|
17
|
+
<% if reload = config["commands"]["ExecReload"] -%>
|
18
|
+
ExecReload=/bin/bash -lc '<%= reload %>'
|
19
19
|
<% end -%>
|
20
20
|
|
21
21
|
Restart=always
|
22
22
|
RestartSec=1
|
23
|
-
TimeoutStopSec=
|
23
|
+
TimeoutStopSec=30
|
24
24
|
KillMode=mixed
|
25
25
|
StandardInput=null
|
26
|
-
SyslogIdentifier
|
26
|
+
SyslogIdentifier=%n
|
27
27
|
|
28
28
|
Environment="PATH=<%= config["path"] %>"
|
29
29
|
<% config["environment"].each do |key, value| -%>
|
data/lib/procsd/version.rb
CHANGED
data/lib/procsd.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.
|
4
|
+
version: 0.4.0
|
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-
|
11
|
+
date: 2018-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -100,6 +100,7 @@ files:
|
|
100
100
|
- lib/procsd.rb
|
101
101
|
- lib/procsd/cli.rb
|
102
102
|
- lib/procsd/generator.rb
|
103
|
+
- lib/procsd/templates/nginx.erb
|
103
104
|
- lib/procsd/templates/service.erb
|
104
105
|
- lib/procsd/templates/target.erb
|
105
106
|
- lib/procsd/version.rb
|