mrsk 0.9.1 → 0.10.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 +158 -13
- data/lib/mrsk/cli/accessory.rb +87 -64
- data/lib/mrsk/cli/app.rb +103 -65
- data/lib/mrsk/cli/base.rb +46 -9
- data/lib/mrsk/cli/build.rb +40 -30
- data/lib/mrsk/cli/lock.rb +37 -0
- data/lib/mrsk/cli/main.rb +91 -49
- data/lib/mrsk/cli/prune.rb +14 -8
- data/lib/mrsk/cli/server.rb +9 -7
- data/lib/mrsk/cli/templates/deploy.yml +2 -0
- data/lib/mrsk/cli/traefik.rb +37 -21
- data/lib/mrsk/commander.rb +40 -34
- data/lib/mrsk/commands/accessory.rb +9 -6
- data/lib/mrsk/commands/app.rb +39 -37
- data/lib/mrsk/commands/auditor.rb +20 -5
- data/lib/mrsk/commands/base.rb +6 -4
- data/lib/mrsk/commands/builder/base.rb +1 -0
- data/lib/mrsk/commands/builder/native.rb +2 -1
- data/lib/mrsk/commands/healthcheck.rb +5 -3
- data/lib/mrsk/commands/lock.rb +63 -0
- data/lib/mrsk/commands/traefik.rb +13 -7
- data/lib/mrsk/configuration/accessory.rb +53 -7
- data/lib/mrsk/configuration/role.rb +13 -4
- data/lib/mrsk/configuration.rb +59 -17
- data/lib/mrsk/utils.rb +18 -2
- data/lib/mrsk/version.rb +1 -1
- data/lib/mrsk.rb +1 -0
- metadata +47 -3
data/lib/mrsk/cli/app.rb
CHANGED
@@ -1,32 +1,31 @@
|
|
1
1
|
class Mrsk::Cli::App < Mrsk::Cli::Base
|
2
2
|
desc "boot", "Boot app on servers (or reboot app if already running)"
|
3
3
|
def boot
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
with_lock do
|
5
|
+
say "Get most recent version available as an image...", :magenta unless options[:version]
|
6
|
+
using_version(version_or_latest) do |version|
|
7
|
+
say "Start container with version #{version} using a #{MRSK.config.readiness_delay}s readiness delay (or reboot if already running)...", :magenta
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
MRSK.config.roles.each do |role|
|
11
|
-
on(role.hosts) do |host|
|
12
|
-
execute *MRSK.auditor.record("Booted app version #{version}"), verbosity: :debug
|
9
|
+
cli = self
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
execute *MRSK.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
11
|
+
on(MRSK.hosts) do |host|
|
12
|
+
roles = MRSK.roles_on(host)
|
13
|
+
|
14
|
+
roles.each do |role|
|
15
|
+
execute *MRSK.auditor(role: role).record("Booted app version #{version}"), verbosity: :debug
|
16
|
+
|
17
|
+
begin
|
18
|
+
if capture_with_info(*MRSK.app(role: role).container_id_for_version(version)).present?
|
19
|
+
tmp_version = "#{version}_#{SecureRandom.hex(8)}"
|
20
|
+
info "Renaming container #{version} to #{tmp_version} as already deployed on #{host}"
|
21
|
+
execute *MRSK.auditor(role: role).record("Renaming container #{version} to #{tmp_version}"), verbosity: :debug
|
22
|
+
execute *MRSK.app(role: role).rename_container(version: version, new_version: tmp_version)
|
23
|
+
end
|
24
|
+
|
25
|
+
old_version = capture_with_info(*MRSK.app(role: role).current_running_version).strip
|
26
|
+
execute *MRSK.app(role: role).run
|
27
|
+
sleep MRSK.config.readiness_delay
|
28
|
+
execute *MRSK.app(role: role).stop(version: old_version), raise_on_non_zero_exit: false if old_version.present?
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|
@@ -36,24 +35,42 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
36
35
|
|
37
36
|
desc "start", "Start existing app container on servers"
|
38
37
|
def start
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
with_lock do
|
39
|
+
on(MRSK.hosts) do |host|
|
40
|
+
roles = MRSK.roles_on(host)
|
41
|
+
|
42
|
+
roles.each do |role|
|
43
|
+
execute *MRSK.auditor.record("Started app version #{MRSK.config.version}"), verbosity: :debug
|
44
|
+
execute *MRSK.app(role: role).start, raise_on_non_zero_exit: false
|
45
|
+
end
|
46
|
+
end
|
42
47
|
end
|
43
48
|
end
|
44
49
|
|
45
50
|
desc "stop", "Stop app container on servers"
|
46
51
|
def stop
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
with_lock do
|
53
|
+
on(MRSK.hosts) do |host|
|
54
|
+
roles = MRSK.roles_on(host)
|
55
|
+
|
56
|
+
roles.each do |role|
|
57
|
+
execute *MRSK.auditor(role: role).record("Stopped app"), verbosity: :debug
|
58
|
+
execute *MRSK.app(role: role).stop, raise_on_non_zero_exit: false
|
59
|
+
end
|
60
|
+
end
|
50
61
|
end
|
51
62
|
end
|
52
63
|
|
53
64
|
# FIXME: Drop in favor of just containers?
|
54
65
|
desc "details", "Show details about app containers"
|
55
66
|
def details
|
56
|
-
on(MRSK.hosts)
|
67
|
+
on(MRSK.hosts) do |host|
|
68
|
+
roles = MRSK.roles_on(host)
|
69
|
+
|
70
|
+
roles.each do |role|
|
71
|
+
puts_by_host host, capture_with_info(*MRSK.app(role: role).info)
|
72
|
+
end
|
73
|
+
end
|
57
74
|
end
|
58
75
|
|
59
76
|
desc "exec [CMD]", "Execute a custom command on servers (use --help to show options)"
|
@@ -65,12 +82,12 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
65
82
|
say "Get current version of running container...", :magenta unless options[:version]
|
66
83
|
using_version(options[:version] || current_running_version) do |version|
|
67
84
|
say "Launching interactive command with version #{version} via SSH from existing container on #{MRSK.primary_host}...", :magenta
|
68
|
-
run_locally { exec MRSK.app.execute_in_existing_container_over_ssh(cmd, host: MRSK.primary_host) }
|
85
|
+
run_locally { exec MRSK.app(role: "web").execute_in_existing_container_over_ssh(cmd, host: MRSK.primary_host) }
|
69
86
|
end
|
70
87
|
|
71
88
|
when options[:interactive]
|
72
89
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
73
|
-
using_version(
|
90
|
+
using_version(version_or_latest) do |version|
|
74
91
|
say "Launching interactive command with version #{version} via SSH from new container on #{MRSK.primary_host}...", :magenta
|
75
92
|
run_locally { exec MRSK.app.execute_in_new_container_over_ssh(cmd, host: MRSK.primary_host) }
|
76
93
|
end
|
@@ -81,14 +98,18 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
81
98
|
say "Launching command with version #{version} from existing container...", :magenta
|
82
99
|
|
83
100
|
on(MRSK.hosts) do |host|
|
84
|
-
|
85
|
-
|
101
|
+
roles = MRSK.roles_on(host)
|
102
|
+
|
103
|
+
roles.each do |role|
|
104
|
+
execute *MRSK.auditor(role: role).record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
105
|
+
puts_by_host host, capture_with_info(*MRSK.app(role: role).execute_in_existing_container(cmd))
|
106
|
+
end
|
86
107
|
end
|
87
108
|
end
|
88
109
|
|
89
110
|
else
|
90
111
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
91
|
-
using_version(
|
112
|
+
using_version(version_or_latest) do |version|
|
92
113
|
say "Launching command with version #{version} from new container...", :magenta
|
93
114
|
on(MRSK.hosts) do |host|
|
94
115
|
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
@@ -121,18 +142,26 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
121
142
|
if options[:follow]
|
122
143
|
run_locally do
|
123
144
|
info "Following logs on #{MRSK.primary_host}..."
|
124
|
-
|
125
|
-
|
145
|
+
|
146
|
+
MRSK.specific_roles ||= ["web"]
|
147
|
+
role = MRSK.roles_on(MRSK.primary_host).first
|
148
|
+
|
149
|
+
info MRSK.app(role: role).follow_logs(host: MRSK.primary_host, grep: grep)
|
150
|
+
exec MRSK.app(role: role).follow_logs(host: MRSK.primary_host, grep: grep)
|
126
151
|
end
|
127
152
|
else
|
128
153
|
since = options[:since]
|
129
154
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
130
155
|
|
131
156
|
on(MRSK.hosts) do |host|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
157
|
+
roles = MRSK.roles_on(host)
|
158
|
+
|
159
|
+
roles.each do |role|
|
160
|
+
begin
|
161
|
+
puts_by_host host, capture_with_info(*MRSK.app(role: role).logs(since: since, lines: lines, grep: grep))
|
162
|
+
rescue SSHKit::Command::Failed
|
163
|
+
puts_by_host host, "Nothing found"
|
164
|
+
end
|
136
165
|
end
|
137
166
|
end
|
138
167
|
end
|
@@ -140,32 +169,48 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
140
169
|
|
141
170
|
desc "remove", "Remove app containers and images from servers"
|
142
171
|
def remove
|
143
|
-
|
144
|
-
|
145
|
-
|
172
|
+
with_lock do
|
173
|
+
stop
|
174
|
+
remove_containers
|
175
|
+
remove_images
|
176
|
+
end
|
146
177
|
end
|
147
178
|
|
148
179
|
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
149
180
|
def remove_container(version)
|
150
|
-
|
151
|
-
|
152
|
-
|
181
|
+
with_lock do
|
182
|
+
on(MRSK.hosts) do |host|
|
183
|
+
roles = MRSK.roles_on(host)
|
184
|
+
|
185
|
+
roles.each do |role|
|
186
|
+
execute *MRSK.auditor(role: role).record("Removed app container with version #{version}"), verbosity: :debug
|
187
|
+
execute *MRSK.app(role: role).remove_container(version: version)
|
188
|
+
end
|
189
|
+
end
|
153
190
|
end
|
154
191
|
end
|
155
192
|
|
156
193
|
desc "remove_containers", "Remove all app containers from servers", hide: true
|
157
194
|
def remove_containers
|
158
|
-
|
159
|
-
|
160
|
-
|
195
|
+
with_lock do
|
196
|
+
on(MRSK.hosts) do |host|
|
197
|
+
roles = MRSK.roles_on(host)
|
198
|
+
|
199
|
+
roles.each do |role|
|
200
|
+
execute *MRSK.auditor(role: role).record("Removed all app containers"), verbosity: :debug
|
201
|
+
execute *MRSK.app(role: role).remove_containers
|
202
|
+
end
|
203
|
+
end
|
161
204
|
end
|
162
205
|
end
|
163
206
|
|
164
207
|
desc "remove_images", "Remove all app images from servers", hide: true
|
165
208
|
def remove_images
|
166
|
-
|
167
|
-
|
168
|
-
|
209
|
+
with_lock do
|
210
|
+
on(MRSK.hosts) do
|
211
|
+
execute *MRSK.auditor.record("Removed all app images"), verbosity: :debug
|
212
|
+
execute *MRSK.app.remove_images
|
213
|
+
end
|
169
214
|
end
|
170
215
|
end
|
171
216
|
|
@@ -189,20 +234,13 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
189
234
|
end
|
190
235
|
end
|
191
236
|
|
192
|
-
def most_recent_version_available(host: MRSK.primary_host)
|
193
|
-
version = nil
|
194
|
-
on(host) { version = capture_with_info(*MRSK.app.most_recent_version_from_available_images).strip }
|
195
|
-
|
196
|
-
if version == "<none>"
|
197
|
-
raise "Most recent image available was not tagged with a version (returned <none>)"
|
198
|
-
else
|
199
|
-
version.presence
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
237
|
def current_running_version(host: MRSK.primary_host)
|
204
238
|
version = nil
|
205
239
|
on(host) { version = capture_with_info(*MRSK.app.current_running_version).strip }
|
206
240
|
version.presence
|
207
241
|
end
|
242
|
+
|
243
|
+
def version_or_latest
|
244
|
+
options[:version] || "latest"
|
245
|
+
end
|
208
246
|
end
|
data/lib/mrsk/cli/base.rb
CHANGED
@@ -6,6 +6,8 @@ module Mrsk::Cli
|
|
6
6
|
class Base < Thor
|
7
7
|
include SSHKit::DSL
|
8
8
|
|
9
|
+
class LockError < StandardError; end
|
10
|
+
|
9
11
|
def self.exit_on_failure?() true end
|
10
12
|
|
11
13
|
class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
|
@@ -25,7 +27,7 @@ module Mrsk::Cli
|
|
25
27
|
def initialize(*)
|
26
28
|
super
|
27
29
|
load_envs
|
28
|
-
initialize_commander(
|
30
|
+
initialize_commander(options_with_subcommand_class_options)
|
29
31
|
end
|
30
32
|
|
31
33
|
private
|
@@ -37,16 +39,12 @@ module Mrsk::Cli
|
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
42
|
+
def options_with_subcommand_class_options
|
43
|
+
options.merge(@_initializer.last[:class_options] || {})
|
44
|
+
end
|
45
|
+
|
40
46
|
def initialize_commander(options)
|
41
47
|
MRSK.tap do |commander|
|
42
|
-
commander.config_file = Pathname.new(File.expand_path(options[:config_file]))
|
43
|
-
commander.destination = options[:destination]
|
44
|
-
commander.version = options[:version]
|
45
|
-
|
46
|
-
commander.specific_hosts = options[:hosts]&.split(",")
|
47
|
-
commander.specific_roles = options[:roles]&.split(",")
|
48
|
-
commander.specific_primary! if options[:primary]
|
49
|
-
|
50
48
|
if options[:verbose]
|
51
49
|
ENV["VERBOSE"] = "1" # For backtraces via cli/start
|
52
50
|
commander.verbosity = :debug
|
@@ -55,6 +53,15 @@ module Mrsk::Cli
|
|
55
53
|
if options[:quiet]
|
56
54
|
commander.verbosity = :error
|
57
55
|
end
|
56
|
+
|
57
|
+
commander.configure \
|
58
|
+
config_file: Pathname.new(File.expand_path(options[:config_file])),
|
59
|
+
destination: options[:destination],
|
60
|
+
version: options[:version]
|
61
|
+
|
62
|
+
commander.specific_hosts = options[:hosts]&.split(",")
|
63
|
+
commander.specific_roles = options[:roles]&.split(",")
|
64
|
+
commander.specific_primary! if options[:primary]
|
58
65
|
end
|
59
66
|
end
|
60
67
|
|
@@ -70,5 +77,35 @@ module Mrsk::Cli
|
|
70
77
|
def audit_broadcast(line)
|
71
78
|
run_locally { execute *MRSK.auditor.broadcast(line), verbosity: :debug }
|
72
79
|
end
|
80
|
+
|
81
|
+
def with_lock
|
82
|
+
acquire_lock
|
83
|
+
|
84
|
+
yield
|
85
|
+
ensure
|
86
|
+
release_lock
|
87
|
+
end
|
88
|
+
|
89
|
+
def acquire_lock
|
90
|
+
if MRSK.lock_count == 0
|
91
|
+
say "Acquiring the deploy lock"
|
92
|
+
on(MRSK.primary_host) { execute *MRSK.lock.acquire("Automatic deploy lock", MRSK.config.version) }
|
93
|
+
end
|
94
|
+
MRSK.lock_count += 1
|
95
|
+
rescue SSHKit::Runner::ExecuteError => e
|
96
|
+
if e.message =~ /cannot create directory/
|
97
|
+
invoke "mrsk:cli:lock:status", []
|
98
|
+
end
|
99
|
+
|
100
|
+
raise LockError, "Deploy lock found"
|
101
|
+
end
|
102
|
+
|
103
|
+
def release_lock
|
104
|
+
MRSK.lock_count -= 1
|
105
|
+
if MRSK.lock_count == 0
|
106
|
+
say "Releasing the deploy lock"
|
107
|
+
on(MRSK.primary_host) { execute *MRSK.lock.release }
|
108
|
+
end
|
109
|
+
end
|
73
110
|
end
|
74
111
|
end
|
data/lib/mrsk/cli/build.rb
CHANGED
@@ -1,26 +1,30 @@
|
|
1
1
|
class Mrsk::Cli::Build < Mrsk::Cli::Base
|
2
2
|
desc "deliver", "Build app and push app image to registry then pull image on servers"
|
3
3
|
def deliver
|
4
|
-
|
5
|
-
|
4
|
+
with_lock do
|
5
|
+
push
|
6
|
+
pull
|
7
|
+
end
|
6
8
|
end
|
7
9
|
|
8
10
|
desc "push", "Build and push app image to registry"
|
9
11
|
def push
|
10
|
-
|
12
|
+
with_lock do
|
13
|
+
cli = self
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
run_locally do
|
16
|
+
begin
|
17
|
+
MRSK.with_verbosity(:debug) { execute *MRSK.builder.push }
|
18
|
+
rescue SSHKit::Command::Failed => e
|
19
|
+
if e.message =~ /(no builder)|(no such file or directory)/
|
20
|
+
error "Missing compatible builder, so creating a new one first"
|
18
21
|
|
19
|
-
|
20
|
-
|
22
|
+
if cli.create
|
23
|
+
MRSK.with_verbosity(:debug) { execute *MRSK.builder.push }
|
24
|
+
end
|
25
|
+
else
|
26
|
+
raise
|
21
27
|
end
|
22
|
-
else
|
23
|
-
raise
|
24
28
|
end
|
25
29
|
end
|
26
30
|
end
|
@@ -28,25 +32,29 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
28
32
|
|
29
33
|
desc "pull", "Pull app image from registry onto servers"
|
30
34
|
def pull
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
+
with_lock do
|
36
|
+
on(MRSK.hosts) do
|
37
|
+
execute *MRSK.auditor.record("Pulled image with version #{MRSK.config.version}"), verbosity: :debug
|
38
|
+
execute *MRSK.builder.clean, raise_on_non_zero_exit: false
|
39
|
+
execute *MRSK.builder.pull
|
40
|
+
end
|
35
41
|
end
|
36
42
|
end
|
37
43
|
|
38
44
|
desc "create", "Create a build setup"
|
39
45
|
def create
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
with_lock do
|
47
|
+
run_locally do
|
48
|
+
begin
|
49
|
+
debug "Using builder: #{MRSK.builder.name}"
|
50
|
+
execute *MRSK.builder.create
|
51
|
+
rescue SSHKit::Command::Failed => e
|
52
|
+
if e.message =~ /stderr=(.*)/
|
53
|
+
error "Couldn't create remote builder: #{$1}"
|
54
|
+
false
|
55
|
+
else
|
56
|
+
raise
|
57
|
+
end
|
50
58
|
end
|
51
59
|
end
|
52
60
|
end
|
@@ -54,9 +62,11 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
54
62
|
|
55
63
|
desc "remove", "Remove build setup"
|
56
64
|
def remove
|
57
|
-
|
58
|
-
|
59
|
-
|
65
|
+
with_lock do
|
66
|
+
run_locally do
|
67
|
+
debug "Using builder: #{MRSK.builder.name}"
|
68
|
+
execute *MRSK.builder.remove
|
69
|
+
end
|
60
70
|
end
|
61
71
|
end
|
62
72
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Mrsk::Cli::Lock < Mrsk::Cli::Base
|
2
|
+
desc "status", "Report lock status"
|
3
|
+
def status
|
4
|
+
handle_missing_lock do
|
5
|
+
on(MRSK.primary_host) { puts capture_with_info(*MRSK.lock.status) }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "acquire", "Acquire the deploy lock"
|
10
|
+
option :message, aliases: "-m", type: :string, desc: "A lock mesasge", required: true
|
11
|
+
def acquire
|
12
|
+
message = options[:message]
|
13
|
+
handle_missing_lock do
|
14
|
+
on(MRSK.primary_host) { execute *MRSK.lock.acquire(message, MRSK.config.version) }
|
15
|
+
say "Set the deploy lock"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "release", "Release the deploy lock"
|
20
|
+
def release
|
21
|
+
handle_missing_lock do
|
22
|
+
on(MRSK.primary_host) { execute *MRSK.lock.release }
|
23
|
+
say "Removed the deploy lock"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def handle_missing_lock
|
29
|
+
yield
|
30
|
+
rescue SSHKit::Runner::ExecuteError => e
|
31
|
+
if e.message =~ /No such file or directory/
|
32
|
+
say "There is no deploy lock"
|
33
|
+
else
|
34
|
+
raise
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|