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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d53c75ef2fad59a884ae2e8a04fff57a68330faf7522ef7bb620affc48a803d6
4
- data.tar.gz: b91edccb441e562d24680034df00fbcdf3518aadd570784fbf31d9f2c07a9ebd
3
+ metadata.gz: 8a09720b016119167213b3c1aab02e56c5e3de0eaa09dc39996d55931048e3e5
4
+ data.tar.gz: 7b435e7ff711c7267cb3f1771d56791fe76c1fb62950daeeacfe923288c314e8
5
5
  SHA512:
6
- metadata.gz: d7037c8e7a41acd4cb199b29251245f3e99f2c9cf62ca99c392ba7dfb77defde655f616161e6ef82319ea14438fb27992a8a88861bb49a2a1efe1771f92d60e0
7
- data.tar.gz: a0810f91aeb9332ea8401d2a7f8ca847f78a37a85f67b71753734fe2f0ef298435f813fc07fa156bb755e05170afd09f2bdc384504041276ca2502b19ab1d7a6
6
+ metadata.gz: de31f5f7e47e1f93446603e48a9a1760fca96bc41ee6dfec0660030fb0fbcdcb3fb4145d4b81fda9c3f4eef5de0a205f54e67e7bb27c91d7ebd0c93c3f5d0c57
7
+ data.tar.gz: 48d36ec263b4ccfd3729059eb85f340717b17406f88e8281955e948fcfe9c30ffe081bad01977e6f9836ecd480361cf94f15cdfe79aa7477c18a6fb6ad9b97bb
data/README.md CHANGED
@@ -6,6 +6,8 @@ Watch the screencast: https://www.youtube.com/watch?v=LL1cV2FXZ5I
6
6
 
7
7
  Join us on Discord: https://discord.gg/YgHVT7GCXS
8
8
 
9
+ Ask questions: https://github.com/mrsked/mrsk/discussions
10
+
9
11
  ## Installation
10
12
 
11
13
  If you have a Ruby environment available, you can install MRSK globally with:
@@ -14,13 +16,13 @@ If you have a Ruby environment available, you can install MRSK globally with:
14
16
  gem install mrsk
15
17
  ```
16
18
 
17
- ...otherwise, you can run a dockerized version via an alias (add this to your ${SHELL}rc to simplify re-use):
19
+ ...otherwise, you can run a dockerized version via an alias (add this to your .bashrc or similar to simplify re-use):
18
20
 
19
21
  ```sh
20
22
  alias mrsk='docker run --rm -it -v $HOME/.ssh:/root/.ssh -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}/:/workdir ghcr.io/mrsked/mrsk'
21
23
  ```
22
24
 
23
- Then, inside your app directory, run `mrsk init` (or `mrsk init --bundle` within Rails apps where you want a bin/mrsk binstub). Now edit the new file `config/deploy.yml`. It could look as simple as this:
25
+ Then, inside your app directory, run `mrsk init` (or `mrsk init --bundle` within Rails 7+ apps where you want a bin/mrsk binstub). Now edit the new file `config/deploy.yml`. It could look as simple as this:
24
26
 
25
27
  ```yaml
26
28
  service: hey
@@ -191,6 +193,15 @@ ssh:
191
193
  user: app
192
194
  ```
193
195
 
196
+ If you are using non-root user, you need to bootstrap your servers manually, before using them with MRSK. On Ubuntu, you'd do:
197
+
198
+ ```bash
199
+ sudo apt update
200
+ sudo apt upgrade -y
201
+ sudo apt install -y docker.io curl git
202
+ sudo usermod -a -G docker ubuntu
203
+ ```
204
+
194
205
  ### Using a proxy SSH host
195
206
 
196
207
  If you need to connect to server through a proxy host, you can use `ssh/proxy`:
@@ -207,6 +218,13 @@ ssh:
207
218
  proxy: "app@192.168.0.1"
208
219
  ```
209
220
 
221
+ Also if you need specific proxy command to connect to the server:
222
+
223
+ ```yaml
224
+ ssh:
225
+ proxy_command: aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --region=us-east-1 ## ssh via aws ssm
226
+ ```
227
+
210
228
  ### Using env variables
211
229
 
212
230
  You can inject env variables into the app containers using `env`:
