kamal 1.8.2 → 2.0.0.beta1

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/kamal/cli/accessory.rb +44 -20
  4. data/lib/kamal/cli/alias/command.rb +9 -0
  5. data/lib/kamal/cli/app/boot.rb +22 -16
  6. data/lib/kamal/cli/app.rb +40 -5
  7. data/lib/kamal/cli/base.rb +19 -51
  8. data/lib/kamal/cli/build.rb +12 -13
  9. data/lib/kamal/cli/healthcheck/barrier.rb +2 -0
  10. data/lib/kamal/cli/healthcheck/poller.rb +18 -39
  11. data/lib/kamal/cli/lock.rb +2 -3
  12. data/lib/kamal/cli/main.rb +54 -51
  13. data/lib/kamal/cli/proxy.rb +224 -0
  14. data/lib/kamal/cli/prune.rb +0 -1
  15. data/lib/kamal/cli/secrets.rb +36 -0
  16. data/lib/kamal/cli/server.rb +2 -3
  17. data/lib/kamal/cli/templates/deploy.yml +4 -21
  18. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  19. data/lib/kamal/cli/templates/secrets +16 -0
  20. data/lib/kamal/cli.rb +1 -0
  21. data/lib/kamal/commander/specifics.rb +3 -3
  22. data/lib/kamal/commander.rb +24 -8
  23. data/lib/kamal/commands/accessory.rb +7 -7
  24. data/lib/kamal/commands/app/assets.rb +8 -8
  25. data/lib/kamal/commands/app/proxy.rb +16 -0
  26. data/lib/kamal/commands/app.rb +7 -15
  27. data/lib/kamal/commands/auditor.rb +6 -3
  28. data/lib/kamal/commands/base.rb +8 -0
  29. data/lib/kamal/commands/builder/base.rb +26 -13
  30. data/lib/kamal/commands/builder/hybrid.rb +21 -0
  31. data/lib/kamal/commands/builder/local.rb +14 -0
  32. data/lib/kamal/commands/builder/remote.rb +63 -0
  33. data/lib/kamal/commands/builder.rb +13 -29
  34. data/lib/kamal/commands/docker.rb +4 -0
  35. data/lib/kamal/commands/hook.rb +5 -2
  36. data/lib/kamal/commands/lock.rb +2 -6
  37. data/lib/kamal/commands/proxy.rb +77 -0
  38. data/lib/kamal/commands/prune.rb +1 -9
  39. data/lib/kamal/commands/server.rb +11 -1
  40. data/lib/kamal/configuration/accessory.rb +14 -2
  41. data/lib/kamal/configuration/alias.rb +15 -0
  42. data/lib/kamal/configuration/builder.rb +52 -18
  43. data/lib/kamal/configuration/docs/alias.yml +26 -0
  44. data/lib/kamal/configuration/docs/builder.yml +26 -23
  45. data/lib/kamal/configuration/docs/configuration.yml +22 -16
  46. data/lib/kamal/configuration/docs/env.yml +10 -11
  47. data/lib/kamal/configuration/docs/proxy.yml +100 -0
  48. data/lib/kamal/configuration/docs/registry.yml +4 -2
  49. data/lib/kamal/configuration/docs/role.yml +3 -5
  50. data/lib/kamal/configuration/env/tag.rb +4 -3
  51. data/lib/kamal/configuration/env.rb +10 -17
  52. data/lib/kamal/configuration/proxy.rb +66 -0
  53. data/lib/kamal/configuration/registry.rb +3 -2
  54. data/lib/kamal/configuration/role.rb +63 -94
  55. data/lib/kamal/configuration/validator/alias.rb +15 -0
  56. data/lib/kamal/configuration/validator/builder.rb +4 -0
  57. data/lib/kamal/configuration/validator/proxy.rb +11 -0
  58. data/lib/kamal/configuration/validator.rb +42 -24
  59. data/lib/kamal/configuration.rb +91 -33
  60. data/lib/kamal/env_file.rb +4 -0
  61. data/lib/kamal/secrets/adapters/base.rb +18 -0
  62. data/lib/kamal/secrets/adapters/bitwarden.rb +64 -0
  63. data/lib/kamal/secrets/adapters/last_pass.rb +30 -0
  64. data/lib/kamal/secrets/adapters/one_password.rb +61 -0
  65. data/lib/kamal/secrets/adapters/test.rb +10 -0
  66. data/lib/kamal/secrets/adapters.rb +14 -0
  67. data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +32 -0
  68. data/lib/kamal/secrets.rb +37 -0
  69. data/lib/kamal/sshkit_with_ext.rb +1 -0
  70. data/lib/kamal/utils.rb +28 -0
  71. data/lib/kamal/version.rb +1 -1
  72. data/lib/kamal.rb +3 -1
  73. metadata +32 -23
  74. data/lib/kamal/cli/env.rb +0 -54
  75. data/lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample +0 -3
  76. data/lib/kamal/cli/templates/template.env +0 -2
  77. data/lib/kamal/cli/traefik.rb +0 -122
  78. data/lib/kamal/commands/app/cord.rb +0 -22
  79. data/lib/kamal/commands/builder/multiarch/remote.rb +0 -61
  80. data/lib/kamal/commands/builder/multiarch.rb +0 -41
  81. data/lib/kamal/commands/builder/native/cached.rb +0 -25
  82. data/lib/kamal/commands/builder/native/remote.rb +0 -67
  83. data/lib/kamal/commands/builder/native.rb +0 -20
  84. data/lib/kamal/commands/traefik.rb +0 -85
  85. data/lib/kamal/configuration/docs/healthcheck.yml +0 -59
  86. data/lib/kamal/configuration/docs/traefik.yml +0 -62
  87. data/lib/kamal/configuration/healthcheck.rb +0 -63
  88. data/lib/kamal/configuration/traefik.rb +0 -60
  89. /data/lib/kamal/cli/templates/sample_hooks/{pre-traefik-reboot.sample → pre-proxy-reboot.sample} +0 -0
