kamal-insecure 2.7.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/kamal +18 -0
- data/lib/kamal/cli/accessory.rb +313 -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 +400 -0
- data/lib/kamal/cli/base.rb +223 -0
- data/lib/kamal/cli/build/clone.rb +61 -0
- data/lib/kamal/cli/build.rb +204 -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 +45 -0
- data/lib/kamal/cli/main.rb +277 -0
- data/lib/kamal/cli/proxy.rb +290 -0
- data/lib/kamal/cli/prune.rb +34 -0
- data/lib/kamal/cli/registry.rb +19 -0
- data/lib/kamal/cli/secrets.rb +49 -0
- data/lib/kamal/cli/server.rb +50 -0
- data/lib/kamal/cli/templates/deploy.yml +101 -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 +17 -0
- data/lib/kamal/cli.rb +9 -0
- data/lib/kamal/commander/specifics.rb +62 -0
- data/lib/kamal/commander.rb +167 -0
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +113 -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 +32 -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 +124 -0
- data/lib/kamal/commands/auditor.rb +39 -0
- data/lib/kamal/commands/base.rb +134 -0
- data/lib/kamal/commands/builder/base.rb +124 -0
- data/lib/kamal/commands/builder/clone.rb +31 -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 +14 -0
- data/lib/kamal/commands/builder/pack.rb +46 -0
- data/lib/kamal/commands/builder/remote.rb +63 -0
- data/lib/kamal/commands/builder.rb +48 -0
- data/lib/kamal/commands/docker.rb +34 -0
- data/lib/kamal/commands/hook.rb +20 -0
- data/lib/kamal/commands/lock.rb +70 -0
- data/lib/kamal/commands/proxy.rb +127 -0
- data/lib/kamal/commands/prune.rb +38 -0
- data/lib/kamal/commands/registry.rb +16 -0
- data/lib/kamal/commands/server.rb +15 -0
- data/lib/kamal/commands.rb +2 -0
- data/lib/kamal/configuration/accessory.rb +241 -0
- data/lib/kamal/configuration/alias.rb +15 -0
- data/lib/kamal/configuration/boot.rb +25 -0
- data/lib/kamal/configuration/builder.rb +211 -0
- data/lib/kamal/configuration/docs/accessory.yml +128 -0
- data/lib/kamal/configuration/docs/alias.yml +26 -0
- data/lib/kamal/configuration/docs/boot.yml +19 -0
- data/lib/kamal/configuration/docs/builder.yml +132 -0
- data/lib/kamal/configuration/docs/configuration.yml +184 -0
- data/lib/kamal/configuration/docs/env.yml +116 -0
- data/lib/kamal/configuration/docs/logging.yml +21 -0
- data/lib/kamal/configuration/docs/proxy.yml +164 -0
- data/lib/kamal/configuration/docs/registry.yml +56 -0
- data/lib/kamal/configuration/docs/role.yml +53 -0
- data/lib/kamal/configuration/docs/servers.yml +27 -0
- data/lib/kamal/configuration/docs/ssh.yml +70 -0
- data/lib/kamal/configuration/docs/sshkit.yml +23 -0
- data/lib/kamal/configuration/env/tag.rb +13 -0
- data/lib/kamal/configuration/env.rb +38 -0
- data/lib/kamal/configuration/logging.rb +33 -0
- data/lib/kamal/configuration/proxy/boot.rb +129 -0
- data/lib/kamal/configuration/proxy.rb +124 -0
- data/lib/kamal/configuration/registry.rb +32 -0
- data/lib/kamal/configuration/role.rb +222 -0
- data/lib/kamal/configuration/servers.rb +25 -0
- data/lib/kamal/configuration/ssh.rb +57 -0
- data/lib/kamal/configuration/sshkit.rb +22 -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 +25 -0
- data/lib/kamal/configuration/validator/registry.rb +25 -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 +191 -0
- data/lib/kamal/configuration/volume.rb +22 -0
- data/lib/kamal/configuration.rb +372 -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/secrets/adapters/aws_secrets_manager.rb +51 -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 +130 -0
- data/lib/kamal/secrets/adapters/test.rb +14 -0
- data/lib/kamal/secrets/adapters.rb +16 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +33 -0
- data/lib/kamal/secrets.rb +42 -0
- data/lib/kamal/sshkit_with_ext.rb +142 -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 +14 -0
- metadata +365 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
class Kamal::Commands::Docker < Kamal::Commands::Base
|
2
|
+
# Install Docker using the https://github.com/docker/docker-install convenience script.
|
3
|
+
def install
|
4
|
+
pipe get_docker, :sh
|
5
|
+
end
|
6
|
+
|
7
|
+
# Checks the Docker client version. Fails if Docker is not installed.
|
8
|
+
def installed?
|
9
|
+
docker "-v"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Checks the Docker server version. Fails if Docker is not running.
|
13
|
+
def running?
|
14
|
+
docker :version
|
15
|
+
end
|
16
|
+
|
17
|
+
# Do we have superuser access to install Docker and start system services?
|
18
|
+
def superuser?
|
19
|
+
[ '[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null' ]
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_network
|
23
|
+
docker :network, :create, :kamal
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def get_docker
|
28
|
+
shell \
|
29
|
+
any \
|
30
|
+
[ :curl, "-fsSL", "https://get.docker.com" ],
|
31
|
+
[ :wget, "-O -", "https://get.docker.com" ],
|
32
|
+
[ :echo, "\"exit 1\"" ]
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Kamal::Commands::Hook < Kamal::Commands::Base
|
2
|
+
def run(hook)
|
3
|
+
[ hook_file(hook) ]
|
4
|
+
end
|
5
|
+
|
6
|
+
def env(secrets: false, **details)
|
7
|
+
tags(**details).env.tap do |env|
|
8
|
+
env.merge!(config.secrets.to_h) if secrets
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def hook_exists?(hook)
|
13
|
+
Pathname.new(hook_file(hook)).exist?
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def hook_file(hook)
|
18
|
+
File.join(config.hooks_path, hook)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "active_support/duration"
|
2
|
+
require "time"
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
class Kamal::Commands::Lock < Kamal::Commands::Base
|
6
|
+
def acquire(message, version)
|
7
|
+
combine \
|
8
|
+
[ :mkdir, lock_dir ],
|
9
|
+
write_lock_details(message, version)
|
10
|
+
end
|
11
|
+
|
12
|
+
def release
|
13
|
+
combine \
|
14
|
+
[ :rm, lock_details_file ],
|
15
|
+
[ :rm, "-r", lock_dir ]
|
16
|
+
end
|
17
|
+
|
18
|
+
def status
|
19
|
+
combine \
|
20
|
+
stat_lock_dir,
|
21
|
+
read_lock_details
|
22
|
+
end
|
23
|
+
|
24
|
+
def ensure_locks_directory
|
25
|
+
[ :mkdir, "-p", locks_dir ]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def write_lock_details(message, version)
|
30
|
+
write \
|
31
|
+
[ :echo, "\"#{Base64.encode64(lock_details(message, version))}\"" ],
|
32
|
+
lock_details_file
|
33
|
+
end
|
34
|
+
|
35
|
+
def read_lock_details
|
36
|
+
pipe \
|
37
|
+
[ :cat, lock_details_file ],
|
38
|
+
[ :base64, "-d" ]
|
39
|
+
end
|
40
|
+
|
41
|
+
def stat_lock_dir
|
42
|
+
write \
|
43
|
+
[ :stat, lock_dir ],
|
44
|
+
"/dev/null"
|
45
|
+
end
|
46
|
+
|
47
|
+
def lock_dir
|
48
|
+
dir_name = [ "lock", config.service, config.destination ].compact.join("-")
|
49
|
+
|
50
|
+
File.join(config.run_directory, dir_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def lock_details_file
|
54
|
+
File.join(lock_dir, "details")
|
55
|
+
end
|
56
|
+
|
57
|
+
def lock_details(message, version)
|
58
|
+
<<~DETAILS.strip
|
59
|
+
Locked by: #{locked_by} at #{Time.now.utc.iso8601}
|
60
|
+
Version: #{version}
|
61
|
+
Message: #{message}
|
62
|
+
DETAILS
|
63
|
+
end
|
64
|
+
|
65
|
+
def locked_by
|
66
|
+
Kamal::Git.user_name
|
67
|
+
rescue Errno::ENOENT
|
68
|
+
"Unknown"
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
class Kamal::Commands::Proxy < Kamal::Commands::Base
|
2
|
+
delegate :argumentize, :optionize, to: Kamal::Utils
|
3
|
+
|
4
|
+
def run
|
5
|
+
pipe boot_config, xargs(docker_run)
|
6
|
+
end
|
7
|
+
|
8
|
+
def start
|
9
|
+
docker :container, :start, container_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def stop(name: container_name)
|
13
|
+
docker :container, :stop, name
|
14
|
+
end
|
15
|
+
|
16
|
+
def start_or_run
|
17
|
+
combine start, run, by: "||"
|
18
|
+
end
|
19
|
+
|
20
|
+
def info
|
21
|
+
docker :ps, "--filter", "name=^#{container_name}$"
|
22
|
+
end
|
23
|
+
|
24
|
+
def version
|
25
|
+
pipe \
|
26
|
+
docker(:inspect, container_name, "--format '{{.Config.Image}}'"),
|
27
|
+
[ :awk, "-F:", "'{print \$NF}'" ]
|
28
|
+
end
|
29
|
+
|
30
|
+
def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
|
31
|
+
pipe \
|
32
|
+
docker(:logs, container_name, ("--since #{since}" if since), ("--tail #{lines}" if lines), ("--timestamps" if timestamps), "2>&1"),
|
33
|
+
("grep '#{grep}'#{" #{grep_options}" if grep_options}" if grep)
|
34
|
+
end
|
35
|
+
|
36
|
+
def follow_logs(host:, timestamps: true, grep: nil, grep_options: nil)
|
37
|
+
run_over_ssh pipe(
|
38
|
+
docker(:logs, container_name, ("--timestamps" if timestamps), "--tail", "10", "--follow", "2>&1"),
|
39
|
+
(%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
|
40
|
+
).join(" "), host: host
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove_container
|
44
|
+
docker :container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy"
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_image
|
48
|
+
docker :image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy"
|
49
|
+
end
|
50
|
+
|
51
|
+
def cleanup_traefik
|
52
|
+
chain \
|
53
|
+
docker(:container, :stop, "traefik"),
|
54
|
+
combine(
|
55
|
+
docker(:container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=Traefik"),
|
56
|
+
docker(:image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=Traefik")
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def ensure_proxy_directory
|
61
|
+
make_directory config.proxy_boot.host_directory
|
62
|
+
end
|
63
|
+
|
64
|
+
def remove_proxy_directory
|
65
|
+
remove_directory config.proxy_boot.host_directory
|
66
|
+
end
|
67
|
+
|
68
|
+
def ensure_apps_config_directory
|
69
|
+
make_directory config.proxy_boot.apps_directory
|
70
|
+
end
|
71
|
+
|
72
|
+
def boot_config
|
73
|
+
[ :echo, "#{substitute(read_boot_options)} #{substitute(read_image)}:#{substitute(read_image_version)} #{substitute(read_run_command)}" ]
|
74
|
+
end
|
75
|
+
|
76
|
+
def read_boot_options
|
77
|
+
read_file(config.proxy_boot.options_file, default: config.proxy_boot.default_boot_options.join(" "))
|
78
|
+
end
|
79
|
+
|
80
|
+
def read_image
|
81
|
+
read_file(config.proxy_boot.image_file, default: config.proxy_boot.image_default)
|
82
|
+
end
|
83
|
+
|
84
|
+
def read_image_version
|
85
|
+
read_file(config.proxy_boot.image_version_file, default: Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION)
|
86
|
+
end
|
87
|
+
|
88
|
+
def read_run_command
|
89
|
+
read_file(config.proxy_boot.run_command_file)
|
90
|
+
end
|
91
|
+
|
92
|
+
def reset_boot_options
|
93
|
+
remove_file config.proxy_boot.options_file
|
94
|
+
end
|
95
|
+
|
96
|
+
def reset_image
|
97
|
+
remove_file config.proxy_boot.image_file
|
98
|
+
end
|
99
|
+
|
100
|
+
def reset_image_version
|
101
|
+
remove_file config.proxy_boot.image_version_file
|
102
|
+
end
|
103
|
+
|
104
|
+
def reset_run_command
|
105
|
+
remove_file config.proxy_boot.run_command_file
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
def container_name
|
110
|
+
config.proxy_boot.container_name
|
111
|
+
end
|
112
|
+
|
113
|
+
def read_file(file, default: nil)
|
114
|
+
combine [ :cat, file, "2>", "/dev/null" ], [ :echo, "\"#{default}\"" ], by: "||"
|
115
|
+
end
|
116
|
+
|
117
|
+
def docker_run
|
118
|
+
docker \
|
119
|
+
:run,
|
120
|
+
"--name", container_name,
|
121
|
+
"--network", "kamal",
|
122
|
+
"--detach",
|
123
|
+
"--restart", "unless-stopped",
|
124
|
+
"--volume", "kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy",
|
125
|
+
*config.proxy_boot.apps_volume.docker_args
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "active_support/duration"
|
2
|
+
require "active_support/core_ext/numeric/time"
|
3
|
+
|
4
|
+
class Kamal::Commands::Prune < Kamal::Commands::Base
|
5
|
+
def dangling_images
|
6
|
+
docker :image, :prune, "--force", "--filter", "label=service=#{config.service}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def tagged_images
|
10
|
+
pipe \
|
11
|
+
docker(:image, :ls, *service_filter, "--format", "'{{.ID}} {{.Repository}}:{{.Tag}}'"),
|
12
|
+
grep("-v -w \"#{active_image_list}\""),
|
13
|
+
"while read image tag; do docker rmi $tag; done"
|
14
|
+
end
|
15
|
+
|
16
|
+
def app_containers(retain:)
|
17
|
+
pipe \
|
18
|
+
docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters),
|
19
|
+
"tail -n +#{retain + 1}",
|
20
|
+
"while read container_id; do docker rm $container_id; done"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def stopped_containers_filters
|
25
|
+
[ "created", "exited", "dead" ].flat_map { |status| [ "--filter", "status=#{status}" ] }
|
26
|
+
end
|
27
|
+
|
28
|
+
def active_image_list
|
29
|
+
# Pull the images that are used by any containers
|
30
|
+
# Append repo:latest - to avoid deleting the latest tag
|
31
|
+
# Append repo:<none> - to avoid deleting dangling images that are in use. Unused dangling images are deleted separately
|
32
|
+
"$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=#{config.service} | tr -d '\\n')#{config.latest_image}\\|#{config.repository}:<none>"
|
33
|
+
end
|
34
|
+
|
35
|
+
def service_filter
|
36
|
+
[ "--filter", "label=service=#{config.service}" ]
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Kamal::Commands::Registry < Kamal::Commands::Base
|
2
|
+
def login(registry_config: nil)
|
3
|
+
registry_config ||= config.registry
|
4
|
+
|
5
|
+
docker :login,
|
6
|
+
registry_config.server,
|
7
|
+
"-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)),
|
8
|
+
"-p", sensitive(Kamal::Utils.escape_shell_value(registry_config.password))
|
9
|
+
end
|
10
|
+
|
11
|
+
def logout(registry_config: nil)
|
12
|
+
registry_config ||= config.registry
|
13
|
+
|
14
|
+
docker :logout, registry_config.server
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Kamal::Commands::Server < Kamal::Commands::Base
|
2
|
+
def ensure_run_directory
|
3
|
+
make_directory config.run_directory
|
4
|
+
end
|
5
|
+
|
6
|
+
def remove_app_directory
|
7
|
+
remove_directory config.app_directory
|
8
|
+
end
|
9
|
+
|
10
|
+
def app_directory_count
|
11
|
+
pipe \
|
12
|
+
[ :ls, config.apps_directory ],
|
13
|
+
[ :wc, "-l" ]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
class Kamal::Configuration::Accessory
|
2
|
+
include Kamal::Configuration::Validation
|
3
|
+
|
4
|
+
DEFAULT_NETWORK = "kamal"
|
5
|
+
|
6
|
+
delegate :argumentize, :optionize, to: Kamal::Utils
|
7
|
+
|
8
|
+
attr_reader :name, :env, :proxy, :registry
|
9
|
+
|
10
|
+
def initialize(name, config:)
|
11
|
+
@name, @config, @accessory_config = name.inquiry, config, config.raw_config["accessories"][name]
|
12
|
+
|
13
|
+
validate! \
|
14
|
+
accessory_config,
|
15
|
+
example: validation_yml["accessories"]["mysql"],
|
16
|
+
context: "accessories/#{name}",
|
17
|
+
with: Kamal::Configuration::Validator::Accessory
|
18
|
+
|
19
|
+
ensure_valid_roles
|
20
|
+
|
21
|
+
@env = initialize_env
|
22
|
+
@proxy = initialize_proxy if running_proxy?
|
23
|
+
@registry = initialize_registry if accessory_config["registry"].present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def service_name
|
27
|
+
accessory_config["service"] || "#{config.service}-#{name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def image
|
31
|
+
[ registry&.server, accessory_config["image"] ].compact.join("/")
|
32
|
+
end
|
33
|
+
|
34
|
+
def hosts
|
35
|
+
hosts_from_host || hosts_from_hosts || hosts_from_roles || hosts_from_tags
|
36
|
+
end
|
37
|
+
|
38
|
+
def port
|
39
|
+
if port = accessory_config["port"]&.to_s
|
40
|
+
port.include?(":") ? port : "#{port}:#{port}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def network_args
|
45
|
+
argumentize "--network", network
|
46
|
+
end
|
47
|
+
|
48
|
+
def publish_args
|
49
|
+
argumentize "--publish", port if port
|
50
|
+
end
|
51
|
+
|
52
|
+
def labels
|
53
|
+
default_labels.merge(accessory_config["labels"] || {})
|
54
|
+
end
|
55
|
+
|
56
|
+
def label_args
|
57
|
+
argumentize "--label", labels
|
58
|
+
end
|
59
|
+
|
60
|
+
def env_args
|
61
|
+
[ *env.clear_args, *argumentize("--env-file", secrets_path) ]
|
62
|
+
end
|
63
|
+
|
64
|
+
def env_directory
|
65
|
+
File.join(config.env_directory, "accessories")
|
66
|
+
end
|
67
|
+
|
68
|
+
def secrets_io
|
69
|
+
env.secrets_io
|
70
|
+
end
|
71
|
+
|
72
|
+
def secrets_path
|
73
|
+
File.join(config.env_directory, "accessories", "#{name}.env")
|
74
|
+
end
|
75
|
+
|
76
|
+
def files
|
77
|
+
accessory_config["files"]&.to_h do |local_to_remote_mapping|
|
78
|
+
local_file, remote_file = local_to_remote_mapping.split(":")
|
79
|
+
[ expand_local_file(local_file), expand_remote_file(remote_file) ]
|
80
|
+
end || {}
|
81
|
+
end
|
82
|
+
|
83
|
+
def directories
|
84
|
+
accessory_config["directories"]&.to_h do |host_to_container_mapping|
|
85
|
+
host_path, container_path = host_to_container_mapping.split(":")
|
86
|
+
[ expand_host_path(host_path), container_path ]
|
87
|
+
end || {}
|
88
|
+
end
|
89
|
+
|
90
|
+
def volumes
|
91
|
+
specific_volumes + remote_files_as_volumes + remote_directories_as_volumes
|
92
|
+
end
|
93
|
+
|
94
|
+
def volume_args
|
95
|
+
argumentize "--volume", volumes
|
96
|
+
end
|
97
|
+
|
98
|
+
def option_args
|
99
|
+
if args = accessory_config["options"]
|
100
|
+
optionize args
|
101
|
+
else
|
102
|
+
[]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def cmd
|
107
|
+
accessory_config["cmd"]
|
108
|
+
end
|
109
|
+
|
110
|
+
def running_proxy?
|
111
|
+
accessory_config["proxy"].present?
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
attr_reader :config, :accessory_config
|
116
|
+
|
117
|
+
def initialize_env
|
118
|
+
Kamal::Configuration::Env.new \
|
119
|
+
config: accessory_config.fetch("env", {}),
|
120
|
+
secrets: config.secrets,
|
121
|
+
context: "accessories/#{name}/env"
|
122
|
+
end
|
123
|
+
|
124
|
+
def initialize_proxy
|
125
|
+
Kamal::Configuration::Proxy.new \
|
126
|
+
config: config,
|
127
|
+
proxy_config: accessory_config["proxy"],
|
128
|
+
context: "accessories/#{name}/proxy",
|
129
|
+
secrets: config.secrets
|
130
|
+
end
|
131
|
+
|
132
|
+
def initialize_registry
|
133
|
+
Kamal::Configuration::Registry.new \
|
134
|
+
config: accessory_config,
|
135
|
+
secrets: config.secrets,
|
136
|
+
context: "accessories/#{name}/registry"
|
137
|
+
end
|
138
|
+
|
139
|
+
def default_labels
|
140
|
+
{ "service" => service_name }
|
141
|
+
end
|
142
|
+
|
143
|
+
def expand_local_file(local_file)
|
144
|
+
if local_file.end_with?("erb")
|
145
|
+
with_clear_env_loaded { read_dynamic_file(local_file) }
|
146
|
+
else
|
147
|
+
Pathname.new(File.expand_path(local_file)).to_s
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def with_clear_env_loaded
|
152
|
+
env.clear.each { |k, v| ENV[k] = v }
|
153
|
+
yield
|
154
|
+
ensure
|
155
|
+
env.clear.each { |k, v| ENV.delete(k) }
|
156
|
+
end
|
157
|
+
|
158
|
+
def read_dynamic_file(local_file)
|
159
|
+
StringIO.new(ERB.new(File.read(local_file)).result)
|
160
|
+
end
|
161
|
+
|
162
|
+
def expand_remote_file(remote_file)
|
163
|
+
service_name + remote_file
|
164
|
+
end
|
165
|
+
|
166
|
+
def specific_volumes
|
167
|
+
accessory_config["volumes"] || []
|
168
|
+
end
|
169
|
+
|
170
|
+
def remote_files_as_volumes
|
171
|
+
accessory_config["files"]&.collect do |local_to_remote_mapping|
|
172
|
+
_, remote_file = local_to_remote_mapping.split(":")
|
173
|
+
"#{service_data_directory + remote_file}:#{remote_file}"
|
174
|
+
end || []
|
175
|
+
end
|
176
|
+
|
177
|
+
def remote_directories_as_volumes
|
178
|
+
accessory_config["directories"]&.collect do |host_to_container_mapping|
|
179
|
+
host_path, container_path = host_to_container_mapping.split(":")
|
180
|
+
[ expand_host_path(host_path), container_path ].join(":")
|
181
|
+
end || []
|
182
|
+
end
|
183
|
+
|
184
|
+
def expand_host_path(host_path)
|
185
|
+
absolute_path?(host_path) ? host_path : File.join(service_data_directory, host_path)
|
186
|
+
end
|
187
|
+
|
188
|
+
def absolute_path?(path)
|
189
|
+
Pathname.new(path).absolute?
|
190
|
+
end
|
191
|
+
|
192
|
+
def service_data_directory
|
193
|
+
"$PWD/#{service_name}"
|
194
|
+
end
|
195
|
+
|
196
|
+
def hosts_from_host
|
197
|
+
[ accessory_config["host"] ] if accessory_config.key?("host")
|
198
|
+
end
|
199
|
+
|
200
|
+
def hosts_from_hosts
|
201
|
+
accessory_config["hosts"] if accessory_config.key?("hosts")
|
202
|
+
end
|
203
|
+
|
204
|
+
def hosts_from_roles
|
205
|
+
if accessory_config.key?("role")
|
206
|
+
config.role(accessory_config["role"])&.hosts
|
207
|
+
elsif accessory_config.key?("roles")
|
208
|
+
accessory_config["roles"].flat_map { |role| config.role(role)&.hosts }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def hosts_from_tags
|
213
|
+
if accessory_config.key?("tag")
|
214
|
+
extract_hosts_from_config_with_tag(accessory_config["tag"])
|
215
|
+
elsif accessory_config.key?("tags")
|
216
|
+
accessory_config["tags"].flat_map { |tag| extract_hosts_from_config_with_tag(tag) }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def extract_hosts_from_config_with_tag(tag)
|
221
|
+
if (servers_with_roles = config.raw_config.servers).is_a?(Hash)
|
222
|
+
servers_with_roles.flat_map do |role, servers_in_role|
|
223
|
+
servers_in_role.filter_map do |host|
|
224
|
+
host.keys.first if host.is_a?(Hash) && host.values.first.include?(tag)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def network
|
231
|
+
accessory_config["network"] || DEFAULT_NETWORK
|
232
|
+
end
|
233
|
+
|
234
|
+
def ensure_valid_roles
|
235
|
+
if accessory_config["roles"] && (missing_roles = accessory_config["roles"] - config.roles.map(&:name)).any?
|
236
|
+
raise Kamal::ConfigurationError, "accessories/#{name}: unknown roles #{missing_roles.join(", ")}"
|
237
|
+
elsif accessory_config["role"] && !config.role(accessory_config["role"])
|
238
|
+
raise Kamal::ConfigurationError, "accessories/#{name}: unknown role #{accessory_config["role"]}"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Kamal::Configuration::Alias
|
2
|
+
include Kamal::Configuration::Validation
|
3
|
+
|
4
|
+
attr_reader :name, :command
|
5
|
+
|
6
|
+
def initialize(name, config:)
|
7
|
+
@name, @command = name.inquiry, config.raw_config["aliases"][name]
|
8
|
+
|
9
|
+
validate! \
|
10
|
+
command,
|
11
|
+
example: validation_yml["aliases"]["uname"],
|
12
|
+
context: "aliases/#{name}",
|
13
|
+
with: Kamal::Configuration::Validator::Alias
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Kamal::Configuration::Boot
|
2
|
+
include Kamal::Configuration::Validation
|
3
|
+
|
4
|
+
attr_reader :boot_config, :host_count
|
5
|
+
|
6
|
+
def initialize(config:)
|
7
|
+
@boot_config = config.raw_config.boot || {}
|
8
|
+
@host_count = config.all_hosts.count
|
9
|
+
validate! boot_config
|
10
|
+
end
|
11
|
+
|
12
|
+
def limit
|
13
|
+
limit = boot_config["limit"]
|
14
|
+
|
15
|
+
if limit.to_s.end_with?("%")
|
16
|
+
[ host_count * limit.to_i / 100, 1 ].max
|
17
|
+
else
|
18
|
+
limit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def wait
|
23
|
+
boot_config["wait"]
|
24
|
+
end
|
25
|
+
end
|