Module: CollinsShell::Console::Commands

Defined in:
lib/collins_shell/console/commands.rb,
lib/collins_shell/console/commands/cd.rb,
lib/collins_shell/console/commands/io.rb,
lib/collins_shell/console/commands/cat.rb,
lib/collins_shell/console/commands/tail.rb,
lib/collins_shell/console/commands/versions.rb,
lib/collins_shell/console/commands/iterators.rb

Constant Summary collapse

Default =
Pry::CommandSet.new do
  import CollinsShell::Console::Commands::Cd
  import CollinsShell::Console::Commands::Cat
  import CollinsShell::Console::Commands::Tail
  import CollinsShell::Console::Commands::Io
  import CollinsShell::Console::Commands::Iterators
  import CollinsShell::Console::Commands::Versions
end
Cd =
Pry::CommandSet.new do
  create_command "cd" do
    include CollinsShell::Console::CommandHelpers

    description "Change context to an asset or path"
    group "Context"

    banner "      Usage: cd /<tag>\n             cd <tag>\n             cd /path\n             cd path\n\n      Changes context to the specified tag or path.\n      Further commands apply to the asset or path.\n    BANNER\n    def process\n      path = arg_string.split(/\\//)\n      stack  = _pry_.binding_stack.dup\n\n      stack = [stack.first] if path.empty?\n\n      path.each do |context|\n        begin\n          case context.chomp\n          when \"\"\n            stack = [stack.first]\n          when \".\"\n            next\n          when \"..\"\n            unless stack.size == 1\n              stack.pop\n            end\n          else\n            # We know we start out with the root fs being the context\n            fs_parent = stack.last.eval('self')\n            # Pushing the path value onto the parent gives us back a child\n            begin\n              fs_child = fs_parent.push(context)\n              # We can't have assets as children of assets, replace current asset with new one\n              output.puts fs_child.path\n              stack.push(Pry.binding_for(fs_child))\n            rescue Exception => e\n              output.puts(\"\#{text.bold('Could not change context:')} \#{e}\")\n            end\n          end\n        rescue Exception => e\n          output.puts e.backtrace\n          output.puts(\"Got exception: \#{e}\")\n        end \n      end # path.each\n      _pry_.binding_stack = stack\n    end # def process\n  end\nend\n"
Io =
Pry::CommandSet.new do
  create_command "wc", "Pipe results to wc to get the number of assets/lines" do
    command_options :keep_retval => true

    group "I/O"

    def process
      args.size
    end
  end

  create_command "more", "Ensure results are paginated" do
    command_options :keep_retval => true

    group "I/O"

    def process
      value = args.map {|a| a.to_s}.join("\n")
      render_output value, opts
      nil
    end
  end

