diff --git a/README.md b/README.md index 286a0b6e..f54dc3e4 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,27 @@ servers: my-label: "50" ``` +### Using container options + +You can specialize the options used to start containers using the `options` definitions: + +```yaml +servers: + web: + - 192.168.0.1 + - 192.168.0.2 + job: + hosts: + - 192.168.0.3 + - 192.168.0.4 + cmd: bin/jobs + options: + cap-add: true + cpu-count: 4 +``` + +That'll start the job containers with `docker run ... --cap-add --cpu-count 4 ...`. + ### Using remote builder for native multi-arch If you're developing on ARM64 (like Apple Silicon), but you want to deploy on AMD64 (x86 64-bit), you can use multi-architecture images. By default, MRSK will setup a local buildx configuration that does this through QEMU emulation. But this can be quite slow, especially on the first build. diff --git a/lib/mrsk/commands/app.rb b/lib/mrsk/commands/app.rb index 4b1de91a..72c8c4dd 100644 --- a/lib/mrsk/commands/app.rb +++ b/lib/mrsk/commands/app.rb @@ -10,6 +10,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base *role.env_args, *config.volume_args, *role.label_args, + *role.option_args, config.absolute_image, role.cmd end diff --git a/lib/mrsk/commands/base.rb b/lib/mrsk/commands/base.rb index a9815c91..e453dddc 100644 --- a/lib/mrsk/commands/base.rb +++ b/lib/mrsk/commands/base.rb @@ -1,6 +1,6 @@ module Mrsk::Commands class Base - delegate :redact, :argumentize_for_cmd, to: Mrsk::Utils + delegate :redact, to: Mrsk::Utils MAX_LOG_SIZE = "10m" diff --git a/lib/mrsk/commands/traefik.rb b/lib/mrsk/commands/traefik.rb index 34e9c907..ee22e64b 100644 --- a/lib/mrsk/commands/traefik.rb +++ b/lib/mrsk/commands/traefik.rb @@ -1,4 +1,6 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base + delegate :optionize, to: Mrsk::Utils + CONTAINER_PORT = 80 def run @@ -11,7 +13,7 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base "traefik", "--providers.docker", "--log.level=DEBUG", - *cmd_args + *cmd_option_args end def start @@ -52,9 +54,9 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base end private - def cmd_args + def cmd_option_args if args = config.raw_config.dig(:traefik, "args") - argumentize_for_cmd args + optionize args else [] end diff --git a/lib/mrsk/configuration/role.rb b/lib/mrsk/configuration/role.rb index 1795649b..69d84be3 100644 --- a/lib/mrsk/configuration/role.rb +++ b/lib/mrsk/configuration/role.rb @@ -1,5 +1,5 @@ class Mrsk::Configuration::Role - delegate :argumentize, :argumentize_env_with_secrets, to: Mrsk::Utils + delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils attr_accessor :name @@ -35,6 +35,14 @@ class Mrsk::Configuration::Role specializations["cmd"] end + def option_args + if args = specializations["options"] + optionize args + else + [] + end + end + def running_traefik? name.web? || specializations["traefik"] end diff --git a/lib/mrsk/utils.rb b/lib/mrsk/utils.rb index 62a193ea..d8860f1a 100644 --- a/lib/mrsk/utils.rb +++ b/lib/mrsk/utils.rb @@ -23,9 +23,9 @@ module Mrsk::Utils end end - # Returns a list of shell-dashed arguments to be used to start a command. - def argumentize_for_cmd(args) - args.collect { |(key, value)| [ "--#{key}", escape_shell_value(value) ] }.flatten + # Returns a list of shell-dashed option arguments. If the value is true, it's treated like a value-less option. + def optionize(args) + args.collect { |(key, value)| [ "--#{key}", value == true ? nil : escape_shell_value(value) ] }.flatten.compact end # Copied from SSHKit::Backend::Abstract#redact to be available inside Commands classes diff --git a/test/commands/app_test.rb b/test/commands/app_test.rb index a62ab80c..da436bcb 100644 --- a/test/commands/app_test.rb +++ b/test/commands/app_test.rb @@ -34,6 +34,15 @@ class CommandsAppTest < ActiveSupport::TestCase @app.run.join(" ") end + test "run with custom options" do + @config[:servers] = { "web" => [ "1.1.1.1" ], "jobs" => { "hosts" => [ "1.1.1.2" ], "cmd" => "bin/jobs", "options" => { "mount" => "somewhere", "cap-add" => true } } } + @app = Mrsk::Commands::App.new Mrsk::Configuration.new(@config).tap { |c| c.version = "999" } + + assert_equal \ + "docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e RAILS_MASTER_KEY=\"456\" --label service=\"app\" --label role=\"jobs\" --mount \"somewhere\" --cap-add dhh/app:999 bin/jobs", + @app.run(role: :jobs).join(" ") + end + test "start" do assert_equal \ "docker start app-999",