Compare commits
1 Commits
v2.3.0
...
proxy-with
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46e3085052 |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -27,7 +27,6 @@ jobs:
|
|||||||
- "3.1"
|
- "3.1"
|
||||||
- "3.2"
|
- "3.2"
|
||||||
- "3.3"
|
- "3.3"
|
||||||
- "3.4.0-preview2"
|
|
||||||
gemfile:
|
gemfile:
|
||||||
- Gemfile
|
- Gemfile
|
||||||
- gemfiles/rails_edge.gemfile
|
- gemfiles/rails_edge.gemfile
|
||||||
@@ -42,9 +41,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Remove gemfile.lock
|
|
||||||
run: rm Gemfile.lock
|
|
||||||
|
|
||||||
- name: Install Ruby
|
- name: Install Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
@@ -53,5 +49,3 @@ jobs:
|
|||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: bin/test
|
run: bin/test
|
||||||
env:
|
|
||||||
RUBYOPT: ${{ startsWith(matrix.ruby-version, '3.4.') && '--enable=frozen-string-literal' || '' }}
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
FROM ruby:3.3-alpine
|
# Use the official Ruby 3.2.0 Alpine image as the base image
|
||||||
|
FROM ruby:3.2.0-alpine
|
||||||
|
|
||||||
# Install docker/buildx-bin
|
# Install docker/buildx-bin
|
||||||
COPY --from=docker/buildx-bin /buildx /usr/libexec/docker/cli-plugins/docker-buildx
|
COPY --from=docker/buildx-bin /buildx /usr/libexec/docker/cli-plugins/docker-buildx
|
||||||
|
|||||||
42
Gemfile.lock
42
Gemfile.lock
@@ -1,24 +1,24 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
kamal (2.3.0)
|
kamal (2.1.1)
|
||||||
activesupport (>= 7.0)
|
activesupport (>= 7.0)
|
||||||
base64 (~> 0.2)
|
base64 (~> 0.2)
|
||||||
bcrypt_pbkdf (~> 1.0)
|
bcrypt_pbkdf (~> 1.0)
|
||||||
concurrent-ruby (~> 1.2)
|
concurrent-ruby (~> 1.2)
|
||||||
dotenv (~> 3.1)
|
dotenv (~> 3.1)
|
||||||
ed25519 (~> 1.2)
|
ed25519 (~> 1.2)
|
||||||
net-ssh (~> 7.3)
|
net-ssh (~> 7.0)
|
||||||
sshkit (>= 1.23.0, < 2.0)
|
sshkit (>= 1.23.0, < 2.0)
|
||||||
thor (~> 1.3)
|
thor (~> 1.3)
|
||||||
zeitwerk (>= 2.6.18, < 3.0)
|
zeitwerk (~> 2.5)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actionpack (7.1.4.1)
|
actionpack (7.1.3.4)
|
||||||
actionview (= 7.1.4.1)
|
actionview (= 7.1.3.4)
|
||||||
activesupport (= 7.1.4.1)
|
activesupport (= 7.1.3.4)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
racc
|
racc
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
@@ -26,13 +26,13 @@ GEM
|
|||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
actionview (7.1.4.1)
|
actionview (7.1.3.4)
|
||||||
activesupport (= 7.1.4.1)
|
activesupport (= 7.1.3.4)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
activesupport (7.1.4.1)
|
activesupport (7.1.3.4)
|
||||||
base64
|
base64
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
@@ -49,7 +49,7 @@ GEM
|
|||||||
bcrypt_pbkdf (1.1.1-x86_64-darwin)
|
bcrypt_pbkdf (1.1.1-x86_64-darwin)
|
||||||
bigdecimal (3.1.8)
|
bigdecimal (3.1.8)
|
||||||
builder (3.3.0)
|
builder (3.3.0)
|
||||||
concurrent-ruby (1.3.4)
|
concurrent-ruby (1.3.3)
|
||||||
connection_pool (2.4.1)
|
connection_pool (2.4.1)
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
debug (1.9.2)
|
debug (1.9.2)
|
||||||
@@ -59,7 +59,7 @@ GEM
|
|||||||
drb (2.2.1)
|
drb (2.2.1)
|
||||||
ed25519 (1.3.0)
|
ed25519 (1.3.0)
|
||||||
erubi (1.13.0)
|
erubi (1.13.0)
|
||||||
i18n (1.14.6)
|
i18n (1.14.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
io-console (0.7.2)
|
io-console (0.7.2)
|
||||||
irb (1.14.0)
|
irb (1.14.0)
|
||||||
@@ -70,7 +70,7 @@ GEM
|
|||||||
loofah (2.22.0)
|
loofah (2.22.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
minitest (5.25.1)
|
minitest (5.24.1)
|
||||||
mocha (2.4.5)
|
mocha (2.4.5)
|
||||||
ruby2_keywords (>= 0.0.5)
|
ruby2_keywords (>= 0.0.5)
|
||||||
mutex_m (0.2.0)
|
mutex_m (0.2.0)
|
||||||
@@ -78,7 +78,7 @@ GEM
|
|||||||
net-ssh (>= 2.6.5, < 8.0.0)
|
net-ssh (>= 2.6.5, < 8.0.0)
|
||||||
net-sftp (4.0.0)
|
net-sftp (4.0.0)
|
||||||
net-ssh (>= 5.0.0, < 8.0.0)
|
net-ssh (>= 5.0.0, < 8.0.0)
|
||||||
net-ssh (7.3.0)
|
net-ssh (7.2.3)
|
||||||
nokogiri (1.16.7-arm64-darwin)
|
nokogiri (1.16.7-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.7-x86_64-darwin)
|
nokogiri (1.16.7-x86_64-darwin)
|
||||||
@@ -92,7 +92,7 @@ GEM
|
|||||||
psych (5.1.2)
|
psych (5.1.2)
|
||||||
stringio
|
stringio
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
rack (3.1.8)
|
rack (3.1.7)
|
||||||
rack-session (2.0.0)
|
rack-session (2.0.0)
|
||||||
rack (>= 3.0.0)
|
rack (>= 3.0.0)
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
@@ -107,9 +107,9 @@ GEM
|
|||||||
rails-html-sanitizer (1.6.0)
|
rails-html-sanitizer (1.6.0)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.21)
|
||||||
nokogiri (~> 1.14)
|
nokogiri (~> 1.14)
|
||||||
railties (7.1.4.1)
|
railties (7.1.3.4)
|
||||||
actionpack (= 7.1.4.1)
|
actionpack (= 7.1.3.4)
|
||||||
activesupport (= 7.1.4.1)
|
activesupport (= 7.1.3.4)
|
||||||
irb
|
irb
|
||||||
rackup (>= 1.0.0)
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
@@ -122,7 +122,8 @@ GEM
|
|||||||
regexp_parser (2.9.2)
|
regexp_parser (2.9.2)
|
||||||
reline (0.5.9)
|
reline (0.5.9)
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
rexml (3.3.9)
|
rexml (3.3.4)
|
||||||
|
strscan
|
||||||
rubocop (1.65.1)
|
rubocop (1.65.1)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (>= 3.17.0)
|
language_server-protocol (>= 3.17.0)
|
||||||
@@ -160,12 +161,13 @@ GEM
|
|||||||
net-sftp (>= 2.1.2)
|
net-sftp (>= 2.1.2)
|
||||||
net-ssh (>= 2.8.0)
|
net-ssh (>= 2.8.0)
|
||||||
stringio (3.1.1)
|
stringio (3.1.1)
|
||||||
|
strscan (3.1.0)
|
||||||
thor (1.3.1)
|
thor (1.3.1)
|
||||||
tzinfo (2.0.6)
|
tzinfo (2.0.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
unicode-display_width (2.5.0)
|
unicode-display_width (2.5.0)
|
||||||
webrick (1.8.2)
|
webrick (1.8.1)
|
||||||
zeitwerk (2.7.1)
|
zeitwerk (2.6.17)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
arm64-darwin
|
arm64-darwin
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ Gem::Specification.new do |spec|
|
|||||||
|
|
||||||
spec.add_dependency "activesupport", ">= 7.0"
|
spec.add_dependency "activesupport", ">= 7.0"
|
||||||
spec.add_dependency "sshkit", ">= 1.23.0", "< 2.0"
|
spec.add_dependency "sshkit", ">= 1.23.0", "< 2.0"
|
||||||
spec.add_dependency "net-ssh", "~> 7.3"
|
spec.add_dependency "net-ssh", "~> 7.0"
|
||||||
spec.add_dependency "thor", "~> 1.3"
|
spec.add_dependency "thor", "~> 1.3"
|
||||||
spec.add_dependency "dotenv", "~> 3.1"
|
spec.add_dependency "dotenv", "~> 3.1"
|
||||||
spec.add_dependency "zeitwerk", ">= 2.6.18", "< 3.0"
|
spec.add_dependency "zeitwerk", "~> 2.5"
|
||||||
spec.add_dependency "ed25519", "~> 1.2"
|
spec.add_dependency "ed25519", "~> 1.2"
|
||||||
spec.add_dependency "bcrypt_pbkdf", "~> 1.0"
|
spec.add_dependency "bcrypt_pbkdf", "~> 1.0"
|
||||||
spec.add_dependency "concurrent-ruby", "~> 1.2"
|
spec.add_dependency "concurrent-ruby", "~> 1.2"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
require "active_support/core_ext/array/conversions"
|
|
||||||
|
|
||||||
class Kamal::Cli::Accessory < Kamal::Cli::Base
|
class Kamal::Cli::Accessory < Kamal::Cli::Base
|
||||||
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
|
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
|
||||||
def boot(name, prepare: true)
|
def boot(name, prepare: true)
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|||||||
puts "No documentation found for #{section}"
|
puts "No documentation found for #{section}"
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "init", "Create config stub in config/deploy.yml and secrets stub in .kamal"
|
desc "init", "Create config stub in config/deploy.yml and env stub in .env"
|
||||||
option :bundle, type: :boolean, default: false, desc: "Add Kamal to the Gemfile and create a bin/kamal binstub"
|
option :bundle, type: :boolean, default: false, desc: "Add Kamal to the Gemfile and create a bin/kamal binstub"
|
||||||
def init
|
def init
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
|||||||
@@ -14,25 +14,23 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|||||||
version = capture_with_info(*KAMAL.proxy.version).strip.presence
|
version = capture_with_info(*KAMAL.proxy.version).strip.presence
|
||||||
|
|
||||||
if version && Kamal::Utils.older_version?(version, Kamal::Configuration::PROXY_MINIMUM_VERSION)
|
if version && Kamal::Utils.older_version?(version, Kamal::Configuration::PROXY_MINIMUM_VERSION)
|
||||||
raise "kamal-proxy version #{version} is too old, run `kamal proxy reboot` in order to update to at least #{Kamal::Configuration::PROXY_MINIMUM_VERSION}"
|
raise "kamal-proxy version #{version} is too old, please reboot to update to at least #{Kamal::Configuration::PROXY_MINIMUM_VERSION}"
|
||||||
end
|
end
|
||||||
execute *KAMAL.proxy.start_or_run
|
execute *KAMAL.proxy.start_or_run
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "boot_config <set|get|reset>", "Manage kamal-proxy boot configuration"
|
desc "boot_config <set|get|clear>", "Mange kamal-proxy boot configuration"
|
||||||
option :publish, type: :boolean, default: true, desc: "Publish the proxy ports on the host"
|
option :publish, type: :boolean, default: true, desc: "Publish the proxy ports on the host"
|
||||||
option :http_port, type: :numeric, default: Kamal::Configuration::PROXY_HTTP_PORT, desc: "HTTP port to publish on the host"
|
option :http_port, type: :numeric, default: Kamal::Configuration::PROXY_HTTP_PORT, desc: "HTTP port to publish on the host"
|
||||||
option :https_port, type: :numeric, default: Kamal::Configuration::PROXY_HTTPS_PORT, desc: "HTTPS port to publish on the host"
|
option :https_port, type: :numeric, default: Kamal::Configuration::PROXY_HTTPS_PORT, desc: "HTTPS port to publish on the host"
|
||||||
option :log_max_size, type: :string, default: Kamal::Configuration::PROXY_LOG_MAX_SIZE, desc: "Max size of proxy logs"
|
|
||||||
option :docker_options, type: :array, default: [], desc: "Docker options to pass to the proxy container", banner: "option=value option2=value2"
|
option :docker_options, type: :array, default: [], desc: "Docker options to pass to the proxy container", banner: "option=value option2=value2"
|
||||||
def boot_config(subcommand)
|
def boot_config(subcommand)
|
||||||
case subcommand
|
case subcommand
|
||||||
when "set"
|
when "set"
|
||||||
boot_options = [
|
boot_options = [
|
||||||
*(KAMAL.config.proxy_publish_args(options[:http_port], options[:https_port]) if options[:publish]),
|
*(KAMAL.config.proxy_publish_args(options[:http_port], options[:https_port]) if options[:publish]),
|
||||||
*(KAMAL.config.proxy_logging_args(options[:log_max_size])),
|
|
||||||
*options[:docker_options].map { |option| "--#{option}" }
|
*options[:docker_options].map { |option| "--#{option}" }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,12 @@ servers:
|
|||||||
# - 192.168.0.1
|
# - 192.168.0.1
|
||||||
# cmd: bin/jobs
|
# cmd: bin/jobs
|
||||||
|
|
||||||
# Enable SSL auto certification via Let's Encrypt and allow for multiple apps on a single web server.
|
# Enable SSL auto certification via Let's Encrypt (and allow for multiple apps on one server).
|
||||||
# Remove this section when using multiple web servers and ensure you terminate SSL at your load balancer.
|
# Set ssl: false if using something like Cloudflare to terminate SSL (but keep host!).
|
||||||
#
|
proxy:
|
||||||
# Note: If using Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
|
|
||||||
proxy:
|
|
||||||
ssl: true
|
ssl: true
|
||||||
host: app.example.com
|
host: app.example.com
|
||||||
# Proxy connects to your container on port 80 by default.
|
# kamal-proxy connects to your container over port 80, use `app_port` to specify a different port.
|
||||||
# app_port: 3000
|
# app_port: 3000
|
||||||
|
|
||||||
# Credentials for your image host.
|
# Credentials for your image host.
|
||||||
@@ -91,7 +89,7 @@ builder:
|
|||||||
# directories:
|
# directories:
|
||||||
# - data:/var/lib/mysql
|
# - data:/var/lib/mysql
|
||||||
# redis:
|
# redis:
|
||||||
# image: valkey/valkey:8
|
# image: redis:7.0
|
||||||
# host: 192.168.0.2
|
# host: 192.168.0.2
|
||||||
# port: 6379
|
# port: 6379
|
||||||
# directories:
|
# directories:
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
echo "Docker set up on $KAMAL_HOSTS..."
|
# A sample docker-setup hook
|
||||||
|
#
|
||||||
|
# Sets up a Docker network on defined hosts which can then be used by the application’s containers
|
||||||
|
|
||||||
|
hosts = ENV["KAMAL_HOSTS"].split(",")
|
||||||
|
|
||||||
|
hosts.each do |ip|
|
||||||
|
destination = "root@#{ip}"
|
||||||
|
puts "Creating a Docker network \"kamal\" on #{destination}"
|
||||||
|
`ssh #{destination} docker network create kamal`
|
||||||
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Kamal::Commands::Accessory < Kamal::Commands::Base
|
class Kamal::Commands::Accessory < Kamal::Commands::Base
|
||||||
attr_reader :accessory_config
|
attr_reader :accessory_config
|
||||||
delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
|
delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
|
||||||
:network_args, :publish_args, :env_args, :volume_args, :label_args, :option_args,
|
:publish_args, :env_args, :volume_args, :label_args, :option_args,
|
||||||
:secrets_io, :secrets_path, :env_directory,
|
:secrets_io, :secrets_path, :env_directory,
|
||||||
to: :accessory_config
|
to: :accessory_config
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|||||||
"--name", service_name,
|
"--name", service_name,
|
||||||
"--detach",
|
"--detach",
|
||||||
"--restart", "unless-stopped",
|
"--restart", "unless-stopped",
|
||||||
*network_args,
|
"--network", "kamal",
|
||||||
*config.logging_args,
|
*config.logging_args,
|
||||||
*publish_args,
|
*publish_args,
|
||||||
*env_args,
|
*env_args,
|
||||||
@@ -64,7 +64,7 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|||||||
docker :run,
|
docker :run,
|
||||||
("-it" if interactive),
|
("-it" if interactive),
|
||||||
"--rm",
|
"--rm",
|
||||||
*network_args,
|
"--network", "kamal",
|
||||||
*env_args,
|
*env_args,
|
||||||
*volume_args,
|
*volume_args,
|
||||||
image,
|
image,
|
||||||
|
|||||||
@@ -11,7 +11,14 @@ module Kamal::Commands
|
|||||||
end
|
end
|
||||||
|
|
||||||
def run_over_ssh(*command, host:)
|
def run_over_ssh(*command, host:)
|
||||||
"ssh#{ssh_proxy_args} -t #{config.ssh.user}@#{host} -p #{config.ssh.port} '#{command.join(" ").gsub("'", "'\\\\''")}'"
|
"ssh".tap do |cmd|
|
||||||
|
if config.ssh.proxy && config.ssh.proxy.is_a?(Net::SSH::Proxy::Jump)
|
||||||
|
cmd << " -J #{config.ssh.proxy.jump_proxies}"
|
||||||
|
elsif config.ssh.proxy && config.ssh.proxy.is_a?(Net::SSH::Proxy::Command)
|
||||||
|
cmd << " -o ProxyCommand='#{config.ssh.proxy.command_line_template}'"
|
||||||
|
end
|
||||||
|
cmd << " -t #{config.ssh.user}@#{host} -p #{config.ssh.port} '#{command.join(" ").gsub("'", "'\\\\''")}'"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def container_id_for(container_name:, only_running: false)
|
def container_id_for(container_name:, only_running: false)
|
||||||
@@ -85,14 +92,5 @@ module Kamal::Commands
|
|||||||
def tags(**details)
|
def tags(**details)
|
||||||
Kamal::Tags.from_config(config, **details)
|
Kamal::Tags.from_config(config, **details)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ssh_proxy_args
|
|
||||||
case config.ssh.proxy
|
|
||||||
when Net::SSH::Proxy::Jump
|
|
||||||
" -J #{config.ssh.proxy.jump_proxies}"
|
|
||||||
when Net::SSH::Proxy::Command
|
|
||||||
" -o ProxyCommand='#{config.ssh.proxy.command_line_template}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
delegate :argumentize, to: Kamal::Utils
|
delegate :argumentize, to: Kamal::Utils
|
||||||
delegate \
|
delegate \
|
||||||
:args, :secrets, :dockerfile, :target, :arches, :local_arches, :remote_arches, :remote,
|
:args, :secrets, :dockerfile, :target, :arches, :local_arches, :remote_arches, :remote,
|
||||||
:cache_from, :cache_to, :ssh, :provenance, :driver, :docker_driver?,
|
:cache_from, :cache_to, :ssh, :driver, :docker_driver?,
|
||||||
to: :builder_config
|
to: :builder_config
|
||||||
|
|
||||||
def clean
|
def clean
|
||||||
@@ -37,7 +37,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_options
|
def build_options
|
||||||
[ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh, *builder_provenance ]
|
[ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_context
|
def build_context
|
||||||
@@ -97,10 +97,6 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
argumentize "--ssh", ssh if ssh.present?
|
argumentize "--ssh", ssh if ssh.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def builder_provenance
|
|
||||||
argumentize "--provenance", provenance unless provenance.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def builder_config
|
def builder_config
|
||||||
config.builder
|
config.builder
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
module Kamal::Commands::Builder::Clone
|
module Kamal::Commands::Builder::Clone
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
delegate :clone_directory, :build_directory, to: :"config.builder"
|
||||||
|
end
|
||||||
|
|
||||||
def clone
|
def clone
|
||||||
git :clone, escaped_root, "--recurse-submodules", path: config.builder.clone_directory.shellescape
|
git :clone, Kamal::Git.root, "--recurse-submodules", path: clone_directory
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone_reset_steps
|
def clone_reset_steps
|
||||||
[
|
[
|
||||||
git(:remote, "set-url", :origin, escaped_root, path: escaped_build_directory),
|
git(:remote, "set-url", :origin, Kamal::Git.root, path: build_directory),
|
||||||
git(:fetch, :origin, path: escaped_build_directory),
|
git(:fetch, :origin, path: build_directory),
|
||||||
git(:reset, "--hard", Kamal::Git.revision, path: escaped_build_directory),
|
git(:reset, "--hard", Kamal::Git.revision, path: build_directory),
|
||||||
git(:clean, "-fdx", path: escaped_build_directory),
|
git(:clean, "-fdx", path: build_directory),
|
||||||
git(:submodule, :update, "--init", path: escaped_build_directory)
|
git(:submodule, :update, "--init", path: build_directory)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone_status
|
def clone_status
|
||||||
git :status, "--porcelain", path: escaped_build_directory
|
git :status, "--porcelain", path: build_directory
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone_revision
|
def clone_revision
|
||||||
git :"rev-parse", :HEAD, path: escaped_build_directory
|
git :"rev-parse", :HEAD, path: build_directory
|
||||||
end
|
|
||||||
|
|
||||||
def escaped_root
|
|
||||||
Kamal::Git.root.shellescape
|
|
||||||
end
|
|
||||||
|
|
||||||
def escaped_build_directory
|
|
||||||
config.builder.build_directory.shellescape
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,10 +14,9 @@ class Kamal::Configuration
|
|||||||
|
|
||||||
include Validation
|
include Validation
|
||||||
|
|
||||||
PROXY_MINIMUM_VERSION = "v0.8.2"
|
PROXY_MINIMUM_VERSION = "v0.7.0"
|
||||||
PROXY_HTTP_PORT = 80
|
PROXY_HTTP_PORT = 80
|
||||||
PROXY_HTTPS_PORT = 443
|
PROXY_HTTPS_PORT = 443
|
||||||
PROXY_LOG_MAX_SIZE = "10m"
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def create_from(config_file:, destination: nil, version: nil)
|
def create_from(config_file:, destination: nil, version: nil)
|
||||||
@@ -253,12 +252,8 @@ class Kamal::Configuration
|
|||||||
argumentize "--publish", [ "#{http_port}:#{PROXY_HTTP_PORT}", "#{https_port}:#{PROXY_HTTPS_PORT}" ]
|
argumentize "--publish", [ "#{http_port}:#{PROXY_HTTP_PORT}", "#{https_port}:#{PROXY_HTTPS_PORT}" ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def proxy_logging_args(max_size)
|
|
||||||
argumentize "--log-opt", "max-size=#{max_size}" if max_size.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def proxy_options_default
|
def proxy_options_default
|
||||||
[ *proxy_publish_args(PROXY_HTTP_PORT, PROXY_HTTPS_PORT), *proxy_logging_args(PROXY_LOG_MAX_SIZE) ]
|
proxy_publish_args PROXY_HTTP_PORT, PROXY_HTTPS_PORT
|
||||||
end
|
end
|
||||||
|
|
||||||
def proxy_image
|
def proxy_image
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
class Kamal::Configuration::Accessory
|
class Kamal::Configuration::Accessory
|
||||||
include Kamal::Configuration::Validation
|
include Kamal::Configuration::Validation
|
||||||
|
|
||||||
DEFAULT_NETWORK = "kamal"
|
|
||||||
|
|
||||||
delegate :argumentize, :optionize, to: Kamal::Utils
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
||||||
|
|
||||||
attr_reader :name, :accessory_config, :env
|
attr_reader :name, :accessory_config, :env
|
||||||
@@ -40,10 +38,6 @@ class Kamal::Configuration::Accessory
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def network_args
|
|
||||||
argumentize "--network", network
|
|
||||||
end
|
|
||||||
|
|
||||||
def publish_args
|
def publish_args
|
||||||
argumentize "--publish", port if port
|
argumentize "--publish", port if port
|
||||||
end
|
end
|
||||||
@@ -179,8 +173,4 @@ class Kamal::Configuration::Accessory
|
|||||||
accessory_config["roles"].flat_map { |role| config.role(role).hosts }
|
accessory_config["roles"].flat_map { |role| config.role(role).hosts }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def network
|
|
||||||
accessory_config["network"] || DEFAULT_NETWORK
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -111,10 +111,6 @@ class Kamal::Configuration::Builder
|
|||||||
builder_config["ssh"]
|
builder_config["ssh"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def provenance
|
|
||||||
builder_config["provenance"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def git_clone?
|
def git_clone?
|
||||||
Kamal::Git.used? && builder_config["context"].nil?
|
Kamal::Git.used? && builder_config["context"].nil?
|
||||||
end
|
end
|
||||||
@@ -170,7 +166,7 @@ class Kamal::Configuration::Builder
|
|||||||
end
|
end
|
||||||
|
|
||||||
def cache_to_config_for_registry
|
def cache_to_config_for_registry
|
||||||
[ "type=registry", "ref=#{cache_image_ref}", builder_config["cache"]&.fetch("options", nil) ].compact.join(",")
|
[ "type=registry", builder_config["cache"]&.fetch("options", nil), "ref=#{cache_image_ref}" ].compact.join(",")
|
||||||
end
|
end
|
||||||
|
|
||||||
def repo_basename
|
def repo_basename
|
||||||
|
|||||||
@@ -90,11 +90,3 @@ accessories:
|
|||||||
# They are not created or copied before mounting:
|
# They are not created or copied before mounting:
|
||||||
volumes:
|
volumes:
|
||||||
- /path/to/mysql-logs:/var/log/mysql
|
- /path/to/mysql-logs:/var/log/mysql
|
||||||
|
|
||||||
# Network
|
|
||||||
#
|
|
||||||
# The network the accessory will be attached to.
|
|
||||||
#
|
|
||||||
# Defaults to kamal:
|
|
||||||
network: custom
|
|
||||||
|
|
||||||
|
|||||||
@@ -102,9 +102,3 @@ builder:
|
|||||||
#
|
#
|
||||||
# The build driver to use, defaults to `docker-container`:
|
# The build driver to use, defaults to `docker-container`:
|
||||||
driver: docker
|
driver: docker
|
||||||
|
|
||||||
# Provenance
|
|
||||||
#
|
|
||||||
# It is used to configure provenance attestations for the build result.
|
|
||||||
# The value can also be a boolean to enable or disable provenance attestations.
|
|
||||||
provenance: mode=max
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ ssh:
|
|||||||
|
|
||||||
# Proxy host
|
# Proxy host
|
||||||
#
|
#
|
||||||
# Specified in the form <host> or <user>@<host>:
|
# Specified in the form <host> or <user>@<host>
|
||||||
proxy: root@proxy-host
|
proxy: proxy-host
|
||||||
|
|
||||||
# Proxy command
|
# Proxy command
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class Kamal::Configuration::Proxy
|
|||||||
def deploy_options
|
def deploy_options
|
||||||
{
|
{
|
||||||
host: hosts,
|
host: hosts,
|
||||||
tls: proxy_config["ssl"].presence,
|
tls: proxy_config["ssl"],
|
||||||
"deploy-timeout": seconds_duration(config.deploy_timeout),
|
"deploy-timeout": seconds_duration(config.deploy_timeout),
|
||||||
"drain-timeout": seconds_duration(config.drain_timeout),
|
"drain-timeout": seconds_duration(config.drain_timeout),
|
||||||
"health-check-interval": seconds_duration(proxy_config.dig("healthcheck", "interval")),
|
"health-check-interval": seconds_duration(proxy_config.dig("healthcheck", "interval")),
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ class Kamal::Configuration::Ssh
|
|||||||
end
|
end
|
||||||
|
|
||||||
def proxy
|
def proxy
|
||||||
if (proxy = ssh_config["proxy"])
|
if proxy = ssh_config["proxy"]
|
||||||
Net::SSH::Proxy::Jump.new(proxy.include?("@") ? proxy : "root@#{proxy}")
|
Net::SSH::Proxy::Jump.new(proxy)
|
||||||
elsif (proxy_command = ssh_config["proxy_command"])
|
elsif proxy_command = ssh_config["proxy_command"]
|
||||||
Net::SSH::Proxy::Command.new(proxy_command)
|
Net::SSH::Proxy::Command.new(proxy_command)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ class Kamal::EnvFile
|
|||||||
def escape_docker_env_file_ascii_value(value)
|
def escape_docker_env_file_ascii_value(value)
|
||||||
# Doublequotes are treated literally in docker env files
|
# Doublequotes are treated literally in docker env files
|
||||||
# so remove leading and trailing ones and unescape any others
|
# so remove leading and trailing ones and unescape any others
|
||||||
value.to_s.dump[1..-2]
|
value.to_s.dump[1..-2].gsub(/\\"/, "\"")
|
||||||
.gsub(/\\"/, "\"")
|
|
||||||
.gsub(/\\#/, "#")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
require "dotenv"
|
require "dotenv"
|
||||||
|
|
||||||
class Kamal::Secrets
|
class Kamal::Secrets
|
||||||
|
attr_reader :secrets_files
|
||||||
|
|
||||||
Kamal::Secrets::Dotenv::InlineCommandSubstitution.install!
|
Kamal::Secrets::Dotenv::InlineCommandSubstitution.install!
|
||||||
|
|
||||||
def initialize(destination: nil)
|
def initialize(destination: nil)
|
||||||
@destination = destination
|
@secrets_files = \
|
||||||
|
[ ".kamal/secrets-common", ".kamal/secrets#{(".#{destination}" if destination)}" ].select { |f| File.exist?(f) }
|
||||||
@mutex = Mutex.new
|
@mutex = Mutex.new
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -14,10 +17,10 @@ class Kamal::Secrets
|
|||||||
secrets.fetch(key)
|
secrets.fetch(key)
|
||||||
end
|
end
|
||||||
rescue KeyError
|
rescue KeyError
|
||||||
if secrets_files.present?
|
if secrets_files
|
||||||
raise Kamal::ConfigurationError, "Secret '#{key}' not found in #{secrets_files.join(", ")}"
|
raise Kamal::ConfigurationError, "Secret '#{key}' not found in #{secrets_files.join(", ")}"
|
||||||
else
|
else
|
||||||
raise Kamal::ConfigurationError, "Secret '#{key}' not found, no secret files (#{secrets_filenames.join(", ")}) provided"
|
raise Kamal::ConfigurationError, "Secret '#{key}' not found, no secret files provided"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -25,18 +28,10 @@ class Kamal::Secrets
|
|||||||
secrets
|
secrets
|
||||||
end
|
end
|
||||||
|
|
||||||
def secrets_files
|
|
||||||
@secrets_files ||= secrets_filenames.select { |f| File.exist?(f) }
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def secrets
|
def secrets
|
||||||
@secrets ||= secrets_files.inject({}) do |secrets, secrets_file|
|
@secrets ||= secrets_files.inject({}) do |secrets, secrets_file|
|
||||||
secrets.merge!(::Dotenv.parse(secrets_file))
|
secrets.merge!(::Dotenv.parse(secrets_file))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def secrets_filenames
|
|
||||||
[ ".kamal/secrets-common", ".kamal/secrets#{(".#{@destination}" if @destination)}" ]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ class Kamal::Secrets::Adapters::Base
|
|||||||
delegate :optionize, to: Kamal::Utils
|
delegate :optionize, to: Kamal::Utils
|
||||||
|
|
||||||
def fetch(secrets, account:, from: nil)
|
def fetch(secrets, account:, from: nil)
|
||||||
check_dependencies!
|
|
||||||
session = login(account)
|
session = login(account)
|
||||||
full_secrets = secrets.map { |secret| [ from, secret ].compact.join("/") }
|
full_secrets = secrets.map { |secret| [ from, secret ].compact.join("/") }
|
||||||
fetch_secrets(full_secrets, account: account, session: session)
|
fetch_secrets(full_secrets, account: account, session: session)
|
||||||
@@ -16,8 +15,4 @@ class Kamal::Secrets::Adapters::Base
|
|||||||
def fetch_secrets(...)
|
def fetch_secrets(...)
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_dependencies!
|
|
||||||
raise NotImplementedError
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -25,15 +25,18 @@ class Kamal::Secrets::Adapters::Bitwarden < Kamal::Secrets::Adapters::Base
|
|||||||
{}.tap do |results|
|
{}.tap do |results|
|
||||||
items_fields(secrets).each do |item, fields|
|
items_fields(secrets).each do |item, fields|
|
||||||
item_json = run_command("get item #{item.shellescape}", session: session, raw: true)
|
item_json = run_command("get item #{item.shellescape}", session: session, raw: true)
|
||||||
raise RuntimeError, "Could not read #{item} from Bitwarden" unless $?.success?
|
raise RuntimeError, "Could not read #{secret} from Bitwarden" unless $?.success?
|
||||||
item_json = JSON.parse(item_json)
|
item_json = JSON.parse(item_json)
|
||||||
|
|
||||||
if fields.any?
|
if fields.any?
|
||||||
results.merge! fetch_secrets_from_fields(fields, item, item_json)
|
fields.each do |field|
|
||||||
|
item_field = item_json["fields"].find { |f| f["name"] == field }
|
||||||
|
raise RuntimeError, "Could not find field #{field} in item #{item} in Bitwarden" unless item_field
|
||||||
|
value = item_field["value"]
|
||||||
|
results["#{item}/#{field}"] = value
|
||||||
|
end
|
||||||
elsif item_json.dig("login", "password")
|
elsif item_json.dig("login", "password")
|
||||||
results[item] = item_json.dig("login", "password")
|
results[item] = item_json.dig("login", "password")
|
||||||
elsif item_json["fields"]&.any?
|
|
||||||
fields = item_json["fields"].pluck("name")
|
|
||||||
results.merge! fetch_secrets_from_fields(fields, item, item_json)
|
|
||||||
else
|
else
|
||||||
raise RuntimeError, "Item #{item} is not a login type item and no fields were specified"
|
raise RuntimeError, "Item #{item} is not a login type item and no fields were specified"
|
||||||
end
|
end
|
||||||
@@ -41,15 +44,6 @@ class Kamal::Secrets::Adapters::Bitwarden < Kamal::Secrets::Adapters::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_secrets_from_fields(fields, item, item_json)
|
|
||||||
fields.to_h do |field|
|
|
||||||
item_field = item_json["fields"].find { |f| f["name"] == field }
|
|
||||||
raise RuntimeError, "Could not find field #{field} in item #{item} in Bitwarden" unless item_field
|
|
||||||
value = item_field["value"]
|
|
||||||
[ "#{item}/#{field}", value ]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def items_fields(secrets)
|
def items_fields(secrets)
|
||||||
{}.tap do |items|
|
{}.tap do |items|
|
||||||
secrets.each do |secret|
|
secrets.each do |secret|
|
||||||
@@ -69,13 +63,4 @@ class Kamal::Secrets::Adapters::Bitwarden < Kamal::Secrets::Adapters::Base
|
|||||||
result = `#{full_command}`.strip
|
result = `#{full_command}`.strip
|
||||||
raw ? result : JSON.parse(result)
|
raw ? result : JSON.parse(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_dependencies!
|
|
||||||
raise RuntimeError, "Bitwarden CLI is not installed" unless cli_installed?
|
|
||||||
end
|
|
||||||
|
|
||||||
def cli_installed?
|
|
||||||
`bw --version 2> /dev/null`
|
|
||||||
$?.success?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,13 +27,4 @@ class Kamal::Secrets::Adapters::LastPass < Kamal::Secrets::Adapters::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_dependencies!
|
|
||||||
raise RuntimeError, "LastPass CLI is not installed" unless cli_installed?
|
|
||||||
end
|
|
||||||
|
|
||||||
def cli_installed?
|
|
||||||
`lpass --version 2> /dev/null`
|
|
||||||
$?.success?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -58,13 +58,4 @@ class Kamal::Secrets::Adapters::OnePassword < Kamal::Secrets::Adapters::Base
|
|||||||
raise RuntimeError, "Could not read #{fields.join(", ")} from #{item} in the #{vault} 1Password vault" unless $?.success?
|
raise RuntimeError, "Could not read #{fields.join(", ")} from #{item} in the #{vault} 1Password vault" unless $?.success?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_dependencies!
|
|
||||||
raise RuntimeError, "1Password CLI is not installed" unless cli_installed?
|
|
||||||
end
|
|
||||||
|
|
||||||
def cli_installed?
|
|
||||||
`op --version 2> /dev/null`
|
|
||||||
$?.success?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,8 +7,4 @@ class Kamal::Secrets::Adapters::Test < Kamal::Secrets::Adapters::Base
|
|||||||
def fetch_secrets(secrets, account:, session:)
|
def fetch_secrets(secrets, account:, session:)
|
||||||
secrets.to_h { |secret| [ secret, secret.reverse ] }
|
secrets.to_h { |secret| [ secret, secret.reverse ] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_dependencies!
|
|
||||||
# no op
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ module Kamal::Utils
|
|||||||
attr = "#{key}=#{escape_shell_value(value)}"
|
attr = "#{key}=#{escape_shell_value(value)}"
|
||||||
attr = self.sensitive(attr, redaction: "#{key}=[REDACTED]") if sensitive
|
attr = self.sensitive(attr, redaction: "#{key}=[REDACTED]") if sensitive
|
||||||
[ argument, attr ]
|
[ argument, attr ]
|
||||||
elsif value == false
|
|
||||||
[ argument, "#{key}=false" ]
|
|
||||||
else
|
else
|
||||||
[ argument, key ]
|
[ argument, key ]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module Kamal
|
module Kamal
|
||||||
VERSION = "2.3.0"
|
VERSION = "2.1.1"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class CliProxyTest < CliTestCase
|
|||||||
test "boot" do
|
test "boot" do
|
||||||
run_command("boot").tap do |output|
|
run_command("boot").tap do |output|
|
||||||
assert_match "docker login", output
|
assert_match "docker login", output
|
||||||
assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") #{KAMAL.config.proxy_image}", output
|
assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\") #{KAMAL.config.proxy_image}", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -18,11 +18,11 @@ class CliProxyTest < CliTestCase
|
|||||||
exception = assert_raises do
|
exception = assert_raises do
|
||||||
run_command("boot").tap do |output|
|
run_command("boot").tap do |output|
|
||||||
assert_match "docker login", output
|
assert_match "docker login", output
|
||||||
assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") #{KAMAL.config.proxy_image}", output
|
assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\") #{KAMAL.config.proxy_image}", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_includes exception.message, "kamal-proxy version v0.0.1 is too old, run `kamal proxy reboot` in order to update to at least #{Kamal::Configuration::PROXY_MINIMUM_VERSION}"
|
assert_includes exception.message, "kamal-proxy version v0.0.1 is too old, please reboot to update to at least #{Kamal::Configuration::PROXY_MINIMUM_VERSION}"
|
||||||
ensure
|
ensure
|
||||||
Thread.report_on_exception = false
|
Thread.report_on_exception = false
|
||||||
end
|
end
|
||||||
@@ -36,7 +36,7 @@ class CliProxyTest < CliTestCase
|
|||||||
|
|
||||||
run_command("boot").tap do |output|
|
run_command("boot").tap do |output|
|
||||||
assert_match "docker login", output
|
assert_match "docker login", output
|
||||||
assert_match "docker container start kamal-proxy || docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") #{KAMAL.config.proxy_image}", output
|
assert_match "docker container start kamal-proxy || docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\") #{KAMAL.config.proxy_image}", output
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
Thread.report_on_exception = false
|
Thread.report_on_exception = false
|
||||||
@@ -57,13 +57,13 @@ class CliProxyTest < CliTestCase
|
|||||||
assert_match "docker container stop kamal-proxy on 1.1.1.1", output
|
assert_match "docker container stop kamal-proxy on 1.1.1.1", output
|
||||||
assert_match "Running docker container stop traefik ; docker container prune --force --filter label=org.opencontainers.image.title=Traefik && docker image prune --all --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.1", output
|
assert_match "Running docker container stop traefik ; docker container prune --force --filter label=org.opencontainers.image.title=Traefik && docker image prune --all --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.1", output
|
||||||
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.1", output
|
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.1", output
|
||||||
assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") #{KAMAL.config.proxy_image} on 1.1.1.1", output
|
assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\") #{KAMAL.config.proxy_image} on 1.1.1.1", output
|
||||||
assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target=\"abcdefabcdef:80\" --deploy-timeout=\"6s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\" on 1.1.1.1", output
|
assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target=\"abcdefabcdef:80\" --deploy-timeout=\"6s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\" on 1.1.1.1", output
|
||||||
|
|
||||||
assert_match "docker container stop kamal-proxy on 1.1.1.2", output
|
assert_match "docker container stop kamal-proxy on 1.1.1.2", output
|
||||||
assert_match "Running docker container stop traefik ; docker container prune --force --filter label=org.opencontainers.image.title=Traefik && docker image prune --all --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.2", output
|
assert_match "Running docker container stop traefik ; docker container prune --force --filter label=org.opencontainers.image.title=Traefik && docker image prune --all --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.2", output
|
||||||
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.2", output
|
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.2", output
|
||||||
assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") #{KAMAL.config.proxy_image} on 1.1.1.2", output
|
assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\") #{KAMAL.config.proxy_image} on 1.1.1.2", output
|
||||||
assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target=\"abcdefabcdef:80\" --deploy-timeout=\"6s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\" on 1.1.1.2", output
|
assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target=\"abcdefabcdef:80\" --deploy-timeout=\"6s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\" on 1.1.1.2", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -198,7 +198,7 @@ class CliProxyTest < CliTestCase
|
|||||||
assert_match "/usr/bin/env mkdir -p .kamal", output
|
assert_match "/usr/bin/env mkdir -p .kamal", output
|
||||||
assert_match "docker network create kamal", output
|
assert_match "docker network create kamal", output
|
||||||
assert_match "docker login -u [REDACTED] -p [REDACTED]", output
|
assert_match "docker login -u [REDACTED] -p [REDACTED]", output
|
||||||
assert_match "docker container start kamal-proxy || docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") basecamp/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION}", output
|
assert_match "docker container start kamal-proxy || docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\") basecamp/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION}", output
|
||||||
assert_match "/usr/bin/env mkdir -p .kamal", output
|
assert_match "/usr/bin/env mkdir -p .kamal", output
|
||||||
assert_match %r{docker rename app-web-latest app-web-latest_replaced_.*}, output
|
assert_match %r{docker rename app-web-latest app-web-latest_replaced_.*}, output
|
||||||
assert_match "/usr/bin/env mkdir -p .kamal/apps/app/env/roles", output
|
assert_match "/usr/bin/env mkdir -p .kamal/apps/app/env/roles", output
|
||||||
@@ -240,7 +240,7 @@ class CliProxyTest < CliTestCase
|
|||||||
run_command("boot_config", "set").tap do |output|
|
run_command("boot_config", "set").tap do |output|
|
||||||
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
||||||
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
||||||
assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\" to .kamal/proxy/options on #{host}", output
|
assert_match "Uploading \"--publish 80:80 --publish 443:443\" to .kamal/proxy/options on #{host}", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -249,25 +249,7 @@ class CliProxyTest < CliTestCase
|
|||||||
run_command("boot_config", "set", "--publish", "false").tap do |output|
|
run_command("boot_config", "set", "--publish", "false").tap do |output|
|
||||||
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
||||||
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
||||||
assert_match "Uploading \"--log-opt max-size=10m\" to .kamal/proxy/options on #{host}", output
|
assert_match "Uploading \"\" to .kamal/proxy/options on #{host}", output
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "boot_config set custom max_size" do
|
|
||||||
run_command("boot_config", "set", "--log-max-size", "100m").tap do |output|
|
|
||||||
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
|
||||||
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
|
||||||
assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=100m\" to .kamal/proxy/options on #{host}", output
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "boot_config set no log max size" do
|
|
||||||
run_command("boot_config", "set", "--log-max-size=").tap do |output|
|
|
||||||
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
|
||||||
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
|
||||||
assert_match "Uploading \"--publish 80:80 --publish 443:443\" to .kamal/proxy/options on #{host}", output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -276,7 +258,7 @@ class CliProxyTest < CliTestCase
|
|||||||
run_command("boot_config", "set", "--http-port", "8080", "--https-port", "8443").tap do |output|
|
run_command("boot_config", "set", "--http-port", "8080", "--https-port", "8443").tap do |output|
|
||||||
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
||||||
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
||||||
assert_match "Uploading \"--publish 8080:80 --publish 8443:443 --log-opt max-size=10m\" to .kamal/proxy/options on #{host}", output
|
assert_match "Uploading \"--publish 8080:80 --publish 8443:443\" to .kamal/proxy/options on #{host}", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -285,14 +267,14 @@ class CliProxyTest < CliTestCase
|
|||||||
run_command("boot_config", "set", "--docker_options", "label=foo=bar", "add_host=thishost:thathost").tap do |output|
|
run_command("boot_config", "set", "--docker_options", "label=foo=bar", "add_host=thishost:thathost").tap do |output|
|
||||||
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
%w[ 1.1.1.1 1.1.1.2 ].each do |host|
|
||||||
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output
|
||||||
assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=10m --label=foo=bar --add_host=thishost:thathost\" to .kamal/proxy/options on #{host}", output
|
assert_match "Uploading \"--publish 80:80 --publish 443:443 --label=foo=bar --add_host=thishost:thathost\" to .kamal/proxy/options on #{host}", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "boot_config get" do
|
test "boot_config get" do
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
.with(:cat, ".kamal/proxy/options", "||", :echo, "\"--publish 80:80 --publish 443:443 --log-opt max-size=10m\"")
|
.with(:cat, ".kamal/proxy/options", "||", :echo, "\"--publish 80:80 --publish 443:443\"")
|
||||||
.returns("--publish 80:80 --publish 8443:443 --label=foo=bar")
|
.returns("--publish 80:80 --publish 8443:443 --label=foo=bar")
|
||||||
.twice
|
.twice
|
||||||
|
|
||||||
|
|||||||
@@ -71,14 +71,6 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
|
|||||||
new_command(:busybox).run.join(" ")
|
new_command(:busybox).run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run in custom network" do
|
|
||||||
@config[:accessories]["mysql"]["network"] = "custom"
|
|
||||||
|
|
||||||
assert_equal \
|
|
||||||
"docker run --name app-mysql --detach --restart unless-stopped --network custom --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/mysql.env --label service=\"app-mysql\" private.registry/mysql:8.0",
|
|
||||||
new_command(:mysql).run.join(" ")
|
|
||||||
end
|
|
||||||
|
|
||||||
test "start" do
|
test "start" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker container start app-mysql",
|
"docker container start app-mysql",
|
||||||
|
|||||||
@@ -135,14 +135,6 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
new_command.deploy(target: "172.1.0.2").join(" ")
|
new_command.deploy(target: "172.1.0.2").join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deploy with SSL false" do
|
|
||||||
@config[:proxy] = { "ssl" => false }
|
|
||||||
|
|
||||||
assert_equal \
|
|
||||||
"docker exec kamal-proxy kamal-proxy deploy app-web --target=\"172.1.0.2:80\" --deploy-timeout=\"30s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\"",
|
|
||||||
new_command.deploy(target: "172.1.0.2").join(" ")
|
|
||||||
end
|
|
||||||
|
|
||||||
test "remove" do
|
test "remove" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker exec kamal-proxy kamal-proxy remove app-web",
|
"docker exec kamal-proxy kamal-proxy remove app-web",
|
||||||
@@ -302,7 +294,7 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "run over ssh with proxy" do
|
test "run over ssh with proxy" do
|
||||||
@config[:ssh] = { "proxy" => "2.2.2.2" }
|
@config[:ssh] = { "proxy" => "2.2.2.2" }
|
||||||
assert_equal "ssh -J root@2.2.2.2 -t root@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -J 2.2.2.2 -t root@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run over ssh with proxy user" do
|
test "run over ssh with proxy user" do
|
||||||
@@ -312,7 +304,7 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "run over ssh with custom user with proxy" do
|
test "run over ssh with custom user with proxy" do
|
||||||
@config[:ssh] = { "user" => "app", "proxy" => "2.2.2.2" }
|
@config[:ssh] = { "user" => "app", "proxy" => "2.2.2.2" }
|
||||||
assert_equal "ssh -J root@2.2.2.2 -t app@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -J 2.2.2.2 -t app@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run over ssh with proxy_command" do
|
test "run over ssh with proxy_command" do
|
||||||
|
|||||||
@@ -144,45 +144,20 @@ class CommandsBuilderTest < ActiveSupport::TestCase
|
|||||||
builder.push.join(" ")
|
builder.push.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "push with provenance" do
|
|
||||||
builder = new_builder_command(builder: { "provenance" => "mode=max" })
|
|
||||||
assert_equal \
|
|
||||||
"docker buildx build --push --platform linux/amd64 --builder kamal-local-docker-container -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile --provenance mode=max .",
|
|
||||||
builder.push.join(" ")
|
|
||||||
end
|
|
||||||
|
|
||||||
test "push with provenance false" do
|
|
||||||
builder = new_builder_command(builder: { "provenance" => false })
|
|
||||||
assert_equal \
|
|
||||||
"docker buildx build --push --platform linux/amd64 --builder kamal-local-docker-container -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile --provenance false .",
|
|
||||||
builder.push.join(" ")
|
|
||||||
end
|
|
||||||
|
|
||||||
test "mirror count" do
|
test "mirror count" do
|
||||||
command = new_builder_command
|
command = new_builder_command
|
||||||
assert_equal "docker info --format '{{index .RegistryConfig.Mirrors 0}}'", command.first_mirror.join(" ")
|
assert_equal "docker info --format '{{index .RegistryConfig.Mirrors 0}}'", command.first_mirror.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "clone path with spaces" do
|
|
||||||
command = new_builder_command
|
|
||||||
Kamal::Git.stubs(:root).returns("/absolute/path with spaces")
|
|
||||||
clone_command = command.clone.join(" ")
|
|
||||||
clone_reset_commands = command.clone_reset_steps.map { |a| a.join(" ") }
|
|
||||||
|
|
||||||
assert_match(%r{path\\ with\\ space}, clone_command)
|
|
||||||
assert_no_match(%r{path with spaces}, clone_command)
|
|
||||||
|
|
||||||
clone_reset_commands.each do |command|
|
|
||||||
assert_match(%r{path\\ with\\ space}, command)
|
|
||||||
assert_no_match(%r{path with spaces}, command)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def new_builder_command(additional_config = {})
|
def new_builder_command(additional_config = {})
|
||||||
Kamal::Commands::Builder.new(Kamal::Configuration.new(@config.deep_merge(additional_config), version: "123"))
|
Kamal::Commands::Builder.new(Kamal::Configuration.new(@config.deep_merge(additional_config), version: "123"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_directory
|
||||||
|
"#{Dir.tmpdir}/kamal-clones/app/kamal/"
|
||||||
|
end
|
||||||
|
|
||||||
def local_arch
|
def local_arch
|
||||||
Kamal::Utils.docker_arch
|
Kamal::Utils.docker_arch
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class CommandsProxyTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "run" do
|
test "run" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") basecamp/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION}",
|
"docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\") basecamp/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION}",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ class CommandsProxyTest < ActiveSupport::TestCase
|
|||||||
@config.delete(:proxy)
|
@config.delete(:proxy)
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") basecamp/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION}",
|
"docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy $(cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\") basecamp/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION}",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ class CommandsProxyTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "get_boot_options" do
|
test "get_boot_options" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\"",
|
"cat .kamal/proxy/options || echo \"--publish 80:80 --publish 443:443\"",
|
||||||
new_command.get_boot_options.join(" ")
|
new_command.get_boot_options.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -152,13 +152,4 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
|
|||||||
test "options" do
|
test "options" do
|
||||||
assert_equal [ "--cpus", "\"4\"", "--memory", "\"2GB\"" ], @config.accessory(:redis).option_args
|
assert_equal [ "--cpus", "\"4\"", "--memory", "\"2GB\"" ], @config.accessory(:redis).option_args
|
||||||
end
|
end
|
||||||
|
|
||||||
test "network_args default" do
|
|
||||||
assert_equal [ "--network", "kamal" ], @config.accessory(:mysql).network_args
|
|
||||||
end
|
|
||||||
|
|
||||||
test "network_args with configured options" do
|
|
||||||
@deploy[:accessories]["mysql"]["network"] = "database"
|
|
||||||
assert_equal [ "--network", "database" ], @config.accessory(:mysql).network_args
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase
|
|||||||
@deploy[:builder] = { "arch" => "amd64", "cache" => { "type" => "registry", "options" => "mode=max,image-manifest=true,oci-mediatypes=true" } }
|
@deploy[:builder] = { "arch" => "amd64", "cache" => { "type" => "registry", "options" => "mode=max,image-manifest=true,oci-mediatypes=true" } }
|
||||||
|
|
||||||
assert_equal "type=registry,ref=dhh/app-build-cache", config.builder.cache_from
|
assert_equal "type=registry,ref=dhh/app-build-cache", config.builder.cache_from
|
||||||
assert_equal "type=registry,ref=dhh/app-build-cache,mode=max,image-manifest=true,oci-mediatypes=true", config.builder.cache_to
|
assert_equal "type=registry,mode=max,image-manifest=true,oci-mediatypes=true,ref=dhh/app-build-cache", config.builder.cache_to
|
||||||
end
|
end
|
||||||
|
|
||||||
test "setting registry cache when using a custom registry" do
|
test "setting registry cache when using a custom registry" do
|
||||||
@@ -72,14 +72,14 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase
|
|||||||
@deploy[:builder] = { "arch" => "amd64", "cache" => { "type" => "registry", "options" => "mode=max,image-manifest=true,oci-mediatypes=true" } }
|
@deploy[:builder] = { "arch" => "amd64", "cache" => { "type" => "registry", "options" => "mode=max,image-manifest=true,oci-mediatypes=true" } }
|
||||||
|
|
||||||
assert_equal "type=registry,ref=registry.example.com/dhh/app-build-cache", config.builder.cache_from
|
assert_equal "type=registry,ref=registry.example.com/dhh/app-build-cache", config.builder.cache_from
|
||||||
assert_equal "type=registry,ref=registry.example.com/dhh/app-build-cache,mode=max,image-manifest=true,oci-mediatypes=true", config.builder.cache_to
|
assert_equal "type=registry,mode=max,image-manifest=true,oci-mediatypes=true,ref=registry.example.com/dhh/app-build-cache", config.builder.cache_to
|
||||||
end
|
end
|
||||||
|
|
||||||
test "setting registry cache with image" do
|
test "setting registry cache with image" do
|
||||||
@deploy[:builder] = { "arch" => "amd64", "cache" => { "type" => "registry", "image" => "kamal", "options" => "mode=max" } }
|
@deploy[:builder] = { "arch" => "amd64", "cache" => { "type" => "registry", "image" => "kamal", "options" => "mode=max" } }
|
||||||
|
|
||||||
assert_equal "type=registry,ref=kamal", config.builder.cache_from
|
assert_equal "type=registry,ref=kamal", config.builder.cache_from
|
||||||
assert_equal "type=registry,ref=kamal,mode=max", config.builder.cache_to
|
assert_equal "type=registry,mode=max,ref=kamal", config.builder.cache_to
|
||||||
end
|
end
|
||||||
|
|
||||||
test "args" do
|
test "args" do
|
||||||
@@ -134,16 +134,6 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase
|
|||||||
assert_equal "default=$SSH_AUTH_SOCK", config.builder.ssh
|
assert_equal "default=$SSH_AUTH_SOCK", config.builder.ssh
|
||||||
end
|
end
|
||||||
|
|
||||||
test "provenance" do
|
|
||||||
assert_nil config.builder.provenance
|
|
||||||
end
|
|
||||||
|
|
||||||
test "setting provenance" do
|
|
||||||
@deploy[:builder]["provenance"] = "mode=max"
|
|
||||||
|
|
||||||
assert_equal "mode=max", config.builder.provenance
|
|
||||||
end
|
|
||||||
|
|
||||||
test "local disabled but no remote set" do
|
test "local disabled but no remote set" do
|
||||||
@deploy[:builder]["local"] = false
|
@deploy[:builder]["local"] = false
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class ConfigurationSshTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "ssh options with proxy host" do
|
test "ssh options with proxy host" do
|
||||||
config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "proxy" => "1.2.3.4" }) })
|
config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "proxy" => "1.2.3.4" }) })
|
||||||
assert_equal "root@1.2.3.4", config.ssh.options[:proxy].jump_proxies
|
assert_equal "1.2.3.4", config.ssh.options[:proxy].jump_proxies
|
||||||
end
|
end
|
||||||
|
|
||||||
test "ssh options with proxy host and user" do
|
test "ssh options with proxy host and user" do
|
||||||
|
|||||||
@@ -11,16 +11,6 @@ class EnvFileTest < ActiveSupport::TestCase
|
|||||||
Kamal::EnvFile.new(env).to_s
|
Kamal::EnvFile.new(env).to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
test "to_s won't escape '#'" do
|
|
||||||
env = {
|
|
||||||
"foo" => '#$foo',
|
|
||||||
"bar" => '#{bar}'
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal "foo=\#$foo\nbar=\#{bar}\n", \
|
|
||||||
Kamal::EnvFile.new(env).to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
test "to_str won't escape chinese characters" do
|
test "to_str won't escape chinese characters" do
|
||||||
env = {
|
env = {
|
||||||
"foo" => '你好 means hello, "欢迎" means welcome, that\'s simple! 😃 {smile}'
|
"foo" => '你好 means hello, "欢迎" means welcome, that\'s simple! 😃 {smile}'
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ require "test_helper"
|
|||||||
|
|
||||||
class BitwardenAdapterTest < SecretAdapterTestCase
|
class BitwardenAdapterTest < SecretAdapterTestCase
|
||||||
test "fetch" do
|
test "fetch" do
|
||||||
stub_ticks.with("bw --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_unlocked
|
stub_unlocked
|
||||||
stub_ticks.with("bw sync").returns("")
|
stub_ticks.with("bw sync").returns("")
|
||||||
stub_mypassword
|
stub_mypassword
|
||||||
@@ -16,8 +14,6 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch with no login" do
|
test "fetch with no login" do
|
||||||
stub_ticks.with("bw --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_unlocked
|
stub_unlocked
|
||||||
stub_ticks.with("bw sync").returns("")
|
stub_ticks.with("bw sync").returns("")
|
||||||
stub_noteitem
|
stub_noteitem
|
||||||
@@ -29,8 +25,6 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch with from" do
|
test "fetch with from" do
|
||||||
stub_ticks.with("bw --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_unlocked
|
stub_unlocked
|
||||||
stub_ticks.with("bw sync").returns("")
|
stub_ticks.with("bw sync").returns("")
|
||||||
stub_myitem
|
stub_myitem
|
||||||
@@ -44,26 +38,7 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
assert_equal expected_json, json
|
assert_equal expected_json, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetch all with from" do
|
|
||||||
stub_ticks.with("bw --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_unlocked
|
|
||||||
stub_ticks.with("bw sync").returns("")
|
|
||||||
stub_noteitem_with_fields
|
|
||||||
|
|
||||||
json = JSON.parse(shellunescape(run_command("fetch", "mynotefields")))
|
|
||||||
|
|
||||||
expected_json = {
|
|
||||||
"mynotefields/field1"=>"secret1", "mynotefields/field2"=>"blam", "mynotefields/field3"=>"fewgrwjgk",
|
|
||||||
"mynotefields/field4"=>"auto"
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal expected_json, json
|
|
||||||
end
|
|
||||||
|
|
||||||
test "fetch with multiple items" do
|
test "fetch with multiple items" do
|
||||||
stub_ticks.with("bw --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_unlocked
|
stub_unlocked
|
||||||
|
|
||||||
stub_ticks.with("bw sync").returns("")
|
stub_ticks.with("bw sync").returns("")
|
||||||
@@ -105,8 +80,6 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch unauthenticated" do
|
test "fetch unauthenticated" do
|
||||||
stub_ticks.with("bw --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_ticks
|
stub_ticks
|
||||||
.with("bw status")
|
.with("bw status")
|
||||||
.returns(
|
.returns(
|
||||||
@@ -128,8 +101,6 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch locked" do
|
test "fetch locked" do
|
||||||
stub_ticks.with("bw --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_ticks
|
stub_ticks
|
||||||
.with("bw status")
|
.with("bw status")
|
||||||
.returns(
|
.returns(
|
||||||
@@ -155,8 +126,6 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch locked with session" do
|
test "fetch locked with session" do
|
||||||
stub_ticks.with("bw --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_ticks
|
stub_ticks
|
||||||
.with("bw status")
|
.with("bw status")
|
||||||
.returns(
|
.returns(
|
||||||
@@ -181,15 +150,6 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
assert_equal expected_json, json
|
assert_equal expected_json, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetch without CLI installed" do
|
|
||||||
stub_ticks_with("bw --version 2> /dev/null", succeed: false)
|
|
||||||
|
|
||||||
error = assert_raises RuntimeError do
|
|
||||||
JSON.parse(shellunescape(run_command("fetch", "mynote")))
|
|
||||||
end
|
|
||||||
assert_equal "Bitwarden CLI is not installed", error.message
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def run_command(*command)
|
def run_command(*command)
|
||||||
stdouted do
|
stdouted do
|
||||||
@@ -254,37 +214,7 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
"collectionIds":[]
|
"collectionIds":[]
|
||||||
}
|
}
|
||||||
JSON
|
JSON
|
||||||
end
|
end
|
||||||
|
|
||||||
def stub_noteitem_with_fields(session: nil)
|
|
||||||
stub_ticks
|
|
||||||
.with("#{"BW_SESSION=#{session} " if session}bw get item mynotefields")
|
|
||||||
.returns(<<~JSON)
|
|
||||||
{
|
|
||||||
"passwordHistory":null,
|
|
||||||
"revisionDate":"2024-09-28T09:07:27.461Z",
|
|
||||||
"creationDate":"2024-09-28T09:07:00.740Z",
|
|
||||||
"deletedDate":null,
|
|
||||||
"object":"item",
|
|
||||||
"id":"aaaaaaaa-cccc-eeee-0000-222222222222",
|
|
||||||
"organizationId":null,
|
|
||||||
"folderId":null,
|
|
||||||
"type":2,
|
|
||||||
"reprompt":0,
|
|
||||||
"name":"noteitem",
|
|
||||||
"notes":"NOTES",
|
|
||||||
"favorite":false,
|
|
||||||
"fields":[
|
|
||||||
{"name":"field1","value":"secret1","type":1,"linkedId":null},
|
|
||||||
{"name":"field2","value":"blam","type":1,"linkedId":null},
|
|
||||||
{"name":"field3","value":"fewgrwjgk","type":1,"linkedId":null},
|
|
||||||
{"name":"field4","value":"auto","type":1,"linkedId":null}
|
|
||||||
],
|
|
||||||
"secureNote":{"type":0},
|
|
||||||
"collectionIds":[]
|
|
||||||
}
|
|
||||||
JSON
|
|
||||||
end
|
|
||||||
|
|
||||||
def stub_myitem
|
def stub_myitem
|
||||||
stub_ticks
|
stub_ticks
|
||||||
@@ -307,8 +237,7 @@ class BitwardenAdapterTest < SecretAdapterTestCase
|
|||||||
"fields":[
|
"fields":[
|
||||||
{"name":"field1","value":"secret1","type":1,"linkedId":null},
|
{"name":"field1","value":"secret1","type":1,"linkedId":null},
|
||||||
{"name":"field2","value":"blam","type":1,"linkedId":null},
|
{"name":"field2","value":"blam","type":1,"linkedId":null},
|
||||||
{"name":"field3","value":"fewgrwjgk","type":1,"linkedId":null},
|
{"name":"field3","value":"fewgrwjgk","type":1,"linkedId":null}
|
||||||
{"name":"field4","value":"auto","type":1,"linkedId":null}
|
|
||||||
],
|
],
|
||||||
"login":{"fido2Credentials":[],"uris":[],"username":null,"password":null,"totp":null,"passwordRevisionDate":null},"collectionIds":[]
|
"login":{"fido2Credentials":[],"uris":[],"username":null,"password":null,"totp":null,"passwordRevisionDate":null},"collectionIds":[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ class LastPassAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch" do
|
test "fetch" do
|
||||||
stub_ticks.with("lpass --version 2> /dev/null")
|
|
||||||
stub_ticks.with("lpass status --color never").returns("Logged in as email@example.com.")
|
stub_ticks.with("lpass status --color never").returns("Logged in as email@example.com.")
|
||||||
|
|
||||||
stub_ticks
|
stub_ticks
|
||||||
@@ -64,7 +63,6 @@ class LastPassAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch with from" do
|
test "fetch with from" do
|
||||||
stub_ticks.with("lpass --version 2> /dev/null")
|
|
||||||
stub_ticks.with("lpass status --color never").returns("Logged in as email@example.com.")
|
stub_ticks.with("lpass status --color never").returns("Logged in as email@example.com.")
|
||||||
|
|
||||||
stub_ticks
|
stub_ticks
|
||||||
@@ -109,8 +107,6 @@ class LastPassAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch with signin" do
|
test "fetch with signin" do
|
||||||
stub_ticks.with("lpass --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_ticks_with("lpass status --color never", succeed: false).returns("Not logged in.")
|
stub_ticks_with("lpass status --color never", succeed: false).returns("Not logged in.")
|
||||||
stub_ticks_with("lpass login email@example.com", succeed: true).returns("")
|
stub_ticks_with("lpass login email@example.com", succeed: true).returns("")
|
||||||
stub_ticks.with("lpass show SECRET1 --json").returns(single_item_json)
|
stub_ticks.with("lpass show SECRET1 --json").returns(single_item_json)
|
||||||
@@ -124,15 +120,6 @@ class LastPassAdapterTest < SecretAdapterTestCase
|
|||||||
assert_equal expected_json, json
|
assert_equal expected_json, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetch without CLI installed" do
|
|
||||||
stub_ticks_with("lpass --version 2> /dev/null", succeed: false)
|
|
||||||
|
|
||||||
error = assert_raises RuntimeError do
|
|
||||||
JSON.parse(shellunescape(run_command("fetch", "SECRET1", "FOLDER1/FSECRET1", "FOLDER1/FSECRET2")))
|
|
||||||
end
|
|
||||||
assert_equal "LastPass CLI is not installed", error.message
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def run_command(*command)
|
def run_command(*command)
|
||||||
stdouted do
|
stdouted do
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ require "test_helper"
|
|||||||
|
|
||||||
class SecretsOnePasswordAdapterTest < SecretAdapterTestCase
|
class SecretsOnePasswordAdapterTest < SecretAdapterTestCase
|
||||||
test "fetch" do
|
test "fetch" do
|
||||||
stub_ticks.with("op --version 2> /dev/null")
|
|
||||||
stub_ticks.with("op account get --account myaccount 2> /dev/null")
|
stub_ticks.with("op account get --account myaccount 2> /dev/null")
|
||||||
|
|
||||||
stub_ticks
|
stub_ticks
|
||||||
@@ -57,7 +56,6 @@ class SecretsOnePasswordAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch with multiple items" do
|
test "fetch with multiple items" do
|
||||||
stub_ticks.with("op --version 2> /dev/null")
|
|
||||||
stub_ticks.with("op account get --account myaccount 2> /dev/null")
|
stub_ticks.with("op account get --account myaccount 2> /dev/null")
|
||||||
|
|
||||||
stub_ticks
|
stub_ticks
|
||||||
@@ -117,8 +115,6 @@ class SecretsOnePasswordAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch with signin, no session" do
|
test "fetch with signin, no session" do
|
||||||
stub_ticks.with("op --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_ticks_with("op account get --account myaccount 2> /dev/null", succeed: false)
|
stub_ticks_with("op account get --account myaccount 2> /dev/null", succeed: false)
|
||||||
stub_ticks_with("op signin --account \"myaccount\" --force --raw", succeed: true).returns("")
|
stub_ticks_with("op signin --account \"myaccount\" --force --raw", succeed: true).returns("")
|
||||||
|
|
||||||
@@ -136,8 +132,6 @@ class SecretsOnePasswordAdapterTest < SecretAdapterTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fetch with signin and session" do
|
test "fetch with signin and session" do
|
||||||
stub_ticks.with("op --version 2> /dev/null")
|
|
||||||
|
|
||||||
stub_ticks_with("op account get --account myaccount 2> /dev/null", succeed: false)
|
stub_ticks_with("op account get --account myaccount 2> /dev/null", succeed: false)
|
||||||
stub_ticks_with("op signin --account \"myaccount\" --force --raw", succeed: true).returns("1234567890")
|
stub_ticks_with("op signin --account \"myaccount\" --force --raw", succeed: true).returns("1234567890")
|
||||||
|
|
||||||
@@ -154,15 +148,6 @@ class SecretsOnePasswordAdapterTest < SecretAdapterTestCase
|
|||||||
assert_equal expected_json, json
|
assert_equal expected_json, json
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetch without CLI installed" do
|
|
||||||
stub_ticks_with("op --version 2> /dev/null", succeed: false)
|
|
||||||
|
|
||||||
error = assert_raises RuntimeError do
|
|
||||||
JSON.parse(shellunescape(run_command("fetch", "--from", "op://myvault/myitem", "section/SECRET1", "section/SECRET2", "section2/SECRET3")))
|
|
||||||
end
|
|
||||||
assert_equal "1Password CLI is not installed", error.message
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def run_command(*command)
|
def run_command(*command)
|
||||||
stdouted do
|
stdouted do
|
||||||
|
|||||||
@@ -31,18 +31,4 @@ class SecretsTest < ActiveSupport::TestCase
|
|||||||
assert_equal "JKL", Kamal::Secrets.new(destination: "nodest")["SECRET2"]
|
assert_equal "JKL", Kamal::Secrets.new(destination: "nodest")["SECRET2"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "no secrets files" do
|
|
||||||
with_test_secrets do
|
|
||||||
error = assert_raises(Kamal::ConfigurationError) do
|
|
||||||
Kamal::Secrets.new["SECRET"]
|
|
||||||
end
|
|
||||||
assert_equal "Secret 'SECRET' not found, no secret files (.kamal/secrets-common, .kamal/secrets) provided", error.message
|
|
||||||
|
|
||||||
error = assert_raises(Kamal::ConfigurationError) do
|
|
||||||
Kamal::Secrets.new(destination: "dest")["SECRET"]
|
|
||||||
end
|
|
||||||
assert_equal "Secret 'SECRET' not found, no secret files (.kamal/secrets-common, .kamal/secrets.dest) provided", error.message
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ require "test_helper"
|
|||||||
|
|
||||||
class UtilsTest < ActiveSupport::TestCase
|
class UtilsTest < ActiveSupport::TestCase
|
||||||
test "argumentize" do
|
test "argumentize" do
|
||||||
assert_equal [ "--label", "foo=\"\\`bar\\`\"", "--label", "baz=\"qux\"", "--label", :quux, "--label", "quuz=false" ], \
|
assert_equal [ "--label", "foo=\"\\`bar\\`\"", "--label", "baz=\"qux\"", "--label", :quux ], \
|
||||||
Kamal::Utils.argumentize("--label", { foo: "`bar`", baz: "qux", quux: nil, quuz: false })
|
Kamal::Utils.argumentize("--label", { foo: "`bar`", baz: "qux", quux: nil })
|
||||||
end
|
end
|
||||||
|
|
||||||
test "argumentize with redacted" do
|
test "argumentize with redacted" do
|
||||||
|
|||||||
Reference in New Issue
Block a user