end
Cat =
Pry::CommandSet.new do
  create_command "cat", "Output data associated with the specified asset" do

    include CollinsShell::Console::CommandHelpers

    group "I/O"

    def options(opt)
      opt.banner "        Usage: cat [-l|--logs] [-b|--brief] [--help]\n\n        Cat the specified asset or log.\n\n        If you are in an asset context, cat does not require the asset tag. In an asset context you can do:\n          cat                               # no-arg displays current asset\n          cat -b                            # short-display\n          cat -l                            # table formatted logs\n          cat /var/log/SEVERITY             # display logs of a specified type\n          cat /var/log/messages             # all logs\n        If you are not in an asset context, cat requires the tag of the asset you want to display.\n          cat                               # display this help\n          cat asset-tag                     # display asset\n          cat -b asset-tag                  # short-display\n          cat -l asset-tag                  # table formatted logs\n          cat /var/log/assets/asset-tag     # display logs for asset\n          cat /var/log/hosts/hostname       # display logs for host with name\n      BANNER\n      opt.on :b, \"brief\", \"Brief output, not detailed\"\n      opt.on :l, \"logs\", \"Display logs as well\"\n    end\n\n    def process\n      stack = _pry_.binding_stack\n      if args.first.to_s.start_with?('/var/log/') then\n        display_logs args.first, stack\n      else\n        tag = resolve_asset_tag args.first, stack\n        if tag.nil? then\n          run \"help\", \"cat\"\n          return\n        end\n        display_asset tag\n      end\n    end\n\n    # Given a logfile specification, parse it and render it\n    def display_logs logfile, stack\n      rejects = ['var', 'log']\n      paths = logfile.split('/').reject{|s| s.empty? || rejects.include?(s)}\n      if paths.size == 2 then\n        display_sub_logs paths[0], paths[1]\n      elsif paths.size == 1 then\n        display_asset_logs tag_from_stack(stack), paths[0]\n      else\n        run \"help\", \"cat\"\n      end\n    end\n\n    # Render logs of the type `/var/log/assets/TAG` or `/var/log/hosts/HOSTNAME`\n    def display_sub_logs arg1, arg2\n      if arg1 == 'assets' then\n        display_asset_logs arg2, 'messages'\n      elsif arg1 == 'hosts' then\n        asset = find_one_asset(['HOSTNAME', arg2])\n        display_asset_logs asset, 'messages'\n      else\n        output.puts \"\#{text.bold('Invalid log type:')} Only 'assets' or 'hosts' are valid, found '\#{arg1}'\"\n        output.puts\n        run \"help\", \"cat\"\n      end\n    end\n\n    # Render logs for an asset according to type, where type is 'messages' (all) or a severity\n    def display_asset_logs asset_tag, type\n      begin\n        asset = Collins::Util.get_asset_or_tag(asset_tag).tag\n      rescue => e\n        output.puts \"\#{text.bold('Invalid asset:')} '\#{asset_tag}' not valid - \#{e}\"\n        return\n      end\n      severity = Collins::Api::Logging::Severity\n      severity_level = severity.value_of type\n      if type == 'messages' then\n        get_and_print_logs asset_tag, \"ASC\"\n      elsif not severity_level.nil? then\n        get_and_print_logs asset_tag, \"ASC\", severity_level\n      else\n        message = \"Only '/var/log/messages' or '/var/log/SEVERITY' are valid here\"\n        sevs = severity.to_a.join(', ')\n        output.puts \"\#{text.bold('Invalid path specified:')}: \#{message}\"\n        output.puts \"Valid severity levels are: \#{sevs}\"\n      end\n    end\n\n    def display_asset tag\n      tag = tag.gsub(/^\\//, '')\n      if asset_exists? tag then\n        asset = get_asset tag\n        show_logs = opts.logs?\n        show_details = !opts.brief?\n        show_color = Pry.color\n        logs = []\n        logs = call_collins(\"logs(\#{tag})\") {|c| c.logs(tag, :size => 5000)} if show_logs\n        printer = CollinsShell::AssetPrinter.new asset, shell_handle, :logs => logs, :detailed => show_details, :color => show_color\n        render_output printer.to_s\n      else\n        output.puts  \"\#{text.bold('No such asset:')} \#{tag}\"\n      end\n    end\n\n    def get_and_print_logs asset_tag, sort, filter = nil, size = 5000\n      logs = call_collins \"logs(\#{asset_tag})\" do |client|\n        client.logs asset_tag, :sort => sort, :size => size, :filter => filter\n      end\n      printer = CollinsShell::LogPrinter.new asset_tag, logs\n      output.puts printer.to_s\n    end\n\n  end # create_command\nend\n"
