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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +13 -0
  4. data/bin/dash +18 -0
  5. data/bin/kamal +18 -0
  6. data/lib/kamal/cli/accessory.rb +342 -0
  7. data/lib/kamal/cli/alias/command.rb +10 -0
  8. data/lib/kamal/cli/app/assets.rb +24 -0
  9. data/lib/kamal/cli/app/boot.rb +126 -0
  10. data/lib/kamal/cli/app/error_pages.rb +33 -0
  11. data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
  12. data/lib/kamal/cli/app.rb +368 -0
  13. data/lib/kamal/cli/base.rb +324 -0
  14. data/lib/kamal/cli/build/clone.rb +59 -0
  15. data/lib/kamal/cli/build/port_forwarding.rb +66 -0
  16. data/lib/kamal/cli/build.rb +242 -0
  17. data/lib/kamal/cli/healthcheck/barrier.rb +33 -0
  18. data/lib/kamal/cli/healthcheck/error.rb +2 -0
  19. data/lib/kamal/cli/healthcheck/poller.rb +42 -0
  20. data/lib/kamal/cli/lock.rb +34 -0
  21. data/lib/kamal/cli/main.rb +299 -0
  22. data/lib/kamal/cli/proxy.rb +419 -0
  23. data/lib/kamal/cli/prune.rb +34 -0
  24. data/lib/kamal/cli/registry.rb +49 -0
  25. data/lib/kamal/cli/secrets.rb +50 -0
  26. data/lib/kamal/cli/server.rb +70 -0
  27. data/lib/kamal/cli/templates/deploy.yml +102 -0
  28. data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +3 -0
  29. data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
  30. data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +14 -0
  31. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  32. data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
  33. data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +51 -0
  34. data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +47 -0
  35. data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +122 -0
  36. data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
  37. data/lib/kamal/cli/templates/secrets +22 -0
  38. data/lib/kamal/cli.rb +9 -0
  39. data/lib/kamal/commander/specifics.rb +62 -0
  40. data/lib/kamal/commander.rb +230 -0
  41. data/lib/kamal/commands/accessory/proxy.rb +16 -0
  42. data/lib/kamal/commands/accessory.rb +118 -0
  43. data/lib/kamal/commands/app/assets.rb +51 -0
  44. data/lib/kamal/commands/app/containers.rb +31 -0
  45. data/lib/kamal/commands/app/error_pages.rb +9 -0
  46. data/lib/kamal/commands/app/execution.rb +38 -0
  47. data/lib/kamal/commands/app/images.rb +13 -0
  48. data/lib/kamal/commands/app/logging.rb +28 -0
  49. data/lib/kamal/commands/app/proxy.rb +32 -0
  50. data/lib/kamal/commands/app.rb +125 -0
  51. data/lib/kamal/commands/auditor.rb +39 -0
  52. data/lib/kamal/commands/base.rb +147 -0
  53. data/lib/kamal/commands/builder/base.rb +143 -0
  54. data/lib/kamal/commands/builder/clone.rb +32 -0
  55. data/lib/kamal/commands/builder/cloud.rb +22 -0
  56. data/lib/kamal/commands/builder/hybrid.rb +21 -0
  57. data/lib/kamal/commands/builder/local.rb +20 -0
  58. data/lib/kamal/commands/builder/pack.rb +46 -0
  59. data/lib/kamal/commands/builder/remote.rb +75 -0
  60. data/lib/kamal/commands/builder.rb +54 -0
  61. data/lib/kamal/commands/docker.rb +50 -0
  62. data/lib/kamal/commands/hook.rb +20 -0
  63. data/lib/kamal/commands/loadbalancer.rb +130 -0
  64. data/lib/kamal/commands/lock.rb +70 -0
  65. data/lib/kamal/commands/proxy.rb +150 -0
  66. data/lib/kamal/commands/prune.rb +38 -0
  67. data/lib/kamal/commands/registry.rb +38 -0
  68. data/lib/kamal/commands/server.rb +15 -0
  69. data/lib/kamal/commands.rb +2 -0
  70. data/lib/kamal/configuration/accessory.rb +280 -0
  71. data/lib/kamal/configuration/alias.rb +15 -0
  72. data/lib/kamal/configuration/boot.rb +29 -0
  73. data/lib/kamal/configuration/builder.rb +218 -0
  74. data/lib/kamal/configuration/docs/accessory.yml +160 -0
  75. data/lib/kamal/configuration/docs/alias.yml +29 -0
  76. data/lib/kamal/configuration/docs/boot.yml +21 -0
  77. data/lib/kamal/configuration/docs/builder.yml +132 -0
  78. data/lib/kamal/configuration/docs/configuration.yml +228 -0
  79. data/lib/kamal/configuration/docs/env.yml +118 -0
  80. data/lib/kamal/configuration/docs/logging.yml +21 -0
  81. data/lib/kamal/configuration/docs/output.yml +25 -0
  82. data/lib/kamal/configuration/docs/proxy.yml +207 -0
  83. data/lib/kamal/configuration/docs/registry.yml +64 -0
  84. data/lib/kamal/configuration/docs/role.yml +54 -0
  85. data/lib/kamal/configuration/docs/servers.yml +27 -0
  86. data/lib/kamal/configuration/docs/ssh.yml +81 -0
  87. data/lib/kamal/configuration/docs/sshkit.yml +31 -0
  88. data/lib/kamal/configuration/env/tag.rb +13 -0
  89. data/lib/kamal/configuration/env.rb +42 -0
  90. data/lib/kamal/configuration/loadbalancer.rb +34 -0
  91. data/lib/kamal/configuration/logging.rb +33 -0
  92. data/lib/kamal/configuration/output.rb +34 -0
  93. data/lib/kamal/configuration/proxy/boot.rb +124 -0
  94. data/lib/kamal/configuration/proxy/run.rb +152 -0
  95. data/lib/kamal/configuration/proxy.rb +156 -0
  96. data/lib/kamal/configuration/registry.rb +40 -0
  97. data/lib/kamal/configuration/role.rb +247 -0
  98. data/lib/kamal/configuration/servers.rb +25 -0
  99. data/lib/kamal/configuration/ssh.rb +76 -0
  100. data/lib/kamal/configuration/sshkit.rb +26 -0
  101. data/lib/kamal/configuration/validation.rb +27 -0
  102. data/lib/kamal/configuration/validator/accessory.rb +13 -0
  103. data/lib/kamal/configuration/validator/alias.rb +15 -0
  104. data/lib/kamal/configuration/validator/builder.rb +15 -0
  105. data/lib/kamal/configuration/validator/configuration.rb +6 -0
  106. data/lib/kamal/configuration/validator/env.rb +54 -0
  107. data/lib/kamal/configuration/validator/proxy.rb +47 -0
  108. data/lib/kamal/configuration/validator/registry.rb +27 -0
  109. data/lib/kamal/configuration/validator/role.rb +13 -0
  110. data/lib/kamal/configuration/validator/servers.rb +7 -0
  111. data/lib/kamal/configuration/validator.rb +251 -0
  112. data/lib/kamal/configuration/volume.rb +29 -0
  113. data/lib/kamal/configuration.rb +465 -0
  114. data/lib/kamal/docker.rb +30 -0
  115. data/lib/kamal/env_file.rb +44 -0
  116. data/lib/kamal/git.rb +37 -0
  117. data/lib/kamal/otel_shipper.rb +176 -0
  118. data/lib/kamal/output/base_logger.rb +29 -0
  119. data/lib/kamal/output/file_logger.rb +51 -0
  120. data/lib/kamal/output/formatter.rb +36 -0
  121. data/lib/kamal/output/otel_logger.rb +70 -0
  122. data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +59 -0
  123. data/lib/kamal/secrets/adapters/base.rb +33 -0
  124. data/lib/kamal/secrets/adapters/bitwarden.rb +81 -0
  125. data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
  126. data/lib/kamal/secrets/adapters/doppler.rb +57 -0
  127. data/lib/kamal/secrets/adapters/enpass.rb +71 -0
  128. data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
  129. data/lib/kamal/secrets/adapters/last_pass.rb +40 -0
  130. data/lib/kamal/secrets/adapters/one_password.rb +104 -0
  131. data/lib/kamal/secrets/adapters/passbolt.rb +129 -0
  132. data/lib/kamal/secrets/adapters/test.rb +16 -0
  133. data/lib/kamal/secrets/adapters.rb +16 -0
  134. data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +47 -0
  135. data/lib/kamal/secrets.rb +53 -0
  136. data/lib/kamal/sshkit_with_ext.rb +273 -0
  137. data/lib/kamal/tags.rb +40 -0
  138. data/lib/kamal/utils/sensitive.rb +20 -0
  139. data/lib/kamal/utils.rb +110 -0
  140. data/lib/kamal/version.rb +3 -0
  141. data/lib/kamal.rb +15 -0
  142. metadata +388 -0
