kamal 2.0.0.alpha → 2.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/kamal/cli/accessory.rb +44 -20
- data/lib/kamal/cli/app/boot.rb +22 -16
- data/lib/kamal/cli/app.rb +37 -3
- data/lib/kamal/cli/base.rb +21 -49
- data/lib/kamal/cli/build.rb +21 -14
- data/lib/kamal/cli/healthcheck/barrier.rb +2 -0
- data/lib/kamal/cli/healthcheck/poller.rb +18 -39
- data/lib/kamal/cli/lock.rb +2 -3
- data/lib/kamal/cli/main.rb +54 -51
- data/lib/kamal/cli/proxy.rb +213 -0
- data/lib/kamal/cli/prune.rb +0 -1
- data/lib/kamal/cli/secrets.rb +36 -0
- data/lib/kamal/cli/server.rb +0 -2
- data/lib/kamal/cli/templates/deploy.yml +0 -11
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/secrets +16 -0
- data/lib/kamal/cli.rb +1 -0
- data/lib/kamal/commander/specifics.rb +3 -3
- data/lib/kamal/commander.rb +17 -9
- data/lib/kamal/commands/accessory.rb +7 -7
- data/lib/kamal/commands/app/assets.rb +8 -8
- data/lib/kamal/commands/app/execution.rb +1 -0
- data/lib/kamal/commands/app/proxy.rb +16 -0
- data/lib/kamal/commands/app.rb +7 -15
- data/lib/kamal/commands/auditor.rb +6 -3
- data/lib/kamal/commands/base.rb +8 -0
- data/lib/kamal/commands/builder/base.rb +2 -6
- data/lib/kamal/commands/builder/hybrid.rb +1 -1
- data/lib/kamal/commands/builder/remote.rb +27 -4
- data/lib/kamal/commands/builder.rb +1 -1
- data/lib/kamal/commands/docker.rb +4 -0
- data/lib/kamal/commands/hook.rb +8 -2
- data/lib/kamal/commands/lock.rb +2 -6
- data/lib/kamal/commands/proxy.rb +72 -0
- data/lib/kamal/commands/prune.rb +1 -9
- data/lib/kamal/commands/server.rb +11 -1
- data/lib/kamal/configuration/accessory.rb +14 -2
- data/lib/kamal/configuration/builder.rb +9 -3
- data/lib/kamal/configuration/docs/builder.yml +20 -10
- data/lib/kamal/configuration/docs/configuration.yml +16 -16
- data/lib/kamal/configuration/docs/env.yml +10 -11
- data/lib/kamal/configuration/docs/proxy.yml +100 -0
- data/lib/kamal/configuration/docs/registry.yml +4 -2
- data/lib/kamal/configuration/docs/role.yml +3 -5
- data/lib/kamal/configuration/env/tag.rb +4 -3
- data/lib/kamal/configuration/env.rb +10 -17
- data/lib/kamal/configuration/proxy.rb +66 -0
- data/lib/kamal/configuration/registry.rb +3 -2
- data/lib/kamal/configuration/role.rb +63 -94
- data/lib/kamal/configuration/validator/builder.rb +2 -0
- data/lib/kamal/configuration/validator/proxy.rb +11 -0
- data/lib/kamal/configuration/validator.rb +3 -1
- data/lib/kamal/configuration.rb +80 -33
- data/lib/kamal/env_file.rb +4 -0
- data/lib/kamal/secrets/adapters/base.rb +18 -0
- data/lib/kamal/secrets/adapters/bitwarden.rb +64 -0
- data/lib/kamal/secrets/adapters/last_pass.rb +30 -0
- data/lib/kamal/secrets/adapters/one_password.rb +61 -0
- data/lib/kamal/secrets/adapters/test.rb +10 -0
- data/lib/kamal/secrets/adapters.rb +14 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +32 -0
- data/lib/kamal/secrets.rb +37 -0
- data/lib/kamal/sshkit_with_ext.rb +1 -0
- data/lib/kamal/utils.rb +12 -0
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +3 -1
- metadata +23 -16
- data/lib/kamal/cli/env.rb +0 -54
- data/lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample +0 -3
- data/lib/kamal/cli/templates/template.env +0 -2
- data/lib/kamal/cli/traefik.rb +0 -122
- data/lib/kamal/commands/app/cord.rb +0 -22
- data/lib/kamal/commands/traefik.rb +0 -85
- data/lib/kamal/configuration/docs/healthcheck.yml +0 -59
- data/lib/kamal/configuration/docs/traefik.yml +0 -62
- data/lib/kamal/configuration/healthcheck.rb +0 -63
- data/lib/kamal/configuration/traefik.rb +0 -60
- /data/lib/kamal/cli/templates/sample_hooks/{pre-traefik-reboot.sample → pre-proxy-reboot.sample} +0 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
# Proxy
|
2
|
+
#
|
3
|
+
# Kamal uses [kamal-proxy](https://github.com/basecamp/kamal-proxy) to provide
|
4
|
+
# gapless deployments. It runs on ports 80 and 443 and forwards requests to the
|
5
|
+
# application container.
|
6
|
+
#
|
7
|
+
# The proxy is configured in the root configuration under `proxy`. These are
|
8
|
+
# options that are set when deploying the application, not when booting the proxy
|
9
|
+
#
|
10
|
+
# They are application specific, so are not shared when multiple applications
|
11
|
+
# run on the same proxy.
|
12
|
+
#
|
13
|
+
# The proxy is enabled by default on the primary role, but can be disabled by
|
14
|
+
# setting `proxy: false`.
|
15
|
+
#
|
16
|
+
# It is disabled by default on all other roles, but can be enabled by setting
|
17
|
+
# `proxy: true`, or providing a proxy configuration.
|
18
|
+
proxy:
|
19
|
+
|
20
|
+
# Host
|
21
|
+
#
|
22
|
+
# The hosts that will be used to serve the app. The proxy will only route requests
|
23
|
+
# to this host to your app.
|
24
|
+
#
|
25
|
+
# If no hosts are set, then all requests will be forwarded, except for matching
|
26
|
+
# requests for other apps deployed on that server that do have a host set.
|
27
|
+
host: foo.example.com
|
28
|
+
|
29
|
+
# App port
|
30
|
+
#
|
31
|
+
# The port the application container is exposed on
|
32
|
+
#
|
33
|
+
# Defaults to 80
|
34
|
+
app_port: 3000
|
35
|
+
|
36
|
+
# SSL
|
37
|
+
#
|
38
|
+
# kamal-proxy can provide automatic HTTPS for your application via Let's Encrypt.
|
39
|
+
#
|
40
|
+
# This requires that we are deploying to a one server and the host option is set.
|
41
|
+
# The host value must point to the server we are deploying to and port 443 must be
|
42
|
+
# open for the Let's Encrypt challenge to succeed.
|
43
|
+
#
|
44
|
+
# Defaults to false
|
45
|
+
ssl: true
|
46
|
+
|
47
|
+
# Response timeout
|
48
|
+
#
|
49
|
+
# How long to wait for requests to complete before timing out, defaults to 30 seconds
|
50
|
+
response_timeout: 10s
|
51
|
+
|
52
|
+
# Healthcheck
|
53
|
+
#
|
54
|
+
# When deploying, the proxy will by default hit /up once every second until we hit
|
55
|
+
# the deploy timeout, with a 5 second timeout for each request.
|
56
|
+
#
|
57
|
+
# Once the app is up, the proxy will stop hitting the healthcheck endpoint.
|
58
|
+
healthcheck:
|
59
|
+
interval: 3
|
60
|
+
path: /health
|
61
|
+
timeout: 3
|
62
|
+
|
63
|
+
# Buffering
|
64
|
+
#
|
65
|
+
# Whether to buffer request and response bodies in the proxy
|
66
|
+
#
|
67
|
+
# By default buffering is enabled with a max request body size of 1GB and no limit
|
68
|
+
# for response size.
|
69
|
+
#
|
70
|
+
# You can also set the memory limit for buffering, which defaults to 1MB, anything
|
71
|
+
# larger than that is written to disk.
|
72
|
+
buffering:
|
73
|
+
requests: true
|
74
|
+
responses: true
|
75
|
+
max_request_body: 40_000_000
|
76
|
+
max_response_body: 0
|
77
|
+
memory: 2_000_000
|
78
|
+
|
79
|
+
# Logging
|
80
|
+
#
|
81
|
+
# Configure request logging for the proxy
|
82
|
+
# You can specify request and response headers to log.
|
83
|
+
# By default, Cache-Control, Last-Modified and User-Agent request headers are logged
|
84
|
+
logging:
|
85
|
+
request_headers:
|
86
|
+
- Cache-Control
|
87
|
+
- X-Forwarded-Proto
|
88
|
+
response_headers:
|
89
|
+
- X-Request-ID
|
90
|
+
- X-Request-Start
|
91
|
+
|
92
|
+
# Forward headers
|
93
|
+
#
|
94
|
+
# Whether to forward the X-Forwarded-For and X-Forwarded-Proto headers (defaults to false)
|
95
|
+
#
|
96
|
+
# If you are behind a trusted proxy, you can set this to true to forward the headers.
|
97
|
+
#
|
98
|
+
# By default kamal-proxy will not forward the headers the ssl option is set to true, and
|
99
|
+
# will forward them if it is set to false.
|
100
|
+
forward_headers: true
|
@@ -27,11 +27,13 @@ registry:
|
|
27
27
|
# and [set up roles and permissions](https://cloud.google.com/artifact-registry/docs/access-control#permissions).
|
28
28
|
# Normally, assigning a roles/artifactregistry.writer role should be sufficient.
|
29
29
|
#
|
30
|
-
# Once the service account is ready, you need to generate and download a JSON key
|
30
|
+
# Once the service account is ready, you need to generate and download a JSON key and base64 encode it:
|
31
31
|
#
|
32
32
|
# ```shell
|
33
|
-
#
|
33
|
+
# base64 -i /path/to/key.json | tr -d "\\n")
|
34
34
|
# ```
|
35
|
+
# You'll then need to set the KAMAL_REGISTRY_PASSWORD secret to that value.
|
36
|
+
#
|
35
37
|
# Use the env variable as password along with _json_key_base64 as username.
|
36
38
|
# Here’s the final configuration:
|
37
39
|
|
@@ -26,7 +26,7 @@ servers:
|
|
26
26
|
#
|
27
27
|
# When there are other options to set, the list of hosts goes under the `hosts` key
|
28
28
|
#
|
29
|
-
# By default only the primary role uses
|
29
|
+
# By default only the primary role uses a proxy, but you can set `proxy` to change
|
30
30
|
# it.
|
31
31
|
#
|
32
32
|
# You can also set a custom cmd to run in the container, and overwrite other settings
|
@@ -35,18 +35,16 @@ servers:
|
|
35
35
|
hosts:
|
36
36
|
- 172.1.0.3
|
37
37
|
- 172.1.0.4: experiment1
|
38
|
-
traefik: true
|
39
38
|
cmd: "bin/jobs"
|
40
39
|
options:
|
41
40
|
memory: 2g
|
42
41
|
cpus: 4
|
43
|
-
healthcheck:
|
44
|
-
...
|
45
42
|
logging:
|
46
43
|
...
|
44
|
+
proxy:
|
45
|
+
...
|
47
46
|
labels:
|
48
47
|
my-label: workers
|
49
48
|
env:
|
50
49
|
...
|
51
50
|
asset_path: /public
|
52
|
-
|
@@ -1,12 +1,13 @@
|
|
1
1
|
class Kamal::Configuration::Env::Tag
|
2
|
-
attr_reader :name, :config
|
2
|
+
attr_reader :name, :config, :secrets
|
3
3
|
|
4
|
-
def initialize(name, config:)
|
4
|
+
def initialize(name, config:, secrets:)
|
5
5
|
@name = name
|
6
6
|
@config = config
|
7
|
+
@secrets = secrets
|
7
8
|
end
|
8
9
|
|
9
10
|
def env
|
10
|
-
Kamal::Configuration::Env.new(config: config)
|
11
|
+
Kamal::Configuration::Env.new(config: config, secrets: secrets)
|
11
12
|
end
|
12
13
|
end
|
@@ -1,36 +1,29 @@
|
|
1
1
|
class Kamal::Configuration::Env
|
2
2
|
include Kamal::Configuration::Validation
|
3
3
|
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :context, :secrets
|
5
|
+
attr_reader :clear, :secret_keys
|
5
6
|
delegate :argumentize, to: Kamal::Utils
|
6
7
|
|
7
|
-
def initialize(config:,
|
8
|
+
def initialize(config:, secrets:, context: "env")
|
8
9
|
@clear = config.fetch("clear", config.key?("secret") || config.key?("tags") ? {} : config)
|
9
|
-
@
|
10
|
-
@
|
10
|
+
@secrets = secrets
|
11
|
+
@secret_keys = config.fetch("secret", [])
|
11
12
|
@context = context
|
12
13
|
validate! config, context: context, with: Kamal::Configuration::Validator::Env
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
-
|
16
|
+
def clear_args
|
17
|
+
argumentize("--env", clear)
|
17
18
|
end
|
18
19
|
|
19
20
|
def secrets_io
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
def secrets
|
24
|
-
@secrets ||= secrets_keys.to_h { |key| [ key, ENV.fetch(key) ] }
|
25
|
-
end
|
26
|
-
|
27
|
-
def secrets_directory
|
28
|
-
File.dirname(secrets_file)
|
21
|
+
Kamal::EnvFile.new(secret_keys.to_h { |key| [ key, secrets[key] ] }).to_io
|
29
22
|
end
|
30
23
|
|
31
24
|
def merge(other)
|
32
25
|
self.class.new \
|
33
|
-
config: { "clear" => clear.merge(other.clear), "secret" =>
|
34
|
-
|
26
|
+
config: { "clear" => clear.merge(other.clear), "secret" => secret_keys | other.secret_keys },
|
27
|
+
secrets: secrets
|
35
28
|
end
|
36
29
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class Kamal::Configuration::Proxy
|
2
|
+
include Kamal::Configuration::Validation
|
3
|
+
|
4
|
+
DEFAULT_LOG_REQUEST_HEADERS = [ "Cache-Control", "Last-Modified", "User-Agent" ]
|
5
|
+
CONTAINER_NAME = "kamal-proxy"
|
6
|
+
|
7
|
+
delegate :argumentize, :optionize, to: Kamal::Utils
|
8
|
+
|
9
|
+
attr_reader :config, :proxy_config
|
10
|
+
|
11
|
+
def initialize(config:, proxy_config:, context: "proxy")
|
12
|
+
@config = config
|
13
|
+
@proxy_config = proxy_config
|
14
|
+
validate! @proxy_config, with: Kamal::Configuration::Validator::Proxy, context: context
|
15
|
+
end
|
16
|
+
|
17
|
+
def app_port
|
18
|
+
proxy_config.fetch("app_port", 80)
|
19
|
+
end
|
20
|
+
|
21
|
+
def ssl?
|
22
|
+
proxy_config.fetch("ssl", false)
|
23
|
+
end
|
24
|
+
|
25
|
+
def host
|
26
|
+
proxy_config["host"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def deploy_options
|
30
|
+
{
|
31
|
+
host: proxy_config["host"],
|
32
|
+
tls: proxy_config["ssl"],
|
33
|
+
"deploy-timeout": seconds_duration(config.deploy_timeout),
|
34
|
+
"drain-timeout": seconds_duration(config.drain_timeout),
|
35
|
+
"health-check-interval": seconds_duration(proxy_config.dig("healthcheck", "interval")),
|
36
|
+
"health-check-timeout": seconds_duration(proxy_config.dig("healthcheck", "timeout")),
|
37
|
+
"health-check-path": proxy_config.dig("healthcheck", "path"),
|
38
|
+
"target-timeout": seconds_duration(proxy_config["response_timeout"]),
|
39
|
+
"buffer-requests": proxy_config.fetch("buffering", { "requests": true }).fetch("requests", true),
|
40
|
+
"buffer-responses": proxy_config.fetch("buffering", { "responses": true }).fetch("responses", true),
|
41
|
+
"buffer-memory": proxy_config.dig("buffering", "memory"),
|
42
|
+
"max-request-body": proxy_config.dig("buffering", "max_request_body"),
|
43
|
+
"max-response-body": proxy_config.dig("buffering", "max_response_body"),
|
44
|
+
"forward-headers": proxy_config.dig("forward_headers"),
|
45
|
+
"log-request-header": proxy_config.dig("logging", "request_headers") || DEFAULT_LOG_REQUEST_HEADERS,
|
46
|
+
"log-response-header": proxy_config.dig("logging", "response_headers")
|
47
|
+
}.compact
|
48
|
+
end
|
49
|
+
|
50
|
+
def deploy_command_args(target:)
|
51
|
+
optionize ({ target: "#{target}:#{app_port}" }).merge(deploy_options)
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_command_args(target:)
|
55
|
+
optionize({ target: "#{target}:#{app_port}" })
|
56
|
+
end
|
57
|
+
|
58
|
+
def merge(other)
|
59
|
+
self.class.new config: config, proxy_config: proxy_config.deep_merge(other.proxy_config)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def seconds_duration(value)
|
64
|
+
value ? "#{value}s" : nil
|
65
|
+
end
|
66
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
class Kamal::Configuration::Registry
|
2
2
|
include Kamal::Configuration::Validation
|
3
3
|
|
4
|
-
attr_reader :registry_config
|
4
|
+
attr_reader :registry_config, :secrets
|
5
5
|
|
6
6
|
def initialize(config:)
|
7
7
|
@registry_config = config.raw_config.registry || {}
|
8
|
+
@secrets = config.secrets
|
8
9
|
validate! registry_config, with: Kamal::Configuration::Validator::Registry
|
9
10
|
end
|
10
11
|
|
@@ -23,7 +24,7 @@ class Kamal::Configuration::Registry
|
|
23
24
|
private
|
24
25
|
def lookup(key)
|
25
26
|
if registry_config[key].is_a?(Array)
|
26
|
-
|
27
|
+
secrets[registry_config[key].first]
|
27
28
|
else
|
28
29
|
registry_config[key]
|
29
30
|
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
class Kamal::Configuration::Role
|
2
2
|
include Kamal::Configuration::Validation
|
3
3
|
|
4
|
-
CORD_FILE = "cord"
|
5
4
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
6
5
|
|
7
|
-
attr_reader :name, :config, :specialized_env, :specialized_logging, :
|
6
|
+
attr_reader :name, :config, :specialized_env, :specialized_logging, :specialized_proxy
|
8
7
|
|
9
8
|
alias to_s name
|
10
9
|
|
@@ -18,16 +17,14 @@ class Kamal::Configuration::Role
|
|
18
17
|
|
19
18
|
@specialized_env = Kamal::Configuration::Env.new \
|
20
19
|
config: specializations.fetch("env", {}),
|
21
|
-
|
20
|
+
secrets: config.secrets,
|
22
21
|
context: "servers/#{name}/env"
|
23
22
|
|
24
23
|
@specialized_logging = Kamal::Configuration::Logging.new \
|
25
24
|
logging_config: specializations.fetch("logging", {}),
|
26
25
|
context: "servers/#{name}/logging"
|
27
26
|
|
28
|
-
|
29
|
-
healthcheck_config: specializations.fetch("healthcheck", {}),
|
30
|
-
context: "servers/#{name}/healthcheck"
|
27
|
+
initialize_specialized_proxy
|
31
28
|
end
|
32
29
|
|
33
30
|
def primary_host
|
@@ -55,7 +52,7 @@ class Kamal::Configuration::Role
|
|
55
52
|
end
|
56
53
|
|
57
54
|
def labels
|
58
|
-
default_labels.merge(
|
55
|
+
default_labels.merge(custom_labels)
|
59
56
|
end
|
60
57
|
|
61
58
|
def label_args
|
@@ -70,87 +67,53 @@ class Kamal::Configuration::Role
|
|
70
67
|
@logging ||= config.logging.merge(specialized_logging)
|
71
68
|
end
|
72
69
|
|
73
|
-
|
74
|
-
|
75
|
-
@envs ||= {}
|
76
|
-
@envs[host] ||= [ config.env, specialized_env, *env_tags(host).map(&:env) ].reduce(:merge)
|
70
|
+
def proxy
|
71
|
+
@proxy ||= config.proxy.merge(specialized_proxy) if running_proxy?
|
77
72
|
end
|
78
73
|
|
79
|
-
def
|
80
|
-
|
74
|
+
def running_proxy?
|
75
|
+
@running_proxy
|
81
76
|
end
|
82
77
|
|
83
|
-
def
|
84
|
-
|
78
|
+
def ssl?
|
79
|
+
running_proxy? && proxy.ssl?
|
85
80
|
end
|
86
81
|
|
82
|
+
def stop_args
|
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
|
87
85
|
|
88
|
-
|
89
|
-
if running_traefik? || healthcheck.set_port_or_path?
|
90
|
-
if cord && uses_cord?
|
91
|
-
optionize({ "health-cmd" => health_check_cmd_with_cord, "health-interval" => healthcheck.interval })
|
92
|
-
.concat(cord_volume.docker_args)
|
93
|
-
else
|
94
|
-
optionize({ "health-cmd" => healthcheck.cmd, "health-interval" => healthcheck.interval })
|
95
|
-
end
|
96
|
-
else
|
97
|
-
[]
|
98
|
-
end
|
86
|
+
[ *argumentize("-t", timeout) ]
|
99
87
|
end
|
100
88
|
|
101
|
-
def
|
102
|
-
@
|
103
|
-
|
104
|
-
config.healthcheck.merge(specialized_healthcheck)
|
105
|
-
else
|
106
|
-
specialized_healthcheck
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def health_check_cmd_with_cord
|
111
|
-
"(#{healthcheck.cmd}) && (stat #{cord_container_file} > /dev/null || exit 1)"
|
112
|
-
end
|
113
|
-
|
114
|
-
|
115
|
-
def running_traefik?
|
116
|
-
if specializations["traefik"].nil?
|
117
|
-
primary?
|
118
|
-
else
|
119
|
-
specializations["traefik"]
|
120
|
-
end
|
89
|
+
def env(host)
|
90
|
+
@envs ||= {}
|
91
|
+
@envs[host] ||= [ config.env, specialized_env, *env_tags(host).map(&:env) ].reduce(:merge)
|
121
92
|
end
|
122
93
|
|
123
|
-
def
|
124
|
-
|
94
|
+
def env_args(host)
|
95
|
+
[ *env(host).clear_args, *argumentize("--env-file", secrets_path) ]
|
125
96
|
end
|
126
97
|
|
127
|
-
|
128
|
-
|
129
|
-
running_traefik? && cord_volume && healthcheck.cmd.present?
|
98
|
+
def env_directory
|
99
|
+
File.join(config.env_directory, "roles")
|
130
100
|
end
|
131
101
|
|
132
|
-
def
|
133
|
-
|
102
|
+
def secrets_io(host)
|
103
|
+
env(host).secrets_io
|
134
104
|
end
|
135
105
|
|
136
|
-
def
|
137
|
-
|
138
|
-
@cord_volume ||= Kamal::Configuration::Volume.new \
|
139
|
-
host_path: File.join(config.run_directory, "cords", [ container_prefix, config.run_id ].join("-")),
|
140
|
-
container_path: cord
|
141
|
-
end
|
106
|
+
def secrets_path
|
107
|
+
File.join(config.env_directory, "roles", "#{name}.env")
|
142
108
|
end
|
143
109
|
|
144
|
-
def
|
145
|
-
|
110
|
+
def asset_volume_args
|
111
|
+
asset_volume&.docker_args
|
146
112
|
end
|
147
113
|
|
148
|
-
def cord_container_directory
|
149
|
-
health_check_options.fetch("cord", nil)
|
150
|
-
end
|
151
114
|
|
152
|
-
def
|
153
|
-
|
115
|
+
def primary?
|
116
|
+
name == @config.primary_role_name
|
154
117
|
end
|
155
118
|
|
156
119
|
|
@@ -168,25 +131,52 @@ class Kamal::Configuration::Role
|
|
168
131
|
end
|
169
132
|
|
170
133
|
def assets?
|
171
|
-
asset_path.present? &&
|
134
|
+
asset_path.present? && running_proxy?
|
172
135
|
end
|
173
136
|
|
174
|
-
def asset_volume(version =
|
137
|
+
def asset_volume(version = config.version)
|
175
138
|
if assets?
|
176
139
|
Kamal::Configuration::Volume.new \
|
177
|
-
host_path:
|
140
|
+
host_path: asset_volume_directory(version), container_path: asset_path
|
178
141
|
end
|
179
142
|
end
|
180
143
|
|
181
|
-
def
|
182
|
-
File.join config.
|
144
|
+
def asset_extracted_directory(version = config.version)
|
145
|
+
File.join config.assets_directory, "extracted", [ name, version ].join("-")
|
183
146
|
end
|
184
147
|
|
185
|
-
def
|
186
|
-
File.join config.
|
148
|
+
def asset_volume_directory(version = config.version)
|
149
|
+
File.join config.assets_directory, "volumes", [ name, version ].join("-")
|
150
|
+
end
|
151
|
+
|
152
|
+
def ensure_one_host_for_ssl
|
153
|
+
if running_proxy? && proxy.ssl? && hosts.size > 1
|
154
|
+
raise Kamal::ConfigurationError, "SSL is only supported on a single server, found #{hosts.size} servers for role #{name}"
|
155
|
+
end
|
187
156
|
end
|
188
157
|
|
189
158
|
private
|
159
|
+
def initialize_specialized_proxy
|
160
|
+
proxy_specializations = specializations["proxy"]
|
161
|
+
|
162
|
+
if primary?
|
163
|
+
# only false means no proxy for non-primary roles
|
164
|
+
@running_proxy = proxy_specializations != false
|
165
|
+
else
|
166
|
+
# false and nil both mean no proxy for non-primary roles
|
167
|
+
@running_proxy = !!proxy_specializations
|
168
|
+
end
|
169
|
+
|
170
|
+
if running_proxy?
|
171
|
+
proxy_config = proxy_specializations == true || proxy_specializations.nil? ? {} : proxy_specializations
|
172
|
+
|
173
|
+
@specialized_proxy = Kamal::Configuration::Proxy.new \
|
174
|
+
config: config,
|
175
|
+
proxy_config: proxy_config,
|
176
|
+
context: "servers/#{name}/proxy"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
190
180
|
def tagged_hosts
|
191
181
|
{}.tap do |tagged_hosts|
|
192
182
|
extract_hosts_from_config.map do |host_config|
|
@@ -221,27 +211,6 @@ class Kamal::Configuration::Role
|
|
221
211
|
end
|
222
212
|
end
|
223
213
|
|
224
|
-
def traefik_labels
|
225
|
-
if running_traefik?
|
226
|
-
{
|
227
|
-
# Setting a service property ensures that the generated service name will be consistent between versions
|
228
|
-
"traefik.http.services.#{traefik_service}.loadbalancer.server.scheme" => "http",
|
229
|
-
|
230
|
-
"traefik.http.routers.#{traefik_service}.rule" => "PathPrefix(`/`)",
|
231
|
-
"traefik.http.routers.#{traefik_service}.priority" => "2",
|
232
|
-
"traefik.http.middlewares.#{traefik_service}-retry.retry.attempts" => "5",
|
233
|
-
"traefik.http.middlewares.#{traefik_service}-retry.retry.initialinterval" => "500ms",
|
234
|
-
"traefik.http.routers.#{traefik_service}.middlewares" => "#{traefik_service}-retry@docker"
|
235
|
-
}
|
236
|
-
else
|
237
|
-
{}
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
def traefik_service
|
242
|
-
container_prefix
|
243
|
-
end
|
244
|
-
|
245
214
|
def custom_labels
|
246
215
|
Hash.new.tap do |labels|
|
247
216
|
labels.merge!(config.labels) if config.labels.present?
|
@@ -7,5 +7,7 @@ class Kamal::Configuration::Validator::Builder < Kamal::Configuration::Validator
|
|
7
7
|
end
|
8
8
|
|
9
9
|
error "Builder arch not set" unless config["arch"].present?
|
10
|
+
|
11
|
+
error "Cannot disable local builds, no remote is set" if config["local"] == false && config["remote"].blank?
|
10
12
|
end
|
11
13
|
end
|
@@ -24,7 +24,9 @@ class Kamal::Configuration::Validator
|
|
24
24
|
example_value = example[key]
|
25
25
|
|
26
26
|
if example_value == "..."
|
27
|
-
|
27
|
+
unless key.to_s == "proxy" && boolean?(value.class)
|
28
|
+
validate_type! value, *(Array if key == :servers), Hash
|
29
|
+
end
|
28
30
|
elsif key == "hosts"
|
29
31
|
validate_servers! value
|
30
32
|
elsif example_value.is_a?(Array)
|