Merge branch 'main' into global-logging-config

This commit is contained in:
Samuel Sieg
2023-03-24 08:35:43 +01:00
22 changed files with 353 additions and 148 deletions

View File

@@ -6,7 +6,8 @@ class CliAccessoryTest < CliTestCase
Mrsk::Cli::Accessory.any_instance.expects(:upload).with("mysql")
run_command("boot", "mysql").tap do |output|
assert_match "docker run --name app-mysql --detach --restart unless-stopped --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 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
end
end
@@ -17,8 +18,10 @@ class CliAccessoryTest < CliTestCase
Mrsk::Cli::Accessory.any_instance.expects(:upload).with("redis")
run_command("boot", "all").tap do |output|
assert_match "docker run --name app-mysql --detach --restart unless-stopped --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 --publish 6379:6379 --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.4", 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-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
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|
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 "Start version 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
@@ -114,7 +126,7 @@ class CliMainTest < CliTestCase
end
test "config" do
run_command("config").tap do |output|
run_command("config", config_file: "deploy_with_accessories").tap do |output|
config = YAML.load(output)
assert_equal ["web"], config[:roles]
@@ -126,6 +138,32 @@ class CliMainTest < CliTestCase
end
end
test "config with roles" do
run_command("config", config_file: "deploy_with_roles").tap do |output|
config = YAML.load(output)
assert_equal ["web", "workers"], config[:roles]
assert_equal ["1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4"], config[:hosts]
assert_equal "999", config[:version]
assert_equal "registry.digitalocean.com/dhh/app", config[:repository]
assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image]
assert_equal "app-999", config[:service_with_version]
end
end
test "config with destination" do
run_command("config", "-d", "world", config_file: "deploy_for_dest").tap do |output|
config = YAML.load(output)
assert_equal ["web"], config[:roles]
assert_equal ["1.1.1.1", "1.1.1.2"], config[:hosts]
assert_equal "999", config[:version]
assert_equal "registry.digitalocean.com/dhh/app", config[:repository]
assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image]
assert_equal "app-999", config[:service_with_version]
end
end
test "init" do
Pathname.any_instance.expects(:exist?).returns(false).twice
FileUtils.stubs(:mkdir_p)
@@ -215,7 +253,7 @@ class CliMainTest < CliTestCase
end
private
def run_command(*command)
stdouted { Mrsk::Cli::Main.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) }
def run_command(*command, config_file: "deploy_with_accessories")
stdouted { Mrsk::Cli::Main.start([*command, "-c", "test/fixtures/#{config_file}.yml"]) }
end
end

View File

@@ -2,23 +2,15 @@ require "test_helper"
class CommanderTest < ActiveSupport::TestCase
setup do
@mrsk = Mrsk::Commander.new config_file: Pathname.new(File.expand_path("fixtures/deploy_with_roles.yml", __dir__))
@mrsk = Mrsk::Commander.new.tap do |mrsk|
mrsk.configure config_file: Pathname.new(File.expand_path("fixtures/deploy_with_roles.yml", __dir__))
end
end
test "lazy configuration" do
assert_equal Mrsk::Configuration, @mrsk.config.class
end
test "commit hash as version" do
assert_equal `git rev-parse HEAD`.strip, @mrsk.config.version
end
test "commit hash as version but not in git" do
@mrsk.expects(:system).with("git rev-parse").returns(nil)
error = assert_raises(RuntimeError) { @mrsk.config }
assert_match /no git repository found/, error.message
end
test "overwriting hosts" do
assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], @mrsk.hosts

View File

