kamal 0.16.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -1013
  3. data/lib/kamal/cli/app.rb +38 -11
  4. data/lib/kamal/cli/base.rb +8 -0
  5. data/lib/kamal/cli/build.rb +18 -1
  6. data/lib/kamal/cli/env.rb +56 -0
  7. data/lib/kamal/cli/healthcheck/poller.rb +64 -0
  8. data/lib/kamal/cli/healthcheck.rb +2 -2
  9. data/lib/kamal/cli/lock.rb +12 -3
  10. data/lib/kamal/cli/main.rb +14 -3
  11. data/lib/kamal/cli/prune.rb +3 -2
  12. data/lib/kamal/cli/server.rb +2 -0
  13. data/lib/kamal/cli/templates/deploy.yml +12 -1
  14. data/lib/kamal/commander.rb +21 -8
  15. data/lib/kamal/commands/accessory.rb +8 -8
  16. data/lib/kamal/commands/app/assets.rb +51 -0
  17. data/lib/kamal/commands/app/containers.rb +23 -0
  18. data/lib/kamal/commands/app/cord.rb +22 -0
  19. data/lib/kamal/commands/app/execution.rb +27 -0
  20. data/lib/kamal/commands/app/images.rb +13 -0
  21. data/lib/kamal/commands/app/logging.rb +18 -0
  22. data/lib/kamal/commands/app.rb +17 -91
  23. data/lib/kamal/commands/auditor.rb +3 -1
  24. data/lib/kamal/commands/base.rb +12 -0
  25. data/lib/kamal/commands/builder/base.rb +6 -0
  26. data/lib/kamal/commands/builder.rb +3 -1
  27. data/lib/kamal/commands/healthcheck.rb +15 -12
  28. data/lib/kamal/commands/lock.rb +2 -2
  29. data/lib/kamal/commands/prune.rb +11 -3
  30. data/lib/kamal/commands/server.rb +5 -0
  31. data/lib/kamal/commands/traefik.rb +26 -8
  32. data/lib/kamal/configuration/accessory.rb +14 -2
  33. data/lib/kamal/configuration/role.rb +112 -19
  34. data/lib/kamal/configuration/ssh.rb +1 -1
  35. data/lib/kamal/configuration/volume.rb +22 -0
  36. data/lib/kamal/configuration.rb +79 -43
  37. data/lib/kamal/env_file.rb +41 -0
  38. data/lib/kamal/git.rb +19 -0
  39. data/lib/kamal/utils.rb +0 -39
  40. data/lib/kamal/version.rb +1 -1
  41. metadata +16 -5
  42. data/lib/kamal/utils/healthcheck_poller.rb +0 -39
@@ -0,0 +1,18 @@
1
+ module Kamal::Commands::App::Logging
2
+ def logs(since: nil, lines: nil, grep: nil)
3
+ pipe \
4
+ current_running_container_id,
5
+ "xargs docker logs#{" --since #{since}" if since}#{" --tail #{lines}" if lines} 2>&1",
6
+ ("grep '#{grep}'" if grep)
7
+ end
8
+
9
+ def follow_logs(host:, grep: nil)
10
+ run_over_ssh \
11
+ pipe(
12
+ current_running_container_id,
13
+ "xargs docker logs --timestamps --tail 10 --follow 2>&1",
14
+ (%(grep "#{grep}") if grep)
15
+ ),
16
+ host: host
17
+ end
18
+ end
@@ -1,34 +1,32 @@
1
1
  class Kamal::Commands::App < Kamal::Commands::Base
2
+ include Assets, Containers, Cord, Execution, Images, Logging
3
+
2
4
  ACTIVE_DOCKER_STATUSES = [ :running, :restarting ]
3
5
 
4
- attr_reader :role
6
+ attr_reader :role, :role_config
5
7
 
6
8
  def initialize(config, role: nil)
7
9
  super(config)
8
10
  @role = role
9
- end
10
-
11
- def start_or_run(hostname: nil)
12
- combine start, run(hostname: hostname), by: "||"
11
+ @role_config = config.role(self.role)
13
12
  end
14
13
 
15
14
  def run(hostname: nil)
16
- role = config.role(self.role)
17
-
18
15
  docker :run,
19
16
  "--detach",
20
17
  "--restart unless-stopped",
21
18
  "--name", container_name,
22
19
  *(["--hostname", hostname] if hostname),
