Skip to content

Commit

Permalink
Allow running detached app commands
Browse files Browse the repository at this point in the history
this is useful for long running rake tasks or scripts
that can be run without having to keep open connection to the server.

Example:
```
kamal app exec 'bin/rails db:backfill_task' --detach
```
  • Loading branch information
aliismayilov committed Oct 21, 2024
1 parent 74a06b0 commit d2edf8d
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 2 deletions.
4 changes: 3 additions & 1 deletion lib/kamal/cli/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ def details
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
option :env, aliases: "-e", type: :hash, desc: "Set environment variables for the command"
option :detach, type: :boolean, default: false, desc: "Execute command in a detached container"
def exec(*cmd)
cmd = Kamal::Utils.join_commands(cmd)
env = options[:env]
detach = options[:detach]
case
when options[:interactive] && options[:reuse]
say "Get current version of running container...", :magenta unless options[:version]
Expand Down Expand Up @@ -138,7 +140,7 @@ def exec(*cmd)

roles.each do |role|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_new_container(cmd, env: env))
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_new_container(cmd, env: env, detach: detach))
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion lib/kamal/commands/app/execution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ def execute_in_existing_container(*command, interactive: false, env:)
*command
end

def execute_in_new_container(*command, interactive: false, env:)
def execute_in_new_container(*command, interactive: false, detach: false, env:)
docker :run,
("-it" if interactive),
("--detach" if detach),
"--rm",
"--network", "kamal",
*role&.env_args(host),
Expand Down
6 changes: 6 additions & 0 deletions test/cli/app_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ class CliAppTest < CliTestCase
end
end

test "exec detach" do
run_command("exec", "--detach", "ruby -v").tap do |output|
assert_match "docker run --detach --rm --env-file .kamal/env/roles/app-web.env dhh/app:latest ruby -v", output
end
end

test "exec with reuse" do
run_command("exec", "--reuse", "ruby -v").tap do |output|
assert_match "sh -c 'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | while read line; do echo ${line#app-web-}; done", output # Get current version
Expand Down
6 changes: 6 additions & 0 deletions test/commands/app_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ class CommandsAppTest < ActiveSupport::TestCase
new_command.execute_in_new_container("bin/rails", "db:setup", env: { "foo" => "bar" }).join(" ")
end

test "execute in new detached container" do
assert_equal \
"docker run --detach --rm --env-file .kamal/env/roles/app-web.env --detach dhh/app:999 bin/rails db:setup",
new_command.execute_in_new_container("bin/rails", "db:setup", detach: true, env: {}).join(" ")
end

test "execute in new container with tags" do
@config[:servers] = [ { "1.1.1.1" => "tag1" } ]
@config[:env]["tags"] = { "tag1" => { "ENV1" => "value1" } }
Expand Down

0 comments on commit d2edf8d

Please sign in to comment.