mrsk 0.10.1 → 0.11.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 +73 -18
- data/lib/mrsk/cli/base.rb +7 -5
- data/lib/mrsk/cli/healthcheck.rb +3 -3
- data/lib/mrsk/cli/lock.rb +2 -2
- data/lib/mrsk/cli/main.rb +21 -11
- data/lib/mrsk/cli/traefik.rb +4 -1
- data/lib/mrsk/cli.rb +1 -0
- data/lib/mrsk/commands/base.rb +1 -1
- data/lib/mrsk/commands/builder/base.rb +1 -1
- data/lib/mrsk/commands/registry.rb +1 -1
- data/lib/mrsk/commands/traefik.rb +16 -3
- data/lib/mrsk/configuration/role.rb +10 -6
- data/lib/mrsk/configuration.rb +3 -1
- data/lib/mrsk/utils/sensitive.rb +19 -0
- data/lib/mrsk/utils.rb +36 -7
- data/lib/mrsk/version.rb +1 -1
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a8c9aac5626518667d963bd8c0b8e757be3dfe2cfed585f1e7d0783e2e017a4
|
4
|
+
data.tar.gz: 9e7c336b776e518d98a3a25a7a3c47858cf40f2d8398c881d8c3ec3f224d1222
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3889961797501b12da9ca958021b98a5a2b1167943813156241e5da44cc35b3d6ace84b56f4850b50e351cd995be099821d791e2136001f37ba700f8310e5128
|
7
|
+
data.tar.gz: a15e1ad08dfc1c8471f0fa341d865ffc237d88aae229cd250b49feb1d4ca10e750ddc2dec85c392974017c13c6d7b2f583fb3af85e840d5dddcbed19c8f90712
|
data/README.md
CHANGED
@@ -6,6 +6,8 @@ Watch the screencast: https://www.youtube.com/watch?v=LL1cV2FXZ5I
|
|
6
6
|
|
7
7
|
Join us on Discord: https://discord.gg/YgHVT7GCXS
|
8
8
|
|
9
|
+
Ask questions: https://github.com/mrsked/mrsk/discussions
|
10
|
+
|
9
11
|
## Installation
|
10
12
|
|
11
13
|
If you have a Ruby environment available, you can install MRSK globally with:
|
@@ -14,13 +16,13 @@ If you have a Ruby environment available, you can install MRSK globally with:
|
|
14
16
|
gem install mrsk
|
15
17
|
```
|
16
18
|
|
17
|
-
...otherwise, you can run a dockerized version via an alias (add this to your
|
19
|
+
...otherwise, you can run a dockerized version via an alias (add this to your .bashrc or similar to simplify re-use):
|
18
20
|
|
19
21
|
```sh
|
20
22
|
alias mrsk='docker run --rm -it -v $HOME/.ssh:/root/.ssh -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}/:/workdir ghcr.io/mrsked/mrsk'
|
21
23
|
```
|
22
24
|
|
23
|
-
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:
|
25
|
+
Then, inside your app directory, run `mrsk init` (or `mrsk init --bundle` within Rails 7+ apps where you want a bin/mrsk binstub). Now edit the new file `config/deploy.yml`. It could look as simple as this:
|
24
26
|
|
25
27
|
```yaml
|
26
28
|
service: hey
|
@@ -191,6 +193,15 @@ ssh:
|
|
191
193
|
user: app
|
192
194
|
```
|
193
195
|
|
196
|
+
If you are using non-root user, you need to bootstrap your servers manually, before using them with MRSK. On Ubuntu, you'd do:
|
197
|
+
|
198
|
+
```bash
|
199
|
+
sudo apt update
|
200
|
+
sudo apt upgrade -y
|
201
|
+
sudo apt install -y docker.io curl git
|
202
|
+
sudo usermod -a -G docker ubuntu
|
203
|
+
```
|
204
|
+
|
194
205
|
### Using a proxy SSH host
|
195
206
|
|
196
207
|
If you need to connect to server through a proxy host, you can use `ssh/proxy`:
|
@@ -207,6 +218,13 @@ ssh:
|
|
207
218
|
proxy: "app@192.168.0.1"
|
208
219
|
```
|
209
220
|
|
221
|
+
Also if you need specific proxy command to connect to the server:
|
222
|
+
|
223
|
+
```yaml
|
224
|
+
ssh:
|
225
|
+
proxy_command: aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --region=us-east-1 ## ssh via aws ssm
|
226
|
+
```
|
227
|
+
|
210
228
|
### Using env variables
|
211
229
|
|
212
230
|
You can inject env variables into the app containers using `env`:
|
@@ -288,8 +306,9 @@ You can specialize the default Traefik rules by setting labels on the containers
|
|
288
306
|
|
289
307
|
```yaml
|
290
308
|
labels:
|
291
|
-
traefik.http.routers.hey.rule: Host(`app.hey.com`)
|
309
|
+
traefik.http.routers.hey-web.rule: Host(`app.hey.com`)
|
292
310
|
```
|
311
|
+
Traefik rules are in the "service-role-destination" format. The default role will be `web` if no rule is specified. If the destination is not specified, it is not included. To give an example, the above rule would become "traefik.http.routers.hey-web.rule" if it was for the "staging" destination.
|
293
312
|
|
294
313
|
Note: The backticks are needed to ensure the rule is passed in correctly and not treated as command substitution by Bash!
|
295
314
|
|
@@ -439,9 +458,9 @@ RUN --mount=type=secret,id=GITHUB_TOKEN \
|
|
439
458
|
rm -rf /usr/local/bundle/cache
|
440
459
|
```
|
441
460
|
|
442
|
-
###
|
461
|
+
### Traefik command arguments
|
443
462
|
|
444
|
-
|
463
|
+
Customize the Traefik command line using `args`:
|
445
464
|
|
446
465
|
```yaml
|
447
466
|
traefik:
|
@@ -450,37 +469,70 @@ traefik:
|
|
450
469
|
accesslog.format: json
|
451
470
|
```
|
452
471
|
|
453
|
-
This
|
472
|
+
This starts the Traefik container with `--accesslog=true --accesslog.format=json` arguments.
|
454
473
|
|
455
|
-
### Traefik
|
474
|
+
### Traefik host port binding
|
456
475
|
|
457
|
-
|
476
|
+
Traefik binds to port 80 by default. Specify an alternative port using `host_port`:
|
458
477
|
|
459
478
|
```yaml
|
460
479
|
traefik:
|
461
480
|
host_port: 8080
|
462
481
|
```
|
463
482
|
|
464
|
-
###
|
483
|
+
### Traefik version, upgrades, and custom images
|
484
|
+
|
485
|
+
MRSK runs the traefik:v2.9 image to track Traefik 2.9.x releases.
|
465
486
|
|
466
|
-
|
487
|
+
To pin Traefik to a specific version or an image published to your registry,
|
488
|
+
specify `image`:
|
489
|
+
|
490
|
+
```yaml
|
491
|
+
traefik:
|
492
|
+
image: traefik:v2.10.0-rc1
|
493
|
+
```
|
494
|
+
|
495
|
+
This is useful for downgrading Traefik if there's an unexpected breaking
|
496
|
+
change in a minor version release, upgrading Traefik to test forthcoming
|
497
|
+
releases, or running your own Traefik-derived image.
|
498
|
+
|
499
|
+
MRSK has not been tested for compatibility with Traefik 3 betas. Please do!
|
500
|
+
|
501
|
+
### Traefik container configuration
|
502
|
+
|
503
|
+
Pass additional Docker configuration for the Traefik container using `options`:
|
467
504
|
|
468
505
|
```yaml
|
469
506
|
traefik:
|
470
507
|
options:
|
471
508
|
publish:
|
472
|
-
|
509
|
+
- 8080:8080
|
473
510
|
volumes:
|
474
|
-
|
511
|
+
- /tmp/example.json:/tmp/example.json
|
475
512
|
memory: 512m
|
476
513
|
```
|
477
514
|
|
478
|
-
This
|
515
|
+
This starts the Traefik container with `--volume /tmp/example.json:/tmp/example.json --publish 8080:8080 --memory 512m` arguments to `docker run`.
|
479
516
|
|
517
|
+
### Traefik container lables
|
480
518
|
|
481
|
-
|
519
|
+
Add labels to Traefik Docker container.
|
482
520
|
|
483
|
-
|
521
|
+
```yaml
|
522
|
+
traefik:
|
523
|
+
lables:
|
524
|
+
- traefik.enable: true
|
525
|
+
- traefik.http.routers.dashboard.rule: Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
|
526
|
+
- traefik.http.routers.dashboard.service: api@internal
|
527
|
+
- traefik.http.routers.dashboard.middlewares: auth
|
528
|
+
- traefik.http.middlewares.auth.basicauth.users: test:$2y$05$H2o72tMaO.TwY1wNQUV1K.fhjRgLHRDWohFvUZOJHBEtUXNKrqUKi # test:password
|
529
|
+
```
|
530
|
+
|
531
|
+
This labels Traefik container with `--label traefik.http.routers.dashboard.middlewares=\"auth\"` and so on.
|
532
|
+
|
533
|
+
### Traefik alternate entrypoints
|
534
|
+
|
535
|
+
You can configure multiple entrypoints for Traefik like so:
|
484
536
|
|
485
537
|
```yaml
|
486
538
|
service: myservice
|
@@ -540,7 +592,7 @@ accessories:
|
|
540
592
|
memory: "2GB"
|
541
593
|
redis:
|
542
594
|
image: redis:latest
|
543
|
-
|
595
|
+
roles:
|
544
596
|
- web
|
545
597
|
port: "36379:6379"
|
546
598
|
volumes:
|
@@ -610,18 +662,21 @@ That'll post a line like follows to a preconfigured chatbot in Basecamp:
|
|
610
662
|
[My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
|
611
663
|
```
|
612
664
|
|
613
|
-
###
|
665
|
+
### Custom healthcheck
|
614
666
|
|
615
|
-
MRSK defaults to checking the health of your application again `/up` on port 3000. You can tailor
|
667
|
+
MRSK defaults to checking the health of your application again `/up` on port 3000 up to 7 times. You can tailor the behaviour with the `healthcheck` setting:
|
616
668
|
|
617
669
|
```yaml
|
618
670
|
healthcheck:
|
619
671
|
path: /healthz
|
620
672
|
port: 4000
|
673
|
+
max_attempts: 7
|
621
674
|
```
|
622
675
|
|
623
676
|
This will ensure your application is configured with a traefik label for the healthcheck against `/healthz` and that the pre-deploy healthcheck that MRSK performs is done against the same path on port 4000.
|
624
677
|
|
678
|
+
The healthcheck also allows for an optional `max_attempts` setting, which will attempt the healthcheck up to the specified number of times before failing the deploy. This is useful for applications that take a while to start up. The default is 7.
|
679
|
+
|
625
680
|
## Commands
|
626
681
|
|
627
682
|
### Running commands on servers
|
data/lib/mrsk/cli/base.rb
CHANGED
@@ -6,8 +6,6 @@ module Mrsk::Cli
|
|
6
6
|
class Base < Thor
|
7
7
|
include SSHKit::DSL
|
8
8
|
|
9
|
-
class LockError < StandardError; end
|
10
|
-
|
11
9
|
def self.exit_on_failure?() true end
|
12
10
|
|
13
11
|
class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
|
@@ -82,8 +80,11 @@ module Mrsk::Cli
|
|
82
80
|
acquire_lock
|
83
81
|
|
84
82
|
yield
|
85
|
-
|
83
|
+
|
86
84
|
release_lock
|
85
|
+
rescue
|
86
|
+
error " \e[31mDeploy lock was not released\e[0m" if MRSK.lock_count > 0
|
87
|
+
raise
|
87
88
|
end
|
88
89
|
|
89
90
|
def acquire_lock
|
@@ -95,9 +96,10 @@ module Mrsk::Cli
|
|
95
96
|
rescue SSHKit::Runner::ExecuteError => e
|
96
97
|
if e.message =~ /cannot create directory/
|
97
98
|
invoke "mrsk:cli:lock:status", []
|
99
|
+
raise LockError, "Deploy lock found"
|
100
|
+
else
|
101
|
+
raise e
|
98
102
|
end
|
99
|
-
|
100
|
-
raise LockError, "Deploy lock found"
|
101
103
|
end
|
102
104
|
|
103
105
|
def release_lock
|
data/lib/mrsk/cli/healthcheck.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
|
2
|
-
MAX_ATTEMPTS = 7
|
3
2
|
|
4
3
|
class HealthcheckError < StandardError; end
|
5
4
|
|
@@ -13,6 +12,7 @@ class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
|
|
13
12
|
|
14
13
|
target = "Health check against #{MRSK.config.healthcheck["path"]}"
|
15
14
|
attempt = 1
|
15
|
+
max_attempts = MRSK.config.healthcheck["max_attempts"]
|
16
16
|
|
17
17
|
begin
|
18
18
|
status = capture_with_info(*MRSK.healthcheck.curl)
|
@@ -23,8 +23,8 @@ class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
|
|
23
23
|
raise HealthcheckError, "#{target} failed with status #{status}"
|
24
24
|
end
|
25
25
|
rescue SSHKit::Command::Failed
|
26
|
-
if attempt <=
|
27
|
-
info "#{target} failed to respond, retrying in #{attempt}s..."
|
26
|
+
if attempt <= max_attempts
|
27
|
+
info "#{target} failed to respond, retrying in #{attempt}s (attempt #{attempt}/#{max_attempts})..."
|
28
28
|
sleep attempt
|
29
29
|
attempt += 1
|
30
30
|
|
data/lib/mrsk/cli/lock.rb
CHANGED
@@ -12,7 +12,7 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
|
|
12
12
|
message = options[:message]
|
13
13
|
handle_missing_lock do
|
14
14
|
on(MRSK.primary_host) { execute *MRSK.lock.acquire(message, MRSK.config.version) }
|
15
|
-
say "
|
15
|
+
say "Acquired the deploy lock"
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -20,7 +20,7 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
|
|
20
20
|
def release
|
21
21
|
handle_missing_lock do
|
22
22
|
on(MRSK.primary_host) { execute *MRSK.lock.release }
|
23
|
-
say "
|
23
|
+
say "Released the deploy lock"
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
data/lib/mrsk/cli/main.rb
CHANGED
@@ -77,21 +77,26 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
77
77
|
with_lock do
|
78
78
|
MRSK.config.version = version
|
79
79
|
|
80
|
-
if
|
80
|
+
if container_available?(version)
|
81
81
|
say "Start version #{version}, then wait #{MRSK.config.readiness_delay}s for app to boot before stopping the old version...", :magenta
|
82
82
|
|
83
83
|
cli = self
|
84
84
|
old_version = nil
|
85
85
|
|
86
86
|
on(MRSK.hosts) do |host|
|
87
|
-
|
87
|
+
roles = MRSK.roles_on(host)
|
88
88
|
|
89
|
-
|
89
|
+
roles.each do |role|
|
90
|
+
app = MRSK.app(role: role)
|
91
|
+
old_version = capture_with_info(*app.current_running_version).strip.presence
|
90
92
|
|
91
|
-
|
92
|
-
sleep MRSK.config.readiness_delay
|
93
|
+
execute *app.start
|
93
94
|
|
94
|
-
|
95
|
+
if old_version
|
96
|
+
sleep MRSK.config.readiness_delay
|
97
|
+
|
98
|
+
execute *app.stop(version: old_version), raise_on_non_zero_exit: false
|
99
|
+
end
|
95
100
|
end
|
96
101
|
end
|
97
102
|
|
@@ -119,7 +124,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
119
124
|
desc "config", "Show combined config (including secrets!)"
|
120
125
|
def config
|
121
126
|
run_locally do
|
122
|
-
puts MRSK.config.to_h.to_yaml
|
127
|
+
puts Mrsk::Utils.redacted(MRSK.config.to_h).to_yaml
|
123
128
|
end
|
124
129
|
end
|
125
130
|
|
@@ -214,10 +219,15 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
214
219
|
subcommand "lock", Mrsk::Cli::Lock
|
215
220
|
|
216
221
|
private
|
217
|
-
def
|
218
|
-
|
219
|
-
|
220
|
-
|
222
|
+
def container_available?(version, host: MRSK.primary_host)
|
223
|
+
available = nil
|
224
|
+
|
225
|
+
on(host) do
|
226
|
+
first_role = MRSK.roles_on(host).first
|
227
|
+
available = capture_with_info(*MRSK.app(role: first_role).container_id_for_version(version)).present?
|
228
|
+
end
|
229
|
+
|
230
|
+
available
|
221
231
|
end
|
222
232
|
|
223
233
|
def deploy_options
|
data/lib/mrsk/cli/traefik.rb
CHANGED
@@ -2,7 +2,10 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
2
2
|
desc "boot", "Boot Traefik on servers"
|
3
3
|
def boot
|
4
4
|
with_lock do
|
5
|
-
on(MRSK.traefik_hosts)
|
5
|
+
on(MRSK.traefik_hosts) do
|
6
|
+
execute *MRSK.registry.login
|
7
|
+
execute *MRSK.traefik.run, raise_on_non_zero_exit: false
|
8
|
+
end
|
6
9
|
end
|
7
10
|
end
|
8
11
|
|
data/lib/mrsk/cli.rb
CHANGED
data/lib/mrsk/commands/base.rb
CHANGED
@@ -2,7 +2,7 @@ class Mrsk::Commands::Registry < Mrsk::Commands::Base
|
|
2
2
|
delegate :registry, to: :config
|
3
3
|
|
4
4
|
def login
|
5
|
-
docker :login, registry["server"], "-u",
|
5
|
+
docker :login, registry["server"], "-u", sensitive(lookup("username")), "-p", sensitive(lookup("password"))
|
6
6
|
end
|
7
7
|
|
8
8
|
def logout
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
2
|
-
delegate :optionize, to: Mrsk::Utils
|
2
|
+
delegate :argumentize, :optionize, to: Mrsk::Utils
|
3
3
|
|
4
|
-
|
4
|
+
DEFAULT_IMAGE = "traefik:v2.9"
|
5
5
|
CONTAINER_PORT = 80
|
6
6
|
|
7
7
|
def run
|
@@ -11,8 +11,9 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|
11
11
|
"--publish", port,
|
12
12
|
"--volume", "/var/run/docker.sock:/var/run/docker.sock",
|
13
13
|
*config.logging_args,
|
14
|
+
*label_args,
|
14
15
|
*docker_options_args,
|
15
|
-
|
16
|
+
image,
|
16
17
|
"--providers.docker",
|
17
18
|
"--log.level=DEBUG",
|
18
19
|
*cmd_option_args
|
@@ -56,6 +57,18 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|
56
57
|
end
|
57
58
|
|
58
59
|
private
|
60
|
+
def label_args
|
61
|
+
argumentize "--label", labels
|
62
|
+
end
|
63
|
+
|
64
|
+
def labels
|
65
|
+
config.traefik["labels"] || []
|
66
|
+
end
|
67
|
+
|
68
|
+
def image
|
69
|
+
config.traefik.fetch("image") { DEFAULT_IMAGE }
|
70
|
+
end
|
71
|
+
|
59
72
|
def docker_options_args
|
60
73
|
optionize(config.traefik["options"] || {})
|
61
74
|
end
|
@@ -74,18 +74,22 @@ class Mrsk::Configuration::Role
|
|
74
74
|
def traefik_labels
|
75
75
|
if running_traefik?
|
76
76
|
{
|
77
|
-
"traefik.http.routers.#{
|
78
|
-
"traefik.http.services.#{
|
79
|
-
"traefik.http.services.#{
|
80
|
-
"traefik.http.middlewares.#{
|
81
|
-
"traefik.http.middlewares.#{
|
82
|
-
"traefik.http.routers.#{
|
77
|
+
"traefik.http.routers.#{traefik_service}.rule" => "PathPrefix(`/`)",
|
78
|
+
"traefik.http.services.#{traefik_service}.loadbalancer.healthcheck.path" => config.healthcheck["path"],
|
79
|
+
"traefik.http.services.#{traefik_service}.loadbalancer.healthcheck.interval" => "1s",
|
80
|
+
"traefik.http.middlewares.#{traefik_service}-retry.retry.attempts" => "5",
|
81
|
+
"traefik.http.middlewares.#{traefik_service}-retry.retry.initialinterval" => "500ms",
|
82
|
+
"traefik.http.routers.#{traefik_service}.middlewares" => "#{traefik_service}-retry@docker"
|
83
83
|
}
|
84
84
|
else
|
85
85
|
{}
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
def traefik_service
|
90
|
+
[ config.service, name, config.destination ].compact.join("-")
|
91
|
+
end
|
92
|
+
|
89
93
|
def custom_labels
|
90
94
|
Hash.new.tap do |labels|
|
91
95
|
labels.merge!(config.labels) if config.labels.present?
|
data/lib/mrsk/configuration.rb
CHANGED
@@ -143,6 +143,8 @@ class Mrsk::Configuration
|
|
143
143
|
if raw_config.ssh.present? && raw_config.ssh["proxy"]
|
144
144
|
Net::SSH::Proxy::Jump.new \
|
145
145
|
raw_config.ssh["proxy"].include?("@") ? raw_config.ssh["proxy"] : "root@#{raw_config.ssh["proxy"]}"
|
146
|
+
elsif raw_config.ssh.present? && raw_config.ssh["proxy_command"]
|
147
|
+
Net::SSH::Proxy::Command.new(raw_config.ssh["proxy_command"])
|
146
148
|
end
|
147
149
|
end
|
148
150
|
|
@@ -156,7 +158,7 @@ class Mrsk::Configuration
|
|
156
158
|
end
|
157
159
|
|
158
160
|
def healthcheck
|
159
|
-
{ "path" => "/up", "port" => 3000 }.merge(raw_config.healthcheck || {})
|
161
|
+
{ "path" => "/up", "port" => 3000, "max_attempts" => 7 }.merge(raw_config.healthcheck || {})
|
160
162
|
end
|
161
163
|
|
162
164
|
def readiness_delay
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
2
|
+
|
3
|
+
class Mrsk::Utils::Sensitive
|
4
|
+
# So SSHKit knows to redact these values.
|
5
|
+
include SSHKit::Redaction
|
6
|
+
|
7
|
+
attr_reader :unredacted, :redaction
|
8
|
+
delegate :to_s, to: :unredacted
|
9
|
+
delegate :inspect, to: :redaction
|
10
|
+
|
11
|
+
def initialize(value, redaction: "[REDACTED]")
|
12
|
+
@unredacted, @redaction = value, redaction
|
13
|
+
end
|
14
|
+
|
15
|
+
# Sensitive values won't leak into YAML output.
|
16
|
+
def encode_with(coder)
|
17
|
+
coder.represent_scalar nil, redaction
|
18
|
+
end
|
19
|
+
end
|
data/lib/mrsk/utils.rb
CHANGED
@@ -2,11 +2,12 @@ module Mrsk::Utils
|
|
2
2
|
extend self
|
3
3
|
|
4
4
|
# Return a list of escaped shell arguments using the same named argument against the passed attributes (hash or array).
|
5
|
-
def argumentize(argument, attributes,
|
5
|
+
def argumentize(argument, attributes, sensitive: false)
|
6
6
|
Array(attributes).flat_map do |key, value|
|
7
7
|
if value.present?
|
8
|
-
|
9
|
-
|
8
|
+
attr = "#{key}=#{escape_shell_value(value)}"
|
9
|
+
attr = self.sensitive(attr, redaction: "#{key}=[REDACTED]") if sensitive
|
10
|
+
[ argument, attr]
|
10
11
|
else
|
11
12
|
[ argument, key ]
|
12
13
|
end
|
@@ -17,7 +18,7 @@ module Mrsk::Utils
|
|
17
18
|
# but redacts and expands secrets.
|
18
19
|
def argumentize_env_with_secrets(env)
|
19
20
|
if (secrets = env["secret"]).present?
|
20
|
-
argumentize("-e", secrets.to_h { |key| [ key, ENV.fetch(key) ] },
|
21
|
+
argumentize("-e", secrets.to_h { |key| [ key, ENV.fetch(key) ] }, sensitive: true) + argumentize("-e", env["clear"])
|
21
22
|
else
|
22
23
|
argumentize "-e", env.fetch("clear", env)
|
23
24
|
end
|
@@ -39,9 +40,37 @@ module Mrsk::Utils
|
|
39
40
|
args.flat_map { |key, value| value.try(:map) { |entry| [key, entry] } || [ [ key, value ] ] }
|
40
41
|
end
|
41
42
|
|
42
|
-
#
|
43
|
-
|
44
|
-
|
43
|
+
# Marks sensitive values for redaction in logs and human-visible output.
|
44
|
+
# Pass `redaction:` to change the default `"[REDACTED]"` redaction, e.g.
|
45
|
+
# `sensitive "#{arg}=#{secret}", redaction: "#{arg}=xxxx"
|
46
|
+
def sensitive(...)
|
47
|
+
Mrsk::Utils::Sensitive.new(...)
|
48
|
+
end
|
49
|
+
|
50
|
+
def redacted(value)
|
51
|
+
case
|
52
|
+
when value.respond_to?(:redaction)
|
53
|
+
value.redaction
|
54
|
+
when value.respond_to?(:transform_values)
|
55
|
+
value.transform_values { |value| redacted value }
|
56
|
+
when value.respond_to?(:map)
|
57
|
+
value.map { |element| redacted element }
|
58
|
+
else
|
59
|
+
value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def unredacted(value)
|
64
|
+
case
|
65
|
+
when value.respond_to?(:unredacted)
|
66
|
+
value.unredacted
|
67
|
+
when value.respond_to?(:transform_values)
|
68
|
+
value.transform_values { |value| unredacted value }
|
69
|
+
when value.respond_to?(:map)
|
70
|
+
value.map { |element| unredacted element }
|
71
|
+
else
|
72
|
+
value
|
73
|
+
end
|
45
74
|
end
|
46
75
|
|
47
76
|
# Escape a value to make it safe for shell use.
|
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.11.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-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.21'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: net-ssh
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '7.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '7.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: thor
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -197,6 +211,7 @@ files:
|
|
197
211
|
- lib/mrsk/configuration/role.rb
|
198
212
|
- lib/mrsk/sshkit_with_ext.rb
|
199
213
|
- lib/mrsk/utils.rb
|
214
|
+
- lib/mrsk/utils/sensitive.rb
|
200
215
|
- lib/mrsk/version.rb
|
201
216
|
homepage: https://github.com/rails/mrsk
|
202
217
|
licenses:
|
@@ -217,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
217
232
|
- !ruby/object:Gem::Version
|
218
233
|
version: '0'
|
219
234
|
requirements: []
|
220
|
-
rubygems_version: 3.4.
|
235
|
+
rubygems_version: 3.4.10
|
221
236
|
signing_key:
|
222
237
|
specification_version: 4
|
223
238
|
summary: Deploy web apps in containers to servers running Docker with zero downtime.
|