Merge branch 'main' into default-to-deploying-config-version

This commit is contained in:
David Heinemeier Hansson
2023-03-23 14:42:31 +01:00
committed by GitHub
16 changed files with 336 additions and 128 deletions

View File

@@ -13,90 +13,79 @@ PATH
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionpack (7.0.4) actionpack (7.0.4.3)
actionview (= 7.0.4) actionview (= 7.0.4.3)
activesupport (= 7.0.4) activesupport (= 7.0.4.3)
rack (~> 2.0, >= 2.2.0) rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionview (7.0.4) actionview (7.0.4.3)
activesupport (= 7.0.4) activesupport (= 7.0.4.3)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0)
activesupport (7.0.4) activesupport (7.0.4.3)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
tzinfo (~> 2.0) tzinfo (~> 2.0)
bcrypt_pbkdf (1.1.0) bcrypt_pbkdf (1.1.0)
builder (3.2.4) builder (3.2.4)
concurrent-ruby (1.1.10) concurrent-ruby (1.2.2)
crass (1.0.6) crass (1.0.6)
debug (1.7.1) debug (1.7.1)
irb (>= 1.5.0)
reline (>= 0.3.1)
dotenv (2.8.1) dotenv (2.8.1)
ed25519 (1.3.0) ed25519 (1.3.0)
erubi (1.12.0) erubi (1.12.0)
i18n (1.12.0) i18n (1.12.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
io-console (0.6.0)
irb (1.6.2)
reline (>= 0.3.0)
loofah (2.19.1) loofah (2.19.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
method_source (1.0.0) method_source (1.0.0)
minitest (5.17.0) minitest (5.18.0)
mocha (2.0.2) mocha (2.0.2)
ruby2_keywords (>= 0.0.5) ruby2_keywords (>= 0.0.5)
net-scp (4.0.0) net-scp (4.0.0)
net-ssh (>= 2.6.5, < 8.0.0) net-ssh (>= 2.6.5, < 8.0.0)
net-ssh (7.0.1) net-ssh (7.1.0)
nokogiri (1.14.0-arm64-darwin) nokogiri (1.14.2-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.14.0-x86_64-darwin) nokogiri (1.14.2-x86_64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.14.0-x86_64-linux) nokogiri (1.14.2-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
racc (1.6.2) racc (1.6.2)
rack (2.2.5) rack (2.2.6.4)
rack-test (2.0.2) rack-test (2.1.0)
rack (>= 1.3) rack (>= 1.3)
rails-dom-testing (2.0.3) rails-dom-testing (2.0.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.4.4) rails-html-sanitizer (1.5.0)
loofah (~> 2.19, >= 2.19.1) loofah (~> 2.19, >= 2.19.1)
railties (7.0.4) railties (7.0.4.3)
actionpack (= 7.0.4) actionpack (= 7.0.4.3)
activesupport (= 7.0.4) activesupport (= 7.0.4.3)
method_source method_source
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0) thor (~> 1.0)
zeitwerk (~> 2.5) zeitwerk (~> 2.5)
rake (13.0.6) rake (13.0.6)
reline (0.3.2)
io-console (~> 0.5)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
sshkit (1.21.3) sshkit (1.21.4)
net-scp (>= 1.1.2) net-scp (>= 1.1.2)
net-ssh (>= 2.8.0) net-ssh (>= 2.8.0)
thor (1.2.1) thor (1.2.1)
tzinfo (2.0.5) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
zeitwerk (2.6.6) zeitwerk (2.6.7)
PLATFORMS PLATFORMS
arm64-darwin-20 arm64-darwin
arm64-darwin-21 x86_64-darwin
arm64-darwin-22
x86_64-darwin-20
x86_64-darwin-21
x86_64-darwin-22
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES

View File

@@ -4,7 +4,7 @@ MRSK deploys web apps anywhere from bare metal to cloud VMs using Docker with ze
Watch the screencast: https://www.youtube.com/watch?v=LL1cV2FXZ5I Watch the screencast: https://www.youtube.com/watch?v=LL1cV2FXZ5I
Join us on Discord: https://discord.gg/DQETs3Pm Join us on Discord: https://discord.gg/YgHVT7GCXS
## Installation ## Installation
@@ -426,6 +426,46 @@ traefik:
host_port: 8080 host_port: 8080
``` ```
### Configure docker options for traefik
We allow users to pass additional docker options to the trafik container like
```yaml
traefik:
options:
publish:
- 8080:8080
volumes:
- /tmp/example.json:/tmp/example.json
memory: 512m
```
This will start the traefik container with a command like: `docker run ... --volume /tmp/example.json:/tmp/example.json --publish 8080:8080 `
### Configure alternate entrypoints for traefik
You can configure multiple entrypoints for traefik like so:
```yaml
service: myservice
labels:
traefik.tcp.routers.other.rule: 'HostSNI(`*`)'
traefik.tcp.routers.other.entrypoints: otherentrypoint
traefik.tcp.services.other.loadbalancer.server.port: 9000
traefik.http.routers.myservice.entrypoints: web
traefik.http.services.myservice.loadbalancer.server.port: 8080
traefik:
options:
publish:
- 9000:9000
args:
entrypoints.web.address: ':80'
entrypoints.otherentrypoint.address: ':9000'
```
### Configuring build args for new images ### Configuring build args for new images
Build arguments that aren't secret can also be configured: Build arguments that aren't secret can also be configured:
@@ -445,7 +485,7 @@ FROM ruby:$RUBY_VERSION-slim as base
### Using accessories for database, cache, search services ### Using accessories for database, cache, search services
You can manage your accessory services via MRSK as well. The services will build off public images, and will not be automatically updated when you deploy: You can manage your accessory services via MRSK as well. Accessories are long-lived services that your app depends on. They are not updated when you deploy.
```yaml ```yaml
accessories: accessories:
@@ -466,10 +506,16 @@ accessories:
port: "36379:6379" port: "36379:6379"
volumes: volumes:
- /var/lib/redis:/data - /var/lib/redis:/data
internal-example:
image: registry.digitalocean.com/user/otherservice:latest
host: 1.1.1.5
port: 44444
``` ```
Now run `mrsk accessory start mysql` to start the MySQL server on 1.1.1.3. See `mrsk accessory` for all the commands possible. Now run `mrsk accessory start mysql` to start the MySQL server on 1.1.1.3. See `mrsk accessory` for all the commands possible.
Accessory images must be public or tagged in your private registry.
### Using Cron ### Using Cron
You can use a specific container to run your Cron jobs: You can use a specific container to run your Cron jobs:

View File

@@ -9,6 +9,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
upload(name) upload(name)
on(accessory.host) do on(accessory.host) do
execute *MRSK.registry.login
execute *MRSK.auditor.record("Booted #{name} accessory"), verbosity: :debug execute *MRSK.auditor.record("Booted #{name} accessory"), verbosity: :debug
execute *accessory.run execute *accessory.run
end end

View File

@@ -40,7 +40,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
invoke "mrsk:cli:prune:all", [], invoke_options invoke "mrsk:cli:prune:all", [], invoke_options
end end
audit_broadcast "Deployed app in #{runtime.to_i} seconds" unless options[:skip_broadcast] audit_broadcast "Deployed #{service_version} in #{runtime.round} seconds" unless options[:skip_broadcast]
end end
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting Traefik, pruning, and registry login" desc "redeploy", "Deploy app to servers without bootstrapping servers, starting Traefik, pruning, and registry login"
@@ -63,7 +63,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
invoke "mrsk:cli:app:boot", [], invoke_options invoke "mrsk:cli:app:boot", [], invoke_options
end end
audit_broadcast "Redeployed app in #{runtime.to_i} seconds" unless options[:skip_broadcast] audit_broadcast "Redeployed #{service_version} in #{runtime.round} seconds" unless options[:skip_broadcast]
end end
desc "rollback [VERSION]", "Rollback app to VERSION" desc "rollback [VERSION]", "Rollback app to VERSION"
@@ -74,18 +74,21 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
say "Start version #{version}, then wait #{MRSK.config.readiness_delay}s for app to boot before stopping the old version...", :magenta say "Start version #{version}, then wait #{MRSK.config.readiness_delay}s for app to boot before stopping the old version...", :magenta
cli = self cli = self
old_version = nil
on(MRSK.hosts) do |host| on(MRSK.hosts) do |host|
old_version = capture_with_info(*MRSK.app.current_running_version).strip.presence old_version = capture_with_info(*MRSK.app.current_running_version).strip.presence
execute *MRSK.app.start execute *MRSK.app.start
sleep MRSK.config.readiness_delay if old_version
sleep MRSK.config.readiness_delay
execute *MRSK.app.stop(version: old_version), raise_on_non_zero_exit: false execute *MRSK.app.stop(version: old_version), raise_on_non_zero_exit: false
end
end end
audit_broadcast "Rolled back app to version #{version}" unless options[:skip_broadcast] audit_broadcast "Rolled back #{service_version(Mrsk::Utils.abbreviate_version(old_version))} to #{service_version}" unless options[:skip_broadcast]
else else
say "The app version '#{version}' is not available as a container (use 'mrsk app containers' for available versions)", :red say "The app version '#{version}' is not available as a container (use 'mrsk app containers' for available versions)", :red
end end
@@ -207,4 +210,8 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
def deploy_options def deploy_options
{ "version" => MRSK.config.version }.merge(options.without("skip_push")) { "version" => MRSK.config.version }.merge(options.without("skip_push"))
end end
def service_version(version = MRSK.config.abbreviated_version)
[ MRSK.config.service, version ].compact.join("@")
end
end end

View File

@@ -1,6 +1,6 @@
class Mrsk::Commands::Accessory < Mrsk::Commands::Base class Mrsk::Commands::Accessory < Mrsk::Commands::Base
attr_reader :accessory_config attr_reader :accessory_config
delegate :service_name, :image, :host, :port, :files, :directories, :env_args, :volume_args, :label_args, to: :accessory_config delegate :service_name, :image, :host, :port, :files, :directories, :publish_args, :env_args, :volume_args, :label_args, to: :accessory_config
def initialize(config, name:) def initialize(config, name:)
super(config) super(config)
@@ -13,7 +13,7 @@ class Mrsk::Commands::Accessory < Mrsk::Commands::Base
"--detach", "--detach",
"--restart", "unless-stopped", "--restart", "unless-stopped",
"--log-opt", "max-size=#{MAX_LOG_SIZE}", "--log-opt", "max-size=#{MAX_LOG_SIZE}",
"--publish", port, *publish_args,
*env_args, *env_args,
*volume_args, *volume_args,
*label_args, *label_args,

View File

@@ -27,7 +27,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
end end
def info def info
docker :ps, *service_filter_with_destination docker :ps, *filter_args
end end
@@ -76,13 +76,13 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
def current_container_id def current_container_id
docker :ps, "--quiet", *service_filter_with_destination docker :ps, "--quiet", *filter_args
end end
def current_running_version def current_running_version
# FIXME: Find more graceful way to extract the version from "app-version" than using sed and tail! # FIXME: Find more graceful way to extract the version from "app-version" than using sed and tail!
pipe \ pipe \
docker(:ps, *service_filter_with_destination, "--format", '"{{.Names}}"'), docker(:ps, *filter_args, "--format", '"{{.Names}}"'),
%(sed 's/-/\\n/g'), %(sed 's/-/\\n/g'),
"tail -n 1" "tail -n 1"
end end
@@ -101,7 +101,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
def list_containers def list_containers
docker :container, :ls, "--all", *service_filter_with_destination docker :container, :ls, "--all", *filter_args
end end
def list_container_names def list_container_names
@@ -115,7 +115,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
end end
def remove_containers def remove_containers
docker :container, :prune, "--force", *service_filter_with_destination docker :container, :prune, "--force", *filter_args
end end
def list_images def list_images
@@ -123,7 +123,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
end end
def remove_images def remove_images
docker :image, :prune, "--all", "--force", *service_filter docker :image, :prune, "--all", "--force", *filter_args
end end
@@ -136,15 +136,13 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
container_id_for(container_name: service_with_version_and_destination(version)) container_id_for(container_name: service_with_version_and_destination(version))
end end
def service_filter def filter_args
[ "--filter", "label=service=#{config.service}" ] argumentize "--filter", filters
end end
def service_filter_with_destination def filters
if config.destination [ "label=service=#{config.service}" ].tap do |filters|
service_filter << "label=destination=#{config.destination}" filters << "label=destination=#{config.destination}" if config.destination
else
service_filter
end end
end end
end end

View File

@@ -1,6 +1,6 @@
module Mrsk::Commands module Mrsk::Commands
class Base class Base
delegate :redact, to: Mrsk::Utils delegate :redact, :argumentize, to: Mrsk::Utils
MAX_LOG_SIZE = "10m" MAX_LOG_SIZE = "10m"

View File

@@ -10,6 +10,7 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
"--log-opt", "max-size=#{MAX_LOG_SIZE}", "--log-opt", "max-size=#{MAX_LOG_SIZE}",
"--publish", port, "--publish", port,
"--volume", "/var/run/docker.sock:/var/run/docker.sock", "--volume", "/var/run/docker.sock:/var/run/docker.sock",
*docker_options_args,
"traefik", "traefik",
"--providers.docker", "--providers.docker",
"--log.level=DEBUG", "--log.level=DEBUG",
@@ -54,6 +55,10 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
end end
private private
def docker_options_args
optionize(config.traefik["options"] || {})
end
def cmd_option_args def cmd_option_args
if args = config.traefik["args"] if args = config.traefik["args"]
optionize args, with: "=" optionize args, with: "="

View File

@@ -15,15 +15,16 @@ class Mrsk::Configuration
class << self class << self
def create_from(base_config_file, destination: nil, version: "missing") def create_from(base_config_file, destination: nil, version: "missing")
new(load_config_file(base_config_file).tap do |config| raw_config = load_config_files(base_config_file, *destination_config_file(base_config_file, destination))
if destination
config.deep_merge! \ new raw_config, destination: destination, version: version
load_config_file destination_config_file(base_config_file, destination)
end
end, destination: destination, version: version)
end end
private private
def load_config_files(*files)
files.inject({}) { |config, file| config.deep_merge! load_config_file(file) }
end
def load_config_file(file) def load_config_file(file)
if file.exist? if file.exist?
YAML.load(ERB.new(IO.read(file)).result).symbolize_keys YAML.load(ERB.new(IO.read(file)).result).symbolize_keys
@@ -33,8 +34,7 @@ class Mrsk::Configuration
end end
def destination_config_file(base_config_file, destination) def destination_config_file(base_config_file, destination)
dir, basename = base_config_file.split base_config_file.sub_ext(".#{destination}.yml") if destination
dir.join basename.to_s.remove(".yml") + ".#{destination}.yml"
end end
end end
@@ -46,6 +46,11 @@ class Mrsk::Configuration
end end
def abbreviated_version
Mrsk::Utils.abbreviate_version(version)
end
def roles def roles
@roles ||= role_names.collect { |role_name| Role.new(role_name, config: self) } @roles ||= role_names.collect { |role_name| Role.new(role_name, config: self) }
end end

View File

@@ -20,13 +20,15 @@ class Mrsk::Configuration::Accessory
end end
def port def port
if specifics["port"].to_s.include?(":") if port = specifics["port"]&.to_s
specifics["port"] port.include?(":") ? port : "#{port}:#{port}"
else
"#{specifics["port"]}:#{specifics["port"]}"
end end
end end
def publish_args
argumentize "--publish", port if port
end
def labels def labels
default_labels.merge(specifics["labels"] || {}) default_labels.merge(specifics["labels"] || {})
end end

View File

@@ -26,14 +26,19 @@ module Mrsk::Utils
# Returns a list of shell-dashed option arguments. If the value is true, it's treated like a value-less option. # Returns a list of shell-dashed option arguments. If the value is true, it's treated like a value-less option.
def optionize(args, with: nil) def optionize(args, with: nil)
options = if with options = if with
args.collect { |(key, value)| value == true ? "--#{key}" : "--#{key}#{with}#{escape_shell_value(value)}" } flatten_args(args).collect { |(key, value)| value == true ? "--#{key}" : "--#{key}#{with}#{escape_shell_value(value)}" }
else else
args.collect { |(key, value)| [ "--#{key}", value == true ? nil : escape_shell_value(value) ] } flatten_args(args).collect { |(key, value)| [ "--#{key}", value == true ? nil : escape_shell_value(value) ] }
end end
options.flatten.compact options.flatten.compact
end end
# Flattens a one-to-many structure into an array of two-element arrays each containing a key-value pair
def flatten_args(args)
args.flat_map { |key, value| value.try(:map) { |entry| [key, entry] } || [ [ key, value ] ] }
end
# Copied from SSHKit::Backend::Abstract#redact to be available inside Commands classes # Copied from SSHKit::Backend::Abstract#redact to be available inside Commands classes
def redact(arg) # Used in execute_command to hide redact() args a user passes in def redact(arg) # Used in execute_command to hide redact() args a user passes in
arg.to_s.extend(SSHKit::Redaction) # to_s due to our inability to extend Integer, etc arg.to_s.extend(SSHKit::Redaction) # to_s due to our inability to extend Integer, etc
@@ -43,4 +48,9 @@ module Mrsk::Utils
def escape_shell_value(value) def escape_shell_value(value)
value.to_s.dump.gsub(/`/, '\\\\`') value.to_s.dump.gsub(/`/, '\\\\`')
end end
# Abbreviate a git revhash for concise display
def abbreviate_version(version)
version[0...7] if version
end
end end

View File

@@ -6,6 +6,7 @@ class CliAccessoryTest < CliTestCase
Mrsk::Cli::Accessory.any_instance.expects(:upload).with("mysql") Mrsk::Cli::Accessory.any_instance.expects(:upload).with("mysql")
run_command("boot", "mysql").tap do |output| run_command("boot", "mysql").tap do |output|
assert_match /docker login.*on 1.1.1.3/, output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=10m --publish 3306:3306 -e [REDACTED] -e MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=10m --publish 3306:3306 -e [REDACTED] -e MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
end end
end end
@@ -17,6 +18,8 @@ class CliAccessoryTest < CliTestCase
Mrsk::Cli::Accessory.any_instance.expects(:upload).with("redis") Mrsk::Cli::Accessory.any_instance.expects(:upload).with("redis")
run_command("boot", "all").tap do |output| run_command("boot", "all").tap do |output|
assert_match /docker login.*on 1.1.1.3/, output
assert_match /docker login.*on 1.1.1.4/, output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=10m --publish 3306:3306 -e [REDACTED] -e MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=10m --publish 3306:3306 -e [REDACTED] -e MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=10m --publish 6379:6379 --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.4", output assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=10m --publish 6379:6379 --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.4", output
end end

View File

@@ -88,11 +88,23 @@ class CliMainTest < CliTestCase
test "rollback good version" do test "rollback good version" do
Mrsk::Cli::Main.any_instance.stubs(:container_name_available?).returns(true) Mrsk::Cli::Main.any_instance.stubs(:container_name_available?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--format", "\"{{.Names}}\"", "|", "sed 's/-/\\n/g'", "|", "tail -n 1").returns("version-to-rollback\n").times(2)
run_command("rollback", "123").tap do |output| run_command("rollback", "123").tap do |output|
assert_match /Start version 123/, output assert_match "Start version 123", output
assert_match /docker ps -q --filter label=service=app | xargs docker stop/, output assert_match "docker start app-123", output
assert_match /docker start app-123/, output assert_match "docker container ls --all --filter name=app-version-to-rollback --quiet | xargs docker stop", output, "Should stop the container that was previously running"
end
end
test "rollback without old version" do
Mrsk::Cli::Main.any_instance.stubs(:container_name_available?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--format", "\"{{.Names}}\"", "|", "sed 's/-/\\n/g'", "|", "tail -n 1").returns("").times(2)
run_command("rollback", "123").tap do |output|
assert_match "Start version 123", output
assert_match "docker start app-123", output
assert_no_match "docker stop", output
end end
end end

View File

@@ -3,11 +3,11 @@ require "test_helper"
class CommandsAccessoryTest < ActiveSupport::TestCase class CommandsAccessoryTest < ActiveSupport::TestCase
setup do setup do
@config = { @config = {
service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, service: "app", image: "dhh/app", registry: { "server" => "private.registry", "username" => "dhh", "password" => "secret" },
servers: [ "1.1.1.1" ], servers: [ "1.1.1.1" ],
accessories: { accessories: {
"mysql" => { "mysql" => {
"image" => "mysql:8.0", "image" => "private.registry/mysql:8.0",
"host" => "1.1.1.5", "host" => "1.1.1.5",
"port" => "3306", "port" => "3306",
"env" => { "env" => {
@@ -32,13 +32,18 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
"volumes" => [ "volumes" => [
"/var/lib/redis:/data" "/var/lib/redis:/data"
] ]
},
"busybox" => {
"image" => "busybox:latest",
"host" => "1.1.1.7"
} }
} }
} }
@config = Mrsk::Configuration.new(@config) @config = Mrsk::Configuration.new(@config)
@mysql = Mrsk::Commands::Accessory.new(@config, name: :mysql) @mysql = Mrsk::Commands::Accessory.new(@config, name: :mysql)
@redis = Mrsk::Commands::Accessory.new(@config, name: :redis) @redis = Mrsk::Commands::Accessory.new(@config, name: :redis)
@busybox = Mrsk::Commands::Accessory.new(@config, name: :busybox)
ENV["MYSQL_ROOT_PASSWORD"] = "secret123" ENV["MYSQL_ROOT_PASSWORD"] = "secret123"
end end
@@ -49,12 +54,16 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
test "run" do test "run" do
assert_equal \ assert_equal \
"docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=10m --publish 3306:3306 -e MYSQL_ROOT_PASSWORD=\"secret123\" -e MYSQL_ROOT_HOST=\"%\" --label service=\"app-mysql\" mysql:8.0", "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=10m --publish 3306:3306 -e MYSQL_ROOT_PASSWORD=\"secret123\" -e MYSQL_ROOT_HOST=\"%\" --label service=\"app-mysql\" private.registry/mysql:8.0",
@mysql.run.join(" ") @mysql.run.join(" ")
assert_equal \ assert_equal \
"docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=10m --publish 6379:6379 -e SOMETHING=\"else\" --volume /var/lib/redis:/data --label service=\"app-redis\" --label cache=\"true\" redis:latest", "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=10m --publish 6379:6379 -e SOMETHING=\"else\" --volume /var/lib/redis:/data --label service=\"app-redis\" --label cache=\"true\" redis:latest",
@redis.run.join(" ") @redis.run.join(" ")
assert_equal \
"docker run --name app-busybox --detach --restart unless-stopped --log-opt max-size=10m --label service=\"app-busybox\" busybox:latest",
@busybox.run.join(" ")
end end
test "start" do test "start" do
@@ -78,7 +87,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
test "execute in new container" do test "execute in new container" do
assert_equal \ assert_equal \
"docker run --rm -e MYSQL_ROOT_PASSWORD=\"secret123\" -e MYSQL_ROOT_HOST=\"%\" mysql:8.0 mysql -u root", "docker run --rm -e MYSQL_ROOT_PASSWORD=\"secret123\" -e MYSQL_ROOT_HOST=\"%\" private.registry/mysql:8.0 mysql -u root",
@mysql.execute_in_new_container("mysql", "-u", "root").join(" ") @mysql.execute_in_new_container("mysql", "-u", "root").join(" ")
end end
@@ -90,7 +99,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
test "execute in new container over ssh" do test "execute in new container over ssh" do
@mysql.stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do @mysql.stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do
assert_match %r|docker run -it --rm -e MYSQL_ROOT_PASSWORD=\"secret123\" -e MYSQL_ROOT_HOST=\"%\" mysql:8.0 mysql -u root|, assert_match %r|docker run -it --rm -e MYSQL_ROOT_PASSWORD=\"secret123\" -e MYSQL_ROOT_HOST=\"%\" private.registry/mysql:8.0 mysql -u root|,
@mysql.execute_in_new_container_over_ssh("mysql", "-u", "root") @mysql.execute_in_new_container_over_ssh("mysql", "-u", "root")
end end
end end

View File

@@ -5,7 +5,6 @@ class CommandsAppTest < ActiveSupport::TestCase
ENV["RAILS_MASTER_KEY"] = "456" ENV["RAILS_MASTER_KEY"] = "456"
@config = { service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, servers: [ "1.1.1.1" ], env: { "secret" => [ "RAILS_MASTER_KEY" ] } } @config = { service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, servers: [ "1.1.1.1" ], env: { "secret" => [ "RAILS_MASTER_KEY" ] } }
@app = Mrsk::Commands::App.new Mrsk::Configuration.new(@config).tap { |c| c.version = "999" }
end end
teardown do teardown do
@@ -15,7 +14,7 @@ class CommandsAppTest < ActiveSupport::TestCase
test "run" do test "run" do
assert_equal \ assert_equal \
"docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e MRSK_CONTAINER_NAME=\"app-999\" -e RAILS_MASTER_KEY=\"456\" --label service=\"app\" --label role=\"web\" --label traefik.http.routers.app.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.services.app.loadbalancer.healthcheck.path=\"/up\" --label traefik.http.services.app.loadbalancer.healthcheck.interval=\"1s\" --label traefik.http.middlewares.app-retry.retry.attempts=\"5\" --label traefik.http.middlewares.app-retry.retry.initialinterval=\"500ms\" --label traefik.http.routers.app.middlewares=\"app-retry@docker\" dhh/app:999", "docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e MRSK_CONTAINER_NAME=\"app-999\" -e RAILS_MASTER_KEY=\"456\" --label service=\"app\" --label role=\"web\" --label traefik.http.routers.app.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.services.app.loadbalancer.healthcheck.path=\"/up\" --label traefik.http.services.app.loadbalancer.healthcheck.interval=\"1s\" --label traefik.http.middlewares.app-retry.retry.attempts=\"5\" --label traefik.http.middlewares.app-retry.retry.initialinterval=\"500ms\" --label traefik.http.routers.app.middlewares=\"app-retry@docker\" dhh/app:999",
@app.run.join(" ") new_command.run.join(" ")
end end
test "run with volumes" do test "run with volumes" do
@@ -23,7 +22,7 @@ class CommandsAppTest < ActiveSupport::TestCase
assert_equal \ assert_equal \
"docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e MRSK_CONTAINER_NAME=\"app-999\" -e RAILS_MASTER_KEY=\"456\" --volume /local/path:/container/path --label service=\"app\" --label role=\"web\" --label traefik.http.routers.app.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.services.app.loadbalancer.healthcheck.path=\"/up\" --label traefik.http.services.app.loadbalancer.healthcheck.interval=\"1s\" --label traefik.http.middlewares.app-retry.retry.attempts=\"5\" --label traefik.http.middlewares.app-retry.retry.initialinterval=\"500ms\" --label traefik.http.routers.app.middlewares=\"app-retry@docker\" dhh/app:999", "docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e MRSK_CONTAINER_NAME=\"app-999\" -e RAILS_MASTER_KEY=\"456\" --volume /local/path:/container/path --label service=\"app\" --label role=\"web\" --label traefik.http.routers.app.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.services.app.loadbalancer.healthcheck.path=\"/up\" --label traefik.http.services.app.loadbalancer.healthcheck.interval=\"1s\" --label traefik.http.middlewares.app-retry.retry.attempts=\"5\" --label traefik.http.middlewares.app-retry.retry.initialinterval=\"500ms\" --label traefik.http.routers.app.middlewares=\"app-retry@docker\" dhh/app:999",
@app.run.join(" ") new_command.run.join(" ")
end end
test "run with custom healthcheck path" do test "run with custom healthcheck path" do
@@ -31,148 +30,237 @@ class CommandsAppTest < ActiveSupport::TestCase
assert_equal \ assert_equal \
"docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e MRSK_CONTAINER_NAME=\"app-999\" -e RAILS_MASTER_KEY=\"456\" --label service=\"app\" --label role=\"web\" --label traefik.http.routers.app.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.services.app.loadbalancer.healthcheck.path=\"/healthz\" --label traefik.http.services.app.loadbalancer.healthcheck.interval=\"1s\" --label traefik.http.middlewares.app-retry.retry.attempts=\"5\" --label traefik.http.middlewares.app-retry.retry.initialinterval=\"500ms\" --label traefik.http.routers.app.middlewares=\"app-retry@docker\" dhh/app:999", "docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e MRSK_CONTAINER_NAME=\"app-999\" -e RAILS_MASTER_KEY=\"456\" --label service=\"app\" --label role=\"web\" --label traefik.http.routers.app.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.services.app.loadbalancer.healthcheck.path=\"/healthz\" --label traefik.http.services.app.loadbalancer.healthcheck.interval=\"1s\" --label traefik.http.middlewares.app-retry.retry.attempts=\"5\" --label traefik.http.middlewares.app-retry.retry.initialinterval=\"500ms\" --label traefik.http.routers.app.middlewares=\"app-retry@docker\" dhh/app:999",
@app.run.join(" ") new_command.run.join(" ")
end end
test "run with custom options" do 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 } } } @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 \ assert_equal \
"docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e MRSK_CONTAINER_NAME=\"app-999\" -e RAILS_MASTER_KEY=\"456\" --label service=\"app\" --label role=\"jobs\" --mount \"somewhere\" --cap-add dhh/app:999 bin/jobs", "docker run --detach --restart unless-stopped --log-opt max-size=10m --name app-999 -e MRSK_CONTAINER_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(" ") new_command.run(role: :jobs).join(" ")
end end
test "start" do test "start" do
assert_equal \ assert_equal \
"docker start app-999", "docker start app-999",
@app.start.join(" ") new_command.start.join(" ")
end
test "start with destination" do
@destination = "staging"
assert_equal \
"docker start app-staging-999",
new_command.start.join(" ")
end end
test "stop" do test "stop" do
assert_equal \ assert_equal \
"docker ps --quiet --filter label=service=app | xargs docker stop", "docker ps --quiet --filter label=service=app | xargs docker stop",
@app.stop.join(" ") new_command.stop.join(" ")
end
test "stop with version" do
assert_equal \
"docker container ls --all --filter name=app-123 --quiet | xargs docker stop",
new_command.stop(version: "123").join(" ")
end end
test "info" do test "info" do
assert_equal \ assert_equal \
"docker ps --filter label=service=app", "docker ps --filter label=service=app",
@app.info.join(" ") new_command.info.join(" ")
end
test "info with destination" do
@destination = "staging"
assert_equal \
"docker ps --filter label=service=app --filter label=destination=staging",
new_command.info.join(" ")
end end
test "logs" do test "logs" do
assert_equal \ assert_equal \
"docker ps --quiet --filter label=service=app | xargs docker logs 2>&1", "docker ps --quiet --filter label=service=app | xargs docker logs 2>&1",
@app.logs.join(" ") new_command.logs.join(" ")
assert_equal \ assert_equal \
"docker ps --quiet --filter label=service=app | xargs docker logs --since 5m 2>&1", "docker ps --quiet --filter label=service=app | xargs docker logs --since 5m 2>&1",
@app.logs(since: "5m").join(" ") new_command.logs(since: "5m").join(" ")
assert_equal \ assert_equal \
"docker ps --quiet --filter label=service=app | xargs docker logs --tail 100 2>&1", "docker ps --quiet --filter label=service=app | xargs docker logs --tail 100 2>&1",
@app.logs(lines: "100").join(" ") new_command.logs(lines: "100").join(" ")
assert_equal \ assert_equal \
"docker ps --quiet --filter label=service=app | xargs docker logs --since 5m --tail 100 2>&1", "docker ps --quiet --filter label=service=app | xargs docker logs --since 5m --tail 100 2>&1",
@app.logs(since: "5m", lines: "100").join(" ") new_command.logs(since: "5m", lines: "100").join(" ")
assert_equal \ assert_equal \
"docker ps --quiet --filter label=service=app | xargs docker logs 2>&1 | grep 'my-id'", "docker ps --quiet --filter label=service=app | xargs docker logs 2>&1 | grep 'my-id'",
@app.logs(grep: "my-id").join(" ") new_command.logs(grep: "my-id").join(" ")
assert_equal \ assert_equal \
"docker ps --quiet --filter label=service=app | xargs docker logs --since 5m 2>&1 | grep 'my-id'", "docker ps --quiet --filter label=service=app | xargs docker logs --since 5m 2>&1 | grep 'my-id'",
@app.logs(since: "5m", grep: "my-id").join(" ") new_command.logs(since: "5m", grep: "my-id").join(" ")
end end
test "follow logs" do test "follow logs" do
@app.stub(:run_over_ssh, ->(cmd, host:) { cmd.join(" ") }) do assert_match \
assert_equal \ "docker ps --quiet --filter label=service=app | xargs docker logs --timestamps --tail 10 --follow 2>&1",
"docker ps --quiet --filter label=service=app | xargs docker logs --timestamps --tail 10 --follow 2>&1", new_command.follow_logs(host: "app-1")
@app.follow_logs(host: "app-1")
assert_equal \ assert_match \
"docker ps --quiet --filter label=service=app | xargs docker logs --timestamps --tail 10 --follow 2>&1 | grep \"Completed\"", "docker ps --quiet --filter label=service=app | xargs docker logs --timestamps --tail 10 --follow 2>&1 | grep \"Completed\"",
@app.follow_logs(host: "app-1", grep: "Completed") new_command.follow_logs(host: "app-1", grep: "Completed")
end
end end
test "execute in new container" do test "execute in new container" do
assert_equal \ assert_equal \
"docker run --rm -e RAILS_MASTER_KEY=\"456\" dhh/app:999 bin/rails db:setup", "docker run --rm -e RAILS_MASTER_KEY=\"456\" dhh/app:999 bin/rails db:setup",
@app.execute_in_new_container("bin/rails", "db:setup").join(" ") new_command.execute_in_new_container("bin/rails", "db:setup").join(" ")
end end
test "execute in existing container" do test "execute in existing container" do
assert_equal \ assert_equal \
"docker exec app-999 bin/rails db:setup", "docker exec app-999 bin/rails db:setup",
@app.execute_in_existing_container("bin/rails", "db:setup").join(" ") new_command.execute_in_existing_container("bin/rails", "db:setup").join(" ")
end end
test "execute in new container over ssh" do test "execute in new container over ssh" do
@app.stub(:run_over_ssh, ->(cmd, host:) { cmd.join(" ") }) do assert_match %r|docker run -it --rm -e RAILS_MASTER_KEY=\"456\" dhh/app:999 bin/rails c|,
assert_match %r|docker run -it --rm -e RAILS_MASTER_KEY=\"456\" dhh/app:999 bin/rails c|, new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1")
@app.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1")
end
end end
test "execute in existing container over ssh" do test "execute in existing container over ssh" do
@app.stub(:run_over_ssh, ->(cmd, host:) { cmd.join(" ") }) do assert_match %r|docker exec -it app-999 bin/rails c|,
assert_match %r|docker exec -it app-999 bin/rails c|, new_command.execute_in_existing_container_over_ssh("bin/rails", "c", host: "app-1")
@app.execute_in_existing_container_over_ssh("bin/rails", "c", host: "app-1")
end
end end
test "run over ssh" do test "run over ssh" do
assert_equal "ssh -t root@1.1.1.1 'ls'", @app.run_over_ssh("ls", host: "1.1.1.1") assert_equal "ssh -t root@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
end end
test "run over ssh with custom user" do test "run over ssh with custom user" do
@app = Mrsk::Commands::App.new Mrsk::Configuration.new(@config.tap { |c| c[:ssh] = { "user" => "app" } }) @config[:ssh] = { "user" => "app" }
assert_equal "ssh -t app@1.1.1.1 'ls'", @app.run_over_ssh("ls", host: "1.1.1.1") assert_equal "ssh -t app@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
end end
test "run over ssh with proxy" do test "run over ssh with proxy" do
@app = Mrsk::Commands::App.new Mrsk::Configuration.new(@config.tap { |c| c[:ssh] = { "proxy" => "2.2.2.2" } }) @config[:ssh] = { "proxy" => "2.2.2.2" }
assert_equal "ssh -J root@2.2.2.2 -t root@1.1.1.1 'ls'", @app.run_over_ssh("ls", host: "1.1.1.1") assert_equal "ssh -J root@2.2.2.2 -t root@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
end end
test "run over ssh with proxy user" do test "run over ssh with proxy user" do
@app = Mrsk::Commands::App.new Mrsk::Configuration.new(@config.tap { |c| c[:ssh] = { "proxy" => "app@2.2.2.2" } }) @config[:ssh] = { "proxy" => "app@2.2.2.2" }
assert_equal "ssh -J app@2.2.2.2 -t root@1.1.1.1 'ls'", @app.run_over_ssh("ls", host: "1.1.1.1") assert_equal "ssh -J app@2.2.2.2 -t root@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
end end
test "run over ssh with custom user with proxy" do test "run over ssh with custom user with proxy" do
@app = Mrsk::Commands::App.new Mrsk::Configuration.new(@config.tap { |c| c[:ssh] = { "user" => "app", "proxy" => "2.2.2.2" } }) @config[:ssh] = { "user" => "app", "proxy" => "2.2.2.2" }
assert_equal "ssh -J root@2.2.2.2 -t app@1.1.1.1 'ls'", @app.run_over_ssh("ls", host: "1.1.1.1") assert_equal "ssh -J root@2.2.2.2 -t app@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
end end
test "current_container_id" do test "current_container_id" do
assert_equal \ assert_equal \
"docker ps --quiet --filter label=service=app", "docker ps --quiet --filter label=service=app",
@app.current_container_id.join(" ") new_command.current_container_id.join(" ")
end
test "current_container_id with destination" do
@destination = "staging"
assert_equal \
"docker ps --quiet --filter label=service=app --filter label=destination=staging",
new_command.current_container_id.join(" ")
end end
test "container_id_for" do test "container_id_for" do
assert_equal \ assert_equal \
"docker container ls --all --filter name=app-999 --quiet", "docker container ls --all --filter name=app-999 --quiet",
@app.container_id_for(container_name: "app-999").join(" ") new_command.container_id_for(container_name: "app-999").join(" ")
end end
test "current_running_version" do test "current_running_version" do
assert_equal \ assert_equal \
"docker ps --filter label=service=app --format \"{{.Names}}\" | sed 's/-/\\n/g' | tail -n 1", "docker ps --filter label=service=app --format \"{{.Names}}\" | sed 's/-/\\n/g' | tail -n 1",
@app.current_running_version.join(" ") new_command.current_running_version.join(" ")
end end
test "most_recent_version_from_available_images" do test "most_recent_version_from_available_images" do
assert_equal \ assert_equal \
"docker image ls --format \"{{.Tag}}\" dhh/app | head -n 1", "docker image ls --format \"{{.Tag}}\" dhh/app | head -n 1",
@app.most_recent_version_from_available_images.join(" ") new_command.most_recent_version_from_available_images.join(" ")
end end
test "list_containers" do
assert_equal \
"docker container ls --all --filter label=service=app",
new_command.list_containers.join(" ")
end
test "list_containers with destination" do
@destination = "staging"
assert_equal \
"docker container ls --all --filter label=service=app --filter label=destination=staging",
new_command.list_containers.join(" ")
end
test "list_container_names" do
assert_equal \
"docker container ls --all --filter label=service=app --format '{{ .Names }}'",
new_command.list_container_names.join(" ")
end
test "remove_container" do
assert_equal \
"docker container ls --all --filter name=app-999 --quiet | xargs docker container rm",
new_command.remove_container(version: "999").join(" ")
end
test "remove_container with destination" do
@destination = "staging"
assert_equal \
"docker container ls --all --filter name=app-staging-999 --quiet | xargs docker container rm",
new_command.remove_container(version: "999").join(" ")
end
test "remove_containers" do
assert_equal \
"docker container prune --force --filter label=service=app",
new_command.remove_containers.join(" ")
end
test "remove_containers with destination" do
@destination = "staging"
assert_equal \
"docker container prune --force --filter label=service=app --filter label=destination=staging",
new_command.remove_containers.join(" ")
end
test "list_images" do
assert_equal \
"docker image ls dhh/app",
new_command.list_images.join(" ")
end
test "remove_images" do
assert_equal \
"docker image prune --all --force --filter label=service=app",
new_command.remove_images.join(" ")
end
test "remove_images with destination" do
@destination = "staging"
assert_equal \
"docker image prune --all --force --filter label=service=app --filter label=destination=staging",
new_command.remove_images.join(" ")
end
private
def new_command
Mrsk::Commands::App.new(Mrsk::Configuration.new(@config, destination: @destination, version: "999"))
end
end end

View File

@@ -19,6 +19,39 @@ class CommandsTraefikTest < ActiveSupport::TestCase
new_command.run.join(" ") new_command.run.join(" ")
end end
test "run with ports configured" do
assert_equal \
"docker run --name traefik --detach --restart unless-stopped --log-opt max-size=10m --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock traefik --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ")
@config[:traefik]["options"] = {"publish" => %w[9000:9000 9001:9001]}
assert_equal \
"docker run --name traefik --detach --restart unless-stopped --log-opt max-size=10m --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --publish \"9000:9000\" --publish \"9001:9001\" traefik --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ")
end
test "run with volumes configured" do
assert_equal \
"docker run --name traefik --detach --restart unless-stopped --log-opt max-size=10m --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock traefik --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ")
@config[:traefik]["options"] = {"volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json] }
assert_equal \
"docker run --name traefik --detach --restart unless-stopped --log-opt max-size=10m --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" traefik --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ")
end
test "run with several options configured" do
assert_equal \
"docker run --name traefik --detach --restart unless-stopped --log-opt max-size=10m --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock traefik --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ")
@config[:traefik]["options"] = {"volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json], "publish" => %w[8080:8080], "memory" => "512m"}
assert_equal \
"docker run --name traefik --detach --restart unless-stopped --log-opt max-size=10m --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" --publish \"8080:8080\" --memory \"512m\" traefik --providers.docker --log.level=DEBUG --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ")
end
test "run without configuration" do test "run without configuration" do
@config.delete(:traefik) @config.delete(:traefik)