mrsk 0.13.2 → 0.15.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 +88 -9
- data/lib/mrsk/cli/accessory.rb +18 -14
- data/lib/mrsk/cli/app.rb +8 -8
- data/lib/mrsk/cli/base.rb +16 -16
- data/lib/mrsk/cli/build.rb +5 -5
- data/lib/mrsk/cli/main.rb +5 -5
- data/lib/mrsk/cli/prune.rb +3 -3
- data/lib/mrsk/cli/templates/sample_hooks/pre-deploy.sample +49 -22
- data/lib/mrsk/cli/traefik.rb +16 -11
- 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/lock.rb +1 -1
- data/lib/mrsk/commands/traefik.rb +5 -3
- data/lib/mrsk/configuration/builder.rb +114 -0
- data/lib/mrsk/configuration.rb +35 -9
- data/lib/mrsk/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b7e74f66fc6480dd991ce88dc5a2527832c6c61ff85ede26625e1e86f1650c9
|
4
|
+
data.tar.gz: 9621b2ae90f53dc652fc41abfa28b000bac895bfcf4a7dc4c82db72787f78e08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6bd519ba6639ae9f8c565177a4cbfd2bcf6e725452b76b1b95032f7e8288227b2ae3e3550d1fd946eb85a37c36b2b2a03419c097f64f0777110b3c06f128a545
|
7
|
+
data.tar.gz: 119a7b8551b5f72a2ba54c0f5c059ef36a47a85852616ffd6df04cd3b168113b5892f571609a228c71330cd3cd69045f7fc03e3005a1b95aec41d3ee6a91c4d4
|
data/README.md
CHANGED
@@ -63,6 +63,24 @@ This will:
|
|
63
63
|
|
64
64
|
Voila! All the servers are now serving the app on port 80. If you're just running a single server, you're ready to go. If you're running multiple servers, you need to put a load balancer in front of them. For subsequent deploys, or if your servers already have Docker and curl installed, you can just run `mrsk deploy`.
|
65
65
|
|
66
|
+
### Rails <7 usage
|
67
|
+
|
68
|
+
MRSK is not needed to be in your application Gemfile to be used. However, if you want to guarantee specific MRSK version in your CI/CD workflows, you can create a separate Gemfile for MRSK, for example, `gemfile/mrsk.gemfile`:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
source 'https://rubygems.org'
|
72
|
+
|
73
|
+
gem 'mrsk', '~> 0.14'
|
74
|
+
```
|
75
|
+
|
76
|
+
Bundle with `BUNDLE_GEMFILE=gemfiles/mrsk.gemfile bundle`.
|
77
|
+
|
78
|
+
After this MRSK can be used for deployment:
|
79
|
+
|
80
|
+
```sh
|
81
|
+
BUNDLE_GEMFILE=gemfiles/mrsk.gemfile bundle exec mrsk deploy
|
82
|
+
```
|
83
|
+
|
66
84
|
## Vision
|
67
85
|
|
68
86
|
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!
|
@@ -123,6 +141,8 @@ This template can safely be checked into git. Then everyone deploying the app ca
|
|
123
141
|
|
124
142
|
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`.
|
125
143
|
|
144
|
+
Note: If you utilize biometrics with 1Password you can remove the `session_token` related parts in the example and just call `op read op://Vault/Docker Hub/password -n`.
|
145
|
+
|
126
146
|
#### Bitwarden as a secret store
|
127
147
|
|
128
148
|
If you are using open source secret store like bitwarden, you can create `.env.erb` as a template which looks up the secrets.
|
@@ -206,13 +226,13 @@ ssh:
|
|
206
226
|
user: app
|
207
227
|
```
|
208
228
|
|
209
|
-
If you are using non-root user, you need to bootstrap your servers manually, before using them with MRSK. On Ubuntu, you'd do:
|
229
|
+
If you are using non-root user (`app` as above example), you need to bootstrap your servers manually, before using them with MRSK. On Ubuntu, you'd do:
|
210
230
|
|
211
231
|
```bash
|
212
232
|
sudo apt update
|
213
233
|
sudo apt upgrade -y
|
214
234
|
sudo apt install -y docker.io curl git
|
215
|
-
sudo usermod -a -G docker
|
235
|
+
sudo usermod -a -G docker app
|
216
236
|
```
|
217
237
|
|
218
238
|
### Using a proxy SSH host
|
@@ -238,6 +258,15 @@ ssh:
|
|
238
258
|
proxy_command: aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --region=us-east-1 ## ssh via aws ssm
|
239
259
|
```
|
240
260
|
|
261
|
+
### Configuring the SSH log level
|
262
|
+
|
263
|
+
```yaml
|
264
|
+
ssh:
|
265
|
+
log_level: debug
|
266
|
+
```
|
267
|
+
|
268
|
+
Valid levels are `debug`, `info`, `warn`, `error` and `fatal` (default).
|
269
|
+
|
241
270
|
### Using env variables
|
242
271
|
|
243
272
|
You can inject env variables into the app containers using `env`:
|
@@ -380,6 +409,16 @@ servers:
|
|
380
409
|
|
381
410
|
That'll start the job containers with `docker run ... --cap-add --cpu-count 4 ...`.
|
382
411
|
|
412
|
+
### Setting a minimum version
|
413
|
+
|
414
|
+
You can set the minimum MRSK version with:
|
415
|
+
|
416
|
+
```yaml
|
417
|
+
minimum_version: 0.13.3
|
418
|
+
```
|
419
|
+
|
420
|
+
Note: versions <= 0.13.2 will ignore this setting.
|
421
|
+
|
383
422
|
### Configuring logging
|
384
423
|
|
385
424
|
You can configure the logging driver and options passed to Docker using `logging`:
|
@@ -463,6 +502,37 @@ builder:
|
|
463
502
|
context: ".."
|
464
503
|
```
|
465
504
|
|
505
|
+
### Using multistage builder cache
|
506
|
+
|
507
|
+
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:
|
508
|
+
|
509
|
+
```yaml
|
510
|
+
# Using GHA cache
|
511
|
+
builder:
|
512
|
+
cache:
|
513
|
+
type: gha
|
514
|
+
|
515
|
+
# Using Registry cache
|
516
|
+
builder:
|
517
|
+
cache:
|
518
|
+
type: registry
|
519
|
+
|
520
|
+
# Using Registry cache with different cache image
|
521
|
+
builder:
|
522
|
+
cache:
|
523
|
+
type: registry
|
524
|
+
# default image name is <image>-build-cache
|
525
|
+
image: application-cache-image
|
526
|
+
|
527
|
+
# Using Registry cache with additinonal cache-to options
|
528
|
+
builder:
|
529
|
+
cache:
|
530
|
+
type: registry
|
531
|
+
options: mode=max,image-manifest=true,oci-mediatypes=true
|
532
|
+
```
|
533
|
+
|
534
|
+
For further insights into build cache optimization, check out documentation on Docker's official website: https://docs.docker.com/build/cache/.
|
535
|
+
|
466
536
|
### Using build secrets for new images
|
467
537
|
|
468
538
|
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:
|
@@ -581,6 +651,16 @@ traefik:
|
|
581
651
|
entrypoints.otherentrypoint.address: ':9000'
|
582
652
|
```
|
583
653
|
|
654
|
+
### Rebooting Traefik
|
655
|
+
|
656
|
+
If you make changes to Traefik args or labels, you'll need to reboot with:
|
657
|
+
|
658
|
+
`mrsk traefik reboot`
|
659
|
+
|
660
|
+
In production, reboot the Traefik containers one by one with a slower but safer approach, using a rolling reboot:
|
661
|
+
|
662
|
+
`mrsk traefik reboot --rolling`
|
663
|
+
|
584
664
|
### Configuring build args for new images
|
585
665
|
|
586
666
|
Build arguments that aren't secret can also be configured:
|
@@ -825,7 +905,7 @@ If you wish to remove the entire application, including Traefik, containers, ima
|
|
825
905
|
|
826
906
|
## Locking
|
827
907
|
|
828
|
-
Commands that are unsafe to run concurrently will take a deploy lock while they run. The lock is the `mrsk_lock
|
908
|
+
Commands that are unsafe to run concurrently will take a deploy lock while they run. The lock is the `mrsk_lock-<service>` directory on the primary server.
|
829
909
|
|
830
910
|
You can check the lock status with:
|
831
911
|
|
@@ -881,8 +961,8 @@ firing a JSON webhook. These variables include:
|
|
881
961
|
- `MRSK_RECORDED_AT` - UTC timestamp in ISO 8601 format, e.g. `2023-04-14T17:07:31Z`
|
882
962
|
- `MRSK_PERFORMER` - the local user performing the command (from `whoami`)
|
883
963
|
- `MRSK_SERVICE_VERSION` - an abbreviated service and version for use in messages, e.g. app@150b24f
|
884
|
-
- `MRSK_VERSION` -
|
885
|
-
- `MRSK_HOSTS` - a comma
|
964
|
+
- `MRSK_VERSION` - the full version being deployed
|
965
|
+
- `MRSK_HOSTS` - a comma-separated list of the hosts targeted by the command
|
886
966
|
- `MRSK_COMMAND` - The command we are running
|
887
967
|
- `MRSK_SUBCOMMAND` - optional: The subcommand we are running
|
888
968
|
- `MRSK_DESTINATION` - optional: destination, e.g. "staging"
|
@@ -899,9 +979,8 @@ Used for pre-build checks - e.g. there are no uncommitted changes or that CI has
|
|
899
979
|
3. pre-deploy
|
900
980
|
For final checks before deploying, e.g. checking CI completed
|
901
981
|
|
902
|
-
3. post-deploy - run after a deploy, redeploy or rollback
|
903
|
-
|
904
|
-
This hook is also passed a `MRSK_RUNTIME` env variable.
|
982
|
+
3. post-deploy - run after a deploy, redeploy or rollback.
|
983
|
+
This hook is also passed a `MRSK_RUNTIME` env variable set to the total seconds the deploy took.
|
905
984
|
|
906
985
|
This could be used to broadcast a deployment message, or register the new version with an APM.
|
907
986
|
|
@@ -912,7 +991,7 @@ The command could look something like:
|
|
912
991
|
curl -q -d content="[My App] ${MRSK_PERFORMER} Rolled back to version ${MRSK_VERSION}" https://3.basecamp.com/XXXXX/integrations/XXXXX/buckets/XXXXX/chats/XXXXX/lines
|
913
992
|
```
|
914
993
|
|
915
|
-
That'll post a line like
|
994
|
+
That'll post a line like the following to a preconfigured chatbot in Basecamp:
|
916
995
|
|
917
996
|
```
|
918
997
|
[My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
|
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
|
-
def boot(name)
|
4
|
-
|
3
|
+
def boot(name, login: true)
|
4
|
+
mutating do
|
5
5
|
if name == "all"
|
6
6
|
MRSK.accessory_names.each { |accessory_name| boot(accessory_name) }
|
7
7
|
else
|
@@ -10,7 +10,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
10
10
|
upload(name)
|
11
11
|
|
12
12
|
on(accessory.hosts) do
|
13
|
-
execute *MRSK.registry.login
|
13
|
+
execute *MRSK.registry.login if login
|
14
14
|
execute *MRSK.auditor.record("Booted #{name} accessory"), verbosity: :debug
|
15
15
|
execute *accessory.run
|
16
16
|
end
|
@@ -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,18 +51,22 @@ 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
|
+
on(accessory.hosts) do
|
57
|
+
execute *MRSK.registry.login
|
58
|
+
end
|
59
|
+
|
56
60
|
stop(name)
|
57
61
|
remove_container(name)
|
58
|
-
boot(name)
|
62
|
+
boot(name, login: false)
|
59
63
|
end
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
63
67
|
desc "start [NAME]", "Start existing accessory container on host"
|
64
68
|
def start(name)
|
65
|
-
|
69
|
+
mutating do
|
66
70
|
with_accessory(name) do |accessory|
|
67
71
|
on(accessory.hosts) do
|
68
72
|
execute *MRSK.auditor.record("Started #{name} accessory"), verbosity: :debug
|
@@ -74,7 +78,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
74
78
|
|
75
79
|
desc "stop [NAME]", "Stop existing accessory container on host"
|
76
80
|
def stop(name)
|
77
|
-
|
81
|
+
mutating do
|
78
82
|
with_accessory(name) do |accessory|
|
79
83
|
on(accessory.hosts) do
|
80
84
|
execute *MRSK.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
@@ -86,7 +90,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
86
90
|
|
87
91
|
desc "restart [NAME]", "Restart existing accessory container on host"
|
88
92
|
def restart(name)
|
89
|
-
|
93
|
+
mutating do
|
90
94
|
with_accessory(name) do
|
91
95
|
stop(name)
|
92
96
|
start(name)
|
@@ -165,7 +169,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
165
169
|
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
166
170
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
167
171
|
def remove(name)
|
168
|
-
|
172
|
+
mutating do
|
169
173
|
if name == "all"
|
170
174
|
MRSK.accessory_names.each { |accessory_name| remove(accessory_name) }
|
171
175
|
else
|
@@ -183,7 +187,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
183
187
|
|
184
188
|
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
185
189
|
def remove_container(name)
|
186
|
-
|
190
|
+
mutating do
|
187
191
|
with_accessory(name) do |accessory|
|
188
192
|
on(accessory.hosts) do
|
189
193
|
execute *MRSK.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
@@ -195,7 +199,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
195
199
|
|
196
200
|
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
197
201
|
def remove_image(name)
|
198
|
-
|
202
|
+
mutating do
|
199
203
|
with_accessory(name) do |accessory|
|
200
204
|
on(accessory.hosts) do
|
201
205
|
execute *MRSK.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
@@ -207,7 +211,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
207
211
|
|
208
212
|
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
209
213
|
def remove_service_directory(name)
|
210
|
-
|
214
|
+
mutating do
|
211
215
|
with_accessory(name) do |accessory|
|
212
216
|
on(accessory.hosts) do
|
213
217
|
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|
|
@@ -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
|
-
|
78
|
-
|
79
|
-
run_hook "pre-connect"
|
75
|
+
def mutating
|
76
|
+
return yield if MRSK.holding_lock?
|
77
|
+
|
78
|
+
MRSK.config.ensure_env_available
|
80
79
|
|
81
|
-
|
80
|
+
run_hook "pre-connect"
|
82
81
|
|
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
|
82
|
+
acquire_lock
|
91
83
|
|
92
|
-
|
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
|
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/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
|
@@ -53,7 +53,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
53
53
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
54
54
|
def redeploy
|
55
55
|
runtime = print_runtime do
|
56
|
-
|
56
|
+
mutating do
|
57
57
|
invoke_options = deploy_options
|
58
58
|
|
59
59
|
if options[:skip_push]
|
@@ -83,7 +83,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
83
83
|
def rollback(version)
|
84
84
|
rolled_back = false
|
85
85
|
runtime = print_runtime do
|
86
|
-
|
86
|
+
mutating do
|
87
87
|
invoke_options = deploy_options
|
88
88
|
|
89
89
|
MRSK.config.version = version
|
@@ -180,7 +180,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
180
180
|
desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
|
181
181
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
182
182
|
def remove
|
183
|
-
|
183
|
+
mutating do
|
184
184
|
if options[:confirmed] || ask("This will remove all containers and images. Are you sure?", limited_to: %w( y N ), default: "N") == "y"
|
185
185
|
invoke "mrsk:cli:traefik:remove", [], options.without(:confirmed)
|
186
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,7 +9,7 @@ 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
15
|
execute *MRSK.prune.dangling_images
|
@@ -20,7 +20,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
20
20
|
|
21
21
|
desc "containers", "Prune all stopped containers, except the last 5"
|
22
22
|
def containers
|
23
|
-
|
23
|
+
mutating do
|
24
24
|
on(MRSK.hosts) do
|
25
25
|
execute *MRSK.auditor.record("Pruned containers"), verbosity: :debug
|
26
26
|
execute *MRSK.prune.containers
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/bin/
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
# A sample pre-deploy hook
|
4
4
|
#
|
@@ -16,8 +16,6 @@
|
|
16
16
|
# MRSK_ROLE (if set)
|
17
17
|
# MRSK_DESTINATION (if set)
|
18
18
|
|
19
|
-
#!/usr/bin/env ruby
|
20
|
-
|
21
19
|
# Only check the build status for production deployments
|
22
20
|
if ENV["MRSK_COMMAND"] == "rollback" || ENV["MRSK_DESTINATION"] != "production"
|
23
21
|
exit 0
|
@@ -41,41 +39,70 @@ def exit_with_error(message)
|
|
41
39
|
exit 1
|
42
40
|
end
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
47
80
|
end
|
48
81
|
|
49
|
-
remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
50
|
-
git_sha = `git rev-parse HEAD`.strip
|
51
82
|
|
52
|
-
|
53
|
-
|
83
|
+
$stdout.sync = true
|
84
|
+
|
85
|
+
puts "Checking build status..."
|
54
86
|
attempts = 0
|
87
|
+
checks = GithubStatusChecks.new
|
55
88
|
|
56
89
|
begin
|
57
90
|
loop do
|
58
|
-
|
59
|
-
state = combined_status[:state]
|
60
|
-
first_status_url = first_status_url(combined_status, state)
|
61
|
-
|
62
|
-
case state
|
91
|
+
case checks.state
|
63
92
|
when "success"
|
64
|
-
puts "
|
93
|
+
puts "Checks passed, see #{checks.first_status_url}"
|
65
94
|
exit 0
|
66
95
|
when "failure"
|
67
|
-
exit_with_error "
|
96
|
+
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
68
97
|
when "pending"
|
69
98
|
attempts += 1
|
70
99
|
end
|
71
100
|
|
72
|
-
|
73
|
-
|
74
|
-
if attempts == MAX_ATTEMPTS
|
75
|
-
exit_with_error "Build status is still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds"
|
76
|
-
end
|
101
|
+
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
77
102
|
|
103
|
+
puts checks.current_status
|
78
104
|
sleep(ATTEMPTS_GAP)
|
105
|
+
checks.refresh!
|
79
106
|
end
|
80
107
|
rescue Octokit::NotFound
|
81
108
|
exit_with_error "Build status could not be found"
|
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
|
@@ -10,17 +10,22 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
10
10
|
end
|
11
11
|
|
12
12
|
desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
|
13
|
+
option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
|
13
14
|
def reboot
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
mutating do
|
16
|
+
on(MRSK.traefik_hosts, in: options[:rolling] ? :sequence : :parallel) do
|
17
|
+
execute *MRSK.auditor.record("Rebooted traefik"), verbosity: :debug
|
18
|
+
execute *MRSK.registry.login
|
19
|
+
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
20
|
+
execute *MRSK.traefik.remove_container
|
21
|
+
execute *MRSK.traefik.run, raise_on_non_zero_exit: false
|
22
|
+
end
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
21
26
|
desc "start", "Start existing Traefik container on servers"
|
22
27
|
def start
|
23
|
-
|
28
|
+
mutating do
|
24
29
|
on(MRSK.traefik_hosts) do
|
25
30
|
execute *MRSK.auditor.record("Started traefik"), verbosity: :debug
|
26
31
|
execute *MRSK.traefik.start, raise_on_non_zero_exit: false
|
@@ -30,7 +35,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
30
35
|
|
31
36
|
desc "stop", "Stop existing Traefik container on servers"
|
32
37
|
def stop
|
33
|
-
|
38
|
+
mutating do
|
34
39
|
on(MRSK.traefik_hosts) do
|
35
40
|
execute *MRSK.auditor.record("Stopped traefik"), verbosity: :debug
|
36
41
|
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
@@ -40,7 +45,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
40
45
|
|
41
46
|
desc "restart", "Restart existing Traefik container on servers"
|
42
47
|
def restart
|
43
|
-
|
48
|
+
mutating do
|
44
49
|
stop
|
45
50
|
start
|
46
51
|
end
|
@@ -77,7 +82,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
77
82
|
|
78
83
|
desc "remove", "Remove Traefik container and image from servers"
|
79
84
|
def remove
|
80
|
-
|
85
|
+
mutating do
|
81
86
|
stop
|
82
87
|
remove_container
|
83
88
|
remove_image
|
@@ -86,7 +91,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
86
91
|
|
87
92
|
desc "remove_container", "Remove Traefik container from servers", hide: true
|
88
93
|
def remove_container
|
89
|
-
|
94
|
+
mutating do
|
90
95
|
on(MRSK.traefik_hosts) do
|
91
96
|
execute *MRSK.auditor.record("Removed traefik container"), verbosity: :debug
|
92
97
|
execute *MRSK.traefik.remove_container
|
@@ -96,7 +101,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
96
101
|
|
97
102
|
desc "remove_image", "Remove Traefik image from servers", hide: true
|
98
103
|
def remove_image
|
99
|
-
|
104
|
+
mutating do
|
100
105
|
on(MRSK.traefik_hosts) do
|
101
106
|
execute *MRSK.auditor.record("Removed traefik image"), verbosity: :debug
|
102
107
|
execute *MRSK.traefik.remove_image
|
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/lock.rb
CHANGED
@@ -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
|
@@ -153,7 +153,15 @@ class Mrsk::Configuration
|
|
153
153
|
end
|
154
154
|
|
155
155
|
def ssh_options
|
156
|
-
{ user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ] }.compact
|
156
|
+
{ user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ], logger: ssh_logger }.compact
|
157
|
+
end
|
158
|
+
|
159
|
+
def ssh_logger
|
160
|
+
@ssh_logger ||= ::Logger.new(STDERR).tap { |logger| logger.level = ssh_log_level }
|
161
|
+
end
|
162
|
+
|
163
|
+
def ssh_log_level
|
164
|
+
(raw_config.ssh && raw_config.ssh["log_level"]) || ::Logger::FATAL
|
157
165
|
end
|
158
166
|
|
159
167
|
|
@@ -165,8 +173,12 @@ class Mrsk::Configuration
|
|
165
173
|
raw_config.readiness_delay || 7
|
166
174
|
end
|
167
175
|
|
176
|
+
def minimum_version
|
177
|
+
raw_config.minimum_version
|
178
|
+
end
|
179
|
+
|
168
180
|
def valid?
|
169
|
-
ensure_required_keys_present &&
|
181
|
+
ensure_required_keys_present && ensure_valid_mrsk_version
|
170
182
|
end
|
171
183
|
|
172
184
|
|
@@ -181,8 +193,9 @@ class Mrsk::Configuration
|
|
181
193
|
service_with_version: service_with_version,
|
182
194
|
env_args: env_args,
|
183
195
|
volume_args: volume_args,
|
184
|
-
ssh_options: ssh_options,
|
185
|
-
|
196
|
+
ssh_options: ssh_options.except(:logger),
|
197
|
+
ssh_log_level: ssh_log_level,
|
198
|
+
builder: builder.to_h,
|
186
199
|
accessories: raw_config.accessories,
|
187
200
|
logging: logging_args,
|
188
201
|
healthcheck: healthcheck
|
@@ -197,6 +210,18 @@ class Mrsk::Configuration
|
|
197
210
|
raw_config.hooks_path || ".mrsk/hooks"
|
198
211
|
end
|
199
212
|
|
213
|
+
def builder
|
214
|
+
Mrsk::Configuration::Builder.new(config: self)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Will raise KeyError if any secret ENVs are missing
|
218
|
+
def ensure_env_available
|
219
|
+
env_args
|
220
|
+
roles.each(&:env_args)
|
221
|
+
|
222
|
+
true
|
223
|
+
end
|
224
|
+
|
200
225
|
private
|
201
226
|
# Will raise ArgumentError if any required config keys are missing
|
202
227
|
def ensure_required_keys_present
|
@@ -221,14 +246,15 @@ class Mrsk::Configuration
|
|
221
246
|
true
|
222
247
|
end
|
223
248
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
249
|
+
def ensure_valid_mrsk_version
|
250
|
+
if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Mrsk::VERSION)
|
251
|
+
raise ArgumentError, "Current version is #{Mrsk::VERSION}, minimum required is #{minimum_version}"
|
252
|
+
end
|
228
253
|
|
229
254
|
true
|
230
255
|
end
|
231
256
|
|
257
|
+
|
232
258
|
def role_names
|
233
259
|
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
234
260
|
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.15.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-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- lib/mrsk/commands/builder/multiarch.rb
|
205
205
|
- lib/mrsk/commands/builder/multiarch/remote.rb
|
206
206
|
- lib/mrsk/commands/builder/native.rb
|
207
|
+
- lib/mrsk/commands/builder/native/cached.rb
|
207
208
|
- lib/mrsk/commands/builder/native/remote.rb
|
208
209
|
- lib/mrsk/commands/docker.rb
|
209
210
|
- lib/mrsk/commands/healthcheck.rb
|
@@ -215,6 +216,7 @@ files:
|
|
215
216
|
- lib/mrsk/configuration.rb
|
216
217
|
- lib/mrsk/configuration/accessory.rb
|
217
218
|
- lib/mrsk/configuration/boot.rb
|
219
|
+
- lib/mrsk/configuration/builder.rb
|
218
220
|
- lib/mrsk/configuration/role.rb
|
219
221
|
- lib/mrsk/sshkit_with_ext.rb
|
220
222
|
- lib/mrsk/tags.rb
|
@@ -241,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
241
243
|
- !ruby/object:Gem::Version
|
242
244
|
version: '0'
|
243
245
|
requirements: []
|
244
|
-
rubygems_version: 3.4.
|
246
|
+
rubygems_version: 3.4.16
|
245
247
|
signing_key:
|
246
248
|
specification_version: 4
|
247
249
|
summary: Deploy web apps in containers to servers running Docker with zero downtime.
|