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.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/kamal/cli/accessory.rb +92 -38
  4. data/lib/kamal/cli/alias/command.rb +10 -0
  5. data/lib/kamal/cli/app/{prepare_assets.rb → assets.rb} +1 -1
  6. data/lib/kamal/cli/app/boot.rb +23 -16
  7. data/lib/kamal/cli/app/error_pages.rb +33 -0
  8. data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
  9. data/lib/kamal/cli/app.rb +132 -30
  10. data/lib/kamal/cli/base.rb +57 -53
  11. data/lib/kamal/cli/build.rb +81 -38
  12. data/lib/kamal/cli/healthcheck/barrier.rb +2 -0
  13. data/lib/kamal/cli/healthcheck/poller.rb +18 -39
  14. data/lib/kamal/cli/lock.rb +2 -3
  15. data/lib/kamal/cli/main.rb +60 -59
  16. data/lib/kamal/cli/proxy.rb +290 -0
  17. data/lib/kamal/cli/prune.rb +0 -1
  18. data/lib/kamal/cli/registry.rb +2 -0
  19. data/lib/kamal/cli/secrets.rb +49 -0
  20. data/lib/kamal/cli/server.rb +6 -5
  21. data/lib/kamal/cli/templates/deploy.yml +53 -53
  22. data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +2 -12
  23. data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
  24. data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +1 -1
  25. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  26. data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
  27. data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +1 -1
  28. data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +1 -1
  29. data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +19 -6
  30. data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
  31. data/lib/kamal/cli/templates/secrets +17 -0
  32. data/lib/kamal/cli.rb +2 -0
  33. data/lib/kamal/commander/specifics.rb +19 -6
  34. data/lib/kamal/commander.rb +39 -32
  35. data/lib/kamal/commands/accessory/proxy.rb +16 -0
  36. data/lib/kamal/commands/accessory.rb +19 -19
  37. data/lib/kamal/commands/app/assets.rb +10 -10
  38. data/lib/kamal/commands/app/containers.rb +2 -2
  39. data/lib/kamal/commands/app/error_pages.rb +9 -0
  40. data/lib/kamal/commands/app/execution.rb +7 -4
  41. data/lib/kamal/commands/app/images.rb +1 -1
  42. data/lib/kamal/commands/app/logging.rb +16 -6
  43. data/lib/kamal/commands/app/proxy.rb +32 -0
  44. data/lib/kamal/commands/app.rb +25 -24
  45. data/lib/kamal/commands/auditor.rb +12 -3
  46. data/lib/kamal/commands/base.rb +54 -8
  47. data/lib/kamal/commands/builder/base.rb +46 -16
  48. data/lib/kamal/commands/builder/clone.rb +16 -14
  49. data/lib/kamal/commands/builder/cloud.rb +22 -0
  50. data/lib/kamal/commands/builder/hybrid.rb +21 -0
  51. data/lib/kamal/commands/builder/local.rb +14 -0
  52. data/lib/kamal/commands/builder/pack.rb +46 -0
  53. data/lib/kamal/commands/builder/remote.rb +63 -0
  54. data/lib/kamal/commands/builder.rb +21 -45
  55. data/lib/kamal/commands/docker.rb +4 -0
  56. data/lib/kamal/commands/hook.rb +8 -2
  57. data/lib/kamal/commands/lock.rb +2 -6
  58. data/lib/kamal/commands/proxy.rb +127 -0
  59. data/lib/kamal/commands/prune.rb +1 -9
  60. data/lib/kamal/commands/registry.rb +9 -7
  61. data/lib/kamal/commands/server.rb +11 -1
  62. data/lib/kamal/configuration/accessory.rb +89 -12
  63. data/lib/kamal/configuration/alias.rb +15 -0
  64. data/lib/kamal/configuration/builder.rb +73 -15
  65. data/lib/kamal/configuration/docs/accessory.yml +53 -15
  66. data/lib/kamal/configuration/docs/alias.yml +26 -0
  67. data/lib/kamal/configuration/docs/boot.yml +3 -3
  68. data/lib/kamal/configuration/docs/builder.yml +63 -38
  69. data/lib/kamal/configuration/docs/configuration.yml +62 -46
  70. data/lib/kamal/configuration/docs/env.yml +61 -17
  71. data/lib/kamal/configuration/docs/logging.yml +3 -3
  72. data/lib/kamal/configuration/docs/proxy.yml +168 -0
  73. data/lib/kamal/configuration/docs/registry.yml +20 -13
  74. data/lib/kamal/configuration/docs/role.yml +14 -13
  75. data/lib/kamal/configuration/docs/servers.yml +2 -2
  76. data/lib/kamal/configuration/docs/ssh.yml +23 -19
  77. data/lib/kamal/configuration/docs/sshkit.yml +4 -4
  78. data/lib/kamal/configuration/env/tag.rb +4 -3
  79. data/lib/kamal/configuration/env.rb +19 -17
  80. data/lib/kamal/configuration/proxy/boot.rb +129 -0
  81. data/lib/kamal/configuration/proxy.rb +124 -0
  82. data/lib/kamal/configuration/registry.rb +7 -6
  83. data/lib/kamal/configuration/role.rb +69 -98
  84. data/lib/kamal/configuration/servers.rb +8 -1
  85. data/lib/kamal/configuration/validator/accessory.rb +6 -2
  86. data/lib/kamal/configuration/validator/alias.rb +15 -0
  87. data/lib/kamal/configuration/validator/builder.rb +6 -0
  88. data/lib/kamal/configuration/validator/proxy.rb +25 -0
  89. data/lib/kamal/configuration/validator/role.rb +3 -1
  90. data/lib/kamal/configuration/validator/servers.rb +1 -1
  91. data/lib/kamal/configuration/validator.rb +62 -24
  92. data/lib/kamal/configuration.rb +96 -50
  93. data/lib/kamal/docker.rb +30 -0
  94. data/lib/kamal/env_file.rb +7 -1
  95. data/lib/kamal/git.rb +10 -0
  96. data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +51 -0
  97. data/lib/kamal/secrets/adapters/base.rb +33 -0
  98. data/lib/kamal/secrets/adapters/bitwarden.rb +81 -0
  99. data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
  100. data/lib/kamal/secrets/adapters/doppler.rb +57 -0
  101. data/lib/kamal/secrets/adapters/enpass.rb +71 -0
  102. data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
  103. data/lib/kamal/secrets/adapters/last_pass.rb +40 -0
  104. data/lib/kamal/secrets/adapters/one_password.rb +104 -0
  105. data/lib/kamal/secrets/adapters/passbolt.rb +130 -0
  106. data/lib/kamal/secrets/adapters/test.rb +14 -0
  107. data/lib/kamal/secrets/adapters.rb +16 -0
  108. data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +33 -0
  109. data/lib/kamal/secrets.rb +42 -0
  110. data/lib/kamal/sshkit_with_ext.rb +1 -0
  111. data/lib/kamal/utils.rb +30 -0
  112. data/lib/kamal/version.rb +1 -1
  113. data/lib/kamal.rb +3 -1
  114. metadata +63 -36
  115. data/lib/kamal/cli/env.rb +0 -54
  116. data/lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample +0 -3
  117. data/lib/kamal/cli/templates/sample_hooks/pre-traefik-reboot.sample +0 -3
  118. data/lib/kamal/cli/templates/template.env +0 -2
  119. data/lib/kamal/cli/traefik.rb +0 -122
  120. data/lib/kamal/commands/app/cord.rb +0 -22
  121. data/lib/kamal/commands/builder/multiarch/remote.rb +0 -65
  122. data/lib/kamal/commands/builder/multiarch.rb +0 -41
  123. data/lib/kamal/commands/builder/native/cached.rb +0 -25
  124. data/lib/kamal/commands/builder/native/remote.rb +0 -67
  125. data/lib/kamal/commands/builder/native.rb +0 -20
  126. data/lib/kamal/commands/traefik.rb +0 -85
  127. data/lib/kamal/configuration/docs/healthcheck.yml +0 -59
  128. data/lib/kamal/configuration/docs/traefik.yml +0 -62
  129. data/lib/kamal/configuration/healthcheck.rb +0 -63
  130. data/lib/kamal/configuration/traefik.rb +0 -60
