lib/kontena/cli/services/exec_command.rb in kontena-cli-1.3.0.rc2 vs lib/kontena/cli/services/exec_command.rb in kontena-cli-1.3.0.rc3

- old
+ new

@@ -1,6 +1,7 @@ require 'shellwords' +require 'json' require_relative 'services_helper' require_relative '../helpers/exec_helper' module Kontena::Cli::Services class ExecCommand < Kontena::Command @@ -10,28 +11,30 @@ include ServicesHelper parameter "NAME", "Service name" parameter "CMD ...", "Command" - option ["-i", "--instance"], "INSTANCE", "Exec on given numbered instance, default first running" do |value| Integer(value) end + 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 ["--interactive"], :flag, "Keep stdin open" + option ["-i", "--interactive"], :flag, "Keep stdin open" + option ["-t", "--tty"], :flag, "Allocate a pseudo-TTY" 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 requires_current_grid def execute + exit_with_error "the input device is not a TTY" if tty? && !STDIN.tty? exit_with_error "--interactive cannot be used with --all" if all? && interactive? service_containers = client.get("services/#{parse_service_id(name)}/containers")['containers'] service_containers.sort_by! { |container| container['instance_number'] } running_containers = service_containers.select{|container| container['status'] == 'running' } - + exit_with_error "Service #{name} does not have any running containers" if running_containers.empty? if all? ret = true service_containers.each do |container| @@ -49,17 +52,17 @@ 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 + else exec_container(container) end else if interactive? interactive_exec(running_containers.first) - else + else exec_container(running_containers.first) end end end @@ -87,29 +90,27 @@ def normal_exec(container) base = self cmd = JSON.dump({ cmd: cmd_list }) exit_status = nil token = require_token - url = ws_url(container['id']) - url << 'shell=true' if shell? - ws = connect(url, token) + ws = connect(url(container['id']), token) ws.on :message do |msg| data = base.parse_message(msg) - if data + if data if data['exit'] exit_status = data['exit'].to_i elsif data['stream'] == 'stdout' $stdout << data['chunk'] - else + else $stderr << data['chunk'] end end end ws.on :open do ws.text(cmd) end - ws.on :close do |e|s + 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? @@ -119,24 +120,37 @@ # @param [Hash] container def interactive_exec(container) token = require_token cmd = JSON.dump({ cmd: cmd_list }) - base = self - url = ws_url(container['id']) << 'interactive=true' - url << '&shell=true' if shell? - ws = connect(url, token) + queue = Queue.new + stdin_stream = nil + ws = connect(url(container['id']), token) ws.on :message do |msg| - base.handle_message(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) end ws.on :close do |e| - exit 1 if e.code != 1000 + if e.code != 1000 + queue << {'exit' => 1} + else + queue << {'exit' => 0} + end end ws.connect - - stream_stdin_to_ws(ws).join + 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?) + end end -end \ No newline at end of file +end