From 998525c93dc4e308e27552325076662216161e58 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 8 Jan 2023 16:20:06 +0100 Subject: [PATCH] Switch to cmd array so we can redact --- lib/mrsk/commands.rb | 12 ++++++++++++ lib/mrsk/commands/app.rb | 28 +++++++++++++++++++--------- lib/mrsk/commands/registry.rb | 6 ++++-- lib/mrsk/commands/traefik.rb | 26 ++++++++++++++++---------- lib/mrsk/configuration.rb | 11 +++++------ lib/tasks/mrsk/app.rake | 23 ++++++++++++++--------- lib/tasks/mrsk/registry.rake | 6 +++--- lib/tasks/mrsk/setup.rb | 2 ++ lib/tasks/mrsk/traefik.rake | 12 ++++++------ 9 files changed, 81 insertions(+), 45 deletions(-) diff --git a/lib/mrsk/commands.rb b/lib/mrsk/commands.rb index 2c2754bf..20eaf673 100644 --- a/lib/mrsk/commands.rb +++ b/lib/mrsk/commands.rb @@ -1,3 +1,5 @@ +require "sshkit" + module Mrsk::Commands class Base attr_accessor :config @@ -5,6 +7,16 @@ module Mrsk::Commands def initialize(config) @config = config end + + private + def docker(*args) + args.unshift :docker + end + + # 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 + arg.to_s.extend(SSHKit::Redaction) # to_s due to our inability to extend Integer, etc + end end end diff --git a/lib/mrsk/commands/app.rb b/lib/mrsk/commands/app.rb index b803760a..a314a991 100644 --- a/lib/mrsk/commands/app.rb +++ b/lib/mrsk/commands/app.rb @@ -1,37 +1,47 @@ -# FIXME: Use Shellwords.join class Mrsk::Commands::App < Mrsk::Commands::Base def push # TODO: Run 'docker buildx create --use' when needed # TODO: Make multiarch an option so Linux users can enjoy speedier builds - "docker buildx build --push --platform=linux/amd64,linux/arm64 -t #{config.absolute_image} ." + docker :buildx, :build, "--push", "--platform linux/amd64,linux/arm64", "-t", config.absolute_image, "." end def pull - "docker pull #{config.absolute_image}" + docker :pull, config.absolute_image end def run - "docker run -d --restart unless-stopped --name #{config.service_with_version} #{config.envs} #{config.labels} #{config.absolute_image}" + docker :run, + "-d", + "--restart unless-stopped", + "--name", config.service_with_version, + "-e", redact("RAILS_MASTER_KEY=#{config.master_key}"), + config.envs, + config.labels, + config.absolute_image end def start - "docker start #{config.service_with_version}" + docker :start, config.service_with_version end def stop - "docker ps -q #{service_filter} | xargs docker stop" + [ "docker ps -q #{service_filter} | xargs docker stop" ] end def info - "docker ps #{service_filter}" + docker :ps, service_filter + end + + def logs + [ "docker ps -q #{service_filter} | xargs docker logs -f" ] end def remove_containers - "docker container prune -f #{service_filter}" + docker :container, :prune, "-f", service_filter end def remove_images - "docker image prune -a -f #{service_filter}" + docker :image, :prune, "-a", "-f", service_filter end private diff --git a/lib/mrsk/commands/registry.rb b/lib/mrsk/commands/registry.rb index 9f2dd1a4..4e9b1ce4 100644 --- a/lib/mrsk/commands/registry.rb +++ b/lib/mrsk/commands/registry.rb @@ -1,9 +1,11 @@ class Mrsk::Commands::Registry < Mrsk::Commands::Base + delegate :registry, to: :config + def login - "docker login #{config.registry["server"]} -u #{config.registry["username"]} -p #{config.registry["password"]}" + docker :login, registry["server"], "-u", redact(registry["username"]), "-p", redact(registry["password"]) end def logout - "docker logout #{config.registry["server"]}" + docker :logout, registry["server"] end end diff --git a/lib/mrsk/commands/traefik.rb b/lib/mrsk/commands/traefik.rb index 045e6aef..47cfe3cd 100644 --- a/lib/mrsk/commands/traefik.rb +++ b/lib/mrsk/commands/traefik.rb @@ -1,29 +1,35 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base def run - "docker run --name traefik " + - "-d --restart unless-stopped " + - "-p 80:80 " + - "-v /var/run/docker.sock:/var/run/docker.sock " + - "traefik --providers.docker" + docker :run, "--name traefik", + "-d", + "--restart unless-stopped", + "-p 80:80", + "-v /var/run/docker.sock:/var/run/docker.sock", + "traefik", + "--providers.docker" end def start - "docker container start traefik" + docker :container, :start, "traefik" end def stop - "docker container stop traefik" + docker :container, :stop, "traefik" end def info - "docker ps --filter name=traefik" + docker :ps, "--filter name=traefik" + end + + def logs + docker :logs, "traefik" end def remove_container - "docker container prune -f --filter label=org.opencontainers.image.title=Traefik" + docker :container, :prune, "-f", "--filter label=org.opencontainers.image.title=Traefik" end def remove_image - "docker image prune -a -f --filter label=org.opencontainers.image.title=Traefik" + docker :image, :prune, "-a", "-f", "--filter label=org.opencontainers.image.title=Traefik" end end diff --git a/lib/mrsk/configuration.rb b/lib/mrsk/configuration.rb index dd85103d..190d574b 100644 --- a/lib/mrsk/configuration.rb +++ b/lib/mrsk/configuration.rb @@ -38,8 +38,7 @@ class Mrsk::Configuration end def envs - parameterize "-e", \ - { "RAILS_MASTER_KEY" => master_key }.merge(env || {}) + parameterize "-e", env if env.present? end def labels @@ -56,6 +55,10 @@ class Mrsk::Configuration { user: config.ssh_user || "root", auth_methods: [ "publickey" ] } end + def master_key + ENV["RAILS_MASTER_KEY"] || File.read(Rails.root.join("config/master.key")) + end + private attr_accessor :config @@ -72,8 +75,4 @@ class Mrsk::Configuration def parameterize(param, hash) hash.collect { |k, v| "#{param} #{k}=#{v}" }.join(" ") end - - def master_key - ENV["RAILS_MASTER_KEY"] || File.read(Rails.root.join("config/master.key")) - end end diff --git a/lib/tasks/mrsk/app.rake b/lib/tasks/mrsk/app.rake index 73e77e91..a8121d99 100644 --- a/lib/tasks/mrsk/app.rake +++ b/lib/tasks/mrsk/app.rake @@ -9,23 +9,23 @@ namespace :mrsk do desc "Build locally and push app image to registry" task :push do - run_locally { execute app.push } unless ENV["VERSION"] + run_locally { execute *app.push } unless ENV["VERSION"] end desc "Pull app image from the registry onto servers" task :pull do - on(MRSK_CONFIG.servers) { execute app.pull } + on(MRSK_CONFIG.servers) { execute *app.pull } end desc "Run app on servers (or start them if they've already been run)" task :run do on(MRSK_CONFIG.servers) do |host| begin - execute app.run + execute *app.run rescue SSHKit::Command::Failed => e if e.message =~ /already in use/ puts "Container with same version already deployed on #{host}, starting that instead" - execute app.start, host: host + execute *app.start, host: host else raise end @@ -35,12 +35,12 @@ namespace :mrsk do desc "Start existing app on servers" task :start do - on(MRSK_CONFIG.servers) { execute app.start, raise_on_non_zero_exit: false } + on(MRSK_CONFIG.servers) { execute *app.start, raise_on_non_zero_exit: false } end desc "Stop app on servers" task :stop do - on(MRSK_CONFIG.servers) { execute app.stop, raise_on_non_zero_exit: false } + on(MRSK_CONFIG.servers) { execute *app.stop, raise_on_non_zero_exit: false } end desc "Start app on servers (use VERSION= to designate which version)" @@ -48,14 +48,19 @@ namespace :mrsk do desc "Display information about app containers" task :info do - on(MRSK_CONFIG.servers) { |host| puts "App Host: #{host}\n" + capture(app.info) + "\n\n" } + on(MRSK_CONFIG.servers) { |host| puts "App Host: #{host}\n" + capture(*app.info) + "\n\n" } + end + + desc "Tail logs from app containers" + task :logs do + on(MRSK_CONFIG.servers) { execute *app.logs } end desc "Remove app containers and images from servers" task remove: %i[ stop ] do on(MRSK_CONFIG.servers) do - execute app.remove_containers - execute app.remove_images + execute *app.remove_containers + execute *app.remove_images end end end diff --git a/lib/tasks/mrsk/registry.rake b/lib/tasks/mrsk/registry.rake index e211d97e..390da856 100644 --- a/lib/tasks/mrsk/registry.rake +++ b/lib/tasks/mrsk/registry.rake @@ -6,13 +6,13 @@ namespace :mrsk do namespace :registry do desc "Login to the registry locally and remotely" task :login do - run_locally { execute registry.login } - on(MRSK_CONFIG.servers) { execute registry.login } + run_locally { execute *registry.login } + on(MRSK_CONFIG.servers) { execute *registry.login } end desc "Logout of the registry remotely" task :logout do - on(MRSK_CONFIG.servers) { execute registry.logout } + on(MRSK_CONFIG.servers) { execute *registry.logout } end end end diff --git a/lib/tasks/mrsk/setup.rb b/lib/tasks/mrsk/setup.rb index 1e5f0569..bdba80a7 100644 --- a/lib/tasks/mrsk/setup.rb +++ b/lib/tasks/mrsk/setup.rb @@ -6,3 +6,5 @@ include SSHKit::DSL MRSK_CONFIG = Mrsk::Configuration.load_file(Rails.root.join("config/deploy.yml")) SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = MRSK_CONFIG.ssh_options } + +SSHKit.config.command_map[:docker] = "docker" diff --git a/lib/tasks/mrsk/traefik.rake b/lib/tasks/mrsk/traefik.rake index de274de8..b7b1f370 100644 --- a/lib/tasks/mrsk/traefik.rake +++ b/lib/tasks/mrsk/traefik.rake @@ -6,17 +6,17 @@ namespace :mrsk do namespace :traefik do desc "Run Traefik on servers" task :run do - on(MRSK_CONFIG.servers) { execute traefik.run, raise_on_non_zero_exit: false } + on(MRSK_CONFIG.servers) { execute *traefik.run, raise_on_non_zero_exit: false } end desc "Start existing Traefik on servers" task :start do - on(MRSK_CONFIG.servers) { execute traefik.start, raise_on_non_zero_exit: false } + on(MRSK_CONFIG.servers) { execute *traefik.start, raise_on_non_zero_exit: false } end desc "Stop Traefik on servers" task :stop do - on(MRSK_CONFIG.servers) { execute traefik.stop, raise_on_non_zero_exit: false } + on(MRSK_CONFIG.servers) { execute *traefik.stop, raise_on_non_zero_exit: false } end desc "Restart Traefik on servers" @@ -24,14 +24,14 @@ namespace :mrsk do desc "Display information about Traefik containers from servers" task :info do - on(MRSK_CONFIG.servers) { |host| puts "Traefik Host: #{host}\n" + capture(traefik.info) + "\n\n" } + on(MRSK_CONFIG.servers) { |host| puts "Traefik Host: #{host}\n" + capture(*traefik.info) + "\n\n" } end desc "Remove Traefik container and image from servers" task remove: %i[ stop ] do on(MRSK_CONFIG.servers) do - execute traefik.remove_container - execute traefik.remove_image + execute *traefik.remove_container + execute *traefik.remove_image end end end