mrsk 0.10.1 → 0.12.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.
@@ -87,6 +87,10 @@ class Mrsk::Configuration
87
87
  roles.select(&:running_traefik?).flat_map(&:hosts).uniq
88
88
  end
89
89
 
90
+ def boot
91
+ Mrsk::Configuration::Boot.new(config: self)
92
+ end
93
+
90
94
 
91
95
  def repository
92
96
  [ raw_config.registry["server"], image ].compact.join("/")
@@ -143,6 +147,8 @@ class Mrsk::Configuration
143
147
  if raw_config.ssh.present? && raw_config.ssh["proxy"]
144
148
  Net::SSH::Proxy::Jump.new \
145
149
  raw_config.ssh["proxy"].include?("@") ? raw_config.ssh["proxy"] : "root@#{raw_config.ssh["proxy"]}"
150
+ elsif raw_config.ssh.present? && raw_config.ssh["proxy_command"]
151
+ Net::SSH::Proxy::Command.new(raw_config.ssh["proxy_command"])
146
152
  end
147
153
  end
148
154
 
@@ -156,7 +162,7 @@ class Mrsk::Configuration
156
162
  end
157
163
 
158
164
  def healthcheck
159
- { "path" => "/up", "port" => 3000 }.merge(raw_config.healthcheck || {})
165
+ { "path" => "/up", "port" => 3000, "max_attempts" => 7 }.merge(raw_config.healthcheck || {})
160
166
  end
161
167
 
162
168
  def readiness_delay
@@ -2,8 +2,8 @@ require "sshkit"
2
2
  require "sshkit/dsl"
3
3
 
4
4
  class SSHKit::Backend::Abstract
5
- def capture_with_info(*args)
6
- capture(*args, verbosity: Logger::INFO)
5
+ def capture_with_info(*args, **kwargs)
6
+ capture(*args, **kwargs, verbosity: Logger::INFO)
7
7
  end
8
8
 
9
9
  def puts_by_host(host, output, type: "App")
@@ -0,0 +1,39 @@
1
+ class Mrsk::Utils::HealthcheckPoller
2
+ TRAEFIK_HEALTHY_DELAY = 2
3
+
4
+ class HealthcheckError < StandardError; end
5
+
6
+ class << self
7
+ def wait_for_healthy(pause_after_ready: false, &block)
8
+ attempt = 1
9
+ max_attempts = MRSK.config.healthcheck["max_attempts"]
10
+
11
+ begin
12
+ case status = block.call
13
+ when "healthy"
14
+ sleep TRAEFIK_HEALTHY_DELAY if pause_after_ready
15
+ when "running" # No health check configured
16
+ sleep MRSK.config.readiness_delay if pause_after_ready
17
+ else
18
+ raise HealthcheckError, "container not ready (#{status})"
19
+ end
20
+ rescue HealthcheckError => e
21
+ if attempt <= max_attempts
22
+ info "#{e.message}, retrying in #{attempt}s (attempt #{attempt}/#{max_attempts})..."
23
+ sleep attempt
24
+ attempt += 1
25
+ retry
26
+ else
27
+ raise
28
+ end
29
+ end
30
+
31
+ info "Container is healthy!"
32
+ end
33
+
34
+ private
35
+ def info(message)
36
+ SSHKit.config.output.info(message)
37
+ end
38
+ end
39
+ end
@@ -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
@@ -1,12 +1,15 @@
1
1
  module Mrsk::Utils
2
2
  extend self
3
3
 
4
+ DOLLAR_SIGN_WITHOUT_SHELL_EXPANSION_REGEX = /\$(?!{[^\}]*\})/
5
+
4
6
  # Return a list of escaped shell arguments using the same named argument against the passed attributes (hash or array).
5
- def argumentize(argument, attributes, redacted: false)
7
+ def argumentize(argument, attributes, sensitive: false)
6
8
  Array(attributes).flat_map do |key, value|
7
9
  if value.present?
8
- escaped_pair = [ key, escape_shell_value(value) ].join("=")
9
- [ argument, redacted ? redact(escaped_pair) : escaped_pair ]
10
+ attr = "#{key}=#{escape_shell_value(value)}"
11
+ attr = self.sensitive(attr, redaction: "#{key}=[REDACTED]") if sensitive
12
+ [ argument, attr]
10
13
  else
11
14
  [ argument, key ]
12
15
  end
@@ -17,7 +20,7 @@ module Mrsk::Utils
17
20
  # but redacts and expands secrets.
