mrsk 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.
|