@@ -11,19 +11,23 @@ class Kamal::Commander::Specifics
11
11
  @primary_role = primary_or_first_role(roles_on(primary_host))
12
12
 
13
13
  stable_sort!(roles) { |role| role == primary_role ? 0 : 1 }
14
- stable_sort!(hosts) { |host| roles_on(host).any? { |role| role == primary_role } ? 0 : 1 }
14
+ sort_primary_role_hosts_first!(hosts)
15
15
  end
16
16
 
17
17
  def roles_on(host)
18
18
  roles.select { |role| role.hosts.include?(host.to_s) }
19
19
  end
20
20
 
21
- def traefik_hosts
22
- config.traefik_hosts & specified_hosts
21
+ def app_hosts
22
+ @app_hosts ||= sort_primary_role_hosts_first!(config.app_hosts & specified_hosts)
23
+ end
24
+
25
+ def proxy_hosts
26
+ config.proxy_hosts & specified_hosts
23
27
  end
24
28
 
25
29
  def accessory_hosts
26
- specific_hosts || config.accessories.flat_map(&:hosts)
30
+ config.accessories.flat_map(&:hosts) & specified_hosts
27
31
  end
28
32
 
29
33
  private
@@ -43,7 +47,16 @@ class Kamal::Commander::Specifics
43
47
  end