18
21
  def argumentize_env_with_secrets(env)
19
22
  if (secrets = env["secret"]).present?
20
- argumentize("-e", secrets.to_h { |key| [ key, ENV.fetch(key) ] }, redacted: true) + argumentize("-e", env["clear"])
23
+ argumentize("-e", secrets.to_h { |key| [ key, ENV.fetch(key) ] }, sensitive: true) + argumentize("-e", env["clear"])
21
24
  else
22
25
  argumentize "-e", env.fetch("clear", env)
23
26
  end
@@ -39,14 +42,44 @@ module Mrsk::Utils
39
42
  args.flat_map { |key, value| value.try(:map) { |entry| [key, entry] } || [ [ key, value ] ] }
40
43
  end
41
44
 
42
- # Copied from SSHKit::Backend::Abstract#redact to be available inside Commands classes
43
- def redact(arg) # Used in execute_command to hide redact() args a user passes in
44
- arg.to_s.extend(SSHKit::Redaction) # to_s due to our inability to extend Integer, etc
45
+ # Marks sensitive values for redaction in logs and human-visible output.
46
+ # Pass `redaction:` to change the default `"[REDACTED]"` redaction, e.g.
47
+ # `sensitive "#{arg}=#{secret}", redaction: "#{arg}=xxxx"
48
+ def sensitive(...)
49
+ Mrsk::Utils::Sensitive.new(...)
50
+ end
51
+
52
+ def redacted(value)
53
+ case
54
+ when value.respond_to?(:redaction)
55
+ value.redaction
56
+ when value.respond_to?(:transform_values)
57
+ value.transform_values { |value| redacted value }
58
+ when value.respond_to?(:map)
59
+ value.map { |element| redacted element }
60
+ else
61
+ value
62
+ end
63
+ end
64
+
65
+ def unredacted(value)
66
+ case
67
+ when value.respond_to?(:unredacted)
68
+ value.unredacted
69
+ when value.respond_to?(:transform_values)
70
+ value.transform_values { |value| unredacted value }
71
+ when value.respond_to?(:map)
72
+ value.map { |element| unredacted element }
73
+ else
74
+ value
75
+ end
45
76
  end
46
77
 
47
78
  # Escape a value to make it safe for shell use.
48
79
  def escape_shell_value(value)
49
- value.to_s.dump.gsub(/`/, '\\\\`')
80
+ value.to_s.dump
81
+ .gsub(/`/, '\\\\`')
82
+ .gsub(DOLLAR_SIGN_WITHOUT_SHELL_EXPANSION_REGEX, '\$')
50
83
  end
51
84
 
52
85
  # Abbreviate a git revhash for concise display
data/lib/mrsk/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mrsk
2
- VERSION = "0.10.1"
2
+ VERSION = "0.12.0"
3
3
  end
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.10.1
4
+ version: 0.12.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-03-29 00:00:00.000000000 Z
11
+ date: 2023-05-02 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
@@ -187,6 +201,7 @@ files:
187
201
  - lib/mrsk/commands/builder/multiarch/remote.rb
188
202
  - lib/mrsk/commands/builder/native.rb
189
203
  - lib/mrsk/commands/builder/native/remote.rb
204
+ - lib/mrsk/commands/docker.rb
190
205
  - lib/mrsk/commands/healthcheck.rb
191
206
  - lib/mrsk/commands/lock.rb
192
207
  - lib/mrsk/commands/prune.rb
@@ -194,9 +209,12 @@ files:
194
209
  - lib/mrsk/commands/traefik.rb
195
210
  - lib/mrsk/configuration.rb
196
211
  - lib/mrsk/configuration/accessory.rb
212
+ - lib/mrsk/configuration/boot.rb
197
213
  - lib/mrsk/configuration/role.rb
198
214
  - lib/mrsk/sshkit_with_ext.rb
199
215
  - lib/mrsk/utils.rb
216
+ - lib/mrsk/utils/healthcheck_poller.rb
217
+ - lib/mrsk/utils/sensitive.rb
200
218
  - lib/mrsk/version.rb
201
219
  homepage: https://github.com/rails/mrsk
202
220
  licenses:
@@ -217,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
235
  - !ruby/object:Gem::Version
218
236
  version: '0'
219
237
  requirements: []
220
- rubygems_version: 3.4.8
238
+ rubygems_version: 3.4.10
221
239
  signing_key:
222
240
  specification_version: 4
223
241
  summary: Deploy web apps in containers to servers running Docker with zero downtime.