dash 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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +13 -0
- data/bin/dash +18 -0
- data/bin/kamal +18 -0
- data/lib/kamal/cli/accessory.rb +342 -0
- data/lib/kamal/cli/alias/command.rb +10 -0
- data/lib/kamal/cli/app/assets.rb +24 -0
- data/lib/kamal/cli/app/boot.rb +126 -0
- data/lib/kamal/cli/app/error_pages.rb +33 -0
- data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
- data/lib/kamal/cli/app.rb +368 -0
- data/lib/kamal/cli/base.rb +324 -0
- data/lib/kamal/cli/build/clone.rb +59 -0
- data/lib/kamal/cli/build/port_forwarding.rb +66 -0
- data/lib/kamal/cli/build.rb +242 -0
- data/lib/kamal/cli/healthcheck/barrier.rb +33 -0
- data/lib/kamal/cli/healthcheck/error.rb +2 -0
- data/lib/kamal/cli/healthcheck/poller.rb +42 -0
- data/lib/kamal/cli/lock.rb +34 -0
- data/lib/kamal/cli/main.rb +299 -0
- data/lib/kamal/cli/proxy.rb +419 -0
- data/lib/kamal/cli/prune.rb +34 -0
- data/lib/kamal/cli/registry.rb +49 -0
- data/lib/kamal/cli/secrets.rb +50 -0
- data/lib/kamal/cli/server.rb +70 -0
- data/lib/kamal/cli/templates/deploy.yml +102 -0
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +14 -0
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +51 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +47 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +122 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/secrets +22 -0
- data/lib/kamal/cli.rb +9 -0
- data/lib/kamal/commander/specifics.rb +62 -0
- data/lib/kamal/commander.rb +230 -0
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +118 -0
- data/lib/kamal/commands/app/assets.rb +51 -0
- data/lib/kamal/commands/app/containers.rb +31 -0
- data/lib/kamal/commands/app/error_pages.rb +9 -0
- data/lib/kamal/commands/app/execution.rb +38 -0
- data/lib/kamal/commands/app/images.rb +13 -0
- data/lib/kamal/commands/app/logging.rb +28 -0
- data/lib/kamal/commands/app/proxy.rb +32 -0
- data/lib/kamal/commands/app.rb +125 -0
- data/lib/kamal/commands/auditor.rb +39 -0
- data/lib/kamal/commands/base.rb +147 -0
- data/lib/kamal/commands/builder/base.rb +143 -0
- data/lib/kamal/commands/builder/clone.rb +32 -0
- data/lib/kamal/commands/builder/cloud.rb +22 -0
- data/lib/kamal/commands/builder/hybrid.rb +21 -0
- data/lib/kamal/commands/builder/local.rb +20 -0
- data/lib/kamal/commands/builder/pack.rb +46 -0
- data/lib/kamal/commands/builder/remote.rb +75 -0
- data/lib/kamal/commands/builder.rb +54 -0
- data/lib/kamal/commands/docker.rb +50 -0
- data/lib/kamal/commands/hook.rb +20 -0
- data/lib/kamal/commands/loadbalancer.rb +130 -0
- data/lib/kamal/commands/lock.rb +70 -0
- data/lib/kamal/commands/proxy.rb +150 -0
- data/lib/kamal/commands/prune.rb +38 -0
- data/lib/kamal/commands/registry.rb +38 -0
- data/lib/kamal/commands/server.rb +15 -0
- data/lib/kamal/commands.rb +2 -0
- data/lib/kamal/configuration/accessory.rb +280 -0
- data/lib/kamal/configuration/alias.rb +15 -0
- data/lib/kamal/configuration/boot.rb +29 -0
- data/lib/kamal/configuration/builder.rb +218 -0
- data/lib/kamal/configuration/docs/accessory.yml +160 -0
- data/lib/kamal/configuration/docs/alias.yml +29 -0
- data/lib/kamal/configuration/docs/boot.yml +21 -0
- data/lib/kamal/configuration/docs/builder.yml +132 -0
- data/lib/kamal/configuration/docs/configuration.yml +228 -0
- data/lib/kamal/configuration/docs/env.yml +118 -0
- data/lib/kamal/configuration/docs/logging.yml +21 -0
- data/lib/kamal/configuration/docs/output.yml +25 -0
- data/lib/kamal/configuration/docs/proxy.yml +207 -0
- data/lib/kamal/configuration/docs/registry.yml +64 -0
- data/lib/kamal/configuration/docs/role.yml +54 -0
- data/lib/kamal/configuration/docs/servers.yml +27 -0
- data/lib/kamal/configuration/docs/ssh.yml +81 -0
- data/lib/kamal/configuration/docs/sshkit.yml +31 -0
- data/lib/kamal/configuration/env/tag.rb +13 -0
- data/lib/kamal/configuration/env.rb +42 -0
- data/lib/kamal/configuration/loadbalancer.rb +34 -0
- data/lib/kamal/configuration/logging.rb +33 -0
- data/lib/kamal/configuration/output.rb +34 -0
- data/lib/kamal/configuration/proxy/boot.rb +124 -0
- data/lib/kamal/configuration/proxy/run.rb +152 -0
- data/lib/kamal/configuration/proxy.rb +156 -0
- data/lib/kamal/configuration/registry.rb +40 -0
- data/lib/kamal/configuration/role.rb +247 -0
- data/lib/kamal/configuration/servers.rb +25 -0
- data/lib/kamal/configuration/ssh.rb +76 -0
- data/lib/kamal/configuration/sshkit.rb +26 -0
- data/lib/kamal/configuration/validation.rb +27 -0
- data/lib/kamal/configuration/validator/accessory.rb +13 -0
- data/lib/kamal/configuration/validator/alias.rb +15 -0
- data/lib/kamal/configuration/validator/builder.rb +15 -0
- data/lib/kamal/configuration/validator/configuration.rb +6 -0
- data/lib/kamal/configuration/validator/env.rb +54 -0
- data/lib/kamal/configuration/validator/proxy.rb +47 -0
- data/lib/kamal/configuration/validator/registry.rb +27 -0
- data/lib/kamal/configuration/validator/role.rb +13 -0
- data/lib/kamal/configuration/validator/servers.rb +7 -0
- data/lib/kamal/configuration/validator.rb +251 -0
- data/lib/kamal/configuration/volume.rb +29 -0
- data/lib/kamal/configuration.rb +465 -0
- data/lib/kamal/docker.rb +30 -0
- data/lib/kamal/env_file.rb +44 -0
- data/lib/kamal/git.rb +37 -0
- 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 +59 -0
- data/lib/kamal/secrets/adapters/base.rb +33 -0
- data/lib/kamal/secrets/adapters/bitwarden.rb +81 -0
- data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
- data/lib/kamal/secrets/adapters/doppler.rb +57 -0
- data/lib/kamal/secrets/adapters/enpass.rb +71 -0
- data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
- data/lib/kamal/secrets/adapters/last_pass.rb +40 -0
- data/lib/kamal/secrets/adapters/one_password.rb +104 -0
- data/lib/kamal/secrets/adapters/passbolt.rb +129 -0
- data/lib/kamal/secrets/adapters/test.rb +16 -0
- data/lib/kamal/secrets/adapters.rb +16 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +47 -0
- data/lib/kamal/secrets.rb +53 -0
- data/lib/kamal/sshkit_with_ext.rb +273 -0
- data/lib/kamal/tags.rb +40 -0
- data/lib/kamal/utils/sensitive.rb +20 -0
- data/lib/kamal/utils.rb +110 -0
- data/lib/kamal/version.rb +3 -0
- data/lib/kamal.rb +15 -0
- metadata +388 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
class Kamal::Configuration::Validator::Env < Kamal::Configuration::Validator
|
|
2
|
+
SPECIAL_KEYS = [ "clear", "secret", "tags" ]
|
|
3
|
+
|
|
4
|
+
def validate!
|
|
5
|
+
if known_keys.any?
|
|
6
|
+
validate_complex_env!
|
|
7
|
+
else
|
|
8
|
+
validate_simple_env!
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
def validate_simple_env!
|
|
14
|
+
validate_hash_of!(config, String)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def validate_complex_env!
|
|
18
|
+
unknown_keys_error unknown_keys if unknown_keys.any?
|
|
19
|
+
|
|
20
|
+
with_context("clear") { validate_hash_of!(config["clear"], String) } if config.key?("clear")
|
|
21
|
+
with_context("secret") { validate_array_of!(config["secret"], String) } if config.key?("secret")
|
|
22
|
+
validate_tags! if config.key?("tags")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def known_keys
|
|
26
|
+
@known_keys ||= config.keys & SPECIAL_KEYS
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def unknown_keys
|
|
30
|
+
@unknown_keys ||= config.keys - SPECIAL_KEYS
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def validate_tags!
|
|
34
|
+
if context == "env"
|
|
35
|
+
with_context("tags") do
|
|
36
|
+
validate_type! config["tags"], Hash
|
|
37
|
+
|
|
38
|
+
config["tags"].each do |tag, value|
|
|
39
|
+
with_context(tag) do
|
|
40
|
+
validate_type! value, Hash
|
|
41
|
+
|
|
42
|
+
Kamal::Configuration::Validator::Env.new(
|
|
43
|
+
value,
|
|
44
|
+
example: example["tags"].values[1],
|
|
45
|
+
context: context
|
|
46
|
+
).validate!
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
error "tags are only allowed in the root env"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
class Kamal::Configuration::Validator::Proxy < Kamal::Configuration::Validator
|
|
2
|
+
def validate!
|
|
3
|
+
unless config.nil?
|
|
4
|
+
super
|
|
5
|
+
|
|
6
|
+
# Skip SSL host validation when a loadbalancer is present
|
|
7
|
+
# since SSL is disabled when using a loadbalancer
|
|
8
|
+
if config["host"].blank? && config["hosts"].blank? && config["ssl"] && config["loadbalancer"].blank?
|
|
9
|
+
error "Must set a host to enable automatic SSL"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
if (config.keys & [ "host", "hosts" ]).size > 1
|
|
13
|
+
error "Specify one of 'host' or 'hosts', not both"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
if config["ssl"].is_a?(Hash)
|
|
17
|
+
if config["ssl"]["certificate_pem"].present? && config["ssl"]["private_key_pem"].blank?
|
|
18
|
+
error "Missing private_key_pem setting (required when certificate_pem is present)"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if config["ssl"]["private_key_pem"].present? && config["ssl"]["certificate_pem"].blank?
|
|
22
|
+
error "Missing certificate_pem setting (required when private_key_pem is present)"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if run_config = config["run"]
|
|
27
|
+
if run_config["bind_ips"].present?
|
|
28
|
+
ensure_valid_bind_ips(config["bind_ips"])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if run_config["publish"] == false
|
|
32
|
+
if run_config["bind_ips"].present? || run_config["http_port"].present? || run_config["https_port"].present?
|
|
33
|
+
error "Cannot set http_port, https_port or bind_ips when publish is false"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
def ensure_valid_bind_ips(bind_ips)
|
|
42
|
+
bind_ips.present? && bind_ips.each do |ip|
|
|
43
|
+
next if ip =~ Resolv::IPv4::Regex || ip =~ Resolv::IPv6::Regex
|
|
44
|
+
error "Invalid publish IP address: #{ip}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class Kamal::Configuration::Validator::Registry < Kamal::Configuration::Validator
|
|
2
|
+
STRING_OR_ONE_ITEM_ARRAY_KEYS = [ "username", "password" ]
|
|
3
|
+
|
|
4
|
+
def validate!
|
|
5
|
+
validate_against_example! \
|
|
6
|
+
config.except(*STRING_OR_ONE_ITEM_ARRAY_KEYS),
|
|
7
|
+
example.except(*STRING_OR_ONE_ITEM_ARRAY_KEYS)
|
|
8
|
+
|
|
9
|
+
validate_string_or_one_item_array! "username"
|
|
10
|
+
validate_string_or_one_item_array! "password"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
def validate_string_or_one_item_array!(key)
|
|
15
|
+
with_context(key) do
|
|
16
|
+
value = config[key]
|
|
17
|
+
|
|
18
|
+
unless config["server"]&.match?("^localhost[:$]")
|
|
19
|
+
error "is required" unless value.present?
|
|
20
|
+
|
|
21
|
+
unless value.is_a?(String) || (value.is_a?(Array) && value.size == 1 && value.first.is_a?(String))
|
|
22
|
+
error "should be a string or an array with one string (for secret lookup)"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class Kamal::Configuration::Validator::Role < Kamal::Configuration::Validator
|
|
2
|
+
def validate!
|
|
3
|
+
validate_type! config, Array, Hash
|
|
4
|
+
|
|
5
|
+
if config.is_a?(Array)
|
|
6
|
+
validate_servers!(config)
|
|
7
|
+
else
|
|
8
|
+
super
|
|
9
|
+
validate_labels!(config["labels"])
|
|
10
|
+
validate_docker_options!(config["options"])
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
class Kamal::Configuration::Validator
|
|
2
|
+
attr_reader :config, :example, :context
|
|
3
|
+
|
|
4
|
+
def initialize(config, example:, context:)
|
|
5
|
+
@config = config
|
|
6
|
+
@example = example
|
|
7
|
+
@context = context
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def validate!
|
|
11
|
+
validate_against_example! config, example
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
def validate_against_example!(validation_config, example)
|
|
16
|
+
validate_type! validation_config, example.class
|
|
17
|
+
|
|
18
|
+
if example.class == Hash
|
|
19
|
+
check_unknown_keys! validation_config, example
|
|
20
|
+
|
|
21
|
+
validation_config.each do |key, value|
|
|
22
|
+
next if extension?(key)
|
|
23
|
+
with_context(key) do
|
|
24
|
+
example_value = example[key]
|
|
25
|
+
|
|
26
|
+
if example_value == "..."
|
|
27
|
+
unless key.to_s == "proxy" && boolean?(value.class)
|
|
28
|
+
validate_type! value, *(Array if key == :servers), Hash
|
|
29
|
+
end
|
|
30
|
+
elsif key.to_s == "ssl"
|
|
31
|
+
validate_type! value, TrueClass, FalseClass, Hash
|
|
32
|
+
elsif key.to_s == "hooks_output"
|
|
33
|
+
validate_hooks_output!(value)
|
|
34
|
+
elsif key == "hosts"
|
|
35
|
+
validate_servers! value
|
|
36
|
+
elsif example_value.is_a?(Array)
|
|
37
|
+
if key == "arch"
|
|
38
|
+
validate_array_of_or_type! value, example_value.first.class
|
|
39
|
+
elsif key.to_s == "config"
|
|
40
|
+
validate_ssh_config!(value)
|
|
41
|
+
elsif key.to_s == "files" || key.to_s == "directories"
|
|
42
|
+
validate_paths!(value)
|
|
43
|
+
else
|
|
44
|
+
validate_array_of! value, example_value.first.class
|
|
45
|
+
end
|
|
46
|
+
elsif example_value.is_a?(Hash)
|
|
47
|
+
case key.to_s
|
|
48
|
+
when "options", "args"
|
|
49
|
+
validate_type! value, Hash
|
|
50
|
+
when "labels"
|
|
51
|
+
validate_hash_of! value, example_value.first[1].class
|
|
52
|
+
else
|
|
53
|
+
validate_against_example! value, example_value
|
|
54
|
+
end
|
|
55
|
+
else
|
|
56
|
+
validate_type! value, example_value.class
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def valid_type?(value, type)
|
|
65
|
+
value.is_a?(type) ||
|
|
66
|
+
(type == String && stringish?(value)) ||
|
|
67
|
+
(boolean?(type) && boolean?(value.class))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def type_description(type)
|
|
71
|
+
if type == Integer || type == Array
|
|
72
|
+
"an #{type.name.downcase}"
|
|
73
|
+
elsif type == TrueClass || type == FalseClass
|
|
74
|
+
"a boolean"
|
|
75
|
+
else
|
|
76
|
+
"a #{type.name.downcase}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def boolean?(type)
|
|
81
|
+
type == TrueClass || type == FalseClass
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def stringish?(value)
|
|
85
|
+
value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def validate_array_of_or_type!(value, type)
|
|
89
|
+
if value.is_a?(Array)
|
|
90
|
+
validate_array_of! value, type
|
|
91
|
+
else
|
|
92
|
+
validate_type! value, type
|
|
93
|
+
end
|
|
94
|
+
rescue Kamal::ConfigurationError
|
|
95
|
+
type_error(Array, type)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def validate_array_of!(array, type)
|
|
99
|
+
validate_type! array, Array
|
|
100
|
+
|
|
101
|
+
array.each_with_index do |value, index|
|
|
102
|
+
with_context(index) do
|
|
103
|
+
validate_type! value, type
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def validate_hash_of!(hash, type)
|
|
109
|
+
validate_type! hash, Hash
|
|
110
|
+
|
|
111
|
+
hash.each do |key, value|
|
|
112
|
+
with_context(key) do
|
|
113
|
+
validate_type! value, type
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def validate_servers!(servers)
|
|
119
|
+
validate_type! servers, Array
|
|
120
|
+
|
|
121
|
+
servers.each_with_index do |server, index|
|
|
122
|
+
with_context(index) do
|
|
123
|
+
validate_type! server, String, Hash
|
|
124
|
+
|
|
125
|
+
if server.is_a?(Hash)
|
|
126
|
+
error "multiple hosts found" unless server.size == 1
|
|
127
|
+
host, tags = server.first
|
|
128
|
+
|
|
129
|
+
with_context(host) do
|
|
130
|
+
validate_type! tags, String, Array
|
|
131
|
+
validate_array_of! tags, String if tags.is_a?(Array)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def validate_ssh_config!(config)
|
|
139
|
+
if config.is_a?(Array)
|
|
140
|
+
validate_array_of! config, String
|
|
141
|
+
elsif boolean?(config.class) || config.is_a?(String)
|
|
142
|
+
# Booleans and Strings are allowed
|
|
143
|
+
else
|
|
144
|
+
type_error(TrueClass, FalseClass, String, Array)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def validate_paths!(paths)
|
|
149
|
+
validate_type! paths, Array
|
|
150
|
+
|
|
151
|
+
paths.each_with_index do |path, index|
|
|
152
|
+
with_context(index) do
|
|
153
|
+
validate_type! path, String, Hash
|
|
154
|
+
|
|
155
|
+
if path.is_a?(Hash)
|
|
156
|
+
%w[local remote mode owner options].each do |key|
|
|
157
|
+
with_context(key) do
|
|
158
|
+
validate_type! path[key], String if path.key?(key)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
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
|
+
|
|
179
|
+
def validate_type!(value, *types)
|
|
180
|
+
type_error(*types) unless types.any? { |type| valid_type?(value, type) }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def error(message)
|
|
184
|
+
raise Kamal::ConfigurationError, "#{error_context}#{message}"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def type_error(*expected_types)
|
|
188
|
+
descriptions = expected_types.map { |type| type_description(type) }.uniq
|
|
189
|
+
error "should be #{descriptions.join(" or ")}"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def unknown_keys_error(unknown_keys)
|
|
193
|
+
error "unknown #{"key".pluralize(unknown_keys.count)}: #{unknown_keys.join(", ")}"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def error_context
|
|
197
|
+
"#{context}: " if context.present?
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def with_context(context)
|
|
201
|
+
old_context = @context
|
|
202
|
+
@context = [ @context, context ].select(&:present?).join("/")
|
|
203
|
+
yield
|
|
204
|
+
ensure
|
|
205
|
+
@context = old_context
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def allow_extensions?
|
|
209
|
+
false
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def extension?(key)
|
|
213
|
+
key.to_s.start_with?("x-")
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def check_unknown_keys!(config, example)
|
|
217
|
+
unknown_keys = config.keys - example.keys
|
|
218
|
+
unknown_keys.reject! { |key| extension?(key) } if allow_extensions?
|
|
219
|
+
unknown_keys_error unknown_keys if unknown_keys.present?
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def validate_labels!(labels)
|
|
223
|
+
return true if labels.blank?
|
|
224
|
+
|
|
225
|
+
with_context("labels") do
|
|
226
|
+
labels.each do |key, _|
|
|
227
|
+
with_context(key) do
|
|
228
|
+
error "invalid label. destination, role, and service are reserved labels" if %w[destination role service].include?(key)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def validate_docker_options!(options)
|
|
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
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class Kamal::Configuration::Volume
|
|
2
|
+
attr_reader :host_path, :container_path, :options
|
|
3
|
+
delegate :argumentize, to: Kamal::Utils
|
|
4
|
+
|
|
5
|
+
def initialize(host_path:, container_path:, options: nil)
|
|
6
|
+
@host_path = host_path
|
|
7
|
+
@container_path = container_path
|
|
8
|
+
@options = options
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def docker_args
|
|
12
|
+
argumentize "--volume", docker_args_string
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def docker_args_string
|
|
16
|
+
volume_string = "#{host_path_for_docker_volume}:#{container_path}"
|
|
17
|
+
volume_string += ":#{options}" if options.present?
|
|
18
|
+
volume_string
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
def host_path_for_docker_volume
|
|
23
|
+
if Pathname.new(host_path).absolute?
|
|
24
|
+
host_path
|
|
25
|
+
else
|
|
26
|
+
"$PWD/#{host_path}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|