@@ -3,11 +3,11 @@ require "test_helper"
class CommandsAccessoryTest < ActiveSupport::TestCase
setup do
@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" ],
accessories: {
"mysql" => {
"image" => "mysql:8.0",
"image" => "private.registry/mysql:8.0",
"host" => "1.1.1.5",
"port" => "3306",
"env" => {
@@ -32,6 +32,10 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
"volumes" => [
"/var/lib/redis:/data"
]
},
"busybox" => {
"image" => "busybox:latest",
"host" => "1.1.1.7"
}
}
}
@@ -45,24 +49,16 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
test "run" do
assert_equal \
"docker run --name app-mysql --detach --restart unless-stopped --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",
new_command(:mysql).run.join(" ")
assert_equal \
"docker run --name app-redis --detach --restart unless-stopped --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",
new_command(:redis).run.join(" ")
end
test "run with logging config" do
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "10m", "max-file" => "3" } }
assert_equal \
"docker run --name app-mysql --detach --restart unless-stopped --publish 3306:3306 --log-driver local --log-opt max-size=\"10m\" --log-opt max-file=\"3\" -e MYSQL_ROOT_PASSWORD=\"secret123\" -e MYSQL_ROOT_HOST=\"%\" --label service=\"app-mysql\" mysql:8.0",
new_command(:mysql).run.join(" ")
assert_equal \
"docker run --name app-redis --detach --restart unless-stopped --publish 6379:6379 --log-driver local --log-opt max-size=\"10m\" --log-opt max-file=\"3\" -e SOMETHING=\"else\" --volume /var/lib/redis:/data --label service=\"app-redis\" --label cache=\"true\" redis:latest",
new_command(:redis).run.join(" ")
"docker run --name app-busybox --detach --restart unless-stopped --log-opt max-size=10m --label service=\"app-busybox\" busybox:latest",
new_command(:busybox).run.join(" ")
end
test "start" do
@@ -86,7 +82,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
test "execute in new container" do
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",
new_command(:mysql).execute_in_new_container("mysql", "-u", "root").join(" ")
end
@@ -97,8 +93,8 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
end
test "execute in new container over ssh" do
new_command(: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|,
@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=\"%\" private.registry/mysql:8.0 mysql -u root|,
new_command(:mysql).execute_in_new_container_over_ssh("mysql", "-u", "root")
end
end

View File

@@ -19,6 +19,39 @@ class CommandsTraefikTest < ActiveSupport::TestCase
new_command.run.join(" ")
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
@config.delete(:traefik)

View File

@@ -3,6 +3,7 @@ require "test_helper"
class ConfigurationTest < ActiveSupport::TestCase
setup do
ENV["RAILS_MASTER_KEY"] = "456"
ENV["VERSION"] = "missing"
@deploy = {
service: "app", image: "dhh/app",
@@ -22,17 +23,23 @@ class ConfigurationTest < ActiveSupport::TestCase
end
teardown do
ENV["RAILS_MASTER_KEY"] = nil
ENV.delete("RAILS_MASTER_KEY")
ENV.delete("VERSION")
end
test "ensure valid keys" do
assert_raise(ArgumentError) do
Mrsk::Configuration.new(@deploy.tap { _1.delete(:service) })
Mrsk::Configuration.new(@deploy.tap { _1.delete(:image) })
Mrsk::Configuration.new(@deploy.tap { _1.delete(:registry) })
%i[ service image registry ].each do |key|
test "#{key} config required" do
assert_raise(ArgumentError) do
Mrsk::Configuration.new @deploy.tap { _1.delete key }
end
end
end
Mrsk::Configuration.new(@deploy.tap { _1[:registry].delete("username") })
Mrsk::Configuration.new(@deploy.tap { _1[:registry].delete("password") })
%w[ username password ].each do |key|
test "registry #{key} required" do
assert_raise(ArgumentError) do
Mrsk::Configuration.new @deploy.tap { _1[:registry].delete key }
end
end
end
@@ -67,8 +74,20 @@ class ConfigurationTest < ActiveSupport::TestCase
end
test "version" do
assert_equal "missing", @config.version
assert_equal "123", Mrsk::Configuration.new(@deploy, version: "123").version
ENV.delete("VERSION")
@config.expects(:system).with("git rev-parse").returns(nil)
error = assert_raises(RuntimeError) { @config.version}
assert_match /no git repository found/, error.message
@config.expects(:current_commit_hash).returns("git-version")
assert_equal "git-version", @config.version
ENV["VERSION"] = "env-version"
assert_equal "env-version", @config.version
@config.version = "arg-version"
assert_equal "arg-version", @config.version
end
test "repository" do
@@ -135,6 +154,39 @@ class ConfigurationTest < ActiveSupport::TestCase
test "valid config" do
assert @config.valid?
assert @config_with_roles.valid?
end
test "hosts required for all roles" do
# Empty server list for implied web role
assert_raises(ArgumentError) do
Mrsk::Configuration.new @deploy.merge(servers: [])
end
# Empty server list
assert_raises(ArgumentError) do
Mrsk::Configuration.new @deploy.merge(servers: { "web" => [] })
end
# Missing hosts key
assert_raises(ArgumentError) do
Mrsk::Configuration.new @deploy.merge(servers: { "web" => {} })
end
# Empty hosts list
assert_raises(ArgumentError) do
Mrsk::Configuration.new @deploy.merge(servers: { "web" => { "hosts" => [] } })
end
# Nil hosts
assert_raises(ArgumentError) do
Mrsk::Configuration.new @deploy.merge(servers: { "web" => { "hosts" => nil } })
end
# One role with hosts, one without
assert_raises(ArgumentError) do
Mrsk::Configuration.new @deploy.merge(servers: { "web" => %w[ web ], "workers" => { "hosts" => %w[ ] } })
end
end
test "ssh options" do
@@ -163,17 +215,17 @@ class ConfigurationTest < ActiveSupport::TestCase
end
test "erb evaluation of yml config" do
config = Mrsk::Configuration.create_from Pathname.new(File.expand_path("fixtures/deploy.erb.yml", __dir__))
config = Mrsk::Configuration.create_from config_file: Pathname.new(File.expand_path("fixtures/deploy.erb.yml", __dir__))
assert_equal "my-user", config.registry["username"]
end
test "destination yml config merge" do
dest_config_file = Pathname.new(File.expand_path("fixtures/deploy_for_dest.yml", __dir__))
config = Mrsk::Configuration.create_from dest_config_file, destination: "world"
config = Mrsk::Configuration.create_from config_file: dest_config_file, destination: "world"
assert_equal "1.1.1.1", config.all_hosts.first
config = Mrsk::Configuration.create_from dest_config_file, destination: "mars"
config = Mrsk::Configuration.create_from config_file: dest_config_file, destination: "mars"
assert_equal "1.1.1.3", config.all_hosts.first
end
@@ -181,7 +233,7 @@ class ConfigurationTest < ActiveSupport::TestCase
dest_config_file = Pathname.new(File.expand_path("fixtures/deploy_for_dest.yml", __dir__))
assert_raises(RuntimeError) do
config = Mrsk::Configuration.create_from dest_config_file, destination: "missing"
config = Mrsk::Configuration.create_from config_file: dest_config_file, destination: "missing"
end
end

View File

@@ -5,8 +5,9 @@ servers:
- 1.1.1.1
- 1.1.1.2
workers:
- 1.1.1.3
- 1.1.1.4
hosts:
- 1.1.1.3
- 1.1.1.4
env:
REDIS_URL: redis://x/y
registry: