class Object

Constants

ALIAS_COMMANDS
DEFAULT_COMMIT_MSG
LOG_SLEEP_TIME_W
POSSIBLE_COMMON_CORE_FOLDERS

we leave possibilites that folders user multiple names when somebody takes fresh projects from git it is expected that person will use dtk-common name

PULL_CATALOGS

Public Instance Methods

determine_common_folder() click to toggle source

Checks for expected names of dtk-common folder and returns name of existing common folder

# File lib/require_first.rb, line 97
def determine_common_folder
  POSSIBLE_COMMON_CORE_FOLDERS.each do |folder|
    path = File.join(File.dirname(__FILE__),'..','..',folder)
    return folder if File.directory?(path)
  end

  return nil
end
dtk_nested_require(dir,*files_x) click to toggle source
# File lib/require_first.rb, line 41
def dtk_nested_require(dir,*files_x)
  files = (files_x.first.kind_of?(Array) ? files_x.first : files_x) 
  caller_dir = caller.first.gsub(/\/[^\/]+$/,"")

  # invalid command will be send here as such needs to be handled.
  # we will throw DtkClient error as invalid command
  files.each do |f|
    begin
      require File.expand_path("#{dir}/#{f}",caller_dir)
    rescue LoadError => e
      if e.message.include? "#{dir}/#{f}"
        raise DTK::Client::DtkError,"Command '#{f}' not found."
      else
        raise e
      end
    end
  end
end
dtk_require(*files_x) click to toggle source
# File lib/require_first.rb, line 26
def dtk_require(*files_x)
  files = (files_x.first.kind_of?(Array) ? files_x.first : files_x) 
  caller_dir = caller.first.gsub(/\/[^\/]+$/,"")
  files.each{|f|require File.expand_path(f,caller_dir)}
end
dtk_require_common_commands(*files_x) click to toggle source
# File lib/require_first.rb, line 37
def dtk_require_common_commands(*files_x)
  dtk_require_from_base(*files_x.map{|f|"commands/common/#{f}"})
end
dtk_require_dtk_common_core(common_library) click to toggle source
# File lib/require_first.rb, line 66
def dtk_require_dtk_common_core(common_library)
  # use common folder else common gem
  common_folder = determine_common_folder()

  if common_folder
    dtk_require("../../" + common_folder + "/lib/#{common_library}")
  elsif is_dtk_common_core_gem_installed?       
    # already loaded so do not do anything
  else
    raise DTK::Client::DtkError,"Common directory/gem not found, please make sure that you have cloned dtk-common folder or installed dtk common gem!"
  end
end
dtk_require_from_base(*files_x) click to toggle source
# File lib/require_first.rb, line 32
def dtk_require_from_base(*files_x)
  #different than just calling dtk_require because of change to context give by caller
  dtk_require(*files_x)
