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
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
# 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
# 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
# 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
# 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
# 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
# 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
# File lib/shell.rb, line 259 def execute_shell_command_internal(line) execute_shell_command(line, DTK::Shell::Context::DTK_ROOT_PROMPT) end
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
# 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
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
# 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
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
# File lib/core.rb, line 120 def print_method_response!(response_ruby_obj) # this will raise error if found DTK::Client::ResponseErrorHandler.check(response_ruby_obj) # this will find appropriate render adapter and give output, returns boolean if print = response_ruby_obj.render_data print = [print] unless print.kind_of?(Array) print.each do |el| if el.kind_of?(String) el.each_line{|l| STDOUT << l} else PP.pp(el,STDOUT) end end end end
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
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
# 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
# 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
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