44
48
 
45
49
  def specified_hosts
46
- (specific_hosts || config.all_hosts) \
47
- .select { |host| (specific_roles || config.roles).flat_map(&:hosts).include?(host) }
50
+ specified_hosts = specific_hosts || config.all_hosts
51
+
52
+ if (specific_role_hosts = specific_roles&.flat_map(&:hosts)).present?
53
+ specified_hosts.select { |host| specific_role_hosts.include?(host) }
54
+ else
55
+ specified_hosts
56
+ end
57
+ end
58
+
59
+ def sort_primary_role_hosts_first!(hosts)
60
+ stable_sort!(hosts) { |host| roles_on(host).any? { |role| role == primary_role } ? 0 : 1 }
48
61
  end
49
62
  end
@@ -1,15 +1,23 @@
1
1
  require "active_support/core_ext/enumerable"
2
2
  require "active_support/core_ext/module/delegation"
3
+ require "active_support/core_ext/object/blank"
3
4
 
4
5
  class Kamal::Commander
5
6
  attr_accessor :verbosity, :holding_lock, :connected
6
- delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :traefik_hosts, :accessory_hosts, to: :specifics
7
+ attr_reader :specific_roles, :specific_hosts
8
+ delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :app_hosts, :proxy_hosts, :accessory_hosts, to: :specifics
7
9
 
8
10
  def initialize
11
+ reset
12
+ end
13
+
14
+ def reset
9
15
  self.verbosity = :info
10
- self.holding_lock = false
16
+ self.holding_lock = ENV["KAMAL_LOCK"] == "true"
11
17
  self.connected = false
12
- @specifics = nil
18
+ @specifics = @specific_roles = @specific_hosts = nil
19
+ @config = @config_kwargs = nil
20
+ @commands = {}
13
21
  end
14
22
 
15
23
  def config
@@ -23,11 +31,17 @@ class Kamal::Commander
23
31
  @config, @config_kwargs = nil, kwargs
24
32
  end
25
33
 
26
- attr_reader :specific_roles, :specific_hosts
34
+ def configured?
35
+ @config || @config_kwargs
36
+ end
27
37
 
28
38
  def specific_primary!
29
39
  @specifics = nil
30
- self.specific_hosts = [ config.primary_host ]
40
+ if specific_roles.present?
41
+ self.specific_hosts = [ specific_roles.first.primary_host ]
42
+ else
43
+ self.specific_hosts = [ config.primary_host ]
44
+ end
31
45
  end
32
46
 
33
47
  def specific_roles=(role_names)
@@ -56,15 +70,17 @@ class Kamal::Commander
56
70
  end
57
71
  end
58
72
 
59
- def accessory_names
60
- config.accessories&.collect(&:name) || []
73
+ def with_specific_hosts(hosts)
74
+ original_hosts, self.specific_hosts = specific_hosts, hosts
75
+ yield
76
+ ensure
77
+ self.specific_hosts = original_hosts
61
78
  end
62
79
 
