kamal 2.8.1 → 2.8.2
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/lib/kamal/cli/build/clone.rb +0 -2
- data/lib/kamal/cli/build/port_forwarding.rb +66 -0
- data/lib/kamal/cli/build.rb +43 -26
- data/lib/kamal/cli/registry.rb +22 -0
- data/lib/kamal/commands/builder/base.rb +10 -0
- data/lib/kamal/commands/builder/hybrid.rb +3 -3
- data/lib/kamal/commands/builder/local.rb +2 -9
- data/lib/kamal/commands/builder/remote.rb +6 -2
- data/lib/kamal/configuration/docs/registry.yml +12 -4
- data/lib/kamal/configuration.rb +11 -0
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +1 -0
- metadata +2 -2
- data/lib/kamal/cli/port_forwarding.rb +0 -55
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ee017d64694e5a35507b9cfc9693e6a6c5fc876e0cfb731d58f0de7dba281f41
         | 
| 4 | 
            +
              data.tar.gz: bcbc84a444f0aa5efb08fd0ab63ea87d5892821f04e9c6ac51d938d78e2547a5
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b285597e99a5d2d90a324e9be64332a71afd66269ac27c8550eebf692758cac14535a14164058fd246d0001fd3b371382ed0b4e4bc9a75e19f8d42e40b4d9365
         | 
| 7 | 
            +
              data.tar.gz: fca4902b9da04bab86d087b0582ed956e2ee174259e47f1a491a4b68c928dc16a501cb0b137d443daeda25c00fd99bcf0f234ce54bf85da7b61002ce90402de0
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            require "concurrent/atomic/count_down_latch"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Kamal::Cli::Build::PortForwarding
         | 
| 4 | 
            +
              attr_reader :hosts, :port, :ssh_options
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def initialize(hosts, port, **ssh_options)
         | 
| 7 | 
            +
                @hosts = hosts
         | 
| 8 | 
            +
                @port = port
         | 
| 9 | 
            +
                @ssh_options = ssh_options
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              def forward
         | 
| 13 | 
            +
                @done = false
         | 
| 14 | 
            +
                forward_ports
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                yield
         | 
| 17 | 
            +
              ensure
         | 
| 18 | 
            +
                stop
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              private
         | 
| 22 | 
            +
                def stop
         | 
| 23 | 
            +
                  @done = true
         | 
| 24 | 
            +
                  @threads.to_a.each(&:join)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def forward_ports
         | 
| 28 | 
            +
                  ready = Concurrent::CountDownLatch.new(hosts.size)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  @threads = hosts.map do |host|
         | 
| 31 | 
            +
                    Thread.new do
         | 
| 32 | 
            +
                      begin
         | 
| 33 | 
            +
                        Net::SSH.start(host, ssh_options[:user], **ssh_options.except(:user)) do |ssh|
         | 
| 34 | 
            +
                          ssh.forward.remote(port, "localhost", port, "127.0.0.1") do |remote_port, bind_address|
         | 
| 35 | 
            +
                            if remote_port == :error
         | 
| 36 | 
            +
                              raise "Failed to establish port forward on #{host}"
         | 
| 37 | 
            +
                            else
         | 
| 38 | 
            +
                              ready.count_down
         | 
| 39 | 
            +
                            end
         | 
| 40 | 
            +
                          end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                          ssh.loop(0.1) do
         | 
| 43 | 
            +
                            if @done
         | 
| 44 | 
            +
                              ssh.forward.cancel_remote(port, "127.0.0.1")
         | 
| 45 | 
            +
                              break
         | 
| 46 | 
            +
                            else
         | 
| 47 | 
            +
                              true
         | 
| 48 | 
            +
                            end
         | 
| 49 | 
            +
                          end
         | 
| 50 | 
            +
                        end
         | 
| 51 | 
            +
                      rescue Exception => e
         | 
| 52 | 
            +
                        error "Error setting up port forwarding to #{host}: #{e.class}: #{e.message}"
         | 
| 53 | 
            +
                        error e.backtrace.join("\n")
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                        raise
         | 