@@ -0,0 +1,54 @@
1
+ # Roles
2
+ #
3
+ # Roles are used to configure different types of servers in the deployment.
4
+ # The most common use for this is to run web servers and job servers.
5
+ #
6
+ # Kamal expects there to be a `web` role, unless you set a different `primary_role`
7
+ # in the root configuration.
8
+
9
+ # Role configuration
10
+ #
11
+ # Roles are specified under the servers key:
12
+ servers:
13
+
14
+ # Simple role configuration
15
+ #
16
+ # This can be a list of hosts if you don't need custom configuration for the role.
17
+ #
18
+ # You can set tags on the hosts for custom env variables (see kamal docs env):
19
+ web:
20
+ - 172.1.0.1
21
+ - 172.1.0.2: experiment1
22
+ - 172.1.0.2: [ experiment1, experiment2 ]
23
+
24
+ # Custom role configuration
25
+ #
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.
32
+ #
33
+ # For the primary role, you can set `proxy: false` to disable the proxy.
34
+ #
35
+ # You can also set a custom `cmd` to run in the container and overwrite other settings
36
+ # from the root configuration.
37
+ workers:
38
+ hosts:
39
+ - 172.1.0.3
40
+ - 172.1.0.4: experiment1
41
+ cmd: "bin/jobs"
42
+ stop_timeout: 30
43
+ options:
44
+ memory: 2g
45
+ cpus: 4
46
+ logging:
47
+ ...
48
+ proxy:
49
+ ...
50
+ labels:
51
+ my-label: workers
52
+ env:
53
+ ...
54
+ asset_path: /public
@@ -0,0 +1,27 @@
1
+ # Servers
2
+ #
3
+ # Servers are split into different roles, with each role having its own configuration.
4
+ #
5
+ # For simpler deployments, though, where all servers are identical, you can just specify a list of servers.
6
+ # They will be implicitly assigned to the `web` role.
7
+ servers:
8
+ - 172.0.0.1
9
+ - 172.0.0.2
10
+ - 172.0.0.3
11
+
12
+ # Tagging servers
13
+ #
14
+ # Servers can be tagged, with the tags used to add custom env variables (see kamal docs env).
15
+ servers:
16
+ - 172.0.0.1
17
+ - 172.0.0.2: experiments
18
+ - 172.0.0.3: [ experiments, three ]
19
+
20
+ # Roles
21
+ #
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
+ servers:
24
+ web:
25
+ ...
26
+ workers:
27
+ ...
@@ -0,0 +1,81 @@
1
+ # SSH configuration
2
+ #
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
+ #
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
+ #
8
+ # ```shell
9
+ # sudo apt update
10
+ # sudo apt upgrade -y
11
+ # sudo apt install -y docker.io curl git
12
+ # sudo usermod -a -G docker app
13
+ # ```
14
+
15
+ # SSH options
16
+ #
17
+ # The options are specified under the ssh key in the configuration file.
18
+ ssh:
19
+
20
+ # The SSH user
21
+ #
22
+ # Defaults to `root`:
23
+ user: app
24
+
25
+ # The SSH port
26
+ #
27
+ # Defaults to 22:
28
+ port: "2222"
29
+
30
+ # Proxy host
31
+ #
32
+ # Specified in the form <host> or <user>@<host>:
33
+ proxy: root@proxy-host
34
+
35
+ # Proxy command
36
+ #
37
+ # A custom proxy command, required for older versions of SSH:
38
+ proxy_command: "ssh -W %h:%p user@proxy"
39
+
40
+ # Log level
41
+ #
42
+ # Defaults to `fatal`. Set this to `debug` if you are having SSH connection issues.
43
+ log_level: debug
44
+
45
+ # Keys only
46
+ #
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.
51
+ keys_only: false
52
+
53
+ # Keys
54
+ #
55
+ # An array of file names of private keys to use for public key
56
+ # and host-based authentication:
57
+ keys: [ "~/.ssh/id.pem" ]
58
+
59
+ # Key data
60
+ #
61
+ # An array of strings, with each element of the array being a secret name.
62
+ key_data:
63
+ - SSH_PRIVATE_KEY
64
+ # You can also provide raw private key in PEM format, but this is deprecated.
65
+ key_data:
66
+ - "-----BEGIN OPENSSH PRIVATE KEY----- ..."
67
+
68
+ # Config
69
+ #
70
+ # Set to true to load the default OpenSSH config files (~/.ssh/config,
71
+ # /etc/ssh_config), to false ignore config files, or to a file path
72
+ # (or array of paths) to load specific configuration. Defaults to true.
73
+ config: [ "~/.ssh/myconfig" ]
74
+
75
+ # Forward agent
76
+ #
77
+ # Whether to forward the local SSH agent to the remote host. Defaults to
78
+ # true (sshkit's default). Set to false when connecting through a jump
79
+ # host or tunnel that does not support agent forwarding (for example,
80
+ # Cloudflare Access for Infrastructure with SSH).
81
+ forward_agent: false
@@ -0,0 +1,31 @@
1
+ # SSHKit
2
+ #
3
+ # [SSHKit](https://github.com/capistrano/sshkit) is the SSH toolkit used by Kamal.
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.
7
+
8
+ # SSHKit options
9
+ #
10
+ # The options are specified under the sshkit key in the configuration file.
11
+ sshkit:
12
+
13
+ # Max concurrent starts
14
+ #
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.
17
+ max_concurrent_starts: 10
18
+
19
+ # Pool idle timeout
20
+ #
21
+ # Kamal sets a long idle timeout of 900 seconds on connections to try to avoid
22
+ # re-connection storms after an idle period, such as building an image or waiting for CI.
23
+ pool_idle_timeout: 300
24
+
25
+ # DNS retry settings
26
+ #
27
+ # Some resolvers (mDNSResponder, systemd-resolved, Tailscale) can drop lookups during
28
+ # bursts of concurrent SSH starts. Kamal will retry DNS failures automatically.
29
+ #
30
+ # Number of retries after the initial attempt. Set to 0 to disable.
31
+ dns_retries: 3
@@ -0,0 +1,13 @@
1
+ class Kamal::Configuration::Env::Tag
2
+ attr_reader :name, :config, :secrets
3
+
4
+ def initialize(name, config:, secrets:)
5
+ @name = name
6
+ @config = config
7
+ @secrets = secrets
8
+ end
9
+
10
+ def env
11
+ Kamal::Configuration::Env.new(config: config, secrets: secrets)
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ class Kamal::Configuration::Env
2
+ include Kamal::Configuration::Validation
3
+
4
+ attr_reader :context, :clear, :secrets, :secret_keys
5
+ delegate :argumentize, to: Kamal::Utils
6
+
7
+ def initialize(config:, secrets:, context: "env")
8
+ @clear = config.fetch("clear", config.key?("secret") || config.key?("tags") ? {} : config)
9
+ @secrets = secrets
10
+ @secret_keys = config.fetch("secret", [])
11
+ @context = context
12
+ validate! config, context: context, with: Kamal::Configuration::Validator::Env
13
+ end
14
+
15
+ def clear_args
16
+ argumentize("--env", clear)
17
+ end
18
+
19
+ def secrets_io
20
+ Kamal::EnvFile.new(aliased_secrets).to_io
21
+ end
22
+
23
+ def merge(other)
24
+ self.class.new \
25
+ config: { "clear" => clear.merge(other.clear), "secret" => secret_keys | other.secret_keys },
26
+ secrets: secrets
27
+ end
28
+
29
+ def to_h
30
+ clear.merge(aliased_secrets)
31
+ end
32
+
33
+ private
34
+ def aliased_secrets
35
+ secret_keys.to_h { |key| extract_alias(key) }.transform_values { |secret_key| secrets[secret_key] }
36
+ end
37
+
38
+ def extract_alias(key)
39
+ key_name, key_aliased_to = key.split(":", 2)
40
+ [ key_name, key_aliased_to || key_name ]
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ class Kamal::Configuration::Loadbalancer < Kamal::Configuration::Proxy
2
+ CONTAINER_NAME = "load-balancer".freeze
3
+ SHARED_CONTAINER_NAME = "kamal-proxy".freeze
4
+
5
+ def self.validation_config_key
6
+ "proxy"
7
+ end
8
+
9
+ def initialize(config:, proxy_config:, secrets:)
10
+ super
11
+ end
12
+
13
+ def deploy_options
14
+ opts = super
15
+
16
+ opts[:host] = hosts if hosts.present?
17
+ opts[:tls] = proxy_config["ssl"].presence
18
+
19
+ opts
20
+ end
21
+
22
+ def directory
23
+ File.join config.run_directory, "loadbalancer"
24
+ end
25
+
26
+ def container_name
27
+ on_proxy_host? ? SHARED_CONTAINER_NAME : CONTAINER_NAME
28
+ end
29
+
30
+ # When loadbalancer is on a proxy host, it takes over the proxy role
31
+ def on_proxy_host?
32
+ config.proxy_hosts.include?(config.proxy.effective_loadbalancer)
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ class Kamal::Configuration::Logging
2
+ delegate :optionize, :argumentize, to: Kamal::Utils
3
+
4
+ include Kamal::Configuration::Validation
5
+
6
+ attr_reader :logging_config
7
+
8
+ def initialize(logging_config:, context: "logging")
9
+ @logging_config = logging_config || {}
10
+ validate! @logging_config, context: context
11
+ end
12
+
13
+ def driver
14
+ logging_config["driver"]
15
+ end
16
+
17
+ def options
18
+ logging_config.fetch("options", {})
19
+ end
20
+
21
+ def merge(other)
22
+ self.class.new logging_config: logging_config.deep_merge(other.logging_config)
23
+ end
24
+
25
+ def args
26
+ if driver.present? || options.present?
27
+ optionize({ "log-driver" => driver }.compact) +
28
+ argumentize("--log-opt", options)
29
+ else
30
+ argumentize("--log-opt", { "max-size" => "10m" })
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ class Kamal::Configuration::Output
2
+ include Kamal::Configuration::Validation
3
+
4
+ LOGGER_TYPES = {
5
+ "otel" => "Kamal::Output::OtelLogger",
6
+ "file" => "Kamal::Output::FileLogger"
7
+ }
8
+
9
+ attr_reader :output_config, :loggers
10
+
11
+ def initialize(config:)
12
+ @config = config
13
+ @output_config = config.raw_config.output || {}
14
+ validate! @output_config unless @output_config.empty?
15
+ @loggers = build_loggers
16
+ end
17
+
18
+ def enabled?
19
+ output_config.present?
20
+ end
21
+
22
+ def to_h
23
+ output_config
24
+ end
25
+
26
+ private
27
+ def build_loggers
28
+ output_config.filter_map do |key, settings|
29
+ if (klass_name = LOGGER_TYPES[key])
30
+ klass_name.constantize.build(settings: settings || {}, config: @config)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,124 @@
1
+ class Kamal::Configuration::Proxy::Boot
2
+ attr_reader :config
3
+ delegate :argumentize, :optionize, to: Kamal::Utils
4
+
5
+ def initialize(config:)
6
+ @config = config
7
+ end
8
+
9
+ def publish_args(http_port, https_port, bind_ips = nil)
10
+ ensure_valid_bind_ips(bind_ips)
11
+
12
+ (bind_ips || [ nil ]).map do |bind_ip|
13
+ bind_ip = format_bind_ip(bind_ip)
14
+ publish_http = [ bind_ip, http_port, Kamal::Configuration::Proxy::Run::DEFAULT_HTTP_PORT ].compact.join(":")
15
+ publish_https = [ bind_ip, https_port, Kamal::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT ].compact.join(":")
16
+
17
+ argumentize "--publish", [ publish_http, publish_https ]
18
+ end.join(" ")
19
+ end
20
+
21
+ def logging_args(max_size)
22
+ argumentize "--log-opt", "max-size=#{max_size}" if max_size.present?
23
+ end
24
+
25
+ def default_boot_options
26
+ [
27
+ *(publish_args(Kamal::Configuration::Proxy::Run::DEFAULT_HTTP_PORT, Kamal::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT, nil)),
28
+ *(logging_args(Kamal::Configuration::Proxy::Run::DEFAULT_LOG_MAX_SIZE))
29
+ ]
30
+ end
31
+
32
+ def repository_name
33
+ "ghcr.io/mhenrixon"
34
+ end
35
+
36
+ def image_name
37
+ "kamal-proxy"
38
+ end
39
+
40
+ def image_default
41
+ "#{repository_name}/#{image_name}"
42
+ end
43
+
44
+ def container_name
45
+ "kamal-proxy"
46
+ end
47
+
48
+ def host_directory
49
+ File.join config.run_directory, "proxy"
50
+ end
51
+
52
+ def options_file
53
+ File.join host_directory, "options"
54
+ end
55
+
56
+ def image_file
57
+ File.join host_directory, "image"
58
+ end
59
+
60
+ def image_version_file
61
+ File.join host_directory, "image_version"
62
+ end
63
+
64
+ def run_command_file
65
+ File.join host_directory, "run_command"
66
+ end
67
+
68
+ def apps_directory
69
+ File.join host_directory, "apps-config"
70
+ end
71
+
72
+ def apps_container_directory
73
+ "/home/kamal-proxy/.apps-config"
74
+ end
75
+
76
+ def apps_volume
77
+ Kamal::Configuration::Volume.new \
78
+ host_path: apps_directory,
79
+ container_path: apps_container_directory
80
+ end
81
+
82
+ def app_directory
83
+ File.join apps_directory, config.service_and_destination
84
+ end
85
+
86
+ def app_container_directory
87
+ File.join apps_container_directory, config.service_and_destination
88
+ end
89
+
90
+ def error_pages_directory
91
+ File.join app_directory, "error_pages"
92
+ end
93
+
94
+ def error_pages_container_directory
95
+ File.join app_container_directory, "error_pages"
96
+ end
97
+
98
+ def tls_directory
99
+ File.join app_directory, "tls"
100
+ end
101
+
102
+ def tls_container_directory
103
+ File.join app_container_directory, "tls"
104
+ end
105
+
106
+ private
107
+ def ensure_valid_bind_ips(bind_ips)
108
+ bind_ips.present? && bind_ips.each do |ip|
109
+ next if ip =~ Resolv::IPv4::Regex || ip =~ Resolv::IPv6::Regex
110
+ raise ArgumentError, "Invalid publish IP address: #{ip}"
111
+ end
112
+
113
+ true
114
+ end
115
+
116
+ def format_bind_ip(ip)
117
+ # Ensure IPv6 address inside square brackets - e.g. [::1]
118
+ if ip =~ Resolv::IPv6::Regex && ip !~ /\A\[.*\]\z/
119
+ "[#{ip}]"
120
+ else
121
+ ip
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,152 @@
1
+ class Kamal::Configuration::Proxy::Run
2
+ MINIMUM_VERSION = "v0.9.2.1"
3
+ DEFAULT_HTTP_PORT = 80
4
+ DEFAULT_HTTPS_PORT = 443
5
+ DEFAULT_LOG_MAX_SIZE = "10m"
6
+
7
+ attr_reader :config, :run_config
8
+ delegate :argumentize, :optionize, to: Kamal::Utils
9
+
10
+ def initialize(config, run_config:, context: "proxy/run")
11
+ @config = config
12
+ @run_config = run_config
13
+ @context = context
14
+ end
15
+
16
+ def debug?
17
+ run_config.fetch("debug", nil)
18
+ end
19
+
20
+ def publish?
21
+ run_config.fetch("publish", true)
22
+ end
23
+
24
+ def http_port
25
+ run_config.fetch("http_port", DEFAULT_HTTP_PORT)
26
+ end
27
+
28
+ def https_port
29
+ run_config.fetch("https_port", DEFAULT_HTTPS_PORT)
30
+ end
31
+
32
+ def bind_ips
33
+ run_config.fetch("bind_ips", nil)
34
+ end
35
+
36
+ def publish_args
37
+ if publish?
38
+ (bind_ips || [ nil ]).map do |bind_ip|
39
+ bind_ip = format_bind_ip(bind_ip)
40
+ publish_http = [ bind_ip, http_port, DEFAULT_HTTP_PORT ].compact.join(":")
41
+ publish_https = [ bind_ip, https_port, DEFAULT_HTTPS_PORT ].compact.join(":")
42
+
43
+ argumentize "--publish", [ publish_http, publish_https ]
44
+ end.join(" ")
45
+ end
46
+ end
47
+
48
+ def log_max_size
49
+ run_config.fetch("log_max_size", DEFAULT_LOG_MAX_SIZE)
50
+ end
51
+
52
+ def logging_args
53
+ argumentize "--log-opt", "max-size=#{log_max_size}" if log_max_size.present?
54
+ end
55
+
56
+ def version
57
+ run_config.fetch("version", MINIMUM_VERSION)
58
+ end
59
+
60
+ def registry
61
+ run_config.fetch("registry", nil)
62
+ end
63
+
64
+ def repository
65
+ run_config.fetch("repository", "ghcr.io/mhenrixon/kamal-proxy")
66
+ end
67
+
68
+ def image
69
+ "#{[ registry, repository ].compact.join("/")}:#{version}"
70
+ end
71
+
72
+ def container_name
73
+ "kamal-proxy"
74
+ end
75
+
76
+ def options_args
77
+ if args = run_config["options"]
78
+ optionize args
79
+ end
80
+ end
81
+
82
+ def run_command
83
+ [ "kamal-proxy", "run", *optionize(run_command_options) ].join(" ")
84
+ end
85
+
86
+ def metrics_port
87
+ run_config["metrics_port"]
88
+ end
89
+
90
+ def run_command_options
91
+ { debug: debug? || nil, "metrics-port": metrics_port }.compact
92
+ end
93
+
94
+ def docker_options_args
95
+ [
96
+ *apps_volume_args,
97
+ *publish_args,
98
+ *logging_args,
99
+ *("--expose=#{metrics_port}" if metrics_port.present?),
100
+ *options_args
101
+ ].compact
102
+ end
103
+
104
+ def host_directory
105
+ File.join config.run_directory, "proxy"
106
+ end
107
+
108
+ def apps_directory
109
+ File.join host_directory, "apps-config"
110
+ end
111
+
112
+ def apps_container_directory
113
+ "/home/kamal-proxy/.apps-config"
114
+ end
115
+
116
+ def apps_volume
117
+ Kamal::Configuration::Volume.new \
118
+ host_path: apps_directory,
119
+ container_path: apps_container_directory
120
+ end
121
+
122
+ def apps_volume_args
123
+ [ apps_volume.docker_args ]
124
+ end
125
+
126
+ def app_directory
127
+ File.join apps_directory, config.service_and_destination
128
+ end
129
+
130
+ def app_container_directory
131
+ File.join apps_container_directory, config.service_and_destination
132
+ end
133
+
134
+ def ==(other)
135
+ other.is_a?(self.class) && run_config == other.run_config
136
+ end
137
+ alias_method :eql?, :==
138
+
139
+ def hash
140
+ run_config.hash
141
+ end
142
+
143
+ private
144
+ def format_bind_ip(ip)
145
+ # Ensure IPv6 address inside square brackets - e.g. [::1]
146
+ if ip =~ Resolv::IPv6::Regex && ip !~ /\A\[.*\]\z/
147
+ "[#{ip}]"
148
+ else
149
+ ip
150
+ end
151
+ end
152
+ end