63
- def accessories_on(host)
64
- config.accessories.select { |accessory| accessory.hosts.include?(host.to_s) }.map(&:name)
80
+ def accessory_names
81
+ config.accessories&.collect(&:name) || []
65
82
  end
66
83
 
67
-
68
84
  def app(role: nil, host: nil)
69
85
  Kamal::Commands::App.new(config, role: role, host: host)
70
86
  end
@@ -78,42 +94,41 @@ class Kamal::Commander
78
94
  end
79
95
 
80
96
  def builder
81
- @builder ||= Kamal::Commands::Builder.new(config)
97
+ @commands[:builder] ||= Kamal::Commands::Builder.new(config)
82
98
  end
83
99
 
84
100
  def docker
85
- @docker ||= Kamal::Commands::Docker.new(config)
86
- end
87
-
88
- def healthcheck
89
- @healthcheck ||= Kamal::Commands::Healthcheck.new(config)
101
+ @commands[:docker] ||= Kamal::Commands::Docker.new(config)
90
102
  end
91
103
 
92
104
  def hook
93
- @hook ||= Kamal::Commands::Hook.new(config)
105
+ @commands[:hook] ||= Kamal::Commands::Hook.new(config)
94
106
  end
95
107
 
96
108
  def lock
97
- @lock ||= Kamal::Commands::Lock.new(config)
109
+ @commands[:lock] ||= Kamal::Commands::Lock.new(config)
110
+ end
111
+
112
+ def proxy
113
+ @commands[:proxy] ||= Kamal::Commands::Proxy.new(config)
98
114
  end
99
115
 
100
116
  def prune
101
- @prune ||= Kamal::Commands::Prune.new(config)
117
+ @commands[:prune] ||= Kamal::Commands::Prune.new(config)
102
118
  end
103
119
 
104
120
  def registry
105
- @registry ||= Kamal::Commands::Registry.new(config)
121
+ @commands[:registry] ||= Kamal::Commands::Registry.new(config)
106
122
  end
107
123
 
108
124
  def server
109
- @server ||= Kamal::Commands::Server.new(config)
125
+ @commands[:server] ||= Kamal::Commands::Server.new(config)
110
126
  end
111
127
 
112
- def traefik
113
- @traefik ||= Kamal::Commands::Traefik.new(config)
128
+ def alias(name)
129
+ config.aliases[name]
114
130
  end
115
131
 
116
-
117
132
  def with_verbosity(level)
118
133
  old_level = self.verbosity
119
134
 
@@ -126,14 +141,6 @@ class Kamal::Commander
126
141
  SSHKit.config.output_verbosity = old_level
127
142
  end
128
143
 
129
- def boot_strategy
130
- if config.boot.limit.present?
131
- { in: :groups, limit: config.boot.limit, wait: config.boot.wait }
132
- else
133
- {}
134
- end
135
- end
136
-
137
144
  def holding_lock?
138
145
  self.holding_lock
139
146
  end
@@ -0,0 +1,16 @@
1
+ module Kamal::Commands::Accessory::Proxy
2
+ delegate :container_name, to: :"config.proxy_boot", prefix: :proxy
3
+
4
+ def deploy(target:)
5
+ proxy_exec :deploy, service_name, *proxy.deploy_command_args(target: target)
6
+ end
7
+
8
+ def remove
9
+ proxy_exec :remove, service_name
10
+ end
11
+
12
+ private
13
+ def proxy_exec(*command)
14
+ docker :exec, proxy_container_name, "kamal-proxy", *command
15
+ end
16
+ end
@@ -1,20 +1,26 @@
1
1
  class Kamal::Commands::Accessory < Kamal::Commands::Base
2
+ include Proxy
3
+
2
4
  attr_reader :accessory_config
3
5
  delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
4
- :publish_args, :env_args, :volume_args, :label_args, :option_args, to: :accessory_config
6
+ :network_args, :publish_args, :env_args, :volume_args, :label_args, :option_args,
7
+ :secrets_io, :secrets_path, :env_directory, :proxy, :running_proxy?, :registry,
8
+ to: :accessory_config
5
9
 
6
10
  def initialize(config, name:)
7
11
  super(config)
8
12
  @accessory_config = config.accessory(name)
9
13
  end
10
14
 
11
- def run
15
+ def run(host: nil)
12
16
  docker :run,
