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.
- checksums.yaml +4 -4
- data/README.md +130 -18
- data/lib/mrsk/cli/app.rb +58 -16
- data/lib/mrsk/cli/base.rb +39 -18
- data/lib/mrsk/cli/build.rb +23 -1
- data/lib/mrsk/cli/healthcheck.rb +3 -34
- data/lib/mrsk/cli/lock.rb +2 -2
- data/lib/mrsk/cli/main.rb +58 -25
- data/lib/mrsk/cli/prune.rb +2 -2
- data/lib/mrsk/cli/server.rb +13 -9
- data/lib/mrsk/cli/traefik.rb +5 -2
- data/lib/mrsk/cli.rb +1 -0
- data/lib/mrsk/commander.rb +24 -3
- data/lib/mrsk/commands/app.rb +25 -12
- data/lib/mrsk/commands/base.rb +3 -1
- data/lib/mrsk/commands/builder/base.rb +10 -3
- data/lib/mrsk/commands/builder.rb +21 -1
- data/lib/mrsk/commands/docker.rb +21 -0
- data/lib/mrsk/commands/healthcheck.rb +3 -2
- data/lib/mrsk/commands/prune.rb +12 -4
- data/lib/mrsk/commands/registry.rb +1 -1
- data/lib/mrsk/commands/traefik.rb +16 -3
- data/lib/mrsk/configuration/boot.rb +20 -0
- data/lib/mrsk/configuration/role.rb +30 -6
- data/lib/mrsk/configuration.rb +7 -1
- data/lib/mrsk/sshkit_with_ext.rb +2 -2
- data/lib/mrsk/utils/healthcheck_poller.rb +39 -0
- data/lib/mrsk/utils/sensitive.rb +19 -0
- data/lib/mrsk/utils.rb +41 -8
- data/lib/mrsk/version.rb +1 -1
- metadata +21 -3
data/lib/mrsk/cli/main.rb
CHANGED
@@ -17,9 +17,6 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
17
17
|
invoke_options = deploy_options
|
18
18
|
|
19
19
|
runtime = print_runtime do
|
20
|
-
say "Ensure curl and Docker are installed...", :magenta
|
21
|
-
invoke "mrsk:cli:server:bootstrap", [], invoke_options
|
22
|
-
|
23
20
|
say "Log into image registry...", :magenta
|
24
21
|
invoke "mrsk:cli:registry:login", [], invoke_options
|
25
22
|
|
@@ -37,7 +34,12 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
37
34
|
say "Ensure app can pass healthcheck...", :magenta
|
38
35
|
invoke "mrsk:cli:healthcheck:perform", [], invoke_options
|
39
36
|
|
40
|
-
|
37
|
+
say "Detect stale containers...", :magenta
|
38
|
+
invoke "mrsk:cli:app:stale_containers", [], invoke_options
|
39
|
+
|
40
|
+
hold_lock_on_error do
|
41
|
+
invoke "mrsk:cli:app:boot", [], invoke_options
|
42
|
+
end
|
41
43
|
|
42
44
|
say "Prune old containers and images...", :magenta
|
43
45
|
invoke "mrsk:cli:prune:all", [], invoke_options
|
@@ -65,7 +67,12 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
65
67
|
say "Ensure app can pass healthcheck...", :magenta
|
66
68
|
invoke "mrsk:cli:healthcheck:perform", [], invoke_options
|
67
69
|
|
68
|
-
|
70
|
+
say "Detect stale containers...", :magenta
|
71
|
+
invoke "mrsk:cli:app:stale_containers", [], invoke_options
|
72
|
+
|
73
|
+
hold_lock_on_error do
|
74
|
+
invoke "mrsk:cli:app:boot", [], invoke_options
|
75
|
+
end
|
69
76
|
end
|
70
77
|
|
71
78
|
audit_broadcast "Redeployed #{service_version} in #{runtime.round} seconds" unless options[:skip_broadcast]
|
@@ -75,29 +82,41 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
75
82
|
desc "rollback [VERSION]", "Rollback app to VERSION"
|
76
83
|
def rollback(version)
|
77
84
|
with_lock do
|
78
|
-
|
79
|
-
|
80
|
-
if container_name_available?(MRSK.config.service_with_version)
|
81
|
-
say "Start version #{version}, then wait #{MRSK.config.readiness_delay}s for app to boot before stopping the old version...", :magenta
|
85
|
+
invoke_options = deploy_options
|
82
86
|
|
83
|
-
|
87
|
+
hold_lock_on_error do
|
88
|
+
MRSK.config.version = version
|
84
89
|
old_version = nil
|
85
90
|
|
86
|
-
|
87
|
-
|
91
|
+
if container_available?(version)
|
92
|
+
say "Start version #{version}, then wait #{MRSK.config.readiness_delay}s for app to boot before stopping the old version...", :magenta
|
93
|
+
|
94
|
+
on(MRSK.hosts) do
|
95
|
+
execute *MRSK.auditor.record("Tagging #{MRSK.config.absolute_image} as the latest image"), verbosity: :debug
|
96
|
+
execute *MRSK.app.tag_current_as_latest
|
97
|
+
end
|
98
|
+
|
99
|
+
on(MRSK.hosts) do |host|
|
100
|
+
roles = MRSK.roles_on(host)
|
101
|
+
|
102
|
+
roles.each do |role|
|
103
|
+
app = MRSK.app(role: role)
|
104
|
+
old_version = capture_with_info(*app.current_running_version).strip.presence
|
88
105
|
|
89
|
-
|
106
|
+
execute *app.start
|
90
107
|
|
91
|
-
|
92
|
-
|
108
|
+
if old_version
|
109
|
+
sleep MRSK.config.readiness_delay
|
93
110
|
|
94
|
-
|
111
|
+
execute *app.stop(version: old_version), raise_on_non_zero_exit: false
|
112
|
+
end
|
113
|
+
end
|
95
114
|
end
|
96
|
-
end
|
97
115
|
|
98
|
-
|
99
|
-
|
100
|
-
|
116
|
+
audit_broadcast "Rolled back #{service_version(Mrsk::Utils.abbreviate_version(old_version))} to #{service_version}" unless options[:skip_broadcast]
|
117
|
+
else
|
118
|
+
say "The app version '#{version}' is not available as a container (use 'mrsk app containers' for available versions)", :red
|
119
|
+
end
|
101
120
|
end
|
102
121
|
end
|
103
122
|
end
|
@@ -119,7 +138,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
119
138
|
desc "config", "Show combined config (including secrets!)"
|
120
139
|
def config
|
121
140
|
run_locally do
|
122
|
-
puts MRSK.config.to_h.to_yaml
|
141
|
+
puts Mrsk::Utils.redacted(MRSK.config.to_h).to_yaml
|
123
142
|
end
|
124
143
|
end
|
125
144
|
|
@@ -214,10 +233,24 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
214
233
|
subcommand "lock", Mrsk::Cli::Lock
|
215
234
|
|
216
235
|
private
|
217
|
-
def
|
218
|
-
|
219
|
-
|
220
|
-
|
236
|
+
def container_available?(version)
|
237
|
+
begin
|
238
|
+
on(MRSK.hosts) do
|
239
|
+
MRSK.roles_on(host).each do |role|
|
240
|
+
container_id = capture_with_info(*MRSK.app(role: role).container_id_for_version(version))
|
241
|
+
raise "Container not found" unless container_id.present?
|
242
|
+
end
|
243
|
+
end
|
244
|
+
rescue SSHKit::Runner::ExecuteError => e
|
245
|
+
if e.message =~ /Container not found/
|
246
|
+
say "Error looking for container version #{version}: #{e.message}"
|
247
|
+
return false
|
248
|
+
else
|
249
|
+
raise
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
true
|
221
254
|
end
|
222
255
|
|
223
256
|
def deploy_options
|
data/lib/mrsk/cli/prune.rb
CHANGED
@@ -7,7 +7,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
desc "images", "Prune
|
10
|
+
desc "images", "Prune dangling images"
|
11
11
|
def images
|
12
12
|
with_lock do
|
13
13
|
on(MRSK.hosts) do
|
@@ -17,7 +17,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
desc "containers", "Prune stopped containers
|
20
|
+
desc "containers", "Prune all stopped containers, except the last 5"
|
21
21
|
def containers
|
22
22
|
with_lock do
|
23
23
|
on(MRSK.hosts) do
|
data/lib/mrsk/cli/server.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
class Mrsk::Cli::Server < Mrsk::Cli::Base
|
2
|
-
desc "bootstrap", "
|
2
|
+
desc "bootstrap", "Set up Docker to run MRSK apps"
|
3
3
|
def bootstrap
|
4
|
-
|
5
|
-
on(MRSK.hosts + MRSK.accessory_hosts) do
|
6
|
-
dependencies_to_install = Array.new.tap do |dependencies|
|
7
|
-
dependencies << "curl" unless execute "which curl", raise_on_non_zero_exit: false
|
8
|
-
dependencies << "docker.io" unless execute "which docker", raise_on_non_zero_exit: false
|
9
|
-
end
|
4
|
+
missing = []
|
10
5
|
|
11
|
-
|
12
|
-
|
6
|
+
on(MRSK.hosts | MRSK.accessory_hosts) do |host|
|
7
|
+
unless execute(*MRSK.docker.installed?, raise_on_non_zero_exit: false)
|
8
|
+
if execute(*MRSK.docker.superuser?, raise_on_non_zero_exit: false)
|
9
|
+
info "Missing Docker on #{host}. Installing…"
|
10
|
+
execute *MRSK.docker.install
|
11
|
+
else
|
12
|
+
missing << host
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
if missing.any?
|
18
|
+
raise "Docker is not installed on #{missing.join(", ")} and can't be automatically installed without having root access and the `curl` command available. Install Docker manually: https://docs.docker.com/engine/install/"
|
19
|
+
end
|
16
20
|
end
|
17
21
|
end
|
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
|
|
@@ -91,7 +94,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
94
|
-
desc "
|
97
|
+
desc "remove_image", "Remove Traefik image from servers", hide: true
|
95
98
|
def remove_image
|
96
99
|
with_lock do
|
97
100
|
on(MRSK.traefik_hosts) do
|
data/lib/mrsk/cli.rb
CHANGED
data/lib/mrsk/commander.rb
CHANGED
@@ -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, :
|
5
|
+
attr_accessor :verbosity, :holding_lock, :hold_lock_on_error
|
6
6
|
|
7
7
|
def initialize
|
8
8
|
self.verbosity = :info
|
9
|
-
self.
|
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
|
@@ -83,6 +92,10 @@ class Mrsk::Commander
|
|
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)
|
data/lib/mrsk/commands/app.rb
CHANGED
@@ -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) :
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
%(
|
98
|
-
|
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
|
data/lib/mrsk/commands/base.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Mrsk::Commands
|
2
2
|
class Base
|
3
|
-
delegate :
|
3
|
+
delegate :sensitive, :argumentize, to: Mrsk::Utils
|
4
|
+
|
5
|
+
DOCKER_HEALTH_STATUS_FORMAT = "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'"
|
4
6
|
|
5
7
|
attr_accessor :config
|
6
8
|
|
@@ -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 ]
|
@@ -28,7 +31,7 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
28
31
|
end
|
29
32
|
|
30
33
|
def build_args
|
31
|
-
argumentize "--build-arg", args,
|
34
|
+
argumentize "--build-arg", args, sensitive: true
|
32
35
|
end
|
33
36
|
|
34
37
|
def build_secrets
|
@@ -36,7 +39,11 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
36
39
|
end
|
37
40
|
|
38
41
|
def build_dockerfile
|
39
|
-
|
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,15 @@ 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
|
21
|
-
|
21
|
+
def status
|
22
|
+
pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
|
22
23
|
end
|
23
24
|
|
24
25
|
def logs
|
data/lib/mrsk/commands/prune.rb
CHANGED
@@ -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
|
6
|
-
docker :image, :prune, "--all", "--force", "--filter", "label=service=#{config.service}", "--filter", "
|
5
|
+
def images
|
6
|
+
docker :image, :prune, "--all", "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true"
|
7
7
|
end
|
8
8
|
|
9
|
-
def containers(
|
10
|
-
|
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
|
@@ -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
|
@@ -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,18 +89,23 @@ class Mrsk::Configuration::Role
|
|
74
89
|
def traefik_labels
|
75
90
|
if running_traefik?
|
76
91
|
{
|
77
|
-
|
78
|
-
"traefik.http.services.#{
|
79
|
-
|
80
|
-
"traefik.http.
|
81
|
-
"traefik.http.middlewares.#{
|
82
|
-
"traefik.http.
|
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
|
+
|
95
|
+
"traefik.http.routers.#{traefik_service}.rule" => "PathPrefix(`/`)",
|
96
|
+
"traefik.http.middlewares.#{traefik_service}-retry.retry.attempts" => "5",
|
97
|
+
"traefik.http.middlewares.#{traefik_service}-retry.retry.initialinterval" => "500ms",
|
98
|
+
"traefik.http.routers.#{traefik_service}.middlewares" => "#{traefik_service}-retry@docker"
|
83
99
|
}
|
84
100
|
else
|
85
101
|
{}
|
86
102
|
end
|
87
103
|
end
|
88
104
|
|
105
|
+
def traefik_service
|
106
|
+
[ config.service, name, config.destination ].compact.join("-")
|
107
|
+
end
|
108
|
+
|
89
109
|
def custom_labels
|
90
110
|
Hash.new.tap do |labels|
|
91
111
|
labels.merge!(config.labels) if config.labels.present?
|
@@ -121,4 +141,8 @@ class Mrsk::Configuration::Role
|
|
121
141
|
new_env["clear"] = (clear_app_env + clear_role_env).uniq
|
122
142
|
end
|
123
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
|
124
148
|
end
|