procsd 0.2.0 → 0.3.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 +26 -0
- data/README.md +47 -27
- data/lib/procsd.rb +1 -0
- data/lib/procsd/cli.rb +161 -86
- data/lib/procsd/generator.rb +11 -8
- data/lib/procsd/templates/service.erb +2 -2
- data/lib/procsd/templates/target.erb +0 -1
- data/lib/procsd/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a029c6f4d0953cc9b21f3508a14e9057a4386fd53c786167737a9097c4175f13
|
4
|
+
data.tar.gz: 2f3fa1f897cb3bbf65aa6e00c1b8fbc0c3f1bde13d5dc559514633404ef691ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f5a266a604bdbc1cbd9519b510a1eabf801d753d8497729a7ed64c2acedcfd6d1873f16da89896fedb5e49467f7c987c09e53ddef6d2d1ff947cbd07501173e
|
7
|
+
data.tar.gz: 8724b65f9e834e5654690148a0c21444608add28c60df412dd76d47ad18f2c2edbef20ae8029de39b2431a2c6585531656e7b4a629d0427f71e717c4bd314d8b
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,30 @@
|
|
1
1
|
# CHANGELOG
|
2
|
+
## 0.3.0
|
3
|
+
* **Breaking change:** `.procsd.yml` renamed to `procsd.yml` (without dot)
|
4
|
+
* **Breaking change:** `environment` option in the procsd.yml now has hash format, not array:
|
5
|
+
|
6
|
+
Was:
|
7
|
+
```
|
8
|
+
environment:
|
9
|
+
- PORT=2501
|
10
|
+
- RAILS_ENV=production
|
11
|
+
- RAILS_LOG_TO_STDOUT=true
|
12
|
+
```
|
13
|
+
|
14
|
+
Now:
|
15
|
+
```
|
16
|
+
environment:
|
17
|
+
PORT: 2501
|
18
|
+
RAILS_ENV: production
|
19
|
+
RAILS_LOG_TO_STDOUT: true
|
20
|
+
```
|
21
|
+
|
22
|
+
* Add `--or-restart` option for `create` command
|
23
|
+
* Options `--user`, `--path` and `--dir` for `create` command are not required anymore (but still can be provided)
|
24
|
+
* Add new `config` command (currently it can print only content for sudoers file: `$ procsd config sudoers`)
|
25
|
+
* Add `--add-to-sudoers` option for `create` command to automatically add `/bin/systemctl start/stop/restart app_name.target` commands to sudoers `/etc/sudoers.d/app_name` file (passwordless sudo)
|
26
|
+
|
27
|
+
|
2
28
|
## 0.2.0
|
3
29
|
* Allow to use erb inside .procsd.yml
|
4
30
|
* Add dotenv support
|
data/README.md
CHANGED
@@ -8,6 +8,8 @@ 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.3.0`. Since version `0.2.0` there are some breaking changes. Check the [CHANGELOG.md](CHANGELOG.md). Run `$ gem update procsd` or `$ bundle update procsd` (if you have already installed procsd) to update to the latest version.
|
12
|
+
|
11
13
|
> Install `procsd` first: `$ gem install procsd`. Required Ruby version is `>= 2.3.0`.
|
12
14
|
|
13
15
|
Let's say you have following application's Procfile:
|
@@ -16,17 +18,18 @@ Let's say you have following application's Procfile:
|
|
16
18
|
web: bundle exec rails server -p $PORT
|
17
19
|
worker: bundle exec sidekiq -e $RAILS_ENV
|
18
20
|
```
|
19
|
-
and you want to have one instance of web process && two instances of worker process. Create inside application directory
|
21
|
+
and you want to have one instance of web process && two instances of worker process. Create inside application directory `procsd.yml` config file:
|
20
22
|
|
21
23
|
```yaml
|
22
24
|
app: sample_app
|
23
25
|
formation: web=1,worker=2
|
24
26
|
environment:
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
PORT: 2501
|
28
|
+
RAILS_ENV: production
|
29
|
+
RAILS_LOG_TO_STDOUT: true
|
28
30
|
```
|
29
|
-
|
31
|
+
|
32
|
+
> The only required option in `procsd.yml` is `app` (application name). Also you can provide custom Systemd directory path (`systemd_dir` option, default is _/etc/systemd/system_)
|
30
33
|
|
31
34
|
Configuration is done.
|
32
35
|
|
@@ -36,9 +39,13 @@ Configuration is done.
|
|
36
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:
|
37
40
|
|
38
41
|
```
|
39
|
-
deploy@server:~/sample_app$ procsd create
|
42
|
+
deploy@server:~/sample_app$ procsd create
|
40
43
|
|
44
|
+
Value of the --user option: deploy
|
45
|
+
Value of the --dir option: /home/deploy/sample_app
|
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
|
41
47
|
Systemd directory: /etc/systemd/system
|
48
|
+
|
42
49
|
create sample_app-web.1.service
|
43
50
|
create sample_app-worker.1.service
|
44
51
|
create sample_app-worker.2.service
|
@@ -47,8 +54,19 @@ Created symlink /etc/systemd/system/multi-user.target.wants/sample_app.target
|
|
47
54
|
Enabled app target sample_app.target
|
48
55
|
Reloaded configuraion (daemon-reload)
|
49
56
|
App services were created and enabled. Run `start` to start them
|
57
|
+
|
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:
|
59
|
+
deploy ALL=NOPASSWD: /bin/systemctl start sample_app.target, /bin/systemctl stop sample_app.target, /bin/systemctl restart sample_app.target
|
50
60
|
```
|
51
61
|
|
62
|
+
You can provide additional options for `create` command:
|
63
|
+
* `--user` - name of the user, default is current _$USER_ env variable
|
64
|
+
* `--dir` - application's working directory, default is current _$PWD_ env variable
|
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` allowing to start/stop/restart app services without password prompt.
|
67
|
+
* `--or-restart` - if option present and servides already created, procsd will skip creation and instead call `restart` command
|
68
|
+
|
69
|
+
|
52
70
|
### Start application
|
53
71
|
> Other control commands: `stop` and `restart`
|
54
72
|
|
@@ -120,7 +138,7 @@ deploy@server:~/sample_app$ procsd status
|
|
120
138
|
2018-11-04T01:54:17+0400 sample_app-worker.2[8827]: 2018-11-03T21:54:17.716Z 8827 TID-gniahzm1r INFO: Starting processing, hit Ctrl-C to stop
|
121
139
|
```
|
122
140
|
|
123
|
-
Also you can see status in
|
141
|
+
Also you can see status in short format:
|
124
142
|
|
125
143
|
```
|
126
144
|
deploy@server:~/sample_app$ procsd status --short
|
@@ -175,18 +193,19 @@ Systemd provides [a lot of possibilities](https://www.digitalocean.com/community
|
|
175
193
|
$ procsd --help
|
176
194
|
|
177
195
|
Commands:
|
178
|
-
procsd --version, -v
|
179
|
-
procsd
|
180
|
-
procsd
|
181
|
-
procsd
|
182
|
-
procsd
|
183
|
-
procsd
|
184
|
-
procsd
|
185
|
-
procsd
|
186
|
-
procsd
|
187
|
-
procsd
|
188
|
-
procsd
|
189
|
-
procsd
|
196
|
+
procsd --version, -v # Print the version
|
197
|
+
procsd config # Show configuration. Available types: sudoers
|
198
|
+
procsd create # Create and enable app services
|
199
|
+
procsd destroy # Stop, disable and remove app services
|
200
|
+
procsd disable # Disable app target
|
201
|
+
procsd enable # Enable app target
|
202
|
+
procsd help [COMMAND] # Describe available commands or one specific command
|
203
|
+
procsd list # List all app services
|
204
|
+
procsd logs # Show app services logs
|
205
|
+
procsd restart # Restart app services
|
206
|
+
procsd start # Start app services
|
207
|
+
procsd status # Show app services status
|
208
|
+
procsd stop # Stop app services
|
190
209
|
```
|
191
210
|
|
192
211
|
|
@@ -209,30 +228,31 @@ Procsd following one rule: simplicity. For export it uses static service files (
|
|
209
228
|
```
|
210
229
|
deploy@server:~/sample_app$ VERBOSE=true procsd logs -n 3
|
211
230
|
|
212
|
-
> Executing command: journalctl --no-pager --
|
231
|
+
> Executing command: `journalctl --no-pager --no-hostname --all --output short-iso -n 3 --unit sample_app-*`
|
213
232
|
|
214
233
|
-- Logs begin at Sun 2018-10-21 00:38:42 +04, end at Sun 2018-11-04 19:17:01 +04. --
|
215
234
|
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
|
216
235
|
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}
|
217
236
|
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
|
218
237
|
```
|
219
|
-
* You can use extended format of
|
220
|
-
> Keep in mind that syntax below is not supported by Foreman or Heroku
|
238
|
+
* You can use extended format of processes commands inside `procsd.yml` to provide additional restart/stop commands for processes:
|
221
239
|
|
222
240
|
> All possible options: `start`, `restart` and `stop`
|
241
|
+
> If procsd.yml has `processes:` option defined, then content of Procfile will be ignored
|
223
242
|
|
224
243
|
```yml
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
244
|
+
processes:
|
245
|
+
web:
|
246
|
+
start: bundle exec rails server -p $PORT
|
247
|
+
restart: bundle exec pumactl phased-restart
|
248
|
+
worker: bundle exec sidekiq -e production
|
249
|
+
app: sample_app
|
229
250
|
```
|
230
251
|
|
231
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 `restart`command for a process, then this command will be called (`$ procsd restart`) by Systemd instead of just killing and starting process again.
|
232
253
|
|
233
254
|
|
234
255
|
## ToDo
|
235
|
-
* Add Capistrano integration examples
|
236
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
|
237
257
|
* Add integration with [Inspeqtor](https://github.com/mperham/inspeqtor) to monitor application services and get alert notifications if something happened
|
238
258
|
|
data/lib/procsd.rb
CHANGED
data/lib/procsd/cli.rb
CHANGED
@@ -5,27 +5,73 @@ require_relative 'generator'
|
|
5
5
|
module Procsd
|
6
6
|
class CLI < Thor
|
7
7
|
class ConfigurationError < StandardError; end
|
8
|
+
class ArgumentError < StandardError; end
|
8
9
|
map %w[--version -v] => :__print_version
|
9
10
|
|
10
11
|
desc "create", "Create and enable app services"
|
11
|
-
option :user, aliases: :u, type: :string,
|
12
|
-
option :dir, aliases: :d, type: :string,
|
13
|
-
option :path, aliases: :p, type: :string,
|
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"
|
15
|
+
option :'or-restart', type: :boolean, banner: "Create and start app services if not created yet, otherwise restart"
|
16
|
+
option :'add-to-sudoers', type: :boolean, banner: "Create sudoers rule at /etc/sudoers.d/app_name to allow manage app target without password prompt"
|
14
17
|
def create
|
15
18
|
preload!
|
16
19
|
|
17
20
|
if !target_exist?
|
21
|
+
opts = {
|
22
|
+
user: options["user"] || ENV["USER"],
|
23
|
+
dir: options["dir"] || ENV["PWD"],
|
24
|
+
path: options["path"] || fetch_path_env
|
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
|
33
|
+
end
|
34
|
+
|
18
35
|
gen = Generator.new
|
19
|
-
gen.export!(services,
|
36
|
+
gen.export!(services, config: @config, options: options.merge(opts))
|
20
37
|
|
21
38
|
enable
|
22
39
|
if execute %w(sudo systemctl daemon-reload)
|
23
40
|
say("Reloaded configuraion (daemon-reload)", :green)
|
24
41
|
end
|
25
42
|
|
26
|
-
|
43
|
+
if options["or-restart"]
|
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)
|
48
|
+
end
|
49
|
+
|
50
|
+
sudoers_rule_content = generate_sudoers_rule(opts[:user])
|
51
|
+
if options["add-to-sudoers"]
|
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
|
68
|
+
end
|
27
69
|
else
|
28
|
-
|
70
|
+
if options["or-restart"]
|
71
|
+
restart
|
72
|
+
else
|
73
|
+
say("App target `#{target_name}` already exists", :red)
|
74
|
+
end
|
29
75
|
end
|
30
76
|
end
|
31
77
|
|
@@ -39,17 +85,20 @@ module Procsd
|
|
39
85
|
|
40
86
|
services.keys.push(target_name).each do |filename|
|
41
87
|
path = File.join(systemd_dir, filename)
|
42
|
-
if File.exist? path
|
43
|
-
execute %W(sudo rm #{path})
|
44
|
-
say "Deleted #{path}"
|
45
|
-
end
|
88
|
+
execute %W(sudo rm #{path}) and say "Deleted #{path}" if File.exist? path
|
46
89
|
end
|
47
90
|
|
48
91
|
if execute %w(sudo systemctl daemon-reload)
|
49
92
|
say("Reloaded configuraion (daemon-reload)", :green)
|
50
93
|
end
|
51
|
-
|
52
94
|
say("App services were stopped, disabled and removed", :green)
|
95
|
+
|
96
|
+
sudoers_file_path = "#{SUDOERS_DIR}/#{app_name}"
|
97
|
+
if File.exist?(sudoers_file_path)
|
98
|
+
if yes?("Remove sudoers rule #{sudoers_file_path} ? (yes/no)")
|
99
|
+
say("Sudoers file removed", :green) if execute %W(sudo rm #{sudoers_file_path})
|
100
|
+
end
|
101
|
+
end
|
53
102
|
else
|
54
103
|
say_target_not_exists
|
55
104
|
end
|
@@ -60,12 +109,9 @@ module Procsd
|
|
60
109
|
preload!
|
61
110
|
say_target_not_exists and return unless target_exist?
|
62
111
|
|
63
|
-
if target_enabled?
|
64
|
-
|
65
|
-
|
66
|
-
if execute %W(sudo systemctl enable #{target_name})
|
67
|
-
say("Enabled app target #{target_name}", :green)
|
68
|
-
end
|
112
|
+
say "Note: app target #{target_name} already enabled" if target_enabled?
|
113
|
+
if execute %W(sudo systemctl enable #{target_name})
|
114
|
+
say("Enabled app target #{target_name}", :green)
|
69
115
|
end
|
70
116
|
end
|
71
117
|
|
@@ -74,12 +120,9 @@ module Procsd
|
|
74
120
|
preload!
|
75
121
|
say_target_not_exists and return unless target_exist?
|
76
122
|
|
77
|
-
if !target_enabled?
|
78
|
-
|
79
|
-
|
80
|
-
if execute %W(sudo systemctl disable #{target_name})
|
81
|
-
say("Disabled app target #{target_name}", :green)
|
82
|
-
end
|
123
|
+
say "Note: app target #{target_name} already disabled" if !target_enabled?
|
124
|
+
if execute %W(sudo systemctl disable #{target_name})
|
125
|
+
say("Disabled app target #{target_name}", :green)
|
83
126
|
end
|
84
127
|
end
|
85
128
|
|
@@ -88,12 +131,9 @@ module Procsd
|
|
88
131
|
preload!
|
89
132
|
say_target_not_exists and return unless target_exist?
|
90
133
|
|
91
|
-
if target_active?
|
92
|
-
|
93
|
-
|
94
|
-
if execute %W(sudo systemctl start #{target_name})
|
95
|
-
say("Started app services (#{target_name})", :green)
|
96
|
-
end
|
134
|
+
say "Note: app target #{target_name} already started/active" if target_active?
|
135
|
+
if execute %W(sudo systemctl start #{target_name})
|
136
|
+
say("Started app services (#{target_name})", :green)
|
97
137
|
end
|
98
138
|
end
|
99
139
|
|
@@ -102,12 +142,9 @@ module Procsd
|
|
102
142
|
preload!
|
103
143
|
say_target_not_exists and return unless target_exist?
|
104
144
|
|
105
|
-
if !target_active?
|
106
|
-
|
107
|
-
|
108
|
-
if execute %W(sudo systemctl stop #{target_name})
|
109
|
-
say("Stopped app services (#{target_name})", :green)
|
110
|
-
end
|
145
|
+
say "Note: app target #{target_name} already stopped/inactive" if !target_active?
|
146
|
+
if execute %W(sudo systemctl stop #{target_name})
|
147
|
+
say("Stopped app services (#{target_name})", :green)
|
111
148
|
end
|
112
149
|
end
|
113
150
|
|
@@ -118,10 +155,10 @@ module Procsd
|
|
118
155
|
|
119
156
|
# If one of the child services of a target has `ExecReload` and `ReloadPropagatedFrom`
|
120
157
|
# options defined, then use `reload-or-restart` to call all services (not the main target)
|
121
|
-
# because https://github.com/systemd/systemd/issues/10638
|
158
|
+
# because of systemd bug https://github.com/systemd/systemd/issues/10638
|
122
159
|
success =
|
123
|
-
if
|
124
|
-
execute %
|
160
|
+
if has_reload?
|
161
|
+
execute %W(sudo systemctl reload-or-restart #{app_name}-* --all)
|
125
162
|
else
|
126
163
|
execute %W(sudo systemctl restart #{target_name})
|
127
164
|
end
|
@@ -139,19 +176,12 @@ module Procsd
|
|
139
176
|
say_target_not_exists and return unless target_exist?
|
140
177
|
|
141
178
|
if options["short"]
|
142
|
-
command = %w(
|
179
|
+
command = %w(systemctl list-units --no-pager --no-legend --all)
|
143
180
|
else
|
144
|
-
command = %w(
|
145
|
-
end
|
146
|
-
|
147
|
-
if options["target"]
|
148
|
-
command << target_name
|
149
|
-
else
|
150
|
-
filtered = filter_services(service_name)
|
151
|
-
say("Can't find any services matching given name: #{service_name}", :red) and return if filtered.empty?
|
152
|
-
command += filtered
|
181
|
+
command = %w(systemctl status --no-pager --output short-iso --all)
|
153
182
|
end
|
154
183
|
|
184
|
+
command << (options["target"] ? target_name : "#{app_name}-#{service_name}*")
|
155
185
|
execute command
|
156
186
|
end
|
157
187
|
|
@@ -164,17 +194,14 @@ module Procsd
|
|
164
194
|
def logs(service_name = nil)
|
165
195
|
preload!
|
166
196
|
|
167
|
-
command = %w(
|
197
|
+
command = %w(journalctl --no-pager --no-hostname --all --output short-iso)
|
168
198
|
command.push("-n", options.fetch("num", "100"))
|
169
199
|
command.push("-f") if options["tail"]
|
170
200
|
command.push("--system") if options["system"]
|
171
201
|
command.push("--priority", options["priority"]) if options["priority"]
|
172
202
|
command.push("--grep", "'" + options["grep"] + "'") if options["grep"]
|
173
203
|
|
174
|
-
|
175
|
-
say("Can't find any services matching given name: #{service_name}", :red) and return if filtered.empty?
|
176
|
-
|
177
|
-
filtered.each { |service| command.push("--unit", service) }
|
204
|
+
command.push("--unit", "#{app_name}-#{service_name}*")
|
178
205
|
execute command
|
179
206
|
end
|
180
207
|
|
@@ -183,7 +210,7 @@ module Procsd
|
|
183
210
|
preload!
|
184
211
|
say_target_not_exists and return unless target_exist?
|
185
212
|
|
186
|
-
execute %W(
|
213
|
+
execute %W(systemctl list-dependencies #{target_name})
|
187
214
|
end
|
188
215
|
|
189
216
|
desc "--version, -v", "Print the version"
|
@@ -191,9 +218,42 @@ module Procsd
|
|
191
218
|
puts VERSION
|
192
219
|
end
|
193
220
|
|
221
|
+
desc "config", "Show configuration. Available types: sudoers"
|
222
|
+
def config(name)
|
223
|
+
preload!
|
224
|
+
|
225
|
+
case name
|
226
|
+
when "sudoers"
|
227
|
+
say generate_sudoers_rule(ENV["USER"])
|
228
|
+
else
|
229
|
+
raise ArgumentError, "Wring type of argument: #{name}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
194
233
|
private
|
195
234
|
|
235
|
+
def generate_sudoers_rule(user)
|
236
|
+
commands = []
|
237
|
+
systemctl_path = `which systemctl`.strip
|
238
|
+
|
239
|
+
%w(start stop restart).each { |cmd| commands << "#{systemctl_path} #{cmd} #{target_name}" }
|
240
|
+
commands << "#{systemctl_path} reload-or-restart #{app_name}-\\* --all" if has_reload?
|
241
|
+
|
242
|
+
"#{user} ALL=NOPASSWD: #{commands.join(', ')}"
|
243
|
+
end
|
244
|
+
|
245
|
+
def has_reload?
|
246
|
+
services.any? { |_, opts| opts["restart"] }
|
247
|
+
end
|
248
|
+
|
249
|
+
def fetch_path_env
|
250
|
+
# get value of the $PATH env variable including ~/.bashrc as well (-i flag)
|
251
|
+
`/bin/bash -ilc 'echo $PATH'`.strip
|
252
|
+
end
|
253
|
+
|
196
254
|
def execute(command)
|
255
|
+
trap("INT") { puts "\nInterrupted" ; exit 130 }
|
256
|
+
|
197
257
|
say("> Executing command: `#{command.join(' ')}`", :yellow) if ENV["VERBOSE"] == "true"
|
198
258
|
system *command
|
199
259
|
end
|
@@ -202,20 +262,12 @@ module Procsd
|
|
202
262
|
say("App target #{target_name} is not exists", :red)
|
203
263
|
end
|
204
264
|
|
205
|
-
def filter_services(service_name)
|
206
|
-
if service_name
|
207
|
-
services.keys.select { |s| s.include?("#{app_name}-#{service_name}") }
|
208
|
-
else
|
209
|
-
services.keys
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
265
|
def target_exist?
|
214
266
|
File.exist?(File.join systemd_dir, target_name)
|
215
267
|
end
|
216
268
|
|
217
269
|
def systemd_dir
|
218
|
-
@
|
270
|
+
@config[:systemd_dir]
|
219
271
|
end
|
220
272
|
|
221
273
|
def target_enabled?
|
@@ -231,15 +283,15 @@ module Procsd
|
|
231
283
|
end
|
232
284
|
|
233
285
|
def app_name
|
234
|
-
@
|
286
|
+
@config[:app]
|
235
287
|
end
|
236
288
|
|
237
289
|
def services
|
238
290
|
all = {}
|
239
|
-
@
|
240
|
-
|
241
|
-
|
242
|
-
all["#{app_name}-#{process_name}.#{i + 1}.service"] =
|
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
|
243
295
|
end
|
244
296
|
end
|
245
297
|
|
@@ -247,32 +299,55 @@ module Procsd
|
|
247
299
|
end
|
248
300
|
|
249
301
|
def preload!
|
250
|
-
|
251
|
-
raise ConfigurationError, ".procsd.yml config file doesn't exists" unless File.exist? ".procsd.yml"
|
302
|
+
@config = {}
|
252
303
|
|
253
|
-
|
254
|
-
|
255
|
-
|
304
|
+
unless system("which", "systemctl", [:out, :err]=>"/dev/null")
|
305
|
+
raise ConfigurationError, "Your OS doesn't has systemctl executable available"
|
306
|
+
end
|
307
|
+
raise ConfigurationError, "Config file procsd.yml doesn't exists" unless File.exist? "procsd.yml"
|
308
|
+
begin
|
309
|
+
procsd = YAML.load(ERB.new(File.read "procsd.yml").result)
|
310
|
+
rescue => e
|
311
|
+
raise ConfigurationError, "Can't read procsd.yml: #{e.inspect}"
|
312
|
+
end
|
256
313
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
314
|
+
raise ConfigurationError, "Missing app name in the procsd.yml file" unless procsd["app"]
|
315
|
+
@config[:app] = procsd["app"]
|
316
|
+
|
317
|
+
# If procsd.yml doesn't contains processes defined, try to read Procfile
|
318
|
+
unless procsd["processes"]
|
319
|
+
msg = "Procfile doesn't exists. Define processes in procsd.yml or create Procfile"
|
320
|
+
raise ConfigurationError, msg unless File.exist? "Procfile"
|
321
|
+
begin
|
322
|
+
procfile = YAML.load_file("Procfile")
|
323
|
+
rescue => e
|
324
|
+
raise ConfigurationError, "Can't read Procfile: #{e.inspect}"
|
264
325
|
end
|
265
326
|
end
|
266
327
|
|
267
|
-
if
|
268
|
-
|
269
|
-
|
328
|
+
if procsd["formation"]
|
329
|
+
formation = procsd["formation"].split(",").map { |f| f.split("=") }.to_h
|
330
|
+
formation.each { |k, v| formation[k] = v.to_i }
|
270
331
|
else
|
271
|
-
|
332
|
+
formation = {}
|
333
|
+
end
|
334
|
+
|
335
|
+
processes = procsd["processes"] || procfile
|
336
|
+
processes.each do |process_name, opts|
|
337
|
+
if opts.kind_of?(Hash)
|
338
|
+
raise ConfigurationError, "Missing start command for `#{process_name}` process" unless opts["start"]
|
339
|
+
else
|
340
|
+
processes[process_name] = { "start" => opts }
|
341
|
+
end
|
342
|
+
|
343
|
+
unless processes[process_name]["count"]
|
344
|
+
processes[process_name]["count"] = formation[process_name] || 1
|
345
|
+
end
|
272
346
|
end
|
273
347
|
|
274
|
-
@
|
275
|
-
@procsd["
|
348
|
+
@config[:processes] = processes
|
349
|
+
@config[:environment] = procsd["environment"] || {}
|
350
|
+
@config[:systemd_dir] = procsd["systemd_dir"] || DEFAULT_SYSTEMD_DIR
|
276
351
|
end
|
277
352
|
end
|
278
353
|
end
|
data/lib/procsd/generator.rb
CHANGED
@@ -6,12 +6,12 @@ module Procsd
|
|
6
6
|
File.dirname(__FILE__)
|
7
7
|
end
|
8
8
|
|
9
|
-
def export!(services,
|
9
|
+
def export!(services, config:, options:)
|
10
10
|
self.destination_root = "/tmp"
|
11
|
-
@
|
12
|
-
say "Systemd directory: #{@
|
11
|
+
@config = config
|
12
|
+
say "Systemd directory: #{@config[:systemd_dir]}"
|
13
13
|
|
14
|
-
app_name = @
|
14
|
+
app_name = @config[:app]
|
15
15
|
target_name = "#{app_name}.target"
|
16
16
|
|
17
17
|
services.each do |service_name, service_command|
|
@@ -19,8 +19,9 @@ module Procsd
|
|
19
19
|
"target_name" => target_name,
|
20
20
|
"id" => service_name.sub(".service", ""),
|
21
21
|
"command" => service_command,
|
22
|
-
"environment" => @
|
22
|
+
"environment" => @config[:environment]
|
23
23
|
)
|
24
|
+
|
24
25
|
generate(service_name, service_config, type: :service)
|
25
26
|
end
|
26
27
|
|
@@ -33,12 +34,14 @@ module Procsd
|
|
33
34
|
|
34
35
|
private
|
35
36
|
|
36
|
-
def generate(filename,
|
37
|
-
template("templates/#{type}.erb", filename,
|
37
|
+
def generate(filename, conf, type:)
|
38
|
+
template("templates/#{type}.erb", filename, conf)
|
38
39
|
|
39
40
|
source_path = File.join(destination_root, filename)
|
40
|
-
dest_path = File.join(@
|
41
|
+
dest_path = File.join(@config[:systemd_dir], filename)
|
41
42
|
system "sudo", "mv", source_path, dest_path
|
43
|
+
ensure
|
44
|
+
File.delete(source_path) if File.exist? source_path
|
42
45
|
end
|
43
46
|
end
|
44
47
|
end
|
@@ -26,6 +26,6 @@ StandardInput=null
|
|
26
26
|
SyslogIdentifier=<%= config["id"] %>
|
27
27
|
|
28
28
|
Environment="PATH=<%= config["path"] %>"
|
29
|
-
<% config["environment"].each do |
|
30
|
-
Environment="<%=
|
29
|
+
<% config["environment"].each do |key, value| -%>
|
30
|
+
Environment="<%= key %>=<%= value %>"
|
31
31
|
<% end -%>
|
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.
|
4
|
+
version: 0.3.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-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|