def websocket_exec(path, cmd, interactive: false, shell: false, tty: false)
      exit_status = nil
      write_thread = nil

      query = {}
      query[:interactive] = interactive if interactive
      query[:shell] = shell if shell
      query[:tty] = tty if tty

      server = require_current_master
      url = websocket_url(path, query)
      token = require_token
      options = WEBSOCKET_CLIENT_OPTIONS.dup
      options[:headers] = {
          'Authorization' => "Bearer #{token.access_token}"
      }
      options[:ssl_params] = {
          verify_mode: ENV['SSL_IGNORE_ERRORS'].to_s == 'true' ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER,
          ca_file: server.ssl_cert_path,
      }
      options[:ssl_hostname] = server.ssl_subject_cn

      logger.debug { "websocket exec connect... #{url}" }

      # we do not expect CloseError, because the server will send an 'exit' message first,
      # and we return before seeing the close frame
      # TODO: handle HTTP 404 errors
      Kontena::Websocket::Client.connect(url, **options) do |ws|
        logger.debug { "websocket exec open" }

        # first frame contains exec command
        websocket_exec_write(ws, 'cmd' => cmd)

        if interactive
          # start new thread to write from stdin to websocket
          write_thread = websocket_exec_write_thread(ws, tty: tty)
        end

        # blocks reading from websocket, returns with exec exit code
        exit_status = websocket_exec_read(ws)

        fail ws.close_reason unless exit_status
      end

    rescue Kontena::Websocket::Error => exc
      exit_with_error(exc)

    rescue => exc
      logger.error { "websocket exec error: #{exc}" }
      raise

    else
      logger.debug { "websocket exec exit: #{exit_status}"}
      return exit_status

    ensure
      if write_thread
        write_thread.kill
        write_thread.join
      end
    end
def websocket_exec(path, cmd, interactive: false, shell: false, tty: false)
      exit_status = nil
      write_thread = nil

      query = {}
      query[:interactive] = interactive if interactive
      query[:shell] = shell if shell
      query[:tty] = tty if tty

      server = require_current_master
      url = websocket_url(path, query)
      token = require_token
      options = WEBSOCKET_CLIENT_OPTIONS.dup
      options[:headers] = {
          'Authorization' => "Bearer #{token.access_token}"
      }
      options[:ssl_params] = {
          verify_mode: ENV['SSL_IGNORE_ERRORS'].to_s == 'true' ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER,
          ca_file: server.ssl_cert_path,
      }
      options[:ssl_hostname] = server.ssl_subject_cn

      logger.debug { "websocket exec connect... #{url}" }

      # we do not expect CloseError, because the server will send an 'exit' message first,
      # and we return before seeing the close frame
      # TODO: handle HTTP 404 errors
      Kontena::Websocket::Client.connect(url, **options) do |ws|
        logger.debug { "websocket exec open" }

        # first frame contains exec command
        websocket_exec_write(ws, 'cmd' => cmd)

        if interactive
          # start new thread to write from stdin to websocket
          write_thread = websocket_exec_write_thread(ws, tty: tty)
        end

        # blocks reading from websocket, returns with exec exit code
        exit_status = websocket_exec_read(ws)

        fail ws.close_reason unless exit_status
      end

    rescue Kontena::Websocket::Error => exc
      exit_with_error(exc)

    rescue => exc
      logger.error { "websocket exec error: #{exc}" }
      raise

    else
      logger.debug { "websocket exec exit: #{exit_status}"}
      return exit_status

    ensure
      if write_thread
        write_thread.kill
        write_thread.join
      end
    end
Connect to server websocket, send from stdin, and write out messages

 @param paths [String]
 @param options [Hash] @see Kontena::Websocket::Client
 @param cmd [Array<String>] command to execute
 @param interactive [Boolean] Interactive TTY on/off
 @param shell [Boolean] Shell on/of
 @param tty [Boolean] TTY on/of
 @return [Integer] exit code