mrsk 0.13.1 → 0.14.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 +51 -5
- data/lib/mrsk/cli/accessory.rb +11 -11
- data/lib/mrsk/cli/app.rb +9 -9
- data/lib/mrsk/cli/base.rb +43 -19
- data/lib/mrsk/cli/build.rb +5 -5
- data/lib/mrsk/cli/lock.rb +1 -1
- data/lib/mrsk/cli/main.rb +11 -5
- data/lib/mrsk/cli/prune.rb +5 -4
- data/lib/mrsk/cli/templates/sample_hooks/pre-deploy.sample +109 -0
- data/lib/mrsk/cli/traefik.rb +8 -8
- data/lib/mrsk/commands/app.rb +4 -3
- data/lib/mrsk/commands/base.rb +5 -1
- data/lib/mrsk/commands/builder/base.rb +12 -16
- data/lib/mrsk/commands/builder/multiarch/remote.rb +6 -14
- data/lib/mrsk/commands/builder/native/cached.rb +16 -0
- data/lib/mrsk/commands/builder/native/remote.rb +3 -11
- data/lib/mrsk/commands/builder/native.rb +2 -2
- data/lib/mrsk/commands/builder.rb +9 -3
- data/lib/mrsk/commands/prune.rb +20 -2
- data/lib/mrsk/commands/traefik.rb +5 -3
- data/lib/mrsk/configuration/builder.rb +114 -0
- data/lib/mrsk/configuration.rb +24 -7
- data/lib/mrsk/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39d645ed2ca51ebf7edeaca7502b9b9d83456ad4fd11d5b905903de2ee76f189
|
4
|
+
data.tar.gz: c8fe80b16c14e89fd333224b887968620a88741c32a8b7cb4ee1cf9c37c1dcd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa4264e442bc470b02ebd946b4667535b9e5e8795cf9cb22c311693f381a0a9671c67d16dd6f1ae6391e48b8e7b2be51fc49671bccd2610e3ec98afd75002a12
|
7
|
+
data.tar.gz: 408f3500ff87847217b08f21c86a189b77e73f54d3b1a407a7e16a9f73fea20a883233cf8970f9f5fb6b6ebdcd69d3a47ec5006e42b7190cffd41c1c5b9bfb60
|
data/README.md
CHANGED
@@ -67,7 +67,7 @@ Voila! All the servers are now serving the app on port 80. If you're just runnin
|
|
67
67
|
|
68
68
|
In the past decade+, there's been an explosion in commercial offerings that make deploying web apps easier. Heroku kicked it off with an incredible offering that stayed ahead of the competition seemingly forever. These days we have excellent alternatives like Fly.io and Render. And hosted Kubernetes is making things easier too on AWS, GCP, Digital Ocean, and elsewhere. But these are all offerings that have you renting computers in the cloud at a premium. If you want to run on your own hardware, or even just have a clear migration path to do so in the future, you need to carefully consider how locked in you get to these commercial platforms. Preferably before the bills swallow your business whole!
|
69
69
|
|
70
|
-
MRSK seeks to bring the advance in ergonomics pioneered by these commercial offerings to deploying web apps anywhere. Whether that's low-cost cloud options without the managed-service markup from the likes of Digital Ocean, Hetzner, OVH, etc
|
70
|
+
MRSK seeks to bring the advance in ergonomics pioneered by these commercial offerings to deploying web apps anywhere. Whether that's low-cost cloud options without the managed-service markup from the likes of Digital Ocean, Hetzner, OVH, etc., or it's your own colocated bare metal. To MRSK, it's all the same. Feed the config file a list of IP addresses with vanilla Ubuntu servers that have seen no prep beyond an added SSH key, and you'll be running in literally minutes.
|
71
71
|
|
72
72
|
This approach gives you enormous portability. You can have your web app deployed on several clouds at ease like this. Or you can buy the baseline with your own hardware, then deploy to a cloud before a big seasonal spike to get more capacity. When you're not locked into a single provider from a tooling perspective, there are a lot of compelling options available.
|
73
73
|
|
@@ -380,6 +380,16 @@ servers:
|
|
380
380
|
|
381
381
|
That'll start the job containers with `docker run ... --cap-add --cpu-count 4 ...`.
|
382
382
|
|
383
|
+
### Setting a minimum version
|
384
|
+
|
385
|
+
You can set the minimum MRSK version with:
|
386
|
+
|
387
|
+
```yaml
|
388
|
+
minimum_version: 0.13.3
|
389
|
+
```
|
390
|
+
|
391
|
+
Note: versions <= 0.13.2 will ignore this setting.
|
392
|
+
|
383
393
|
### Configuring logging
|
384
394
|
|
385
395
|
You can configure the logging driver and options passed to Docker using `logging`:
|
@@ -463,6 +473,37 @@ builder:
|
|
463
473
|
context: ".."
|
464
474
|
```
|
465
475
|
|
476
|
+
### Using multistage builder cache
|
477
|
+
|
478
|
+
Docker multistage build cache can singlehandedly speed up your builds by a lot. Currently MRSK only supports using the GHA cache or the Registry cache:
|
479
|
+
|
480
|
+
```yaml
|
481
|
+
# Using GHA cache
|
482
|
+
builder:
|
483
|
+
cache:
|
484
|
+
type: gha
|
485
|
+
|
486
|
+
# Using Registry cache
|
487
|
+
builder:
|
488
|
+
cache:
|
489
|
+
type: registry
|
490
|
+
|
491
|
+
# Using Registry cache with different cache image
|
492
|
+
builder:
|
493
|
+
cache:
|
494
|
+
type: registry
|
495
|
+
# default image name is <image>-build-cache
|
496
|
+
image: application-cache-image
|
497
|
+
|
498
|
+
# Using Registry cache with additinonal cache-to options
|
499
|
+
builder:
|
500
|
+
cache:
|
501
|
+
type: registry
|
502
|
+
options: mode=max,image-manifest=true,oci-mediatypes=true
|
503
|
+
```
|
504
|
+
|
505
|
+
For further insights into build cache optimization, check out documentation on Docker's official website: https://docs.docker.com/build/cache/.
|
506
|
+
|
466
507
|
### Using build secrets for new images
|
467
508
|
|
468
509
|
Some images need a secret passed in during build time, like a GITHUB_TOKEN, to give access to private gem repositories. This can be done by having the secret in ENV, then referencing it in the builder configuration:
|
@@ -670,7 +711,7 @@ This assumes the Cron settings are stored in `config/crontab`.
|
|
670
711
|
|
671
712
|
### Healthcheck
|
672
713
|
|
673
|
-
MRSK uses Docker
|
714
|
+
MRSK uses Docker healthchecks to check the health of your application during deployment. Traefik uses this same healthcheck status to determine when a container is ready to receive traffic.
|
674
715
|
|
675
716
|
The healthcheck defaults to testing the HTTP response to the path `/up` on port 3000, up to 7 times. You can tailor this behaviour with the `healthcheck` setting:
|
676
717
|
|
@@ -840,7 +881,7 @@ Message: Automatic deploy lock
|
|
840
881
|
You can also manually acquire and release the lock
|
841
882
|
|
842
883
|
```
|
843
|
-
mrsk lock acquire -m "Doing
|
884
|
+
mrsk lock acquire -m "Doing maintenance"
|
844
885
|
```
|
845
886
|
|
846
887
|
```
|
@@ -882,11 +923,13 @@ firing a JSON webhook. These variables include:
|
|
882
923
|
- `MRSK_PERFORMER` - the local user performing the command (from `whoami`)
|
883
924
|
- `MRSK_SERVICE_VERSION` - an abbreviated service and version for use in messages, e.g. app@150b24f
|
884
925
|
- `MRSK_VERSION` - an full version being deployed
|
885
|
-
- `MRSK_DESTINATION` - optional: destination, e.g. "staging"
|
886
926
|
- `MRSK_HOSTS` - a comma separated list of the hosts targeted by the command
|
927
|
+
- `MRSK_COMMAND` - The command we are running
|
928
|
+
- `MRSK_SUBCOMMAND` - optional: The subcommand we are running
|
929
|
+
- `MRSK_DESTINATION` - optional: destination, e.g. "staging"
|
887
930
|
- `MRSK_ROLE` - optional: role targeted, e.g. "web"
|
888
931
|
|
889
|
-
There are
|
932
|
+
There are four hooks:
|
890
933
|
|
891
934
|
1. pre-connect
|
892
935
|
Called before taking the deploy lock. For checks that need to run before connecting to remote hosts - e.g. DNS warming.
|
@@ -894,6 +937,9 @@ Called before taking the deploy lock. For checks that need to run before connect
|
|
894
937
|
2. pre-build
|
895
938
|
Used for pre-build checks - e.g. there are no uncommitted changes or that CI has passed.
|
896
939
|
|
940
|
+
3. pre-deploy
|
941
|
+
For final checks before deploying, e.g. checking CI completed
|
942
|
+
|
897
943
|
3. post-deploy - run after a deploy, redeploy or rollback
|
898
944
|
|
899
945
|
This hook is also passed a `MRSK_RUNTIME` env variable.
|
data/lib/mrsk/cli/accessory.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
2
2
|
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
|
3
3
|
def boot(name)
|
4
|
-
|
4
|
+
mutating do
|
5
5
|
if name == "all"
|
6
6
|
MRSK.accessory_names.each { |accessory_name| boot(accessory_name) }
|
7
7
|
else
|
@@ -21,7 +21,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
21
21
|
|
22
22
|
desc "upload [NAME]", "Upload accessory files to host", hide: true
|
23
23
|
def upload(name)
|
24
|
-
|
24
|
+
mutating do
|
25
25
|
with_accessory(name) do |accessory|
|
26
26
|
on(accessory.hosts) do
|
27
27
|
accessory.files.each do |(local, remote)|
|
@@ -38,7 +38,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
38
38
|
|
39
39
|
desc "directories [NAME]", "Create accessory directories on host", hide: true
|
40
40
|
def directories(name)
|
41
|
-
|
41
|
+
mutating do
|
42
42
|
with_accessory(name) do |accessory|
|
43
43
|
on(accessory.hosts) do
|
44
44
|
accessory.directories.keys.each do |host_path|
|
@@ -51,7 +51,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
51
51
|
|
52
52
|
desc "reboot [NAME]", "Reboot existing accessory on host (stop container, remove container, start new container)"
|
53
53
|
def reboot(name)
|
54
|
-
|
54
|
+
mutating do
|
55
55
|
with_accessory(name) do |accessory|
|
56
56
|
stop(name)
|
57
57
|
remove_container(name)
|
@@ -62,7 +62,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
62
62
|
|
63
63
|
desc "start [NAME]", "Start existing accessory container on host"
|
64
64
|
def start(name)
|
65
|
-
|
65
|
+
mutating do
|
66
66
|
with_accessory(name) do |accessory|
|
67
67
|
on(accessory.hosts) do
|
68
68
|
execute *MRSK.auditor.record("Started #{name} accessory"), verbosity: :debug
|
@@ -74,7 +74,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
74
74
|
|
75
75
|
desc "stop [NAME]", "Stop existing accessory container on host"
|
76
76
|
def stop(name)
|
77
|
-
|
77
|
+
mutating do
|
78
78
|
with_accessory(name) do |accessory|
|
79
79
|
on(accessory.hosts) do
|
80
80
|
execute *MRSK.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
@@ -86,7 +86,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
86
86
|
|
87
87
|
desc "restart [NAME]", "Restart existing accessory container on host"
|
88
88
|
def restart(name)
|
89
|
-
|
89
|
+
mutating do
|
90
90
|
with_accessory(name) do
|
91
91
|
stop(name)
|
92
92
|
start(name)
|
@@ -165,7 +165,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
165
165
|
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
166
166
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
167
167
|
def remove(name)
|
168
|
-
|
168
|
+
mutating do
|
169
169
|
if name == "all"
|
170
170
|
MRSK.accessory_names.each { |accessory_name| remove(accessory_name) }
|
171
171
|
else
|
@@ -183,7 +183,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
183
183
|
|
184
184
|
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
185
185
|
def remove_container(name)
|
186
|
-
|
186
|
+
mutating do
|
187
187
|
with_accessory(name) do |accessory|
|
188
188
|
on(accessory.hosts) do
|
189
189
|
execute *MRSK.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
@@ -195,7 +195,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
195
195
|
|
196
196
|
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
197
197
|
def remove_image(name)
|
198
|
-
|
198
|
+
mutating do
|
199
199
|
with_accessory(name) do |accessory|
|
200
200
|
on(accessory.hosts) do
|
201
201
|
execute *MRSK.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
@@ -207,7 +207,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
207
207
|
|
208
208
|
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
209
209
|
def remove_service_directory(name)
|
210
|
-
|
210
|
+
mutating do
|
211
211
|
with_accessory(name) do |accessory|
|
212
212
|
on(accessory.hosts) do
|
213
213
|
execute *accessory.remove_service_directory
|
data/lib/mrsk/cli/app.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Cli::App < Mrsk::Cli::Base
|
2
2
|
desc "boot", "Boot app on servers (or reboot app if already running)"
|
3
3
|
def boot
|
4
|
-
|
4
|
+
mutating do
|
5
5
|
hold_lock_on_error do
|
6
6
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
7
7
|
using_version(version_or_latest) do |version|
|
@@ -29,7 +29,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
29
29
|
execute *auditor.record("Booted app version #{version}"), verbosity: :debug
|
30
30
|
|
31
31
|
old_version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
32
|
-
execute *app.start_or_run
|
32
|
+
execute *app.start_or_run(hostname: "#{host}-#{SecureRandom.hex(6)}")
|
33
33
|
|
34
34
|
Mrsk::Utils::HealthcheckPoller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
|
35
35
|
|
@@ -43,7 +43,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
43
43
|
|
44
44
|
desc "start", "Start existing app container on servers"
|
45
45
|
def start
|
46
|
-
|
46
|
+
mutating do
|
47
47
|
on(MRSK.hosts) do |host|
|
48
48
|
roles = MRSK.roles_on(host)
|
49
49
|
|
@@ -57,7 +57,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
57
57
|
|
58
58
|
desc "stop", "Stop app container on servers"
|
59
59
|
def stop
|
60
|
-
|
60
|
+
mutating do
|
61
61
|
on(MRSK.hosts) do |host|
|
62
62
|
roles = MRSK.roles_on(host)
|
63
63
|
|
@@ -135,7 +135,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
135
135
|
desc "stale_containers", "Detect app stale containers"
|
136
136
|
option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the stale containers found"
|
137
137
|
def stale_containers
|
138
|
-
|
138
|
+
mutating do
|
139
139
|
stop = options[:stop]
|
140
140
|
|
141
141
|
cli = self
|
@@ -202,7 +202,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
202
202
|
|
203
203
|
desc "remove", "Remove app containers and images from servers"
|
204
204
|
def remove
|
205
|
-
|
205
|
+
mutating do
|
206
206
|
stop
|
207
207
|
remove_containers
|
208
208
|
remove_images
|
@@ -211,7 +211,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
211
211
|
|
212
212
|
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
213
213
|
def remove_container(version)
|
214
|
-
|
214
|
+
mutating do
|
215
215
|
on(MRSK.hosts) do |host|
|
216
216
|
roles = MRSK.roles_on(host)
|
217
217
|
|
@@ -225,7 +225,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
225
225
|
|
226
226
|
desc "remove_containers", "Remove all app containers from servers", hide: true
|
227
227
|
def remove_containers
|
228
|
-
|
228
|
+
mutating do
|
229
229
|
on(MRSK.hosts) do |host|
|
230
230
|
roles = MRSK.roles_on(host)
|
231
231
|
|
@@ -239,7 +239,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
239
239
|
|
240
240
|
desc "remove_images", "Remove all app images from servers", hide: true
|
241
241
|
def remove_images
|
242
|
-
|
242
|
+
mutating do
|
243
243
|
on(MRSK.hosts) do
|
244
244
|
execute *MRSK.auditor.record("Removed all app images"), verbosity: :debug
|
245
245
|
execute *MRSK.app.remove_images
|
data/lib/mrsk/cli/base.rb
CHANGED
@@ -72,28 +72,28 @@ module Mrsk::Cli
|
|
72
72
|
puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
|
73
73
|
end
|
74
74
|
|
75
|
-
def
|
76
|
-
if MRSK.holding_lock?
|
77
|
-
yield
|
78
|
-
else
|
79
|
-
run_hook "pre-connect"
|
75
|
+
def mutating
|
76
|
+
return yield if MRSK.holding_lock?
|
80
77
|
|
81
|
-
|
78
|
+
MRSK.config.ensure_env_available
|
82
79
|
|
83
|
-
|
84
|
-
yield
|
85
|
-
rescue
|
86
|
-
if MRSK.hold_lock_on_error?
|
87
|
-
error " \e[31mDeploy lock was not released\e[0m"
|
88
|
-
else
|
89
|
-
release_lock
|
90
|
-
end
|
80
|
+
run_hook "pre-connect"
|
91
81
|
|
92
|
-
|
82
|
+
acquire_lock
|
83
|
+
|
84
|
+
begin
|
85
|
+
yield
|
86
|
+
rescue
|
87
|
+
if MRSK.hold_lock_on_error?
|
88
|
+
error " \e[31mDeploy lock was not released\e[0m"
|
89
|
+
else
|
90
|
+
release_lock
|
93
91
|
end
|
94
92
|
|
95
|
-
|
93
|
+
raise
|
96
94
|
end
|
95
|
+
|
96
|
+
release_lock
|
97
97
|
end
|
98
98
|
|
99
99
|
def acquire_lock
|
@@ -133,15 +133,39 @@ module Mrsk::Cli
|
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
def run_hook(hook, **
|
136
|
+
def run_hook(hook, **extra_details)
|
137
137
|
if !options[:skip_hooks] && MRSK.hook.hook_exists?(hook)
|
138
|
+
details = { hosts: MRSK.hosts.join(","), command: command, subcommand: subcommand }
|
139
|
+
|
138
140
|
say "Running the #{hook} hook...", :magenta
|
139
141
|
run_locally do
|
140
|
-
MRSK.with_verbosity(:debug) { execute *MRSK.hook.run(hook, **details,
|
142
|
+
MRSK.with_verbosity(:debug) { execute *MRSK.hook.run(hook, **details, **extra_details) }
|
141
143
|
rescue SSHKit::Command::Failed
|
142
144
|
raise HookError.new("Hook `#{hook}` failed")
|
143
145
|
end
|
144
146
|
end
|
145
147
|
end
|
146
|
-
|
148
|
+
|
149
|
+
def command
|
150
|
+
@mrsk_command ||= begin
|
151
|
+
invocation_class, invocation_commands = *first_invocation
|
152
|
+
if invocation_class == Mrsk::Cli::Main
|
153
|
+
invocation_commands[0]
|
154
|
+
else
|
155
|
+
Mrsk::Cli::Main.subcommand_classes.find { |command, clazz| clazz == invocation_class }[0]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def subcommand
|
161
|
+
@mrsk_subcommand ||= begin
|
162
|
+
invocation_class, invocation_commands = *first_invocation
|
163
|
+
invocation_commands[0] if invocation_class != Mrsk::Cli::Main
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def first_invocation
|
168
|
+
instance_variable_get("@_invocations").first
|
169
|
+
end
|
170
|
+
end
|
147
171
|
end
|
data/lib/mrsk/cli/build.rb
CHANGED
@@ -3,7 +3,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
3
3
|
|
4
4
|
desc "deliver", "Build app and push app image to registry then pull image on servers"
|
5
5
|
def deliver
|
6
|
-
|
6
|
+
mutating do
|
7
7
|
push
|
8
8
|
pull
|
9
9
|
end
|
@@ -11,7 +11,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
11
11
|
|
12
12
|
desc "push", "Build and push app image to registry"
|
13
13
|
def push
|
14
|
-
|
14
|
+
mutating do
|
15
15
|
cli = self
|
16
16
|
|
17
17
|
verify_local_dependencies
|
@@ -37,7 +37,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
37
37
|
|
38
38
|
desc "pull", "Pull app image from registry onto servers"
|
39
39
|
def pull
|
40
|
-
|
40
|
+
mutating do
|
41
41
|
on(MRSK.hosts) do
|
42
42
|
execute *MRSK.auditor.record("Pulled image with version #{MRSK.config.version}"), verbosity: :debug
|
43
43
|
execute *MRSK.builder.clean, raise_on_non_zero_exit: false
|
@@ -48,7 +48,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
48
48
|
|
49
49
|
desc "create", "Create a build setup"
|
50
50
|
def create
|
51
|
-
|
51
|
+
mutating do
|
52
52
|
run_locally do
|
53
53
|
begin
|
54
54
|
debug "Using builder: #{MRSK.builder.name}"
|
@@ -67,7 +67,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
67
67
|
|
68
68
|
desc "remove", "Remove build setup"
|
69
69
|
def remove
|
70
|
-
|
70
|
+
mutating do
|
71
71
|
run_locally do
|
72
72
|
debug "Using builder: #{MRSK.builder.name}"
|
73
73
|
execute *MRSK.builder.remove
|
data/lib/mrsk/cli/lock.rb
CHANGED
@@ -7,7 +7,7 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
|
|
7
7
|
end
|
8
8
|
|
9
9
|
desc "acquire", "Acquire the deploy lock"
|
10
|
-
option :message, aliases: "-m", type: :string, desc: "A lock
|
10
|
+
option :message, aliases: "-m", type: :string, desc: "A lock message", required: true
|
11
11
|
def acquire
|
12
12
|
message = options[:message]
|
13
13
|
raise_if_locked do
|
data/lib/mrsk/cli/main.rb
CHANGED
@@ -2,7 +2,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
2
2
|
desc "setup", "Setup all accessories and deploy app to servers"
|
3
3
|
def setup
|
4
4
|
print_runtime do
|
5
|
-
|
5
|
+
mutating do
|
6
6
|
invoke "mrsk:cli:server:bootstrap"
|
7
7
|
invoke "mrsk:cli:accessory:boot", [ "all" ]
|
8
8
|
deploy
|
@@ -14,7 +14,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
14
14
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
15
15
|
def deploy
|
16
16
|
runtime = print_runtime do
|
17
|
-
|
17
|
+
mutating do
|
18
18
|
invoke_options = deploy_options
|
19
19
|
|
20
20
|
say "Log into image registry...", :magenta
|
@@ -28,6 +28,8 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
28
28
|
invoke "mrsk:cli:build:deliver", [], invoke_options
|
29
29
|
end
|
30
30
|
|
31
|
+
run_hook "pre-deploy"
|
32
|
+
|
31
33
|
say "Ensure Traefik is running...", :magenta
|
32
34
|
invoke "mrsk:cli:traefik:boot", [], invoke_options
|
33
35
|
|
@@ -51,7 +53,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
51
53
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
52
54
|
def redeploy
|
53
55
|
runtime = print_runtime do
|
54
|
-
|
56
|
+
mutating do
|
55
57
|
invoke_options = deploy_options
|
56
58
|
|
57
59
|
if options[:skip_push]
|
@@ -62,6 +64,8 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
62
64
|
invoke "mrsk:cli:build:deliver", [], invoke_options
|
63
65
|
end
|
64
66
|
|
67
|
+
run_hook "pre-deploy"
|
68
|
+
|
65
69
|
say "Ensure app can pass healthcheck...", :magenta
|
66
70
|
invoke "mrsk:cli:healthcheck:perform", [], invoke_options
|
67
71
|
|
@@ -79,13 +83,15 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
79
83
|
def rollback(version)
|
80
84
|
rolled_back = false
|
81
85
|
runtime = print_runtime do
|
82
|
-
|
86
|
+
mutating do
|
83
87
|
invoke_options = deploy_options
|
84
88
|
|
85
89
|
MRSK.config.version = version
|
86
90
|
old_version = nil
|
87
91
|
|
88
92
|
if container_available?(version)
|
93
|
+
run_hook "pre-deploy"
|
94
|
+
|
89
95
|
invoke "mrsk:cli:app:boot", [], invoke_options.merge(version: version)
|
90
96
|
rolled_back = true
|
91
97
|
else
|
@@ -174,7 +180,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
174
180
|
desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
|
175
181
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
176
182
|
def remove
|
177
|
-
|
183
|
+
mutating do
|
178
184
|
if options[:confirmed] || ask("This will remove all containers and images. Are you sure?", limited_to: %w( y N ), default: "N") == "y"
|
179
185
|
invoke "mrsk:cli:traefik:remove", [], options.without(:confirmed)
|
180
186
|
invoke "mrsk:cli:app:remove", [], options.without(:confirmed)
|
data/lib/mrsk/cli/prune.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
2
2
|
desc "all", "Prune unused images and stopped containers"
|
3
3
|
def all
|
4
|
-
|
4
|
+
mutating do
|
5
5
|
containers
|
6
6
|
images
|
7
7
|
end
|
@@ -9,17 +9,18 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
9
9
|
|
10
10
|
desc "images", "Prune dangling images"
|
11
11
|
def images
|
12
|
-
|
12
|
+
mutating do
|
13
13
|
on(MRSK.hosts) do
|
14
14
|
execute *MRSK.auditor.record("Pruned images"), verbosity: :debug
|
15
|
-
execute *MRSK.prune.
|
15
|
+
execute *MRSK.prune.dangling_images
|
16
|
+
execute *MRSK.prune.tagged_images
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
21
|
desc "containers", "Prune all stopped containers, except the last 5"
|
21
22
|
def containers
|
22
|
-
|
23
|
+
mutating do
|
23
24
|
on(MRSK.hosts) do
|
24
25
|
execute *MRSK.auditor.record("Pruned containers"), verbosity: :debug
|
25
26
|
execute *MRSK.prune.containers
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# A sample pre-deploy hook
|
4
|
+
#
|
5
|
+
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
|
6
|
+
#
|
7
|
+
# Fails unless the combined status is "success"
|
8
|
+
#
|
9
|
+
# These environment variables are available:
|
10
|
+
# MRSK_RECORDED_AT
|
11
|
+
# MRSK_PERFORMER
|
12
|
+
# MRSK_VERSION
|
13
|
+
# MRSK_HOSTS
|
14
|
+
# MRSK_COMMAND
|
15
|
+
# MRSK_SUBCOMMAND
|
16
|
+
# MRSK_ROLE (if set)
|
17
|
+
# MRSK_DESTINATION (if set)
|
18
|
+
|
19
|
+
# Only check the build status for production deployments
|
20
|
+
if ENV["MRSK_COMMAND"] == "rollback" || ENV["MRSK_DESTINATION"] != "production"
|
21
|
+
exit 0
|
22
|
+
end
|
23
|
+
|
24
|
+
require "bundler/inline"
|
25
|
+
|
26
|
+
# true = install gems so this is fast on repeat invocations
|
27
|
+
gemfile(true, quiet: true) do
|
28
|
+
source "https://rubygems.org"
|
29
|
+
|
30
|
+
gem "octokit"
|
31
|
+
gem "faraday-retry"
|
32
|
+
end
|
33
|
+
|
34
|
+
MAX_ATTEMPTS = 72
|
35
|
+
ATTEMPTS_GAP = 10
|
36
|
+
|
37
|
+
def exit_with_error(message)
|
38
|
+
$stderr.puts message
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
class GithubStatusChecks
|
43
|
+
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
47
|
+
@git_sha = `git rev-parse HEAD`.strip
|
48
|
+
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
49
|
+
refresh!
|
50
|
+
end
|
51
|
+
|
52
|
+
def refresh!
|
53
|
+
@combined_status = github_client.combined_status(remote_url, git_sha)
|
54
|
+
end
|
55
|
+
|
56
|
+
def state
|
57
|
+
combined_status[:state]
|
58
|
+
end
|
59
|
+
|
60
|
+
def first_status_url
|
61
|
+
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
62
|
+
first_status && first_status[:target_url]
|
63
|
+
end
|
64
|
+
|
65
|
+
def complete_count
|
66
|
+
combined_status[:statuses].count { |status| status[:state] != "pending"}
|
67
|
+
end
|
68
|
+
|
69
|
+
def total_count
|
70
|
+
combined_status[:statuses].count
|
71
|
+
end
|
72
|
+
|
73
|
+
def current_status
|
74
|
+
if total_count > 0
|
75
|
+
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
|
76
|
+
else
|
77
|
+
"Build not started..."
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
$stdout.sync = true
|
84
|
+
|
85
|
+
puts "Checking build status..."
|
86
|
+
attempts = 0
|
87
|
+
checks = GithubStatusChecks.new
|
88
|
+
|
89
|
+
begin
|
90
|
+
loop do
|
91
|
+
case checks.state
|
92
|
+
when "success"
|
93
|
+
puts "Checks passed, see #{checks.first_status_url}"
|
94
|
+
exit 0
|
95
|
+
when "failure"
|
96
|
+
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
97
|
+
when "pending"
|
98
|
+
attempts += 1
|
99
|
+
end
|
100
|
+
|
101
|
+
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
102
|
+
|
103
|
+
puts checks.current_status
|
104
|
+
sleep(ATTEMPTS_GAP)
|
105
|
+
checks.refresh!
|
106
|
+
end
|
107
|
+
rescue Octokit::NotFound
|
108
|
+
exit_with_error "Build status could not be found"
|
109
|
+
end
|
data/lib/mrsk/cli/traefik.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
2
2
|
desc "boot", "Boot Traefik on servers"
|
3
3
|
def boot
|
4
|
-
|
4
|
+
mutating do
|
5
5
|
on(MRSK.traefik_hosts) do
|
6
6
|
execute *MRSK.registry.login
|
7
7
|
execute *MRSK.traefik.run, raise_on_non_zero_exit: false
|
@@ -11,7 +11,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
11
11
|
|
12
12
|
desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
|
13
13
|
def reboot
|
14
|
-
|
14
|
+
mutating do
|
15
15
|
stop
|
16
16
|
remove_container
|
17
17
|
boot
|
@@ -20,7 +20,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
20
20
|
|
21
21
|
desc "start", "Start existing Traefik container on servers"
|
22
22
|
def start
|
23
|
-
|
23
|
+
mutating do
|
24
24
|
on(MRSK.traefik_hosts) do
|
25
25
|
execute *MRSK.auditor.record("Started traefik"), verbosity: :debug
|
26
26
|
execute *MRSK.traefik.start, raise_on_non_zero_exit: false
|
@@ -30,7 +30,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
30
30
|
|
31
31
|
desc "stop", "Stop existing Traefik container on servers"
|
32
32
|
def stop
|
33
|
-
|
33
|
+
mutating do
|
34
34
|
on(MRSK.traefik_hosts) do
|
35
35
|
execute *MRSK.auditor.record("Stopped traefik"), verbosity: :debug
|
36
36
|
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
@@ -40,7 +40,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
40
40
|
|
41
41
|
desc "restart", "Restart existing Traefik container on servers"
|
42
42
|
def restart
|
43
|
-
|
43
|
+
mutating do
|
44
44
|
stop
|
45
45
|
start
|
46
46
|
end
|
@@ -77,7 +77,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
77
77
|
|
78
78
|
desc "remove", "Remove Traefik container and image from servers"
|
79
79
|
def remove
|
80
|
-
|
80
|
+
mutating do
|
81
81
|
stop
|
82
82
|
remove_container
|
83
83
|
remove_image
|
@@ -86,7 +86,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
86
86
|
|
87
87
|
desc "remove_container", "Remove Traefik container from servers", hide: true
|
88
88
|
def remove_container
|
89
|
-
|
89
|
+
mutating do
|
90
90
|
on(MRSK.traefik_hosts) do
|
91
91
|
execute *MRSK.auditor.record("Removed traefik container"), verbosity: :debug
|
92
92
|
execute *MRSK.traefik.remove_container
|
@@ -96,7 +96,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
96
96
|
|
97
97
|
desc "remove_image", "Remove Traefik image from servers", hide: true
|
98
98
|
def remove_image
|
99
|
-
|
99
|
+
mutating do
|
100
100
|
on(MRSK.traefik_hosts) do
|
101
101
|
execute *MRSK.auditor.record("Removed traefik image"), verbosity: :debug
|
102
102
|
execute *MRSK.traefik.remove_image
|
data/lib/mrsk/commands/app.rb
CHANGED
@@ -8,17 +8,18 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
|
8
8
|
@role = role
|
9
9
|
end
|
10
10
|
|
11
|
-
def start_or_run
|
12
|
-
combine start, run, by: "||"
|
11
|
+
def start_or_run(hostname: nil)
|
12
|
+
combine start, run(hostname: hostname), by: "||"
|
13
13
|
end
|
14
14
|
|
15
|
-
def run
|
15
|
+
def run(hostname: nil)
|
16
16
|
role = config.role(self.role)
|
17
17
|
|
18
18
|
docker :run,
|
19
19
|
"--detach",
|
20
20
|
"--restart unless-stopped",
|
21
21
|
"--name", container_name,
|
22
|
+
*(["--hostname", hostname] if hostname),
|
22
23
|
"-e", "MRSK_CONTAINER_NAME=\"#{container_name}\"",
|
23
24
|
*role.env_args,
|
24
25
|
*role.health_check_args,
|
data/lib/mrsk/commands/base.rb
CHANGED
@@ -13,7 +13,11 @@ module Mrsk::Commands
|
|
13
13
|
|
14
14
|
def run_over_ssh(*command, host:)
|
15
15
|
"ssh".tap do |cmd|
|
16
|
-
|
16
|
+
if config.ssh_proxy && config.ssh_proxy.is_a?(Net::SSH::Proxy::Jump)
|
17
|
+
cmd << " -J #{config.ssh_proxy.jump_proxies}"
|
18
|
+
elsif config.ssh_proxy && config.ssh_proxy.is_a?(Net::SSH::Proxy::Command)
|
19
|
+
cmd << " -o ProxyCommand='#{config.ssh_proxy.command_line_template}'"
|
20
|
+
end
|
17
21
|
cmd << " -t #{config.ssh_user}@#{host} '#{command.join(" ")}'"
|
18
22
|
end
|
19
23
|
end
|
@@ -3,6 +3,7 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
3
3
|
class BuilderError < StandardError; end
|
4
4
|
|
5
5
|
delegate :argumentize, to: Mrsk::Utils
|
6
|
+
delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, to: :builder_config
|
6
7
|
|
7
8
|
def clean
|
8
9
|
docker :image, :rm, "--force", config.absolute_image
|
@@ -13,11 +14,11 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def build_options
|
16
|
-
[ *build_tags, *build_labels, *build_args, *build_secrets, *build_dockerfile ]
|
17
|
+
[ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile ]
|
17
18
|
end
|
18
19
|
|
19
20
|
def build_context
|
20
|
-
context
|
21
|
+
config.builder.context
|
21
22
|
end
|
22
23
|
|
23
24
|
|
@@ -26,6 +27,13 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
26
27
|
[ "-t", config.absolute_image, "-t", config.latest_image ]
|
27
28
|
end
|
28
29
|
|
30
|
+
def build_cache
|
31
|
+
if cache_to && cache_from
|
32
|
+
["--cache-to", cache_to,
|
33
|
+
"--cache-from", cache_from]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
29
37
|
def build_labels
|
30
38
|
argumentize "--label", { service: config.service }
|
31
39
|
end
|
@@ -46,19 +54,7 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
46
54
|
end
|
47
55
|
end
|
48
56
|
|
49
|
-
def
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
def secrets
|
54
|
-
(config.builder && config.builder["secrets"]) || []
|
55
|
-
end
|
56
|
-
|
57
|
-
def dockerfile
|
58
|
-
(config.builder && config.builder["dockerfile"]) || "Dockerfile"
|
59
|
-
end
|
60
|
-
|
61
|
-
def context
|
62
|
-
(config.builder && config.builder["context"]) || "."
|
57
|
+
def builder_config
|
58
|
+
config.builder
|
63
59
|
end
|
64
60
|
end
|
@@ -22,17 +22,17 @@ class Mrsk::Commands::Builder::Multiarch::Remote < Mrsk::Commands::Builder::Mult
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def create_local_buildx
|
25
|
-
docker :buildx, :create, "--name", builder_name, builder_name_with_arch(
|
25
|
+
docker :buildx, :create, "--name", builder_name, builder_name_with_arch(local_arch), "--platform", "linux/#{local_arch}"
|
26
26
|
end
|
27
27
|
|
28
28
|
def append_remote_buildx
|
29
|
-
docker :buildx, :create, "--append", "--name", builder_name, builder_name_with_arch(
|
29
|
+
docker :buildx, :create, "--append", "--name", builder_name, builder_name_with_arch(remote_arch), "--platform", "linux/#{remote_arch}"
|
30
30
|
end
|
31
31
|
|
32
32
|
def create_contexts
|
33
33
|
combine \
|
34
|
-
create_context(
|
35
|
-
create_context(
|
34
|
+
create_context(local_arch, local_host),
|
35
|
+
create_context(remote_arch, remote_host)
|
36
36
|
end
|
37
37
|
|
38
38
|
def create_context(arch, host)
|
@@ -41,19 +41,11 @@ class Mrsk::Commands::Builder::Multiarch::Remote < Mrsk::Commands::Builder::Mult
|
|
41
41
|
|
42
42
|
def remove_contexts
|
43
43
|
combine \
|
44
|
-
remove_context(
|
45
|
-
remove_context(
|
44
|
+
remove_context(local_arch),
|
45
|
+
remove_context(remote_arch)
|
46
46
|
end
|
47
47
|
|
48
48
|
def remove_context(arch)
|
49
49
|
docker :context, :rm, builder_name_with_arch(arch)
|
50
50
|
end
|
51
|
-
|
52
|
-
def local
|
53
|
-
config.builder["local"]
|
54
|
-
end
|
55
|
-
|
56
|
-
def remote
|
57
|
-
config.builder["remote"]
|
58
|
-
end
|
59
51
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Mrsk::Commands::Builder::Native::Cached < Mrsk::Commands::Builder::Native
|
2
|
+
def create
|
3
|
+
docker :buildx, :create, "--use", "--driver=docker-container"
|
4
|
+
end
|
5
|
+
|
6
|
+
def remove
|
7
|
+
docker :buildx, :rm, builder_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def push
|
11
|
+
docker :buildx, :build,
|
12
|
+
"--push",
|
13
|
+
*build_options,
|
14
|
+
build_context
|
15
|
+
end
|
16
|
+
end
|
@@ -28,29 +28,21 @@ class Mrsk::Commands::Builder::Native::Remote < Mrsk::Commands::Builder::Native
|
|
28
28
|
|
29
29
|
|
30
30
|
private
|
31
|
-
def arch
|
32
|
-
config.builder["remote"]["arch"]
|
33
|
-
end
|
34
|
-
|
35
|
-
def host
|
36
|
-
config.builder["remote"]["host"]
|
37
|
-
end
|
38
|
-
|
39
31
|
def builder_name
|
40
32
|
"mrsk-#{config.service}-native-remote"
|
41
33
|
end
|
42
34
|
|
43
35
|
def builder_name_with_arch
|
44
|
-
"#{builder_name}-#{
|
36
|
+
"#{builder_name}-#{remote_arch}"
|
45
37
|
end
|
46
38
|
|
47
39
|
def platform
|
48
|
-
"linux/#{
|
40
|
+
"linux/#{remote_arch}"
|
49
41
|
end
|
50
42
|
|
51
43
|
def create_context
|
52
44
|
docker :context, :create,
|
53
|
-
builder_name_with_arch, "--description", "'#{builder_name} #{
|
45
|
+
builder_name_with_arch, "--description", "'#{builder_name} #{remote_arch} native host'", "--docker", "'host=#{remote_host}'"
|
54
46
|
end
|
55
47
|
|
56
48
|
def remove_context
|
@@ -7,11 +7,13 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
|
|
7
7
|
|
8
8
|
def target
|
9
9
|
case
|
10
|
-
when config.builder && config.builder
|
10
|
+
when !config.builder.multiarch? && !config.builder.cached?
|
11
11
|
native
|
12
|
-
when config.builder && config.builder
|
12
|
+
when !config.builder.multiarch? && config.builder.cached?
|
13
|
+
native_cached
|
14
|
+
when config.builder.local? && config.builder.remote?
|
13
15
|
multiarch_remote
|
14
|
-
when config.builder
|
16
|
+
when config.builder.remote?
|
15
17
|
native_remote
|
16
18
|
else
|
17
19
|
multiarch
|
@@ -22,6 +24,10 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
|
|
22
24
|
@native ||= Mrsk::Commands::Builder::Native.new(config)
|
23
25
|
end
|
24
26
|
|
27
|
+
def native_cached
|
28
|
+
@native ||= Mrsk::Commands::Builder::Native::Cached.new(config)
|
29
|
+
end
|
30
|
+
|
25
31
|
def native_remote
|
26
32
|
@native ||= Mrsk::Commands::Builder::Native::Remote.new(config)
|
27
33
|
end
|
data/lib/mrsk/commands/prune.rb
CHANGED
@@ -2,13 +2,20 @@ require "active_support/duration"
|
|
2
2
|
require "active_support/core_ext/numeric/time"
|
3
3
|
|
4
4
|
class Mrsk::Commands::Prune < Mrsk::Commands::Base
|
5
|
-
def
|
5
|
+
def dangling_images
|
6
6
|
docker :image, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true"
|
7
7
|
end
|
8
8
|
|
9
|
+
def tagged_images
|
10
|
+
pipe \
|
11
|
+
docker(:image, :ls, *service_filter, "--format", "'{{.ID}} {{.Repository}}:{{.Tag}}'"),
|
12
|
+
"grep -v -w \"#{active_image_list}\"",
|
13
|
+
"while read image tag; do docker rmi $tag; done"
|
14
|
+
end
|
15
|
+
|
9
16
|
def containers(keep_last: 5)
|
10
17
|
pipe \
|
11
|
-
docker(:ps, "-q", "-a",
|
18
|
+
docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters),
|
12
19
|
"tail -n +#{keep_last + 1}",
|
13
20
|
"while read container_id; do docker rm $container_id; done"
|
14
21
|
end
|
@@ -17,4 +24,15 @@ class Mrsk::Commands::Prune < Mrsk::Commands::Base
|
|
17
24
|
def stopped_containers_filters
|
18
25
|
[ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] }
|
19
26
|
end
|
27
|
+
|
28
|
+
def active_image_list
|
29
|
+
# Pull the images that are used by any containers
|
30
|
+
# Append repo:latest - to avoid deleting the latest tag
|
31
|
+
# Append repo:<none> - to avoid deleting dangling images that are in use. Unused dangling images are deleted separately
|
32
|
+
"$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=#{config.service} | tr -d '\\n')#{config.latest_image}\\|#{config.repository}:<none>"
|
33
|
+
end
|
34
|
+
|
35
|
+
def service_filter
|
36
|
+
[ "--filter", "label=service=#{config.service}" ]
|
37
|
+
end
|
20
38
|
end
|
@@ -3,6 +3,9 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|
3
3
|
|
4
4
|
DEFAULT_IMAGE = "traefik:v2.9"
|
5
5
|
CONTAINER_PORT = 80
|
6
|
+
DEFAULT_ARGS = {
|
7
|
+
'log.level' => 'DEBUG'
|
8
|
+
}
|
6
9
|
|
7
10
|
def run
|
8
11
|
docker :run, "--name traefik",
|
@@ -16,7 +19,6 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|
16
19
|
*docker_options_args,
|
17
20
|
image,
|
18
21
|
"--providers.docker",
|
19
|
-
"--log.level=DEBUG",
|
20
22
|
*cmd_option_args
|
21
23
|
end
|
22
24
|
|
@@ -86,9 +88,9 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|
86
88
|
|
87
89
|
def cmd_option_args
|
88
90
|
if args = config.traefik["args"]
|
89
|
-
optionize args, with: "="
|
91
|
+
optionize DEFAULT_ARGS.merge(args), with: "="
|
90
92
|
else
|
91
|
-
|
93
|
+
optionize DEFAULT_ARGS, with: "="
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
@@ -0,0 +1,114 @@
|
|
1
|
+
class Mrsk::Configuration::Builder
|
2
|
+
def initialize(config:)
|
3
|
+
@options = config.raw_config.builder || {}
|
4
|
+
@image = config.image
|
5
|
+
@server = config.registry["server"]
|
6
|
+
|
7
|
+
valid?
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
@options
|
12
|
+
end
|
13
|
+
|
14
|
+
def multiarch?
|
15
|
+
@options["multiarch"] != false
|
16
|
+
end
|
17
|
+
|
18
|
+
def local?
|
19
|
+
!!@options["local"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def remote?
|
23
|
+
!!@options["remote"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def cached?
|
27
|
+
!!@options["cache"]
|
28
|
+
end
|
29
|
+
|
30
|
+
def args
|
31
|
+
@options["args"] || {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def secrets
|
35
|
+
@options["secrets"] || []
|
36
|
+
end
|
37
|
+
|
38
|
+
def dockerfile
|
39
|
+
@options["dockerfile"] || "Dockerfile"
|
40
|
+
end
|
41
|
+
|
42
|
+
def context
|
43
|
+
@options["context"] || "."
|
44
|
+
end
|
45
|
+
|
46
|
+
def local_arch
|
47
|
+
@options["local"]["arch"] if local?
|
48
|
+
end
|
49
|
+
|
50
|
+
def local_host
|
51
|
+
@options["local"]["host"] if local?
|
52
|
+
end
|
53
|
+
|
54
|
+
def remote_arch
|
55
|
+
@options["remote"]["arch"] if remote?
|
56
|
+
end
|
57
|
+
|
58
|
+
def remote_host
|
59
|
+
@options["remote"]["host"] if remote?
|
60
|
+
end
|
61
|
+
|
62
|
+
def cache_from
|
63
|
+
if cached?
|
64
|
+
case @options["cache"]["type"]
|
65
|
+
when "gha"
|
66
|
+
cache_from_config_for_gha
|
67
|
+
when "registry"
|
68
|
+
cache_from_config_for_registry
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def cache_to
|
74
|
+
if cached?
|
75
|
+
case @options["cache"]["type"]
|
76
|
+
when "gha"
|
77
|
+
cache_to_config_for_gha
|
78
|
+
when "registry"
|
79
|
+
cache_to_config_for_registry
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def valid?
|
86
|
+
if @options["local"] && !@options["remote"]
|
87
|
+
raise ArgumentError, "You must specify both local and remote builder config for remote multiarch builds"
|
88
|
+
end
|
89
|
+
|
90
|
+
if @options["cache"] && @options["cache"]["type"]
|
91
|
+
raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless ["gha", "registry"].include?(@options["cache"]["type"])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def cache_image
|
96
|
+
@options["cache"]&.fetch("image", nil) || "#{@image}-build-cache"
|
97
|
+
end
|
98
|
+
|
99
|
+
def cache_from_config_for_gha
|
100
|
+
"type=gha"
|
101
|
+
end
|
102
|
+
|
103
|
+
def cache_from_config_for_registry
|
104
|
+
[ "type=registry", "ref=#{@server}/#{cache_image}" ].compact.join(",")
|
105
|
+
end
|
106
|
+
|
107
|
+
def cache_to_config_for_gha
|
108
|
+
[ "type=gha", @options["cache"]&.fetch("options", nil)].compact.join(",")
|
109
|
+
end
|
110
|
+
|
111
|
+
def cache_to_config_for_registry
|
112
|
+
[ "type=registry", @options["cache"]&.fetch("options", nil), "ref=#{@server}/#{cache_image}" ].compact.join(",")
|
113
|
+
end
|
114
|
+
end
|
data/lib/mrsk/configuration.rb
CHANGED
@@ -6,7 +6,7 @@ require "erb"
|
|
6
6
|
require "net/ssh/proxy/jump"
|
7
7
|
|
8
8
|
class Mrsk::Configuration
|
9
|
-
delegate :service, :image, :servers, :env, :labels, :registry, :
|
9
|
+
delegate :service, :image, :servers, :env, :labels, :registry, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
|
10
10
|
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils
|
11
11
|
|
12
12
|
attr_accessor :destination
|
@@ -165,8 +165,12 @@ class Mrsk::Configuration
|
|
165
165
|
raw_config.readiness_delay || 7
|
166
166
|
end
|
167
167
|
|
168
|
+
def minimum_version
|
169
|
+
raw_config.minimum_version
|
170
|
+
end
|
171
|
+
|
168
172
|
def valid?
|
169
|
-
ensure_required_keys_present &&
|
173
|
+
ensure_required_keys_present && ensure_valid_mrsk_version
|
170
174
|
end
|
171
175
|
|
172
176
|
|
@@ -182,7 +186,7 @@ class Mrsk::Configuration
|
|
182
186
|
env_args: env_args,
|
183
187
|
volume_args: volume_args,
|
184
188
|
ssh_options: ssh_options,
|
185
|
-
builder:
|
189
|
+
builder: builder.to_h,
|
186
190
|
accessories: raw_config.accessories,
|
187
191
|
logging: logging_args,
|
188
192
|
healthcheck: healthcheck
|
@@ -197,6 +201,18 @@ class Mrsk::Configuration
|
|
197
201
|
raw_config.hooks_path || ".mrsk/hooks"
|
198
202
|
end
|
199
203
|
|
204
|
+
def builder
|
205
|
+
Mrsk::Configuration::Builder.new(config: self)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Will raise KeyError if any secret ENVs are missing
|
209
|
+
def ensure_env_available
|
210
|
+
env_args
|
211
|
+
roles.each(&:env_args)
|
212
|
+
|
213
|
+
true
|
214
|
+
end
|
215
|
+
|
200
216
|
private
|
201
217
|
# Will raise ArgumentError if any required config keys are missing
|
202
218
|
def ensure_required_keys_present
|
@@ -221,14 +237,15 @@ class Mrsk::Configuration
|
|
221
237
|
true
|
222
238
|
end
|
223
239
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
240
|
+
def ensure_valid_mrsk_version
|
241
|
+
if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Mrsk::VERSION)
|
242
|
+
raise ArgumentError, "Current version is #{Mrsk::VERSION}, minimum required is #{minimum_version}"
|
243
|
+
end
|
228
244
|
|
229
245
|
true
|
230
246
|
end
|
231
247
|
|
248
|
+
|
232
249
|
def role_names
|
233
250
|
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
234
251
|
end
|
data/lib/mrsk/version.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.14.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-
|
11
|
+
date: 2023-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -190,6 +190,7 @@ files:
|
|
190
190
|
- lib/mrsk/cli/templates/sample_hooks/post-deploy.sample
|
191
191
|
- lib/mrsk/cli/templates/sample_hooks/pre-build.sample
|
192
192
|
- lib/mrsk/cli/templates/sample_hooks/pre-connect.sample
|
193
|
+
- lib/mrsk/cli/templates/sample_hooks/pre-deploy.sample
|
193
194
|
- lib/mrsk/cli/templates/template.env
|
194
195
|
- lib/mrsk/cli/traefik.rb
|
195
196
|
- lib/mrsk/commander.rb
|
@@ -203,6 +204,7 @@ files:
|
|
203
204
|
- lib/mrsk/commands/builder/multiarch.rb
|
204
205
|
- lib/mrsk/commands/builder/multiarch/remote.rb
|
205
206
|
- lib/mrsk/commands/builder/native.rb
|
207
|
+
- lib/mrsk/commands/builder/native/cached.rb
|
206
208
|
- lib/mrsk/commands/builder/native/remote.rb
|
207
209
|
- lib/mrsk/commands/docker.rb
|
208
210
|
- lib/mrsk/commands/healthcheck.rb
|
@@ -214,6 +216,7 @@ files:
|
|
214
216
|
- lib/mrsk/configuration.rb
|
215
217
|
- lib/mrsk/configuration/accessory.rb
|
216
218
|
- lib/mrsk/configuration/boot.rb
|
219
|
+
- lib/mrsk/configuration/builder.rb
|
217
220
|
- lib/mrsk/configuration/role.rb
|
218
221
|
- lib/mrsk/sshkit_with_ext.rb
|
219
222
|
- lib/mrsk/tags.rb
|