| 56 | 
            +
                      end
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  raise "Timed out waiting for port forwarding to be established" unless ready.wait(30)
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def error(message)
         | 
| 64 | 
            +
                  SSHKit.config.output.error(message)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
            end
         | 
    
        data/lib/kamal/cli/build.rb
    CHANGED
    
    | @@ -1,5 +1,3 @@ | |
| 1 | 
            -
            require "uri"
         | 
| 2 | 
            -
             | 
| 3 1 | 
             
            class Kamal::Cli::Build < Kamal::Cli::Base
         | 
| 4 2 | 
             
              class BuildError < StandardError; end
         | 
| 5 3 |  | 
| @@ -38,29 +36,31 @@ class Kamal::Cli::Build < Kamal::Cli::Base | |
| 38 36 | 
             
                  say "Building with uncommitted changes:\n #{uncommitted_changes}", :yellow
         | 
| 39 37 | 
             
                end
         | 
| 40 38 |  | 
| 41 | 
            -
                 | 
| 42 | 
            -
                   | 
| 43 | 
            -
                     | 
| 44 | 
            -
                       | 
| 45 | 
            -
             | 
| 46 | 
            -
                       | 
| 47 | 
            -
                         | 
| 48 | 
            -
             | 
| 49 | 
            -
                           | 
| 50 | 
            -
             | 
| 51 | 
            -
                           | 
| 39 | 
            +
                forward_local_registry_port_for_remote_builder do
         | 
| 40 | 
            +
                  with_env(KAMAL.config.builder.secrets) do
         | 
| 41 | 
            +
                    run_locally do
         | 
| 42 | 
            +
                      begin
         | 
| 43 | 
            +
                        execute *KAMAL.builder.inspect_builder
         | 
| 44 | 
            +
                      rescue SSHKit::Command::Failed => e
         | 
| 45 | 
            +
                        if e.message =~ /(context not found|no builder|no compatible builder|does not exist)/
         | 
| 46 | 
            +
                          warn "Missing compatible builder, so creating a new one first"
         | 
| 47 | 
            +
                          begin
         | 
| 48 | 
            +
                            cli.remove
         | 
| 49 | 
            +
                          rescue SSHKit::Command::Failed
         | 
| 50 | 
            +
                            raise unless e.message =~ /(context not found|no builder|does not exist)/
         | 
| 51 | 
            +
                          end
         | 
| 52 | 
            +
                          cli.create
         | 
| 53 | 
            +
                        else
         | 
| 54 | 
            +
                          raise
         | 
| 52 55 | 
             
                        end
         | 
| 53 | 
            -
                        cli.create
         | 
| 54 | 
            -
                      else
         | 
| 55 | 
            -
                        raise
         | 
| 56 56 | 
             
                      end
         | 
| 57 | 
            -
                    end
         | 
| 58 57 |  | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 58 | 
            +
                      # Get the command here to ensure the Dir.chdir doesn't interfere with it
         | 
| 59 | 
            +
                      push = KAMAL.builder.push(cli.options[:output], no_cache: cli.options[:no_cache])
         | 
| 61 60 |  | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 61 | 
            +
                      KAMAL.with_verbosity(:debug) do
         | 
| 62 | 
            +
                        Dir.chdir(KAMAL.config.builder.build_directory) { execute *push, env: KAMAL.builder.push_env }
         | 
| 63 | 
            +
                      end
         | 
| 64 64 | 
             
                    end
         | 
| 65 65 | 
             
                  end
         | 
| 66 66 | 
             
                end
         | 
| @@ -70,7 +70,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base | |
| 70 70 | 
             
              def pull
         | 
| 71 71 | 
             
                login_to_registry_remotely unless KAMAL.registry.local?
         | 
| 72 72 |  | 
| 73 | 
            -
                forward_local_registry_port do
         | 
| 73 | 
            +
                forward_local_registry_port(KAMAL.hosts, **KAMAL.config.ssh.options) do
         | 
| 74 74 | 
             
                  if (first_hosts = mirror_hosts).any?
         | 
| 75 75 | 
             
                    #  Pull on a single host per mirror first to seed them
         | 
| 76 76 | 
             
                    say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
         | 