@@ -1,61 +0,0 @@
1
- class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Multiarch
2
- def create
3
- combine \
4
- create_contexts,
5
- create_local_buildx,
6
- append_remote_buildx
7
- end
8
-
9
- def remove
10
- combine \
11
- remove_contexts,
12
- super
13
- end
14
-
15
- def context_hosts
16
- chain \
17
- context_host(builder_name_with_arch(local_arch)),
18
- context_host(builder_name_with_arch(remote_arch))
19
- end
20
-
21
- def config_context_hosts
22
- [ local_host, remote_host ].compact
23
- end
24
-
25
- private
26
- def builder_name
27
- super + "-remote"
28
- end
29
-
30
- def builder_name_with_arch(arch)
31
- "#{builder_name}-#{arch}"
32
- end
33
-
34
- def create_local_buildx
35
- docker :buildx, :create, "--name", builder_name, builder_name_with_arch(local_arch), "--platform", "linux/#{local_arch}"
36
- end
37
-
38
- def append_remote_buildx
39
- docker :buildx, :create, "--append", "--name", builder_name, builder_name_with_arch(remote_arch), "--platform", "linux/#{remote_arch}"
40
- end
41
-
42
- def create_contexts
43
- combine \
44
- create_context(local_arch, local_host),
45
- create_context(remote_arch, remote_host)
46
- end
47
-
48
- def create_context(arch, host)
49
- docker :context, :create, builder_name_with_arch(arch), "--description", "'#{builder_name} #{arch} native host'", "--docker", "'host=#{host}'"
50
- end
51
-
52
- def remove_contexts
53
- combine \
54
- remove_context(local_arch),
55
- remove_context(remote_arch)
56
- end
57
-
58
- def remove_context(arch)
59
- docker :context, :rm, builder_name_with_arch(arch)
60
- end
61
- end
@@ -1,41 +0,0 @@
1
- class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
2
- def create
3
- docker :buildx, :create, "--use", "--name", builder_name
4
- end
5
-
6
- def remove
7
- docker :buildx, :rm, builder_name
8
- end
9
-
10
- def info
11
- combine \
12
- docker(:context, :ls),
13
- docker(:buildx, :ls)
14
- end
15
-
16
- def push
17
- docker :buildx, :build,
18
- "--push",
19
- "--platform", platform_names,
20
- "--builder", builder_name,
21
- *build_options,
22
- build_context
23
- end
24
-
25
- def context_hosts
26
- docker :buildx, :inspect, builder_name, "> /dev/null"
27
- end
28
-
29
- private
30
- def builder_name
31
- "kamal-#{config.service}-multiarch"
32
- end
33
-
34
- def platform_names
35
- if local_arch
36
- "linux/#{local_arch}"
37
- else
38
- "linux/amd64,linux/arm64"
39
- end
40
- end
41
- end
@@ -1,25 +0,0 @@
1
- class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Native
2
- def create
3
- docker :buildx, :create, "--name", builder_name, "--use", "--driver=docker-container"
4
- end
5
-
6
- def remove
7
- docker :buildx, :rm, builder_name
8
- end
9
-
10
- def push
11
- docker :buildx, :build,
12
- "--push",
13
- *build_options,
14
- build_context
15
- end
16
-
17
- def context_hosts
18
- docker :buildx, :inspect, builder_name, "> /dev/null"
19
- end
20
-
21
- private
22
- def builder_name
23
- "kamal-#{config.service}-native-cached"
24
- end
25
- end
@@ -1,67 +0,0 @@
1
- class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Native
2
- def create
3
- chain \
4
- create_context,
5
- create_buildx
6
- end
7
-
8
- def remove
9
- chain \
10
- remove_context,
11
- remove_buildx
12
- end
13
-
14
- def info
15
- chain \
16
- docker(:context, :ls),
17
- docker(:buildx, :ls)
18
- end
19
-
20
- def push
21
- docker :buildx, :build,
22
- "--push",
23
- "--platform", platform,
24
- "--builder", builder_name,
25
- *build_options,
26
- build_context
27
- end
28
-
29
- def context_hosts
30
- context_host(builder_name_with_arch)
31
- end
32
-
33
- def config_context_hosts
34
- [ remote_host ]
35
- end
36
-
37
-
38
- private
39
- def builder_name
40
- "kamal-#{config.service}-native-remote"
41
- end
42
-
43
- def builder_name_with_arch
44
- "#{builder_name}-#{remote_arch}"
45
- end
46
-
47
- def platform
48
- "linux/#{remote_arch}"
49
- end
50
-
51
- def create_context
52
- docker :context, :create,
53
- builder_name_with_arch, "--description", "'#{builder_name} #{remote_arch} native host'", "--docker", "'host=#{remote_host}'"
54
- end
55
-
56
- def remove_context
57
- docker :context, :rm, builder_name_with_arch
58
- end
59
-
60
- def create_buildx
61
- docker :buildx, :create, "--name", builder_name, builder_name_with_arch, "--platform", platform
62
- end
63
-
64
- def remove_buildx
65
- docker :buildx, :rm, builder_name
66
- end
67
- end
@@ -1,20 +0,0 @@
1
- class Kamal::Commands::Builder::Native < Kamal::Commands::Builder::Base
2
- def create
3
- # No-op on native without cache
4
- end
5
-
6
- def remove
7
- # No-op on native without cache
8
- end
9
-
10
- def info
11
- # No-op on native
12
- end
13
-
14
- def push
15
- combine \
16
- docker(:build, *build_options, build_context),
17
- docker(:push, config.absolute_image),
18
- docker(:push, config.latest_image)
19
- end
20
- end
@@ -1,85 +0,0 @@
1
- class Kamal::Commands::Traefik < Kamal::Commands::Base
2
- delegate :argumentize, :optionize, to: Kamal::Utils
3
- delegate :port, :publish?, :labels, :env, :image, :options, :args, to: :"config.traefik"
4
-
5
- def run
6
- docker :run, "--name traefik",
7
- "--detach",
8
- "--restart", "unless-stopped",
9
- *publish_args,
10
- "--volume", "/var/run/docker.sock:/var/run/docker.sock",
11
- *env_args,
12
- *config.logging_args,
13
- *label_args,
14
- *docker_options_args,
15
- image,
16
- "--providers.docker",
17
- *cmd_option_args
18
- end
19
-
20
- def start
21
- docker :container, :start, "traefik"
22
- end
23
-
24
- def stop
25
- docker :container, :stop, "traefik"
26
- end
27
-
28
- def start_or_run
29
- any start, run
30
- end
31
-
32
- def info
33
- docker :ps, "--filter", "name=^traefik$"
34
- end
35
-
36
- def logs(since: nil, lines: nil, grep: nil, grep_options: nil)
37
- pipe \
38
- docker(:logs, "traefik", (" --since #{since}" if since), (" --tail #{lines}" if lines), "--timestamps", "2>&1"),
39
- ("grep '#{grep}'#{" #{grep_options}" if grep_options}" if grep)
40
- end
41
-
42
- def follow_logs(host:, grep: nil, grep_options: nil)
43
- run_over_ssh pipe(
44
- docker(:logs, "traefik", "--timestamps", "--tail", "10", "--follow", "2>&1"),
45
- (%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
46
- ).join(" "), host: host
47
- end
48
-
49
- def remove_container
50
- docker :container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=Traefik"
51
- end
52
-
53
- def remove_image
54
- docker :image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=Traefik"
55
- end
56
-
57
- def make_env_directory
58
- make_directory(env.secrets_directory)
59
- end
60
-
61
- def remove_env_file
62
- [ :rm, "-f", env.secrets_file ]
63
- end
64
-
65
- private
66
- def publish_args
67
- argumentize "--publish", port if publish?
68
- end
69
-
70
- def label_args
71
- argumentize "--label", labels
72
- end
73
-
74
- def env_args
75
- env.args
76
- end
77
-
78
- def docker_options_args
79
- optionize(options)
80
- end
81
-
82
- def cmd_option_args
83
- optionize args, with: "="
84
- end
85
- end
@@ -1,59 +0,0 @@
1
- # Healthcheck configuration
2
- #
3
- # On roles that are running Traefik, Kamal will supply a default healthcheck to `docker run`.
4
- # For other roles, by default no healthcheck is supplied.
5
- #
6
- # If no healthcheck is supplied and the image does not define one, they we wait for the container
7
- # to reach a running state and then pause for the readiness delay.
8
- #
9
- # The default healthcheck is `curl -f http://localhost:<port>/<path>`, so it assumes that `curl`
10
- # is available within the container.
11
-
12
- # Healthcheck options
13
- #
14
- # These go under the `healthcheck` key in the root or role configuration.
15
- healthcheck:
16
-
17
- # Command
18
- #
19
- # The command to run, defaults to `curl -f http://localhost:<port>/<path>` on roles running Traefik
20
- cmd: "curl -f http://localhost"
21
-
22
- # Interval
23
- #
24
- # The Docker healthcheck interval, defaults to `1s`
25
- interval: 10s
26
-
27
- # Max attempts
28
- #
29
- # The maximum number of times we poll the container to see if it is healthy, defaults to `7`
30
- # Each check is separated by an increasing interval starting with 1 second.
31
- max_attempts: 3
32
-
33
- # Port
34
- #
35
- # The port to use in the healthcheck, defaults to `3000`
36
- port: "80"
37
-
38
- # Path
39
- #
40
- # The path to use in the healthcheck, defaults to `/up`
41
- path: /health
42
-
43
- # Cords for zero-downtime deployments
44
- #
45
- # The cord file is used for zero-downtime deployments. The healthcheck is augmented with a check
46
- # for the existance of the file. This allows us to delete the file and force the container to
47
- # become unhealthy, causing Traefik to stop routing traffic to it.
48
- #
49
- # Kamal mounts a volume at this location and creates the file before starting the container.
50
- # You can set the value to `false` to disable the cord file, but this loses the zero-downtime
51
- # guarantee.
52
- #
53
- # The default value is `/tmp/kamal-cord`
54
- cord: /cord
55
-
56
- # Log lines
57
- #
58
- # Number of lines to log from the container when the healthcheck fails, defaults to `50`
59
- log_lines: 100
@@ -1,62 +0,0 @@
1
- # Traefik
2
- #
3
- # Traefik is a reverse proxy, used by Kamal for zero-downtime deployments.
4
- #
5
- # We start an instance on the hosts in it's own container.
6
- #
7
- # During a deployment:
8
- # 1. We start a new container which Traefik automatically detects due to the labels we have applied
9
- # 2. Traefik starts routing traffic to the new container
10
- # 3. We force the old container to fail it's healthcheck, causing Traefik to stop routing traffic to it
11
- # 4. We stop the old container
12
-
13
- # Traefik settings
14
- #
15
- # Traekik is configured in the root configuration under `traefik`.
16
- traefik:
17
-
18
- # Image
19
- #
20
- # The Traefik image to use, defaults to `traefik:v2.10`
21
- image: traefik:v2.9
22
-
23
- # Host port
24
- #
25
- # The host port to publish the Traefik container on, defaults to `80`
26
- host_port: "8080"
27
-
28
- # Disabling publishing
29
- #
30
- # To avoid publishing the Traefik container, set this to `false`
31
- publish: false
32
-
33
- # Labels
34
- #
35
- # Additional labels to apply to the Traefik container
36
- labels:
37
- traefik.http.routers.catchall.entryPoints: http
38
- traefik.http.routers.catchall.rule: PathPrefix(`/`)
39
- traefik.http.routers.catchall.service: unavailable
40
- traefik.http.routers.catchall.priority: "1"
41
- traefik.http.services.unavailable.loadbalancer.server.port: "0"
42
-
43
- # Arguments
44
- #
45
- # Additional arguments to pass to the Traefik container
46
- args:
47
- entryPoints.http.address: ":80"
48
- entryPoints.http.forwardedHeaders.insecure: true
49
- accesslog: true
50
- accesslog.format: json
51
-
52
- # Options
53
- #
54
- # Additional options to pass to `docker run`
55
- options:
56
- cpus: 2
57
-
58
- # Environment variables
59
- #
60
- # See kamal docs env
61
- env:
62
- ...
@@ -1,63 +0,0 @@
1
- class Kamal::Configuration::Healthcheck
2
- include Kamal::Configuration::Validation
3
-
4
- attr_reader :healthcheck_config
5
-
6
- def initialize(healthcheck_config:, context: "healthcheck")
7
- @healthcheck_config = healthcheck_config || {}
8
- validate! @healthcheck_config, context: context
9
- end
10
-
11
- def merge(other)
12
- self.class.new healthcheck_config: healthcheck_config.deep_merge(other.healthcheck_config)
13
- end
14
-
15
- def cmd
16
- healthcheck_config.fetch("cmd", http_health_check)
17
- end
18
-
19
- def port
20
- healthcheck_config.fetch("port", 3000)
21
- end
22
-
23
- def path
24
- healthcheck_config.fetch("path", "/up")
25
- end
26
-
27
- def max_attempts
28
- healthcheck_config.fetch("max_attempts", 7)
29
- end
30
-
31
- def interval
32
- healthcheck_config.fetch("interval", "1s")
33
- end
34
-
35
- def cord
36
- healthcheck_config.fetch("cord", "/tmp/kamal-cord")
37
- end
38
-
39
- def log_lines
40
- healthcheck_config.fetch("log_lines", 50)
41
- end
42
-
43
- def set_port_or_path?
44
- healthcheck_config["port"].present? || healthcheck_config["path"].present?
45
- end
46
-
47
- def to_h
48
- {
49
- "cmd" => cmd,
50
- "interval" => interval,
51
- "max_attempts" => max_attempts,
52
- "port" => port,
53
- "path" => path,
54
- "cord" => cord,
55
- "log_lines" => log_lines
56
- }
57
- end
58
-
59
- private
60
- def http_health_check
61
- "curl -f #{URI.join("http://localhost:#{port}", path)} || exit 1" if path.present? || port.present?
62
- end
63
- end
@@ -1,60 +0,0 @@
1
- class Kamal::Configuration::Traefik
2
- DEFAULT_IMAGE = "traefik:v2.10"
3
- CONTAINER_PORT = 80
4
- DEFAULT_ARGS = {
5
- "log.level" => "DEBUG"
6
- }
7
- DEFAULT_LABELS = {
8
- # These ensure we serve a 502 rather than a 404 if no containers are available
9
- "traefik.http.routers.catchall.entryPoints" => "http",
10
- "traefik.http.routers.catchall.rule" => "PathPrefix(`/`)",
11
- "traefik.http.routers.catchall.service" => "unavailable",
12
- "traefik.http.routers.catchall.priority" => 1,
13
- "traefik.http.services.unavailable.loadbalancer.server.port" => "0"
14
- }
15
-
16
- include Kamal::Configuration::Validation
17
-
18
- attr_reader :config, :traefik_config
19
-
20
- def initialize(config:)
21
- @config = config
22
- @traefik_config = config.raw_config.traefik || {}
23
- validate! traefik_config
24
- end
25
-
26
- def publish?
27
- traefik_config["publish"] != false
28
- end
29
-
30
- def labels
31
- DEFAULT_LABELS.merge(traefik_config["labels"] || {})
32
- end
33
-
34
- def env
35
- Kamal::Configuration::Env.new \
36
- config: traefik_config.fetch("env", {}),
37
- secrets_file: File.join(config.host_env_directory, "traefik", "traefik.env"),
38
- context: "traefik/env"
39
- end
40
-
41
- def host_port
42
- traefik_config.fetch("host_port", CONTAINER_PORT)
43
- end
44
-
45
- def options
46
- traefik_config.fetch("options", {})
47
- end
48
-
49
- def port
50
- "#{host_port}:#{CONTAINER_PORT}"
51
- end
52
-
53
- def args
54
- DEFAULT_ARGS.merge(traefik_config.fetch("args", {}))
55
- end
56
-
57
- def image
58
- traefik_config.fetch("image", DEFAULT_IMAGE)
59
- end
60
- end