mrsk 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +45 -14
- data/bin/mrsk +1 -2
- data/lib/mrsk/cli/accessory.rb +37 -10
- data/lib/mrsk/cli/app.rb +36 -19
- data/lib/mrsk/cli/base.rb +10 -0
- data/lib/mrsk/cli/build.rb +5 -4
- data/lib/mrsk/cli/main.rb +27 -14
- data/lib/mrsk/cli/prune.rb +8 -4
- data/lib/mrsk/cli/registry.rb +1 -3
- data/lib/mrsk/cli/server.rb +0 -2
- data/lib/mrsk/cli/templates/template.env +2 -0
- data/lib/mrsk/cli/traefik.rb +16 -6
- data/lib/mrsk/cli.rb +0 -4
- data/lib/mrsk/commander.rb +6 -10
- data/lib/mrsk/commands/accessory.rb +7 -9
- data/lib/mrsk/commands/app.rb +9 -19
- data/lib/mrsk/commands/auditor.rb +34 -0
- data/lib/mrsk/commands/base.rb +9 -2
- data/lib/mrsk/commands/builder/base.rb +0 -2
- data/lib/mrsk/commands/builder/multiarch/remote.rb +0 -2
- data/lib/mrsk/commands/builder/multiarch.rb +0 -2
- data/lib/mrsk/commands/builder/native/remote.rb +0 -2
- data/lib/mrsk/commands/builder/native.rb +0 -2
- data/lib/mrsk/commands/builder.rb +0 -7
- data/lib/mrsk/commands/prune.rb +0 -1
- data/lib/mrsk/commands/registry.rb +0 -2
- data/lib/mrsk/commands/traefik.rb +0 -2
- data/lib/mrsk/configuration/role.rb +6 -1
- data/lib/mrsk/configuration.rb +13 -11
- data/lib/mrsk/version.rb +1 -1
- data/lib/mrsk.rb +6 -2
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bb523ac89ed682e739e8312da6ee60bc45baace7f1d3b836d3fb62b9c75539f
|
4
|
+
data.tar.gz: 5f3552e54648aa2f0c7c795b921c0523a3deca6aeb22d5bd2fa6901dec169e33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afd16123f8681337a1f30c0908221b96c8e9540485126f20a63741ed524bd9f282cc7bdaa1aad50f61f68887f7fe1176435f5dd2f58605780de2866ee141c40c
|
7
|
+
data.tar.gz: e40f284df37ec58990c4a2e2ebd8765641eb797fb83abeed8d02aa857dd40a079793a62c5c98bfe3204c9bddeb8ebf1e2a33dde09cf337050cb4283eb05485df
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# MRSK
|
2
2
|
|
3
|
-
MRSK deploys
|
3
|
+
MRSK deploys web apps in containers to servers running Docker with zero downtime. It uses the dynamic reverse-proxy Traefik to hold requests while the new application container is started and the old one is stopped. It works seamlessly across multiple hosts, using SSHKit to execute commands.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
Install MRSK globally with `gem install mrsk`. Then, inside your app directory, run `mrsk
|
7
|
+
Install MRSK globally with `gem install mrsk`. Then, inside your app directory, run `mrsk init` (or `mrsk init --bundle` within Rails apps where you want a bin/mrsk binstub). Now edit the new file `config/deploy.yml`. It could look as simple as this:
|
8
8
|
|
9
9
|
```yaml
|
10
10
|
service: hey
|
@@ -15,12 +15,17 @@ servers:
|
|
15
15
|
registry:
|
16
16
|
username: registry-user-name
|
17
17
|
password: <%= ENV.fetch("MRSK_REGISTRY_PASSWORD") %>
|
18
|
+
env:
|
19
|
+
secret:
|
20
|
+
- RAILS_MASTER_KEY
|
18
21
|
```
|
19
22
|
|
20
|
-
|
23
|
+
Then edit your `.env` file to add your registry password as `MRSK_REGISTRY_PASSWORD` (and your `RAILS_MASTER_KEY` for production with a Rails app).
|
24
|
+
|
25
|
+
Now you're ready to deploy to the servers:
|
21
26
|
|
22
27
|
```
|
23
|
-
|
28
|
+
mrsk deploy
|
24
29
|
```
|
25
30
|
|
26
31
|
This will:
|
@@ -68,10 +73,27 @@ registry:
|
|
68
73
|
|
69
74
|
### Using a different SSH user than root
|
70
75
|
|
71
|
-
The default SSH user is root, but you can change it using `
|
76
|
+
The default SSH user is root, but you can change it using `ssh/user`:
|
77
|
+
|
78
|
+
```yaml
|
79
|
+
ssh:
|
80
|
+
user: app
|
81
|
+
```
|
82
|
+
|
83
|
+
### Using a proxy SSH host
|
84
|
+
|
85
|
+
If you need to connect to server through a proxy host, you can use `ssh/proxy`:
|
86
|
+
|
87
|
+
```yaml
|
88
|
+
ssh:
|
89
|
+
proxy: "192.168.0.1" # defaults to root as the user
|
90
|
+
```
|
91
|
+
|
92
|
+
Or with specific user:
|
72
93
|
|
73
94
|
```yaml
|
74
|
-
|
95
|
+
ssh:
|
96
|
+
proxy: "app@192.168.0.1"
|
75
97
|
```
|
76
98
|
|
77
99
|
### Using env variables
|
@@ -265,14 +287,6 @@ ARG RUBY_VERSION
|
|
265
287
|
FROM ruby:$RUBY_VERSION-slim as base
|
266
288
|
```
|
267
289
|
|
268
|
-
### Using without RAILS_MASTER_KEY
|
269
|
-
|
270
|
-
If you're using MRSK with older Rails apps that predate RAILS_MASTER_KEY, or with a non-Rails app, you can skip the default usage and reference:
|
271
|
-
|
272
|
-
```yaml
|
273
|
-
skip_master_key: true
|
274
|
-
```
|
275
|
-
|
276
290
|
### Using accessories for database, cache, search services
|
277
291
|
|
278
292
|
You can manage your accessory services via MRSK as well. The services will build off public images, and will not be automatically updated when you deploy:
|
@@ -300,6 +314,23 @@ accessories:
|
|
300
314
|
|
301
315
|
Now run `mrsk accessory start mysql` to start the MySQL server on 1.1.1.3. See `mrsk accessory` for all the commands possible.
|
302
316
|
|
317
|
+
### Using a generated .env file
|
318
|
+
|
319
|
+
If you're using a centralized secret store, like 1Password, you can create `.env.erb` as a template which looks up the secrets. Example of a .env.erb file:
|
320
|
+
|
321
|
+
```erb
|
322
|
+
<% if (session_token = `op signin --account my-one-password-account --raw`.strip) != "" %># Generated by mrsk envify
|
323
|
+
GITHUB_TOKEN=<%= `gh config get -h github.com oauth_token`.strip %>
|
324
|
+
MRSK_REGISTRY_PASSWORD=<%= `op read "op://Vault/Docker Hub/password" -n --session #{session_token}` %>
|
325
|
+
RAILS_MASTER_KEY=<%= `op read "op://Vault/My App/RAILS_MASTER_SECRET" -n --session #{session_token}` %>
|
326
|
+
MYSQL_ROOT_PASSWORD=<%= `op read "op://Vault/My App/MYSQL_ROOT_PASSWORD" -n --session #{session_token}` %>
|
327
|
+
<% else raise ArgumentError, "Session token missing" end %>
|
328
|
+
```
|
329
|
+
|
330
|
+
This template can safely be checked into git. Then everyone deploying the app can run `mrsk envify` when they setup the app for the first time or passwords change to get the correct `.env` file.
|
331
|
+
|
332
|
+
If you need separate env variables for different destinations, you can set them with `.env.destination.erb` for the template, which will generate `.env.staging` when run with `mrsk envify -d staging`.
|
333
|
+
|
303
334
|
## Commands
|
304
335
|
|
305
336
|
### Running commands on servers
|
data/bin/mrsk
CHANGED
data/lib/mrsk/cli/accessory.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "mrsk/cli/base"
|
2
|
-
|
3
1
|
class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
4
2
|
desc "boot [NAME]", "Boot accessory service on host (use NAME=all to boot all accessories)"
|
5
3
|
def boot(name)
|
@@ -9,7 +7,11 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
9
7
|
with_accessory(name) do |accessory|
|
10
8
|
directories(name)
|
11
9
|
upload(name)
|
12
|
-
|
10
|
+
|
11
|
+
on(accessory.host) do
|
12
|
+
execute *MRSK.auditor.record("accessory #{name} boot"), verbosity: :debug
|
13
|
+
execute *accessory.run
|
14
|
+
end
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -18,6 +20,8 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
18
20
|
def upload(name)
|
19
21
|
with_accessory(name) do |accessory|
|
20
22
|
on(accessory.host) do
|
23
|
+
execute *MRSK.auditor.record("accessory #{name} upload files"), verbosity: :debug
|
24
|
+
|
21
25
|
accessory.files.each do |(local, remote)|
|
22
26
|
accessory.ensure_local_file_present(local)
|
23
27
|
|
@@ -33,6 +37,8 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
33
37
|
def directories(name)
|
34
38
|
with_accessory(name) do |accessory|
|
35
39
|
on(accessory.host) do
|
40
|
+
execute *MRSK.auditor.record("accessory #{name} create directories"), verbosity: :debug
|
41
|
+
|
36
42
|
accessory.directories.keys.each do |host_path|
|
37
43
|
execute *accessory.make_directory(host_path)
|
38
44
|
end
|
@@ -52,14 +58,20 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
52
58
|
desc "start [NAME]", "Start existing accessory on host"
|
53
59
|
def start(name)
|
54
60
|
with_accessory(name) do |accessory|
|
55
|
-
on(accessory.host)
|
61
|
+
on(accessory.host) do
|
62
|
+
execute *MRSK.auditor.record("accessory #{name} start"), verbosity: :debug
|
63
|
+
execute *accessory.start
|
64
|
+
end
|
56
65
|
end
|
57
66
|
end
|
58
67
|
|
59
68
|
desc "stop [NAME]", "Stop accessory on host"
|
60
69
|
def stop(name)
|
61
70
|
with_accessory(name) do |accessory|
|
62
|
-
on(accessory.host)
|
71
|
+
on(accessory.host) do
|
72
|
+
execute *MRSK.auditor.record("accessory #{name} stop"), verbosity: :debug
|
73
|
+
execute *accessory.stop, raise_on_non_zero_exit: false
|
74
|
+
end
|
63
75
|
end
|
64
76
|
end
|
65
77
|
|
@@ -98,11 +110,17 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
98
110
|
|
99
111
|
when options[:reuse]
|
100
112
|
say "Launching command from existing container...", :magenta
|
101
|
-
on(accessory.host)
|
113
|
+
on(accessory.host) do
|
114
|
+
execute *MRSK.auditor.record("accessory #{name} cmd '#{cmd}'"), verbosity: :debug
|
115
|
+
capture_with_info(*accessory.execute_in_existing_container(cmd))
|
116
|
+
end
|
102
117
|
|
103
118
|
else
|
104
119
|
say "Launching command from new container...", :magenta
|
105
|
-
on(accessory.host)
|
120
|
+
on(accessory.host) do
|
121
|
+
execute *MRSK.auditor.record("accessory #{name} cmd '#{cmd}'"), verbosity: :debug
|
122
|
+
capture_with_info(*accessory.execute_in_new_container(cmd))
|
123
|
+
end
|
106
124
|
end
|
107
125
|
end
|
108
126
|
end
|
@@ -150,21 +168,30 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
150
168
|
desc "remove_container [NAME]", "Remove accessory container from host"
|
151
169
|
def remove_container(name)
|
152
170
|
with_accessory(name) do |accessory|
|
153
|
-
on(accessory.host)
|
171
|
+
on(accessory.host) do
|
172
|
+
execute *MRSK.auditor.record("accessory #{name} remove container"), verbosity: :debug
|
173
|
+
execute *accessory.remove_container
|
174
|
+
end
|
154
175
|
end
|
155
176
|
end
|
156
177
|
|
157
178
|
desc "remove_image [NAME]", "Remove accessory image from host"
|
158
179
|
def remove_image(name)
|
159
180
|
with_accessory(name) do |accessory|
|
160
|
-
on(accessory.host)
|
181
|
+
on(accessory.host) do
|
182
|
+
execute *MRSK.auditor.record("accessory #{name} remove image"), verbosity: :debug
|
183
|
+
execute *accessory.remove_image
|
184
|
+
end
|
161
185
|
end
|
162
186
|
end
|
163
187
|
|
164
188
|
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host"
|
165
189
|
def remove_service_directory(name)
|
166
190
|
with_accessory(name) do |accessory|
|
167
|
-
on(accessory.host)
|
191
|
+
on(accessory.host) do
|
192
|
+
execute *MRSK.auditor.record("accessory #{name} remove service directory"), verbosity: :debug
|
193
|
+
execute *accessory.remove_service_directory
|
194
|
+
end
|
168
195
|
end
|
169
196
|
end
|
170
197
|
|
data/lib/mrsk/cli/app.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "mrsk/cli/base"
|
2
|
-
|
3
1
|
class Mrsk::Cli::App < Mrsk::Cli::Base
|
4
2
|
desc "boot", "Boot app on servers (or reboot app if already running)"
|
5
3
|
def boot
|
@@ -14,6 +12,8 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
14
12
|
|
15
13
|
MRSK.config.roles.each do |role|
|
16
14
|
on(role.hosts) do |host|
|
15
|
+
execute *MRSK.auditor.record("app boot version #{version}"), verbosity: :debug
|
16
|
+
|
17
17
|
begin
|
18
18
|
execute *MRSK.app.run(role: role.name)
|
19
19
|
rescue SSHKit::Command::Failed => e
|
@@ -33,19 +33,25 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
33
33
|
|
34
34
|
desc "start", "Start existing app on servers (use --version=<git-hash> to designate specific version)"
|
35
35
|
def start
|
36
|
-
on(MRSK.hosts)
|
36
|
+
on(MRSK.hosts) do
|
37
|
+
execute *MRSK.auditor.record("app start version #{MRSK.version}"), verbosity: :debug
|
38
|
+
execute *MRSK.app.start, raise_on_non_zero_exit: false
|
39
|
+
end
|
37
40
|
end
|
38
|
-
|
41
|
+
|
39
42
|
desc "stop", "Stop app on servers"
|
40
43
|
def stop
|
41
|
-
on(MRSK.hosts)
|
44
|
+
on(MRSK.hosts) do
|
45
|
+
execute *MRSK.auditor.record("app stop"), verbosity: :debug
|
46
|
+
execute *MRSK.app.stop, raise_on_non_zero_exit: false
|
47
|
+
end
|
42
48
|
end
|
43
|
-
|
49
|
+
|
44
50
|
desc "details", "Display details about app containers"
|
45
51
|
def details
|
46
52
|
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.info) }
|
47
53
|
end
|
48
|
-
|
54
|
+
|
49
55
|
desc "exec [CMD]", "Execute a custom command on servers"
|
50
56
|
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
|
51
57
|
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
|
@@ -68,15 +74,22 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
68
74
|
when options[:reuse]
|
69
75
|
say "Get current version of running container...", :magenta unless options[:version]
|
70
76
|
using_version(options[:version] || current_running_version) do |version|
|
71
|
-
say "Launching command with version #{version} from existing container
|
72
|
-
|
77
|
+
say "Launching command with version #{version} from existing container...", :magenta
|
78
|
+
|
79
|
+
on(MRSK.hosts) do |host|
|
80
|
+
execute *MRSK.auditor.record("app cmd '#{cmd}' with version #{version}"), verbosity: :debug
|
81
|
+
puts_by_host host, capture_with_info(*MRSK.app.execute_in_existing_container(cmd))
|
82
|
+
end
|
73
83
|
end
|
74
84
|
|
75
85
|
else
|
76
86
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
77
87
|
using_version(options[:version] || most_recent_version_available) do |version|
|
78
|
-
say "Launching command with version #{version} from new container
|
79
|
-
on(MRSK.hosts)
|
88
|
+
say "Launching command with version #{version} from new container...", :magenta
|
89
|
+
on(MRSK.hosts) do |host|
|
90
|
+
execute *MRSK.auditor.record("app cmd '#{cmd}' with version #{version}"), verbosity: :debug
|
91
|
+
puts_by_host host, capture_with_info(*MRSK.app.execute_in_new_container(cmd))
|
92
|
+
end
|
80
93
|
end
|
81
94
|
end
|
82
95
|
end
|
@@ -91,11 +104,6 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
91
104
|
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.list_images) }
|
92
105
|
end
|
93
106
|
|
94
|
-
desc "current", "Return the current running container ID"
|
95
|
-
def current
|
96
|
-
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.current_container_id) }
|
97
|
-
end
|
98
|
-
|
99
107
|
desc "logs", "Show lines from app on servers"
|
100
108
|
option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
101
109
|
option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
|
@@ -134,17 +142,26 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
134
142
|
|
135
143
|
desc "remove_container [VERSION]", "Remove app container with given version from servers"
|
136
144
|
def remove_container(version)
|
137
|
-
on(MRSK.hosts)
|
145
|
+
on(MRSK.hosts) do
|
146
|
+
execute *MRSK.auditor.record("app remove container #{version}"), verbosity: :debug
|
147
|
+
execute *MRSK.app.remove_container(version: version)
|
148
|
+
end
|
138
149
|
end
|
139
150
|
|
140
151
|
desc "remove_containers", "Remove all app containers from servers"
|
141
152
|
def remove_containers
|
142
|
-
on(MRSK.hosts)
|
153
|
+
on(MRSK.hosts) do
|
154
|
+
execute *MRSK.auditor.record("app remove containers"), verbosity: :debug
|
155
|
+
execute *MRSK.app.remove_containers
|
156
|
+
end
|
143
157
|
end
|
144
158
|
|
145
159
|
desc "remove_images", "Remove all app images from servers"
|
146
160
|
def remove_images
|
147
|
-
on(MRSK.hosts)
|
161
|
+
on(MRSK.hosts) do
|
162
|
+
execute *MRSK.auditor.record("app remove images"), verbosity: :debug
|
163
|
+
execute *MRSK.app.remove_images
|
164
|
+
end
|
148
165
|
end
|
149
166
|
|
150
167
|
desc "current_version", "Shows the version currently running"
|
data/lib/mrsk/cli/base.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "thor"
|
2
|
+
require "dotenv"
|
2
3
|
require "mrsk/sshkit_with_ext"
|
3
4
|
|
4
5
|
module Mrsk::Cli
|
@@ -21,10 +22,19 @@ module Mrsk::Cli
|
|
21
22
|
|
22
23
|
def initialize(*)
|
23
24
|
super
|
25
|
+
load_envs
|
24
26
|
initialize_commander(options)
|
25
27
|
end
|
26
28
|
|
27
29
|
private
|
30
|
+
def load_envs
|
31
|
+
if destination = options[:destination]
|
32
|
+
Dotenv.load(".env.#{destination}", ".env")
|
33
|
+
else
|
34
|
+
Dotenv.load(".env")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
28
38
|
def initialize_commander(options)
|
29
39
|
MRSK.tap do |commander|
|
30
40
|
commander.config_file = Pathname.new(File.expand_path(options[:config_file]))
|
data/lib/mrsk/cli/build.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "mrsk/cli/base"
|
2
|
-
|
3
1
|
class Mrsk::Cli::Build < Mrsk::Cli::Base
|
4
2
|
desc "deliver", "Deliver a newly built app image to servers"
|
5
3
|
def deliver
|
@@ -11,7 +9,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
11
9
|
def push
|
12
10
|
cli = self
|
13
11
|
|
14
|
-
run_locally do
|
12
|
+
run_locally do
|
15
13
|
begin
|
16
14
|
MRSK.with_verbosity(:debug) { execute *MRSK.builder.push }
|
17
15
|
rescue SSHKit::Command::Failed => e
|
@@ -30,7 +28,10 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
30
28
|
|
31
29
|
desc "pull", "Pull app image from the registry onto servers"
|
32
30
|
def pull
|
33
|
-
on(MRSK.hosts)
|
31
|
+
on(MRSK.hosts) do
|
32
|
+
execute *MRSK.auditor.record("build pull image #{MRSK.version}"), verbosity: :debug
|
33
|
+
execute *MRSK.builder.pull
|
34
|
+
end
|
34
35
|
end
|
35
36
|
|
36
37
|
desc "create", "Create a local build setup"
|
data/lib/mrsk/cli/main.rb
CHANGED
@@ -1,13 +1,3 @@
|
|
1
|
-
require "mrsk/cli/base"
|
2
|
-
|
3
|
-
require "mrsk/cli/accessory"
|
4
|
-
require "mrsk/cli/app"
|
5
|
-
require "mrsk/cli/build"
|
6
|
-
require "mrsk/cli/prune"
|
7
|
-
require "mrsk/cli/registry"
|
8
|
-
require "mrsk/cli/server"
|
9
|
-
require "mrsk/cli/traefik"
|
10
|
-
|
11
1
|
class Mrsk::Cli::Main < Mrsk::Cli::Base
|
12
2
|
desc "setup", "Setup all accessories and deploy the app to servers"
|
13
3
|
def setup
|
@@ -70,6 +60,13 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
70
60
|
invoke "mrsk:cli:accessory:details", [ "all" ]
|
71
61
|
end
|
72
62
|
|
63
|
+
desc "audit", "Show audit log from servers"
|
64
|
+
def audit
|
65
|
+
on(MRSK.hosts) do |host|
|
66
|
+
puts_by_host host, capture_with_info(*MRSK.auditor.reveal)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
73
70
|
desc "config", "Show combined config"
|
74
71
|
def config
|
75
72
|
run_locally do
|
@@ -77,22 +74,29 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
77
74
|
end
|
78
75
|
end
|
79
76
|
|
80
|
-
desc "
|
81
|
-
option :
|
82
|
-
def
|
77
|
+
desc "init", "Create config stub in config/deploy.yml and env stub in .env"
|
78
|
+
option :bundle, type: :boolean, default: false, desc: "Add MRSK to the Gemfile and create a bin/mrsk binstub"
|
79
|
+
def init
|
83
80
|
require "fileutils"
|
84
81
|
|
85
82
|
if (deploy_file = Pathname.new(File.expand_path("config/deploy.yml"))).exist?
|
86
83
|
puts "Config file already exists in config/deploy.yml (remove first to create a new one)"
|
87
84
|
else
|
85
|
+
FileUtils.mkdir_p deploy_file.dirname
|
88
86
|
FileUtils.cp_r Pathname.new(File.expand_path("templates/deploy.yml", __dir__)), deploy_file
|
89
87
|
puts "Created configuration file in config/deploy.yml"
|
90
88
|
end
|
91
89
|
|
92
|
-
unless
|
90
|
+
unless (deploy_file = Pathname.new(File.expand_path(".env"))).exist?
|
91
|
+
FileUtils.cp_r Pathname.new(File.expand_path("templates/template.env", __dir__)), deploy_file
|
92
|
+
puts "Created .env file"
|
93
|
+
end
|
94
|
+
|
95
|
+
if options[:bundle]
|
93
96
|
if (binstub = Pathname.new(File.expand_path("bin/mrsk"))).exist?
|
94
97
|
puts "Binstub already exists in bin/mrsk (remove first to create a new one)"
|
95
98
|
else
|
99
|
+
puts "Adding MRSK to Gemfile and bundle..."
|
96
100
|
`bundle add mrsk`
|
97
101
|
`bundle binstubs mrsk`
|
98
102
|
puts "Created binstub file in bin/mrsk"
|
@@ -100,6 +104,15 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
100
104
|
end
|
101
105
|
end
|
102
106
|
|
107
|
+
desc "envify", "Create .env by evaluating .env.erb (or .env.staging.erb -> .env.staging when using -d staging)"
|
108
|
+
def envify
|
109
|
+
if destination = options[:destination]
|
110
|
+
File.write(".env.#{destination}", ERB.new(IO.read(Pathname.new(File.expand_path(".env.#{destination}.erb")))).result)
|
111
|
+
else
|
112
|
+
File.write(".env", ERB.new(IO.read(Pathname.new(File.expand_path(".env.erb")))).result)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
103
116
|
desc "remove", "Remove Traefik, app, and registry session from servers"
|
104
117
|
def remove
|
105
118
|
invoke "mrsk:cli:traefik:remove"
|
data/lib/mrsk/cli/prune.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "mrsk/cli/base"
|
2
|
-
|
3
1
|
class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
4
2
|
desc "all", "Prune unused images and stopped containers"
|
5
3
|
def all
|
@@ -9,11 +7,17 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
9
7
|
|
10
8
|
desc "images", "Prune unused images older than 30 days"
|
11
9
|
def images
|
12
|
-
on(MRSK.hosts)
|
10
|
+
on(MRSK.hosts) do
|
11
|
+
execute *MRSK.auditor.record("prune images"), verbosity: :debug
|
12
|
+
execute *MRSK.prune.images
|
13
|
+
end
|
13
14
|
end
|
14
15
|
|
15
16
|
desc "containers", "Prune stopped containers for the service older than 3 days"
|
16
17
|
def containers
|
17
|
-
on(MRSK.hosts)
|
18
|
+
on(MRSK.hosts) do
|
19
|
+
execute *MRSK.auditor.record("prune containers"), verbosity: :debug
|
20
|
+
execute *MRSK.prune.containers
|
21
|
+
end
|
18
22
|
end
|
19
23
|
end
|
data/lib/mrsk/cli/registry.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
require "mrsk/cli/base"
|
2
|
-
|
3
1
|
class Mrsk::Cli::Registry < Mrsk::Cli::Base
|
4
2
|
desc "login", "Login to the registry locally and remotely"
|
5
3
|
def login
|
6
|
-
run_locally
|
4
|
+
run_locally { execute *MRSK.registry.login }
|
7
5
|
on(MRSK.hosts) { execute *MRSK.registry.login }
|
8
6
|
rescue ArgumentError => e
|
9
7
|
puts e.message
|
data/lib/mrsk/cli/server.rb
CHANGED
data/lib/mrsk/cli/traefik.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "mrsk/cli/base"
|
2
|
-
|
3
1
|
class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
4
2
|
desc "boot", "Boot Traefik on servers"
|
5
3
|
def boot
|
@@ -15,12 +13,18 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
15
13
|
|
16
14
|
desc "start", "Start existing Traefik on servers"
|
17
15
|
def start
|
18
|
-
on(MRSK.traefik_hosts)
|
16
|
+
on(MRSK.traefik_hosts) do
|
17
|
+
execute *MRSK.auditor.record("traefik start"), verbosity: :debug
|
18
|
+
execute *MRSK.traefik.start, raise_on_non_zero_exit: false
|
19
|
+
end
|
19
20
|
end
|
20
21
|
|
21
22
|
desc "stop", "Stop Traefik on servers"
|
22
23
|
def stop
|
23
|
-
on(MRSK.traefik_hosts)
|
24
|
+
on(MRSK.traefik_hosts) do
|
25
|
+
execute *MRSK.auditor.record("traefik stop"), verbosity: :debug
|
26
|
+
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
27
|
+
end
|
24
28
|
end
|
25
29
|
|
26
30
|
desc "restart", "Restart Traefik on servers"
|
@@ -67,11 +71,17 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
67
71
|
|
68
72
|
desc "remove_container", "Remove Traefik container from servers"
|
69
73
|
def remove_container
|
70
|
-
on(MRSK.traefik_hosts)
|
74
|
+
on(MRSK.traefik_hosts) do
|
75
|
+
execute *MRSK.auditor.record("traefik remove container"), verbosity: :debug
|
76
|
+
execute *MRSK.traefik.remove_container
|
77
|
+
end
|
71
78
|
end
|
72
79
|
|
73
80
|
desc "remove_container", "Remove Traefik image from servers"
|
74
81
|
def remove_image
|
75
|
-
on(MRSK.traefik_hosts)
|
82
|
+
on(MRSK.traefik_hosts) do
|
83
|
+
execute *MRSK.auditor.record("traefik remove image"), verbosity: :debug
|
84
|
+
execute *MRSK.traefik.remove_image
|
85
|
+
end
|
76
86
|
end
|
77
87
|
end
|
data/lib/mrsk/cli.rb
CHANGED
data/lib/mrsk/commander.rb
CHANGED
@@ -1,13 +1,5 @@
|
|
1
1
|
require "active_support/core_ext/enumerable"
|
2
2
|
|
3
|
-
require "mrsk/configuration"
|
4
|
-
require "mrsk/commands/accessory"
|
5
|
-
require "mrsk/commands/app"
|
6
|
-
require "mrsk/commands/builder"
|
7
|
-
require "mrsk/commands/prune"
|
8
|
-
require "mrsk/commands/traefik"
|
9
|
-
require "mrsk/commands/registry"
|
10
|
-
|
11
3
|
class Mrsk::Commander
|
12
4
|
attr_accessor :config_file, :destination, :verbosity, :version
|
13
5
|
|
@@ -77,8 +69,12 @@ class Mrsk::Commander
|
|
77
69
|
Mrsk::Commands::Accessory.new(config, name: name)
|
78
70
|
end
|
79
71
|
|
72
|
+
def auditor
|
73
|
+
@auditor ||= Mrsk::Commands::Auditor.new(config)
|
74
|
+
end
|
75
|
+
|
80
76
|
|
81
|
-
def with_verbosity(level)
|
77
|
+
def with_verbosity(level)
|
82
78
|
old_level = SSHKit.config.output_verbosity
|
83
79
|
SSHKit.config.output_verbosity = level
|
84
80
|
yield
|
@@ -89,7 +85,7 @@ class Mrsk::Commander
|
|
89
85
|
# Test-induced damage!
|
90
86
|
def reset
|
91
87
|
@config = @config_file = @destination = @version = nil
|
92
|
-
@app = @builder = @traefik = @registry = @prune = nil
|
88
|
+
@app = @builder = @traefik = @registry = @prune = @auditor = nil
|
93
89
|
@verbosity = :info
|
94
90
|
end
|
95
91
|
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require "mrsk/commands/base"
|
2
|
-
|
3
1
|
class Mrsk::Commands::Accessory < Mrsk::Commands::Base
|
4
2
|
attr_reader :accessory_config
|
5
3
|
delegate :service_name, :image, :host, :port, :files, :directories, :env_args, :volume_args, :label_args, to: :accessory_config
|
@@ -10,7 +8,7 @@ class Mrsk::Commands::Accessory < Mrsk::Commands::Base
|
|
10
8
|
end
|
11
9
|
|
12
10
|
def run
|
13
|
-
docker :run,
|
11
|
+
docker :run,
|
14
12
|
"--name", service_name,
|
15
13
|
"-d",
|
16
14
|
"--restart", "unless-stopped",
|
@@ -41,10 +39,10 @@ class Mrsk::Commands::Accessory < Mrsk::Commands::Base
|
|
41
39
|
end
|
42
40
|
|
43
41
|
def follow_logs(grep: nil)
|
44
|
-
run_over_ssh
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
run_over_ssh \
|
43
|
+
pipe \
|
44
|
+
docker(:logs, service_name, "-t", "-n", "10", "-f", "2>&1"),
|
45
|
+
(%(grep "#{grep}") if grep)
|
48
46
|
end
|
49
47
|
|
50
48
|
|
@@ -66,11 +64,11 @@ class Mrsk::Commands::Accessory < Mrsk::Commands::Base
|
|
66
64
|
end
|
67
65
|
|
68
66
|
def execute_in_existing_container_over_ssh(*command)
|
69
|
-
run_over_ssh execute_in_existing_container(*command, interactive: true)
|
67
|
+
run_over_ssh execute_in_existing_container(*command, interactive: true)
|
70
68
|
end
|
71
69
|
|
72
70
|
def execute_in_new_container_over_ssh(*command)
|
73
|
-
run_over_ssh execute_in_new_container(*command, interactive: true)
|
71
|
+
run_over_ssh execute_in_new_container(*command, interactive: true)
|
74
72
|
end
|
75
73
|
|
76
74
|
def run_over_ssh(command)
|
data/lib/mrsk/commands/app.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "mrsk/commands/base"
|
2
|
-
|
3
1
|
class Mrsk::Commands::App < Mrsk::Commands::Base
|
4
2
|
def run(role: :web)
|
5
3
|
role = config.role(role)
|
@@ -8,7 +6,6 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
|
8
6
|
"-d",
|
9
7
|
"--restart unless-stopped",
|
10
8
|
"--name", service_with_version,
|
11
|
-
*rails_master_key_arg,
|
12
9
|
*role.env_args,
|
13
10
|
*config.volume_args,
|
14
11
|
*role.label_args,
|
@@ -37,11 +34,13 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
|
37
34
|
end
|
38
35
|
|
39
36
|
def follow_logs(host:, grep: nil)
|
40
|
-
run_over_ssh
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
run_over_ssh \
|
38
|
+
pipe(
|
39
|
+
current_container_id,
|
40
|
+
"xargs docker logs -t -n 10 -f 2>&1",
|
41
|
+
(%(grep "#{grep}") if grep)
|
42
|
+
),
|
43
|
+
host: host
|
45
44
|
end
|
46
45
|
|
47
46
|
|
@@ -56,7 +55,6 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
|
56
55
|
docker :run,
|
57
56
|
("-it" if interactive),
|
58
57
|
"--rm",
|
59
|
-
*rails_master_key_arg,
|
60
58
|
*config.env_args,
|
61
59
|
*config.volume_args,
|
62
60
|
config.absolute_image,
|
@@ -64,11 +62,11 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
|
64
62
|
end
|
65
63
|
|
66
64
|
def execute_in_existing_container_over_ssh(*command, host:)
|
67
|
-
run_over_ssh execute_in_existing_container(*command, interactive: true)
|
65
|
+
run_over_ssh execute_in_existing_container(*command, interactive: true), host: host
|
68
66
|
end
|
69
67
|
|
70
68
|
def execute_in_new_container_over_ssh(*command, host:)
|
71
|
-
run_over_ssh execute_in_new_container(*command, interactive: true)
|
69
|
+
run_over_ssh execute_in_new_container(*command, interactive: true), host: host
|
72
70
|
end
|
73
71
|
|
74
72
|
|
@@ -130,12 +128,4 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
|
130
128
|
def service_filter
|
131
129
|
[ "--filter", "label=service=#{config.service}" ]
|
132
130
|
end
|
133
|
-
|
134
|
-
def rails_master_key_arg
|
135
|
-
if master_key = config.master_key
|
136
|
-
[ "-e", redact("RAILS_MASTER_KEY=#{master_key}") ]
|
137
|
-
else
|
138
|
-
[]
|
139
|
-
end
|
140
|
-
end
|
141
131
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "active_support/core_ext/time/conversions"
|
2
|
+
|
3
|
+
class Mrsk::Commands::Auditor < Mrsk::Commands::Base
|
4
|
+
def record(line)
|
5
|
+
append \
|
6
|
+
[ :echo, tagged_line(line) ],
|
7
|
+
audit_log_file
|
8
|
+
end
|
9
|
+
|
10
|
+
def reveal
|
11
|
+
[ :tail, "-n", 50, audit_log_file ]
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def audit_log_file
|
16
|
+
"mrsk-#{config.service}-audit.log"
|
17
|
+
end
|
18
|
+
|
19
|
+
def tagged_line(line)
|
20
|
+
"'#{tags} #{line}'"
|
21
|
+
end
|
22
|
+
|
23
|
+
def tags
|
24
|
+
"[#{timestamp}] [#{performer}]"
|
25
|
+
end
|
26
|
+
|
27
|
+
def performer
|
28
|
+
`whoami`.strip
|
29
|
+
end
|
30
|
+
|
31
|
+
def timestamp
|
32
|
+
Time.now.to_fs(:db)
|
33
|
+
end
|
34
|
+
end
|
data/lib/mrsk/commands/base.rb
CHANGED
@@ -8,8 +8,11 @@ module Mrsk::Commands
|
|
8
8
|
@config = config
|
9
9
|
end
|
10
10
|
|
11
|
-
def run_over_ssh(command, host:)
|
12
|
-
"ssh
|
11
|
+
def run_over_ssh(*command, host:)
|
12
|
+
"ssh".tap do |cmd|
|
13
|
+
cmd << " -J #{config.ssh_proxy.jump_proxies}" if config.ssh_proxy
|
14
|
+
cmd << " -t #{config.ssh_user}@#{host} '#{command.join(" ")}'"
|
15
|
+
end
|
13
16
|
end
|
14
17
|
|
15
18
|
private
|
@@ -28,6 +31,10 @@ module Mrsk::Commands
|
|
28
31
|
combine *commands, by: "|"
|
29
32
|
end
|
30
33
|
|
34
|
+
def append(*commands)
|
35
|
+
combine *commands, by: ">>"
|
36
|
+
end
|
37
|
+
|
31
38
|
def xargs(command)
|
32
39
|
[ :xargs, command ].flatten
|
33
40
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require "mrsk/commands/base"
|
2
|
-
|
3
1
|
class Mrsk::Commands::Builder < Mrsk::Commands::Base
|
4
2
|
delegate :create, :remove, :push, :pull, :info, to: :target
|
5
3
|
|
@@ -36,8 +34,3 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
|
|
36
34
|
@multiarch_remote ||= Mrsk::Commands::Builder::Multiarch::Remote.new(config)
|
37
35
|
end
|
38
36
|
end
|
39
|
-
|
40
|
-
require "mrsk/commands/builder/native"
|
41
|
-
require "mrsk/commands/builder/native/remote"
|
42
|
-
require "mrsk/commands/builder/multiarch"
|
43
|
-
require "mrsk/commands/builder/multiarch/remote"
|
data/lib/mrsk/commands/prune.rb
CHANGED
@@ -96,7 +96,12 @@ class Mrsk::Configuration::Role
|
|
96
96
|
def merged_env_with_secrets
|
97
97
|
merged_env.tap do |new_env|
|
98
98
|
new_env["secret"] = Array(config.env["secret"]) + Array(specialized_env["secret"])
|
99
|
-
|
99
|
+
|
100
|
+
# If there's no secret/clear split, everything is clear
|
101
|
+
clear_app_env = config.env["secret"] ? Array(config.env["clear"]) : Array(config.env["clear"] || config.env)
|
102
|
+
clear_role_env = specialized_env["secret"] ? Array(specialized_env["clear"]) : Array(specialized_env["clear"] || specialized_env)
|
103
|
+
|
104
|
+
new_env["clear"] = (clear_app_env + clear_role_env).uniq
|
100
105
|
end
|
101
106
|
end
|
102
107
|
end
|
data/lib/mrsk/configuration.rb
CHANGED
@@ -3,7 +3,7 @@ require "active_support/core_ext/string/inquiry"
|
|
3
3
|
require "active_support/core_ext/module/delegation"
|
4
4
|
require "pathname"
|
5
5
|
require "erb"
|
6
|
-
require "
|
6
|
+
require "net/ssh/proxy/jump"
|
7
7
|
|
8
8
|
class Mrsk::Configuration
|
9
9
|
delegate :service, :image, :servers, :env, :labels, :registry, :builder, to: :raw_config, allow_nil: true
|
@@ -104,17 +104,22 @@ class Mrsk::Configuration
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def ssh_user
|
107
|
-
raw_config.
|
107
|
+
if raw_config.ssh.present?
|
108
|
+
raw_config.ssh["user"] || "root"
|
109
|
+
else
|
110
|
+
"root"
|
111
|
+
end
|
108
112
|
end
|
109
113
|
|
110
|
-
def
|
111
|
-
|
114
|
+
def ssh_proxy
|
115
|
+
if raw_config.ssh.present? && raw_config.ssh["proxy"]
|
116
|
+
Net::SSH::Proxy::Jump.new \
|
117
|
+
raw_config.ssh["proxy"].include?("@") ? raw_config.ssh["proxy"] : "root@#{raw_config.ssh["proxy"]}"
|
118
|
+
end
|
112
119
|
end
|
113
120
|
|
114
|
-
def
|
115
|
-
|
116
|
-
ENV["RAILS_MASTER_KEY"] || File.read(Pathname.new(File.expand_path("config/master.key")))
|
117
|
-
end
|
121
|
+
def ssh_options
|
122
|
+
{ user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ] }.compact
|
118
123
|
end
|
119
124
|
|
120
125
|
|
@@ -171,6 +176,3 @@ class Mrsk::Configuration
|
|
171
176
|
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
172
177
|
end
|
173
178
|
end
|
174
|
-
|
175
|
-
require "mrsk/configuration/role"
|
176
|
-
require "mrsk/configuration/accessory"
|
data/lib/mrsk/version.rb
CHANGED
data/lib/mrsk.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mrsk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '2.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: zeitwerk
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.5'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.5'
|
69
83
|
description:
|
70
84
|
email: dhh@hey.com
|
71
85
|
executables:
|
@@ -87,11 +101,13 @@ files:
|
|
87
101
|
- lib/mrsk/cli/registry.rb
|
88
102
|
- lib/mrsk/cli/server.rb
|
89
103
|
- lib/mrsk/cli/templates/deploy.yml
|
104
|
+
- lib/mrsk/cli/templates/template.env
|
90
105
|
- lib/mrsk/cli/traefik.rb
|
91
106
|
- lib/mrsk/commander.rb
|
92
107
|
- lib/mrsk/commands.rb
|
93
108
|
- lib/mrsk/commands/accessory.rb
|
94
109
|
- lib/mrsk/commands/app.rb
|
110
|
+
- lib/mrsk/commands/auditor.rb
|
95
111
|
- lib/mrsk/commands/base.rb
|
96
112
|
- lib/mrsk/commands/builder.rb
|
97
113
|
- lib/mrsk/commands/builder/base.rb
|