Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set env variables when running kamal app exec #751

Merged
merged 1 commit into from Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 6 additions & 4 deletions lib/kamal/cli/app.rb
Expand Up @@ -70,21 +70,23 @@ def details
desc "exec [CMD]", "Execute a custom command on servers (use --help to show options)"
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"
def exec(cmd)
env = options[:env]
case
when options[:interactive] && options[:reuse]
say "Get current version of running container...", :magenta unless options[:version]
using_version(options[:version] || current_running_version) do |version|
say "Launching interactive command with version #{version} via SSH from existing container on #{KAMAL.primary_host}...", :magenta
run_locally { exec KAMAL.app(role: KAMAL.primary_role).execute_in_existing_container_over_ssh(cmd, host: KAMAL.primary_host) }
run_locally { exec KAMAL.app(role: KAMAL.primary_role).execute_in_existing_container_over_ssh(cmd, host: KAMAL.primary_host, env: env) }
end

when options[:interactive]
say "Get most recent version available as an image...", :magenta unless options[:version]
using_version(version_or_latest) do |version|
say "Launching interactive command with version #{version} via SSH from new container on #{KAMAL.primary_host}...", :magenta
run_locally do
exec KAMAL.app(role: KAMAL.primary_role).execute_in_new_container_over_ssh(cmd, host: KAMAL.primary_host)
exec KAMAL.app(role: KAMAL.primary_role).execute_in_new_container_over_ssh(cmd, host: KAMAL.primary_host, env: env)
end
end

Expand All @@ -98,7 +100,7 @@ def exec(cmd)

roles.each do |role|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}", role: role), verbosity: :debug
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_existing_container(cmd))
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_existing_container(cmd, env: env))
end
end
end
Expand All @@ -112,7 +114,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).execute_in_new_container(cmd))
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_new_container(cmd, env: env))
end
end
end
Expand Down
14 changes: 8 additions & 6 deletions lib/kamal/commands/app/execution.rb
@@ -1,27 +1,29 @@
module Kamal::Commands::App::Execution
def execute_in_existing_container(*command, interactive: false)
def execute_in_existing_container(*command, interactive: false, env:)
docker :exec,
("-it" if interactive),
*argumentize("--env", env),
container_name,
*command
end

def execute_in_new_container(*command, interactive: false)
def execute_in_new_container(*command, interactive: false, env:)
docker :run,
("-it" if interactive),
"--rm",
*role&.env_args,
*argumentize("--env", env),
*config.volume_args,
*role&.option_args,
config.absolute_image,
*command
end

def execute_in_existing_container_over_ssh(*command, host:)
run_over_ssh execute_in_existing_container(*command, interactive: true), host: host
def execute_in_existing_container_over_ssh(*command, host:, env:)
run_over_ssh execute_in_existing_container(*command, interactive: true, env: env), host: host
end

def execute_in_new_container_over_ssh(*command, host:)
run_over_ssh execute_in_new_container(*command, interactive: true), host: host
def execute_in_new_container_over_ssh(*command, host:, env:)
run_over_ssh execute_in_new_container(*command, interactive: true, env: env), host: host
end
end
24 changes: 18 additions & 6 deletions test/commands/app_test.rb
Expand Up @@ -174,36 +174,48 @@ class CommandsAppTest < ActiveSupport::TestCase
test "execute in new container" do
assert_equal \
"docker run --rm --env-file .kamal/env/roles/app-web.env dhh/app:999 bin/rails db:setup",
new_command.execute_in_new_container("bin/rails", "db:setup").join(" ")
new_command.execute_in_new_container("bin/rails", "db:setup", env: {}).join(" ")
end

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

test "execute in new container with custom options" do
@config[:servers] = { "web" => { "hosts" => [ "1.1.1.1" ], "options" => { "mount" => "somewhere", "cap-add" => true } } }
assert_equal \
"docker run --rm --env-file .kamal/env/roles/app-web.env --mount \"somewhere\" --cap-add dhh/app:999 bin/rails db:setup",
new_command.execute_in_new_container("bin/rails", "db:setup").join(" ")
new_command.execute_in_new_container("bin/rails", "db:setup", env: {}).join(" ")
end

test "execute in existing container" do
assert_equal \
"docker exec app-web-999 bin/rails db:setup",
new_command.execute_in_existing_container("bin/rails", "db:setup").join(" ")
new_command.execute_in_existing_container("bin/rails", "db:setup", env: {}).join(" ")
end

test "execute in existing container with env" do
assert_equal \
"docker exec --env foo=\"bar\" app-web-999 bin/rails db:setup",
new_command.execute_in_existing_container("bin/rails", "db:setup", env: { "foo" => "bar" }).join(" ")
end

test "execute in new container over ssh" do
assert_match %r{docker run -it --rm --env-file .kamal/env/roles/app-web.env dhh/app:999 bin/rails c},
new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1")
new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1", env: {})
end

test "execute in new container with custom options over ssh" do
@config[:servers] = { "web" => { "hosts" => [ "1.1.1.1" ], "options" => { "mount" => "somewhere", "cap-add" => true } } }
assert_match %r{docker run -it --rm --env-file .kamal/env/roles/app-web.env --mount \"somewhere\" --cap-add dhh/app:999 bin/rails c},
new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1")
new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1", env: {})
end

test "execute in existing container over ssh" do
assert_match %r{docker exec -it app-web-999 bin/rails c},
new_command.execute_in_existing_container_over_ssh("bin/rails", "c", host: "app-1")
new_command.execute_in_existing_container_over_ssh("bin/rails", "c", host: "app-1", env: {})
end

test "run over ssh" do
Expand Down