end
execute_shell_command(line, prompt) click to toggle source
# File lib/shell.rb, line 126
def execute_shell_command(line, prompt)
   begin
    # remove single/double quotes from string because shellwords module is not able to parse it
    if matched = line.scan(/['"]/)
      line.gsub!(/['"]/, '') if matched.size.odd?
    end

    # some special cases
    raise DTK::Shell::ExitSignal if line == 'exit'
    return prompt if line.empty?
    if line == 'clear'
      DTK::Client::OsUtil::clear_screen
      return prompt
    end
    # when using help on root this is needed
    line = 'dtk help' if (line == 'help' && MainContext.get_context.root?)

    args = Shellwords.split(line)
    cmd = args.shift

    # support command alias (ls for list etc.)
    cmd = preprocess_commands(cmd)

    # DEV only reload shell
    if ::DTK::Configuration.get(:development_mode)
      if ('restart' == cmd)
        puts "DEV Reloading shell ..."
        ::DTK::Client::OsUtil.dev_reload_shell()
        return prompt
      end
    end


    if ('cc' == cmd)
      # in case there is no params we just reload command
      args << "/" if args.empty?
      prompt = MainContext.get_context.change_context(args, cmd)
    elsif ('popc' == cmd)
        MainContext.get_context.dirs.shift()
        args << (MainContext.get_context.dirs.first.nil? ? '/' : MainContext.get_context.dirs.first)
        prompt = MainContext.get_context.change_context(args, cmd)
    elsif ('pushc' == cmd)
      if args.empty?
        args << (MainContext.get_context.dirs[1].nil? ? '/' : MainContext.get_context.dirs[1])
        MainContext.get_context.dirs.unshift(args.first)
        MainContext.get_context.dirs.uniq!
        prompt = MainContext.get_context.change_context(args, cmd)
      else
        prompt = MainContext.get_context.change_context(args)
        # using regex to remove dtk: and > from path returned by change_context
        # e.g transform dtk:/assembly/node> to /assembly/node
        full_path = prompt.match(/[dtk:](\/.*)[>]/)[1]
        MainContext.get_context.dirs.unshift(full_path)
      end
    elsif ('dirs' == cmd)
      puts MainContext.get_context.dirs.inspect
    else

      # get all next-context-candidates (e.g. for assembly get all assembly_names)
      context_candidates = MainContext.get_context.get_ac_candidates_for_context(MainContext.get_context.active_context.last_context(), MainContext.get_context.active_context())

      # this part of the code is used for calling of nested commands from base context (dtk:/>assembly/assembly_id converge)
      # base_command is used to check if first command from n-level is valid e.g.
      # (dtk:/>assembly/assembly_id converge - chech if 'assembly' exists in context_candidates)
      # revert_context is used to return to context which command is called from after command is executed
      base_command = cmd.split('/').first
      revert_context = false

      if context_candidates.include?(base_command)
        MainContext.get_context.change_context([cmd])
        cmd = args.shift
        revert_context = true
      end

      if cmd.nil?
        prompt = MainContext.get_context.change_context(["-"]) if revert_context
        raise DTK::Client::DtkValidationError, "You have to provide command after context name. Usage: CONTEXT-TYPE/CONTEXT-NAME COMMAND [ARG1] .. [ARG2]."
      end

      # send monkey patch class information about context
      Thor.set_context(MainContext.get_context)

      # we get command and hash params, will return Validation error if command is not valid
      entity_name, method_name, context_params, thor_options, invalid_options = MainContext.get_context.get_command_parameters(cmd,args)

      # check if command is executed from parent context (e.g assembly_name list-nodes)
      if context_candidates.include?(method_name)
        context_params.add_context_to_params(method_name, entity_name, method_name)
        method_name = context_params.method_arguments.shift if context_params.method_arguments.size > 0
      else
        unless MainContext.get_context.method_valid?(method_name)
          prompt = MainContext.get_context.change_context(["-"]) if revert_context
          raise DTK::Client::DtkValidationError, "Method '#{method_name}' is not valid in current context."
        end
      end

      # raise validation error if option is not valid
      raise DTK::Client::DtkValidationError.new("Option '#{invalid_options.first||method_name}' is not valid for current command!", true) unless invalid_options.empty?

      # execute command via Thor
      current_contex_path = MainContext.get_context.active_context.full_path
      top_level_execute(entity_name, method_name, context_params, thor_options, true)

      # when 'delete' or 'delete-and-destroy' command is executed reload cached tasks with latest commands
      unless (args.nil? || args.empty?)
        MainContext.get_context.reload_cached_tasks(entity_name) if (method_name.include?('delete') || method_name.include?('import'))
      end

      # check execution status, prints status to sttout
      DTK::Shell::StatusMonitor.check_status()

      # if we change context while executing command, change prompt as well
      unless current_contex_path.eql?(MainContext.get_context.active_context.full_path)
        prompt = "dtk:#{MainContext.get_context.active_context.full_path}>"
      end

      # after nested command called from base context is executed successfully, return to context which command is executed from
      # this is the same as 'cd -' command is executed
      prompt = MainContext.get_context.change_context(["-"]) if revert_context
    end
  rescue DTK::Client::DSLParsing => e
    DTK::Client::OsUtil.print(e.message, :red)
  rescue DTK::Client::DtkValidationError => e
    DTK::Client::OsUtil.print(e.message, :yellow)
  rescue DTK::Shell::Error => e
    DtkLogger.instance.error(e.message, true)
  end

  return prompt
end
execute_shell_command_internal(line) click to toggle source
# File lib/shell.rb, line 259
def execute_shell_command_internal(line)
  execute_shell_command(line, DTK::Shell::Context::DTK_ROOT_PROMPT)
end
gem_only_available?() click to toggle source

this returns true if there is no common folder e.g. dtk-common in parent folder, and gem is installed

# File lib/require_first.rb, line 62
def gem_only_available?()
  return !determine_common_folder() && is_dtk_common_core_gem_installed?
end
init_shell_context() click to toggle source
# File lib/shell.rb, line 105
def init_shell_context()
  begin
    # @context      = DTK::Shell::Context.new
    @shell_header = DTK::Shell::HeaderShell.new

    # loads root context
    MainContext.get_context.load_context()

    @t1   = nil
    Readline.completion_append_character=''
    DTK::Shell::Context.load_session_history().each do |c|
      Readline::HISTORY.push(c)
    end

  rescue DTK::Client::DtkError => e
    DtkLogger.instance.error(e.message, true)
    puts "Exiting ..."
    raise DTK::Shell::ExitSignal
  end
end
is_dtk_common_core_gem_installed?() click to toggle source

Check if dtk-common gem has been installed if so use common gem. If there is no gem logic from dtk_require_dtk_common will try to find commond folder. DEVELOPER NOTE: Uninstall dtk-common gem when changing dtk-common to avoid re-building gem.

# File lib/require_first.rb, line 85
def is_dtk_common_core_gem_installed?
  begin
    # if no exception gem is found
    gem 'dtk-common-core'
    return true
  rescue Gem::LoadError
    return false
  end
end
load_command(command_name) click to toggle source
# File lib/core.rb, line 137
def load_command(command_name)
  parser_adapter = DTK::Client::Config[:cli_parser] || "thor"

  dtk_nested_require("parser/adapters",parser_adapter)
  dtk_nested_require("commands/#{parser_adapter}",command_name)
end
preprocess_commands(original_command) click to toggle source

support for alias commands (ls for list, cd for cc etc.)

# File lib/shell.rb, line 59
def preprocess_commands(original_command)
  command = ALIAS_COMMANDS[original_command]
  # return command if alias for specific command exist in predefined ALIAS_COMMANDS
  # else return entered command because there is no alias for it
  return (command.nil? ? original_command : command)
end
print_method_response!(response_ruby_obj) click to toggle source
resolve_direct_access(params, config_exists=nil) click to toggle source

check if .add_direct_access file exists, if not then add direct access and create .add_direct_access file

# File lib/core.rb, line 156
def resolve_direct_access(params, config_exists=nil)
  return if params[:username_exists]

  puts "Processing ..." if config_exists
  # check to see if catalog credentials are set
  conn = DTK::Client::Session.get_connection
  response = conn.post DTK::Client::CommandBase.class, conn.rest_url("account/check_catalog_credentials"), {}

  # set catalog credentails
  if response.ok? && !response.data['catalog_credentials_set']
    # setting up catalog credentials
    catalog_creds = DTK::Client::Configurator.ask_catalog_credentials
    unless catalog_creds.empty?
      response = conn.post DTK::Client::CommandBase.class, conn.rest_url("account/set_catalog_credentials"), { :username => catalog_creds[:username], :password => catalog_creds[:password], :validate => true}
      if errors = response['errors']
        DTK::Client::OsUtil.print("#{errors.first['message']} You will have to set catalog credentials manually ('dtk account set-catalog-credentials').", :yellow)
      end
    end
  end

  # response = DTK::Client::Account.add_access(params[:ssh_key_path])
  response, matched_pub_key, matched_username = DTK::Client::Account.add_key(params[:ssh_key_path], true, "#{DTK::Client::Session.connection_username}-client")

  if !response.ok?
    DTK::Client::OsUtil.print("We were not able to add access for current user. #{response.error_message}. In order to properly use dtk-shell you will have to add access manually ('dtk account add-ssh-key').\n", :yellow)
  elsif matched_pub_key
    # message will be displayed by add key # TODO: Refactor this flow
    DTK::Client::OsUtil.print("Provided SSH PUB key has already been added.", :yellow)
    DTK::Client::Configurator.add_current_user_to_direct_access
  elsif matched_username
    DTK::Client::OsUtil.print("User with provided name already exists.", :yellow)
  else
    # commented out because 'add_key' method called above will also print the same message
    # DTK::Client::OsUtil.print("Your SSH PUB key has been successfully added.", :yellow)
    DTK::Client::Configurator.add_current_user_to_direct_access
  end

  response
end
run_shell_command() click to toggle source

RUNTIME PART - STARTS HERE

# File lib/shell.rb, line 67
def run_shell_command()
  # init shell client
  init_shell_context()

  # prompt init
  prompt = DTK::Shell::Context::DTK_ROOT_PROMPT

  # trap CTRL-C and remove current text without leaving the dtk-shell
  trap("INT"){
    puts "\n"
    raise Interrupt
  }

  # runtime part
  begin
    while line = Readline.readline(prompt, true)
      prompt = execute_shell_command(line, prompt) unless line.strip.empty?
    end
  rescue DTK::Shell::ExitSignal => e
    # do nothing
  rescue ArgumentError => e
    puts e.backtrace if ::DTK::Configuration.get(:development_mode)
    retry
  rescue Interrupt => e
    retry
  rescue Exception => e
    client_internal_error = DTK::Client::DtkError::Client.label()
    DtkLogger.instance.error_pp("[#{client_internal_error}] #{e.message}", e.backtrace)
  ensure
    puts "\n" unless e.is_a? DTK::Shell::ExitSignal
    # logout
    DTK::Client::Session.logout()
    # save users history
    DTK::Shell::Context.save_session_history(Readline::HISTORY.to_a)
    exit!
  end
end
top_level_execute(entity_name, method_name, context_params=nil, options_args=nil, shell_execute=false) click to toggle source
# File lib/core.rb, line 47
def top_level_execute(entity_name, method_name, context_params=nil, options_args=nil, shell_execute=false)
  begin
    top_level_execute_core(entity_name, method_name, context_params, options_args, shell_execute)
  rescue DTK::Client::DtkLoginRequiredError
    # re-logging user and repeating request
    DTK::Client::OsUtil.print("Session expired: re-establishing session & repeating given task", :yellow)
    DTK::Client::Session.re_initialize
    top_level_execute_core(entity_name, method_name, context_params, options_args, shell_execute)
  end
end
top_level_execute_core(entity_name, method_name, context_params=nil, options_args=nil, shell_execute=false) click to toggle source
# File lib/core.rb, line 58
def top_level_execute_core(entity_name, method_name, context_params=nil, options_args=nil, shell_execute=false)
  extend DTK::Client::OsUtil

  entity_class = nil

  begin
    include DTK::Client::Auxiliary

    entity_name = entity_name.gsub("-","_")
    load_command(entity_name)
    conn = DTK::Client::Session.get_connection

    # if connection parameters are not set up properly then don't execute any command
    return if validate_connection(conn)

    # call proper thor class and task
    entity_class = DTK::Client.const_get "#{cap_form(entity_name)}"

    # call forwarding, in case there is no task for given entity we switch to last (n-context) and try than
    unless (entity_class.task_names.include?(method_name))
      entity_class = DTK::Client.const_get "#{cap_form(context_params.last_entity_name.to_s)}"
    end

    response_ruby_obj = entity_class.execute_from_cli(conn,method_name,context_params,options_args,shell_execute)

    # it will raise DTK::Client::Error in case of error response
    print_method_response!(response_ruby_obj)

    # process/print queued message from server
    DTK::Shell::MessageQueue.print_messages

  rescue DTK::Client::DtkLoginRequiredError => e
    # this error is handled in method above
    raise e
  rescue DTK::Client::DSLParsing => e
    DTK::Client::OsUtil.print(e.message, :red)
  rescue DTK::Client::DtkValidationError => e
    validation_message = e.message

    # if !e.skip_usage_info && entity_class && method_name
    #   usage_info = entity_class.get_usage_info(entity_name, method_name)
    #   validation_message += ", usage: #{usage_info}"
    # end

    if e.display_usage_info && entity_class && method_name
      usage_info = entity_class.get_usage_info(entity_name, method_name)
      validation_message += ", usage: #{usage_info}"

      validation_message.gsub!("^^", '') if validation_message.include?("^^")
      validation_message.gsub!("HIDE_FROM_BASE ", '') if validation_message.include?("HIDE_FROM_BASE")
    end

    DTK::Client::OsUtil.print(validation_message, :yellow)
  rescue DTK::Client::DtkError => e
    # this are expected application errors
    DtkLogger.instance.error_pp(e.message, e.backtrace)
  rescue Exception => e
    client_internal_error = DTK::Client::DtkError::Client.label()
    DtkLogger.instance.fatal_pp("[#{client_internal_error}] DTK has encountered an error #{e.class}: #{e.message}", e.backtrace)
  end
end
validate_connection(connection) click to toggle source

check if connection is set up properly

# File lib/core.rb, line 145
def validate_connection(connection)
  if connection.connection_error?
    connection.print_warning
    puts "\nDTK will now exit. Please set up your connection properly and try again."
    return true
  end

  false
end