- 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 <<-BANNER
Usage: cd /<tag>
cd <tag>
cd /path
cd path
Changes context to the specified tag or path.
Further commands apply to the asset or path.
BANNER
def process
path = arg_string.split(/\//)
stack = _pry_.binding_stack.dup
stack = [stack.first] if path.empty?
path.each do |context|
begin
case context.chomp
when ""
stack = [stack.first]
when "."
next
when ".."
unless stack.size == 1
stack.pop
end
else
fs_parent = stack.last.eval('self')
begin
fs_child = fs_parent.push(context)
output.puts fs_child.path
stack.push(Pry.binding_for(fs_child))
rescue Exception => e
output.puts("#{text.bold('Could not change context:')} #{e}")
end
end
rescue Exception => e
output.puts e.backtrace
output.puts("Got exception: #{e}")
end
end _pry_.binding_stack = stack
end end
end
- 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 <<-BANNER
Usage: cat [-l|--logs] [-b|--brief] [--help]
Cat the specified asset or log.
If you are in an asset context, cat does not require the asset tag. In an asset context you can do:
cat # no-arg displays current asset
cat -b # short-display
cat -l # table formatted logs
cat /var/log/SEVERITY # display logs of a specified type
cat /var/log/messages # all logs
If you are not in an asset context, cat requires the tag of the asset you want to display.
cat # display this help
cat asset-tag # display asset
cat -b asset-tag # short-display
cat -l asset-tag # table formatted logs
cat /var/log/assets/asset-tag # display logs for asset
cat /var/log/hosts/hostname # display logs for host with name
BANNER
opt.on :b, "brief", "Brief output, not detailed"
opt.on :l, "logs", "Display logs as well"
end
def process
stack = _pry_.binding_stack
if args.first.to_s.start_with?('/var/log/') then
display_logs args.first, stack
else
tag = resolve_asset_tag args.first, stack
if tag.nil? then
run "help", "cat"
return
end
display_asset tag
end
end
def display_logs logfile, stack
rejects = ['var', 'log']
paths = logfile.split('/').reject{|s| s.empty? || rejects.include?(s)}
if paths.size == 2 then
display_sub_logs paths[0], paths[1]
elsif paths.size == 1 then
display_asset_logs tag_from_stack(stack), paths[0]
else
run "help", "cat"
end
end
def display_sub_logs arg1, arg2
if arg1 == 'assets' then
display_asset_logs arg2, 'messages'
elsif arg1 == 'hosts' then
asset = find_one_asset(['HOSTNAME', arg2])
display_asset_logs asset, 'messages'
else
output.puts "#{text.bold('Invalid log type:')} Only 'assets' or 'hosts' are valid, found '#{arg1}'"
output.puts
run "help", "cat"
end
end
def display_asset_logs asset_tag, type
begin
asset = Collins::Util.get_asset_or_tag(asset_tag).tag
rescue => e
output.puts "#{text.bold('Invalid asset:')} '#{asset_tag}' not valid - #{e}"
return
end
severity = Collins::Api::Logging::Severity
severity_level = severity.value_of type
if type == 'messages' then
get_and_print_logs asset_tag, "ASC"
elsif not severity_level.nil? then
get_and_print_logs asset_tag, "ASC", severity_level
else
message = "Only '/var/log/messages' or '/var/log/SEVERITY' are valid here"
sevs = severity.to_a.join(', ')
output.puts "#{text.bold('Invalid path specified:')}: #{message}"
output.puts "Valid severity levels are: #{sevs}"
end
end
def display_asset tag
tag = tag.gsub(/^\//, '')
if asset_exists? tag then
asset = get_asset tag
show_logs = opts.logs?
show_details = !opts.brief?
show_color = Pry.color
logs = []
logs = call_collins("logs(#{tag})") {|c| c.logs(tag, :size => 5000)} if show_logs
printer = CollinsShell::AssetPrinter.new asset, shell_handle, :logs => logs, :detailed => show_details, :color => show_color
render_output printer.to_s
else
output.puts "#{text.bold('No such asset:')} #{tag}"
end
end
def get_and_print_logs asset_tag, sort, filter = nil, size = 5000
logs = call_collins "logs(#{asset_tag})" do |client|
client.logs asset_tag, :sort => sort, :size => size, :filter => filter
end
printer = CollinsShell::LogPrinter.new asset_tag, logs
output.puts printer.to_s
end
end end
- 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 <<-BANNER
Usage: tail [OPTION] [FILE]
tail --follow [FILE]
tail --lines N [FILE]
tail --sleep N [FILE]
Tail the specified log.
If you are in an asset context, tail does not require the asset tag. In an asset context you can do:
tail # display this help
tail -n 10 # same as tail -n 10 /var/log/messages
tail -f # same as tail -f /var/log/messages
tail [-n|-f] /var/log/SEVERITY # tail /var/log/SEVERITY
tail /var/log/messages # last 10 messages
tail -n 10 /var/log/messages # last 10 messages
tail -f /var/log/messages # follow log messages
If you are not in an asset context, log requires the tag of the asset you want to display.
tail # display this help
tail [-n|-f] asset-tag # same as tail in asset context
tail [-n|-f] /var/log/messages # show logs for all assets (requires permission)
tail [-n|-f] /var/log/assets/asset-tag # same as tail in asset context
tail [-n|-f] /var/log/hosts/hostname # same as tail in asset context, but finds host
BANNER
opt.on :f, "follow", "Output appended data as file grows" do
@got_flags = true
@follow = true
end
opt.on :t, :test, "Show logs that have already been seen" do
@got_flags = true
@test = true
end
integer_arg(opt, :n, :lines, "Show the last n lines of the file") do |i|
@lines = i
end
integer_arg(opt, :s, :sleep, "Sleep this many seconds between pools") do |i|
@sleep = i
end
end
def process
stack = _pry_.binding_stack
if args.first.to_s.start_with?('/var/log/') then
display_logs args.first, stack
else
tag = args.first.to_s.strip
if tag.empty? then
if asset_context?(stack) and @got_flags then
display_asset_logs tag_from_stack(stack), 'messages'
else
run "help", "tail"
return
end
else
display_asset_logs tag, 'messages'
end
end
end
def display_logs logfile, stack
rejects = ['var', 'log']
paths = logfile.split('/').reject{|s| s.empty? || rejects.include?(s)}
if paths.size == 2 then
display_sub_logs paths[0], paths[1]
elsif paths.size == 1 then
display_asset_logs (tag_from_stack(stack) || 'all'), paths[0]
else
run "help", "tag"
end
end
def display_sub_logs arg1, arg2
if arg1 == 'assets' then
display_asset_logs arg2, 'messages'
elsif arg1 == 'hosts' then
asset = find_one_asset(['HOSTNAME', arg2])
display_asset_logs asset, 'messages'
else
output.puts "#{text.bold('Invalid log type:')} Only 'assets' or 'hosts' are valid, found '#{arg1}'"
output.puts
run "help", "tag"
end
end
def display_asset_logs asset_tag, type
begin
asset = Collins::Util.get_asset_or_tag(asset_tag).tag
rescue => e
output.puts "#{text.bold('Invalid asset:')} '#{asset_tag}' not valid - #{e}"
return
end
severity = Collins::Api::Logging::Severity
severity_level = severity.value_of type
if type == 'messages' then
get_and_print_logs asset_tag
elsif not severity_level.nil? then
get_and_print_logs asset_tag, severity_level
else
message = "Only '/var/log/messages' or '/var/log/SEVERITY' are valid here"
sevs = severity.to_a.join(', ')
output.puts "#{text.bold('Invalid path specified:')}: #{message}"
output.puts "Valid severity levels are: #{sevs}"
end
end
def all? tag
tag.to_s.downcase == 'all'
end
def get_and_print_logs asset_tag, filter = nil
size = @lines
if not @follow then
logs = call_collins "logs(#{asset_tag})" do |client|
client.logs asset_tag, :sort => "DESC", :size => size, :filter => filter, :all_tag => 'all'
end.reverse
printer = CollinsShell::LogPrinter.new asset_tag, :logs => logs, :all_tag => 'all'
output.puts printer.to_s
else
seen = Set.new
printer = CollinsShell::LogPrinter.new asset_tag, :streaming => true, :all_tag => 'all'
while true do
logs = call_collins "logs(#{asset_tag})" do |client|
client.logs asset_tag, :sort => "DESC", :size => size, :filter => filter, :all_tag => 'all'
end.reverse
unseen_logs = select_logs_for_follow seen, logs
if unseen_logs.size > 0 then
output.puts printer.render(unseen_logs)
end
sleep(@sleep)
end end end
def select_logs_for_follow seen_set, logs
if @test then
logs
else
unseen_logs = logs.reject {|l| seen_set.include?(l.to_s.hash)}
unseen_logs.each {|l| seen_set << l.to_s.hash}
unseen_logs
end
end
end end
- Versions =
Pry::CommandSet.new do
create_command "latest", "Latest version of collins shell" do
group "Software"
def options(opt)
opt.banner <<-BANNER
Usage: latest
Display the latest version of collins shell
BANNER
end
def process
o = CollinsShell::Console.options
render_output CollinsShell::Cli.new([], o).get_latest_version
end
end
create_command "version", "Current version of collins shell" do
group "Software"
def options(opt)
opt.banner <<-BANNER
Usage: version
Display the current version of collins shell
BANNER
end
def process
o = CollinsShell::Console.options
render_output CollinsShell::Cli.new([], o).get_version
end
end
end
- 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 <<-BANNER
Usage: ls [-d|--delimiter] [-g|--grep] [-F--format] [-f|--flood] [path]
ls provides you information based on your current context (either a path or an asset)
When in an asset, ls will show you available commands
When in a path, ls will show you either assets that match the path or values appropriate for the path
When in no context, will show you available tags (to use in a path)
Examples:
ls /HOSTNAME/.*dev.*
ls /HOSTNAME/.*dev.* --format='{{hostname}} {{status}} {{tag}}' --grep=blake
You can customize the default format used by ls (when applied to assets) by creating a ~/.pryrc file with contents like:
Pry.config.default_asset_format = '{{tag}} {{hostname}} {{status}}'
Where the rhs of the default_asset_format is the format you want to use
BANNER
opt
opt.on :d, "delimiter", "Delimiter for use with --format, defaults to \\n", :argument => true
opt.on :r, "return", "Return values as well as outputting them"
opt.on :g, "grep", "A regular expression to provide to results", :argument => true, :optional => false
opt.on :F, "format", "Provide a format for output", :argument => true, :optional => false
end
def process
first_after_pipe = arg_string.split('|', 2)[1].to_s.split(' ').first
path = args.first.to_s.split(/\//).reject{|s| s.empty? || s == "|" }
stack = _pry_.binding_stack.dup
fs_node = stack.last.eval('self')
display_commands = (fs_node.asset? && args.empty?)
if not fs_node.root? and not arg_string.start_with?('/') then
path.each do |context|
case context.chomp
when "", "." next
when ".." fs_node = fs_node.pop
else
begin
fs_node = fs_node.push(context)
rescue Exception => e
output.puts("#{text.bold('Could not check context:')} #{e}")
raise e
end
end
end
path = fs_node.stack
end
if command_block || get_format then
details = true
else
details = false
end
if display_commands then
output.puts("Available formats (see ls --help):")
r = fs_node.asset_methods(true)
process_values r.map{|m| "{{#{m}}}"}, 8
output.puts("\nAvailable commands:")
process_values fs_node.available_commands, 8
output.puts()
elsif path.size == 0 then
value = get_all_tags
process_values value
elsif path.size % 2 == 1 then
if virtual_tags.include?(path.last) then
value = ["virtual tags have no values"]
else
value = get_tag_values(path.last) || []
end
process_values value
else
assets = find_assets(path, details)
process_values assets, 6
end
end
def process_values init_value, size = 4
should_return = opts.return?
cmd = arg_string.split('|',2)
if cmd.size == 2 then
cmds = cmd.last.split('|').map{|s| s.strip}
else
cmds = []
end
formatted = init_value
formatter = nil
if opts.format? then
formatter = opts[:format]
elsif Pry.config.default_asset_format then
if formatted.any? {|o| o.is_a?(Collins::Asset)} then
formatter = Pry.config.default_asset_format
end
end
if formatter then
formatted = formatted.map do |v|
a = CollinsShell::Util::AssetStache.new(v)
a.render formatter
end
end
grep_regex = Regexp.new(opts[:g] || ".")
if formatted.respond_to?(:grep) then
formatted = formatted.grep(grep_regex)
end
if cmds.size > 0 then
results = formatted
while cmd = cmds.shift do
results = run(cmd, results).retval
end
value = results
value_s = results.to_s
else
value = formatted
if formatter then
delim = Collins::Option(opts[:delimiter]).get_or_else("\n")
value_s = value.join(delim)
else
value_s = format_values(value, size)
end
end
if command_block then
command_block.call(value)
else
if not value.nil? and not value_s.empty? then
render_output value_s, opts
end
value if should_return
end
end
def get_format
if opts.format? then
opts[:format]
elsif Pry.config.default_asset_format then
Pry.config.default_asset_format
else
nil
end
end
def format_values array, width = 4
return "" if array.empty?
t = Terminal::Table.new
t.style = {:border_x => "", :border_y => "", :border_i => ""}
line = []
array.each do |o|
line << o
if line.size >= width then
t << line
line = []
end
end
if not line.empty? then
t << line
end
t.to_s
end
end
end