| @@ -210,13 +210,30 @@ class Kamal::Cli::Build < Kamal::Cli::Base | |
| 210 210 | 
             
                  end
         | 
| 211 211 | 
             
                end
         | 
| 212 212 |  | 
| 213 | 
            -
                def  | 
| 213 | 
            +
                def forward_local_registry_port_for_remote_builder(&block)
         | 
| 214 | 
            +
                  if KAMAL.builder.remote?
         | 
| 215 | 
            +
                    remote_uri = URI(KAMAL.config.builder.remote)
         | 
| 216 | 
            +
                    forward_local_registry_port([ remote_uri.host ], **remote_builder_ssh_options(remote_uri), &block)
         | 
| 217 | 
            +
                  else
         | 
| 218 | 
            +
                    yield
         | 
| 219 | 
            +
                  end
         | 
| 220 | 
            +
                end
         | 
| 221 | 
            +
             | 
| 222 | 
            +
                def forward_local_registry_port(hosts, **ssh_options, &block)
         | 
| 214 223 | 
             
                  if KAMAL.config.registry.local?
         | 
| 215 | 
            -
                     | 
| 216 | 
            -
             | 
| 217 | 
            -
                      forward(&block)
         | 
| 224 | 
            +
                    say "Setting up local registry port forwarding to #{hosts.join(', ')}..."
         | 
| 225 | 
            +
                    PortForwarding.new(hosts, KAMAL.config.registry.local_port, **ssh_options).forward(&block)
         | 
| 218 226 | 
             
                  else
         | 
| 219 227 | 
             
                    yield
         | 
| 220 228 | 
             
                  end
         | 
| 221 229 | 
             
                end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                def remote_builder_ssh_options(remote_uri)
         | 
| 232 | 
            +
                  { user: remote_uri.user,
         | 
| 233 | 
            +
                    port: remote_uri.port,
         | 
| 234 | 
            +
                    keepalive: KAMAL.config.ssh.options[:keepalive],
         | 
| 235 | 
            +
                    keepalive_interval: KAMAL.config.ssh.options[:keepalive_interval],
         | 
| 236 | 
            +
                    logger: KAMAL.config.ssh.options[:logger]
         | 
| 237 | 
            +
                  }.compact
         | 
| 238 | 
            +
                end
         | 
| 222 239 | 
             
            end
         | 
    
        data/lib/kamal/cli/registry.rb
    CHANGED
    
    | @@ -24,4 +24,26 @@ class Kamal::Cli::Registry < Kamal::Cli::Base | |
| 24 24 | 
             
                  on(KAMAL.hosts) { execute *KAMAL.registry.logout } unless options[:skip_remote]
         | 
| 25 25 | 
             
                end
         | 
| 26 26 | 
             
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              desc "login", "Log in to remote registry locally and remotely"
         | 
| 29 | 
            +
              option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
         | 
| 30 | 
            +
              option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
         | 
| 31 | 
            +
              def login
         | 
| 32 | 
            +
                if KAMAL.registry.local?
         | 
| 33 | 
            +
                  raise "Cannot use login command with a local registry. Use `kamal registry setup` instead."
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                setup
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              desc "logout", "Log out of remote registry locally and remotely"
         | 
| 40 | 
            +
              option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
         | 
| 41 | 
            +
              option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
         | 
| 42 | 
            +
              def logout
         | 
| 43 | 
            +
                if KAMAL.registry.local?
         | 
| 44 | 
            +
                  raise "Cannot use logout command with a local registry. Use `kamal registry remove` instead."
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                remove
         | 
| 48 | 
            +
              end
         | 
| 27 49 | 
             
            end
         | 
| @@ -127,6 +127,16 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base | |
| 127 127 | 
             
                  config.builder
         | 
| 128 128 | 
             
                end
         | 
| 129 129 |  | 
| 130 | 
            +
                def registry_config
         | 
| 131 | 
            +
                  config.registry
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                def driver_options
         | 
| 135 | 
            +
                  if registry_config.local?
         | 
| 136 | 
            +
                    [ "--driver-opt", "network=host" ]
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
             | 
| 130 140 | 
             
                def platform_options(arches)
         | 
