Class: WavefrontCli::Base
- Inherits:
-
Object
- Object
- WavefrontCli::Base
- Includes:
- Wavefront::Validators
- Defined in:
- lib/wavefront-cli/base.rb
Overview
Parent of all the CLI classes. This class uses metaprogramming techniques to try to make adding new CLI commands and sub-commands as simple as possible.
To define a subcommand ‘cmd’, you only need add it to the docopt description in the relevant section, and create a method ‘do_cmd’. The WavefrontCli::Base::dispatch() method will find it, and call it. If your subcommand has multiple words, like ‘delete tag’, your do method would be called do_delete_tag. The do_ methods are able to access the Wavefront SDK object as wf, and all docopt options as options.
Direct Known Subclasses
Alert, CloudIntegration, Dashboard, Event, ExternalLink, Integration, MaintenanceWindow, Message, Metric, Notificant, Proxy, Query, SavedSearch, Source, User, Webhook, Write
Instance Attribute Summary collapse
-
#klass ⇒ Object
Returns the value of attribute klass.
-
#klass_word ⇒ Object
Returns the value of attribute klass_word.
-
#options ⇒ Object
Returns the value of attribute options.
-
#wf ⇒ Object
Returns the value of attribute wf.
Instance Method Summary collapse
- #check_status(status) ⇒ Object
-
#dispatch ⇒ nil
Works out the user’s command by matching any options docopt has set to ‘true’ with any ‘do_’ method in the class.
-
#display(data, method) ⇒ Object
Display a Ruby object as JSON, YAML, or human-readable.
- #do_delete ⇒ Object
- #do_describe ⇒ Object
- #do_import ⇒ Object
-
#do_list ⇒ Object
Below here are common methods.
- #do_search ⇒ Object
- #do_tag_add ⇒ Object
- #do_tag_clear ⇒ Object
- #do_tag_delete ⇒ Object
- #do_tag_set ⇒ Object
- #do_tags ⇒ Object
- #do_undelete ⇒ Object
- #do_update ⇒ Object
-
#format_var ⇒ Symbol
To allow a user to default to different output formats for different object, we are able to define a format for each class.
-
#handle_error(method, code) ⇒ Object
This gives us a chance to catch different errors in WavefrontDisplay classes.
- #handle_response(resp, format, method) ⇒ Object
-
#import_to_create(raw) ⇒ Object
Most things will re-import with the POST method if you remove the ID.
-
#initialize(options) ⇒ Base
constructor
A new instance of Base.
- #load_display_class ⇒ Object
-
#load_file(path) ⇒ Hash
Give it a path to a file (as a string) and it will return the contents of that file as a Ruby object.
-
#mk_creds ⇒ Hash
Make a wavefront-sdk credentials object from standard options.
-
#mk_opts ⇒ Hash
Make a common wavefront-sdk options object from standard CLI options.
- #run ⇒ Object
- #validate_id ⇒ Object
- #validate_input ⇒ Object
-
#validate_opts ⇒ Object
There are things we need to have.
- #validate_tags ⇒ Object
- #validator_exception ⇒ Object
-
#validator_method ⇒ Object
We normally validate with a predictable method name.
Constructor Details
#initialize(options) ⇒ Base
Returns a new instance of Base.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/wavefront-cli/base.rb', line 26 def initialize() = sdk_class = self.class.name.sub(/Cli/, '') @klass_word = sdk_class.split('::').last.downcase validate_input if .include?(:help) && [:help] puts exit 0 end require File.join('wavefront-sdk', @klass_word) @klass = Object.const_get(sdk_class) send(:post_initialize, ) if respond_to?(:post_initialize) end |
Instance Attribute Details
#klass ⇒ Object
Returns the value of attribute klass.
22 23 24 |
# File 'lib/wavefront-cli/base.rb', line 22 def klass @klass end |
#klass_word ⇒ Object
Returns the value of attribute klass_word.
22 23 24 |
# File 'lib/wavefront-cli/base.rb', line 22 def klass_word @klass_word end |
#options ⇒ Object
Returns the value of attribute options.
22 23 24 |
# File 'lib/wavefront-cli/base.rb', line 22 def end |
#wf ⇒ Object
Returns the value of attribute wf.
22 23 24 |
# File 'lib/wavefront-cli/base.rb', line 22 def wf @wf end |
Instance Method Details
#check_status(status) ⇒ Object
192 193 194 |
# File 'lib/wavefront-cli/base.rb', line 192 def check_status(status) status.respond_to?(:result) && status.result == 'OK' end |
#dispatch ⇒ nil
Works out the user’s command by matching any options docopt has set to ‘true’ with any ‘do_’ method in the class. Then calls that method, and displays whatever it returns.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/wavefront-cli/base.rb', line 129 def dispatch # # Take a list of do_ methods, remove the 'do_' from their name, # and break them into arrays of '_' separated words. # m_list = methods.select { |m| m.to_s.start_with?('do_') }.map do |m| m.to_s.split('_')[1..-1] end # Sort that array of arrays by length, longest first. Then look # through each deconstructed method name and see if the user # supplied an option for each component. Call the first one that # matches. The order will ensure we match "do_delete_tags" before # we match "do_delete". # m_list.sort_by(&:length).reverse.each do |m| if m.reject { |w| [w.to_sym] }.empty? method = (%w(do) + m).join('_') return display(public_send(method), method) end end if respond_to?(:do_default) return display(public_send(:do_default), :do_default) end raise WavefrontCli::Exception::UnhandledCommand end |
#display(data, method) ⇒ Object
Display a Ruby object as JSON, YAML, or human-readable. We provide a default method to format human-readable output, but you can override it by creating your own humanize_command_output method control how its output is handled by setting the response instance variable.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/wavefront-cli/base.rb', line 171 def display(data, method) exit if [:noop] [:status, :response].each do |b| abort "no #{b} block in API response" unless data.respond_to?(b) end unless check_status(data.status) handle_error(method, data.status.code) if format_var == :human abort "API #{data.status.code}: #{data.status.message}." end resp = if data.response.respond_to?(:items) data.response.items else data.response end handle_response(resp, format_var, method) end |
#do_delete ⇒ Object
285 286 287 |
# File 'lib/wavefront-cli/base.rb', line 285 def do_delete wf.delete([:'<id>']) end |
#do_describe ⇒ Object
268 269 270 |
# File 'lib/wavefront-cli/base.rb', line 268 def do_describe wf.describe([:'<id>']) end |
#do_import ⇒ Object
272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/wavefront-cli/base.rb', line 272 def do_import raw = load_file([:'<file>']) begin prepped = import_to_create(raw) rescue => e puts e if [:debug] raise 'could not parse input.' end wf.create(prepped) end |
#do_list ⇒ Object
Below here are common methods. Most are used by most classes, but if they don’t match a command described in the docopt text, the dispatcher will never call them. So, there’s no harm inheriting unneeded things. Some classes override them.
264 265 266 |
# File 'lib/wavefront-cli/base.rb', line 264 def do_list wf.list([:offset] || 0, [:limit] || 100) end |
#do_search ⇒ Object
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/wavefront-cli/base.rb', line 298 def do_search require 'wavefront-sdk/search' wfs = Wavefront::Search.new(mk_creds, mk_opts) query = [:'<condition>'].each_with_object([]) do |c, aggr| key, value = c.split(/\W/, 2) q = { key: key, value: value } q[:matchingMethod] = 'EXACT' if c.start_with?("#{key}=") q[:matchingMethod] = 'STARTSWITH' if c.start_with?("#{key}^") aggr.<< q end wfs.search(klass_word, query, { limit: [:limit], offset: [:offset] || [:cursor]}) end |
#do_tag_add ⇒ Object
318 319 320 |
# File 'lib/wavefront-cli/base.rb', line 318 def do_tag_add wf.tag_add([:'<id>'], [:'<tag>'].first) end |
#do_tag_clear ⇒ Object
330 331 332 |
# File 'lib/wavefront-cli/base.rb', line 330 def do_tag_clear wf.tag_set([:'<id>'], []) end |
#do_tag_delete ⇒ Object
322 323 324 |
# File 'lib/wavefront-cli/base.rb', line 322 def do_tag_delete wf.tag_delete([:'<id>'], [:'<tag>'].first) end |
#do_tag_set ⇒ Object
326 327 328 |
# File 'lib/wavefront-cli/base.rb', line 326 def do_tag_set wf.tag_set([:'<id>'], [:'<tag>']) end |
#do_tags ⇒ Object
314 315 316 |
# File 'lib/wavefront-cli/base.rb', line 314 def wf.([:'<id>']) end |
#do_undelete ⇒ Object
289 290 291 |
# File 'lib/wavefront-cli/base.rb', line 289 def do_undelete wf.undelete([:'<id>']) end |
#do_update ⇒ Object
293 294 295 296 |
# File 'lib/wavefront-cli/base.rb', line 293 def do_update k, v = [:'<key=value>'].split('=') wf.update([:'<id>'], k => v) end |
#format_var ⇒ Symbol
To allow a user to default to different output formats for different object, we are able to define a format for each class. instance, alertformat or agentformat. This method returns such a string appropriate for the inheriting class.
116 117 118 119 |
# File 'lib/wavefront-cli/base.rb', line 116 def format_var [:format].to_sym # (self.class.name.split('::').last.downcase + 'format').to_sym end |
#handle_error(method, code) ⇒ Object
This gives us a chance to catch different errors in WavefrontDisplay classes. If nothing catches, them abort.
199 200 201 202 |
# File 'lib/wavefront-cli/base.rb', line 199 def handle_error(method, code) k = load_display_class k.new({}, ).run_error([method, code].join('_')) end |
#handle_response(resp, format, method) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/wavefront-cli/base.rb', line 204 def handle_response(resp, format, method) case format when :json puts resp.to_json when :yaml # We don't want the YAML keys to be symbols. puts JSON.parse(resp.to_json).to_yaml when :ruby p resp when :human k = load_display_class k.new(resp, ).run(method) else raise "Unknown output format '#{format}'." end end |
#import_to_create(raw) ⇒ Object
Most things will re-import with the POST method if you remove the ID.
337 338 339 |
# File 'lib/wavefront-cli/base.rb', line 337 def import_to_create(raw) raw.delete_if { |k, _v| k == 'id' } end |
#load_display_class ⇒ Object
220 221 222 223 |
# File 'lib/wavefront-cli/base.rb', line 220 def load_display_class require_relative File.join('display', klass_word) Object.const_get(klass.name.sub('Wavefront', 'WavefrontDisplay')) end |
#load_file(path) ⇒ Hash
Give it a path to a file (as a string) and it will return the contents of that file as a Ruby object. Automatically detects JSON and YAML. Raises an exception if it doesn’t look like either.
246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/wavefront-cli/base.rb', line 246 def load_file(path) file = Pathname.new(path) raise 'Import file does not exist.' unless file.exist? if file.extname == '.json' JSON.parse(IO.read(file)) elsif file.extname == '.yaml' || file.extname == '.yml' YAML.safe_load(IO.read(file)) else raise 'Unsupported file format.' end end |
#mk_creds ⇒ Hash
Make a wavefront-sdk credentials object from standard options.
89 90 91 92 93 94 |
# File 'lib/wavefront-cli/base.rb', line 89 def mk_creds { token: [:token], endpoint: [:endpoint], agent: "wavefront-cli-#{WF_CLI_VERSION}" } end |
#mk_opts ⇒ Hash
Make a common wavefront-sdk options object from standard CLI options.
101 102 103 104 105 106 |
# File 'lib/wavefront-cli/base.rb', line 101 def mk_opts { debug: [:debug], verbose: [:verbose], noop: [:noop], } end |
#run ⇒ Object
43 44 45 46 |
# File 'lib/wavefront-cli/base.rb', line 43 def run @wf = klass.new(mk_creds, mk_opts) dispatch end |
#validate_id ⇒ Object
78 79 80 81 82 |
# File 'lib/wavefront-cli/base.rb', line 78 def validate_id send(validator_method, [:'<id>']) rescue validator_exception abort "'#{options[:'<id>']}' is not a valid #{klass_word} ID." end |
#validate_input ⇒ Object
62 63 64 65 66 |
# File 'lib/wavefront-cli/base.rb', line 62 def validate_input validate_id if [:'<id>'] if [:'<tag>'] send(:extra_validation) if respond_to?(:extra_validation) end |
#validate_opts ⇒ Object
There are things we need to have. If we don’t have them, stop the user right now. Also, if we’re in debug mode, print out a hash of options, which can be very useful when doing actual debugging. Some classes may have to override this method. The writer, for instance, uses a proxy and has no token.
231 232 233 234 |
# File 'lib/wavefront-cli/base.rb', line 231 def validate_opts raise 'Please supply an API token.' unless [:token] raise 'Please supply an API endpoint.' unless [:endpoint] end |
#validate_tags ⇒ Object
68 69 70 71 72 73 74 75 76 |
# File 'lib/wavefront-cli/base.rb', line 68 def Array([:'<tag>']).each do |t| begin send(:wf_tag?, t) rescue Wavefront::Exception::InvalidTag abort "'#{t}' is not a valid tag." end end end |
#validator_exception ⇒ Object
56 57 58 59 60 |
# File 'lib/wavefront-cli/base.rb', line 56 def validator_exception Object.const_get( "Wavefront::Exception::Invalid#{klass_word.capitalize}Id" ) end |
#validator_method ⇒ Object
We normally validate with a predictable method name. Alert IDs are validated with #wf_alert_id? etc. If you need to change that, override this method.
52 53 54 |
# File 'lib/wavefront-cli/base.rb', line 52 def validator_method "wf_#{klass_word}_id?".to_sym end |