mrsk 0.12.0 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -2
- data/lib/mrsk/cli/app.rb +4 -4
- data/lib/mrsk/cli/healthcheck.rb +1 -0
- data/lib/mrsk/cli/main.rb +7 -0
- data/lib/mrsk/commander.rb +2 -2
- data/lib/mrsk/commands/auditor.rb +22 -20
- data/lib/mrsk/commands/base.rb +1 -0
- data/lib/mrsk/commands/healthcheck.rb +4 -0
- data/lib/mrsk/commands/lock.rb +2 -2
- data/lib/mrsk/commands/prune.rb +1 -1
- data/lib/mrsk/sshkit_with_ext.rb +40 -0
- data/lib/mrsk/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '099a4dc2dc59df4e0c5301b85a579970dbc6c46a3c1e2a634f4461c5cff1f241'
|
4
|
+
data.tar.gz: 0a0356837992a9847b7b6bc51956c26a4fb0e8fbb9809753a5bdb373bacd3aee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06f1365b5f8a7cc2064f2bd184224dba12b1c0ebf57cc502c9ac423a9d0ae96a601161fe681764f9a660b8a6214f2a86b79d1c4d46d9ff13819c52159db14318
|
7
|
+
data.tar.gz: 7acf9c5d2e26709b391a2c9915300c569fd6cc7d841b55cd3a497122d2d2317b8d2b3df4e634f14c38acd571fbc8aac51096a221f10097f8ce68d6c07dbce038
|
data/README.md
CHANGED
@@ -308,7 +308,7 @@ You can specialize the default Traefik rules by setting labels on the containers
|
|
308
308
|
labels:
|
309
309
|
traefik.http.routers.hey-web.rule: Host(`app.hey.com`)
|
310
310
|
```
|
311
|
-
Traefik rules are in the "service-role-destination" format. The default role will be `web` if no rule is specified. If the destination is not specified, it is not included. To give an example, the above rule would become "traefik.http.routers.hey-web.rule" if it was for the "staging" destination.
|
311
|
+
Traefik rules are in the "service-role-destination" format. The default role will be `web` if no rule is specified. If the destination is not specified, it is not included. To give an example, the above rule would become "traefik.http.routers.hey-web-staging.rule" if it was for the "staging" destination.
|
312
312
|
|
313
313
|
Note: The backticks are needed to ensure the rule is passed in correctly and not treated as command substitution by Bash!
|
314
314
|
|
@@ -677,6 +677,21 @@ That'll post a line like follows to a preconfigured chatbot in Basecamp:
|
|
677
677
|
[My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
|
678
678
|
```
|
679
679
|
|
680
|
+
`MRSK_*` environment variables are available to the broadcast command for
|
681
|
+
fine-grained audit reporting, e.g. for triggering deployment reports or
|
682
|
+
firing a JSON webhook. These variables include:
|
683
|
+
- `MRSK_RECORDED_AT` - UTC timestamp in ISO 8601 format, e.g. `2023-04-14T17:07:31Z`
|
684
|
+
- `MRSK_PERFORMER` - the local user performing the command (from `whoami`)
|
685
|
+
- `MRSK_MESSAGE` - the full audit message, e.g. "Deployed app@150b24f"
|
686
|
+
- `MRSK_DESTINATION` - optional: destination, e.g. "staging"
|
687
|
+
- `MRSK_ROLE` - optional: role targeted, e.g. "web"
|
688
|
+
|
689
|
+
Use `mrsk broadcast` to test and troubleshoot your broadcast command:
|
690
|
+
|
691
|
+
```bash
|
692
|
+
mrsk broadcast -m "test audit message"
|
693
|
+
```
|
694
|
+
|
680
695
|
### Healthcheck
|
681
696
|
|
682
697
|
MRSK uses Docker healtchecks to check the health of your application during deployment. Traefik uses this same healthcheck status to determine when a container is ready to receive traffic.
|
@@ -714,7 +729,7 @@ servers:
|
|
714
729
|
|
715
730
|
The healthcheck allows for an optional `max_attempts` setting, which will attempt the healthcheck up to the specified number of times before failing the deploy. This is useful for applications that take a while to start up. The default is 7.
|
716
731
|
|
717
|
-
Note
|
732
|
+
Note: The HTTP health checks assume that the `curl` command is available inside the container. If that's not the case, use the healthcheck's `cmd` option to specify an alternative check that the container supports.
|
718
733
|
|
719
734
|
## Commands
|
720
735
|
|
data/lib/mrsk/cli/app.rb
CHANGED
@@ -60,7 +60,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
60
60
|
roles = MRSK.roles_on(host)
|
61
61
|
|
62
62
|
roles.each do |role|
|
63
|
-
execute *MRSK.auditor
|
63
|
+
execute *MRSK.auditor.record("Stopped app", role: role), verbosity: :debug
|
64
64
|
execute *MRSK.app(role: role).stop, raise_on_non_zero_exit: false
|
65
65
|
end
|
66
66
|
end
|
@@ -107,7 +107,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
107
107
|
roles = MRSK.roles_on(host)
|
108
108
|
|
109
109
|
roles.each do |role|
|
110
|
-
execute *MRSK.auditor
|
110
|
+
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on app version #{version}", role: role), verbosity: :debug
|
111
111
|
puts_by_host host, capture_with_info(*MRSK.app(role: role).execute_in_existing_container(cmd))
|
112
112
|
end
|
113
113
|
end
|
@@ -214,7 +214,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
214
214
|
roles = MRSK.roles_on(host)
|
215
215
|
|
216
216
|
roles.each do |role|
|
217
|
-
execute *MRSK.auditor
|
217
|
+
execute *MRSK.auditor.record("Removed app container with version #{version}", role: role), verbosity: :debug
|
218
218
|
execute *MRSK.app(role: role).remove_container(version: version)
|
219
219
|
end
|
220
220
|
end
|
@@ -228,7 +228,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
228
228
|
roles = MRSK.roles_on(host)
|
229
229
|
|
230
230
|
roles.each do |role|
|
231
|
-
execute *MRSK.auditor
|
231
|
+
execute *MRSK.auditor.record("Removed all app containers", role: role), verbosity: :debug
|
232
232
|
execute *MRSK.app(role: role).remove_containers
|
233
233
|
end
|
234
234
|
end
|
data/lib/mrsk/cli/healthcheck.rb
CHANGED
@@ -9,6 +9,7 @@ class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
|
|
9
9
|
Mrsk::Utils::HealthcheckPoller.wait_for_healthy { capture_with_info(*MRSK.healthcheck.status) }
|
10
10
|
rescue Mrsk::Utils::HealthcheckPoller::HealthcheckError => e
|
11
11
|
error capture_with_info(*MRSK.healthcheck.logs)
|
12
|
+
error capture_with_pretty_json(*MRSK.healthcheck.container_health_log)
|
12
13
|
raise
|
13
14
|
ensure
|
14
15
|
execute *MRSK.healthcheck.stop, raise_on_non_zero_exit: false
|
data/lib/mrsk/cli/main.rb
CHANGED
@@ -200,6 +200,13 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
200
200
|
end
|
201
201
|
end
|
202
202
|
|
203
|
+
desc "broadcast", "Broadcast an audit message"
|
204
|
+
option :message, aliases: "-m", type: :string, desc: "Audit mesasge", required: true
|
205
|
+
def broadcast
|
206
|
+
say "Broadcast: #{options[:message]}", :magenta
|
207
|
+
audit_broadcast options[:message]
|
208
|
+
end
|
209
|
+
|
203
210
|
desc "version", "Show MRSK version"
|
204
211
|
def version
|
205
212
|
puts Mrsk::VERSION
|
data/lib/mrsk/commander.rb
CHANGED
@@ -84,8 +84,8 @@ class Mrsk::Commander
|
|
84
84
|
Mrsk::Commands::Accessory.new(config, name: name)
|
85
85
|
end
|
86
86
|
|
87
|
-
def auditor(
|
88
|
-
Mrsk::Commands::Auditor.new(config,
|
87
|
+
def auditor(**details)
|
88
|
+
Mrsk::Commands::Auditor.new(config, **details)
|
89
89
|
end
|
90
90
|
|
91
91
|
def builder
|
@@ -1,24 +1,24 @@
|
|
1
|
-
require "
|
1
|
+
require "time"
|
2
2
|
|
3
3
|
class Mrsk::Commands::Auditor < Mrsk::Commands::Base
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :details
|
5
5
|
|
6
|
-
def initialize(config,
|
6
|
+
def initialize(config, **details)
|
7
7
|
super(config)
|
8
|
-
@
|
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,
|
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,
|
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
|
35
|
-
|
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
|
39
|
-
|
40
|
+
def audit_tags(**details)
|
41
|
+
tags_for **self.details.merge(details)
|
40
42
|
end
|
41
43
|
|
42
|
-
def
|
43
|
-
"'#{
|
44
|
+
def broadcast_args(line, **details)
|
45
|
+
"'#{broadcast_tags(**details).join(" ")} #{line}'"
|
44
46
|
end
|
45
47
|
|
46
|
-
def
|
47
|
-
|
48
|
+
def broadcast_tags(**details)
|
49
|
+
tags_for **self.details.merge(details).except(:recorded_at)
|
48
50
|
end
|
49
51
|
|
50
|
-
def
|
51
|
-
"[#{
|
52
|
+
def tags_for(**details)
|
53
|
+
details.compact.values.map { |value| "[#{value}]" }
|
52
54
|
end
|
53
55
|
|
54
|
-
def
|
55
|
-
"
|
56
|
+
def env_for(**details)
|
57
|
+
self.details.merge(details).compact.transform_keys { |detail| "MRSK_#{detail.upcase}" }
|
56
58
|
end
|
57
59
|
end
|
data/lib/mrsk/commands/base.rb
CHANGED
@@ -3,6 +3,7 @@ module Mrsk::Commands
|
|
3
3
|
delegate :sensitive, :argumentize, to: Mrsk::Utils
|
4
4
|
|
5
5
|
DOCKER_HEALTH_STATUS_FORMAT = "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'"
|
6
|
+
DOCKER_HEALTH_LOG_FORMAT = "'{{json .State.Health}}'"
|
6
7
|
|
7
8
|
attr_accessor :config
|
8
9
|
|
@@ -22,6 +22,10 @@ class Mrsk::Commands::Healthcheck < Mrsk::Commands::Base
|
|
22
22
|
pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
|
23
23
|
end
|
24
24
|
|
25
|
+
def container_health_log
|
26
|
+
pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_LOG_FORMAT))
|
27
|
+
end
|
28
|
+
|
25
29
|
def logs
|
26
30
|
pipe container_id, xargs(docker(:logs, "--tail", 50, "2>&1"))
|
27
31
|
end
|
data/lib/mrsk/commands/lock.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require "active_support/duration"
|
2
|
-
require "
|
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.
|
52
|
+
Locked by: #{locked_by} at #{Time.now.utc.iso8601}
|
53
53
|
Version: #{version}
|
54
54
|
Message: #{message}
|
55
55
|
DETAILS
|
data/lib/mrsk/commands/prune.rb
CHANGED
@@ -3,7 +3,7 @@ require "active_support/core_ext/numeric/time"
|
|
3
3
|
|
4
4
|
class Mrsk::Commands::Prune < Mrsk::Commands::Base
|
5
5
|
def images
|
6
|
-
docker :image, :prune, "--
|
6
|
+
docker :image, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true"
|
7
7
|
end
|
8
8
|
|
9
9
|
def containers(keep_last: 5)
|
data/lib/mrsk/sshkit_with_ext.rb
CHANGED
@@ -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
7
|
def capture_with_info(*args, **kwargs)
|
6
8
|
capture(*args, **kwargs, verbosity: Logger::INFO)
|
7
9
|
end
|
8
10
|
|
11
|
+
def capture_with_pretty_json(*args, **kwargs)
|
12
|
+
JSON.pretty_generate(JSON.parse(capture(*args, **kwargs)))
|
13
|
+
end
|
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
|
data/lib/mrsk/version.rb
CHANGED
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.12.
|
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-05-
|
11
|
+
date: 2023-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|