mrsk 0.11.0 → 0.12.1

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.
@@ -2,11 +2,12 @@ require "active_support/core_ext/enumerable"
2
2
  require "active_support/core_ext/module/delegation"
3
3
 
4
4
  class Mrsk::Commander
5
- attr_accessor :verbosity, :lock_count
5
+ attr_accessor :verbosity, :holding_lock, :hold_lock_on_error
6
6
 
7
7
  def initialize
8
8
  self.verbosity = :info
9
- self.lock_count = 0
9
+ self.holding_lock = false
10
+ self.hold_lock_on_error = false
10
11
  end
11
12
 
12
13
  def config
@@ -35,7 +36,7 @@ class Mrsk::Commander
35
36
  end
36
37
 
37
38
  def primary_host
38
- specific_hosts&.first || config.primary_web_host
39
+ specific_hosts&.first || specific_roles&.first&.primary_host || config.primary_web_host
39
40
  end
40
41
 
41
42
  def roles
@@ -50,6 +51,14 @@ class Mrsk::Commander
50
51
  end
51
52
  end
52
53
 
54
+ def boot_strategy
55
+ if config.boot.limit.present?
56
+ { in: :groups, limit: config.boot.limit, wait: config.boot.wait }
57
+ else
58
+ {}
59
+ end
60
+ end
61
+
53
62
  def roles_on(host)
54
63
  roles.select { |role| role.hosts.include?(host.to_s) }.map(&:name)
55
64
  end
@@ -75,14 +84,18 @@ class Mrsk::Commander
75
84
  Mrsk::Commands::Accessory.new(config, name: name)
76
85
  end
77
86
 
78
- def auditor(role: nil)
79
- Mrsk::Commands::Auditor.new(config, role: role)
87
+ def auditor(**details)
88
+ Mrsk::Commands::Auditor.new(config, **details)
80
89
  end
81
90
 
82
91
  def builder
83
92
  @builder ||= Mrsk::Commands::Builder.new(config)
84
93
  end
85
94
 
95
+ def docker
96
+ @docker ||= Mrsk::Commands::Docker.new(config)
97
+ end
98
+
86
99
  def healthcheck
87
100
  @healthcheck ||= Mrsk::Commands::Healthcheck.new(config)
88
101
  end
@@ -115,6 +128,14 @@ class Mrsk::Commander
115
128
  SSHKit.config.output_verbosity = old_level
116
129
  end
117
130
 
131
+ def holding_lock?
132
+ self.holding_lock
133
+ end
134
+
135
+ def hold_lock_on_error?
136
+ self.hold_lock_on_error
137
+ end
138
+
118
139
  private
119
140
  # Lazy setup of SSHKit
120
141
  def configure_sshkit_with(config)
@@ -15,6 +15,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
15
15
  "--name", container_name,
16
16
  "-e", "MRSK_CONTAINER_NAME=\"#{container_name}\"",
17
17
  *role.env_args,
18
+ *role.health_check_args,
18
19
  *config.logging_args,
19
20
  *config.volume_args,
20
21
  *role.label_args,
@@ -27,9 +28,13 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
27
28
  docker :start, container_name
28
29
  end
29
30
 
31
+ def status(version:)
32
+ pipe container_id_for_version(version), xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
33
+ end
34
+
30
35
  def stop(version: nil)
31
36
  pipe \
32
- version ? container_id_for_version(version) : current_container_id,
37
+ version ? container_id_for_version(version) : current_running_container_id,
33
38
  xargs(config.stop_wait_time ? docker(:stop, "-t", config.stop_wait_time) : docker(:stop))
34
39
  end
35
40
 
@@ -40,7 +45,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
40
45
 
41
46
  def logs(since: nil, lines: nil, grep: nil)
42
47
  pipe \
43
- current_container_id,
48
+ current_running_container_id,
44
49
  "xargs docker logs#{" --since #{since}" if since}#{" --tail #{lines}" if lines} 2>&1",
45
50
  ("grep '#{grep}'" if grep)
46
51
  end
@@ -48,7 +53,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
48
53
  def follow_logs(host:, grep: nil)
49
54
  run_over_ssh \
50
55
  pipe(
51
- current_container_id,
56
+ current_running_container_id,
52
57
  "xargs docker logs --timestamps --tail 10 --follow 2>&1",
53
58
  (%(grep "#{grep}") if grep)
54
59
  ),
@@ -82,8 +87,8 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
82
87
  end
83
88
 
