diff --git a/lib/kamal/configuration.rb b/lib/kamal/configuration.rb index bd22178f..b291dfde 100644 --- a/lib/kamal/configuration.rb +++ b/lib/kamal/configuration.rb @@ -149,8 +149,12 @@ class Kamal::Configuration proxy_roles.flat_map(&:name) end + def proxy_accessories + accessories.select(&:running_proxy?) + end + def proxy_hosts - proxy_roles.flat_map(&:hosts).uniq + (proxy_roles.flat_map(&:hosts) + proxy_accessories.flat_map(&:hosts)).uniq end def repository @@ -367,22 +371,26 @@ class Kamal::Configuration end def ensure_required_keys_present - %i[ service image registry servers ].each do |key| + %i[ service image registry ].each do |key| raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present? end - unless role(primary_role_name).present? - raise Kamal::ConfigurationError, "The primary_role #{primary_role_name} isn't defined" - end + if raw_config.servers.nil? + raise Kamal::ConfigurationError, "No servers or accessories specified" unless raw_config.accessories.present? + else + unless role(primary_role_name).present? + raise Kamal::ConfigurationError, "The primary_role #{primary_role_name} isn't defined" + end - if primary_role.hosts.empty? - raise Kamal::ConfigurationError, "No servers specified for the #{primary_role.name} primary_role" - end + if primary_role.hosts.empty? + raise Kamal::ConfigurationError, "No servers specified for the #{primary_role.name} primary_role" + end - unless allow_empty_roles? - roles.each do |role| - if role.hosts.empty? - raise Kamal::ConfigurationError, "No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true" + unless allow_empty_roles? + roles.each do |role| + if role.hosts.empty? + raise Kamal::ConfigurationError, "No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true" + end end end end diff --git a/lib/kamal/configuration/servers.rb b/lib/kamal/configuration/servers.rb index ef5e7942..d0ccffd1 100644 --- a/lib/kamal/configuration/servers.rb +++ b/lib/kamal/configuration/servers.rb @@ -13,6 +13,13 @@ class Kamal::Configuration::Servers private def role_names - servers_config.is_a?(Array) ? [ "web" ] : servers_config.keys.sort + case servers_config + when Array + [ "web" ] + when NilClass + [] + else + servers_config.keys.sort + end end end diff --git a/lib/kamal/configuration/validator/servers.rb b/lib/kamal/configuration/validator/servers.rb index 5a734c78..7e80de95 100644 --- a/lib/kamal/configuration/validator/servers.rb +++ b/lib/kamal/configuration/validator/servers.rb @@ -1,6 +1,6 @@ class Kamal::Configuration::Validator::Servers < Kamal::Configuration::Validator def validate! - validate_type! config, Array, Hash + validate_type! config, Array, Hash, NilClass validate_servers! config if config.is_a?(Array) end diff --git a/test/configuration_test.rb b/test/configuration_test.rb index 04be59ea..571ed0a7 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -30,7 +30,7 @@ class ConfigurationTest < ActiveSupport::TestCase %i[ service image registry ].each do |key| test "#{key} config required" do assert_raise(Kamal::ConfigurationError) do - Kamal::Configuration.new @deploy.tap { _1.delete key } + Kamal::Configuration.new @deploy.tap { |config| config.delete key } end end end @@ -38,21 +38,36 @@ class ConfigurationTest < ActiveSupport::TestCase %w[ username password ].each do |key| test "registry #{key} required" do assert_raise(Kamal::ConfigurationError) do - Kamal::Configuration.new @deploy.tap { _1[:registry].delete key } + Kamal::Configuration.new @deploy.tap { |config| config[:registry].delete key } end end end test "service name valid" do assert_nothing_raised do - Kamal::Configuration.new(@deploy.tap { _1[:service] = "hey-app1_primary" }) - Kamal::Configuration.new(@deploy.tap { _1[:service] = "MyApp" }) + Kamal::Configuration.new(@deploy.tap { |config| config[:service] = "hey-app1_primary" }) + Kamal::Configuration.new(@deploy.tap { |config| config[:service] = "MyApp" }) end end test "service name invalid" do assert_raise(Kamal::ConfigurationError) do - Kamal::Configuration.new @deploy.tap { _1[:service] = "app.com" } + Kamal::Configuration.new @deploy.tap { |config| config[:service] = "app.com" } + end + end + + test "servers required" do + assert_raise(Kamal::ConfigurationError) do + Kamal::Configuration.new @deploy.tap { |config| config.delete(:servers) } + end + end + + test "servers not required with accessories" do + assert_nothing_raised do + @deploy.delete(:servers) + @deploy[:accessories] = { "foo" => { "image" => "foo/bar", "host" => "1.1.1.1" } } + + Kamal::Configuration.new(@deploy) end end @@ -250,7 +265,7 @@ class ConfigurationTest < ActiveSupport::TestCase test "destination required" do dest_config_file = Pathname.new(File.expand_path("fixtures/deploy_for_required_dest.yml", __dir__)) - assert_raises(Kamal::ConfigurationError) do + assert_raises(ArgumentError, "You must specify a destination") do config = Kamal::Configuration.create_from config_file: dest_config_file end diff --git a/test/integration/accessory_test.rb b/test/integration/accessory_test.rb index bc2fb378..0ed7a7b9 100644 --- a/test/integration/accessory_test.rb +++ b/test/integration/accessory_test.rb @@ -21,6 +21,32 @@ class AccessoryTest < IntegrationTest assert_accessory_not_running :busybox end + test "proxied: boot, stop, start, restart, logs, remove" do + @app = "app_with_proxied_accessory" + + kamal :proxy, :boot + + kamal :accessory, :boot, :netcat + assert_accessory_running :netcat + assert_netcat_is_up + + kamal :accessory, :stop, :netcat + assert_accessory_not_running :netcat + assert_netcat_not_found + + kamal :accessory, :start, :netcat + assert_accessory_running :netcat + assert_netcat_is_up + + kamal :accessory, :restart, :netcat + assert_accessory_running :netcat + assert_netcat_is_up + + kamal :accessory, :remove, :netcat, "-y" + assert_accessory_not_running :netcat + assert_netcat_not_found + end + private def assert_accessory_running(name) assert_match /registry:4443\/busybox:1.36.0 "sh -c 'echo \\"Start/, accessory_details(name) @@ -33,4 +59,25 @@ class AccessoryTest < IntegrationTest def accessory_details(name) kamal :accessory, :details, name, capture: true end + + def assert_netcat_is_up + response = netcat_response + debug_response_code(response, "200") + assert_equal "200", response.code + end + + def assert_netcat_not_found + response = netcat_response + debug_response_code(response, "404") + assert_equal "404", response.code + end + + def netcat_response + uri = URI.parse("http://127.0.0.1:12345/up") + http = Net::HTTP.new(uri.host, uri.port) + request = Net::HTTP::Get.new(uri) + request["Host"] = "netcat" + + http.request(request) + end end diff --git a/test/integration/docker/deployer/app_with_proxied_accessory/config/deploy.yml b/test/integration/docker/deployer/app_with_proxied_accessory/config/deploy.yml index ae4e217b..e904544c 100644 --- a/test/integration/docker/deployer/app_with_proxied_accessory/config/deploy.yml +++ b/test/integration/docker/deployer/app_with_proxied_accessory/config/deploy.yml @@ -1,7 +1,5 @@ service: app_with_proxied_accessory image: app_with_proxied_accessory -servers: - - vm1 env: clear: CLEAR_TOKEN: 4321 @@ -24,15 +22,13 @@ accessories: service: custom-busybox image: registry:4443/busybox:1.36.0 cmd: sh -c 'echo "Starting busybox..."; trap exit term; while true; do sleep 1; done' - roles: - - web + host: vm1 netcat: service: netcat image: registry:4443/busybox:1.36.0 cmd: > sh -c 'echo "Starting netcat..."; while true; do echo -e "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello Ruby" | nc -l -p 80; done' - roles: - - web + host: vm1 port: 12345:80 proxy: host: netcat diff --git a/test/integration/proxied_accessory_test.rb b/test/integration/proxied_accessory_test.rb deleted file mode 100644 index 10f3cff8..00000000 --- a/test/integration/proxied_accessory_test.rb +++ /dev/null @@ -1,63 +0,0 @@ -require_relative "integration_test" - -class ProxiedAccessoryTest < IntegrationTest - test "boot, stop, start, restart, logs, remove" do - @app = "app_with_proxied_accessory" - - kamal :deploy - - kamal :accessory, :boot, :netcat - assert_accessory_running :netcat - assert_netcat_is_up - - kamal :accessory, :stop, :netcat - assert_accessory_not_running :netcat - assert_netcat_not_found - - kamal :accessory, :start, :netcat - assert_accessory_running :netcat - assert_netcat_is_up - - kamal :accessory, :restart, :netcat - assert_accessory_running :netcat - assert_netcat_is_up - - kamal :accessory, :remove, :netcat, "-y" - assert_accessory_not_running :netcat - assert_netcat_not_found - end - - private - def assert_accessory_running(name) - assert_match /registry:4443\/busybox:1.36.0 "sh -c 'echo \\"Start/, accessory_details(name) - end - - def assert_accessory_not_running(name) - assert_no_match /registry:4443\/busybox:1.36.0 "sh -c 'echo \\"Start/, accessory_details(name) - end - - def accessory_details(name) - kamal :accessory, :details, name, capture: true - end - - def assert_netcat_is_up - response = netcat_response - debug_response_code(response, "200") - assert_equal "200", response.code - end - - def assert_netcat_not_found - response = netcat_response - debug_response_code(response, "404") - assert_equal "404", response.code - end - - def netcat_response - uri = URI.parse("http://127.0.0.1:12345/up") - http = Net::HTTP.new(uri.host, uri.port) - request = Net::HTTP::Get.new(uri) - request["Host"] = "netcat" - - http.request(request) - end -end