@@ -288,8 +306,9 @@ You can specialize the default Traefik rules by setting labels on the containers
288
306
 
289
307
  ```yaml
290
308
  labels:
291
- traefik.http.routers.hey.rule: Host(`app.hey.com`)
309
+ traefik.http.routers.hey-web.rule: Host(`app.hey.com`)
292
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.
293
312
 
294
313
  Note: The backticks are needed to ensure the rule is passed in correctly and not treated as command substitution by Bash!
295
314
 
@@ -312,6 +331,21 @@ servers:
312
331
  my-label: "50"
313
332
  ```
314
333
 
334
+ ### Using shell expansion
335
+
336
+ You can use shell expansion to interpolate values from the host machine into labels and env variables with the `${}` syntax.
337
+ Anything within the curly braces will be executed on the host machine and the result will be interpolated into the label or env variable.
338
+
339
+ ```yaml
340
+ labels:
341
+ host-machine: "${cat /etc/hostname}"
342
+
343
+ env:
344
+ HOST_DEPLOYMENT_DIR: "${PWD}"
345
+ ```
346
+
347
+ Note: Any other occurrence of `$` will be escaped to prevent unwanted shell expansion!
348
+
315
349
  ### Using container options
316
350
 
317
351
  You can specialize the options used to start containers using the `options` definitions:
@@ -439,9 +473,9 @@ RUN --mount=type=secret,id=GITHUB_TOKEN \
439
473
  rm -rf /usr/local/bundle/cache
440
474
  ```
441
475
 
442
- ### Using command arguments for Traefik
476
+ ### Traefik command arguments
443
477
 
444
- You can customize the traefik command line:
478
+ Customize the Traefik command line using `args`:
445
479
 
446
480
  ```yaml
447
481
  traefik:
@@ -450,37 +484,70 @@ traefik:
450
484
  accesslog.format: json
451
485
  ```
452
486
 
453
- This will start the traefik container with `--accesslog=true accesslog.format=json`.
487
+ This starts the Traefik container with `--accesslog=true --accesslog.format=json` arguments.
454
488
 
455
- ### Traefik's host port binding
489
+ ### Traefik host port binding
456
490
 
457
- By default Traefik binds to port 80 of the host machine, it can be configured to use an alternative port:
491
+ Traefik binds to port 80 by default. Specify an alternative port using `host_port`:
458
492
 
459
493
  ```yaml
460
494
  traefik:
461
495
  host_port: 8080
462
496
  ```
463
497
 
464
- ### Configure docker options for traefik
498
+ ### Traefik version, upgrades, and custom images
465
499
 
466
- We allow users to pass additional docker options to the trafik container like
500
+ MRSK runs the traefik:v2.9 image to track Traefik 2.9.x releases.
501
+
502
+ To pin Traefik to a specific version or an image published to your registry,
503
+ specify `image`:
504
+
505
+ ```yaml
506
+ traefik:
507
+ image: traefik:v2.10.0-rc1
508
+ ```
509
+
510
+ This is useful for downgrading Traefik if there's an unexpected breaking
511
+ change in a minor version release, upgrading Traefik to test forthcoming
512
+ releases, or running your own Traefik-derived image.
513
+
514
+ MRSK has not been tested for compatibility with Traefik 3 betas. Please do!
515
+
516
+ ### Traefik container configuration
517
+
518
+ Pass additional Docker configuration for the Traefik container using `options`:
467
519
 
468
520
  ```yaml
469
521
  traefik:
470
522
  options:
471
523
  publish:
472
- - 8080:8080
524
+ - 8080:8080
473
525
  volumes:
474
- - /tmp/example.json:/tmp/example.json
526
+ - /tmp/example.json:/tmp/example.json
475
527
  memory: 512m
476
528
  ```
477
529
 
478
- This will start the traefik container with a command like: `docker run ... --volume /tmp/example.json:/tmp/example.json --publish 8080:8080 `
530
+ This starts the Traefik container with `--volume /tmp/example.json:/tmp/example.json --publish 8080:8080 --memory 512m` arguments to `docker run`.
531
+
532
+ ### Traefik container labels
533
+
534
+ Add labels to Traefik Docker container.
479
535
 
536
+ ```yaml
537
+ traefik:
538
+ labels:
539
+ traefik.enable: true
540
+ traefik.http.routers.dashboard.rule: Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
541
+ traefik.http.routers.dashboard.service: api@internal
542
+ traefik.http.routers.dashboard.middlewares: auth
543
+ traefik.http.middlewares.auth.basicauth.users: test:$2y$05$H2o72tMaO.TwY1wNQUV1K.fhjRgLHRDWohFvUZOJHBEtUXNKrqUKi # test:password
544
+ ```
545
+
546
+ This labels Traefik container with `--label traefik.http.routers.dashboard.middlewares=\"auth\"` and so on.
480
547
 