| 131 141 | 
             
                  argumentize "--platform", arches.map { |arch| "linux/#{arch}" }.join(",") if arches.any?
         | 
| 132 142 | 
             
                end
         | 
| @@ -8,14 +8,14 @@ class Kamal::Commands::Builder::Hybrid < Kamal::Commands::Builder::Remote | |
| 8 8 |  | 
| 9 9 | 
             
              private
         | 
| 10 10 | 
             
                def builder_name
         | 
| 11 | 
            -
                  "kamal-hybrid-#{driver}-#{ | 
| 11 | 
            +
                  "kamal-hybrid-#{driver}-#{remote_builder_name_suffix}"
         | 
| 12 12 | 
             
                end
         | 
| 13 13 |  | 
| 14 14 | 
             
                def create_local_buildx
         | 
| 15 | 
            -
                  docker :buildx, :create, *platform_options(local_arches), "--name", builder_name, "--driver=#{driver}"
         | 
| 15 | 
            +
                  docker :buildx, :create, *platform_options(local_arches), "--name", builder_name, "--driver=#{driver}", *driver_options
         | 
| 16 16 | 
             
                end
         | 
| 17 17 |  | 
| 18 18 | 
             
                def append_remote_buildx
         | 
| 19 | 
            -
                  docker :buildx, :create, *platform_options(remote_arches), "--append", "--name", builder_name, remote_context_name
         | 
| 19 | 
            +
                  docker :buildx, :create, *platform_options(remote_arches), "--append", "--name", builder_name, *driver_options, remote_context_name
         | 
| 20 20 | 
             
                end
         | 
| 21 21 | 
             
            end
         | 
| @@ -2,14 +2,7 @@ class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base | |
| 2 2 | 
             
              def create
         | 
| 3 3 | 
             
                return if docker_driver?
         | 
| 4 4 |  | 
| 5 | 
            -
                 | 
| 6 | 
            -
                  if KAMAL.registry.local?
         | 
| 7 | 
            -
                    "--driver=#{driver} --driver-opt network=host"
         | 
| 8 | 
            -
                  else
         | 
| 9 | 
            -
                    "--driver=#{driver}"
         | 
| 10 | 
            -
                  end
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                docker :buildx, :create, "--name", builder_name, options
         | 
| 5 | 
            +
                docker :buildx, :create, "--name", builder_name, "--driver=#{driver}", *driver_options
         | 
| 13 6 | 
             
              end
         | 
| 14 7 |  | 
| 15 8 | 
             
              def remove
         | 
| @@ -18,7 +11,7 @@ class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base | |
| 18 11 |  | 
| 19 12 | 
             
              private
         | 
| 20 13 | 
             
                def builder_name
         | 
| 21 | 
            -
                  if  | 
| 14 | 
            +
                  if registry_config.local?
         | 
| 22 15 | 
             
                    "kamal-local-registry-#{driver}"
         | 
| 23 16 | 
             
                  else
         | 
| 24 17 | 
             
                    "kamal-local-#{driver}"
         | 
| @@ -34,13 +34,17 @@ class Kamal::Commands::Builder::Remote < Kamal::Commands::Builder::Base | |
| 34 34 |  | 
| 35 35 | 
             
              private
         | 
| 36 36 | 
             
                def builder_name
         | 
| 37 | 
            -
                  "kamal-remote-#{ | 
| 37 | 
            +
                  "kamal-remote-#{remote_builder_name_suffix}"
         | 
| 38 38 | 
             
                end
         | 
| 39 39 |  | 
| 40 40 | 
             
                def remote_context_name
         | 
| 41 41 | 
             
                  "#{builder_name}-context"
         | 
| 42 42 | 
             
                end
         | 
| 43 43 |  | 
| 44 | 
            +
                def remote_builder_name_suffix
         | 
| 45 | 
            +
                  "#{remote.gsub(/[^a-z0-9_-]/, "-")}#{registry_config.local? ? "-local-registry" : "" }"
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 44 48 | 
             
                def inspect_buildx
         | 
| 45 49 | 
             
                  pipe \
         | 
