Skip to content

Commit e152b06

Browse files
committed
Fix proxy host overrides in destination configs
1 parent eee0083 commit e152b06

4 files changed

Lines changed: 195 additions & 2 deletions

File tree

lib/kamal/configuration.rb

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,50 @@ def load_raw_config(config_file:, destination: nil)
3131

3232
private
3333
def load_config_files(*files)
34-
files.inject({}) { |config, file| config.deep_merge! load_config_file(file) }
34+
files.inject({}) { |config, file| merge_config_file(config, load_config_file(file)) }
35+
end
36+
37+
def merge_config_file(config, overrides)
38+
remove_replaced_proxy_host_keys(config, overrides)
39+
config.deep_merge! overrides
40+
end
41+
42+
def remove_replaced_proxy_host_keys(config, overrides)
43+
remove_replaced_host_key value_for(config, :proxy), value_for(overrides, :proxy)
44+
remove_replaced_role_proxy_host_keys value_for(config, :servers), value_for(overrides, :servers)
45+
end
46+
47+
def remove_replaced_role_proxy_host_keys(config, overrides)
48+
return unless config.is_a?(Hash) && overrides.is_a?(Hash)
49+
50+
overrides.each do |role, role_overrides|
51+
role_config = value_for(config, role)
52+
remove_replaced_host_key value_for(role_config, "proxy"), value_for(role_overrides, "proxy")
53+
end
54+
end
55+
56+
def remove_replaced_host_key(config, overrides)
57+
return unless config.is_a?(Hash) && overrides.is_a?(Hash)
58+
59+
delete_key(config, "host") if has_key?(overrides, "hosts")
60+
delete_key(config, "hosts") if has_key?(overrides, "host")
61+
end
62+
63+
def has_key?(config, key)
64+
config.key?(key) || config.key?(key.to_sym)
65+
end
66+
67+
def delete_key(config, key)
68+
config.delete(key)
69+
config.delete(key.to_sym)
70+
end
71+
72+
def value_for(config, key)
73+
return unless config.is_a?(Hash)
74+
75+
alternate_key = key.is_a?(Symbol) ? key.to_s : key.to_sym
76+
return config[key] if config.key?(key)
77+
config[alternate_key] if config.key?(alternate_key)
3578
end
3679

3780
def load_config_file(file)

lib/kamal/configuration/proxy.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,19 @@ def stop_command_args(**options)
110110
end
111111

112112
def merge(other)
113-
self.class.new config: config, proxy_config: other.proxy_config.deep_merge(proxy_config), role_name: role_name, secrets: secrets
113+
self.class.new config: config, proxy_config: merge_proxy_config(other.proxy_config, proxy_config), role_name: role_name, secrets: secrets
114114
end
115115

116116
private
117+
def merge_proxy_config(inherited_config, overrides)
118+
inherited_config = inherited_config.dup
119+
120+
inherited_config.delete("host") if overrides.key?("hosts")
121+
inherited_config.delete("hosts") if overrides.key?("host")
122+
123+
inherited_config.deep_merge(overrides)
124+
end
125+
117126
def tls_path(directory, filename)
118127
File.join([ directory, role_name, filename ].compact) if custom_ssl_certificate?
119128
end

test/configuration/role_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,20 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
6868
assert_equal [ "--label", "service=\"app\"", "--label", "role=\"beta\"", "--label", "destination" ], config.role(:beta).label_args
6969
end
7070

71+
test "role proxy hosts replace root proxy host" do
72+
@deploy_with_roles[:proxy] = { "host" => "app.example.com" }
73+
@deploy_with_roles[:servers]["web"] = { "hosts" => [ "1.1.1.1" ], "proxy" => { "hosts" => [ "web.example.com", "files.example.com" ] } }
74+
75+
assert_equal [ "web.example.com", "files.example.com" ], config_with_roles.role(:web).proxy.hosts
76+
end
77+
78+
test "role proxy host replaces root proxy hosts" do
79+
@deploy_with_roles[:proxy] = { "hosts" => [ "app.example.com", "files.example.com" ] }
80+
@deploy_with_roles[:servers]["workers"]["proxy"] = { "host" => "jobs.example.com" }
81+
82+
assert_equal [ "jobs.example.com" ], config_with_roles.role(:workers).proxy.hosts
83+
end
84+
7185
test "env overwritten by role" do
7286
assert_equal "redis://a/b", config_with_roles.role(:workers).env("1.1.1.3").clear["REDIS_URL"]
7387