23
20
  "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
24
- *role.env_args,
25
- *role.health_check_args,
21
+ *role_config.env_args,
22
+ *role_config.health_check_args,
26
23
  *config.logging_args,
27
24
  *config.volume_args,
28
- *role.label_args,
29
- *role.option_args,
25
+ *role_config.asset_volume_args,
26
+ *role_config.label_args,
27
+ *role_config.option_args,
30
28
  config.absolute_image,
31
- role.cmd
29
+ role_config.cmd
32
30
  end
33
31
 
34
32
  def start
@@ -50,53 +48,6 @@ class Kamal::Commands::App < Kamal::Commands::Base
50
48
  end
51
49
 
52
50
 
53
- def logs(since: nil, lines: nil, grep: nil)
54
- pipe \
55
- current_running_container_id,
56
- "xargs docker logs#{" --since #{since}" if since}#{" --tail #{lines}" if lines} 2>&1",
57
- ("grep '#{grep}'" if grep)
58
- end
59
-
60
- def follow_logs(host:, grep: nil)
61
- run_over_ssh \
62
- pipe(
63
- current_running_container_id,
64
- "xargs docker logs --timestamps --tail 10 --follow 2>&1",
65
- (%(grep "#{grep}") if grep)
66
- ),
67
- host: host
68
- end
69
-
70
-
71
- def execute_in_existing_container(*command, interactive: false)
72
- docker :exec,
73
- ("-it" if interactive),
74
- container_name,
75
- *command
76
- end
77
-
78
- def execute_in_new_container(*command, interactive: false)
79
- role = config.role(self.role)
80
-
81
- docker :run,
82
- ("-it" if interactive),
83
- "--rm",
84
- *config.env_args,
85
- *config.volume_args,
86
- *role&.option_args,
87
- config.absolute_image,
88
- *command
89
- end
90
-
91
- def execute_in_existing_container_over_ssh(*command, host:)
92
- run_over_ssh execute_in_existing_container(*command, interactive: true), host: host
93
- end
94
-
95
- def execute_in_new_container_over_ssh(*command, host:)
96
- run_over_ssh execute_in_new_container(*command, interactive: true), host: host
97
- end
98
-
99
-
100
51
  def current_running_container_id
101
52
  docker :ps, "--quiet", *filter_args(statuses: ACTIVE_DOCKER_STATUSES), "--latest"
102
53
  end
@@ -112,47 +63,22 @@ class Kamal::Commands::App < Kamal::Commands::Base
112
63
  def list_versions(*docker_args, statuses: nil)
113
64
  pipe \
114
65
  docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