| 46 50 | 
             
                    docker(:buildx, :inspect, builder_name),
         | 
| @@ -62,7 +66,7 @@ class Kamal::Commands::Builder::Remote < Kamal::Commands::Builder::Base | |
| 62 66 | 
             
                end
         | 
| 63 67 |  | 
| 64 68 | 
             
                def create_buildx
         | 
| 65 | 
            -
                  docker :buildx, :create, "--name", builder_name, remote_context_name
         | 
| 69 | 
            +
                  docker :buildx, :create, "--name", builder_name, *driver_options, remote_context_name
         | 
| 66 70 | 
             
                end
         | 
| 67 71 |  | 
| 68 72 | 
             
                def remove_buildx
         | 
| @@ -1,19 +1,27 @@ | |
| 1 1 | 
             
            # Registry
         | 
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # The default registry is Docker Hub, but you can change it using `registry/server`.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # Using a local container registry
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # If the registry server starts with `localhost`, Kamal will start a local Docker registry
         | 
| 8 | 
            +
            # on that port and push the app image to it.
         | 
| 9 | 
            +
            registry:
         | 
| 10 | 
            +
              server: localhost:5555
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            # Using Docker Hub as the container registry
         | 
| 4 13 | 
             
            #
         | 
| 5 14 | 
             
            # By default, Docker Hub creates public repositories. To avoid making your images public,
         | 
| 6 15 | 
             
            # set up a private repository before deploying, or change the default repository privacy
         | 
| 7 16 | 
             
            # settings to private in your [Docker Hub settings](https://hub.docker.com/repository-settings/default-privacy).
         | 
| 8 17 | 
             
            #
         | 
| 9 | 
            -
            # A reference to a secret (in this case, ` | 
| 18 | 
            +
            # A reference to a secret (in this case, `KAMAL_REGISTRY_PASSWORD`) will look up the secret
         | 
| 10 19 | 
             
            # in the local environment:
         | 
| 11 20 | 
             
            registry:
         | 
| 12 | 
            -
              server: registry.digitalocean.com
         | 
| 13 21 | 
             
              username:
         | 
| 14 | 
            -
                -  | 
| 22 | 
            +
                - <your docker hub username>
         | 
| 15 23 | 
             
              password:
         | 
| 16 | 
            -
                -  | 
| 24 | 
            +
                - KAMAL_REGISTRY_PASSWORD
         | 
| 17 25 |  | 
| 18 26 | 
             
            # Using AWS ECR as the container registry
         | 
| 19 27 | 
             
            #
         | 
    
        data/lib/kamal/configuration.rb
    CHANGED
    
    | @@ -76,6 +76,7 @@ class Kamal::Configuration | |
| 76 76 | 
             
                ensure_no_traefik_reboot_hooks
         | 
| 77 77 | 
             
                ensure_one_host_for_ssl_roles
         | 
| 78 78 | 
             
                ensure_unique_hosts_for_ssl_roles
         | 
| 79 | 
            +
                ensure_local_registry_remote_builder_has_ssh_url
         | 
| 79 80 | 
             
              end
         | 
| 80 81 |  | 
| 81 82 | 
             
              def version=(version)
         | 
| @@ -363,6 +364,16 @@ class Kamal::Configuration | |
| 363 364 | 
             
                  true
         | 
| 364 365 | 
             
                end
         | 
| 365 366 |  | 
| 367 | 
            +
                def ensure_local_registry_remote_builder_has_ssh_url
         | 
| 368 | 
            +
                  if registry.local? && builder.remote?
         | 
| 369 | 
            +
                    unless URI(builder.remote).scheme == "ssh"
         | 
| 370 | 
            +
                      raise Kamal::ConfigurationError, "Local registry with remote builder requires an SSH URL (e.g., ssh://user@host)"
         | 
| 371 | 
            +
                    end
         | 
| 372 | 
            +
                  end
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                  true
         | 
| 375 | 
            +
                end
         | 
| 376 | 
            +
             | 
| 366 377 | 
             
                def role_names
         | 
| 367 378 | 
             
                  raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
         | 