13
17
  "--name", service_name,
14
18
  "--detach",
15
19
  "--restart", "unless-stopped",
20
+ *network_args,
16
21
  *config.logging_args,
17
22
  *publish_args,
23
+ *([ "--env", "KAMAL_HOST=\"#{host}\"" ] if host),
18
24
  *env_args,
19
25
  *volume_args,
20
26
  *label_args,
@@ -31,36 +37,35 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
31
37
  docker :container, :stop, service_name
32
38
  end
33
39
 
34
- def info
35
- docker :ps, *service_filter
40
+ def info(all: false, quiet: false)
41
+ docker :ps, *("-a" if all), *("-q" if quiet), *service_filter
36
42
  end
37
43
 
38
-
39
- def logs(since: nil, lines: nil, grep: nil, grep_options: nil)
44
+ def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
40
45
  pipe \
41
- docker(:logs, service_name, (" --since #{since}" if since), (" --tail #{lines}" if lines), "--timestamps", "2>&1"),
46
+ docker(:logs, service_name, (" --since #{since}" if since), (" --tail #{lines}" if lines), ("--timestamps" if timestamps), "2>&1"),
42
47
  ("grep '#{grep}'#{" #{grep_options}" if grep_options}" if grep)
43
48
  end
44
49
 
45
- def follow_logs(grep: nil, grep_options: nil)
50
+ def follow_logs(timestamps: true, grep: nil, grep_options: nil)
46
51
  run_over_ssh \
47
52
  pipe \
48
- docker(:logs, service_name, "--timestamps", "--tail", "10", "--follow", "2>&1"),
53
+ docker(:logs, service_name, ("--timestamps" if timestamps), "--tail", "10", "--follow", "2>&1"),
49
54
  (%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
50
55
  end
51
56
 
52
-
53
57
  def execute_in_existing_container(*command, interactive: false)
54
58
  docker :exec,
55
- ("-it" if interactive),
59
+ (docker_interactive_args if interactive),
56
60
  service_name,
57
61
  *command
58
62
  end
59
63
 
60
64
  def execute_in_new_container(*command, interactive: false)
61
65
  docker :run,
62
- ("-it" if interactive),
66
+ (docker_interactive_args if interactive),
63
67
  "--rm",
68
+ *network_args,
64
69
  *env_args,
65
70
  *volume_args,
66
71
  image,
@@ -79,7 +84,6 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
79
84
  super command, host: hosts.first
80
85
  end
81
86
 
82
-
83
87
  def ensure_local_file_present(local_file)
84
88
  if !local_file.is_a?(StringIO) && !Pathname.new(local_file).exist?
85
89
  raise "Missing file: #{local_file}"
@@ -98,12 +102,8 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
98
102
  docker :image, :rm, "--force", image
99
103
  end
100
104
 
101
- def make_env_directory
102
- make_directory accessory_config.env.secrets_directory
103
- end
104
-
105
- def remove_env_file
106
- [ :rm, "-f", accessory_config.env.secrets_file ]
105
+ def ensure_env_directory
106
+ make_directory env_directory
107
107
  end
108
108
 
109
109
  private
@@ -3,18 +3,18 @@ module Kamal::Commands::App::Assets
3
3
  asset_container = "#{role.container_prefix}-assets"
4
4
 
5
5
  combine \
6
- make_directory(role.asset_extracted_path),
7
- [ *docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true" ],
8
- docker(:run, "--name", asset_container, "--detach", "--rm", config.absolute_image, "sleep 1000000"),
9
- docker(:cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_path),
10
- docker(:stop, "-t 1", asset_container),
6
+ make_directory(role.asset_extracted_directory),
7
+ [ *docker(:container, :rm, asset_container, "2> /dev/null"), "|| true" ],
8
+ docker(:container, :create, "--name", asset_container, config.absolute_image),
9
+ docker(:container, :cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_directory),
10
+ docker(:container, :rm, asset_container),
11
11
  by: "&&"
12
12
  end
13
13
 
14
14
  def sync_asset_volumes(old_version: nil)
15
- new_extracted_path, new_volume_path = role.asset_extracted_path(config.version), role.asset_volume.host_path
15
+ new_extracted_path, new_volume_path = role.asset_extracted_directory(config.version), role.asset_volume.host_path
16
16
  if old_version.present?
17
- old_extracted_path, old_volume_path = role.asset_extracted_path(old_version), role.asset_volume(old_version).host_path
17
+ old_extracted_path, old_volume_path = role.asset_extracted_directory(old_version), role.asset_volume(old_version).host_path
18
18
  end
19
19
 
20
20
  commands = [ make_directory(new_volume_path), copy_contents(new_extracted_path, new_volume_path) ]
@@ -29,8 +29,8 @@ module Kamal::Commands::App::Assets
29
29
 
30
30
  def clean_up_assets
31
31
  chain \
32
- find_and_remove_older_siblings(role.asset_extracted_path),
33
- find_and_remove_older_siblings(role.asset_volume_path)
32
+ find_and_remove_older_siblings(role.asset_extracted_directory),
33
+ find_and_remove_older_siblings(role.asset_volume_directory)
34
34
  end
35
35
 
36
36
  private
@@ -39,7 +39,7 @@ module Kamal::Commands::App::Assets
39
39
  :find,
40
40
  Pathname.new(path).dirname.to_s,
41
41
  "-maxdepth 1",
42
- "-name", "'#{role.container_prefix}-*'",
42
+ "-name", "'#{role.name}-*'",
43
43
  "!", "-name", Pathname.new(path).basename.to_s,
44
44
  "-exec rm -rf \"{}\" +"
45
45
  ]
@@ -2,7 +2,7 @@ module Kamal::Commands::App::Containers
2
2
  DOCKER_HEALTH_LOG_FORMAT = "'{{json .State.Health}}'"
3
3
 
4
4
  def list_containers
5
- docker :container, :ls, "--all", *filter_args
5
+ docker :container, :ls, "--all", *container_filter_args
6
6
  end
7
7
 
8
8
  def list_container_names
@@ -20,7 +20,7 @@ module Kamal::Commands::App::Containers
20
20
  end
21
21
 
22
22
  def remove_containers
23
- docker :container, :prune, "--force", *filter_args
23
+ docker :container, :prune, "--force", *container_filter_args
24
24
  end
25
25
 
26
26
  def container_health_log(version:)
@@ -0,0 +1,9 @@
1
+ module Kamal::Commands::App::ErrorPages
2
+ def create_error_pages_directory
3
+ make_directory(config.proxy_boot.error_pages_directory)
4
+ end
5
+
6
+ def clean_up_error_pages
7
+ [ :find, config.proxy_boot.error_pages_directory, "-mindepth", "1", "-maxdepth", "1", "!", "-name", KAMAL.config.version, "-exec", "rm", "-rf", "{} +" ]
8
+ end
9
+ end
@@ -1,18 +1,21 @@
1
1
  module Kamal::Commands::App::Execution
2
2
  def execute_in_existing_container(*command, interactive: false, env:)
3
3
  docker :exec,
4
- ("-it" if interactive),
4
+ (docker_interactive_args if interactive),
5
5
  *argumentize("--env", env),
6
6
  container_name,
7
7
  *command
8
8
  end
9
9
 
10
- def execute_in_new_container(*command, interactive: false, env:)
10
+ def execute_in_new_container(*command, interactive: false, detach: false, env:)
11
11
  docker :run,
12
- ("-it" if interactive),
13
- "--rm",
12
+ (docker_interactive_args if interactive),
13
+ ("--detach" if detach),
14
+ ("--rm" unless detach),
15
+ "--network", "kamal",
14
16
  *role&.env_args(host),
15
17
  *argumentize("--env", env),
18
+ *role.logging_args,
16
19
  *config.volume_args,
17
20
  *role&.option_args,
18
21
  config.absolute_image,
@@ -4,7 +4,7 @@ module Kamal::Commands::App::Images
4
4
  end
5
5
 
6
6
  def remove_images
7
- docker :image, :prune, "--all", "--force", *filter_args
7
+ docker :image, :prune, "--all", "--force", *image_filter_args
8
8
  end
9
9
 
10
10
  def tag_latest_image
@@ -1,18 +1,28 @@
1
1
  module Kamal::Commands::App::Logging
2
- def logs(version: nil, since: nil, lines: nil, grep: nil, grep_options: nil)
2
+ def logs(container_id: nil, timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
3
3
  pipe \
4
- version ? container_id_for_version(version) : current_running_container_id,
5
- "xargs docker logs#{" --since #{since}" if since}#{" --tail #{lines}" if lines} 2>&1",
4
+ container_id_command(container_id),
5
+ "xargs docker logs#{" --timestamps" if timestamps}#{" --since #{since}" if since}#{" --tail #{lines}" if lines} 2>&1",
6
6
  ("grep '#{grep}'#{" #{grep_options}" if grep_options}" if grep)
7
7
  end
8
8
 
9
- def follow_logs(host:, lines: nil, grep: nil, grep_options: nil)
9
+ def follow_logs(host:, container_id: nil, timestamps: true, lines: nil, grep: nil, grep_options: nil)
10
10
  run_over_ssh \
11
11
  pipe(
12
- current_running_container_id,
13
- "xargs docker logs --timestamps#{" --tail #{lines}" if lines} --follow 2>&1",
12
+ container_id_command(container_id),
13
+ "xargs docker logs#{" --timestamps" if timestamps}#{" --tail #{lines}" if lines} --follow 2>&1",
14
14
  (%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
15
15
  ),
16
16
  host: host
17
17
  end
18
+
19
+ private
20
+
21
+ def container_id_command(container_id)
22
+ case container_id
23
+ when Array then container_id
24
+ when String, Symbol then "echo #{container_id}"
25
+ else current_running_container_id
26
+ end
27
+ end
18
28
  end
@@ -0,0 +1,32 @@
1
+ module Kamal::Commands::App::Proxy
2
+ delegate :container_name, to: :"config.proxy_boot", prefix: :proxy
3
+
4
+ def deploy(target:)
5
+ proxy_exec :deploy, role.container_prefix, *role.proxy.deploy_command_args(target: target)
6
+ end
7
+
8
+ def remove
9
+ proxy_exec :remove, role.container_prefix
10
+ end
11
+
12
+ def live
13
+ proxy_exec :resume, role.container_prefix
14
+ end
15
+
16
+ def maintenance(**options)
17
+ proxy_exec :stop, role.container_prefix, *role.proxy.stop_command_args(**options)
18
+ end
19
+
20
+ def remove_proxy_app_directory
21
+ remove_directory config.proxy_boot.app_directory
22
+ end
23
+
24
+ def create_ssl_directory
25
+ make_directory(File.join(config.proxy_boot.tls_directory, role.name))
26
+ end
27
+
28
+ private
29
+ def proxy_exec(*command)
30
+ docker :exec, proxy_container_name, "kamal-proxy", *command
31
+ end
32
+ end
@@ -1,10 +1,12 @@
1
1
  class Kamal::Commands::App < Kamal::Commands::Base
2
- include Assets, Containers, Cord, Execution, Images, Logging
2
+ include Assets, Containers, ErrorPages, Execution, Images, Logging, Proxy
3
3
 
4
4
  ACTIVE_DOCKER_STATUSES = [ :running, :restarting ]
5
5
 
6
6
  attr_reader :role, :host
7
7
 
8
+ delegate :container_name, to: :role
9
+
8
10
  def initialize(config, role: nil, host: nil)
9
11
  super(config)
10
12
  @role = role
@@ -16,11 +18,12 @@ class Kamal::Commands::App < Kamal::Commands::Base
16
18
  "--detach",
17
19
  "--restart unless-stopped",
18
20
  "--name", container_name,
21
+ "--network", "kamal",
19
22
  *([ "--hostname", hostname ] if hostname),
20
- "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
21
- "-e", "KAMAL_VERSION=\"#{config.version}\"",
23
+ "--env", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
24
+ "--env", "KAMAL_VERSION=\"#{config.version}\"",
25
+ "--env", "KAMAL_HOST=\"#{host}\"",
22
26
  *role.env_args(host),
23
- *role.health_check_args,
24
27
  *role.logging_args,
25
28
  *config.volume_args,
26
29
  *role.asset_volume_args,
@@ -41,11 +44,11 @@ class Kamal::Commands::App < Kamal::Commands::Base
41
44
  def stop(version: nil)
42
45
  pipe \
43
46
  version ? container_id_for_version(version) : current_running_container_id,
44
- xargs(config.stop_wait_time ? docker(:stop, "-t", config.stop_wait_time) : docker(:stop))
47
+ xargs(docker(:stop, *role.stop_args))
45
48
  end
46
49
 
47
50
  def info
48
- docker :ps, *filter_args
51
+ docker :ps, *container_filter_args
49
52
  end
50
53
 
51
54
 
@@ -65,25 +68,15 @@ class Kamal::Commands::App < Kamal::Commands::Base
65
68
 
66
69
  def list_versions(*docker_args, statuses: nil)
67
70
  pipe \
68
- docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
71
+ docker(:ps, *container_filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
69
72
  extract_version_from_name
70
73
  end
71
74
 
72
-
73
- def make_env_directory
74
- make_directory role.env(host).secrets_directory
75
- end
76
-
77
- def remove_env_file
78
- [ :rm, "-f", role.env(host).secrets_file ]
75
+ def ensure_env_directory
76
+ make_directory role.env_directory
79
77
  end
80
78
 
81
-
82
79
  private
83
- def container_name(version = nil)
84
- [ role.container_prefix, version || config.version ].compact.join("-")
85
- end
86
-
87
80
  def latest_image_id
88
81
  docker :image, :ls, *argumentize("--filter", "reference=#{config.latest_image}"), "--format", "'{{.ID}}'"
89
82
  end
@@ -99,11 +92,15 @@ class Kamal::Commands::App < Kamal::Commands::Base
99
92
  end
100
93
 
101
94
  def latest_container(format:, filters: nil)
102
- docker :ps, "--latest", *format, *filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
95
+ docker :ps, "--latest", *format, *container_filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
96
+ end
97
+
98
+ def container_filter_args(statuses: nil)
99
+ argumentize "--filter", container_filters(statuses: statuses)
103
100
  end
104
101
 
105
- def filter_args(statuses: nil)
106
- argumentize "--filter", filters(statuses: statuses)
102
+ def image_filter_args
103
+ argumentize "--filter", image_filters
107
104
  end
108
105
 
109
106
  def extract_version_from_name
@@ -111,13 +108,17 @@ class Kamal::Commands::App < Kamal::Commands::Base
111
108
  %(while read line; do echo ${line##{role.container_prefix}-}; done)
112
109
  end
113
110
 
114
- def filters(statuses: nil)
111
+ def container_filters(statuses: nil)
115
112
  [ "label=service=#{config.service}" ].tap do |filters|
116
- filters << "label=destination=#{config.destination}" if config.destination
113
+ filters << "label=destination=#{config.destination}"
117
114
  filters << "label=role=#{role}" if role
118
115
  statuses&.each do |status|
119
116
  filters << "status=#{status}"
120
117
  end
121
118
  end
122
119
  end
120
+
121
+ def image_filters
122
+ [ "label=service=#{config.service}" ]
123
+ end
123
124
  end
@@ -1,5 +1,6 @@
1
1
  class Kamal::Commands::Auditor < Kamal::Commands::Base
2
2
  attr_reader :details
3
+ delegate :escape_shell_value, to: Kamal::Utils
3
4
 
4
5
  def initialize(config, **details)
5
6
  super(config)
@@ -8,9 +9,9 @@ class Kamal::Commands::Auditor < Kamal::Commands::Base
8
9
 
9
10
  # Runs remotely
10
11
  def record(line, **details)
11
- append \
12
- [ :echo, audit_tags(**details).except(:version, :service_version, :service).to_s, line ],
13
- audit_log_file
12
+ combine \
13
+ make_run_directory,
14
+ append([ :echo, escape_shell_value(audit_line(line, **details)) ], audit_log_file)
14
15
  end
15
16
 
16
17
  def reveal
@@ -27,4 +28,12 @@ class Kamal::Commands::Auditor < Kamal::Commands::Base
27
28
  def audit_tags(**details)
28
29
  tags(**self.details, **details)
29
30
  end
31
+
32
+ def make_run_directory
33
+ [ :mkdir, "-p", config.run_directory ]
34
+ end
35
+
36
+ def audit_line(line, **details)
37
+ "#{audit_tags(**details).except(:version, :service_version, :service)} #{line}"
38
+ end
30
39
  end