481
- ### Configure alternate entrypoints for traefik
548
+ ### Traefik alternate entrypoints
482
549
 
483
- You can configure multiple entrypoints for traefik like so:
550
+ You can configure multiple entrypoints for Traefik like so:
484
551
 
485
552
  ```yaml
486
553
  service: myservice
@@ -540,7 +607,7 @@ accessories:
540
607
  memory: "2GB"
541
608
  redis:
542
609
  image: redis:latest
543
- role:
610
+ roles:
544
611
  - web
545
612
  port: "36379:6379"
546
613
  volumes:
@@ -610,18 +677,45 @@ That'll post a line like follows to a preconfigured chatbot in Basecamp:
610
677
  [My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
611
678
  ```
612
679
 
613
- ### Using custom healthcheck path or port
680
+ ### Healthcheck
681
+
682
+ 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.
614
683
 
615
- MRSK defaults to checking the health of your application again `/up` on port 3000. You can tailor both with the `healthcheck` setting:
684
+ The healthcheck defaults to testing the HTTP response to the path `/up` on port 3000, up to 7 times. You can tailor this behaviour with the `healthcheck` setting:
616
685
 
617
686
  ```yaml
618
687
  healthcheck:
619
688
  path: /healthz
620
689
  port: 4000
690
+ max_attempts: 7
621
691
  ```
622
692
 
623
693
  This will ensure your application is configured with a traefik label for the healthcheck against `/healthz` and that the pre-deploy healthcheck that MRSK performs is done against the same path on port 4000.
624
694
 
695
+ You can also specify a custom healthcheck command, which is useful for non-HTTP services:
696
+
697
+ ```yaml
698
+ healthcheck:
699
+ cmd: /bin/check_health
700
+ ```
701
+
702
+ The top-level healthcheck configuration applies to all services that use
703
+ Traefik, by default. You can also specialize the configuration at the role
704
+ level:
705
+
706
+ ```yaml
707
+ servers:
708
+ job:
709
+ hosts: ...
710
+ cmd: bin/jobs
711
+ healthcheck:
712
+ cmd: bin/check
713
+ ```
714
+
715
+ 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
+
717
+ Note that the HTTP health checks assume that the `curl` command is avilable 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
+
625
719
  ## Commands
626
720
 
627
721
  ### Running commands on servers
@@ -761,6 +855,24 @@ mrsk lock acquire -m "Doing maintanence"
761
855
  mrsk lock release
