From db97f985d3de68dc3c71842cd8b4504684a5bb2d Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 25 Mar 2024 21:45:03 +1300 Subject: [PATCH] Ensure the container is stopped if it's not correctly started. (#32) --- lib/async/container/controller.rb | 14 +++++++------ test/async/container/.bad.rb | 28 ++++++++++++++++++++++++++ test/async/container/controller.rb | 32 ++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100755 test/async/container/.bad.rb diff --git a/lib/async/container/controller.rb b/lib/async/container/controller.rb index 8e6e587..e68ff20 100644 --- a/lib/async/container/controller.rb +++ b/lib/async/container/controller.rb @@ -135,18 +135,20 @@ def restart raise SetupError, container end - # Make this swap as atomic as possible: + # The following swap should be atomic: old_container = @container @container = container + container = nil + + if old_container + Console.logger.debug(self, "Stopping old container...") + old_container&.stop + end - Console.logger.debug(self, "Stopping old container...") - old_container&.stop @notify&.ready! - rescue + ensure # If we are leaving this function with an exception, try to kill the container: container&.stop(false) - - raise end # Reload the existing container. Children instances will be reloaded using `SIGHUP`. diff --git a/test/async/container/.bad.rb b/test/async/container/.bad.rb new file mode 100755 index 0000000..8c3b4c2 --- /dev/null +++ b/test/async/container/.bad.rb @@ -0,0 +1,28 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2020-2022, by Samuel Williams. + +require_relative '../../../lib/async/container/controller' + +class Bad < Async::Container::Controller + def setup(container) + container.run(name: "bad", count: 1, restart: true) do |instance| + # Deliberately missing call to `instance.ready!`: + # instance.ready! + + $stdout.puts "Ready..." + $stdout.flush + + sleep + ensure + $stdout.puts "Exiting..." + $stdout.flush + end + end +end + +controller = Bad.new + +controller.run diff --git a/test/async/container/controller.rb b/test/async/container/controller.rb index 782cab6..5941fd9 100644 --- a/test/async/container/controller.rb +++ b/test/async/container/controller.rb @@ -88,6 +88,38 @@ def controller.setup(container) end end + with 'bad controller' do + let(:controller_path) {File.expand_path(".bad.rb", __dir__)} + + let(:pipe) {IO.pipe} + let(:input) {pipe.first} + let(:output) {pipe.last} + + let(:pid) {@pid} + + def before + @pid = Process.spawn("bundle", "exec", controller_path, out: output) + output.close + + super + end + + def after + Process.kill(:TERM, @pid) + Process.wait(@pid) + + super + end + + it "fails to start" do + expect(input.gets).to be == "Ready...\n" + + Process.kill(:INT, @pid) + + expect(input.gets).to be == "Exiting...\n" + end + end + with 'signals' do let(:controller_path) {File.expand_path(".dots.rb", __dir__)}