kamal 1.8.3 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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 -65
  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,65 +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
-
62
- def platform_names
63
- "linux/#{local_arch},linux/#{remote_arch}"
64
- end
65
- 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