collins_shell 0.2.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.pryrc +1 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +18 -0
  4. data/Gemfile.lock +59 -0
  5. data/README.md +335 -0
  6. data/Rakefile +64 -0
  7. data/VERSION +1 -0
  8. data/bin/collins-shell +36 -0
  9. data/collins_shell.gemspec +95 -0
  10. data/lib/collins_shell.rb +3 -0
  11. data/lib/collins_shell/asset.rb +198 -0
  12. data/lib/collins_shell/cli.rb +185 -0
  13. data/lib/collins_shell/console.rb +129 -0
  14. data/lib/collins_shell/console/asset.rb +127 -0
  15. data/lib/collins_shell/console/cache.rb +17 -0
  16. data/lib/collins_shell/console/command_helpers.rb +131 -0
  17. data/lib/collins_shell/console/commands.rb +28 -0
  18. data/lib/collins_shell/console/commands/cat.rb +123 -0
  19. data/lib/collins_shell/console/commands/cd.rb +61 -0
  20. data/lib/collins_shell/console/commands/io.rb +26 -0
  21. data/lib/collins_shell/console/commands/iterators.rb +190 -0
  22. data/lib/collins_shell/console/commands/tail.rb +178 -0
  23. data/lib/collins_shell/console/commands/versions.rb +42 -0
  24. data/lib/collins_shell/console/filesystem.rb +121 -0
  25. data/lib/collins_shell/console/options_helpers.rb +8 -0
  26. data/lib/collins_shell/errors.rb +7 -0
  27. data/lib/collins_shell/ip_address.rb +144 -0
  28. data/lib/collins_shell/ipmi.rb +67 -0
  29. data/lib/collins_shell/monkeypatch.rb +60 -0
  30. data/lib/collins_shell/provision.rb +152 -0
  31. data/lib/collins_shell/state.rb +98 -0
  32. data/lib/collins_shell/tag.rb +41 -0
  33. data/lib/collins_shell/thor.rb +209 -0
  34. data/lib/collins_shell/util.rb +120 -0
  35. data/lib/collins_shell/util/asset_printer.rb +265 -0
  36. data/lib/collins_shell/util/asset_stache.rb +32 -0
  37. data/lib/collins_shell/util/log_printer.rb +187 -0
  38. data/lib/collins_shell/util/printer_util.rb +28 -0
  39. metadata +200 -0
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.14
data/bin/collins-shell ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ENV.key?('COLLINS_DEBUG') && ENV['COLLINS_DEBUG'] then
4
+ HTTP_DEBUG = true
5
+ end
6
+
7
+ # Make sure we're running a valid version of ruby
8
+ if RUBY_VERSION !~ /^1\.9\./ then
9
+ puts("We require ruby 1.9.2")
10
+ exit(1)
11
+ end
12
+
13
+ if not ENV.key?('HOME') || ENV['HOME'].empty? then
14
+ puts("No HOME environment found. Exiting")
15
+ exit(2)
16
+ end
17
+
18
+ $:.unshift File.join File.dirname(__FILE__), *%w[.. lib]
19
+ # Allows us to use a dev version of collins-client if needed
20
+ $:.unshift File.join File.dirname(__FILE__), *%w[.. .. ruby collins-client lib]
21
+ %w[collins_shell collins_shell/cli].each {|f| require f}
22
+
23
+ begin
24
+ CollinsShell::Cli.start
25
+ rescue SystemExit => e
26
+ rescue CollinsShell::ConfigurationError => e
27
+ cli = CollinsShell::Cli.new
28
+ cli.say_status("fatal", "your environment is not setup correctly", :red)
29
+ cli.say_status("message", "#{e.message}")
30
+ exit(1)
31
+ rescue Exception => e
32
+ cli = CollinsShell::Cli.new
33
+ cli.print_error e
34
+ exit(2)
35
+ end
36
+ exit(0)
@@ -0,0 +1,95 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "collins_shell"
8
+ s.version = "0.2.14"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Blake Matheny"]
12
+ s.date = "2012-10-31"
13
+ s.description = "Provides basic CLI for interacting with Collins API"
14
+ s.email = "bmatheny@tumblr.com"
15
+ s.executables = ["collins-shell"]
16
+ s.extra_rdoc_files = [
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".pryrc",
21
+ ".rvmrc",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/collins-shell",
28
+ "collins_shell.gemspec",
29
+ "lib/collins_shell.rb",
30
+ "lib/collins_shell/asset.rb",
31
+ "lib/collins_shell/cli.rb",
32
+ "lib/collins_shell/console.rb",
33
+ "lib/collins_shell/console/asset.rb",
34
+ "lib/collins_shell/console/cache.rb",
35
+ "lib/collins_shell/console/command_helpers.rb",
36
+ "lib/collins_shell/console/commands.rb",
37
+ "lib/collins_shell/console/commands/cat.rb",
38
+ "lib/collins_shell/console/commands/cd.rb",
39
+ "lib/collins_shell/console/commands/io.rb",
40
+ "lib/collins_shell/console/commands/iterators.rb",
41
+ "lib/collins_shell/console/commands/tail.rb",
42
+ "lib/collins_shell/console/commands/versions.rb",
43
+ "lib/collins_shell/console/filesystem.rb",
44
+ "lib/collins_shell/console/options_helpers.rb",
45
+ "lib/collins_shell/errors.rb",
46
+ "lib/collins_shell/ip_address.rb",
47
+ "lib/collins_shell/ipmi.rb",
48
+ "lib/collins_shell/monkeypatch.rb",
49
+ "lib/collins_shell/provision.rb",
50
+ "lib/collins_shell/state.rb",
51
+ "lib/collins_shell/tag.rb",
52
+ "lib/collins_shell/thor.rb",
53
+ "lib/collins_shell/util.rb",
54
+ "lib/collins_shell/util/asset_printer.rb",
55
+ "lib/collins_shell/util/asset_stache.rb",
56
+ "lib/collins_shell/util/log_printer.rb",
57
+ "lib/collins_shell/util/printer_util.rb"
58
+ ]
59
+ s.homepage = "https://github.com/tumblr/collins/tree/master/support/collins-shell"
60
+ s.licenses = ["APL 2.0"]
61
+ s.require_paths = ["lib"]
62
+ s.rubygems_version = "1.8.24"
63
+ s.summary = "Shell for Collins API"
64
+
65
+ if s.respond_to? :specification_version then
66
+ s.specification_version = 3
67
+
68
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69
+ s.add_runtime_dependency(%q<collins_client>, ["~> 0.2.7"])
70
+ s.add_runtime_dependency(%q<highline>, ["~> 1.6.15"])
71
+ s.add_runtime_dependency(%q<mustache>, ["~> 0.99.4"])
72
+ s.add_runtime_dependency(%q<pry>, ["~> 0.9.9.6"])
73
+ s.add_runtime_dependency(%q<rubygems-update>, ["~> 1.8.24"])
74
+ s.add_runtime_dependency(%q<terminal-table>, ["~> 1.4.5"])
75
+ s.add_runtime_dependency(%q<thor>, ["~> 0.16.0"])
76
+ else
77
+ s.add_dependency(%q<collins_client>, ["~> 0.2.7"])
78
+ s.add_dependency(%q<highline>, ["~> 1.6.15"])
79
+ s.add_dependency(%q<mustache>, ["~> 0.99.4"])
80
+ s.add_dependency(%q<pry>, ["~> 0.9.9.6"])
81
+ s.add_dependency(%q<rubygems-update>, ["~> 1.8.24"])
82
+ s.add_dependency(%q<terminal-table>, ["~> 1.4.5"])
83
+ s.add_dependency(%q<thor>, ["~> 0.16.0"])
84
+ end
85
+ else
86
+ s.add_dependency(%q<collins_client>, ["~> 0.2.7"])
87
+ s.add_dependency(%q<highline>, ["~> 1.6.15"])
88
+ s.add_dependency(%q<mustache>, ["~> 0.99.4"])
89
+ s.add_dependency(%q<pry>, ["~> 0.9.9.6"])
90
+ s.add_dependency(%q<rubygems-update>, ["~> 1.8.24"])
91
+ s.add_dependency(%q<terminal-table>, ["~> 1.4.5"])
92
+ s.add_dependency(%q<thor>, ["~> 0.16.0"])
93
+ end
94
+ end
95
+
@@ -0,0 +1,3 @@
1
+ $:.unshift File.join File.dirname(__FILE__)
2
+ require 'collins_shell/errors'
3
+ require 'pp'
@@ -0,0 +1,198 @@
1
+ require 'collins_shell/thor'
2
+ require 'collins_shell/util'
3
+ require 'collins_shell/util/asset_printer'
4
+ require 'thor'
5
+ require 'thor/group'
6
+
7
+ module CollinsShell
8
+
9
+ class Asset < Thor
10
+ include ThorHelper
11
+ include CollinsShell::Util
12
+ namespace :asset
13
+ def self.banner task, namespace = true, subcommand = false
14
+ "#{basename} #{task.formatted_usage(self, true, subcommand).gsub(':',' ')}"
15
+ end
16
+
17
+ desc 'create', 'create an asset in collins'
18
+ use_collins_options
19
+ use_tag_option(true)
20
+ method_option :ipmi, :type => :boolean, :desc => 'Generate IPMI data'
21
+ method_option :status, :type => :string, :desc => 'Status of asset'
22
+ method_option :type, :type => :string, :desc => 'Asset type'
23
+ def create
24
+ call_collins get_collins_client, "create asset" do |client|
25
+ asset = client.create! options.tag, :generate_ipmi => options.ipmi, :status => options.status, :type => options.type
26
+ print_find_results asset, nil
27
+ end
28
+ end
29
+
30
+ desc 'delete', 'delete an asset in collins (must be cancelled)'
31
+ use_collins_options
32
+ use_tag_option(true)
33
+ method_option :reason, :required => true, :type => :string, :desc => 'Reason to delete asset'
34
+ def delete
35
+ call_collins get_collins_client, "delete asset" do |client|
36
+ if client.delete!(options.tag, :reason => options.reason) then
37
+ say_success "deleted asset #{options.tag}"
38
+ else
39
+ say_error "deleting asset #{options.tag}", :exit => true
40
+ end
41
+ end
42
+ end
43
+
44
+ desc 'find', 'find assets using the specified selector'
45
+ use_collins_options
46
+ use_selector_option(true)
47
+ method_option :confirm, :type => :boolean, :default => :true, :desc => 'Require exec confirmation. Defaults to true'
48
+ method_option :details, :type => :boolean, :desc => 'Output assets how you see them in get'
49
+ method_option :exec, :type => :string, :desc => 'Execute a command using the data from this asset. Use {{hostname}}, {{ipmi.password}}, etc for substitution'
50
+ method_option :header, :type => :boolean, :default => true, :desc => 'Display a tag header. Defaults to true'
51
+ method_option :logs, :type => :boolean, :default => false, :desc => 'Also display asset logs, only used with details (SLOW)'
52
+ method_option :size, :type => :numeric, :default => 50, :desc => 'Number of results to find. Defaults to 50'
53
+ method_option :tags, :type => :array, :desc => 'Tags to display of form: tag1 tag2 tag3. e.g. --tags=hostname tag backend_ip_address'
54
+ method_option :url, :type => :boolean, :default => true, :desc => 'Display a URL along with tags or the default listing'
55
+ def find
56
+ client = get_collins_client
57
+ tags = options.tags || [:stuff]
58
+ selector = get_selector options.selector, tags, options["size"], options.remote
59
+ assets = client.find selector
60
+ if options.details then
61
+ assets.each do |asset|
62
+ if not options.quiet then
63
+ logs = []
64
+ if options.logs and not options.exec? then
65
+ logs = client.logs(asset, :size => 5000, :SORT => "DESC").reverse
66
+ end
67
+ printer = CollinsShell::AssetPrinter.new asset, self, :separator => '*',
68
+ :logs => logs,
69
+ :detailed => !options.exec?
70
+ puts printer
71
+ end
72
+ asset_exec asset, options.exec, options.confirm
73
+ end
74
+ else
75
+ if not options.quiet then
76
+ print_find_results assets, options.tags, :header => options.header, :url => options.url
77
+ end
78
+ assets.each {|asset| asset_exec(asset, options.exec, options.confirm)}
79
+ end
80
+ end
81
+
82
+ desc 'find_similar TAG', 'get similar unallocated assets for a gven asset'
83
+ use_collins_options
84
+ method_option :size, :type => :numeric, :default => 50, :desc => 'number of results to find. Defaults to 50'
85
+ method_option :details, :type => :boolean, :desc => 'Output assets how you see them in get'
86
+ method_option :sort, :type => :string, :default => "DESC", :desc => 'sort direction, either ASC or DESC'
87
+ method_option :sort_type, :type => :string, :default => "distribution", :desc => 'sorting type'
88
+ method_option :only_unallocated, :type => :boolean, :default => true, :desc => 'only return unallocated assets, defaults to true'
89
+ def find_similar tag
90
+ call_collins get_collins_client, "similar asset" do |client|
91
+ as_asset = Collins::Asset.new(tag)
92
+ assets = client.find_similar as_asset, options["size"], options["sort"], options.sort_type, options.only_unallocated
93
+ if options.details then
94
+ assets.each do |asset|
95
+ printer = CollinsShell::AssetPrinter.new asset, self, :separator => '*'
96
+ puts printer
97
+ end
98
+ else
99
+ print_find_results assets, options.tags
100
+ end
101
+ end
102
+ end
103
+
104
+ desc 'get TAG', 'get an asset and display its attributes'
105
+ use_collins_options
106
+ method_option :confirm, :type => :boolean, :default => :true, :desc => 'Require exec confirmation. Defaults to true'
107
+ method_option :exec, :type => :string, :desc => 'Execute a command using the data from this asset. Use {{hostname}}, {{ipmi.password}}, etc for substitution'
108
+ method_option :logs, :type => :boolean, :default => false, :desc => 'Also display asset logs'
109
+ method_option :remote, :type => :string, :desc => 'Remote location to search. This is a tag in collins corresponding to the datacenter asset'
110
+ def get tag
111
+ asset = asset_get tag, options
112
+ if asset then
113
+ if not options.quiet then
114
+ logs = []
115
+ if options.logs and not options.exec? then
116
+ logs = call_collins(get_collins_client, "logs") do |client|
117
+ client.logs(asset, :size => 5000, :sort => "DESC").reverse
118
+ end
119
+ end
120
+ printer = CollinsShell::AssetPrinter.new asset, self, :logs => logs, :detailed => !options.exec?
121
+ puts printer
122
+ end
123
+ asset_exec asset, options.exec, options.confirm
124
+ else
125
+ say_error "No such asset #{tag}"
126
+ end
127
+ end
128
+
129
+ desc 'set_attribute KEY VALUE', 'set an attribute in collins'
130
+ use_collins_options
131
+ use_tag_option
132
+ use_selector_option
133
+ method_option :json, :type => :boolean, :default => false, :desc => 'Encode as JSON value. Only works for arrays and hashes.'
134
+ def set_attribute key, value
135
+ batch_selector_operation Hash[
136
+ :remote => options.remote,
137
+ :operation => "set_attribute",
138
+ :success_message => proc {|asset| "Set attribute on #{asset.tag}"},
139
+ :error_message => proc{|asset| "Setting attribute on #{asset.tag}"},
140
+ :confirmation_message => proc do |assets|
141
+ "You are about to set #{key}=#{value} on #{assets.length} hosts. ARE YOU SURE?"
142
+ end
143
+ ] do |client,asset|
144
+ if options.json then
145
+ value = JSON.dump(eval(value))
146
+ end
147
+ client.set_attribute!(asset, key, value)
148
+ end
149
+ end
150
+
151
+ desc 'delete_attribute KEY', 'delete an attribute in collins'
152
+ use_collins_options
153
+ use_tag_option
154
+ use_selector_option
155
+ def delete_attribute key
156
+ batch_selector_operation Hash[
157
+ :remote => options.remote,
158
+ :operation => "delete_attribute",
159
+ :success_message => proc {|asset| "Delete attribute on #{asset.tag}"},
160
+ :error_message => proc{|asset| "Delete attribute on #{asset.tag}"},
161
+ :confirmation_message => proc do |assets|
162
+ "You are about to delete #{key} on #{assets.length} hosts. ARE YOU SURE?"
163
+ end
164
+ ] do |client,asset|
165
+ client.delete_attribute!(asset, key)
166
+ end
167
+ end
168
+
169
+ desc 'set_status', 'set status, state, or both on an asset'
170
+ use_collins_options
171
+ use_tag_option
172
+ use_selector_option
173
+ method_option :reason, :type => :string, :required => true, :desc => 'Reason for changing status'
174
+ method_option :state, :type => :string, :required => false, :desc => 'Set state of asset as well'
175
+ method_option :status, :type => :string, :required => false, :desc => 'Set status of asset'
176
+ def set_status
177
+ status = options.status
178
+ state = options.state
179
+ reason = options.reason
180
+ if status.nil? && state.nil? then
181
+ raise ::Collins::ExpectationFailedError.new("set_status requires either a status or a state")
182
+ end
183
+ batch_selector_operation Hash[
184
+ :remote => options.remote,
185
+ :operation => "set_status",
186
+ :success_message => proc {|asset| "Set status to #{status} on #{asset.tag}"},
187
+ :error_message => proc{|asset| "Setting status on #{asset.tag}"},
188
+ :confirmation_message => proc do |assets|
189
+ "You are about to set status to #{status} on #{assets.length} hosts. ARE YOU SURE?"
190
+ end
191
+ ] do |client,asset|
192
+ client.set_status!(asset, :reason => reason, :status => status, :state => state)
193
+ end
194
+ end
195
+
196
+ end
197
+
198
+ end
@@ -0,0 +1,185 @@
1
+ require 'collins_shell/asset'
2
+ require 'collins_shell/console'
3
+ require 'collins_shell/ip_address'
4
+ require 'collins_shell/ipmi'
5
+ require 'collins_shell/provision'
6
+ require 'collins_shell/state'
7
+ require 'collins_shell/tag'
8
+ require 'collins_shell/thor'
9
+ require 'collins_shell/util'
10
+ require 'collins_shell/util/log_printer'
11
+ require 'thor'
12
+ require 'thor/group'
13
+
14
+ # Need these to support latest method
15
+ require 'rubygems'
16
+ require 'rubygems/spec_fetcher'
17
+
18
+ module CollinsShell
19
+
20
+ class Cli < Thor
21
+
22
+ include ThorHelper
23
+ include CollinsShell::Util
24
+
25
+ register(CollinsShell::Asset, 'asset', 'asset <command>', 'Asset related commands')
26
+ register(CollinsShell::Tag, 'tag', 'tag <command>', 'Tag related commands')
27
+ register(CollinsShell::IpAddress, 'ip_address', 'ip_address <command>', 'IP address related commands')
28
+ register(CollinsShell::Ipmi, 'ipmi', 'ipmi <command>', 'IPMI related commands')
29
+ register(CollinsShell::Provision, 'provision', 'provision <command>', 'Provisioning related commands')
30
+ register(CollinsShell::State, 'state', 'state <command>', 'State management related commands - use with care')
31
+
32
+ desc 'latest', 'check if there is a newer version of collins-shell'
33
+ def latest
34
+ puts(get_latest_version)
35
+ end
36
+
37
+ desc 'version', 'current version of collins-shell'
38
+ def version
39
+ puts("collins-shell #{get_version}")
40
+ end
41
+
42
+ desc 'power_status', 'check power status on an asset'
43
+ use_collins_options
44
+ use_tag_option
45
+ use_selector_option
46
+ def power_status
47
+ batch_selector_operation Hash[
48
+ :remote => options.remote,
49
+ :operation => "power_status",
50
+ :success_message => proc {|asset| "Got power status for #{asset.tag}"},
51
+ :error_message => proc{|asset| "Error getting power status for #{asset.tag}"},
52
+ :confirmation_message => proc do |assets|
53
+ "You are about to check the power status of #{assets.length} hosts. ARE YOU SURE?"
54
+ end
55
+ ] do |client,asset|
56
+ puts('*'*80)
57
+ begin
58
+ status = client.power_status(asset)
59
+ say_success "Power status of #{asset.tag}: #{status}"
60
+ rescue Exception => e
61
+ print_error e, "Unable to check power status of #{asset.tag}", false
62
+ end
63
+ end
64
+ end
65
+
66
+ desc 'power ACTION', 'perform power action (off, on, rebootSoft, rebootHard, etc) on an asset'
67
+ use_collins_options
68
+ use_tag_option(true)
69
+ method_option :reason, :type => :string, :required => true, :desc => 'Reason for reboot'
70
+ def power action
71
+ action = Collins::Power.normalize_action(action)
72
+ call_collins get_collins_client, "power" do |client|
73
+ client.log! options.tag, options.reason, 'ALERT'
74
+ if client.power!(options.tag, action) then
75
+ say_success "power #{action} on #{options.tag}"
76
+ else
77
+ say_error "power #{action} on #{options.tag}", :exit => true
78
+ end
79
+ end
80
+ end
81
+
82
+ desc 'log MESSAGE', 'log a message on an asset'
83
+ use_collins_options
84
+ use_tag_option
85
+ use_selector_option
86
+ method_option :severity, :type => :string, :desc => 'Log severity (EMERGENCY, WARN, etc)'
87
+ def log message
88
+ batch_selector_operation Hash[
89
+ :remote => options.remote,
90
+ :operation => "log",
91
+ :success_message => proc {|asset| "Logged to #{asset.tag}"},
92
+ :error_message => proc{|asset| "Log to #{asset.tag}"},
93
+ :confirmation_message => proc do |assets|
94
+ "You are about to log '#{message}' on #{assets.length} hosts. ARE YOU SURE?"
95
+ end
96
+ ] do |client,asset|
97
+ client.log!(asset, message, options.severity)
98
+ end
99
+ end
100
+
101
+ desc 'logs TAG', 'fetch logs for an asset specified by its tag. Use "all" for all logs'
102
+ use_collins_options
103
+ use_page_options(5000)
104
+ method_option :severity, :type => :array, :desc => 'Severities to include'
105
+ def logs tag
106
+ call_collins get_collins_client, "logs" do |client|
107
+ severity = []
108
+ if options.severity? then
109
+ severity = options.severity
110
+ end
111
+ params = Hash[
112
+ :filter => severity.join(';'),
113
+ :size => options[:size].to_i,
114
+ :sort => options[:sort]
115
+ ]
116
+ logs = client.logs tag, params.merge(:all_tag => 'all')
117
+ logs.reverse! if options[:sort].to_s.downcase == 'desc'
118
+ printer = CollinsShell::LogPrinter.new(tag, logs)
119
+ puts printer.render
120
+ end
121
+ end
122
+
123
+ desc 'console', 'drop into the interactive collins shell'
124
+ use_collins_options
125
+ def console
126
+ ensure_password
127
+ CollinsShell::Console.launch(options)
128
+ end
129
+
130
+ no_tasks do
131
+ def get_version
132
+ version_file = File.absolute_path(File.join(File.dirname(__FILE__), '..', '..', 'VERSION'))
133
+ if File.exists?(version_file) then
134
+ File.read(version_file)
135
+ else
136
+ "0.0.0"
137
+ end
138
+ end
139
+
140
+ def get_latest_version
141
+ begin
142
+ Gem.sources = ["http://repo.tumblr.net:9929"]
143
+ gem = Gem::SpecFetcher.new
144
+ shell = gem.fetch(Gem::Dependency.new('collins_shell')).flatten.first
145
+ rescue Exception => e
146
+ return "Could not retrieve latest gem info from #{Gem.sources.join(',')}"
147
+ end
148
+ if shell.nil? then
149
+ "Could not retrieve latest gem info from repo.tumblr.net"
150
+ else
151
+ my_version = Gem::Version.new(get_version)
152
+ if shell.version == my_version then
153
+ "You are running the most recent version of collins-shell: #{my_version.to_s}"
154
+ elsif shell.version > my_version then
155
+ "Time to upgrade! You are running collins-shell #{my_version.to_s}, latest is #{shell.version.to_s}"
156
+ else
157
+ "You are probably a developer. You are running version #{my_version.to_s}, latest published is #{shell.version.to_s}"
158
+ end
159
+ end
160
+ end
161
+
162
+ def print_error e, cmd = nil, separator = true
163
+ cmd = "unable to run command '#{$0} #{ARGV.join(' ')}'" unless cmd
164
+ say_status("fatal", cmd, :red)
165
+ say_status("exception", "#{e.message}", :red)
166
+ is_debug = ARGV.include?("--debug") || ARGV.include?("--trace")
167
+ if e.is_a?(Collins::RequestError) then
168
+ puts('*'*80) if separator
169
+ say_status("details", e.description(is_debug).strip.sub(e.message,""), :red)
170
+ end
171
+ if ARGV.include?("--debug") || ARGV.include?("--trace") then
172
+ say_status("DEBUG", "local backtrace is presented latest to earliest (reverse cronological)")
173
+ puts("#{e.class.to_s}")
174
+ e.backtrace.each do |line|
175
+ file, line, method = line.split(":", 3)
176
+ puts "\t\t#{method}(#{file}:#{line})"
177
+ end
178
+ end
179
+ end # print_error
180
+ end # no_tasks
181
+
182
+
183
+ end
184
+
185
+ end