test/configuration_test.rb

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,122 @@ class ConfigurationTest < ActiveSupport::TestCase
267267
assert_equal "1.1.1.3", config.all_hosts.first
268268
end
269269

270+
test "destination proxy hosts replace proxy host" do
271+
with_config_files(
272+
"deploy.yml" => <<~YAML,
273+
service: app
274+
image: dhh/app
275+
registry:
276+
username: dhh
277+
password: secret
278+
builder:
279+
arch: amd64
280+
servers:
281+
- 1.1.1.1
282+
proxy:
283+
host: myapp.dev
284+
YAML
285+
"deploy.staging.yml" => <<~YAML
286+
proxy:
287+
hosts:
288+
- myapp.dev
289+
- files.myapp.dev
290+
YAML
291+
) do |config_file|
292+
config = Kamal::Configuration.create_from config_file: config_file, destination: "staging"
293+
assert_equal [ "myapp.dev", "files.myapp.dev" ], config.proxy.hosts
294+
end
295+
end
296+
297+
test "destination proxy host replaces proxy hosts" do
298+
with_config_files(
299+
"deploy.yml" => <<~YAML,
300+
service: app
301+
image: dhh/app
302+
registry:
303+
username: dhh
304+
password: secret
305+
builder:
306+
arch: amd64
307+
servers:
308+
- 1.1.1.1
309+
proxy:
310+
hosts:
311+
- myapp.dev
312+
- files.myapp.dev
313+
YAML
314+
"deploy.staging.yml" => <<~YAML
315+
proxy:
316+
host: myapp.dev
317+
YAML
318+
) do |config_file|
319+
config = Kamal::Configuration.create_from config_file: config_file, destination: "staging"
320+
assert_equal [ "myapp.dev" ], config.proxy.hosts
321+
end
322+
end
323+
324+
test "destination role proxy hosts replace role proxy host" do
325+
with_config_files(
326+
"deploy.yml" => <<~YAML,
327+
service: app
328+
image: dhh/app
329+
registry:
330+
username: dhh
331+
password: secret
332+
builder:
333+
arch: amd64
334+
servers:
335+
web:
336+
hosts:
337+
- 1.1.1.1
338+
proxy:
339+
host: web.myapp.dev
340+
YAML
341+
"deploy.staging.yml" => <<~YAML
342+
servers:
343+
web:
344+
proxy:
345+
hosts:
346+
- web.myapp.dev
347+
- files.myapp.dev
348+
YAML
349+
) do |config_file|
350+
config = Kamal::Configuration.create_from config_file: config_file, destination: "staging"
351+
assert_equal [ "web.myapp.dev", "files.myapp.dev" ], config.role(:web).proxy.hosts
352+
end
353+
end
354+
355+
test "destination role proxy host replaces role proxy hosts" do
356+
with_config_files(
357+
"deploy.yml" => <<~YAML,
358+
service: app
359+
image: dhh/app
360+
registry:
361+
username: dhh
362+
password: secret
363+
builder:
364+
arch: amd64
365+
servers:
366+
web:
367+
hosts:
368+
- 1.1.1.1
369+
proxy:
370+
hosts:
371+
- web.myapp.dev
372+
- files.myapp.dev
373+
YAML
374+
"deploy.staging.yml" => <<~YAML
375+
servers:
376+
web:
377+
proxy:
378+
host: web.myapp.dev
379+
YAML
380+
) do |config_file|
381+
config = Kamal::Configuration.create_from config_file: config_file, destination: "staging"
382+
assert_equal [ "web.myapp.dev" ], config.role(:web).proxy.hosts
383+
end
384+
end
385+
270386
test "destination yml config file missing" do
271387
dest_config_file = Pathname.new(File.expand_path("fixtures/deploy_for_dest.yml", __dir__))
272388

@@ -463,4 +579,15 @@ class ConfigurationTest < ActiveSupport::TestCase
463579
end
464580
assert_match /Invalid hooks_output 'invalid' for hook 'pre-deploy'/, error.message
465581
end
582+
583+
private
584+
def with_config_files(files)
585+
Dir.mktmpdir do |dir|
586+
files.each do |name, contents|
587+
File.write(File.join(dir, name), contents)
588+
end
589+
590+
yield Pathname.new(File.join(dir, "deploy.yml"))
591+
end
592+
end
466593
end

0 commit comments

Comments
 (0)