Tail =
Pry::CommandSet.new do
  create_command "tail", "Print the last lines of a log file" do

    include CollinsShell::Console::CommandHelpers

    group "I/O"

    def setup
      @lines = 10
      @follow = false
      @got_flags = false
      @sleep = 10
      @test = false
    end

    def integer_arg(opt, short, long, description, &block)
      opt.on short, long, description, :argument => true, :as => :integer do |i|
        if i < 1 then
          raise ArgumentError.new("Missing a required argument for --#{long}")
        end
        @got_flags = true
        block.call(i)
      end
    end

    def options(opt)
      opt.banner "        Usage: tail [OPTION] [FILE]\n               tail --follow [FILE]\n               tail --lines N [FILE]\n               tail --sleep N [FILE]\n\n        Tail the specified log.\n\n        If you are in an asset context, tail does not require the asset tag. In an asset context you can do:\n          tail                                   # display this help\n          tail -n 10                             # same as tail -n 10 /var/log/messages\n          tail -f                                # same as tail -f /var/log/messages\n          tail [-n|-f] /var/log/SEVERITY         # tail /var/log/SEVERITY\n          tail /var/log/messages                 # last 10 messages\n          tail -n 10 /var/log/messages           # last 10 messages\n          tail -f /var/log/messages              # follow log messages\n        If you are not in an asset context, log requires the tag of the asset you want to display.\n          tail                                   # display this help\n          tail [-n|-f] asset-tag                 # same as tail in asset context\n          tail [-n|-f] /var/log/messages         # show logs for all assets (requires permission)\n          tail [-n|-f] /var/log/assets/asset-tag # same as tail in asset context\n          tail [-n|-f] /var/log/hosts/hostname   # same as tail in asset context, but finds host\n      BANNER\n      opt.on :f, \"follow\", \"Output appended data as file grows\" do\n        @got_flags = true\n        @follow = true\n      end\n      opt.on :t, :test, \"Show logs that have already been seen\" do\n        @got_flags = true\n        @test = true\n      end\n      integer_arg(opt, :n, :lines, \"Show the last n lines of the file\") do |i|\n        @lines = i\n      end\n      integer_arg(opt, :s, :sleep, \"Sleep this many seconds between pools\") do |i|\n        @sleep = i\n      end\n    end\n\n    def process\n      stack = _pry_.binding_stack\n      if args.first.to_s.start_with?('/var/log/') then\n        display_logs args.first, stack\n      else\n        tag = args.first.to_s.strip\n        if tag.empty? then\n          if asset_context?(stack) and @got_flags then\n            display_asset_logs tag_from_stack(stack), 'messages'\n          else\n            run \"help\", \"tail\"\n            return\n          end\n        else\n          display_asset_logs tag, 'messages'\n        end\n      end\n    end\n\n    # Given a logfile specification, parse it and render it\n    def display_logs logfile, stack\n      rejects = ['var', 'log']\n      paths = logfile.split('/').reject{|s| s.empty? || rejects.include?(s)}\n      if paths.size == 2 then\n        display_sub_logs paths[0], paths[1]\n      elsif paths.size == 1 then\n        display_asset_logs (tag_from_stack(stack) || 'all'), paths[0]\n      else\n        run \"help\", \"tag\"\n      end\n    end\n\n    # Render logs of the type `/var/log/assets/TAG` or `/var/log/hosts/HOSTNAME`\n    def display_sub_logs arg1, arg2\n      if arg1 == 'assets' then\n        display_asset_logs arg2, 'messages'\n      elsif arg1 == 'hosts' then\n        asset = find_one_asset(['HOSTNAME', arg2])\n        display_asset_logs asset, 'messages'\n      else\n        output.puts \"\#{text.bold('Invalid log type:')} Only 'assets' or 'hosts' are valid, found '\#{arg1}'\"\n        output.puts\n        run \"help\", \"tag\"\n      end\n    end\n\n    # Render logs for an asset according to type, where type is 'messages' (all) or a severity\n    def display_asset_logs asset_tag, type\n      begin\n        asset = Collins::Util.get_asset_or_tag(asset_tag).tag\n      rescue => e\n        output.puts \"\#{text.bold('Invalid asset:')} '\#{asset_tag}' not valid - \#{e}\"\n        return\n      end\n      severity = Collins::Api::Logging::Severity\n      severity_level = severity.value_of type\n      if type == 'messages' then\n        get_and_print_logs asset_tag\n      elsif not severity_level.nil? then\n        get_and_print_logs asset_tag, severity_level\n      else\n        message = \"Only '/var/log/messages' or '/var/log/SEVERITY' are valid here\"\n        sevs = severity.to_a.join(', ')\n        output.puts \"\#{text.bold('Invalid path specified:')}: \#{message}\"\n        output.puts \"Valid severity levels are: \#{sevs}\"\n      end\n    end\n\n    def all? tag\n      tag.to_s.downcase == 'all'\n    end\n\n    def get_and_print_logs asset_tag, filter = nil\n      size = @lines\n      if not @follow then\n        logs = call_collins \"logs(\#{asset_tag})\" do |client|\n          client.logs asset_tag, :sort => \"DESC\", :size => size, :filter => filter, :all_tag => 'all'\n        end.reverse\n        printer = CollinsShell::LogPrinter.new asset_tag, :logs => logs, :all_tag => 'all'\n        output.puts printer.to_s\n      else\n        seen = Set.new\n        printer = CollinsShell::LogPrinter.new asset_tag, :streaming => true, :all_tag => 'all'\n        while true do\n          logs = call_collins \"logs(\#{asset_tag})\" do |client|\n            client.logs asset_tag, :sort => \"DESC\", :size => size, :filter => filter, :all_tag => 'all'\n          end.reverse\n          unseen_logs = select_logs_for_follow seen, logs\n          if unseen_logs.size > 0 then\n            output.puts printer.render(unseen_logs)\n          end\n          sleep(@sleep)\n        end # while true\n      end # else for follow\n    end\n\n    def select_logs_for_follow seen_set, logs\n      if @test then\n        logs\n      else\n        unseen_logs = logs.reject {|l| seen_set.include?(l.to_s.hash)}\n        unseen_logs.each {|l| seen_set << l.to_s.hash}\n        unseen_logs\n      end\n    end\n\n  end # create_command\nend\n"
