mrsk 0.11.0 → 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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