84
89
 
85
- def current_container_id
86
- docker :ps, "--quiet", *filter_args
90
+ def current_running_container_id
91
+ docker :ps, "--quiet", *filter_args(status: :running), "--latest"
87
92
  end
88
93
 
89
94
  def container_id_for_version(version)
@@ -91,11 +96,14 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
91
96
  end
92
97
 
93
98
  def current_running_version
94
- # FIXME: Find more graceful way to extract the version from "app-version" than using sed and tail!
99
+ list_versions("--latest", status: :running)
100
+ end
101
+
102
+ def list_versions(*docker_args, status: nil)
95
103
  pipe \
96
- docker(:ps, *filter_args, "--format", '"{{.Names}}"'),
97
- %(sed 's/-/\\n/g'),
98
- "tail -n 1"
104
+ docker(:ps, *filter_args(status: status), *docker_args, "--format", '"{{.Names}}"'),
105
+ %(grep -oE "\\-[^-]+$"), # Extract SHA from "service-role-dest-SHA"
106
+ %(cut -c 2-)
99
107
  end
100
108
 
101
109
  def list_containers
@@ -128,20 +136,25 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
128
136
  docker :image, :prune, "--all", "--force", *filter_args
129
137
  end
130
138
 
139
+ def tag_current_as_latest
140
+ docker :tag, config.absolute_image, config.latest_image
141
+ end
142
+
131
143
 
132
144
  private
133
145
  def container_name(version = nil)
134
146
  [ config.service, role, config.destination, version || config.version ].compact.join("-")
135
147
  end
136
148
 
137
- def filter_args
138
- argumentize "--filter", filters
149
+ def filter_args(status: nil)
150
+ argumentize "--filter", filters(status: status)
139
151
  end
140
152
 
141
- def filters
153
+ def filters(status: nil)
142
154
  [ "label=service=#{config.service}" ].tap do |filters|
143
155
  filters << "label=destination=#{config.destination}" if config.destination
144
156
  filters << "label=role=#{role}" if role
157
+ filters << "status=#{status}" if status
145
158
  end
146
159
  end
147
160
  end
@@ -1,24 +1,24 @@
1
- require "active_support/core_ext/time/conversions"
1
+ require "time"
2
2
 
3
3
  class Mrsk::Commands::Auditor < Mrsk::Commands::Base
4
- attr_reader :role
4
+ attr_reader :details
5
5
 
6
- def initialize(config, role: nil)
6
+ def initialize(config, **details)
7
7
  super(config)
8
- @role = role
8
+ @details = default_details.merge(details)
9
9
  end
10
10
 
11
11
  # Runs remotely
12
- def record(line)
12
+ def record(line, **details)
13
13
  append \
14
- [ :echo, tagged_record_line(line) ],
14
+ [ :echo, *audit_tags(**details), line ],
15
15
  audit_log_file
16
16
  end
17
17
 
18
18
  # Runs locally
19
- def broadcast(line)
19
+ def broadcast(line, **details)
20
20
  if broadcast_cmd = config.audit_broadcast_cmd
21
- [ broadcast_cmd, tagged_broadcast_line(line) ]
21
+ [ broadcast_cmd, *broadcast_args(line, **details), env: env_for(event: line, **details) ]
22
22
  end
23
23
  end
24
24
 
@@ -31,27 +31,29 @@ class Mrsk::Commands::Auditor < Mrsk::Commands::Base
31
31
  [ "mrsk", config.service, config.destination, "audit.log" ].compact.join("-")
32
32
  end
33
33
 
34
- def tagged_record_line(line)
35
- tagged_line recorded_at_tag, performer_tag, role_tag, line
34
+ def default_details
35
+ { recorded_at: Time.now.utc.iso8601,
36
+ performer: `whoami`.chomp,
37
+ destination: config.destination }
36
38
  end
37
39
 
38
- def tagged_broadcast_line(line)
39
- tagged_line performer_tag, role_tag, line
40
+ def audit_tags(**details)
41
+ tags_for **self.details.merge(details)
40
42
  end
41
43
 
42
- def tagged_line(*tags_and_line)
43
- "'#{tags_and_line.compact.join(" ")}'"
44
+ def broadcast_args(line, **details)
45
+ "'#{broadcast_tags(**details).join(" ")} #{line}'"
44
46
  end
45
47
 
46
- def recorded_at_tag
47
- "[#{Time.now.to_fs(:db)}]"
48
+ def broadcast_tags(**details)
49
+ tags_for **self.details.merge(details).except(:recorded_at)
48
50
  end
