From 4bf77ccd1b1e7b36b9b928c5332d16b3c7ab92cc Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Fri, 10 Mar 2023 11:26:35 +0100 Subject: [PATCH 01/14] Allow `deploy`/`deliver` without building and pushing the image --- lib/mrsk/cli/build.rb | 3 ++- lib/mrsk/cli/main.rb | 13 +++++++------ test/cli/build_test.rb | 25 +++++++++++++++++++++++-- test/test_helper.rb | 10 ++++++++++ 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/lib/mrsk/cli/build.rb b/lib/mrsk/cli/build.rb index 3e8912d7..ec39282e 100644 --- a/lib/mrsk/cli/build.rb +++ b/lib/mrsk/cli/build.rb @@ -1,7 +1,8 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base desc "deliver", "Build app and push app image to registry then pull image on servers" + option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push" def deliver - push + push unless options[:skip_push] pull end diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index 73268f50..163c0327 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -9,27 +9,28 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base end desc "deploy", "Deploy app to servers" + option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push" def deploy runtime = print_runtime do say "Ensure curl and Docker are installed...", :magenta - invoke "mrsk:cli:server:bootstrap" + invoke "mrsk:cli:server:bootstrap", [], options.without(:skip_push) say "Log into image registry...", :magenta - invoke "mrsk:cli:registry:login" + invoke "mrsk:cli:registry:login", [], options.without(:skip_push) say "Build and push app image...", :magenta invoke "mrsk:cli:build:deliver" say "Ensure Traefik is running...", :magenta - invoke "mrsk:cli:traefik:boot" + invoke "mrsk:cli:traefik:boot", [], options.without(:skip_push) say "Ensure app can pass healthcheck...", :magenta - invoke "mrsk:cli:healthcheck:perform" + invoke "mrsk:cli:healthcheck:perform", [], options.without(:skip_push) - invoke "mrsk:cli:app:boot" + invoke "mrsk:cli:app:boot", [], options.without(:skip_push) say "Prune old containers and images...", :magenta - invoke "mrsk:cli:prune:all" + invoke "mrsk:cli:prune:all", [], options.without(:skip_push) end audit_broadcast "Deployed app in #{runtime.to_i} seconds" unless options[:skip_broadcast] diff --git a/test/cli/build_test.rb b/test/cli/build_test.rb index 3d5f4431..2ded0193 100644 --- a/test/cli/build_test.rb +++ b/test/cli/build_test.rb @@ -1,10 +1,31 @@ require_relative "cli_test_case" class CliBuildTest < CliTestCase + test "deliver" do + run_command("deliver").tap do |output| + assert_match /docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder mrsk-app-multiarch -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile \. as .*\@localhost/, output + assert_match /docker image rm --force dhh\/app:999 on 1\.1\.1\.2/, output + assert_match /docker pull dhh\/app:999 on 1\.1\.1\.1/, output + end + end + + test "deliver without push" do + run_command("deliver", "--skip-push").tap do |output| + assert_match /docker image rm --force dhh\/app:999 on 1\.1\.1\.2/, output + assert_match /docker pull dhh\/app:999 on 1\.1\.1\.1/, output + end + end + + test "push" do + run_command("push").tap do |output| + assert_match /docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder mrsk-app-multiarch -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile \. as .*\@localhost/, output + end + end + test "pull" do run_command("pull").tap do |output| - assert_match /docker image rm --force dhh\/app:999/, output - assert_match /docker pull dhh\/app:999/, output + assert_match /docker image rm --force dhh\/app:999 on 1\.1\.1\.2/, output + assert_match /docker pull dhh\/app:999 on 1\.1\.1\.1/, output end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 4c381df2..6f29420b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -9,7 +9,17 @@ require "mrsk" ActiveSupport::LogSubscriber.logger = ActiveSupport::Logger.new(STDOUT) if ENV["VERBOSE"] +# Applies to remote commands only SSHKit.config.backend = SSHKit::Backend::Printer +# Ensure local commands use the printer backend too +module SSHKit + module DSL + def run_locally(&block) + SSHKit::Backend::Printer.new(SSHKit::Host.new(:local), &block).run + end + end +end + class ActiveSupport::TestCase end From 9b39f2f3ab85c5fc028ed765347954b466a1d529 Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Sun, 12 Mar 2023 10:41:04 +0100 Subject: [PATCH 02/14] Keep it simple for the proposal --- lib/mrsk/cli/build.rb | 4 ++-- lib/mrsk/cli/main.rb | 35 ++++++++++++++++++++++++----------- test/cli/build_test.rb | 21 --------------------- test/test_helper.rb | 9 --------- 4 files changed, 26 insertions(+), 43 deletions(-) diff --git a/lib/mrsk/cli/build.rb b/lib/mrsk/cli/build.rb index ec39282e..cb106741 100644 --- a/lib/mrsk/cli/build.rb +++ b/lib/mrsk/cli/build.rb @@ -1,8 +1,8 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base desc "deliver", "Build app and push app image to registry then pull image on servers" - option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push" + option :use_prebuilt_image, aliases: "-P", type: :boolean, default: false, desc: "Use prebuilt image, skip build" def deliver - push unless options[:skip_push] + push unless options[:use_prebuilt_image] pull end diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index 163c0327..b77e5256 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -9,38 +9,51 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base end desc "deploy", "Deploy app to servers" - option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push" + option :use_prebuilt_image, aliases: "-P", type: :boolean, default: false, desc: "Use prebuilt image, skip build" def deploy runtime = print_runtime do + options_without_prebuilt_image = options.without(:use_prebuilt_image) + say "Ensure curl and Docker are installed...", :magenta - invoke "mrsk:cli:server:bootstrap", [], options.without(:skip_push) + invoke "mrsk:cli:server:bootstrap", [], options_without_prebuilt_image say "Log into image registry...", :magenta - invoke "mrsk:cli:registry:login", [], options.without(:skip_push) + invoke "mrsk:cli:registry:login", [], options_without_prebuilt_image - say "Build and push app image...", :magenta - invoke "mrsk:cli:build:deliver" + unless options[:use_prebuilt_image] + say "Build and push app image...", :magenta + invoke "mrsk:cli:build:push" + end + + say "Pull image onto servers...", :magenta + invoke "mrsk:cli:build:pull", [], options_without_prebuilt_image say "Ensure Traefik is running...", :magenta - invoke "mrsk:cli:traefik:boot", [], options.without(:skip_push) + invoke "mrsk:cli:traefik:boot", [], options_without_prebuilt_image say "Ensure app can pass healthcheck...", :magenta - invoke "mrsk:cli:healthcheck:perform", [], options.without(:skip_push) + invoke "mrsk:cli:healthcheck:perform", [], options_without_prebuilt_image - invoke "mrsk:cli:app:boot", [], options.without(:skip_push) + invoke "mrsk:cli:app:boot", [], options_without_prebuilt_image say "Prune old containers and images...", :magenta - invoke "mrsk:cli:prune:all", [], options.without(:skip_push) + invoke "mrsk:cli:prune:all", [], options_without_prebuilt_image end audit_broadcast "Deployed app in #{runtime.to_i} seconds" unless options[:skip_broadcast] end desc "redeploy", "Deploy app to servers without bootstrapping servers, starting Traefik, pruning, and registry login" + option :use_prebuilt_image, aliases: "-P", type: :boolean, default: false, desc: "Use prebuilt image, skip build" def redeploy runtime = print_runtime do - say "Build and push app image...", :magenta - invoke "mrsk:cli:build:deliver" + unless options[:use_prebuilt_image] + say "Build and push app image...", :magenta + invoke "mrsk:cli:build:push" + end + + say "Pull image onto servers...", :magenta + invoke "mrsk:cli:build:pull" say "Ensure app can pass healthcheck...", :magenta invoke "mrsk:cli:healthcheck:perform" diff --git a/test/cli/build_test.rb b/test/cli/build_test.rb index 2ded0193..b2ce6fd4 100644 --- a/test/cli/build_test.rb +++ b/test/cli/build_test.rb @@ -1,27 +1,6 @@ require_relative "cli_test_case" class CliBuildTest < CliTestCase - test "deliver" do - run_command("deliver").tap do |output| - assert_match /docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder mrsk-app-multiarch -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile \. as .*\@localhost/, output - assert_match /docker image rm --force dhh\/app:999 on 1\.1\.1\.2/, output - assert_match /docker pull dhh\/app:999 on 1\.1\.1\.1/, output - end - end - - test "deliver without push" do - run_command("deliver", "--skip-push").tap do |output| - assert_match /docker image rm --force dhh\/app:999 on 1\.1\.1\.2/, output - assert_match /docker pull dhh\/app:999 on 1\.1\.1\.1/, output - end - end - - test "push" do - run_command("push").tap do |output| - assert_match /docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder mrsk-app-multiarch -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile \. as .*\@localhost/, output - end - end - test "pull" do run_command("pull").tap do |output| assert_match /docker image rm --force dhh\/app:999 on 1\.1\.1\.2/, output diff --git a/test/test_helper.rb b/test/test_helper.rb index 6f29420b..bf1f7b49 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -12,14 +12,5 @@ ActiveSupport::LogSubscriber.logger = ActiveSupport::Logger.new(STDOUT) if ENV[" # Applies to remote commands only SSHKit.config.backend = SSHKit::Backend::Printer -# Ensure local commands use the printer backend too -module SSHKit - module DSL - def run_locally(&block) - SSHKit::Backend::Printer.new(SSHKit::Host.new(:local), &block).run - end - end -end - class ActiveSupport::TestCase end From ff0170076e8c70e821869614ee46779567381a07 Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Sun, 12 Mar 2023 10:44:33 +0100 Subject: [PATCH 03/14] Simplify --- lib/mrsk/cli/main.rb | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index b77e5256..5d24c3f7 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -12,32 +12,25 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base option :use_prebuilt_image, aliases: "-P", type: :boolean, default: false, desc: "Use prebuilt image, skip build" def deploy runtime = print_runtime do - options_without_prebuilt_image = options.without(:use_prebuilt_image) - say "Ensure curl and Docker are installed...", :magenta - invoke "mrsk:cli:server:bootstrap", [], options_without_prebuilt_image + invoke "mrsk:cli:server:bootstrap", [], options.without(:use_prebuilt_image) say "Log into image registry...", :magenta - invoke "mrsk:cli:registry:login", [], options_without_prebuilt_image + invoke "mrsk:cli:registry:login", [], options.without(:use_prebuilt_image) - unless options[:use_prebuilt_image] - say "Build and push app image...", :magenta - invoke "mrsk:cli:build:push" - end - - say "Pull image onto servers...", :magenta - invoke "mrsk:cli:build:pull", [], options_without_prebuilt_image + say "Build and push app image...", :magenta + invoke "mrsk:cli:build:deliver" say "Ensure Traefik is running...", :magenta - invoke "mrsk:cli:traefik:boot", [], options_without_prebuilt_image + invoke "mrsk:cli:traefik:boot", [], options.without(:use_prebuilt_image) say "Ensure app can pass healthcheck...", :magenta - invoke "mrsk:cli:healthcheck:perform", [], options_without_prebuilt_image + invoke "mrsk:cli:healthcheck:perform", [], options.without(:use_prebuilt_image) - invoke "mrsk:cli:app:boot", [], options_without_prebuilt_image + invoke "mrsk:cli:app:boot", [], options.without(:use_prebuilt_image) say "Prune old containers and images...", :magenta - invoke "mrsk:cli:prune:all", [], options_without_prebuilt_image + invoke "mrsk:cli:prune:all", [], options.without(:use_prebuilt_image) end audit_broadcast "Deployed app in #{runtime.to_i} seconds" unless options[:skip_broadcast] From 47af6d94835087f3bd3e9b5e26fee2431835de05 Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Sun, 12 Mar 2023 10:53:29 +0100 Subject: [PATCH 04/14] Is a global option better? --- lib/mrsk/cli/base.rb | 8 +++++--- lib/mrsk/cli/build.rb | 3 +-- lib/mrsk/cli/main.rb | 23 ++++++++--------------- lib/mrsk/commander.rb | 2 +- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/mrsk/cli/base.rb b/lib/mrsk/cli/base.rb index c7a87964..8fa3fc6b 100644 --- a/lib/mrsk/cli/base.rb +++ b/lib/mrsk/cli/base.rb @@ -12,6 +12,7 @@ module Mrsk::Cli class_option :quiet, type: :boolean, aliases: "-q", desc: "Minimal logging" class_option :version, desc: "Run commands against a specific app version" + class_option :use_prebuilt_image, type: :boolean, default: false, desc: "Use prebuilt image, skip building" class_option :primary, type: :boolean, aliases: "-p", desc: "Run commands only on primary host instead of all" class_option :hosts, aliases: "-h", desc: "Run commands on these hosts instead of all (separate by comma)" @@ -39,9 +40,10 @@ module Mrsk::Cli def initialize_commander(options) MRSK.tap do |commander| - commander.config_file = Pathname.new(File.expand_path(options[:config_file])) - commander.destination = options[:destination] - commander.version = options[:version] + commander.config_file = Pathname.new(File.expand_path(options[:config_file])) + commander.destination = options[:destination] + commander.version = options[:version] + commander.use_prebuilt_image = options[:use_prebuilt_image] commander.specific_hosts = options[:hosts]&.split(",") commander.specific_roles = options[:roles]&.split(",") diff --git a/lib/mrsk/cli/build.rb b/lib/mrsk/cli/build.rb index cb106741..dd721d6f 100644 --- a/lib/mrsk/cli/build.rb +++ b/lib/mrsk/cli/build.rb @@ -1,8 +1,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base desc "deliver", "Build app and push app image to registry then pull image on servers" - option :use_prebuilt_image, aliases: "-P", type: :boolean, default: false, desc: "Use prebuilt image, skip build" def deliver - push unless options[:use_prebuilt_image] + push unless MRSK.use_prebuilt_image pull end diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index 5d24c3f7..73268f50 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -9,44 +9,37 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base end desc "deploy", "Deploy app to servers" - option :use_prebuilt_image, aliases: "-P", type: :boolean, default: false, desc: "Use prebuilt image, skip build" def deploy runtime = print_runtime do say "Ensure curl and Docker are installed...", :magenta - invoke "mrsk:cli:server:bootstrap", [], options.without(:use_prebuilt_image) + invoke "mrsk:cli:server:bootstrap" say "Log into image registry...", :magenta - invoke "mrsk:cli:registry:login", [], options.without(:use_prebuilt_image) + invoke "mrsk:cli:registry:login" say "Build and push app image...", :magenta invoke "mrsk:cli:build:deliver" say "Ensure Traefik is running...", :magenta - invoke "mrsk:cli:traefik:boot", [], options.without(:use_prebuilt_image) + invoke "mrsk:cli:traefik:boot" say "Ensure app can pass healthcheck...", :magenta - invoke "mrsk:cli:healthcheck:perform", [], options.without(:use_prebuilt_image) + invoke "mrsk:cli:healthcheck:perform" - invoke "mrsk:cli:app:boot", [], options.without(:use_prebuilt_image) + invoke "mrsk:cli:app:boot" say "Prune old containers and images...", :magenta - invoke "mrsk:cli:prune:all", [], options.without(:use_prebuilt_image) + invoke "mrsk:cli:prune:all" end audit_broadcast "Deployed app in #{runtime.to_i} seconds" unless options[:skip_broadcast] end desc "redeploy", "Deploy app to servers without bootstrapping servers, starting Traefik, pruning, and registry login" - option :use_prebuilt_image, aliases: "-P", type: :boolean, default: false, desc: "Use prebuilt image, skip build" def redeploy runtime = print_runtime do - unless options[:use_prebuilt_image] - say "Build and push app image...", :magenta - invoke "mrsk:cli:build:push" - end - - say "Pull image onto servers...", :magenta - invoke "mrsk:cli:build:pull" + say "Build and push app image...", :magenta + invoke "mrsk:cli:build:deliver" say "Ensure app can pass healthcheck...", :magenta invoke "mrsk:cli:healthcheck:perform" diff --git a/lib/mrsk/commander.rb b/lib/mrsk/commander.rb index be2fd2e3..d9fb5347 100644 --- a/lib/mrsk/commander.rb +++ b/lib/mrsk/commander.rb @@ -1,7 +1,7 @@ require "active_support/core_ext/enumerable" class Mrsk::Commander - attr_accessor :config_file, :destination, :verbosity, :version + attr_accessor :config_file, :destination, :verbosity, :version, :use_prebuilt_image def initialize(config_file: nil, destination: nil, verbosity: :info) @config_file, @destination, @verbosity = config_file, destination, verbosity From 6232175ef8f2c516140b40e9044a2c9b054592da Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Sun, 12 Mar 2023 10:56:12 +0100 Subject: [PATCH 05/14] Undo changes from experimenting --- test/cli/build_test.rb | 4 ++-- test/test_helper.rb | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/cli/build_test.rb b/test/cli/build_test.rb index b2ce6fd4..3d5f4431 100644 --- a/test/cli/build_test.rb +++ b/test/cli/build_test.rb @@ -3,8 +3,8 @@ require_relative "cli_test_case" class CliBuildTest < CliTestCase test "pull" do run_command("pull").tap do |output| - assert_match /docker image rm --force dhh\/app:999 on 1\.1\.1\.2/, output - assert_match /docker pull dhh\/app:999 on 1\.1\.1\.1/, output + assert_match /docker image rm --force dhh\/app:999/, output + assert_match /docker pull dhh\/app:999/, output end end diff --git a/test/test_helper.rb b/test/test_helper.rb index bf1f7b49..4c381df2 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -9,7 +9,6 @@ require "mrsk" ActiveSupport::LogSubscriber.logger = ActiveSupport::Logger.new(STDOUT) if ENV["VERBOSE"] -# Applies to remote commands only SSHKit.config.backend = SSHKit::Backend::Printer class ActiveSupport::TestCase From 9b666e54f3855a2eca545c61ed86a3260f7148d8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 13 Mar 2023 10:43:44 -0400 Subject: [PATCH 06/14] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8481e532..56d26116 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ 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 +Join us on Discord: https://discord.gg/DQETs3Pm + ## Installation Install MRSK globally with `gem install mrsk`. Then, inside your app directory, run `mrsk init` (or `mrsk init --bundle` within Rails apps where you want a bin/mrsk binstub). Now edit the new file `config/deploy.yml`. It could look as simple as this: From 3e0b71b631998f80226e230a6e415aec9964f262 Mon Sep 17 00:00:00 2001 From: Richard Taylor Date: Mon, 13 Mar 2023 14:51:54 +0000 Subject: [PATCH 07/14] Fix healthcheck test Looks like the tests started failing on the options healthcheck PR after merging the container name env var PR. --- test/commands/healthcheck_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/commands/healthcheck_test.rb b/test/commands/healthcheck_test.rb index cd1f0d2d..effd3ef3 100644 --- a/test/commands/healthcheck_test.rb +++ b/test/commands/healthcheck_test.rb @@ -33,7 +33,7 @@ class CommandsHealthcheckTest < ActiveSupport::TestCase test "run with custom options" do @config[:servers] = { "web" => { "hosts" => [ "1.1.1.1" ], "options" => { "mount" => "somewhere" } } } assert_equal \ - "docker run --detach --name healthcheck-app-123 --publish 3999:3000 --label service=healthcheck-app --mount \"somewhere\" dhh/app:123", + "docker run --detach --name healthcheck-app-123 --publish 3999:3000 --label service=healthcheck-app -e MRSK_CONTAINER_NAME=\"healthcheck-app\" --mount \"somewhere\" dhh/app:123", new_command.run.join(" ") end From cb15800d2529cd0cb29ab916bbac029a7edbc39c Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Mon, 13 Mar 2023 16:02:24 +0100 Subject: [PATCH 08/14] Move option to `deploy`/`redeploy`, rename to `skip-push` --- lib/mrsk/cli/base.rb | 8 +++----- lib/mrsk/cli/build.rb | 2 +- lib/mrsk/cli/main.rb | 38 +++++++++++++++++++++++++++----------- lib/mrsk/commander.rb | 2 +- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/lib/mrsk/cli/base.rb b/lib/mrsk/cli/base.rb index 8fa3fc6b..c7a87964 100644 --- a/lib/mrsk/cli/base.rb +++ b/lib/mrsk/cli/base.rb @@ -12,7 +12,6 @@ module Mrsk::Cli class_option :quiet, type: :boolean, aliases: "-q", desc: "Minimal logging" class_option :version, desc: "Run commands against a specific app version" - class_option :use_prebuilt_image, type: :boolean, default: false, desc: "Use prebuilt image, skip building" class_option :primary, type: :boolean, aliases: "-p", desc: "Run commands only on primary host instead of all" class_option :hosts, aliases: "-h", desc: "Run commands on these hosts instead of all (separate by comma)" @@ -40,10 +39,9 @@ module Mrsk::Cli def initialize_commander(options) MRSK.tap do |commander| - commander.config_file = Pathname.new(File.expand_path(options[:config_file])) - commander.destination = options[:destination] - commander.version = options[:version] - commander.use_prebuilt_image = options[:use_prebuilt_image] + commander.config_file = Pathname.new(File.expand_path(options[:config_file])) + commander.destination = options[:destination] + commander.version = options[:version] commander.specific_hosts = options[:hosts]&.split(",") commander.specific_roles = options[:roles]&.split(",") diff --git a/lib/mrsk/cli/build.rb b/lib/mrsk/cli/build.rb index dd721d6f..3e8912d7 100644 --- a/lib/mrsk/cli/build.rb +++ b/lib/mrsk/cli/build.rb @@ -1,7 +1,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base desc "deliver", "Build app and push app image to registry then pull image on servers" def deliver - push unless MRSK.use_prebuilt_image + push pull end diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index 73268f50..e34cdaf3 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -9,42 +9,58 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base end desc "deploy", "Deploy app to servers" + option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push" def deploy + invoke_options = options.without(:skip_push) + runtime = print_runtime do say "Ensure curl and Docker are installed...", :magenta - invoke "mrsk:cli:server:bootstrap" + invoke "mrsk:cli:server:bootstrap", [], invoke_options say "Log into image registry...", :magenta invoke "mrsk:cli:registry:login" - say "Build and push app image...", :magenta - invoke "mrsk:cli:build:deliver" + if options[:skip_push] + say "Pull app image...", :magenta + invoke "mrsk:cli:build:pull", [], invoke_options + else + say "Build and push app image...", :magenta + invoke "mrsk:cli:build:deliver", [], invoke_options + end say "Ensure Traefik is running...", :magenta - invoke "mrsk:cli:traefik:boot" + invoke "mrsk:cli:traefik:boot", [], invoke_options say "Ensure app can pass healthcheck...", :magenta - invoke "mrsk:cli:healthcheck:perform" + invoke "mrsk:cli:healthcheck:perform", [], invoke_options - invoke "mrsk:cli:app:boot" + invoke "mrsk:cli:app:boot", [], invoke_options say "Prune old containers and images...", :magenta - invoke "mrsk:cli:prune:all" + invoke "mrsk:cli:prune:all", [], invoke_options end audit_broadcast "Deployed app in #{runtime.to_i} seconds" unless options[:skip_broadcast] end desc "redeploy", "Deploy app to servers without bootstrapping servers, starting Traefik, pruning, and registry login" + option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push" def redeploy + invoke_options = options.without(:skip_push) + runtime = print_runtime do - say "Build and push app image...", :magenta - invoke "mrsk:cli:build:deliver" + if options[:skip_push] + say "Pull app image...", :magenta + invoke "mrsk:cli:build:pull", [], invoke_options + else + say "Build and push app image...", :magenta + invoke "mrsk:cli:build:deliver", [], invoke_options + end say "Ensure app can pass healthcheck...", :magenta - invoke "mrsk:cli:healthcheck:perform" + invoke "mrsk:cli:healthcheck:perform", [], invoke_options - invoke "mrsk:cli:app:boot" + invoke "mrsk:cli:app:boot", [], invoke_options end audit_broadcast "Redeployed app in #{runtime.to_i} seconds" unless options[:skip_broadcast] diff --git a/lib/mrsk/commander.rb b/lib/mrsk/commander.rb index d9fb5347..be2fd2e3 100644 --- a/lib/mrsk/commander.rb +++ b/lib/mrsk/commander.rb @@ -1,7 +1,7 @@ require "active_support/core_ext/enumerable" class Mrsk::Commander - attr_accessor :config_file, :destination, :verbosity, :version, :use_prebuilt_image + attr_accessor :config_file, :destination, :verbosity, :version def initialize(config_file: nil, destination: nil, verbosity: :info) @config_file, @destination, @verbosity = config_file, destination, verbosity From c29d1ddebad235df7978abbc0feb32b79c65619e Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Mon, 13 Mar 2023 16:05:21 +0100 Subject: [PATCH 09/14] Fix --- lib/mrsk/cli/main.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index e34cdaf3..c9d81115 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -18,7 +18,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base invoke "mrsk:cli:server:bootstrap", [], invoke_options say "Log into image registry...", :magenta - invoke "mrsk:cli:registry:login" + invoke "mrsk:cli:registry:login", [], invoke_options if options[:skip_push] say "Pull app image...", :magenta From 0ac2cd2a4bccd92421e1942fb8b55062f0a889e1 Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Tue, 14 Mar 2023 11:49:31 +0100 Subject: [PATCH 10/14] Add tests for deploy/redeploy commands --- test/cli/main_test.rb | 68 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/cli/main_test.rb b/test/cli/main_test.rb index 9f5640e4..fe7d4d6a 100644 --- a/test/cli/main_test.rb +++ b/test/cli/main_test.rb @@ -6,6 +6,74 @@ class CliMainTest < CliTestCase assert_equal Mrsk::VERSION, version end + test "deploy" do + invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_broadcast" => false, "skip_push" => false} + + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:server:bootstrap", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:prune:all", [], invoke_options) + + run_command("deploy").tap do |output| + assert_match /Ensure curl and Docker are installed/, output + assert_match /Log into image registry/, output + assert_match /Build and push app image/, output + assert_match /Ensure Traefik is running/, output + assert_match /Ensure app can pass healthcheck/, output + assert_match /Prune old containers and images/, output + end + end + + test "deploy with skip_push" do + invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_broadcast" => false, "skip_push" => true } + + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:server:bootstrap", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:pull", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:prune:all", [], invoke_options) + + run_command("deploy", "--skip_push").tap do |output| + assert_match /Ensure curl and Docker are installed/, output + assert_match /Log into image registry/, output + assert_match /Pull app image/, output + assert_match /Ensure Traefik is running/, output + assert_match /Ensure app can pass healthcheck/, output + assert_match /Prune old containers and images/, output + end + end + + test "redeploy" do + invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_broadcast" => false, "skip_push" => false} + + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options) + + run_command("redeploy").tap do |output| + assert_match /Build and push app image/, output + assert_match /Ensure app can pass healthcheck/, output + end + end + + test "redeploy with skip_push" do + invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_broadcast" => false, "skip_push" => true } + + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:pull", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options) + + run_command("redeploy", "--skip_push").tap do |output| + assert_match /Pull app image/, output + assert_match /Ensure app can pass healthcheck/, output + end + end + test "rollback bad version" do run_command("details") # Preheat MRSK const From 50ee954ca94a7a920a08df306ff81389287c4406 Mon Sep 17 00:00:00 2001 From: Jacopo Date: Tue, 14 Mar 2023 11:58:26 +0100 Subject: [PATCH 11/14] Fix Traefik retry middleware As per [Traefik docs](https://doc.traefik.io/traefik/middlewares/overview/#configuration-example) a middleware to be activated needs to be applied to a route. Change the default settings to apply the `retry` middleware on every role with Traefik enabled. --- lib/mrsk/configuration/role.rb | 5 +++-- test/commands/app_test.rb | 6 +++--- test/configuration/role_test.rb | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/mrsk/configuration/role.rb b/lib/mrsk/configuration/role.rb index cc56be3d..f3e20e9a 100644 --- a/lib/mrsk/configuration/role.rb +++ b/lib/mrsk/configuration/role.rb @@ -73,8 +73,9 @@ class Mrsk::Configuration::Role "traefik.http.routers.#{config.service}.rule" => "PathPrefix(`/`)", "traefik.http.services.#{config.service}.loadbalancer.healthcheck.path" => config.healthcheck["path"], "traefik.http.services.#{config.service}.loadbalancer.healthcheck.interval" => "1s", - "traefik.http.middlewares.#{config.service}.retry.attempts" => "5", - "traefik.http.middlewares.#{config.service}.retry.initialinterval" => "500ms" + "traefik.http.middlewares.#{config.service}-retry.retry.attempts" => "5", + "traefik.http.middlewares.#{config.service}-retry.retry.initialinterval" => "500ms", + "traefik.http.routers.#{config.service}.middlewares" => "#{config.service}-retry@docker" } else {} diff --git a/test/commands/app_test.rb b/test/commands/app_test.rb index 806db53b..c1a30f46 100644 --- a/test/commands/app_test.rb +++ b/test/commands/app_test.rb @@ -14,7 +14,7 @@ class CommandsAppTest < ActiveSupport::TestCase test "run" do 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.attempts=\"5\" --label traefik.http.middlewares.app.retry.initialinterval=\"500ms\" 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(" ") end @@ -22,7 +22,7 @@ class CommandsAppTest < ActiveSupport::TestCase @config[:volumes] = ["/local/path:/container/path" ] 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.attempts=\"5\" --label traefik.http.middlewares.app.retry.initialinterval=\"500ms\" 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(" ") end @@ -30,7 +30,7 @@ class CommandsAppTest < ActiveSupport::TestCase @config[:healthcheck] = { "path" => "/healthz" } 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.attempts=\"5\" --label traefik.http.middlewares.app.retry.initialinterval=\"500ms\" 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(" ") end diff --git a/test/configuration/role_test.rb b/test/configuration/role_test.rb index 1017221f..0f83d3df 100644 --- a/test/configuration/role_test.rb +++ b/test/configuration/role_test.rb @@ -42,7 +42,7 @@ class ConfigurationRoleTest < ActiveSupport::TestCase end test "special label args for web" do - assert_equal [ "--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.attempts=\"5\"", "--label", "traefik.http.middlewares.app.retry.initialinterval=\"500ms\""], @config.role(:web).label_args + assert_equal [ "--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\"" ], @config.role(:web).label_args end test "custom labels" do @@ -66,7 +66,7 @@ class ConfigurationRoleTest < ActiveSupport::TestCase c[:servers]["beta"] = { "traefik" => "true", "hosts" => [ "1.1.1.5" ] } }) - assert_equal [ "--label", "service=\"app\"", "--label", "role=\"beta\"", "--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.attempts=\"5\"", "--label", "traefik.http.middlewares.app.retry.initialinterval=\"500ms\"" ], config.role(:beta).label_args + assert_equal [ "--label", "service=\"app\"", "--label", "role=\"beta\"", "--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\"" ], config.role(:beta).label_args end test "env overwritten by role" do From 3ca5bc50b674607f9f594fe538dd809b75644d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20B=C3=BChlmann?= Date: Tue, 14 Mar 2023 15:04:33 +0100 Subject: [PATCH 12/14] Properly pass traefik command options Traefik command options need to be passed as `--key=value`, not `--key value`. --- lib/mrsk/commands/traefik.rb | 2 +- lib/mrsk/utils.rb | 10 ++++++++-- test/commands/traefik_test.rb | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/mrsk/commands/traefik.rb b/lib/mrsk/commands/traefik.rb index 67ab448a..b9ff19c1 100644 --- a/lib/mrsk/commands/traefik.rb +++ b/lib/mrsk/commands/traefik.rb @@ -56,7 +56,7 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base private def cmd_option_args if args = config.traefik["args"] - optionize args + optionize args, with: "=" else [] end diff --git a/lib/mrsk/utils.rb b/lib/mrsk/utils.rb index d8860f1a..1e763250 100644 --- a/lib/mrsk/utils.rb +++ b/lib/mrsk/utils.rb @@ -24,8 +24,14 @@ module Mrsk::Utils end # 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 + def optionize(args, with: nil) + options = if with + args.collect { |(key, value)| value == true ? "--#{key}" : "--#{key}#{with}#{escape_shell_value(value)}" } + else + args.collect { |(key, value)| [ "--#{key}", value == true ? nil : escape_shell_value(value) ] } + end + + options.flatten.compact end # Copied from SSHKit::Backend::Abstract#redact to be available inside Commands classes diff --git a/test/commands/traefik_test.rb b/test/commands/traefik_test.rb index 97310623..5ca51e13 100644 --- a/test/commands/traefik_test.rb +++ b/test/commands/traefik_test.rb @@ -4,18 +4,18 @@ class CommandsTraefikTest < ActiveSupport::TestCase setup do @config = { service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, servers: [ "1.1.1.1" ], - traefik: { "args" => { "accesslog.format" => "json", "metrics.prometheus.buckets" => "0.1,0.3,1.2,5.0" } } + traefik: { "args" => { "accesslog.format" => "json", "api.insecure" => true, "metrics.prometheus.buckets" => "0.1,0.3,1.2,5.0" } } } end test "run" 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\" --metrics.prometheus.buckets \"0.1,0.3,1.2,5.0\"", + "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]["host_port"] = "8080" assert_equal \ - "docker run --name traefik --detach --restart unless-stopped --log-opt max-size=10m --publish 8080:80 --volume /var/run/docker.sock:/var/run/docker.sock traefik --providers.docker --log.level=DEBUG --accesslog.format \"json\" --metrics.prometheus.buckets \"0.1,0.3,1.2,5.0\"", + "docker run --name traefik --detach --restart unless-stopped --log-opt max-size=10m --publish 8080: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(" ") end From 46dad1ee6c2507b971e576963f446d68df1bdaf4 Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Tue, 14 Mar 2023 15:58:12 +0100 Subject: [PATCH 13/14] Add tests for main CLI commands --- lib/mrsk/cli/main.rb | 6 ++- test/cli/main_test.rb | 105 ++++++++++++++++++++++++++++++++++++++++-- test/test_helper.rb | 11 +++++ 3 files changed, 116 insertions(+), 6 deletions(-) diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index c9d81115..328af480 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -135,8 +135,10 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base puts "Binstub already exists in bin/mrsk (remove first to create a new one)" else puts "Adding MRSK to Gemfile and bundle..." - `bundle add mrsk` - `bundle binstubs mrsk` + run_locally do + execute :bundle, :add, :mrsk + execute :bundle, :binstubs, :mrsk + end puts "Created binstub file in bin/mrsk" end end diff --git a/test/cli/main_test.rb b/test/cli/main_test.rb index fe7d4d6a..f72741dd 100644 --- a/test/cli/main_test.rb +++ b/test/cli/main_test.rb @@ -1,13 +1,16 @@ require_relative "cli_test_case" class CliMainTest < CliTestCase - test "version" do - version = stdouted { Mrsk::Cli::Main.new.version } - assert_equal Mrsk::VERSION, version + test "setup" do + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:server:bootstrap") + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:accessory:boot", [ "all" ]) + Mrsk::Cli::Main.any_instance.expects(:deploy) + + run_command("setup") end test "deploy" do - invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_broadcast" => false, "skip_push" => false} + invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_broadcast" => false, "skip_push" => false } Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:server:bootstrap", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options) @@ -93,6 +96,95 @@ class CliMainTest < CliTestCase end end + test "details" do + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:details") + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:details") + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:accessory:details", [ "all" ]) + + run_command("details") + end + + test "audit" do + run_command("audit").tap do |output| + assert_match /tail -n 50 mrsk-app-audit.log on 1.1.1.1/, output + assert_match /App Host: 1.1.1.1/, output + assert_match /tail -n 50 mrsk-app-audit.log on 1.1.1.2/, output + assert_match /App Host: 1.1.1.2/, output + end + end + + test "config" do + run_command("config").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 "dhh/app", config[:repository] + assert_equal "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) + FileUtils.stubs(:cp_r) + + run_command("init").tap do |output| + assert_match /Created configuration file in config\/deploy.yml/, output + assert_match /Created \.env file/, output + end + end + + test "init with existing config" do + Pathname.any_instance.expects(:exist?).returns(true).twice + + run_command("init").tap do |output| + assert_match /Config file already exists in config\/deploy.yml \(remove first to create a new one\)/, output + end + end + + test "init with bundle option" do + Pathname.any_instance.expects(:exist?).returns(false).times(3) + FileUtils.stubs(:mkdir_p) + FileUtils.stubs(:cp_r) + + run_command("init", "--bundle").tap do |output| + assert_match /Created configuration file in config\/deploy.yml/, output + assert_match /Created \.env file/, output + assert_match /Adding MRSK to Gemfile and bundle/, output + assert_match /bundle add mrsk/, output + assert_match /bundle binstubs mrsk/, output + assert_match /Created binstub file in bin\/mrsk/, output + end + end + + test "init with bundle option and existing binstub" do + Pathname.any_instance.expects(:exist?).returns(true).times(3) + FileUtils.stubs(:mkdir_p) + FileUtils.stubs(:cp_r) + + run_command("init", "--bundle").tap do |output| + assert_match /Config file already exists in config\/deploy.yml \(remove first to create a new one\)/, output + assert_match /Binstub already exists in bin\/mrsk \(remove first to create a new one\)/, output + end + end + + test "envify" do + File.expects(:read).with(".env.erb").returns("HELLO=<%= 'world' %>") + File.expects(:write).with(".env", "HELLO=world", perm: 0600) + + run_command("envify") + end + + test "envify with destination" do + File.expects(:read).with(".env.staging.erb").returns("HELLO=<%= 'world' %>") + File.expects(:write).with(".env.staging", "HELLO=world", perm: 0600) + + run_command("envify", "-d", "staging") + end + test "remove with confirmation" do run_command("remove", "-y").tap do |output| assert_match /docker container stop traefik/, output @@ -117,6 +209,11 @@ class CliMainTest < CliTestCase end end + test "version" do + version = stdouted { Mrsk::Cli::Main.new.version } + assert_equal Mrsk::VERSION, version + end + private def run_command(*command) stdouted { Mrsk::Cli::Main.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) } diff --git a/test/test_helper.rb b/test/test_helper.rb index 4c381df2..62cceb5b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -9,7 +9,18 @@ require "mrsk" ActiveSupport::LogSubscriber.logger = ActiveSupport::Logger.new(STDOUT) if ENV["VERBOSE"] +# Applies to remote commands only, +# see https://github.com/capistrano/sshkit/blob/master/lib/sshkit/dsl.rb#L9 SSHKit.config.backend = SSHKit::Backend::Printer +# Ensure local commands use the printer backend too +module SSHKit + module DSL + def run_locally(&block) + SSHKit::Backend::Printer.new(SSHKit::Host.new(:local), &block).run + end + end +end + class ActiveSupport::TestCase end From 3fd2f3f2c540b9f2d5ee0b4aad5d256ed5ce7e13 Mon Sep 17 00:00:00 2001 From: Samuel Sieg Date: Tue, 14 Mar 2023 16:05:57 +0100 Subject: [PATCH 14/14] Improve comments --- test/test_helper.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 62cceb5b..3704e8e2 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -9,11 +9,11 @@ require "mrsk" ActiveSupport::LogSubscriber.logger = ActiveSupport::Logger.new(STDOUT) if ENV["VERBOSE"] -# Applies to remote commands only, -# see https://github.com/capistrano/sshkit/blob/master/lib/sshkit/dsl.rb#L9 +# Applies to remote commands only. SSHKit.config.backend = SSHKit::Backend::Printer -# Ensure local commands use the printer backend too +# Ensure local commands use the printer backend too. +# See https://github.com/capistrano/sshkit/blob/master/lib/sshkit/dsl.rb#L9 module SSHKit module DSL def run_locally(&block)