Versions =
Pry::CommandSet.new do
  create_command "latest", "Latest version of collins shell" do
    group "Software"

    def options(opt)
      opt.banner "        Usage: latest\n\n        Display the latest version of collins shell\n      BANNER\n    end\n\n    def process\n      o = CollinsShell::Console.options\n      render_output CollinsShell::Cli.new([], o).get_latest_version\n    end\n\n  end # create_command\n\n  create_command \"version\", \"Current version of collins shell\" do\n    group \"Software\"\n\n    def options(opt)\n      opt.banner <<-BANNER\n        Usage: version\n\n        Display the current version of collins shell\n      BANNER\n    end\n\n    def process\n      o = CollinsShell::Console.options\n      render_output CollinsShell::Cli.new([], o).get_version\n    end\n\n  end # create_command\n\nend\n"
Iterators =
Pry::CommandSet.new do

  create_command "ls", "Find assets according to specific criteria" do
    include CollinsShell::Console::OptionsHelpers
    include CollinsShell::Console::CommandHelpers

    command_options :keep_retval => true, :takes_block => true
    group "Context"

    # --return (after printing also return value) and --flood (disable paging)
    def options opt
      opt.banner "        Usage: ls [-d|--delimiter] [-g|--grep] [-F--format] [-f|--flood] [path]\n\n        ls provides you information based on your current context (either a path or an asset)\n\n        When in an asset, ls will show you available commands\n        When in a path, ls will show you either assets that match the path or values appropriate for the path\n        When in no context, will show you available tags (to use in a path)\n\n        Examples:\n          ls /HOSTNAME/.*dev.*\n          ls /HOSTNAME/.*dev.* --format='{{hostname}} {{status}} {{tag}}' --grep=blake\n\n        You can customize the default format used by ls (when applied to assets) by creating a ~/.pryrc file with contents like:\n\n            Pry.config.default_asset_format = '{{tag}} {{hostname}} {{status}}'\n\n        Where the rhs of the default_asset_format is the format you want to use\n      BANNER\n      pager_options opt\n      opt.on :d, \"delimiter\", \"Delimiter for use with --format, defaults to \\\\n\", :argument => true\n      opt.on :r, \"return\", \"Return values as well as outputting them\"\n      opt.on :g, \"grep\", \"A regular expression to provide to results\", :argument => true, :optional => false\n      opt.on :F, \"format\", \"Provide a format for output\", :argument => true, :optional => false\n    end\n\n    def process\n      # If a pipe is being used, grab the first pipe command\n      first_after_pipe = arg_string.split('|', 2)[1].to_s.split(' ').first\n      # Take a /PATH/FORMAT and convert it to an array\n      path = args.first.to_s.split(/\\//).reject{|s| s.empty? || s == \"|\" }\n      # Account for Filesystem context\n      stack  = _pry_.binding_stack.dup\n      fs_node = stack.last.eval('self')\n      # Are we just getting a naked query?\n      display_commands = (fs_node.asset? && args.empty?)\n      # Doing an ls in the stack, relative\n      if not fs_node.root? and not arg_string.start_with?('/') then\n        path.each do |context|\n          case context.chomp\n          when \"\", \".\" # blank is empty root node, . is self. Do nothing\n            next\n          when \"..\" # Up one level\n            fs_node = fs_node.pop\n          else\n            begin\n              fs_node = fs_node.push(context)\n            rescue Exception => e\n              output.puts(\"\#{text.bold('Could not check context:')} \#{e}\")\n              raise e\n            end\n          end\n        end\n        path = fs_node.stack\n      end\n      if command_block || get_format then\n        details = true\n      else\n        details = false\n      end\n      # Should we just display commands?\n      if display_commands then\n        output.puts(\"Available formats (see ls --help):\")\n        r = fs_node.asset_methods(true)\n        process_values r.map{|m| \"{{\#{m}}}\"}, 8\n        output.puts(\"\\nAvailable commands:\")\n        process_values fs_node.available_commands, 8\n        output.puts()\n      # If we have nothing, grab all tags\n      elsif path.size == 0 then\n        value = get_all_tags\n        process_values value\n      # If we have an odd number, the last one is a tag so grab the values\n      # for that tag\n      elsif path.size % 2 == 1 then\n        if virtual_tags.include?(path.last) then\n          value = [\"virtual tags have no values\"]\n        else\n          value = get_tag_values(path.last) || []\n        end\n        process_values value\n      # If we have an even number, grab assets that have these tag/values\n      else\n        assets = find_assets(path, details)\n        process_values assets, 6\n      end\n    end\n\n    # Handle faking unix pipes to commands, block invocation, and printing\n    def process_values init_value, size = 4\n      should_return = opts.return?\n      cmd = arg_string.split('|',2)\n\n      if cmd.size == 2 then\n        cmds = cmd.last.split('|').map{|s| s.strip}\n      else\n        cmds = []\n      end\n\n      formatted = init_value\n      formatter = nil\n      if opts.format? then\n        formatter = opts[:format]\n      elsif Pry.config.default_asset_format then\n        if formatted.any? {|o| o.is_a?(Collins::Asset)} then\n          formatter = Pry.config.default_asset_format\n        end\n      end\n\n      if formatter then\n        formatted = formatted.map do |v|\n          a = CollinsShell::Util::AssetStache.new(v)\n          a.render formatter\n        end\n      end\n\n      grep_regex = Regexp.new(opts[:g] || \".\")\n      if formatted.respond_to?(:grep) then\n        formatted = formatted.grep(grep_regex)\n      end\n\n      if cmds.size > 0 then\n        results = formatted\n        while cmd = cmds.shift do\n          results = run(cmd, results).retval\n        end\n        value = results\n        value_s = results.to_s\n      else\n        value = formatted\n        if formatter then\n          delim = Collins::Option(opts[:delimiter]).get_or_else(\"\\n\")\n          value_s = value.join(delim)\n        else\n          value_s = format_values(value, size)\n        end\n      end\n      if command_block then\n        command_block.call(value)\n      else\n        # Handle commands like 'more' that should not return a value\n        if not value.nil? and not value_s.empty? then\n          render_output value_s, opts\n        end\n        value if should_return\n      end\n    end\n\n    def get_format\n      if opts.format? then\n        opts[:format]\n      elsif Pry.config.default_asset_format then\n        Pry.config.default_asset_format\n      else\n        nil\n      end\n    end\n\n    def format_values array, width = 4\n      return \"\" if array.empty?\n      t = Terminal::Table.new\n      t.style = {:border_x => \"\", :border_y => \"\", :border_i => \"\"}\n      line = []\n      array.each do |o|\n        line << o\n        if line.size >= width then\n          t << line\n          line = []\n        end\n      end\n      if not line.empty? then\n        t << line\n      end\n      t.to_s\n    end\n  end\nend\n"