lib/kontena/cli/services/exec_command.rb in kontena-cli-1.4.0.pre1 vs lib/kontena/cli/services/exec_command.rb in kontena-cli-1.4.0.pre2
- old
+ new
@@ -8,18 +8,27 @@
include Kontena::Cli::Common
include Kontena::Cli::GridOptions
include Kontena::Cli::Helpers::ExecHelper
include ServicesHelper
+ class ExecExit < StandardError
+ attr_reader :exit_status
+
+ def initialize(exit_status, message = nil)
+ super(message)
+ @exit_status = exit_status
+ end
+ end
+
parameter "NAME", "Service name"
parameter "CMD ...", "Command"
option ["--instance"], "INSTANCE", "Exec on given numbered instance, default first running" do |value| Integer(value) end
option ["-a", "--all"], :flag, "Exec on all running instances"
- option ["--shell"], :flag, "Execute as a shell command"
- option ["-i", "--interactive"], :flag, "Keep stdin open"
- option ["-t", "--tty"], :flag, "Allocate a pseudo-TTY"
+ option ["--shell"], :flag, "Execute as a shell command", default: false
+ option ["-i", "--interactive"], :flag, "Keep stdin open", default: false
+ option ["-t", "--tty"], :flag, "Allocate a pseudo-TTY", default: false
option ["--skip"], :flag, "Skip failed instances when executing --all"
option ["--silent"], :flag, "Do not show exec status"
option ["--verbose"], :flag, "Show exec status"
requires_current_master
@@ -37,11 +46,11 @@
if all?
ret = true
service_containers.each do |container|
if container['status'] == 'running'
- if !exec_container(container)
+ if !execute_container(container)
ret = false
end
else
warning "Service #{name} container #{container['name']} is #{container['status']}, skipping"
end
@@ -50,107 +59,47 @@
elsif instance
if !(container = service_containers.find{|c| c['instance_number'] == instance})
exit_with_error "Service #{name} does not have container instance #{instance}"
elsif container['status'] != 'running'
exit_with_error "Service #{name} container #{container['name']} is not running, it is #{container['status']}"
- elsif interactive?
- interactive_exec(container)
else
- exec_container(container)
+ execute_container(container)
end
else
- if interactive?
- interactive_exec(running_containers.first)
- else
- exec_container(running_containers.first)
- end
+ execute_container(running_containers.first)
end
end
- # Exits if exec returns with non-zero
- # @param [Hash] container
- def exec_container(container)
- exit_status = nil
- if !silent? && (verbose? || all?)
- spinner "Executing command on #{container['name']}" do
- exit_status = normal_exec(container)
-
- raise Kontena::Cli::SpinAbort if exit_status != 0
- end
+ # Run block with spinner by default if --all, or when using --verbose.
+ # Do not use spinner if --silent.
+ def maybe_spinner(msg, &block)
+ if (all? || verbose?) && !silent?
+ spinner(msg, &block)
else
- exit_status = normal_exec(container)
+ yield
end
-
- exit exit_status if exit_status != 0 && !skip?
-
- return exit_status == 0
end
# @param [Hash] container
- # @return [Boolean]
- def normal_exec(container)
- base = self
- cmd = JSON.dump({ cmd: cmd_list })
- exit_status = nil
- token = require_token
- ws = connect(url(container['id']), token)
- ws.on :message do |msg|
- data = base.parse_message(msg)
- if data
- if data['exit']
- exit_status = data['exit'].to_i
- elsif data['stream'] == 'stdout'
- $stdout << data['chunk']
- else
- $stderr << data['chunk']
- end
- end
+ # @raise [SystemExit] if exec exits with non-zero status, and not --skip
+ # @return [true] exit exit status zero
+ # @return [false] exit exit status non-zero and --skip
+ def execute_container(container)
+ maybe_spinner "Executing command on #{container['name']}" do
+ exit_status = container_exec(container['id'], self.cmd_list,
+ interactive: interactive?,
+ shell: shell?,
+ tty: tty?,
+ )
+ raise ExecExit.new(exit_status) unless exit_status.zero?
end
- ws.on :open do
- ws.text(cmd)
+ rescue ExecExit => exc
+ if skip?
+ return false
+ else
+ exit exc.exit_status
end
- ws.on :close do |e|
- exit_status = 1 if exit_status.nil? && e.code != 1000
- end
- ws.connect
-
- sleep 0.01 until !exit_status.nil?
-
- exit_status
- end
-
- # @param [Hash] container
- def interactive_exec(container)
- token = require_token
- cmd = JSON.dump({ cmd: cmd_list })
- queue = Queue.new
- stdin_stream = nil
- ws = connect(url(container['id']), token)
- ws.on :message do |msg|
- data = self.parse_message(msg)
- queue << data if data.is_a?(Hash)
- end
- ws.on :open do
- ws.text(cmd)
- stdin_stream = self.stream_stdin_to_ws(ws, tty: self.tty?)
- end
- ws.on :close do |e|
- if e.code != 1000
- queue << {'exit' => 1}
- else
- queue << {'exit' => 0}
- end
- end
- ws.connect
- while msg = queue.pop
- self.handle_message(msg)
- end
- rescue SystemExit
- stdin_stream.kill if stdin_stream
- raise
- end
-
- def url(container_id)
- ws_url(container_id, shell: shell?, interactive: interactive?, tty: tty?)
+ else
+ return true
end
end
end