- 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"
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"