762
856
  ```
763
857
 
858
+ ## Rolling deployments
859
+
860
+ When deploying to large numbers of hosts, you might prefer not to restart your services on every host at the same time.
861
+
862
+ MRSK's default is to boot new containers on all hosts in parallel. But you can control this by configuring `boot/limit` and `boot/wait` as options:
863
+
864
+ ```yaml
865
+ service: myservice
866
+
867
+ boot:
868
+ limit: 10 # Can also specify as a percentage of total hosts, such as "25%"
869
+ wait: 2
870
+ ```
871
+
872
+ When `limit` is specified, containers will be booted on, at most, `limit` hosts at once. MRSK will pause for `wait` seconds between batches.
873
+
874
+ These settings only apply when booting containers (using `mrsk deploy`, or `mrsk app boot`). For other commands, MRSK continues to run commands in parallel across all hosts.
875
+
764
876
  ## Stage of development
765
877
 
766
878
  This is beta software. Commands may still move around. But we're live in production at [37signals](https://37signals.com).
data/lib/mrsk/cli/app.rb CHANGED
@@ -6,27 +6,33 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
6
6
  using_version(version_or_latest) do |version|
7
7
  say "Start container with version #{version} using a #{MRSK.config.readiness_delay}s readiness delay (or reboot if already running)...", :magenta
8
8
 
9
- cli = self
9
+ on(MRSK.hosts) do
10
+ execute *MRSK.auditor.record("Tagging #{MRSK.config.absolute_image} as the latest image"), verbosity: :debug
11
+ execute *MRSK.app.tag_current_as_latest
12
+ end
10
13
 
11
- on(MRSK.hosts) do |host|
14
+ on(MRSK.hosts, **MRSK.boot_strategy) do |host|
12
15
  roles = MRSK.roles_on(host)
13
16
 
14
17
  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?
18
+ app = MRSK.app(role: role)
19
+ auditor = MRSK.auditor(role: role)
20
+
21
+ execute *auditor.record("Booted app version #{version}"), verbosity: :debug
22
+
23
+ if capture_with_info(*app.container_id_for_version(version), raise_on_non_zero_exit: false).present?
24
+ tmp_version = "#{version}_#{SecureRandom.hex(8)}"
25
+ info "Renaming container #{version} to #{tmp_version} as already deployed on #{host}"
26
+ execute *auditor.record("Renaming container #{version} to #{tmp_version}"), verbosity: :debug
27
+ execute *app.rename_container(version: version, new_version: tmp_version)
29
28
  end
29
+
30
+ old_version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
31
+ execute *app.run
32
+
33
+ Mrsk::Utils::HealthcheckPoller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
34
+
35
+ execute *app.stop(version: old_version), raise_on_non_zero_exit: false if old_version.present?
30
36
  end
31
37
  end
32
38
  end
@@ -124,6 +130,31 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
124
130
  on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.list_containers) }
125
131
  end
126
132
 
133
+ desc "stale_containers", "Detect app stale containers"
134
+ option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the stale containers found"
135
+ def stale_containers
136
+ with_lock do
137
+ stop = options[:stop]
138
+
139
+ cli = self
140
+
141
+ on(MRSK.hosts) do |host|
142
+ roles = MRSK.roles_on(host)
143
+
144
+ roles.each do |role|
145
+ cli.send(:stale_versions, host: host, role: role).each do |version|
146
+ if stop
147
+ puts_by_host host, "Stopping stale container for role #{role} with version #{version}"
148
+ execute *MRSK.app(role: role).stop(version: version), raise_on_non_zero_exit: false
149
+ else
150
+ puts_by_host host, "Detected stale container for role #{role} with version #{version} (use `mrsk app stale_containers --stop` to stop)"
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+
127
158
  desc "images", "Show app images on servers"
128
159
  def images
129
160
  on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.list_images) }
@@ -240,6 +271,17 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
240
271
  version.presence
241
272
  end
242
273
 
274
+ def stale_versions(host:, role:)
275
+ versions = nil
276
+ on(host) do
277
+ versions = \
278
+ capture_with_info(*MRSK.app(role: role).list_versions, raise_on_non_zero_exit: false)
279
+ .split("\n")
280
+ .drop(1)
281
+ end
282
+ versions
283
+ end
284
+
243
285
  def version_or_latest
244
286
  options[:version] || "latest"
245
287
  end
data/lib/mrsk/cli/base.rb CHANGED
@@ -6,8 +6,6 @@ module Mrsk::Cli
6
6
  class Base < Thor
7
7
  include SSHKit::DSL
8
8
 
9
- class LockError < StandardError; end
10
-
11
9
  def self.exit_on_failure?() true end
12
10
 
13
11
  class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
@@ -79,32 +77,55 @@ module Mrsk::Cli
79
77
  end
80
78
 
81
79
  def with_lock
82
- acquire_lock
80
+ if MRSK.holding_lock?
81
+ yield
82
+ else
83
+ acquire_lock
84
+
85
+ begin
86
+ yield
87
+ rescue
88
+ if MRSK.hold_lock_on_error?
89
+ error " \e[31mDeploy lock was not released\e[0m"
90
+ else
91
+ release_lock
92
+ end
93
+
94
+ raise
95
+ end
83
96
 
84
- yield
85
- ensure
86
- release_lock
97
+ release_lock
98
+ end
87
99
  end
88
100
 
89
101
  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
102
+ say "Acquiring the deploy lock"
103
+ on(MRSK.primary_host) { execute *MRSK.lock.acquire("Automatic deploy lock", MRSK.config.version) }
104
+
105
+ MRSK.holding_lock = true
95
106
  rescue SSHKit::Runner::ExecuteError => e
96
107
  if e.message =~ /cannot create directory/
97
- invoke "mrsk:cli:lock:status", []
108
+ on(MRSK.primary_host) { execute *MRSK.lock.status }
109
+ raise LockError, "Deploy lock found"
110
+ else
111
+ raise e
98
112
  end
99
-
100
- raise LockError, "Deploy lock found"
101
113
  end
102
114
 
103
115
  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 }
116
+ say "Releasing the deploy lock"
117
+ on(MRSK.primary_host) { execute *MRSK.lock.release }
118
+
119
+ MRSK.holding_lock = false
120
+ end
121
+
122
+ def hold_lock_on_error
123
+ if MRSK.hold_lock_on_error?
124
+ yield
125
+ else
126
+ MRSK.hold_lock_on_error = true
127
+ yield
128
+ MRSK.hold_lock_on_error = false
108
129
  end
109
130
  end
110
131
  end
@@ -1,4 +1,6 @@
1
1
  class Mrsk::Cli::Build < Mrsk::Cli::Base
2
+ class BuildError < StandardError; end
3
+
2
4
  desc "deliver", "Build app and push app image to registry then pull image on servers"
3
5
  def deliver
4
6
  with_lock do
@@ -14,7 +16,9 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
14
16
 
15
17
  run_locally do
16
18
  begin
17
- MRSK.with_verbosity(:debug) { execute *MRSK.builder.push }
19
+ if cli.verify_local_dependencies
20
+ MRSK.with_verbosity(:debug) { execute *MRSK.builder.push }
21
+ end
18
22
  rescue SSHKit::Command::Failed => e
19
23
  if e.message =~ /(no builder)|(no such file or directory)/
20
24
  error "Missing compatible builder, so creating a new one first"
@@ -77,4 +81,22 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
77
81
  puts capture(*MRSK.builder.info)
78
82
  end
79
83
  end
84
+
85
+
86
+ desc "", "" # Really a private method, but needed to be invoked from #push
87
+ def verify_local_dependencies
88
+ run_locally do
89
+ begin
90
+ execute *MRSK.builder.ensure_local_dependencies_installed
91
+ rescue SSHKit::Command::Failed => e
92
+ build_error = e.message =~ /command not found/ ?
93
+ "Docker is not installed locally" :
94
+ "Docker buildx plugin is not installed locally"
95
+
96
+ raise BuildError, build_error
97
+ end
98
+ end
99
+
100
+ true
101
+ end
80
102
  end
@@ -1,8 +1,4 @@
1
1
  class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
2
- MAX_ATTEMPTS = 7
3
-
4
- class HealthcheckError < StandardError; end
5
-
6
2
  default_command :perform
7
3
 
8
4
  desc "perform", "Health check current app version"
@@ -10,37 +6,10 @@ class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
10
6
  on(MRSK.primary_host) do
11
7
  begin
12
8
  execute *MRSK.healthcheck.run
13
-
14
- target = "Health check against #{MRSK.config.healthcheck["path"]}"
15
- attempt = 1
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..."
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
- if e.message =~ /curl/
40
- raise SSHKit::Command::Failed, "#{target} failed to return 200 OK!"
41
- else
42
- raise
43
- end
12
+ raise
44
13
  ensure
45
14
  execute *MRSK.healthcheck.stop, raise_on_non_zero_exit: false
46
15
  execute *MRSK.healthcheck.remove, raise_on_non_zero_exit: false
data/lib/mrsk/cli/lock.rb CHANGED
@@ -12,7 +12,7 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
12
12
  message = options[:message]
13
13
  handle_missing_lock do
14
14
  on(MRSK.primary_host) { execute *MRSK.lock.acquire(message, MRSK.config.version) }
15
- say "Set the deploy lock"
15
+ say "Acquired the deploy lock"
16
16
  end
17
17
  end
18
18
 
@@ -20,7 +20,7 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
20
20
  def release
21
21
  handle_missing_lock do
22
22
  on(MRSK.primary_host) { execute *MRSK.lock.release }
23
- say "Removed the deploy lock"
23
+ say "Released the deploy lock"
24
24
  end
25
25
  end
26
26