kamal 1.3.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kamal/cli/accessory.rb +38 -29
- data/lib/kamal/cli/app/boot.rb +67 -0
- data/lib/kamal/cli/app/prepare_assets.rb +24 -0
- data/lib/kamal/cli/app.rb +25 -67
- data/lib/kamal/cli/base.rb +23 -8
- data/lib/kamal/cli/env.rb +3 -5
- data/lib/kamal/cli/main.rb +7 -4
- data/lib/kamal/cli/prune.rb +6 -2
- data/lib/kamal/cli/server.rb +3 -1
- data/lib/kamal/cli/templates/deploy.yml +5 -1
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +7 -0
- data/lib/kamal/cli/traefik.rb +16 -13
- data/lib/kamal/commander/specifics.rb +49 -0
- data/lib/kamal/commander.rb +9 -33
- data/lib/kamal/commands/accessory.rb +2 -2
- data/lib/kamal/commands/app/assets.rb +12 -12
- data/lib/kamal/commands/app/cord.rb +4 -4
- data/lib/kamal/commands/app/execution.rb +10 -8
- data/lib/kamal/commands/app/images.rb +1 -1
- data/lib/kamal/commands/app/logging.rb +2 -2
- data/lib/kamal/commands/app.rb +38 -18
- data/lib/kamal/commands/auditor.rb +1 -1
- data/lib/kamal/commands/base.rb +12 -0
- data/lib/kamal/commands/builder/base.rb +22 -5
- data/lib/kamal/commands/builder/multiarch.rb +17 -9
- data/lib/kamal/commands/builder/native/cached.rb +7 -6
- data/lib/kamal/commands/builder/native/remote.rb +9 -9
- data/lib/kamal/commands/builder/native.rb +8 -7
- data/lib/kamal/commands/docker.rb +10 -1
- data/lib/kamal/commands/healthcheck.rb +0 -1
- data/lib/kamal/commands/hook.rb +1 -1
- data/lib/kamal/commands/lock.rb +19 -9
- data/lib/kamal/commands/prune.rb +4 -4
- data/lib/kamal/commands/registry.rb +4 -1
- data/lib/kamal/commands/server.rb +1 -1
- data/lib/kamal/commands/traefik.rb +10 -16
- data/lib/kamal/configuration/accessory.rb +10 -20
- data/lib/kamal/configuration/boot.rb +1 -1
- data/lib/kamal/configuration/builder.rb +11 -3
- data/lib/kamal/configuration/env.rb +40 -0
- data/lib/kamal/configuration/role.rb +23 -40
- data/lib/kamal/configuration.rb +53 -21
- data/lib/kamal/env_file.rb +12 -15
- data/lib/kamal/sshkit_with_ext.rb +1 -0
- data/lib/kamal/utils.rb +7 -3
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +1 -1
- metadata +8 -3
@@ -1,7 +1,7 @@
|
|
1
1
|
class Kamal::Commands::Docker < Kamal::Commands::Base
|
2
2
|
# Install Docker using the https://github.com/docker/docker-install convenience script.
|
3
3
|
def install
|
4
|
-
pipe
|
4
|
+
pipe get_docker, :sh
|
5
5
|
end
|
6
6
|
|
7
7
|
# Checks the Docker client version. Fails if Docker is not installed.
|
@@ -18,4 +18,13 @@ class Kamal::Commands::Docker < Kamal::Commands::Base
|
|
18
18
|
def superuser?
|
19
19
|
[ '[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null' ]
|
20
20
|
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def get_docker
|
24
|
+
shell \
|
25
|
+
any \
|
26
|
+
[ :curl, "-fsSL", "https://get.docker.com" ],
|
27
|
+
[ :wget, "-O -", "https://get.docker.com" ],
|
28
|
+
[ :echo, "\"exit 1\"" ]
|
29
|
+
end
|
21
30
|
end
|
data/lib/kamal/commands/hook.rb
CHANGED
data/lib/kamal/commands/lock.rb
CHANGED
@@ -5,14 +5,14 @@ require "base64"
|
|
5
5
|
class Kamal::Commands::Lock < Kamal::Commands::Base
|
6
6
|
def acquire(message, version)
|
7
7
|
combine \
|
8
|
-
[:mkdir, lock_dir],
|
8
|
+
[ :mkdir, lock_dir ],
|
9
9
|
write_lock_details(message, version)
|
10
10
|
end
|
11
11
|
|
12
12
|
def release
|
13
13
|
combine \
|
14
|
-
[:rm, lock_details_file],
|
15
|
-
[:rm, "-r", lock_dir]
|
14
|
+
[ :rm, lock_details_file ],
|
15
|
+
[ :rm, "-r", lock_dir ]
|
16
16
|
end
|
17
17
|
|
18
18
|
def status
|
@@ -21,31 +21,41 @@ class Kamal::Commands::Lock < Kamal::Commands::Base
|
|
21
21
|
read_lock_details
|
22
22
|
end
|
23
23
|
|
24
|
+
def ensure_locks_directory
|
25
|
+
[ :mkdir, "-p", locks_dir ]
|
26
|
+
end
|
27
|
+
|
24
28
|
private
|
25
29
|
def write_lock_details(message, version)
|
26
30
|
write \
|
27
|
-
[:echo, "\"#{Base64.encode64(lock_details(message, version))}\""],
|
31
|
+
[ :echo, "\"#{Base64.encode64(lock_details(message, version))}\"" ],
|
28
32
|
lock_details_file
|
29
33
|
end
|
30
34
|
|
31
35
|
def read_lock_details
|
32
36
|
pipe \
|
33
|
-
[:cat, lock_details_file],
|
34
|
-
[:base64, "-d"]
|
37
|
+
[ :cat, lock_details_file ],
|
38
|
+
[ :base64, "-d" ]
|
35
39
|
end
|
36
40
|
|
37
41
|
def stat_lock_dir
|
38
42
|
write \
|
39
|
-
[:stat, lock_dir],
|
43
|
+
[ :stat, lock_dir ],
|
40
44
|
"/dev/null"
|
41
45
|
end
|
42
46
|
|
47
|
+
def locks_dir
|
48
|
+
File.join(config.run_directory, "locks")
|
49
|
+
end
|
50
|
+
|
43
51
|
def lock_dir
|
44
|
-
|
52
|
+
dir_name = [ config.service, config.destination ].compact.join("-")
|
53
|
+
|
54
|
+
File.join(locks_dir, dir_name)
|
45
55
|
end
|
46
56
|
|
47
57
|
def lock_details_file
|
48
|
-
|
58
|
+
File.join(lock_dir, "details")
|
49
59
|
end
|
50
60
|
|
51
61
|
def lock_details(message, version)
|
data/lib/kamal/commands/prune.rb
CHANGED
@@ -13,10 +13,10 @@ 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 app_containers(
|
16
|
+
def app_containers(retain:)
|
17
17
|
pipe \
|
18
18
|
docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters),
|
19
|
-
"tail -n +#{
|
19
|
+
"tail -n +#{retain + 1}",
|
20
20
|
"while read container_id; do docker rm $container_id; done"
|
21
21
|
end
|
22
22
|
|
@@ -26,7 +26,7 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
|
|
26
26
|
|
27
27
|
private
|
28
28
|
def stopped_containers_filters
|
29
|
-
[ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] }
|
29
|
+
[ "created", "exited", "dead" ].flat_map { |status| [ "--filter", "status=#{status}" ] }
|
30
30
|
end
|
31
31
|
|
32
32
|
def active_image_list
|
@@ -43,4 +43,4 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
|
|
43
43
|
def healthcheck_service_filter
|
44
44
|
[ "--filter", "label=service=#{config.healthcheck_service}" ]
|
45
45
|
end
|
46
|
-
|
46
|
+
end
|
@@ -2,7 +2,10 @@ class Kamal::Commands::Registry < Kamal::Commands::Base
|
|
2
2
|
delegate :registry, to: :config
|
3
3
|
|
4
4
|
def login
|
5
|
-
docker :login,
|
5
|
+
docker :login,
|
6
|
+
registry["server"],
|
7
|
+
"-u", sensitive(Kamal::Utils.escape_shell_value(lookup("username"))),
|
8
|
+
"-p", sensitive(Kamal::Utils.escape_shell_value(lookup("password")))
|
6
9
|
end
|
7
10
|
|
8
11
|
def logout
|
@@ -1,10 +1,10 @@
|
|
1
1
|
class Kamal::Commands::Traefik < Kamal::Commands::Base
|
2
2
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
3
3
|
|
4
|
-
DEFAULT_IMAGE = "traefik:v2.
|
4
|
+
DEFAULT_IMAGE = "traefik:v2.10"
|
5
5
|
CONTAINER_PORT = 80
|
6
6
|
DEFAULT_ARGS = {
|
7
|
-
|
7
|
+
"log.level" => "DEBUG"
|
8
8
|
}
|
9
9
|
DEFAULT_LABELS = {
|
10
10
|
# These ensure we serve a 502 rather than a 404 if no containers are available
|
@@ -39,7 +39,7 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def start_or_run
|
42
|
-
|
42
|
+
any start, run
|
43
43
|
end
|
44
44
|
|
45
45
|
def info
|
@@ -71,20 +71,18 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
|
|
71
71
|
"#{host_port}:#{CONTAINER_PORT}"
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
75
|
-
Kamal::
|
76
|
-
|
77
|
-
|
78
|
-
def host_env_file_path
|
79
|
-
File.join host_env_directory, "traefik.env"
|
74
|
+
def env
|
75
|
+
Kamal::Configuration::Env.from_config \
|
76
|
+
config: config.traefik.fetch("env", {}),
|
77
|
+
secrets_file: File.join(config.host_env_directory, "traefik", "traefik.env")
|
80
78
|
end
|
81
79
|
|
82
80
|
def make_env_directory
|
83
|
-
make_directory(
|
81
|
+
make_directory(env.secrets_directory)
|
84
82
|
end
|
85
83
|
|
86
84
|
def remove_env_file
|
87
|
-
[:rm, "-f",
|
85
|
+
[ :rm, "-f", env.secrets_file ]
|
88
86
|
end
|
89
87
|
|
90
88
|
private
|
@@ -97,11 +95,7 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
|
|
97
95
|
end
|
98
96
|
|
99
97
|
def env_args
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
def host_env_directory
|
104
|
-
File.join config.host_env_directory, "traefik"
|
98
|
+
env.args
|
105
99
|
end
|
106
100
|
|
107
101
|
def labels
|
@@ -8,7 +8,7 @@ class Kamal::Configuration::Accessory
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def service_name
|
11
|
-
"#{config.service}-#{name}"
|
11
|
+
specifics["service"] || "#{config.service}-#{name}"
|
12
12
|
end
|
13
13
|
|
14
14
|
def image
|
@@ -16,7 +16,7 @@ class Kamal::Configuration::Accessory
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def hosts
|
19
|
-
if (specifics.keys & ["host", "hosts", "roles"]).size != 1
|
19
|
+
if (specifics.keys & [ "host", "hosts", "roles" ]).size != 1
|
20
20
|
raise ArgumentError, "Specify one of `host`, `hosts` or `roles` for accessory `#{name}`"
|
21
21
|
end
|
22
22
|
|
@@ -42,23 +42,13 @@ class Kamal::Configuration::Accessory
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def env
|
45
|
-
|
46
|
-
|
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"
|
45
|
+
Kamal::Configuration::Env.from_config \
|
46
|
+
config: specifics.fetch("env", {}),
|
47
|
+
secrets_file: File.join(config.host_env_directory, "accessories", "#{service_name}.env")
|
58
48
|
end
|
59
49
|
|
60
50
|
def env_args
|
61
|
-
|
51
|
+
env.args
|
62
52
|
end
|
63
53
|
|
64
54
|
def files
|
@@ -111,10 +101,10 @@ class Kamal::Configuration::Accessory
|
|
111
101
|
end
|
112
102
|
|
113
103
|
def with_clear_env_loaded
|
114
|
-
|
104
|
+
env.clear.each { |k, v| ENV[k] = v }
|
115
105
|
yield
|
116
106
|
ensure
|
117
|
-
|
107
|
+
env.clear.each { |k, v| ENV.delete(k) }
|
118
108
|
end
|
119
109
|
|
120
110
|
def read_dynamic_file(local_file)
|
@@ -144,7 +134,7 @@ class Kamal::Configuration::Accessory
|
|
144
134
|
end
|
145
135
|
|
146
136
|
def expand_host_path(host_path)
|
147
|
-
absolute_path?(host_path) ? host_path :
|
137
|
+
absolute_path?(host_path) ? host_path : File.join(service_data_directory, host_path)
|
148
138
|
end
|
149
139
|
|
150
140
|
def absolute_path?(path)
|
@@ -159,7 +149,7 @@ class Kamal::Configuration::Accessory
|
|
159
149
|
if specifics.key?("host")
|
160
150
|
host = specifics["host"]
|
161
151
|
if host
|
162
|
-
[host]
|
152
|
+
[ host ]
|
163
153
|
else
|
164
154
|
raise ArgumentError, "Missing host for accessory `#{name}`"
|
165
155
|
end
|
@@ -40,7 +40,7 @@ class Kamal::Configuration::Builder
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def context
|
43
|
-
@options["context"] || "."
|
43
|
+
@options["context"] || (git_archive? ? "-" : ".")
|
44
44
|
end
|
45
45
|
|
46
46
|
def local_arch
|
@@ -81,10 +81,18 @@ class Kamal::Configuration::Builder
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
+
def ssh
|
85
|
+
@options["ssh"]
|
86
|
+
end
|
87
|
+
|
88
|
+
def git_archive?
|
89
|
+
Kamal::Git.used? && @options["context"].nil?
|
90
|
+
end
|
91
|
+
|
84
92
|
private
|
85
93
|
def valid?
|
86
94
|
if @options["cache"] && @options["cache"]["type"]
|
87
|
-
raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless ["gha", "registry"].include?(@options["cache"]["type"])
|
95
|
+
raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless [ "gha", "registry" ].include?(@options["cache"]["type"])
|
88
96
|
end
|
89
97
|
end
|
90
98
|
|
@@ -105,7 +113,7 @@ class Kamal::Configuration::Builder
|
|
105
113
|
end
|
106
114
|
|
107
115
|
def cache_to_config_for_gha
|
108
|
-
[ "type=gha", @options["cache"]&.fetch("options", nil)].compact.join(",")
|
116
|
+
[ "type=gha", @options["cache"]&.fetch("options", nil) ].compact.join(",")
|
109
117
|
end
|
110
118
|
|
111
119
|
def cache_to_config_for_registry
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Kamal::Configuration::Env
|
2
|
+
attr_reader :secrets_keys, :clear, :secrets_file
|
3
|
+
delegate :argumentize, to: Kamal::Utils
|
4
|
+
|
5
|
+
def self.from_config(config:, secrets_file: nil)
|
6
|
+
secrets_keys = config.fetch("secret", [])
|
7
|
+
clear = config.fetch("clear", config.key?("secret") ? {} : config)
|
8
|
+
|
9
|
+
new clear: clear, secrets_keys: secrets_keys, secrets_file: secrets_file
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(clear:, secrets_keys:, secrets_file:)
|
13
|
+
@clear = clear
|
14
|
+
@secrets_keys = secrets_keys
|
15
|
+
@secrets_file = secrets_file
|
16
|
+
end
|
17
|
+
|
18
|
+
def args
|
19
|
+
[ "--env-file", secrets_file, *argumentize("--env", clear) ]
|
20
|
+
end
|
21
|
+
|
22
|
+
def secrets_io
|
23
|
+
StringIO.new(Kamal::EnvFile.new(secrets).to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
def secrets
|
27
|
+
@secrets ||= secrets_keys.to_h { |key| [ key, ENV.fetch(key) ] }
|
28
|
+
end
|
29
|
+
|
30
|
+
def secrets_directory
|
31
|
+
File.dirname(secrets_file)
|
32
|
+
end
|
33
|
+
|
34
|
+
def merge(other)
|
35
|
+
self.class.new \
|
36
|
+
clear: @clear.merge(other.clear),
|
37
|
+
secrets_keys: @secrets_keys | other.secrets_keys,
|
38
|
+
secrets_file: secrets_file
|
39
|
+
end
|
40
|
+
end
|
@@ -3,9 +3,10 @@ class Kamal::Configuration::Role
|
|
3
3
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
4
4
|
|
5
5
|
attr_accessor :name
|
6
|
+
alias to_s name
|
6
7
|
|
7
8
|
def initialize(name, config:)
|
8
|
-
|
9
|
+
@name, @config = name.inquiry, config
|
9
10
|
end
|
10
11
|
|
11
12
|
def primary_host
|
@@ -36,29 +37,25 @@ class Kamal::Configuration::Role
|
|
36
37
|
argumentize "--label", labels
|
37
38
|
end
|
38
39
|
|
40
|
+
def logging_args
|
41
|
+
args = config.logging || {}
|
42
|
+
args.deep_merge!(specializations["logging"]) if specializations["logging"].present?
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
if args.any?
|
45
|
+
optionize({ "log-driver" => args["driver"] }.compact) +
|
46
|
+
argumentize("--log-opt", args["options"])
|
43
47
|
else
|
44
|
-
|
48
|
+
config.logging_args
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
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
52
|
|
56
|
-
def
|
57
|
-
|
53
|
+
def env
|
54
|
+
@env ||= base_env.merge(specialized_env)
|
58
55
|
end
|
59
56
|
|
60
57
|
def env_args
|
61
|
-
|
58
|
+
env.args
|
62
59
|
end
|
63
60
|
|
64
61
|
def asset_volume_args
|
@@ -101,7 +98,7 @@ class Kamal::Configuration::Role
|
|
101
98
|
end
|
102
99
|
|
103
100
|
def primary?
|
104
|
-
@config.primary_role
|
101
|
+
self == @config.primary_role
|
105
102
|
end
|
106
103
|
|
107
104
|
|
@@ -110,13 +107,13 @@ class Kamal::Configuration::Role
|
|
110
107
|
end
|
111
108
|
|
112
109
|
def cord_host_directory
|
113
|
-
File.join config.run_directory_as_docker_volume, "cords", [container_prefix, config.run_id].join("-")
|
110
|
+
File.join config.run_directory_as_docker_volume, "cords", [ container_prefix, config.run_id ].join("-")
|
114
111
|
end
|
115
112
|
|
116
113
|
def cord_volume
|
117
114
|
if (cord = health_check_options["cord"])
|
118
115
|
@cord_volume ||= Kamal::Configuration::Volume.new \
|
119
|
-
host_path: File.join(config.run_directory, "cords", [container_prefix, config.run_id].join("-")),
|
116
|
+
host_path: File.join(config.run_directory, "cords", [ container_prefix, config.run_id ].join("-")),
|
120
117
|
container_path: cord
|
121
118
|
end
|
122
119
|
end
|
@@ -179,11 +176,7 @@ class Kamal::Configuration::Role
|
|
179
176
|
end
|
180
177
|
|
181
178
|
def default_labels
|
182
|
-
|
183
|
-
{ "service" => config.service, "role" => name, "destination" => config.destination }
|
184
|
-
else
|
185
|
-
{ "service" => config.service, "role" => name }
|
186
|
-
end
|
179
|
+
{ "service" => config.service, "role" => name, "destination" => config.destination }
|
187
180
|
end
|
188
181
|
|
189
182
|
def traefik_labels
|
@@ -204,7 +197,7 @@ class Kamal::Configuration::Role
|
|
204
197
|
end
|
205
198
|
|
206
199
|
def traefik_service
|
207
|
-
|
200
|
+
container_prefix
|
208
201
|
end
|
209
202
|
|
210
203
|
def custom_labels
|
@@ -216,31 +209,21 @@ class Kamal::Configuration::Role
|
|
216
209
|
|
217
210
|
def specializations
|
218
211
|
if config.servers.is_a?(Array) || config.servers[name].is_a?(Array)
|
219
|
-
{
|
212
|
+
{}
|
220
213
|
else
|
221
214
|
config.servers[name].except("hosts")
|
222
215
|
end
|
223
216
|
end
|
224
217
|
|
225
218
|
def specialized_env
|
226
|
-
specializations
|
227
|
-
end
|
228
|
-
|
229
|
-
def merged_env
|
230
|
-
config.env&.merge(specialized_env) || {}
|
219
|
+
Kamal::Configuration::Env.from_config config: specializations.fetch("env", {})
|
231
220
|
end
|
232
221
|
|
233
222
|
# Secrets are stored in an array, which won't merge by default, so have to do it by hand.
|
234
|
-
def
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
# If there's no secret/clear split, everything is clear
|
239
|
-
clear_app_env = config.env["secret"] ? Array(config.env["clear"]) : Array(config.env["clear"] || config.env)
|
240
|
-
clear_role_env = specialized_env["secret"] ? Array(specialized_env["clear"]) : Array(specialized_env["clear"] || specialized_env)
|
241
|
-
|
242
|
-
new_env["clear"] = clear_app_env.to_h.merge(clear_role_env.to_h)
|
243
|
-
end
|
223
|
+
def base_env
|
224
|
+
Kamal::Configuration::Env.from_config \
|
225
|
+
config: config.env,
|
226
|
+
secrets_file: File.join(config.host_env_directory, "roles", "#{container_prefix}.env")
|
244
227
|
end
|
245
228
|
|
246
229
|
def http_health_check(port:, path:)
|
data/lib/kamal/configuration.rb
CHANGED
@@ -6,7 +6,7 @@ require "erb"
|
|
6
6
|
require "net/ssh/proxy/jump"
|
7
7
|
|
8
8
|
class Kamal::Configuration
|
9
|
-
delegate :service, :image, :servers, :
|
9
|
+
delegate :service, :image, :servers, :labels, :registry, :stop_wait_time, :hooks_path, :logging, to: :raw_config, allow_nil: true
|
10
10
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
11
11
|
|
12
12
|
attr_reader :destination, :raw_config
|
@@ -88,11 +88,23 @@ class Kamal::Configuration
|
|
88
88
|
|
89
89
|
|
90
90
|
def all_hosts
|
91
|
-
roles.flat_map(&:hosts).uniq
|
91
|
+
(roles + accessories).flat_map(&:hosts).uniq
|
92
92
|
end
|
93
93
|
|
94
94
|
def primary_host
|
95
|
-
|
95
|
+
primary_role&.primary_host
|
96
|
+
end
|
97
|
+
|
98
|
+
def primary_role_name
|
99
|
+
raw_config.primary_role || "web"
|
100
|
+
end
|
101
|
+
|
102
|
+
def primary_role
|
103
|
+
role(primary_role_name)
|
104
|
+
end
|
105
|
+
|
106
|
+
def allow_empty_roles?
|
107
|
+
raw_config.allow_empty_roles
|
96
108
|
end
|
97
109
|
|
98
110
|
def traefik_roles
|
@@ -116,7 +128,11 @@ class Kamal::Configuration
|
|
116
128
|
end
|
117
129
|
|
118
130
|
def latest_image
|
119
|
-
"#{repository}
|
131
|
+
"#{repository}:#{latest_tag}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def latest_tag
|
135
|
+
[ "latest", *destination ].join("-")
|
120
136
|
end
|
121
137
|
|
122
138
|
def service_with_version
|
@@ -127,6 +143,10 @@ class Kamal::Configuration
|
|
127
143
|
raw_config.require_destination
|
128
144
|
end
|
129
145
|
|
146
|
+
def retain_containers
|
147
|
+
raw_config.retain_containers || 5
|
148
|
+
end
|
149
|
+
|
130
150
|
|
131
151
|
def volume_args
|
132
152
|
if raw_config.volumes.present?
|
@@ -137,9 +157,9 @@ class Kamal::Configuration
|
|
137
157
|
end
|
138
158
|
|
139
159
|
def logging_args
|
140
|
-
if
|
141
|
-
optionize({ "log-driver" =>
|
142
|
-
argumentize("--log-opt",
|
160
|
+
if logging.present?
|
161
|
+
optionize({ "log-driver" => logging["driver"] }.compact) +
|
162
|
+
argumentize("--log-opt", logging["options"])
|
143
163
|
else
|
144
164
|
argumentize("--log-opt", { "max-size" => "10m" })
|
145
165
|
end
|
@@ -200,25 +220,22 @@ class Kamal::Configuration
|
|
200
220
|
raw_config.hooks_path || ".kamal/hooks"
|
201
221
|
end
|
202
222
|
|
203
|
-
def host_env_directory
|
204
|
-
"#{run_directory}/env"
|
205
|
-
end
|
206
|
-
|
207
223
|
def asset_path
|
208
224
|
raw_config.asset_path
|
209
225
|
end
|
210
226
|
|
211
|
-
|
212
|
-
|
227
|
+
|
228
|
+
def host_env_directory
|
229
|
+
File.join(run_directory, "env")
|
213
230
|
end
|
214
231
|
|
215
|
-
def
|
216
|
-
raw_config.
|
232
|
+
def env
|
233
|
+
raw_config.env || {}
|
217
234
|
end
|
218
235
|
|
219
236
|
|
220
237
|
def valid?
|
221
|
-
ensure_destination_if_required && ensure_required_keys_present && ensure_valid_kamal_version
|
238
|
+
ensure_destination_if_required && ensure_required_keys_present && ensure_valid_kamal_version && ensure_retain_containers_valid && ensure_valid_service_name
|
222
239
|
end
|
223
240
|
|
224
241
|
def to_h
|
@@ -264,12 +281,12 @@ class Kamal::Configuration
|
|
264
281
|
raise ArgumentError, "You must specify a password for the registry in config/deploy.yml (or set the ENV variable if that's used)"
|
265
282
|
end
|
266
283
|
|
267
|
-
unless role_names.include?(
|
268
|
-
raise ArgumentError, "The primary_role #{
|
284
|
+
unless role_names.include?(primary_role_name)
|
285
|
+
raise ArgumentError, "The primary_role #{primary_role_name} isn't defined"
|
269
286
|
end
|
270
287
|
|
271
|
-
if
|
272
|
-
raise ArgumentError, "No servers specified for the #{primary_role} primary_role"
|
288
|
+
if primary_role.hosts.empty?
|
289
|
+
raise ArgumentError, "No servers specified for the #{primary_role.name} primary_role"
|
273
290
|
end
|
274
291
|
|
275
292
|
unless allow_empty_roles?
|
@@ -283,6 +300,12 @@ class Kamal::Configuration
|
|
283
300
|
true
|
284
301
|
end
|
285
302
|
|
303
|
+
def ensure_valid_service_name
|
304
|
+
raise ArgumentError, "Service name can only include alphanumeric characters, hyphens, and underscores" unless raw_config[:service] =~ /^[a-z0-9_-]+$/i
|
305
|
+
|
306
|
+
true
|
307
|
+
end
|
308
|
+
|
286
309
|
def ensure_valid_kamal_version
|
287
310
|
if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Kamal::VERSION)
|
288
311
|
raise ArgumentError, "Current version is #{Kamal::VERSION}, minimum required is #{minimum_version}"
|
@@ -291,6 +314,12 @@ class Kamal::Configuration
|
|
291
314
|
true
|
292
315
|
end
|
293
316
|
|
317
|
+
def ensure_retain_containers_valid
|
318
|
+
raise ArgumentError, "Must retain at least 1 container" if retain_containers < 1
|
319
|
+
|
320
|
+
true
|
321
|
+
end
|
322
|
+
|
294
323
|
|
295
324
|
def role_names
|
296
325
|
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
@@ -299,7 +328,10 @@ class Kamal::Configuration
|
|
299
328
|
def git_version
|
300
329
|
@git_version ||=
|
301
330
|
if Kamal::Git.used?
|
302
|
-
|
331
|
+
if Kamal::Git.uncommitted_changes.present? && !builder.git_archive?
|
332
|
+
uncommitted_suffix = "_uncommitted_#{SecureRandom.hex(8)}"
|
333
|
+
end
|
334
|
+
[ Kamal::Git.revision, uncommitted_suffix ].compact.join
|
303
335
|
else
|
304
336
|
raise "Can't use commit hash as version, no git repository found in #{Dir.pwd}"
|
305
337
|
end
|