From c0f50957834162ea89a0468f66eaa57de5f07259 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 17 Apr 2014 16:20:49 -0700 Subject: [PATCH] providers/docker: wait for running state, error if not --- plugins/providers/docker/action.rb | 4 +- .../docker/action/wait_for_running.rb | 66 +++++++++++++++++++ plugins/providers/docker/config.rb | 8 +++ plugins/providers/docker/driver.rb | 2 +- plugins/providers/docker/errors.rb | 8 +++ templates/locales/providers_docker.yml | 27 ++++++++ 6 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 plugins/providers/docker/action/wait_for_running.rb diff --git a/plugins/providers/docker/action.rb b/plugins/providers/docker/action.rb index 773836e96..bfec69985 100644 --- a/plugins/providers/docker/action.rb +++ b/plugins/providers/docker/action.rb @@ -34,8 +34,8 @@ module VagrantPlugins b2.use SyncedFolderCleanup b2.use SyncedFolders b2.use PrepareNFSSettings - # This will actually create and start, but that's fine b2.use Create + b2.use WaitForRunning b2.use action_boot else b2.use PrepareNFSValidIds @@ -205,6 +205,7 @@ module VagrantPlugins Vagrant::Action::Builder.new.tap do |b| # TODO: b.use SetHostname b.use Start + b.use WaitForRunning b.use Call, HasSSH do |env, b2| if env[:result] @@ -230,6 +231,7 @@ module VagrantPlugins autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids") autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings") autoload :Start, action_root.join("start") + autoload :WaitForRunning, action_root.join("wait_for_running") end end end diff --git a/plugins/providers/docker/action/wait_for_running.rb b/plugins/providers/docker/action/wait_for_running.rb new file mode 100644 index 000000000..89164ddcb --- /dev/null +++ b/plugins/providers/docker/action/wait_for_running.rb @@ -0,0 +1,66 @@ +require "thread" + +require "log4r" + +module VagrantPlugins + module DockerProvider + module Action + class WaitForRunning + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant::docker::waitforrunning") + end + + def call(env) + machine = env[:machine] + + wait = true + if !machine.provider_config.remains_running + wait = false + elsif machine.provider.state.id == :running + wait = false + end + + # If we're not waiting, just return + return @app.call(env) if !wait + + machine.ui.output(I18n.t("docker_provider.waiting_for_running")) + + # First, make sure it leaves the stopped state if its supposed to. + after = sleeper(5) + while machine.provider.state.id == :stopped + if after[:done] + raise Errors::StateStopped + end + sleep 0.2 + end + + # Then, wait for it to become running + after = sleeper(30) + while true + state = machine.provider.state + break if state.id == :running + @logger.info("Waiting for container to run. State: #{state.id}") + + if after[:done] + raise Errors::StateNotRunning + end + + sleep 0.2 + end + + @app.call(env) + end + + protected + + def sleeper(duration) + Thread.new(duration) do |d| + sleep(d) + Thread.current[:done] = true + end + end + end + end + end +end diff --git a/plugins/providers/docker/config.rb b/plugins/providers/docker/config.rb index 9e33307b9..e505176cc 100644 --- a/plugins/providers/docker/config.rb +++ b/plugins/providers/docker/config.rb @@ -14,6 +14,12 @@ module VagrantPlugins # provisioning, etc. attr_accessor :has_ssh + # True if the docker container is meant to stay in the "running" + # state (is a long running process). By default this is true. + # + # @return [Boolean] + attr_accessor :remains_running + # The name of the machine in the Vagrantfile set with # "vagrant_vagrantfile" that will be the docker host. Defaults # to "default" @@ -43,6 +49,7 @@ module VagrantPlugins @image = UNSET_VALUE @ports = [] @privileged = UNSET_VALUE + @remains_running = UNSET_VALUE @volumes = [] @vagrant_machine = UNSET_VALUE @vagrant_vagrantfile = UNSET_VALUE @@ -54,6 +61,7 @@ module VagrantPlugins @has_ssh = false if @has_ssh == UNSET_VALUE @image = nil if @image == UNSET_VALUE @privileged = false if @privileged == UNSET_VALUE + @remains_running = true if @remains_running == UNSET_VALUE @vagrant_machine = nil if @vagrant_machine == UNSET_VALUE @vagrant_vagrantfile = nil if @vagrant_vagrantfile == UNSET_VALUE end diff --git a/plugins/providers/docker/driver.rb b/plugins/providers/docker/driver.rb index 0ec6b4b1f..0d679501a 100644 --- a/plugins/providers/docker/driver.rb +++ b/plugins/providers/docker/driver.rb @@ -58,7 +58,7 @@ module VagrantPlugins end def start(cid) - unless running?(cid) + if !running?(cid) execute('docker', 'start', cid) # This resets the cached information we have around, allowing `vagrant reload`s # to work properly diff --git a/plugins/providers/docker/errors.rb b/plugins/providers/docker/errors.rb index 7d5cb2f32..9d9504cfb 100644 --- a/plugins/providers/docker/errors.rb +++ b/plugins/providers/docker/errors.rb @@ -21,6 +21,14 @@ module VagrantPlugins error_key(:docker_provider_nfs_without_privileged) end + class StateNotRunning < DockerError + error_key(:state_not_running) + end + + class StateStopped < DockerError + error_key(:state_stopped) + end + class SyncedFolderNonDocker < DockerError error_key(:synced_folder_non_docker) end diff --git a/templates/locales/providers_docker.yml b/templates/locales/providers_docker.yml index 48fc0a14d..61568ff92 100644 --- a/templates/locales/providers_docker.yml +++ b/templates/locales/providers_docker.yml @@ -32,6 +32,8 @@ en: ssh_through_host_vm: |- SSH will be proxied through the Docker virtual machine since we're not running Docker natively. This is just a notice, and not an error. + waiting_for_running: |- + Waiting for container to enter "running" state... messages: destroying: |- @@ -99,6 +101,31 @@ en: Stderr: %{stderr} Stdout: %{stdout} + state_not_running: |- + The container never entered the "running" state, or entered it + briefly but reverted back to another state. Please verify that + the configuration of the container is correct. + + If you meant for this container to not remain running, please + set the Docker provider configuration "remains_running" to "false": + + config.vm.provider "docker" do |d| + d.remains_running = false + end + + state_stopped: |- + The container started either never left the "stopped" state or + very quickly reverted to the "stopped" state. This is usually + because the container didn't execute a command that kept it running, + and usually indicates a misconfiguration. + + If you meant for this container to not remain running, please + set the Docker provider configuration "remains_running" to "false": + + config.vm.provider "docker" do |d| + d.remains_running = false + end + synced_folder_non_docker: |- The "docker" synced folder type can't be used because the provider in use is not Docker. This synced folder type only works with the