49
51
 
50
- def performer_tag
51
- "[#{`whoami`.strip}]"
52
+ def tags_for(**details)
53
+ details.compact.values.map { |value| "[#{value}]" }
52
54
  end
53
55
 
54
- def role_tag
55
- "[#{role}]" if role
56
+ def env_for(**details)
57
+ self.details.merge(details).compact.transform_keys { |detail| "MRSK_#{detail.upcase}" }
56
58
  end
57
59
  end
@@ -2,6 +2,9 @@ module Mrsk::Commands
2
2
  class Base
3
3
  delegate :sensitive, :argumentize, to: Mrsk::Utils
4
4
 
5
+ DOCKER_HEALTH_STATUS_FORMAT = "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'"
6
+ DOCKER_HEALTH_LOG_FORMAT = "'{{json .State.Health}}'"
7
+
5
8
  attr_accessor :config
6
9
 
7
10
  def initialize(config)
@@ -1,4 +1,7 @@
1
+
1
2
  class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
3
+ class BuilderError < StandardError; end
4
+
2
5
  delegate :argumentize, to: Mrsk::Utils
3
6
 
4
7
  def clean
@@ -7,7 +10,6 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
7
10
 
8
11
  def pull
9
12
  docker :pull, config.absolute_image
10
- docker :pull, config.latest_image
11
13
  end
12
14
 
13
15
  def build_options
@@ -18,6 +20,7 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
18
20
  context
19
21
  end
20
22
 
23
+
21
24
  private
22
25
  def build_tags
23
26
  [ "-t", config.absolute_image, "-t", config.latest_image ]
@@ -36,7 +39,11 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
36
39
  end
37
40
 
38
41
  def build_dockerfile
39
- argumentize "--file", dockerfile
42
+ if Pathname.new(File.expand_path(dockerfile)).exist?
43
+ argumentize "--file", dockerfile
44
+ else
45
+ raise BuilderError, "Missing #{dockerfile}"
46
+ end
40
47
  end
41
48
 
42
49
  def args
@@ -2,7 +2,7 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
2
2
  delegate :create, :remove, :push, :clean, :pull, :info, to: :target
3
3
 
4
4
  def name
5
- target.class.to_s.remove("Mrsk::Commands::Builder::").underscore
5
+ target.class.to_s.remove("Mrsk::Commands::Builder::").underscore.inquiry
6
6
  end
7
7
 
8
8
  def target
@@ -33,4 +33,24 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
33
33
  def multiarch_remote
34
34
  @multiarch_remote ||= Mrsk::Commands::Builder::Multiarch::Remote.new(config)
35
35
  end
36
+
37
+
38
+ def ensure_local_dependencies_installed
39
+ if name.native?
40
+ ensure_local_docker_installed
41
+ else
42
+ combine \
43
+ ensure_local_docker_installed,
44
+ ensure_local_buildx_installed
45
+ end
46
+ end
47
+
48
+ private
49
+ def ensure_local_docker_installed
50
+ docker "--version"
51
+ end
52
+
53
+ def ensure_local_buildx_installed
54
+ docker :buildx, "version"
55
+ end
36
56
  end
@@ -0,0 +1,21 @@
1
+ class Mrsk::Commands::Docker < Mrsk::Commands::Base
2
+ # Install Docker using the https://github.com/docker/docker-install convenience script.
3
+ def install
4
+ pipe [ :curl, "-fsSL", "https://get.docker.com" ], :sh
5
+ end
6
+
7
+ # Checks the Docker client version. Fails if Docker is not installed.
8
+ def installed?
9
+ docker "-v"
10
+ end
11
+
12
+ # Checks the Docker server version. Fails if Docker is not running.
13
+ def running?
14
+ docker :version
15
+ end
16
+
17
+ # Do we have superuser access to install Docker and start system services?
18
+ def superuser?
19
+ [ '[ "${EUID:-$(id -u)}" -eq 0 ]' ]
20
+ end
21
+ end
@@ -11,14 +11,19 @@ class Mrsk::Commands::Healthcheck < Mrsk::Commands::Base
11
11
  "--label", "service=#{container_name}",
12
12
  "-e", "MRSK_CONTAINER_NAME=\"#{container_name}\"",
13
13
  *web.env_args,
14
+ *web.health_check_args,
14
15
  *config.volume_args,
15
16
  *web.option_args,
16
17
  config.absolute_image,
