-
Notifications
You must be signed in to change notification settings - Fork 691
Detect accessory image changes during boot #1808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -13,11 +13,17 @@ def boot(name, prepare: true) | |||||
| with_accessory(name) do |accessory, hosts| | ||||||
| booted_hosts = Concurrent::Array.new | ||||||
| on(hosts) do |host| | ||||||
| booted_hosts << host.to_s if capture_with_info(*accessory.info(all: true, quiet: true)).strip.presence | ||||||
| if capture_with_info(*accessory.info(all: true, quiet: true)).strip.presence | ||||||
| running_image = capture_with_info(*accessory.running_image).strip.presence | ||||||
| if running_image && running_image != accessory.image | ||||||
| raise "Accessory `#{name}` image has changed (#{running_image} → #{accessory.image}), run `kamal accessory reboot #{name}` to update" | ||||||
|
||||||
| raise "Accessory `#{name}` image has changed (#{running_image} → #{accessory.image}), run `kamal accessory reboot #{name}` to update" | |
| raise "Accessory `#{name}` image has changed on host #{host} (#{running_image} → #{accessory.image}), run `kamal accessory reboot #{name}` to update" |
Outdated
Copilot
AI
Mar 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accessory.info(all: true, ...) uses docker ps -a, so it will match stopped containers as well as running ones. The skip message says "already running with the correct image", which can be inaccurate when a container exists but is not running; consider wording that reflects "container already exists" (or switch the existence check to running-only if that’s the intent).
| say "Skipping booting `#{name}` on #{booted_hosts.sort.join(", ")}, already running with the correct image", :yellow | |
| say "Skipping booting `#{name}` on #{booted_hosts.sort.join(", ")}, container already exists with the correct image", :yellow |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,42 @@ class CliAccessoryTest < CliTestCase | |
| end | ||
| end | ||
|
|
||
| test "boot with image changed" do | ||
| Thread.report_on_exception = false | ||
|
|
||
| SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info) | ||
| .with(:docker, :ps, "-a", "-q", "--filter", "label=service=app-mysql") | ||
| .returns("abc123") | ||
|
|
||
| SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info) | ||
| .with(:docker, :inspect, "app-mysql", "--format '{{.Config.Image}}'") | ||
| .returns("private.registry/mysql:5.6") | ||
|
|
||
| exception = assert_raises do | ||
| run_command("boot", "mysql") | ||
| end | ||
|
|
||
| assert_includes exception.message, "Accessory `mysql` image has changed (private.registry/mysql:5.6 → private.registry/mysql:5.7)" | ||
| assert_includes exception.message, "run `kamal accessory reboot mysql` to update" | ||
| ensure | ||
| Thread.report_on_exception = true | ||
| end | ||
|
Comment on lines
+23
to
+42
|
||
|
|
||
| test "boot with same image" do | ||
| SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info) | ||
| .with(:docker, :ps, "-a", "-q", "--filter", "label=service=app-mysql") | ||
| .returns("abc123") | ||
|
|
||
| SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info) | ||
| .with(:docker, :inspect, "app-mysql", "--format '{{.Config.Image}}'") | ||
| .returns("private.registry/mysql:5.7") | ||
|
|
||
| run_command("boot", "mysql").tap do |output| | ||
| assert_match "already running with the correct image", output | ||
| assert_no_match(/docker run/, output) | ||
| end | ||
| end | ||
|
|
||
| test "boot all" do | ||
| Kamal::Cli::Accessory.any_instance.expects(:directories).with("mysql") | ||
| Kamal::Cli::Accessory.any_instance.expects(:upload).with("mysql") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The container existence check uses
docker ps -a -q --filter label=service=..., but the follow-updocker inspecttargetsservice_name(container name). If a container matches the label but is not named exactlyservice_name(e.g., renamed/manual container),docker inspectwill fail and abortboot. Consider capturing the container id(s) fromaccessory.info(...)and inspecting that id (or first id) instead, so the inspect target matches the existence filter.