115
- %(while read line; do echo ${line##{service_role_dest}-}; done) # Extract SHA from "service-role-dest-SHA"
116
- end
117
-
118
- def list_containers
119
- docker :container, :ls, "--all", *filter_args
120
- end
121
-
122
- def list_container_names
123
- [ *list_containers, "--format", "'{{ .Names }}'" ]
66
+ %(while read line; do echo ${line##{role_config.container_prefix}-}; done) # Extract SHA from "service-role-dest-SHA"
124
67
  end
125
68
 
126
- def remove_container(version:)
127
- pipe \
128
- container_id_for(container_name: container_name(version)),
129
- xargs(docker(:container, :rm))
130
- end
131
-
132
- def rename_container(version:, new_version:)
133
- docker :rename, container_name(version), container_name(new_version)
134
- end
135
-
136
- def remove_containers
137
- docker :container, :prune, "--force", *filter_args
138
- end
139
-
140
- def list_images
141
- docker :image, :ls, config.repository
142
- end
143
69
 
144
- def remove_images
145
- docker :image, :prune, "--all", "--force", *filter_args
70
+ def make_env_directory
71
+ make_directory role_config.host_env_directory
146
72
  end
147
73
 
148
- def tag_current_as_latest
149
- docker :tag, config.absolute_image, config.latest_image
74
+ def remove_env_file
75
+ [ :rm, "-f", role_config.host_env_file_path ]
150
76
  end
151
77
 
152
78
 
153
79
  private
154
80
  def container_name(version = nil)
155
- [ config.service, role, config.destination, version || config.version ].compact.join("-")
81
+ [ role_config.container_prefix, version || config.version ].compact.join("-")
156
82
  end
157
83
 
158
84
  def filter_args(statuses: nil)
@@ -160,7 +86,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
160
86
  end
161
87
 
162
88
  def service_role_dest
163
- [config.service, role, config.destination].compact.join("-")
89
+ [ config.service, role, config.destination ].compact.join("-")
164
90
  end
165
91
 
166
92
  def filters(statuses: nil)
@@ -19,7 +19,9 @@ class Kamal::Commands::Auditor < Kamal::Commands::Base
19
19
 
20
20
  private
21
21
  def audit_log_file
22
- [ "kamal", config.service, config.destination, "audit.log" ].compact.join("-")
22
+ file = [ config.service, config.destination, "audit.log" ].compact.join("-")
23
+
24
+ "#{config.run_directory}/#{file}"
23
25
  end
24
26
 
25
27
  def audit_tags(**details)
@@ -26,6 +26,18 @@ module Kamal::Commands
26
26
  docker :container, :ls, *("--all" unless only_running), "--filter", "name=^#{container_name}$", "--quiet"
27
27
  end
28
28
 
29
+ def make_directory_for(remote_file)
30
+ make_directory Pathname.new(remote_file).dirname.to_s
31
+ end
32
+
33
+ def make_directory(path)
34
+ [ :mkdir, "-p", path ]
35
+ end
36
+
37
+ def remove_directory(path)
38
+ [ :rm, "-r", path ]
39
+ end
40
+
29
41
  private
30
42
  def combine(*commands, by: "&&")
31
43
  commands
@@ -21,6 +21,12 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
21
21
  config.builder.context
22
22
  end
23
23
 
24
+ def validate_image
25
+ pipe \
26
+ docker(:inspect, "-f", "'{{ .Config.Labels.service }}'", config.absolute_image),
27
+ [:grep, "-x", config.service, "||", "(echo \"Image #{config.absolute_image} is missing the `service` label\" && exit 1)"]
28
+ end
29
+
24
30
 
25
31
  private
26
32
  def build_tags
@@ -1,5 +1,7 @@
1
+ require "active_support/core_ext/string/filters"
2
+
1
3
  class Kamal::Commands::Builder < Kamal::Commands::Base
2
- delegate :create, :remove, :push, :clean, :pull, :info, to: :target
4
+ delegate :create, :remove, :push, :clean, :pull, :info, :validate_image, to: :target
3
5
 
4
6
  def name
5
7
  target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry
@@ -1,5 +1,4 @@
1
1
  class Kamal::Commands::Healthcheck < Kamal::Commands::Base
2
- EXPOSED_PORT = 3999
3
2
 
4
3
  def run
5
4
  web = config.role(:web)
@@ -7,11 +6,11 @@ class Kamal::Commands::Healthcheck < Kamal::Commands::Base
7
6
  docker :run,
8
7
  "--detach",
9
8
  "--name", container_name_with_version,
10
- "--publish", "#{EXPOSED_PORT}:#{config.healthcheck["port"]}",
11
- "--label", "service=#{container_name}",
12
- "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
9
+ "--publish", "#{exposed_port}:#{config.healthcheck["port"]}",
10
+ "--label", "service=#{config.healthcheck_service}",
11
+ "-e", "KAMAL_CONTAINER_NAME=\"#{config.healthcheck_service}\"",
13
12
  *web.env_args,
14
- *web.health_check_args,
13
+ *web.health_check_args(cord: false),
15
14
  *config.volume_args,
16
15
  *web.option_args,
17
16
  config.absolute_image,
@@ -27,7 +26,7 @@ class Kamal::Commands::Healthcheck < Kamal::Commands::Base
27
26
  end
28
27
 
29
28
  def logs
30
- pipe container_id, xargs(docker(:logs, "--tail", 50, "2>&1"))
29
+ pipe container_id, xargs(docker(:logs, "--tail", log_lines, "2>&1"))
31
30
  end
32
31
 
33
32
  def stop
@@ -39,12 +38,8 @@ class Kamal::Commands::Healthcheck < Kamal::Commands::Base
39
38
  end
40
39
 
41
40
  private
42
- def container_name
43
- [ "healthcheck", config.service, config.destination ].compact.join("-")
44
- end
45
-
46
41
  def container_name_with_version
47
- "#{container_name}-#{config.version}"
42
+ "#{config.healthcheck_service}-#{config.version}"
48
43
  end
49
44
 
50
45
  def container_id
@@ -52,6 +47,14 @@ class Kamal::Commands::Healthcheck < Kamal::Commands::Base
52
47
  end
53
48
 
54
49
  def health_url
55
- "http://localhost:#{EXPOSED_PORT}#{config.healthcheck["path"]}"
50
+ "http://localhost:#{exposed_port}#{config.healthcheck["path"]}"
51
+ end
52
+
53
+ def exposed_port
54
+ config.healthcheck["exposed_port"]
55
+ end
56
+
57
+ def log_lines
58
+ config.healthcheck["log_lines"]
56
59
  end
57
60
  end
@@ -40,7 +40,7 @@ class Kamal::Commands::Lock < Kamal::Commands::Base
40
40
  end
41
41
 
42
42
  def lock_dir
43
- "kamal_lock-#{config.service}"
43
+ "#{config.run_directory}/lock-#{config.service}"
44
44
  end
45
45
 
46
46
  def lock_details_file
@@ -56,7 +56,7 @@ class Kamal::Commands::Lock < Kamal::Commands::Base
56
56
  end
57
57
 
58
58
  def locked_by
59
- `git config user.name`.strip
59
+ Kamal::Git.user_name
60
60
  rescue Errno::ENOENT
61
61
  "Unknown"
62
62
  end
@@ -3,7 +3,7 @@ require "active_support/core_ext/numeric/time"
3
3
 
4
4
  class Kamal::Commands::Prune < Kamal::Commands::Base
5
5
  def dangling_images
6
- docker :image, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true"
6
+ docker :image, :prune, "--force", "--filter", "label=service=#{config.service}"
7
7
  end
8
8
 
9
9
  def tagged_images
@@ -13,13 +13,17 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
13
13
  "while read image tag; do docker rmi $tag; done"
14
14
  end
15
15
 
16
- def containers(keep_last: 5)
16
+ def app_containers(keep_last: 5)
17
17
  pipe \
18
18
  docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters),
19
19
  "tail -n +#{keep_last + 1}",
20
20
  "while read container_id; do docker rm $container_id; done"
21
21
  end
22
22
 
23
+ def healthcheck_containers
24
+ docker :container, :prune, "--force", *healthcheck_service_filter
25
+ end
26
+
23
27
  private
24
28
  def stopped_containers_filters
25
29
  [ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] }
@@ -35,4 +39,8 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
35
39
  def service_filter
36
40
  [ "--filter", "label=service=#{config.service}" ]
37
41
  end
38
- end
42
+
43
+ def healthcheck_service_filter
44
+ [ "--filter", "label=service=#{config.healthcheck_service}" ]
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ class Kamal::Commands::Server < Kamal::Commands::Base
2
+ def ensure_run_directory
3
+ [:mkdir, "-p", config.run_directory]
4
+ end
5
+ end
@@ -1,5 +1,5 @@
1
1
  class Kamal::Commands::Traefik < Kamal::Commands::Base
2
- delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Kamal::Utils
2
+ delegate :argumentize, :optionize, to: Kamal::Utils
3
3
 
4
4
  DEFAULT_IMAGE = "traefik:v2.9"
5
5
  CONTAINER_PORT = 80
@@ -11,7 +11,7 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
11
11
  docker :run, "--name traefik",
12
12
  "--detach",
13
13
  "--restart", "unless-stopped",
14
- "--publish", port,
14
+ *publish_args,
15
15
  "--volume", "/var/run/docker.sock:/var/run/docker.sock",
16
16
  *env_args,
17
17
  *config.logging_args,
@@ -63,19 +63,37 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
63
63
  "#{host_port}:#{CONTAINER_PORT}"
64
64
  end
65
65
 
66
+ def env_file
67
+ Kamal::EnvFile.new(config.traefik.fetch("env", {}))
68
+ end
69
+
70
+ def host_env_file_path
71
+ File.join host_env_directory, "traefik.env"
72
+ end
73
+
74
+ def make_env_directory
75
+ make_directory(host_env_directory)
76
+ end
77
+
78
+ def remove_env_file
79
+ [:rm, "-f", host_env_file_path]
80
+ end
81
+
66
82
  private
83
+ def publish_args
84
+ argumentize "--publish", port unless config.traefik["publish"] == false
85
+ end
86
+
67
87
  def label_args
68
88
  argumentize "--label", labels
69
89
  end
70
90
 
71
91
  def env_args
72
- env_config = config.traefik["env"] || {}
92
+ argumentize "--env-file", host_env_file_path
93
+ end
73
94
 
74
- if env_config.present?
75
- argumentize_env_with_secrets(env_config)
76
- else
77
- []
78
- end
95
+ def host_env_directory
96
+ File.join config.host_env_directory, "traefik"
79
97
  end
80
98
 
81
99
  def labels
@@ -1,5 +1,5 @@
1
1
  class Kamal::Configuration::Accessory
2
- delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Kamal::Utils
2
+ delegate :argumentize, :optionize, to: Kamal::Utils
3
3
 
4
4
  attr_accessor :name, :specifics
5
5
 
@@ -45,8 +45,20 @@ class Kamal::Configuration::Accessory
45
45
  specifics["env"] || {}
46
46
  end
47
47
 
48
+ def env_file
49
+ Kamal::EnvFile.new(env)
50
+ end
51
+
52
+ def host_env_directory
53
+ File.join config.host_env_directory, "accessories"
54
+ end
55
+
56
+ def host_env_file_path
57
+ File.join host_env_directory, "#{service_name}.env"
58
+ end
59
+
48
60
  def env_args
49
- argumentize_env_with_secrets env
61
+ argumentize "--env-file", host_env_file_path
50
62
  end
51
63
 
52
64
  def files
@@ -1,5 +1,6 @@
1
1
  class Kamal::Configuration::Role
2
- delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Kamal::Utils
2
+ CORD_FILE = "cord"
3
+ delegate :argumentize, :optionize, to: Kamal::Utils
3
4
 
4
5
  attr_accessor :name
5
6
 
@@ -15,6 +16,18 @@ class Kamal::Configuration::Role
15
16
  @hosts ||= extract_hosts_from_config
16
17
  end
17
18
 
19
+ def cmd
20
+ specializations["cmd"]
21
+ end
22
+
23
+ def option_args
24
+ if args = specializations["options"]
25
+ optionize args
26
+ else
27
+ []
28
+ end
29
+ end
30
+
18
31
  def labels
19
32
  default_labels.merge(traefik_labels).merge(custom_labels)
20
33
  end
@@ -23,6 +36,7 @@ class Kamal::Configuration::Role
23
36
  argumentize "--label", labels
24
37
  end
25
38
 
39
+
26
40
  def env
27
41
  if config.env && config.env["secret"]
28
42
  merged_env_with_secrets
@@ -31,46 +45,117 @@ class Kamal::Configuration::Role
31
45
  end
32
46
  end
33
47
 
48
+ def env_file
49
+ Kamal::EnvFile.new(env)
50
+ end
51
+
52
+ def host_env_directory
53
+ File.join config.host_env_directory, "roles"
54
+ end
55
+
56
+ def host_env_file_path
57
+ File.join host_env_directory, "#{[config.service, name, config.destination].compact.join("-")}.env"
58
+ end
59
+
34
60
  def env_args
35
- argumentize_env_with_secrets env
61
+ argumentize "--env-file", host_env_file_path
62
+ end
63
+
64
+ def asset_volume_args
65
+ asset_volume&.docker_args
36
66
  end
37
67
 
38
- def health_check_args
68
+
69
+ def health_check_args(cord: true)
39
70
  if health_check_cmd.present?
40
- optionize({ "health-cmd" => health_check_cmd, "health-interval" => health_check_interval })
71
+ if cord && uses_cord?
72
+ optionize({ "health-cmd" => health_check_cmd_with_cord, "health-interval" => health_check_interval })
73
+ .concat(cord_volume.docker_args)
74
+ else
75
+ optionize({ "health-cmd" => health_check_cmd, "health-interval" => health_check_interval })
76
+ end
41
77
  else
42
78
  []
43
79
  end
44
80
  end
45
81
 
46
82
  def health_check_cmd
47
- options = specializations["healthcheck"] || {}
48
- options = config.healthcheck.merge(options) if running_traefik?
83
+ health_check_options["cmd"] || http_health_check(port: health_check_options["port"], path: health_check_options["path"])
84
+ end
49
85
 
50
- options["cmd"] || http_health_check(port: options["port"], path: options["path"])
86
+ def health_check_cmd_with_cord
87
+ "(#{health_check_cmd}) && (stat #{cord_container_file} > /dev/null || exit 1)"
51
88
  end
52
89
 
53
90
  def health_check_interval
54
- options = specializations["healthcheck"] || {}
55
- options = config.healthcheck.merge(options) if running_traefik?
91
+ health_check_options["interval"] || "1s"
92
+ end
93
+
56
94
 
57
- options["interval"] || "1s"
95
+ def running_traefik?
96
+ name.web? || specializations["traefik"]
58
97
  end
59
98
 
60
- def cmd
61
- specializations["cmd"]
99
+
100
+ def uses_cord?
101
+ running_traefik? && cord_volume && health_check_cmd.present?
62
102
  end
63
103
 
64
- def option_args
65
- if args = specializations["options"]
66
- optionize args
67
- else
68
- []
104
+ def cord_host_directory
105
+ File.join config.run_directory_as_docker_volume, "cords", [container_prefix, config.run_id].join("-")
106
+ end
107
+
108
+ def cord_volume
109
+ if (cord = health_check_options["cord"])
110
+ @cord_volume ||= Kamal::Configuration::Volume.new \
111
+ host_path: File.join(config.run_directory, "cords", [container_prefix, config.run_id].join("-")),
112
+ container_path: cord
69
113
  end
70
114
  end
71
115
 
72
- def running_traefik?
73
- name.web? || specializations["traefik"]
116
+ def cord_host_file
117
+ File.join cord_volume.host_path, CORD_FILE
118
+ end
119
+
120
+ def cord_container_directory
121
+ health_check_options.fetch("cord", nil)
122
+ end
123
+
124
+ def cord_container_file
125
+ File.join cord_volume.container_path, CORD_FILE
126
+ end
127
+
128
+
129
+ def container_name(version = nil)
130
+ [ container_prefix, version || config.version ].compact.join("-")
131
+ end
132
+
133
+ def container_prefix
134
+ [ config.service, name, config.destination ].compact.join("-")
135
+ end
136
+
137
+
138
+ def asset_path
139
+ specializations["asset_path"] || config.asset_path
140
+ end
141
+
142
+ def assets?
143
+ asset_path.present? && running_traefik?
144
+ end
145
+
146
+ def asset_volume(version = nil)
147
+ if assets?
148
+ Kamal::Configuration::Volume.new \
149
+ host_path: asset_volume_path(version), container_path: asset_path
150
+ end
151
+ end
152
+
153
+ def asset_extracted_path(version = nil)
154
+ File.join config.run_directory, "assets", "extracted", container_name(version)
155
+ end
156
+
157
+ def asset_volume_path(version = nil)
158
+ File.join config.run_directory, "assets", "volumes", container_name(version)
74
159
  end
75
160
 
76
161
  private
@@ -152,4 +237,12 @@ class Kamal::Configuration::Role
152
237
  def http_health_check(port:, path:)
153
238
  "curl -f #{URI.join("http://localhost:#{port}", path)} || exit 1" if path.present? || port.present?
154
239
  end
240
+
241
+ def health_check_options
242
+ @health_check_options ||= begin
243
+ options = specializations["healthcheck"] || {}
244
+ options = config.healthcheck.merge(options) if running_traefik?
245
+ options
246
+ end
247
+ end
155
248
  end
@@ -18,7 +18,7 @@ class Kamal::Configuration::Ssh
18
18
  end
19
19
 
20
20
  def options
21
- { user: user, proxy: proxy, auth_methods: [ "publickey" ], logger: logger, keepalive: true, keepalive_interval: 30 }.compact
21
+ { user: user, proxy: proxy, logger: logger, keepalive: true, keepalive_interval: 30 }.compact
22
22
  end
23
23
 
24
24
  def to_h
@@ -0,0 +1,22 @@
1
+ class Kamal::Configuration::Volume
2
+ attr_reader :host_path, :container_path
3
+ delegate :argumentize, to: Kamal::Utils
4
+
5
+ def initialize(host_path:, container_path:)
6
+ @host_path = host_path
7
+ @container_path = container_path
8
+ end
9
+
10
+ def docker_args
11
+ argumentize "--volume", "#{host_path_for_docker_volume}:#{container_path}"
12
+ end
13
+
14
+ private
15
+ def host_path_for_docker_volume
16
+ if Pathname.new(host_path).absolute?
17
+ host_path
18
+ else
19
+ File.join "$(pwd)", host_path
20
+ end
21
+ end
22
+ end