kamal 2.10.1 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/kamal/cli/accessory.rb +48 -39
- data/lib/kamal/cli/alias/command.rb +2 -2
- data/lib/kamal/cli/app.rb +57 -48
- data/lib/kamal/cli/base.rb +118 -17
- data/lib/kamal/cli/build.rb +10 -7
- data/lib/kamal/cli/lock.rb +5 -16
- data/lib/kamal/cli/main.rb +59 -53
- data/lib/kamal/cli/proxy.rb +9 -9
- data/lib/kamal/cli/prune.rb +3 -3
- data/lib/kamal/cli/server.rb +34 -15
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +1 -1
- data/lib/kamal/cli/templates/secrets +4 -0
- data/lib/kamal/commander.rb +71 -17
- data/lib/kamal/commands/accessory.rb +3 -2
- data/lib/kamal/commands/app/logging.rb +1 -1
- data/lib/kamal/commands/app.rb +1 -1
- data/lib/kamal/commands/base.rb +15 -2
- data/lib/kamal/commands/builder/clone.rb +2 -1
- data/lib/kamal/commands/docker.rb +17 -1
- data/lib/kamal/commands/proxy.rb +1 -1
- data/lib/kamal/configuration/accessory.rb +13 -5
- data/lib/kamal/configuration/docs/alias.yml +3 -0
- data/lib/kamal/configuration/docs/configuration.yml +37 -2
- data/lib/kamal/configuration/docs/env.yml +6 -4
- data/lib/kamal/configuration/docs/output.yml +25 -0
- data/lib/kamal/configuration/docs/role.yml +1 -0
- data/lib/kamal/configuration/docs/ssh.yml +8 -0
- data/lib/kamal/configuration/output.rb +34 -0
- data/lib/kamal/configuration/proxy/run.rb +10 -1
- data/lib/kamal/configuration/role.rb +18 -6
- data/lib/kamal/configuration/ssh.rb +5 -1
- data/lib/kamal/configuration/validator.rb +29 -2
- data/lib/kamal/configuration.rb +41 -3
- data/lib/kamal/git.rb +1 -1
- data/lib/kamal/otel_shipper.rb +176 -0
- data/lib/kamal/output/base_logger.rb +29 -0
- data/lib/kamal/output/file_logger.rb +51 -0
- data/lib/kamal/output/formatter.rb +36 -0
- data/lib/kamal/output/otel_logger.rb +70 -0
- data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +10 -2
- data/lib/kamal/secrets/adapters/passbolt.rb +1 -1
- data/lib/kamal/secrets.rb +1 -1
- data/lib/kamal/sshkit_with_ext.rb +9 -4
- data/lib/kamal/version.rb +1 -1
- metadata +23 -2
data/lib/kamal/commands/base.rb
CHANGED
|
@@ -11,11 +11,11 @@ module Kamal::Commands
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def run_over_ssh(*command, host:)
|
|
14
|
-
"ssh#{ssh_proxy_args}#{ssh_keys_args} -t #{config.ssh.user}@#{host} -p #{config.ssh.port} '#{command.join(" ").gsub("'", "'\\\\''")}'"
|
|
14
|
+
"ssh#{ssh_config_args}#{ssh_proxy_args}#{ssh_keys_args} -t #{config.ssh.user}@#{host} -p #{config.ssh.port} '#{command.join(" ").gsub("'", "'\\\\''")}'"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def container_id_for(container_name:, only_running: false)
|
|
18
|
-
docker :container, :ls, *("--all" unless only_running), "--filter", "name=^#{container_name}$", "--quiet"
|
|
18
|
+
docker :container, :ls, *("--all" unless only_running), "--filter", "'name=^#{container_name}$'", "--quiet"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def make_directory_for(remote_file)
|
|
@@ -100,6 +100,19 @@ module Kamal::Commands
|
|
|
100
100
|
Kamal::Tags.from_config(config, **details)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
def ssh_config_args
|
|
104
|
+
case config.ssh.config
|
|
105
|
+
when Array
|
|
106
|
+
config.ssh.config.map { |file| " -F #{file}" }.join
|
|
107
|
+
when String
|
|
108
|
+
" -F #{config.ssh.config}"
|
|
109
|
+
when true
|
|
110
|
+
"" # Use default SSH config
|
|
111
|
+
when false
|
|
112
|
+
" -F /dev/null" # Ignore SSH config
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
103
116
|
def ssh_proxy_args
|
|
104
117
|
case config.ssh.proxy
|
|
105
118
|
when Net::SSH::Proxy::Jump
|
|
@@ -9,7 +9,8 @@ module Kamal::Commands::Builder::Clone
|
|
|
9
9
|
git(:fetch, :origin, path: escaped_build_directory),
|
|
10
10
|
git(:reset, "--hard", Kamal::Git.revision, path: escaped_build_directory),
|
|
11
11
|
git(:clean, "-fdx", path: escaped_build_directory),
|
|
12
|
-
git(:submodule, :update, "--init", path: escaped_build_directory)
|
|
12
|
+
git(:submodule, :update, "--init", path: escaped_build_directory),
|
|
13
|
+
git(:gc, "--auto", "--quiet", path: escaped_build_directory)
|
|
13
14
|
]
|
|
14
15
|
end
|
|
15
16
|
|
|
@@ -16,7 +16,23 @@ class Kamal::Commands::Docker < Kamal::Commands::Base
|
|
|
16
16
|
|
|
17
17
|
# Do we have superuser access to install Docker and start system services?
|
|
18
18
|
def superuser?
|
|
19
|
-
[ '[ "${EUID:-$(id -u)}" -eq 0 ] ||
|
|
19
|
+
[ '[ "${EUID:-$(id -u)}" -eq 0 ] || sudo -nl usermod >/dev/null' ]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def root?
|
|
23
|
+
[ '[ "${EUID:-$(id -u)}" -eq 0 ]' ]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def in_docker_group?
|
|
27
|
+
[ 'id -nG "${USER:-$(id -un)}" | grep -qw docker' ]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add_to_docker_group
|
|
31
|
+
[ 'sudo -n usermod -aG docker "${USER:-$(id -un)}"' ]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def refresh_session
|
|
35
|
+
[ "kill -HUP $PPID" ]
|
|
20
36
|
end
|
|
21
37
|
|
|
22
38
|
def create_network
|
data/lib/kamal/commands/proxy.rb
CHANGED
|
@@ -102,11 +102,11 @@ class Kamal::Configuration::Accessory
|
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def option_args
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
optionize docker_options.reject { |key, _| key.to_s == "restart" }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def restart_policy
|
|
109
|
+
restart_policy_option || "unless-stopped"
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
def cmd
|
|
@@ -173,6 +173,14 @@ class Kamal::Configuration::Accessory
|
|
|
173
173
|
accessory_config["volumes"] || []
|
|
174
174
|
end
|
|
175
175
|
|
|
176
|
+
def docker_options
|
|
177
|
+
accessory_config["options"] || {}
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def restart_policy_option
|
|
181
|
+
docker_options.find { |key, _| key.to_s == "restart" }&.last
|
|
182
|
+
end
|
|
183
|
+
|
|
176
184
|
def path_volumes(paths)
|
|
177
185
|
paths.map do |local, config|
|
|
178
186
|
Kamal::Configuration::Volume.new \
|
|
@@ -85,16 +85,38 @@ asset_path: /path/to/assets
|
|
|
85
85
|
# See https://kamal-deploy.org/docs/hooks for more information:
|
|
86
86
|
hooks_path: /user_home/kamal/hooks
|
|
87
87
|
|
|
88
|
+
# Hook output
|
|
89
|
+
#
|
|
90
|
+
# Hook output visibility. Can be set globally or per-hook.
|
|
91
|
+
# CLI flags (`-v`, `-q`) override these settings.
|
|
92
|
+
#
|
|
93
|
+
# - `:quiet` - hook output is hidden
|
|
94
|
+
# - `:verbose` - hook output is shown
|
|
95
|
+
#
|
|
96
|
+
# With no setting, hook output follows CLI verbosity flags.
|
|
97
|
+
#
|
|
98
|
+
# Note: Failed hooks always show output in the error message regardless of setting.
|
|
99
|
+
#
|
|
100
|
+
# Global setting for all hooks:
|
|
101
|
+
hooks_output: :verbose
|
|
102
|
+
# Or per-hook settings:
|
|
103
|
+
hooks_output:
|
|
104
|
+
pre-deploy: :verbose
|
|
105
|
+
pre-build: :quiet
|
|
106
|
+
|
|
88
107
|
# Secrets path
|
|
89
108
|
#
|
|
90
109
|
# Path to secrets, defaults to `.kamal/secrets`.
|
|
91
|
-
# Kamal
|
|
110
|
+
# Kamal looks for `<secrets_path>-common` first and then `<secrets_path>`.
|
|
111
|
+
# When using destinations, it instead looks for `<secrets_path>-common` first and then
|
|
112
|
+
# `<secrets_path>.<destination>`. Later files override earlier ones.
|
|
92
113
|
secrets_path: /user_home/kamal/secrets
|
|
93
114
|
|
|
94
115
|
# Error pages
|
|
95
116
|
#
|
|
96
117
|
# A directory relative to the app root to find error pages for the proxy to serve.
|
|
97
|
-
#
|
|
118
|
+
# Name each page after the HTTP status code it serves, e.g. 404.html, 500.html,
|
|
119
|
+
# 502.html, 503.html, and 504.html.
|
|
98
120
|
error_pages_path: public
|
|
99
121
|
|
|
100
122
|
# Require destinations
|
|
@@ -139,6 +161,13 @@ deploy_timeout: 10
|
|
|
139
161
|
# How long to wait for a container to drain, default 30:
|
|
140
162
|
drain_timeout: 10
|
|
141
163
|
|
|
164
|
+
# Stop timeout
|
|
165
|
+
#
|
|
166
|
+
# How long to wait for a container to stop after SIGTERM, default is
|
|
167
|
+
# the drain_timeout for non-proxied roles and 10s (Docker default) for proxied roles.
|
|
168
|
+
# Can be overridden per role:
|
|
169
|
+
stop_timeout: 30
|
|
170
|
+
|
|
142
171
|
# Run directory
|
|
143
172
|
#
|
|
144
173
|
# Directory to store kamal runtime files in on the host, default `.kamal`:
|
|
@@ -186,6 +215,12 @@ boot:
|
|
|
186
215
|
logging:
|
|
187
216
|
...
|
|
188
217
|
|
|
218
|
+
# Output
|
|
219
|
+
#
|
|
220
|
+
# Configure output loggers (OTel, file), see kamal docs output:
|
|
221
|
+
output:
|
|
222
|
+
...
|
|
223
|
+
|
|
189
224
|
# Aliases
|
|
190
225
|
#
|
|
191
226
|
# Alias configuration, see kamal docs alias:
|
|
@@ -14,12 +14,14 @@ env:
|
|
|
14
14
|
|
|
15
15
|
# Secrets
|
|
16
16
|
#
|
|
17
|
-
# Kamal uses dotenv to automatically load environment variables
|
|
17
|
+
# Kamal uses dotenv to automatically load environment variables from the configured secrets files.
|
|
18
18
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
19
|
+
# Common secrets across all destinations can be set in `.kamal/secrets-common`. Kamal looks for
|
|
20
|
+
# `.kamal/secrets-common` first, then `.kamal/secrets`, with later values overriding earlier ones.
|
|
21
21
|
#
|
|
22
|
-
#
|
|
22
|
+
# If you are using destinations, Kamal looks for `.kamal/secrets-common` first, then
|
|
23
|
+
# `.kamal/secrets.<destination>`. The non-destination `.kamal/secrets` file is not read when a
|
|
24
|
+
# destination is selected.
|
|
23
25
|
#
|
|
24
26
|
# This file can be used to set variables like `KAMAL_REGISTRY_PASSWORD` or database passwords.
|
|
25
27
|
# You can use variable or command substitution in the secrets file.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Output
|
|
2
|
+
#
|
|
3
|
+
# Configure where Kamal sends command output logs.
|
|
4
|
+
|
|
5
|
+
# Output options
|
|
6
|
+
#
|
|
7
|
+
# The options are specified under the output key in the configuration file.
|
|
8
|
+
output:
|
|
9
|
+
|
|
10
|
+
# OTel
|
|
11
|
+
#
|
|
12
|
+
# Ship deploy logs to an OpenTelemetry-compatible endpoint via OTLP HTTP.
|
|
13
|
+
#
|
|
14
|
+
# Logs are sent as OTLP log records with resource attributes derived from
|
|
15
|
+
# Kamal's deploy tags (service, version, performer, destination, etc.)
|
|
16
|
+
otel:
|
|
17
|
+
endpoint: http://otel-gateway:4318
|
|
18
|
+
|
|
19
|
+
# File
|
|
20
|
+
#
|
|
21
|
+
# Write deploy logs to a file on the local machine.
|
|
22
|
+
#
|
|
23
|
+
# One log file is created per deploy, named with the timestamp and command.
|
|
24
|
+
file:
|
|
25
|
+
path: /var/log/kamal/
|
|
@@ -71,3 +71,11 @@ ssh:
|
|
|
71
71
|
# /etc/ssh_config), to false ignore config files, or to a file path
|
|
72
72
|
# (or array of paths) to load specific configuration. Defaults to true.
|
|
73
73
|
config: [ "~/.ssh/myconfig" ]
|
|
74
|
+
|
|
75
|
+
# Forward agent
|
|
76
|
+
#
|
|
77
|
+
# Whether to forward the local SSH agent to the remote host. Defaults to
|
|
78
|
+
# true (sshkit's default). Set to false when connecting through a jump
|
|
79
|
+
# host or tunnel that does not support agent forwarding (for example,
|
|
80
|
+
# Cloudflare Access for Infrastructure with SSH).
|
|
81
|
+
forward_agent: false
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class Kamal::Configuration::Output
|
|
2
|
+
include Kamal::Configuration::Validation
|
|
3
|
+
|
|
4
|
+
LOGGER_TYPES = {
|
|
5
|
+
"otel" => "Kamal::Output::OtelLogger",
|
|
6
|
+
"file" => "Kamal::Output::FileLogger"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
attr_reader :output_config, :loggers
|
|
10
|
+
|
|
11
|
+
def initialize(config:)
|
|
12
|
+
@config = config
|
|
13
|
+
@output_config = config.raw_config.output || {}
|
|
14
|
+
validate! @output_config unless @output_config.empty?
|
|
15
|
+
@loggers = build_loggers
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def enabled?
|
|
19
|
+
output_config.present?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
output_config
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
def build_loggers
|
|
28
|
+
output_config.filter_map do |key, settings|
|
|
29
|
+
if (klass_name = LOGGER_TYPES[key])
|
|
30
|
+
klass_name.constantize.build(settings: settings || {}, config: @config)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class Kamal::Configuration::Proxy::Run
|
|
2
|
-
MINIMUM_VERSION = "v0.9.
|
|
2
|
+
MINIMUM_VERSION = "v0.9.2"
|
|
3
3
|
DEFAULT_HTTP_PORT = 80
|
|
4
4
|
DEFAULT_HTTPS_PORT = 443
|
|
5
5
|
DEFAULT_LOG_MAX_SIZE = "10m"
|
|
@@ -131,6 +131,15 @@ class Kamal::Configuration::Proxy::Run
|
|
|
131
131
|
File.join apps_container_directory, config.service_and_destination
|
|
132
132
|
end
|
|
133
133
|
|
|
134
|
+
def ==(other)
|
|
135
|
+
other.is_a?(self.class) && run_config == other.run_config
|
|
136
|
+
end
|
|
137
|
+
alias_method :eql?, :==
|
|
138
|
+
|
|
139
|
+
def hash
|
|
140
|
+
run_config.hash
|
|
141
|
+
end
|
|
142
|
+
|
|
134
143
|
private
|
|
135
144
|
def format_bind_ip(ip)
|
|
136
145
|
# Ensure IPv6 address inside square brackets - e.g. [::1]
|
|
@@ -44,11 +44,11 @@ class Kamal::Configuration::Role
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def option_args
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
optionize docker_options.reject { |key, _| key.to_s == "restart" }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def restart_policy
|
|
51
|
+
restart_policy_option || "unless-stopped"
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def labels
|
|
@@ -81,11 +81,15 @@ class Kamal::Configuration::Role
|
|
|
81
81
|
|
|
82
82
|
def stop_args
|
|
83
83
|
# When deploying with the proxy, kamal-proxy will drain request before returning so we don't need to wait.
|
|
84
|
-
timeout = running_proxy? ? nil : config.drain_timeout
|
|
84
|
+
timeout = stop_timeout || (running_proxy? ? nil : config.drain_timeout)
|
|
85
85
|
|
|
86
86
|
[ *argumentize("-t", timeout) ]
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
def stop_timeout
|
|
90
|
+
specializations["stop_timeout"] || config.stop_timeout
|
|
91
|
+
end
|
|
92
|
+
|
|
89
93
|
def env(host)
|
|
90
94
|
@envs ||= {}
|
|
91
95
|
@envs[host] ||= [ config.env, specialized_env, *env_tags(host).map(&:env) ].reduce(:merge)
|
|
@@ -217,6 +221,14 @@ class Kamal::Configuration::Role
|
|
|
217
221
|
@role_config ||= config.raw_config.servers.is_a?(Array) ? {} : config.raw_config.servers[name]
|
|
218
222
|
end
|
|
219
223
|
|
|
224
|
+
def docker_options
|
|
225
|
+
specializations["options"] || {}
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def restart_policy_option
|
|
229
|
+
docker_options.find { |key, _| key.to_s == "restart" }&.last
|
|
230
|
+
end
|
|
231
|
+
|
|
220
232
|
def custom_labels
|
|
221
233
|
Hash.new.tap do |labels|
|
|
222
234
|
labels.merge!(config.labels) if config.labels.present?
|
|
@@ -53,8 +53,12 @@ class Kamal::Configuration::Ssh
|
|
|
53
53
|
ssh_config["config"]
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def forward_agent
|
|
57
|
+
ssh_config["forward_agent"]
|
|
58
|
+
end
|
|
59
|
+
|
|
56
60
|
def options
|
|
57
|
-
{ user: user, port: port, proxy: proxy, logger: logger, keepalive: true, keepalive_interval: 30, keys_only: keys_only, keys: keys, key_data: key_data, config: config
|
|
61
|
+
{ user: user, port: port, proxy: proxy, logger: logger, keepalive: true, keepalive_interval: 30, keys_only: keys_only, keys: keys, key_data: key_data, config: config, forward_agent: forward_agent }.compact
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
def to_h
|
|
@@ -29,6 +29,8 @@ class Kamal::Configuration::Validator
|
|
|
29
29
|
end
|
|
30
30
|
elsif key.to_s == "ssl"
|
|
31
31
|
validate_type! value, TrueClass, FalseClass, Hash
|
|
32
|
+
elsif key.to_s == "hooks_output"
|
|
33
|
+
validate_hooks_output!(value)
|
|
32
34
|
elsif key == "hosts"
|
|
33
35
|
validate_servers! value
|
|
34
36
|
elsif example_value.is_a?(Array)
|
|
@@ -161,6 +163,19 @@ class Kamal::Configuration::Validator
|
|
|
161
163
|
end
|
|
162
164
|
end
|
|
163
165
|
|
|
166
|
+
def validate_hooks_output!(value)
|
|
167
|
+
# hooks_output can be either a symbol/string (global) or a hash (per-hook)
|
|
168
|
+
if value.is_a?(Hash)
|
|
169
|
+
value.each do |hook, level|
|
|
170
|
+
with_context(hook) do
|
|
171
|
+
validate_type! level, String, Symbol
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
else
|
|
175
|
+
validate_type! value, String, Symbol
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
164
179
|
def validate_type!(value, *types)
|
|
165
180
|
type_error(*types) unless types.any? { |type| valid_type?(value, type) }
|
|
166
181
|
end
|
|
@@ -217,8 +232,20 @@ class Kamal::Configuration::Validator
|
|
|
217
232
|
end
|
|
218
233
|
|
|
219
234
|
def validate_docker_options!(options)
|
|
220
|
-
if options
|
|
221
|
-
|
|
235
|
+
if restart_policy = options&.find { |key, _| key.to_s == "restart" }
|
|
236
|
+
validate_restart_policy!(restart_policy.last)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def validate_restart_policy!(restart_policy)
|
|
241
|
+
with_context("options/restart") do
|
|
242
|
+
unless restart_policy.is_a?(String)
|
|
243
|
+
error %(should be a string. Use "no" to disable restarts)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
unless restart_policy.match?(/\A(?:no|always|unless-stopped|on-failure(?::\d+)?)\z/)
|
|
247
|
+
error "should be no, always, unless-stopped, on-failure, or on-failure:N"
|
|
248
|
+
end
|
|
222
249
|
end
|
|
223
250
|
end
|
|
224
251
|
end
|
data/lib/kamal/configuration.rb
CHANGED
|
@@ -6,11 +6,13 @@ require "erb"
|
|
|
6
6
|
require "net/ssh/proxy/jump"
|
|
7
7
|
|
|
8
8
|
class Kamal::Configuration
|
|
9
|
+
HOOKS_OUTPUT_LEVELS = [ :quiet, :verbose ].freeze
|
|
10
|
+
|
|
9
11
|
delegate :service, :labels, :hooks_path, to: :raw_config, allow_nil: true
|
|
10
12
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
|
11
13
|
|
|
12
14
|
attr_reader :destination, :raw_config, :secrets
|
|
13
|
-
attr_reader :accessories, :aliases, :boot, :builder, :env, :logging, :proxy, :proxy_boot, :servers, :ssh, :sshkit, :registry
|
|
15
|
+
attr_reader :accessories, :aliases, :boot, :builder, :env, :logging, :output, :proxy, :proxy_boot, :servers, :ssh, :sshkit, :registry
|
|
14
16
|
|
|
15
17
|
include Validation
|
|
16
18
|
|
|
@@ -18,11 +20,15 @@ class Kamal::Configuration
|
|
|
18
20
|
def create_from(config_file:, destination: nil, version: nil)
|
|
19
21
|
ENV["KAMAL_DESTINATION"] = destination
|
|
20
22
|
|
|
21
|
-
raw_config =
|
|
23
|
+
raw_config = load_raw_config(config_file: config_file, destination: destination)
|
|
22
24
|
|
|
23
25
|
new raw_config, destination: destination, version: version
|
|
24
26
|
end
|
|
25
27
|
|
|
28
|
+
def load_raw_config(config_file:, destination: nil)
|
|
29
|
+
load_config_files(config_file, *destination_config_file(config_file, destination))
|
|
30
|
+
end
|
|
31
|
+
|
|
26
32
|
private
|
|
27
33
|
def load_config_files(*files)
|
|
28
34
|
files.inject({}) { |config, file| config.deep_merge! load_config_file(file) }
|
|
@@ -32,7 +38,9 @@ class Kamal::Configuration
|
|
|
32
38
|
if file.exist?
|
|
33
39
|
# Newer Psych doesn't load aliases by default
|
|
34
40
|
load_method = YAML.respond_to?(:unsafe_load) ? :unsafe_load : :load
|
|
35
|
-
|
|
41
|
+
template = File.read(file)
|
|
42
|
+
rendered = ERB.new(template, trim_mode: "-").result
|
|
43
|
+
YAML.send(load_method, rendered).symbolize_keys
|
|
36
44
|
else
|
|
37
45
|
raise "Configuration file not found in #{file}"
|
|
38
46
|
end
|
|
@@ -63,6 +71,7 @@ class Kamal::Configuration
|
|
|
63
71
|
@env = Env.new(config: @raw_config.env || {}, secrets: secrets)
|
|
64
72
|
|
|
65
73
|
@logging = Logging.new(logging_config: @raw_config.logging)
|
|
74
|
+
@output = Output.new(config: self)
|
|
66
75
|
@proxy = Proxy.new(config: self, proxy_config: @raw_config.proxy, secrets: secrets)
|
|
67
76
|
@proxy_boot = Proxy::Boot.new(config: self)
|
|
68
77
|
@ssh = Ssh.new(config: self)
|
|
@@ -78,6 +87,7 @@ class Kamal::Configuration
|
|
|
78
87
|
ensure_unique_hosts_for_ssl_roles
|
|
79
88
|
ensure_local_registry_remote_builder_has_ssh_url
|
|
80
89
|
ensure_no_conflicting_proxy_runs
|
|
90
|
+
ensure_valid_hooks_output!
|
|
81
91
|
end
|
|
82
92
|
|
|
83
93
|
def version=(version)
|
|
@@ -231,6 +241,10 @@ class Kamal::Configuration
|
|
|
231
241
|
raw_config.drain_timeout || 30
|
|
232
242
|
end
|
|
233
243
|
|
|
244
|
+
def stop_timeout
|
|
245
|
+
raw_config.stop_timeout
|
|
246
|
+
end
|
|
247
|
+
|
|
234
248
|
def run_directory
|
|
235
249
|
".kamal"
|
|
236
250
|
end
|
|
@@ -279,6 +293,15 @@ class Kamal::Configuration
|
|
|
279
293
|
env_tags.detect { |t| t.name == name.to_s }
|
|
280
294
|
end
|
|
281
295
|
|
|
296
|
+
def hooks_output_for(hook)
|
|
297
|
+
case raw_config.hooks_output
|
|
298
|
+
when Symbol, String
|
|
299
|
+
raw_config.hooks_output.to_sym
|
|
300
|
+
when Hash
|
|
301
|
+
raw_config.hooks_output[hook]&.to_sym
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
282
305
|
def to_h
|
|
283
306
|
{
|
|
284
307
|
roles: role_names,
|
|
@@ -409,6 +432,21 @@ class Kamal::Configuration
|
|
|
409
432
|
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
|
410
433
|
end
|
|
411
434
|
|
|
435
|
+
def ensure_valid_hooks_output!
|
|
436
|
+
case raw_config.hooks_output
|
|
437
|
+
when Symbol, String
|
|
438
|
+
validate_hooks_output_level!(raw_config.hooks_output.to_sym)
|
|
439
|
+
when Hash
|
|
440
|
+
raw_config.hooks_output.each { |hook, level| validate_hooks_output_level!(level.to_sym, hook) }
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def validate_hooks_output_level!(level, hook = nil)
|
|
445
|
+
return if HOOKS_OUTPUT_LEVELS.include?(level)
|
|
446
|
+
context = hook ? " for hook '#{hook}'" : ""
|
|
447
|
+
raise Kamal::ConfigurationError, "Invalid hooks_output '#{level}'#{context}, must be one of: #{HOOKS_OUTPUT_LEVELS.join(', ')}"
|
|
448
|
+
end
|
|
449
|
+
|
|
412
450
|
def git_version
|
|
413
451
|
@git_version ||=
|
|
414
452
|
if Kamal::Git.used?
|