17
18
  web.cmd
18
19
  end
19
20
 
20
- def curl
21
- [ :curl, "--silent", "--output", "/dev/null", "--write-out", "'%{http_code}'", "--max-time", "2", health_url ]
21
+ def status
22
+ pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
23
+ end
24
+
25
+ def container_health_log
26
+ pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_LOG_FORMAT))
22
27
  end
23
28
 
24
29
  def logs
@@ -1,5 +1,5 @@
1
1
  require "active_support/duration"
2
- require "active_support/core_ext/numeric/time"
2
+ require "time"
3
3
 
4
4
  class Mrsk::Commands::Lock < Mrsk::Commands::Base
5
5
  def acquire(message, version)
@@ -49,7 +49,7 @@ class Mrsk::Commands::Lock < Mrsk::Commands::Base
49
49
 
50
50
  def lock_details(message, version)
51
51
  <<~DETAILS.strip
52
- Locked by: #{locked_by} at #{Time.now.gmtime}
52
+ Locked by: #{locked_by} at #{Time.now.utc.iso8601}
53
53
  Version: #{version}
54
54
  Message: #{message}
55
55
  DETAILS
@@ -2,11 +2,19 @@ require "active_support/duration"
2
2
  require "active_support/core_ext/numeric/time"
3
3
 
4
4
  class Mrsk::Commands::Prune < Mrsk::Commands::Base
5
- def images(until_hours: 7.days.in_hours.to_i)
6
- docker :image, :prune, "--all", "--force", "--filter", "label=service=#{config.service}", "--filter", "until=#{until_hours}h"
5
+ def images
6
+ docker :image, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true"
7
7
  end
8
8
 
9
- def containers(until_hours: 3.days.in_hours.to_i)
10
- docker :container, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "until=#{until_hours}h"
9
+ def containers(keep_last: 5)
10
+ pipe \
11
+ docker(:ps, "-q", "-a", "--filter", "label=service=#{config.service}", *stopped_containers_filters),
12
+ "tail -n +#{keep_last + 1}",
13
+ "while read container_id; do docker rm $container_id; done"
11
14
  end
15
+
16
+ private
17
+ def stopped_containers_filters
18
+ [ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] }
19
+ end
12
20
  end
@@ -0,0 +1,20 @@
1
+ class Mrsk::Configuration::Boot
2
+ def initialize(config:)
3
+ @options = config.raw_config.boot || {}
4
+ @host_count = config.all_hosts.count
5
+ end
6
+
7
+ def limit
8
+ limit = @options["limit"]
9
+
10
+ if limit.to_s.end_with?("%")
11
+ @host_count * limit.to_i / 100
12
+ else
13
+ limit
14
+ end
15
+ end
16
+
17
+ def wait
18
+ @options["wait"]
19
+ end
20
+ end
@@ -35,6 +35,21 @@ class Mrsk::Configuration::Role
35
35
  argumentize_env_with_secrets env
36
36
  end
37
37
 
38
+ def health_check_args
39
+ if health_check_cmd.present?
40
+ optionize({ "health-cmd" => health_check_cmd, "health-interval" => "1s" })
41
+ else
42
+ []
43
+ end
44
+ end
45
+
46
+ def health_check_cmd
47
+ options = specializations["healthcheck"] || {}
48
+ options = config.healthcheck.merge(options) if running_traefik?
49
+
50
+ options["cmd"] || http_health_check(port: options["port"], path: options["path"])
51
+ end
52
+
38
53
  def cmd
39
54
  specializations["cmd"]
40
55
  end
@@ -74,9 +89,10 @@ class Mrsk::Configuration::Role
74
89
  def traefik_labels
75
90
  if running_traefik?
