mrsk 0.11.0 → 0.13.2
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 +141 -37
- data/bin/mrsk +1 -1
- data/lib/mrsk/cli/accessory.rb +0 -2
- data/lib/mrsk/cli/app.rb +66 -22
- data/lib/mrsk/cli/base.rb +80 -22
- data/lib/mrsk/cli/build.rb +20 -0
- data/lib/mrsk/cli/healthcheck.rb +4 -34
- data/lib/mrsk/cli/lock.rb +5 -5
- data/lib/mrsk/cli/main.rb +62 -53
- data/lib/mrsk/cli/prune.rb +4 -3
- data/lib/mrsk/cli/server.rb +13 -9
- data/lib/mrsk/cli/templates/deploy.yml +0 -4
- data/lib/mrsk/cli/templates/sample_hooks/post-deploy.sample +14 -0
- data/lib/mrsk/cli/templates/sample_hooks/pre-build.sample +51 -0
- data/lib/mrsk/cli/templates/sample_hooks/pre-connect.sample +47 -0
- data/lib/mrsk/cli/templates/sample_hooks/pre-deploy.sample +82 -0
- data/lib/mrsk/cli/traefik.rb +1 -1
- data/lib/mrsk/cli.rb +1 -0
- data/lib/mrsk/commander.rb +34 -9
- data/lib/mrsk/commands/app.rb +37 -15
- data/lib/mrsk/commands/auditor.rb +7 -36
- data/lib/mrsk/commands/base.rb +9 -2
- data/lib/mrsk/commands/builder/base.rb +9 -2
- data/lib/mrsk/commands/builder.rb +21 -1
- data/lib/mrsk/commands/docker.rb +21 -0
- data/lib/mrsk/commands/healthcheck.rb +7 -2
- data/lib/mrsk/commands/hook.rb +14 -0
- data/lib/mrsk/commands/lock.rb +2 -2
- data/lib/mrsk/commands/prune.rb +30 -4
- data/lib/mrsk/commands/traefik.rb +12 -1
- data/lib/mrsk/configuration/boot.rb +20 -0
- data/lib/mrsk/configuration/role.rb +29 -2
- data/lib/mrsk/configuration.rb +15 -9
- data/lib/mrsk/sshkit_with_ext.rb +46 -2
- data/lib/mrsk/tags.rb +39 -0
- data/lib/mrsk/utils/healthcheck_poller.rb +39 -0
- data/lib/mrsk/utils.rb +13 -2
- data/lib/mrsk/version.rb +1 -1
- metadata +11 -2
data/lib/mrsk/cli/healthcheck.rb
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
|
|
2
|
-
|
|
3
|
-
class HealthcheckError < StandardError; end
|
|
4
|
-
|
|
5
2
|
default_command :perform
|
|
6
3
|
|
|
7
4
|
desc "perform", "Health check current app version"
|
|
@@ -9,38 +6,11 @@ class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
|
|
|
9
6
|
on(MRSK.primary_host) do
|
|
10
7
|
begin
|
|
11
8
|
execute *MRSK.healthcheck.run
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
attempt = 1
|
|
15
|
-
max_attempts = MRSK.config.healthcheck["max_attempts"]
|
|
16
|
-
|
|
17
|
-
begin
|
|
18
|
-
status = capture_with_info(*MRSK.healthcheck.curl)
|
|
19
|
-
|
|
20
|
-
if status == "200"
|
|
21
|
-
info "#{target} succeeded with 200 OK!"
|
|
22
|
-
else
|
|
23
|
-
raise HealthcheckError, "#{target} failed with status #{status}"
|
|
24
|
-
end
|
|
25
|
-
rescue SSHKit::Command::Failed
|
|
26
|
-
if attempt <= max_attempts
|
|
27
|
-
info "#{target} failed to respond, retrying in #{attempt}s (attempt #{attempt}/#{max_attempts})..."
|
|
28
|
-
sleep attempt
|
|
29
|
-
attempt += 1
|
|
30
|
-
|
|
31
|
-
retry
|
|
32
|
-
else
|
|
33
|
-
raise
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
rescue SSHKit::Command::Failed, HealthcheckError => e
|
|
9
|
+
Mrsk::Utils::HealthcheckPoller.wait_for_healthy { capture_with_info(*MRSK.healthcheck.status) }
|
|
10
|
+
rescue Mrsk::Utils::HealthcheckPoller::HealthcheckError => e
|
|
37
11
|
error capture_with_info(*MRSK.healthcheck.logs)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
raise SSHKit::Command::Failed, "#{target} failed to return 200 OK!"
|
|
41
|
-
else
|
|
42
|
-
raise
|
|
43
|
-
end
|
|
12
|
+
error capture_with_pretty_json(*MRSK.healthcheck.container_health_log)
|
|
13
|
+
raise
|
|
44
14
|
ensure
|
|
45
15
|
execute *MRSK.healthcheck.stop, raise_on_non_zero_exit: false
|
|
46
16
|
execute *MRSK.healthcheck.remove, raise_on_non_zero_exit: false
|
data/lib/mrsk/cli/lock.rb
CHANGED
|
@@ -2,16 +2,16 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
|
|
|
2
2
|
desc "status", "Report lock status"
|
|
3
3
|
def status
|
|
4
4
|
handle_missing_lock do
|
|
5
|
-
on(MRSK.primary_host) { puts
|
|
5
|
+
on(MRSK.primary_host) { puts capture_with_debug(*MRSK.lock.status) }
|
|
6
6
|
end
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
desc "acquire", "Acquire the deploy lock"
|
|
10
|
-
option :message, aliases: "-m", type: :string, desc: "A lock
|
|
10
|
+
option :message, aliases: "-m", type: :string, desc: "A lock message", required: true
|
|
11
11
|
def acquire
|
|
12
12
|
message = options[:message]
|
|
13
|
-
|
|
14
|
-
on(MRSK.primary_host) { execute *MRSK.lock.acquire(message, MRSK.config.version) }
|
|
13
|
+
raise_if_locked do
|
|
14
|
+
on(MRSK.primary_host) { execute *MRSK.lock.acquire(message, MRSK.config.version), verbosity: :debug }
|
|
15
15
|
say "Acquired the deploy lock"
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -19,7 +19,7 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
|
|
|
19
19
|
desc "release", "Release the deploy lock"
|
|
20
20
|
def release
|
|
21
21
|
handle_missing_lock do
|
|
22
|
-
on(MRSK.primary_host) { execute *MRSK.lock.release }
|
|
22
|
+
on(MRSK.primary_host) { execute *MRSK.lock.release, verbosity: :debug }
|
|
23
23
|
say "Released the deploy lock"
|
|
24
24
|
end
|
|
25
25
|
end
|
data/lib/mrsk/cli/main.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
2
2
|
desc "setup", "Setup all accessories and deploy app to servers"
|
|
3
3
|
def setup
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
print_runtime do
|
|
5
|
+
with_lock do
|
|
6
6
|
invoke "mrsk:cli:server:bootstrap"
|
|
7
7
|
invoke "mrsk:cli:accessory:boot", [ "all" ]
|
|
8
8
|
deploy
|
|
@@ -13,12 +13,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
|
13
13
|
desc "deploy", "Deploy app to servers"
|
|
14
14
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
|
15
15
|
def deploy
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
runtime = print_runtime do
|
|
20
|
-
say "Ensure curl and Docker are installed...", :magenta
|
|
21
|
-
invoke "mrsk:cli:server:bootstrap", [], invoke_options
|
|
16
|
+
runtime = print_runtime do
|
|
17
|
+
with_lock do
|
|
18
|
+
invoke_options = deploy_options
|
|
22
19
|
|
|
23
20
|
say "Log into image registry...", :magenta
|
|
24
21
|
invoke "mrsk:cli:registry:login", [], invoke_options
|
|
@@ -31,29 +28,34 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
|
31
28
|
invoke "mrsk:cli:build:deliver", [], invoke_options
|
|
32
29
|
end
|
|
33
30
|
|
|
31
|
+
run_hook "pre-deploy"
|
|
32
|
+
|
|
34
33
|
say "Ensure Traefik is running...", :magenta
|
|
35
34
|
invoke "mrsk:cli:traefik:boot", [], invoke_options
|
|
36
35
|
|
|
37
36
|
say "Ensure app can pass healthcheck...", :magenta
|
|
38
37
|
invoke "mrsk:cli:healthcheck:perform", [], invoke_options
|
|
39
38
|
|
|
39
|
+
say "Detect stale containers...", :magenta
|
|
40
|
+
invoke "mrsk:cli:app:stale_containers", [], invoke_options
|
|
41
|
+
|
|
40
42
|
invoke "mrsk:cli:app:boot", [], invoke_options
|
|
41
43
|
|
|
42
44
|
say "Prune old containers and images...", :magenta
|
|
43
45
|
invoke "mrsk:cli:prune:all", [], invoke_options
|
|
44
46
|
end
|
|
45
|
-
|
|
46
|
-
audit_broadcast "Deployed #{service_version} in #{runtime.round} seconds" unless options[:skip_broadcast]
|
|
47
47
|
end
|
|
48
|
+
|
|
49
|
+
run_hook "post-deploy", runtime: runtime.round
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting Traefik, pruning, and registry login"
|
|
51
53
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
|
52
54
|
def redeploy
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
runtime = print_runtime do
|
|
56
|
+
with_lock do
|
|
57
|
+
invoke_options = deploy_options
|
|
55
58
|
|
|
56
|
-
runtime = print_runtime do
|
|
57
59
|
if options[:skip_push]
|
|
58
60
|
say "Pull app image...", :magenta
|
|
59
61
|
invoke "mrsk:cli:build:pull", [], invoke_options
|
|
@@ -62,49 +64,43 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
|
62
64
|
invoke "mrsk:cli:build:deliver", [], invoke_options
|
|
63
65
|
end
|
|
64
66
|
|
|
67
|
+
run_hook "pre-deploy"
|
|
68
|
+
|
|
65
69
|
say "Ensure app can pass healthcheck...", :magenta
|
|
66
70
|
invoke "mrsk:cli:healthcheck:perform", [], invoke_options
|
|
67
71
|
|
|
72
|
+
say "Detect stale containers...", :magenta
|
|
73
|
+
invoke "mrsk:cli:app:stale_containers", [], invoke_options
|
|
74
|
+
|
|
68
75
|
invoke "mrsk:cli:app:boot", [], invoke_options
|
|
69
76
|
end
|
|
70
|
-
|
|
71
|
-
audit_broadcast "Redeployed #{service_version} in #{runtime.round} seconds" unless options[:skip_broadcast]
|
|
72
77
|
end
|
|
78
|
+
|
|
79
|
+
run_hook "post-deploy", runtime: runtime.round
|
|
73
80
|
end
|
|
74
81
|
|
|
75
82
|
desc "rollback [VERSION]", "Rollback app to VERSION"
|
|
76
83
|
def rollback(version)
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
rolled_back = false
|
|
85
|
+
runtime = print_runtime do
|
|
86
|
+
with_lock do
|
|
87
|
+
invoke_options = deploy_options
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
say "Start version #{version}, then wait #{MRSK.config.readiness_delay}s for app to boot before stopping the old version...", :magenta
|
|
82
|
-
|
|
83
|
-
cli = self
|
|
89
|
+
MRSK.config.version = version
|
|
84
90
|
old_version = nil
|
|
85
91
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
roles.each do |role|
|
|
90
|
-
app = MRSK.app(role: role)
|
|
91
|
-
old_version = capture_with_info(*app.current_running_version).strip.presence
|
|
92
|
+
if container_available?(version)
|
|
93
|
+
run_hook "pre-deploy"
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
execute *app.stop(version: old_version), raise_on_non_zero_exit: false
|
|
99
|
-
end
|
|
100
|
-
end
|
|
95
|
+
invoke "mrsk:cli:app:boot", [], invoke_options.merge(version: version)
|
|
96
|
+
rolled_back = true
|
|
97
|
+
else
|
|
98
|
+
say "The app version '#{version}' is not available as a container (use 'mrsk app containers' for available versions)", :red
|
|
101
99
|
end
|
|
102
|
-
|
|
103
|
-
audit_broadcast "Rolled back #{service_version(Mrsk::Utils.abbreviate_version(old_version))} to #{service_version}" unless options[:skip_broadcast]
|
|
104
|
-
else
|
|
105
|
-
say "The app version '#{version}' is not available as a container (use 'mrsk app containers' for available versions)", :red
|
|
106
100
|
end
|
|
107
101
|
end
|
|
102
|
+
|
|
103
|
+
run_hook "post-deploy", runtime: runtime.round if rolled_back
|
|
108
104
|
end
|
|
109
105
|
|
|
110
106
|
desc "details", "Show details about all containers"
|
|
@@ -146,6 +142,14 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
|
146
142
|
puts "Created .env file"
|
|
147
143
|
end
|
|
148
144
|
|
|
145
|
+
unless (hooks_dir = Pathname.new(File.expand_path(".mrsk/hooks"))).exist?
|
|
146
|
+
hooks_dir.mkpath
|
|
147
|
+
Pathname.new(File.expand_path("templates/sample_hooks", __dir__)).each_child do |sample_hook|
|
|
148
|
+
FileUtils.cp sample_hook, hooks_dir, preserve: true
|
|
149
|
+
end
|
|
150
|
+
puts "Created sample hooks in .mrsk/hooks"
|
|
151
|
+
end
|
|
152
|
+
|
|
149
153
|
if options[:bundle]
|
|
150
154
|
if (binstub = Pathname.new(File.expand_path("bin/mrsk"))).exist?
|
|
151
155
|
puts "Binstub already exists in bin/mrsk (remove first to create a new one)"
|
|
@@ -203,6 +207,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
|
203
207
|
desc "healthcheck", "Healthcheck application"
|
|
204
208
|
subcommand "healthcheck", Mrsk::Cli::Healthcheck
|
|
205
209
|
|
|
210
|
+
desc "lock", "Manage the deploy lock"
|
|
211
|
+
subcommand "lock", Mrsk::Cli::Lock
|
|
212
|
+
|
|
206
213
|
desc "prune", "Prune old application images and containers"
|
|
207
214
|
subcommand "prune", Mrsk::Cli::Prune
|
|
208
215
|
|
|
@@ -215,26 +222,28 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
|
215
222
|
desc "traefik", "Manage Traefik load balancer"
|
|
216
223
|
subcommand "traefik", Mrsk::Cli::Traefik
|
|
217
224
|
|
|
218
|
-
desc "lock", "Manage the deploy lock"
|
|
219
|
-
subcommand "lock", Mrsk::Cli::Lock
|
|
220
|
-
|
|
221
225
|
private
|
|
222
|
-
def container_available?(version
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
def container_available?(version)
|
|
227
|
+
begin
|
|
228
|
+
on(MRSK.hosts) do
|
|
229
|
+
MRSK.roles_on(host).each do |role|
|
|
230
|
+
container_id = capture_with_info(*MRSK.app(role: role).container_id_for_version(version))
|
|
231
|
+
raise "Container not found" unless container_id.present?
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
rescue SSHKit::Runner::ExecuteError => e
|
|
235
|
+
if e.message =~ /Container not found/
|
|
236
|
+
say "Error looking for container version #{version}: #{e.message}"
|
|
237
|
+
return false
|
|
238
|
+
else
|
|
239
|
+
raise
|
|
240
|
+
end
|
|
228
241
|
end
|
|
229
242
|
|
|
230
|
-
|
|
243
|
+
true
|
|
231
244
|
end
|
|
232
245
|
|
|
233
246
|
def deploy_options
|
|
234
247
|
{ "version" => MRSK.config.version }.merge(options.without("skip_push"))
|
|
235
248
|
end
|
|
236
|
-
|
|
237
|
-
def service_version(version = MRSK.config.abbreviated_version)
|
|
238
|
-
[ MRSK.config.service, version ].compact.join("@")
|
|
239
|
-
end
|
|
240
249
|
end
|
data/lib/mrsk/cli/prune.rb
CHANGED
|
@@ -7,17 +7,18 @@ 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
|
|
14
14
|
execute *MRSK.auditor.record("Pruned images"), verbosity: :debug
|
|
15
|
-
execute *MRSK.prune.
|
|
15
|
+
execute *MRSK.prune.dangling_images
|
|
16
|
+
execute *MRSK.prune.tagged_images
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
desc "containers", "Prune stopped containers
|
|
21
|
+
desc "containers", "Prune all stopped containers, except the last 5"
|
|
21
22
|
def containers
|
|
22
23
|
with_lock do
|
|
23
24
|
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
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# A sample post-deploy hook
|
|
4
|
+
#
|
|
5
|
+
# These environment variables are available:
|
|
6
|
+
# MRSK_RECORDED_AT
|
|
7
|
+
# MRSK_PERFORMER
|
|
8
|
+
# MRSK_VERSION
|
|
9
|
+
# MRSK_HOSTS
|
|
10
|
+
# MRSK_ROLE (if set)
|
|
11
|
+
# MRSK_DESTINATION (if set)
|
|
12
|
+
# MRSK_RUNTIME
|
|
13
|
+
|
|
14
|
+
echo "$MRSK_PERFORMER deployed $MRSK_VERSION to $MRSK_DESTINATION in $MRSK_RUNTIME seconds"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# A sample pre-build hook
|
|
4
|
+
#
|
|
5
|
+
# Checks:
|
|
6
|
+
# 1. We have a clean checkout
|
|
7
|
+
# 2. A remote is configured
|
|
8
|
+
# 3. The branch has been pushed to the remote
|
|
9
|
+
# 4. The version we are deploying matches the remote
|
|
10
|
+
#
|
|
11
|
+
# These environment variables are available:
|
|
12
|
+
# MRSK_RECORDED_AT
|
|
13
|
+
# MRSK_PERFORMER
|
|
14
|
+
# MRSK_VERSION
|
|
15
|
+
# MRSK_HOSTS
|
|
16
|
+
# MRSK_ROLE (if set)
|
|
17
|
+
# MRSK_DESTINATION (if set)
|
|
18
|
+
|
|
19
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
20
|
+
echo "Git checkout is not clean, aborting..." >&2
|
|
21
|
+
git status --porcelain >&2
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
first_remote=$(git remote)
|
|
26
|
+
|
|
27
|
+
if [ -z "$first_remote" ]; then
|
|
28
|
+
echo "No git remote set, aborting..." >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
current_branch=$(git branch --show-current)
|
|
33
|
+
|
|
34
|
+
if [ -z "$current_branch" ]; then
|
|
35
|
+
echo "No git remote set, aborting..." >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
|
|
40
|
+
|
|
41
|
+
if [ -z "$remote_head" ]; then
|
|
42
|
+
echo "Branch not pushed to remote, aborting..." >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [ "$MRSK_VERSION" != "$remote_head" ]; then
|
|
47
|
+
echo "Version ($MRSK_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# A sample pre-connect check
|
|
4
|
+
#
|
|
5
|
+
# Warms DNS before connecting to hosts in parallel
|
|
6
|
+
#
|
|
7
|
+
# These environment variables are available:
|
|
8
|
+
# MRSK_RECORDED_AT
|
|
9
|
+
# MRSK_PERFORMER
|
|
10
|
+
# MRSK_VERSION
|
|
11
|
+
# MRSK_HOSTS
|
|
12
|
+
# MRSK_ROLE (if set)
|
|
13
|
+
# MRSK_DESTINATION (if set)
|
|
14
|
+
# MRSK_RUNTIME
|
|
15
|
+
|
|
16
|
+
hosts = ENV["MRSK_HOSTS"].split(",")
|
|
17
|
+
results = nil
|
|
18
|
+
max = 3
|
|
19
|
+
|
|
20
|
+
elapsed = Benchmark.realtime do
|
|
21
|
+
results = hosts.map do |host|
|
|
22
|
+
Thread.new do
|
|
23
|
+
tries = 1
|
|
24
|
+
|
|
25
|
+
begin
|
|
26
|
+
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
|
|
27
|
+
rescue SocketError
|
|
28
|
+
if tries < max
|
|
29
|
+
puts "Retrying DNS warmup: #{host}"
|
|
30
|
+
tries += 1
|
|
31
|
+
sleep rand
|
|
32
|
+
retry
|
|
33
|
+
else
|
|
34
|
+
puts "DNS warmup failed: #{host}"
|
|
35
|
+
host
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
tries
|
|
40
|
+
end
|
|
41
|
+
end.map(&:value)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
retries = results.sum - hosts.size
|
|
45
|
+
nopes = results.count { |r| r == max }
|
|
46
|
+
|
|
47
|
+
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# A sample pre-deploy hook
|
|
4
|
+
#
|
|
5
|
+
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
|
|
6
|
+
#
|
|
7
|
+
# Fails unless the combined status is "success"
|
|
8
|
+
#
|
|
9
|
+
# These environment variables are available:
|
|
10
|
+
# MRSK_RECORDED_AT
|
|
11
|
+
# MRSK_PERFORMER
|
|
12
|
+
# MRSK_VERSION
|
|
13
|
+
# MRSK_HOSTS
|
|
14
|
+
# MRSK_COMMAND
|
|
15
|
+
# MRSK_SUBCOMMAND
|
|
16
|
+
# MRSK_ROLE (if set)
|
|
17
|
+
# MRSK_DESTINATION (if set)
|
|
18
|
+
|
|
19
|
+
#!/usr/bin/env ruby
|
|
20
|
+
|
|
21
|
+
# Only check the build status for production deployments
|
|
22
|
+
if ENV["MRSK_COMMAND"] == "rollback" || ENV["MRSK_DESTINATION"] != "production"
|
|
23
|
+
exit 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "bundler/inline"
|
|
27
|
+
|
|
28
|
+
# true = install gems so this is fast on repeat invocations
|
|
29
|
+
gemfile(true, quiet: true) do
|
|
30
|
+
source "https://rubygems.org"
|
|
31
|
+
|
|
32
|
+
gem "octokit"
|
|
33
|
+
gem "faraday-retry"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
MAX_ATTEMPTS = 72
|
|
37
|
+
ATTEMPTS_GAP = 10
|
|
38
|
+
|
|
39
|
+
def exit_with_error(message)
|
|
40
|
+
$stderr.puts message
|
|
41
|
+
exit 1
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def first_status_url(combined_status, state)
|
|
45
|
+
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
|
46
|
+
first_status && first_status[:target_url]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
|
50
|
+
git_sha = `git rev-parse HEAD`.strip
|
|
51
|
+
|
|
52
|
+
repository = Octokit::Repository.from_url(remote_url)
|
|
53
|
+
github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
|
54
|
+
attempts = 0
|
|
55
|
+
|
|
56
|
+
begin
|
|
57
|
+
loop do
|
|
58
|
+
combined_status = github_client.combined_status(remote_url, git_sha)
|
|
59
|
+
state = combined_status[:state]
|
|
60
|
+
first_status_url = first_status_url(combined_status, state)
|
|
61
|
+
|
|
62
|
+
case state
|
|
63
|
+
when "success"
|
|
64
|
+
puts "Build passed, see #{first_status_url}"
|
|
65
|
+
exit 0
|
|
66
|
+
when "failure"
|
|
67
|
+
exit_with_error "Build failed, see #{first_status_url}"
|
|
68
|
+
when "pending"
|
|
69
|
+
attempts += 1
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
puts "Waiting #{ATTEMPTS_GAP} more seconds for build to complete#{", see #{first_status_url}" if first_status_url}..."
|
|
73
|
+
|
|
74
|
+
if attempts == MAX_ATTEMPTS
|
|
75
|
+
exit_with_error "Build status is still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
sleep(ATTEMPTS_GAP)
|
|
79
|
+
end
|
|
80
|
+
rescue Octokit::NotFound
|
|
81
|
+
exit_with_error "Build status could not be found"
|
|
82
|
+
end
|
data/lib/mrsk/cli/traefik.rb
CHANGED
|
@@ -94,7 +94,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
desc "
|
|
97
|
+
desc "remove_image", "Remove Traefik image from servers", hide: true
|
|
98
98
|
def remove_image
|
|
99
99
|
with_lock do
|
|
100
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
|
|
@@ -75,18 +84,30 @@ class Mrsk::Commander
|
|
|
75
84
|
Mrsk::Commands::Accessory.new(config, name: name)
|
|
76
85
|
end
|
|
77
86
|
|
|
78
|
-
def auditor(
|
|
79
|
-
Mrsk::Commands::Auditor.new(config,
|
|
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
|
|
89
102
|
|
|
103
|
+
def hook
|
|
104
|
+
@hook ||= Mrsk::Commands::Hook.new(config)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def lock
|
|
108
|
+
@lock ||= Mrsk::Commands::Lock.new(config)
|
|
109
|
+
end
|
|
110
|
+
|
|
90
111
|
def prune
|
|
91
112
|
@prune ||= Mrsk::Commands::Prune.new(config)
|
|
92
113
|
end
|
|
@@ -99,10 +120,6 @@ class Mrsk::Commander
|
|
|
99
120
|
@traefik ||= Mrsk::Commands::Traefik.new(config)
|
|
100
121
|
end
|
|
101
122
|
|
|
102
|
-
def lock
|
|
103
|
-
@lock ||= Mrsk::Commands::Lock.new(config)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
123
|
def with_verbosity(level)
|
|
107
124
|
old_level = self.verbosity
|
|
108
125
|
|
|
@@ -115,6 +132,14 @@ class Mrsk::Commander
|
|
|
115
132
|
SSHKit.config.output_verbosity = old_level
|
|
116
133
|
end
|
|
117
134
|
|
|
135
|
+
def holding_lock?
|
|
136
|
+
self.holding_lock
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def hold_lock_on_error?
|
|
140
|
+
self.hold_lock_on_error
|
|
141
|
+
end
|
|
142
|
+
|
|
118
143
|
private
|
|
119
144
|
# Lazy setup of SSHKit
|
|
120
145
|
def configure_sshkit_with(config)
|