From 9a84460754cf771532935dc0728615a6424253e7 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 17 Jan 2023 13:35:55 +0100 Subject: [PATCH] Add option for two-part configs with the destination option --- lib/mrsk/cli.rb | 6 +++--- lib/mrsk/cli/base.rb | 13 +++++++++++- lib/mrsk/commander.rb | 9 ++++----- lib/mrsk/configuration.rb | 27 +++++++++++++++++++------ test/configuration_test.rb | 20 +++++++++++++++++- test/fixtures/deploy_for_dest.mars.yml | 5 +++++ test/fixtures/deploy_for_dest.world.yml | 5 +++++ test/fixtures/deploy_for_dest.yml | 6 ++++++ 8 files changed, 75 insertions(+), 16 deletions(-) create mode 100644 test/fixtures/deploy_for_dest.mars.yml create mode 100644 test/fixtures/deploy_for_dest.world.yml create mode 100644 test/fixtures/deploy_for_dest.yml diff --git a/lib/mrsk/cli.rb b/lib/mrsk/cli.rb index 5413e350..35fc8f44 100644 --- a/lib/mrsk/cli.rb +++ b/lib/mrsk/cli.rb @@ -1,9 +1,9 @@ require "mrsk" -MRSK = Mrsk::Commander.new \ - config_file: Pathname.new(File.expand_path("config/deploy.yml")) - module Mrsk::Cli end +# SSHKit uses instance eval, so we need a global const for ergonomics +MRSK = Mrsk::Commander.new + require "mrsk/cli/main" diff --git a/lib/mrsk/cli/base.rb b/lib/mrsk/cli/base.rb index a2821915..87ffbafb 100644 --- a/lib/mrsk/cli/base.rb +++ b/lib/mrsk/cli/base.rb @@ -10,12 +10,23 @@ module Mrsk::Cli class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging" + class_option :config_file, aliases: "-c", default: "config/deploy.yml", desc: "Path to config file (default: config/deploy.yml)" + class_option :destination, aliases: "-d", desc: "Specify destination to be used for config file (west -> deploy.west.yml)" + def initialize(*) super - MRSK.verbose = options[:verbose] + initialize_commander(options) end private + def initialize_commander(options) + MRSK.tap do |commander| + commander.config_file = Pathname.new(File.expand_path(options[:config_file])) + commander.destination = options[:destination] + commander.verbose = options[:verbose] + end + end + def print_runtime started_at = Time.now yield diff --git a/lib/mrsk/commander.rb b/lib/mrsk/commander.rb index 26c87b5d..10e15b1d 100644 --- a/lib/mrsk/commander.rb +++ b/lib/mrsk/commander.rb @@ -6,15 +6,14 @@ require "mrsk/commands/traefik" require "mrsk/commands/registry" class Mrsk::Commander - attr_reader :config - attr_accessor :verbose + attr_accessor :config_file, :destination, :verbose - def initialize(config_file:) - @config_file = config_file + def initialize(config_file: nil, destination: nil, verbose: false) + @config_file, @destination, @verbose = config_file, destination, verbose end def config - @config ||= Mrsk::Configuration.load_file(@config_file).tap { |config| setup_with(config) } + @config ||= Mrsk::Configuration.create_from(config_file, destination: destination).tap { |config| setup_with(config) } end diff --git a/lib/mrsk/configuration.rb b/lib/mrsk/configuration.rb index 0920d276..b8a23826 100644 --- a/lib/mrsk/configuration.rb +++ b/lib/mrsk/configuration.rb @@ -9,17 +9,32 @@ class Mrsk::Configuration delegate :service, :image, :servers, :env, :labels, :registry, :builder, to: :config, allow_nil: true class << self - def load_file(file) - if file.exist? - new YAML.load(ERB.new(IO.read(file)).result).symbolize_keys - else - raise "Configuration file not found in #{file}" - end + def create_from(base_config_file, destination: nil) + new(load_config_file(base_config_file).tap do |config| + if destination + config.merge! \ + load_config_file destination_config_file(base_config_file, destination) + end + end) end def argumentize(argument, attributes, redacted: false) attributes.flat_map { |k, v| [ argument, redacted ? Mrsk::Utils.redact("#{k}=#{v}") : "#{k}=#{v}" ] } end + + private + def load_config_file(file) + if file.exist? + YAML.load(ERB.new(IO.read(file)).result).symbolize_keys + else + raise "Configuration file not found in #{file}" + end + end + + def destination_config_file(base_config_file, destination) + dir, basename = base_config_file.split + dir.join basename.to_s.remove(".yml") + ".#{destination}.yml" + end end def initialize(config, validate: true) diff --git a/test/configuration_test.rb b/test/configuration_test.rb index 406e9fbc..9170c67e 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -111,7 +111,25 @@ class ConfigurationTest < ActiveSupport::TestCase test "erb evaluation of yml config" do - config = Mrsk::Configuration.load_file Pathname.new(File.expand_path("fixtures/deploy.erb.yml", __dir__)) + config = Mrsk::Configuration.create_from 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" + assert_equal "1.1.1.1", config.hosts.first + + config = Mrsk::Configuration.create_from dest_config_file, destination: "mars" + assert_equal "1.1.1.3", config.hosts.first + end + + test "destination yml config file missing" do + 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" + end + end end diff --git a/test/fixtures/deploy_for_dest.mars.yml b/test/fixtures/deploy_for_dest.mars.yml new file mode 100644 index 00000000..c4fb213a --- /dev/null +++ b/test/fixtures/deploy_for_dest.mars.yml @@ -0,0 +1,5 @@ +servers: + - 1.1.1.3 + - 1.1.1.4 +env: + REDIS_URL: redis://a/b diff --git a/test/fixtures/deploy_for_dest.world.yml b/test/fixtures/deploy_for_dest.world.yml new file mode 100644 index 00000000..764dd236 --- /dev/null +++ b/test/fixtures/deploy_for_dest.world.yml @@ -0,0 +1,5 @@ +servers: + - 1.1.1.1 + - 1.1.1.2 +env: + REDIS_URL: redis://x/y diff --git a/test/fixtures/deploy_for_dest.yml b/test/fixtures/deploy_for_dest.yml new file mode 100644 index 00000000..96740aec --- /dev/null +++ b/test/fixtures/deploy_for_dest.yml @@ -0,0 +1,6 @@ +service: app +image: dhh/app +registry: + server: registry.digitalocean.com + username: <%= "my-user" %> + password: <%= "my-password" %>