76
91
  {
92
+ # Setting a service property ensures that the generated service name will be consistent between versions
93
+ "traefik.http.services.#{traefik_service}.loadbalancer.server.scheme" => "http",
94
+
77
95
  "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
96
  "traefik.http.middlewares.#{traefik_service}-retry.retry.attempts" => "5",
81
97
  "traefik.http.middlewares.#{traefik_service}-retry.retry.initialinterval" => "500ms",
82
98
  "traefik.http.routers.#{traefik_service}.middlewares" => "#{traefik_service}-retry@docker"
@@ -125,4 +141,8 @@ class Mrsk::Configuration::Role
125
141
  new_env["clear"] = (clear_app_env + clear_role_env).uniq
126
142
  end
127
143
  end
144
+
145
+ def http_health_check(port:, path:)
146
+ "curl -f #{URI.join("http://localhost:#{port}", path)} || exit 1" if path.present? || port.present?
147
+ end
128
148
  end
@@ -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("/")
@@ -1,12 +1,52 @@
1
1
  require "sshkit"
2
2
  require "sshkit/dsl"
3
+ require "active_support/core_ext/hash/deep_merge"
4
+ require "json"
3
5
 
4
6
  class SSHKit::Backend::Abstract
5
- def capture_with_info(*args)
6
- capture(*args, verbosity: Logger::INFO)
7
+ def capture_with_info(*args, **kwargs)
8
+ capture(*args, **kwargs, verbosity: Logger::INFO)
9
+ end
10
+
11
+ def capture_with_pretty_json(*args, **kwargs)
12
+ JSON.pretty_generate(JSON.parse(capture(*args, **kwargs)))
7
13
  end
8
14
 
9
15
  def puts_by_host(host, output, type: "App")
10
16
  puts "#{type} Host: #{host}\n#{output}\n\n"
11
17
  end
18
+
19
+ # Our execution pattern is for the CLI execute args lists returned
20
+ # from commands, but this doesn't support returning execution options
21
+ # from the command.
22
+ #
23
+ # Support this by using kwargs for CLI options and merging with the
24
+ # args-extracted options.
25
+ module CommandEnvMerge
26
+ private
27
+
28
+ # Override to merge options returned by commands in the args list with
29
+ # options passed by the CLI and pass them along as kwargs.
30
+ def command(args, options)
31
+ more_options, args = args.partition { |a| a.is_a? Hash }
32
+ more_options << options
33
+
34
+ build_command(args, **more_options.reduce(:deep_merge))
35
+ end
36
+
37
+ # Destructure options to pluck out env for merge
38
+ def build_command(args, env: nil, **options)
39
+ # Rely on native Ruby kwargs precedence rather than explicit Hash merges
40
+ SSHKit::Command.new(*args, **default_command_options, **options, env: env_for(env))
41
+ end
42
+
43
+ def default_command_options
44
+ { in: pwd_path, host: @host, user: @user, group: @group }
45
+ end
46
+
47
+ def env_for(env)
48
+ @env.to_h.merge(env.to_h)
49
+ end
50
+ end
51
+ prepend CommandEnvMerge
12
52
  end
@@ -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
data/lib/mrsk/utils.rb CHANGED
@@ -1,6 +1,8 @@
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
7
  def argumentize(argument, attributes, sensitive: false)
6
8
  Array(attributes).flat_map do |key, value|
@@ -75,7 +77,9 @@ module Mrsk::Utils
75
77
 
76
78
  # Escape a value to make it safe for shell use.
77
79
  def escape_shell_value(value)
78
- value.to_s.dump.gsub(/`/, '\\\\`')
80
+ value.to_s.dump
81
+ .gsub(/`/, '\\\\`')
82
+ .gsub(DOLLAR_SIGN_WITHOUT_SHELL_EXPANSION_REGEX, '\$')
79
83
  end
80
84
 
81
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.11.0"
2
+ VERSION = "0.12.1"
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.11.0
4
+ version: 0.12.1
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-04-17 00:00:00.000000000 Z
11
+ date: 2023-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -201,6 +201,7 @@ files:
201
201
  - lib/mrsk/commands/builder/multiarch/remote.rb
202
202
  - lib/mrsk/commands/builder/native.rb
203
203
  - lib/mrsk/commands/builder/native/remote.rb
204
+ - lib/mrsk/commands/docker.rb
204
205
  - lib/mrsk/commands/healthcheck.rb
205
206
  - lib/mrsk/commands/lock.rb
206
207
  - lib/mrsk/commands/prune.rb
@@ -208,9 +209,11 @@ files:
208
209
  - lib/mrsk/commands/traefik.rb
209
210
  - lib/mrsk/configuration.rb
210
211
  - lib/mrsk/configuration/accessory.rb
212
+ - lib/mrsk/configuration/boot.rb
211
213
  - lib/mrsk/configuration/role.rb
212
214
  - lib/mrsk/sshkit_with_ext.rb
213
215
  - lib/mrsk/utils.rb
216
+ - lib/mrsk/utils/healthcheck_poller.rb
214
217
  - lib/mrsk/utils/sensitive.rb
215
218
  - lib/mrsk/version.rb
216
219
  homepage: https://github.com/rails/mrsk