kamal 1.8.3 → 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 +4 -4
- data/README.md +1 -1
- data/lib/kamal/cli/accessory.rb +92 -38
- data/lib/kamal/cli/alias/command.rb +10 -0
- data/lib/kamal/cli/app/{prepare_assets.rb → assets.rb} +1 -1
- data/lib/kamal/cli/app/boot.rb +23 -16
- 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 +132 -30
- data/lib/kamal/cli/base.rb +57 -53
- data/lib/kamal/cli/build.rb +81 -38
- 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 +60 -59
- data/lib/kamal/cli/proxy.rb +290 -0
- data/lib/kamal/cli/prune.rb +0 -1
- data/lib/kamal/cli/registry.rb +2 -0
- data/lib/kamal/cli/secrets.rb +49 -0
- data/lib/kamal/cli/server.rb +6 -5
- data/lib/kamal/cli/templates/deploy.yml +53 -53
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +2 -12
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +1 -1
- 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 +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +19 -6
- 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 +2 -0
- data/lib/kamal/commander/specifics.rb +19 -6
- data/lib/kamal/commander.rb +39 -32
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +19 -19
- data/lib/kamal/commands/app/assets.rb +10 -10
- data/lib/kamal/commands/app/containers.rb +2 -2
- data/lib/kamal/commands/app/error_pages.rb +9 -0
- data/lib/kamal/commands/app/execution.rb +7 -4
- data/lib/kamal/commands/app/images.rb +1 -1
- data/lib/kamal/commands/app/logging.rb +16 -6
- data/lib/kamal/commands/app/proxy.rb +32 -0
- data/lib/kamal/commands/app.rb +25 -24
- data/lib/kamal/commands/auditor.rb +12 -3
- data/lib/kamal/commands/base.rb +54 -8
- data/lib/kamal/commands/builder/base.rb +46 -16
- data/lib/kamal/commands/builder/clone.rb +16 -14
- 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 +21 -45
- 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 +127 -0
- data/lib/kamal/commands/prune.rb +1 -9
- data/lib/kamal/commands/registry.rb +9 -7
- data/lib/kamal/commands/server.rb +11 -1
- data/lib/kamal/configuration/accessory.rb +89 -12
- data/lib/kamal/configuration/alias.rb +15 -0
- data/lib/kamal/configuration/builder.rb +73 -15
- data/lib/kamal/configuration/docs/accessory.yml +53 -15
- data/lib/kamal/configuration/docs/alias.yml +26 -0
- data/lib/kamal/configuration/docs/boot.yml +3 -3
- data/lib/kamal/configuration/docs/builder.yml +63 -38
- data/lib/kamal/configuration/docs/configuration.yml +62 -46
- data/lib/kamal/configuration/docs/env.yml +61 -17
- data/lib/kamal/configuration/docs/logging.yml +3 -3
- data/lib/kamal/configuration/docs/proxy.yml +168 -0
- data/lib/kamal/configuration/docs/registry.yml +20 -13
- data/lib/kamal/configuration/docs/role.yml +14 -13
- data/lib/kamal/configuration/docs/servers.yml +2 -2
- data/lib/kamal/configuration/docs/ssh.yml +23 -19
- data/lib/kamal/configuration/docs/sshkit.yml +4 -4
- data/lib/kamal/configuration/env/tag.rb +4 -3
- data/lib/kamal/configuration/env.rb +19 -17
- data/lib/kamal/configuration/proxy/boot.rb +129 -0
- data/lib/kamal/configuration/proxy.rb +124 -0
- data/lib/kamal/configuration/registry.rb +7 -6
- data/lib/kamal/configuration/role.rb +69 -98
- data/lib/kamal/configuration/servers.rb +8 -1
- data/lib/kamal/configuration/validator/accessory.rb +6 -2
- data/lib/kamal/configuration/validator/alias.rb +15 -0
- data/lib/kamal/configuration/validator/builder.rb +6 -0
- data/lib/kamal/configuration/validator/proxy.rb +25 -0
- data/lib/kamal/configuration/validator/role.rb +3 -1
- data/lib/kamal/configuration/validator/servers.rb +1 -1
- data/lib/kamal/configuration/validator.rb +62 -24
- data/lib/kamal/configuration.rb +96 -50
- data/lib/kamal/docker.rb +30 -0
- data/lib/kamal/env_file.rb +7 -1
- data/lib/kamal/git.rb +10 -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 +1 -0
- data/lib/kamal/utils.rb +30 -0
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +3 -1
- metadata +63 -36
- 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/sample_hooks/pre-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/builder/multiarch/remote.rb +0 -65
- data/lib/kamal/commands/builder/multiarch.rb +0 -41
- data/lib/kamal/commands/builder/native/cached.rb +0 -25
- data/lib/kamal/commands/builder/native/remote.rb +0 -67
- data/lib/kamal/commands/builder/native.rb +0 -20
- 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
@@ -1,10 +1,13 @@
|
|
1
1
|
# Registry
|
2
2
|
#
|
3
|
-
# The default registry is Docker Hub, but you can change it using registry/server
|
3
|
+
# The default registry is Docker Hub, but you can change it using `registry/server`.
|
4
4
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
5
|
+
# By default, Docker Hub creates public repositories. To avoid making your images public,
|
6
|
+
# set up a private repository before deploying, or change the default repository privacy
|
7
|
+
# settings to private in your [Docker Hub settings](https://hub.docker.com/repository-settings/default-privacy).
|
8
|
+
#
|
9
|
+
# A reference to a secret (in this case, `DOCKER_REGISTRY_TOKEN`) will look up the secret
|
10
|
+
# in the local environment:
|
8
11
|
registry:
|
9
12
|
server: registry.digitalocean.com
|
10
13
|
username:
|
@@ -13,28 +16,31 @@ registry:
|
|
13
16
|
- DOCKER_REGISTRY_TOKEN
|
14
17
|
|
15
18
|
# Using AWS ECR as the container registry
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
+
#
|
20
|
+
# You will need to have the AWS CLI installed locally for this to work.
|
21
|
+
# AWS ECR’s access token is only valid for 12 hours. In order to avoid having to manually regenerate the token every time, you can use ERB in the `deploy.yml` file to shell out to the AWS CLI command and obtain the token:
|
19
22
|
registry:
|
20
23
|
server: <your aws account id>.dkr.ecr.<your aws region id>.amazonaws.com
|
21
24
|
username: AWS
|
22
25
|
password: <%= %x(aws ecr get-login-password) %>
|
23
26
|
|
24
27
|
# Using GCP Artifact Registry as the container registry
|
25
|
-
#
|
28
|
+
#
|
29
|
+
# To sign into Artifact Registry, you need to
|
26
30
|
# [create a service account](https://cloud.google.com/iam/docs/service-accounts-create#creating)
|
27
31
|
# and [set up roles and permissions](https://cloud.google.com/artifact-registry/docs/access-control#permissions).
|
28
|
-
# Normally, assigning
|
32
|
+
# Normally, assigning the `roles/artifactregistry.writer` role should be sufficient.
|
29
33
|
#
|
30
|
-
# Once the service account is ready, you need to generate and download a JSON key
|
34
|
+
# Once the service account is ready, you need to generate and download a JSON key and base64 encode it:
|
31
35
|
#
|
32
36
|
# ```shell
|
33
|
-
#
|
37
|
+
# base64 -i /path/to/key.json | tr -d "\\n"
|
34
38
|
# ```
|
35
|
-
#
|
39
|
+
#
|
40
|
+
# You'll then need to set the `KAMAL_REGISTRY_PASSWORD` secret to that value.
|
41
|
+
#
|
42
|
+
# Use the environment variable as the password along with `_json_key_base64` as the username.
|
36
43
|
# Here’s the final configuration:
|
37
|
-
|
38
44
|
registry:
|
39
45
|
server: <your registry region>-docker.pkg.dev
|
40
46
|
username: _json_key_base64
|
@@ -44,6 +50,7 @@ registry:
|
|
44
50
|
# Validating the configuration
|
45
51
|
#
|
46
52
|
# You can validate the configuration by running:
|
53
|
+
#
|
47
54
|
# ```shell
|
48
55
|
# kamal registry login
|
49
56
|
# ```
|
@@ -1,22 +1,21 @@
|
|
1
1
|
# Roles
|
2
2
|
#
|
3
3
|
# Roles are used to configure different types of servers in the deployment.
|
4
|
-
# The most common use for this is to run
|
4
|
+
# The most common use for this is to run web servers and job servers.
|
5
5
|
#
|
6
6
|
# Kamal expects there to be a `web` role, unless you set a different `primary_role`
|
7
7
|
# in the root configuration.
|
8
8
|
|
9
9
|
# Role configuration
|
10
10
|
#
|
11
|
-
# Roles are specified under the servers key
|
11
|
+
# Roles are specified under the servers key:
|
12
12
|
servers:
|
13
13
|
|
14
14
|
# Simple role configuration
|
15
15
|
#
|
16
|
+
# This can be a list of hosts if you don't need custom configuration for the role.
|
16
17
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# You can set tags on the hosts for custom env variables (see kamal docs env)
|
18
|
+
# You can set tags on the hosts for custom env variables (see kamal docs env):
|
20
19
|
web:
|
21
20
|
- 172.1.0.1
|
22
21
|
- 172.1.0.2: experiment1
|
@@ -24,29 +23,31 @@ servers:
|
|
24
23
|
|
25
24
|
# Custom role configuration
|
26
25
|
#
|
27
|
-
# When there are other options to set, the list of hosts goes under the `hosts` key
|
26
|
+
# When there are other options to set, the list of hosts goes under the `hosts` key.
|
27
|
+
#
|
28
|
+
# By default, only the primary role uses a proxy.
|
29
|
+
#
|
30
|
+
# For other roles, you can set it to `proxy: true` to enable it and inherit the root proxy
|
31
|
+
# configuration or provide a map of options to override the root configuration.
|
28
32
|
#
|
29
|
-
#
|
30
|
-
# it.
|
33
|
+
# For the primary role, you can set `proxy: false` to disable the proxy.
|
31
34
|
#
|
32
|
-
# You can also set a custom cmd to run in the container
|
35
|
+
# You can also set a custom `cmd` to run in the container and overwrite other settings
|
33
36
|
# from the root configuration.
|
34
37
|
workers:
|
35
38
|
hosts:
|
36
39
|
- 172.1.0.3
|
37
40
|
- 172.1.0.4: experiment1
|
38
|
-
traefik: true
|
39
41
|
cmd: "bin/jobs"
|
40
42
|
options:
|
41
43
|
memory: 2g
|
42
44
|
cpus: 4
|
43
|
-
healthcheck:
|
44
|
-
...
|
45
45
|
logging:
|
46
46
|
...
|
47
|
+
proxy:
|
48
|
+
...
|
47
49
|
labels:
|
48
50
|
my-label: workers
|
49
51
|
env:
|
50
52
|
...
|
51
53
|
asset_path: /public
|
52
|
-
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Servers are split into different roles, with each role having its own configuration.
|
4
4
|
#
|
5
|
-
# For simpler deployments though where all servers are identical, you can just specify a list of servers
|
5
|
+
# For simpler deployments, though, where all servers are identical, you can just specify a list of servers.
|
6
6
|
# They will be implicitly assigned to the `web` role.
|
7
7
|
servers:
|
8
8
|
- 172.0.0.1
|
@@ -19,7 +19,7 @@ servers:
|
|
19
19
|
|
20
20
|
# Roles
|
21
21
|
#
|
22
|
-
# For more complex deployments (e.g
|
22
|
+
# For more complex deployments (e.g., if you are running job hosts), you can specify roles and configure each separately (see kamal docs role):
|
23
23
|
servers:
|
24
24
|
web:
|
25
25
|
...
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# SSH configuration
|
2
2
|
#
|
3
|
-
# Kamal uses SSH to connect run commands on your hosts.
|
4
|
-
# By default it will attempt to connect to the root user on port 22
|
3
|
+
# Kamal uses SSH to connect and run commands on your hosts.
|
4
|
+
# By default, it will attempt to connect to the root user on port 22.
|
5
5
|
#
|
6
|
-
# If you are using non-root user, you may need to bootstrap your servers manually
|
6
|
+
# If you are using a non-root user, you may need to bootstrap your servers manually before using them with Kamal. On Ubuntu, you’d do:
|
7
7
|
#
|
8
8
|
# ```shell
|
9
9
|
# sudo apt update
|
@@ -12,7 +12,6 @@
|
|
12
12
|
# sudo usermod -a -G docker app
|
13
13
|
# ```
|
14
14
|
|
15
|
-
|
16
15
|
# SSH options
|
17
16
|
#
|
18
17
|
# The options are specified under the ssh key in the configuration file.
|
@@ -20,47 +19,52 @@ ssh:
|
|
20
19
|
|
21
20
|
# The SSH user
|
22
21
|
#
|
23
|
-
# Defaults to `root
|
24
|
-
#
|
22
|
+
# Defaults to `root`:
|
25
23
|
user: app
|
26
24
|
|
27
25
|
# The SSH port
|
28
26
|
#
|
29
|
-
# Defaults to 22
|
27
|
+
# Defaults to 22:
|
30
28
|
port: "2222"
|
31
29
|
|
32
30
|
# Proxy host
|
33
31
|
#
|
34
|
-
# Specified in the form <host> or <user>@<host
|
32
|
+
# Specified in the form <host> or <user>@<host>:
|
35
33
|
proxy: root@proxy-host
|
36
34
|
|
37
35
|
# Proxy command
|
38
36
|
#
|
39
|
-
# A custom proxy command, required for older versions of SSH
|
37
|
+
# A custom proxy command, required for older versions of SSH:
|
40
38
|
proxy_command: "ssh -W %h:%p user@proxy"
|
41
39
|
|
42
40
|
# Log level
|
43
41
|
#
|
44
|
-
# Defaults to `fatal`. Set this to debug if you are having
|
45
|
-
# SSH connection issues.
|
42
|
+
# Defaults to `fatal`. Set this to `debug` if you are having SSH connection issues.
|
46
43
|
log_level: debug
|
47
44
|
|
48
|
-
# Keys
|
45
|
+
# Keys only
|
49
46
|
#
|
50
|
-
# Set to true to use only private keys from keys and key_data parameters,
|
51
|
-
# even if ssh-agent offers more identities. This option is intended for
|
52
|
-
# situations where ssh-agent offers many different
|
53
|
-
#
|
47
|
+
# Set to `true` to use only private keys from the `keys` and `key_data` parameters,
|
48
|
+
# even if ssh-agent offers more identities. This option is intended for
|
49
|
+
# situations where ssh-agent offers many different identities or you
|
50
|
+
# need to overwrite all identities and force a single one.
|
54
51
|
keys_only: false
|
55
52
|
|
56
53
|
# Keys
|
57
54
|
#
|
58
|
-
# An array of file names of private keys to use for
|
59
|
-
# and
|
55
|
+
# An array of file names of private keys to use for public key
|
56
|
+
# and host-based authentication:
|
60
57
|
keys: [ "~/.ssh/id.pem" ]
|
61
58
|
|
62
|
-
# Key
|
59
|
+
# Key data
|
63
60
|
#
|
64
61
|
# An array of strings, with each element of the array being
|
65
62
|
# a raw private key in PEM format.
|
66
63
|
key_data: [ "-----BEGIN OPENSSH PRIVATE KEY-----" ]
|
64
|
+
|
65
|
+
# Config
|
66
|
+
#
|
67
|
+
# Set to true to load the default OpenSSH config files (~/.ssh/config,
|
68
|
+
# /etc/ssh_config), to false ignore config files, or to a file path
|
69
|
+
# (or array of paths) to load specific configuration. Defaults to true.
|
70
|
+
config: true
|
@@ -2,8 +2,8 @@
|
|
2
2
|
#
|
3
3
|
# [SSHKit](https://github.com/capistrano/sshkit) is the SSH toolkit used by Kamal.
|
4
4
|
#
|
5
|
-
# The default settings should be sufficient for most use cases, but
|
6
|
-
# when connecting to a large number of hosts you may need to adjust
|
5
|
+
# The default, settings should be sufficient for most use cases, but
|
6
|
+
# when connecting to a large number of hosts, you may need to adjust.
|
7
7
|
|
8
8
|
# SSHKit options
|
9
9
|
#
|
@@ -13,11 +13,11 @@ sshkit:
|
|
13
13
|
# Max concurrent starts
|
14
14
|
#
|
15
15
|
# Creating SSH connections concurrently can be an issue when deploying to many servers.
|
16
|
-
# By default Kamal will limit concurrent connection starts to 30 at a time.
|
16
|
+
# By default, Kamal will limit concurrent connection starts to 30 at a time.
|
17
17
|
max_concurrent_starts: 10
|
18
18
|
|
19
19
|
# Pool idle timeout
|
20
20
|
#
|
21
21
|
# Kamal sets a long idle timeout of 900 seconds on connections to try to avoid
|
22
|
-
# re-connection storms after an idle period,
|
22
|
+
# re-connection storms after an idle period, such as building an image or waiting for CI.
|
23
23
|
pool_idle_timeout: 300
|
@@ -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,38 @@
|
|
1
1
|
class Kamal::Configuration::Env
|
2
2
|
include Kamal::Configuration::Validation
|
3
3
|
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :context, :clear, :secret_keys
|
5
5
|
delegate :argumentize, to: Kamal::Utils
|
6
6
|
|
7
|
-
def initialize(config:,
|
7
|
+
def initialize(config:, secrets:, context: "env")
|
8
8
|
@clear = config.fetch("clear", config.key?("secret") || config.key?("tags") ? {} : config)
|
9
|
-
@
|
10
|
-
@
|
9
|
+
@secrets = secrets
|
10
|
+
@secret_keys = config.fetch("secret", [])
|
11
11
|
@context = context
|
12
12
|
validate! config, context: context, with: Kamal::Configuration::Validator::Env
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def clear_args
|
16
|
+
argumentize("--env", clear)
|
17
17
|
end
|
18
18
|
|
19
19
|
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)
|
20
|
+
Kamal::EnvFile.new(aliased_secrets).to_io
|
29
21
|
end
|
30
22
|
|
31
23
|
def merge(other)
|
32
24
|
self.class.new \
|
33
|
-
config: { "clear" => clear.merge(other.clear), "secret" =>
|
34
|
-
|
25
|
+
config: { "clear" => clear.merge(other.clear), "secret" => secret_keys | other.secret_keys },
|
26
|
+
secrets: @secrets
|
35
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def aliased_secrets
|
31
|
+
secret_keys.to_h { |key| extract_alias(key) }.transform_values { |secret_key| @secrets[secret_key] }
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_alias(key)
|
35
|
+
key_name, key_aliased_to = key.split(":", 2)
|
36
|
+
[ key_name, key_aliased_to || key_name ]
|
37
|
+
end
|
36
38
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
class Kamal::Configuration::Proxy::Boot
|
2
|
+
MINIMUM_VERSION = "v0.9.0"
|
3
|
+
DEFAULT_HTTP_PORT = 80
|
4
|
+
DEFAULT_HTTPS_PORT = 443
|
5
|
+
DEFAULT_LOG_MAX_SIZE = "10m"
|
6
|
+
|
7
|
+
attr_reader :config
|
8
|
+
delegate :argumentize, :optionize, to: Kamal::Utils
|
9
|
+
|
10
|
+
def initialize(config:)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
def publish_args(http_port, https_port, bind_ips = nil)
|
15
|
+
ensure_valid_bind_ips(bind_ips)
|
16
|
+
|
17
|
+
(bind_ips || [ nil ]).map do |bind_ip|
|
18
|
+
bind_ip = format_bind_ip(bind_ip)
|
19
|
+
publish_http = [ bind_ip, http_port, DEFAULT_HTTP_PORT ].compact.join(":")
|
20
|
+
publish_https = [ bind_ip, https_port, DEFAULT_HTTPS_PORT ].compact.join(":")
|
21
|
+
|
22
|
+
argumentize "--publish", [ publish_http, publish_https ]
|
23
|
+
end.join(" ")
|
24
|
+
end
|
25
|
+
|
26
|
+
def logging_args(max_size)
|
27
|
+
argumentize "--log-opt", "max-size=#{max_size}" if max_size.present?
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_boot_options
|
31
|
+
[
|
32
|
+
*(publish_args(DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT, nil)),
|
33
|
+
*(logging_args(DEFAULT_LOG_MAX_SIZE))
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
def repository_name
|
38
|
+
"basecamp"
|
39
|
+
end
|
40
|
+
|
41
|
+
def image_name
|
42
|
+
"kamal-proxy"
|
43
|
+
end
|
44
|
+
|
45
|
+
def image_default
|
46
|
+
"#{repository_name}/#{image_name}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def container_name
|
50
|
+
"kamal-proxy"
|
51
|
+
end
|
52
|
+
|
53
|
+
def host_directory
|
54
|
+
File.join config.run_directory, "proxy"
|
55
|
+
end
|
56
|
+
|
57
|
+
def options_file
|
58
|
+
File.join host_directory, "options"
|
59
|
+
end
|
60
|
+
|
61
|
+
def image_file
|
62
|
+
File.join host_directory, "image"
|
63
|
+
end
|
64
|
+
|
65
|
+
def image_version_file
|
66
|
+
File.join host_directory, "image_version"
|
67
|
+
end
|
68
|
+
|
69
|
+
def run_command_file
|
70
|
+
File.join host_directory, "run_command"
|
71
|
+
end
|
72
|
+
|
73
|
+
def apps_directory
|
74
|
+
File.join host_directory, "apps-config"
|
75
|
+
end
|
76
|
+
|
77
|
+
def apps_container_directory
|
78
|
+
"/home/kamal-proxy/.apps-config"
|
79
|
+
end
|
80
|
+
|
81
|
+
def apps_volume
|
82
|
+
Kamal::Configuration::Volume.new \
|
83
|
+
host_path: apps_directory,
|
84
|
+
container_path: apps_container_directory
|
85
|
+
end
|
86
|
+
|
87
|
+
def app_directory
|
88
|
+
File.join apps_directory, config.service_and_destination
|
89
|
+
end
|
90
|
+
|
91
|
+
def app_container_directory
|
92
|
+
File.join apps_container_directory, config.service_and_destination
|
93
|
+
end
|
94
|
+
|
95
|
+
def error_pages_directory
|
96
|
+
File.join app_directory, "error_pages"
|
97
|
+
end
|
98
|
+
|
99
|
+
def error_pages_container_directory
|
100
|
+
File.join app_container_directory, "error_pages"
|
101
|
+
end
|
102
|
+
|
103
|
+
def tls_directory
|
104
|
+
File.join app_directory, "tls"
|
105
|
+
end
|
106
|
+
|
107
|
+
def tls_container_directory
|
108
|
+
File.join app_container_directory, "tls"
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
def ensure_valid_bind_ips(bind_ips)
|
113
|
+
bind_ips.present? && bind_ips.each do |ip|
|
114
|
+
next if ip =~ Resolv::IPv4::Regex || ip =~ Resolv::IPv6::Regex
|
115
|
+
raise ArgumentError, "Invalid publish IP address: #{ip}"
|
116
|
+
end
|
117
|
+
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def format_bind_ip(ip)
|
122
|
+
# Ensure IPv6 address inside square brackets - e.g. [::1]
|
123
|
+
if ip =~ Resolv::IPv6::Regex && ip !~ /\A\[.*\]\z/
|
124
|
+
"[#{ip}]"
|
125
|
+
else
|
126
|
+
ip
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,124 @@
|
|
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, :role_name, :secrets
|
10
|
+
|
11
|
+
def initialize(config:, proxy_config:, role_name: nil, secrets:, context: "proxy")
|
12
|
+
@config = config
|
13
|
+
@proxy_config = proxy_config
|
14
|
+
@proxy_config = {} if @proxy_config.nil?
|
15
|
+
@role_name = role_name
|
16
|
+
@secrets = secrets
|
17
|
+
validate! @proxy_config, with: Kamal::Configuration::Validator::Proxy, context: context
|
18
|
+
end
|
19
|
+
|
20
|
+
def app_port
|
21
|
+
proxy_config.fetch("app_port", 80)
|
22
|
+
end
|
23
|
+
|
24
|
+
def ssl?
|
25
|
+
proxy_config.fetch("ssl", false)
|
26
|
+
end
|
27
|
+
|
28
|
+
def hosts
|
29
|
+
proxy_config["hosts"] || proxy_config["host"]&.split(",") || []
|
30
|
+
end
|
31
|
+
|
32
|
+
def custom_ssl_certificate?
|
33
|
+
ssl = proxy_config["ssl"]
|
34
|
+
return false unless ssl.is_a?(Hash)
|
35
|
+
ssl["certificate_pem"].present? && ssl["private_key_pem"].present?
|
36
|
+
end
|
37
|
+
|
38
|
+
def certificate_pem_content
|
39
|
+
ssl = proxy_config["ssl"]
|
40
|
+
return nil unless ssl.is_a?(Hash)
|
41
|
+
secrets[ssl["certificate_pem"]]
|
42
|
+
end
|
43
|
+
|
44
|
+
def private_key_pem_content
|
45
|
+
ssl = proxy_config["ssl"]
|
46
|
+
return nil unless ssl.is_a?(Hash)
|
47
|
+
secrets[ssl["private_key_pem"]]
|
48
|
+
end
|
49
|
+
|
50
|
+
def host_tls_cert
|
51
|
+
tls_path(config.proxy_boot.tls_directory, "cert.pem")
|
52
|
+
end
|
53
|
+
|
54
|
+
def host_tls_key
|
55
|
+
tls_path(config.proxy_boot.tls_directory, "key.pem")
|
56
|
+
end
|
57
|
+
|
58
|
+
def container_tls_cert
|
59
|
+
tls_path(config.proxy_boot.tls_container_directory, "cert.pem")
|
60
|
+
end
|
61
|
+
|
62
|
+
def container_tls_key
|
63
|
+
tls_path(config.proxy_boot.tls_container_directory, "key.pem") if custom_ssl_certificate?
|
64
|
+
end
|
65
|
+
|
66
|
+
def deploy_options
|
67
|
+
{
|
68
|
+
host: hosts,
|
69
|
+
tls: ssl? ? true : nil,
|
70
|
+
"tls-certificate-path": container_tls_cert,
|
71
|
+
"tls-private-key-path": container_tls_key,
|
72
|
+
"deploy-timeout": seconds_duration(config.deploy_timeout),
|
73
|
+
"drain-timeout": seconds_duration(config.drain_timeout),
|
74
|
+
"health-check-interval": seconds_duration(proxy_config.dig("healthcheck", "interval")),
|
75
|
+
"health-check-timeout": seconds_duration(proxy_config.dig("healthcheck", "timeout")),
|
76
|
+
"health-check-path": proxy_config.dig("healthcheck", "path"),
|
77
|
+
"target-timeout": seconds_duration(proxy_config["response_timeout"]),
|
78
|
+
"buffer-requests": proxy_config.fetch("buffering", { "requests": true }).fetch("requests", true),
|
79
|
+
"buffer-responses": proxy_config.fetch("buffering", { "responses": true }).fetch("responses", true),
|
80
|
+
"buffer-memory": proxy_config.dig("buffering", "memory"),
|
81
|
+
"max-request-body": proxy_config.dig("buffering", "max_request_body"),
|
82
|
+
"max-response-body": proxy_config.dig("buffering", "max_response_body"),
|
83
|
+
"path-prefix": proxy_config.dig("path_prefix"),
|
84
|
+
"strip-path-prefix": proxy_config.dig("strip_path_prefix"),
|
85
|
+
"forward-headers": proxy_config.dig("forward_headers"),
|
86
|
+
"tls-redirect": proxy_config.dig("ssl_redirect"),
|
87
|
+
"log-request-header": proxy_config.dig("logging", "request_headers") || DEFAULT_LOG_REQUEST_HEADERS,
|
88
|
+
"log-response-header": proxy_config.dig("logging", "response_headers"),
|
89
|
+
"error-pages": error_pages
|
90
|
+
}.compact
|
91
|
+
end
|
92
|
+
|
93
|
+
def deploy_command_args(target:)
|
94
|
+
optionize ({ target: "#{target}:#{app_port}" }).merge(deploy_options), with: "="
|
95
|
+
end
|
96
|
+
|
97
|
+
def stop_options(drain_timeout: nil, message: nil)
|
98
|
+
{
|
99
|
+
"drain-timeout": seconds_duration(drain_timeout),
|
100
|
+
message: message
|
101
|
+
}.compact
|
102
|
+
end
|
103
|
+
|
104
|
+
def stop_command_args(**options)
|
105
|
+
optionize stop_options(**options), with: "="
|
106
|
+
end
|
107
|
+
|
108
|
+
def merge(other)
|
109
|
+
self.class.new config: config, proxy_config: other.proxy_config.deep_merge(proxy_config), role_name: role_name, secrets: secrets
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def tls_path(directory, filename)
|
114
|
+
File.join([ directory, role_name, filename ].compact) if custom_ssl_certificate?
|
115
|
+
end
|
116
|
+
|
117
|
+
def seconds_duration(value)
|
118
|
+
value ? "#{value}s" : nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def error_pages
|
122
|
+
File.join config.proxy_boot.error_pages_container_directory, config.version if config.error_pages_path
|
123
|
+
end
|
124
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
class Kamal::Configuration::Registry
|
2
2
|
include Kamal::Configuration::Validation
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
validate! registry_config, with: Kamal::Configuration::Validator::Registry
|
4
|
+
def initialize(config:, secrets:, context: "registry")
|
5
|
+
@registry_config = config["registry"] || {}
|
6
|
+
@secrets = secrets
|
7
|
+
validate! registry_config, context: context, with: Kamal::Configuration::Validator::Registry
|
9
8
|
end
|
10
9
|
|
11
10
|
def server
|
@@ -21,9 +20,11 @@ class Kamal::Configuration::Registry
|
|
21
20
|
end
|
22
21
|
|
23
22
|
private
|
23
|
+
attr_reader :registry_config, :secrets
|
24
|
+
|
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
|