| 368 379 | 
             
                end
         | 
    
        data/lib/kamal/version.rb
    CHANGED
    
    
    
        data/lib/kamal.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: kamal
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2.8. | 
| 4 | 
            +
              version: 2.8.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - David Heinemeier Hansson
         | 
| @@ -224,12 +224,12 @@ files: | |
| 224 224 | 
             
            - lib/kamal/cli/base.rb
         | 
| 225 225 | 
             
            - lib/kamal/cli/build.rb
         | 
| 226 226 | 
             
            - lib/kamal/cli/build/clone.rb
         | 
| 227 | 
            +
            - lib/kamal/cli/build/port_forwarding.rb
         | 
| 227 228 | 
             
            - lib/kamal/cli/healthcheck/barrier.rb
         | 
| 228 229 | 
             
            - lib/kamal/cli/healthcheck/error.rb
         | 
| 229 230 | 
             
            - lib/kamal/cli/healthcheck/poller.rb
         | 
| 230 231 | 
             
            - lib/kamal/cli/lock.rb
         | 
| 231 232 | 
             
            - lib/kamal/cli/main.rb
         | 
| 232 | 
            -
            - lib/kamal/cli/port_forwarding.rb
         | 
| 233 233 | 
             
            - lib/kamal/cli/proxy.rb
         | 
| 234 234 | 
             
            - lib/kamal/cli/prune.rb
         | 
| 235 235 | 
             
            - lib/kamal/cli/registry.rb
         | 
| @@ -1,55 +0,0 @@ | |
| 1 | 
            -
            require "concurrent/atomic/count_down_latch"
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            class Kamal::Cli::PortForwarding
         | 
| 4 | 
            -
              attr_reader :hosts, :port
         | 
| 5 | 
            -
             | 
| 6 | 
            -
              def initialize(hosts, port)
         | 
| 7 | 
            -
                @hosts = hosts
         | 
| 8 | 
            -
                @port = port
         | 
| 9 | 
            -
              end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
              def forward
         | 
| 12 | 
            -
                @done = false
         | 
| 13 | 
            -
                forward_ports
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                yield
         | 
| 16 | 
            -
              ensure
         | 
| 17 | 
            -
                stop
         | 
| 18 | 
            -
              end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
              private
         | 
| 21 | 
            -
             | 
| 22 | 
            -
              def stop
         | 
| 23 | 
            -
                @done = true
         | 
| 24 | 
            -
                @threads.to_a.each(&:join)
         | 
| 25 | 
            -
              end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
              def forward_ports
         | 
| 28 | 
            -
                ready = Concurrent::CountDownLatch.new(hosts.size)
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                @threads = hosts.map do |host|
         | 
| 31 | 
            -
                  Thread.new do
         | 
| 32 | 
            -
                    Net::SSH.start(host, KAMAL.config.ssh.user, **{ proxy: KAMAL.config.ssh.proxy }.compact) do |ssh|
         | 
| 33 | 
            -
                      ssh.forward.remote(port, "localhost", port, "127.0.0.1") do |remote_port, bind_address|
         | 
| 34 | 
            -
                        if remote_port == :error
         | 
| 35 | 
            -
                          raise "Failed to establish port forward on #{host}"
         | 
| 36 | 
            -
                        else
         | 
| 37 | 
            -
                          ready.count_down
         | 
| 38 | 
            -
                        end
         | 
| 39 | 
            -
                      end
         | 
| 40 | 
            -
             | 
| 41 | 
            -
                      ssh.loop(0.1) do
         | 
| 42 | 
            -
                        if @done
         | 
| 43 | 
            -
                          ssh.forward.cancel_remote(port, "127.0.0.1")
         | 
| 44 | 
            -
                          break
         | 
| 45 | 
            -
                        else
         | 
| 46 | 
            -
                          true
         | 
| 47 | 
            -
                        end
         | 
| 48 | 
            -
                      end
         | 
| 49 | 
            -
                    end
         | 
| 50 | 
            -
                  end
         | 
| 51 | 
            -
                end
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                raise "Timed out waiting for port forwarding to be established" unless ready.wait(10)
         | 
| 54 | 
            -
              end
         | 
| 55 | 
            -
            end
         |