Class: WavefrontCliController

Inherits:
Object
  • Object
show all
Includes:
WavefrontCli::Constants
Defined in:
lib/wavefront-cli/controller.rb

Overview

Dynamically generate a CLI interface from files which describe each subcomand.

Constant Summary

Constants included from WavefrontCli::Constants

WavefrontCli::Constants::ALL_PAGE_SIZE, WavefrontCli::Constants::DEFAULT_CONFIG, WavefrontCli::Constants::DEFAULT_OPTS, WavefrontCli::Constants::EVENT_STATE_DIR, WavefrontCli::Constants::HUMAN_TIME_FORMAT, WavefrontCli::Constants::HUMAN_TIME_FORMAT_MS, WavefrontCli::Constants::SEARCH_SPLIT

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ WavefrontCliController

Returns a new instance of WavefrontCliController.



32
33
34
35
36
37
38
39
40
# File 'lib/wavefront-cli/controller.rb', line 32

def initialize(args)
  @args = args
  @cmds = load_commands
  @usage = docopt_hash
  cmd, opts = parse_args
  @opts = parse_opts(opts)
  cli_class_obj = cli_class(cmd, @opts)
  run_command(cli_class_obj)
end

Instance Attribute Details

#argsObject (readonly)

Returns the value of attribute args.



28
29
30
# File 'lib/wavefront-cli/controller.rb', line 28

def args
  @args
end

#cmdsObject (readonly)

Returns the value of attribute cmds.



28
29
30
# File 'lib/wavefront-cli/controller.rb', line 28

def cmds
  @cmds
end

#optsObject (readonly)

Returns the value of attribute opts.



28
29
30
# File 'lib/wavefront-cli/controller.rb', line 28

def opts
  @opts
end

#twObject (readonly)

Returns the value of attribute tw.



28
29
30
# File 'lib/wavefront-cli/controller.rb', line 28

def tw
  @tw
end

#usageObject (readonly)

Returns the value of attribute usage.



28
29
30
# File 'lib/wavefront-cli/controller.rb', line 28

def usage
  @usage
end

Instance Method Details

#backtrace_message(err) ⇒ Object

rubocop:enable Metrics/MethodLength rubocop:enable Metrics/AbcSize



183
184
185
186
187
188
189
# File 'lib/wavefront-cli/controller.rb', line 183

def backtrace_message(err)
  if opts[:debug]
    warn "Backtrace:\n\t#{err.backtrace.join("\n\t")}"
  else
    puts "Re-run command with '-D' for backtrace."
  end
end

#cli_class(cmd, opts) ⇒ Object

Get the CLI class we need to run the command we’ve been given.

Parameters:

Returns:

  • WavefrontCli::cmd



109
110
111
112
113
114
115
116
117
# File 'lib/wavefront-cli/controller.rb', line 109

def cli_class(cmd, opts)
  load_cli_class(cmd, opts)
rescue WavefrontCli::Exception::UnhandledCommand
  abort 'Fatal error. Unsupported command. Please open a Github issue.'
rescue WavefrontCli::Exception::InvalidInput => e
  abort "Invalid input. #{e.message}"
rescue RuntimeError => e
  abort "Unable to run command. #{e.message}."
end

#default_helpString

What you see when you do ‘wf –help’ rubocop:disable Metrics/MethodLength

Returns:



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/wavefront-cli/controller.rb', line 46

def default_help
  s = ['Wavefront CLI',
       '',
       'Usage:',
       "  #{CMD} command [options]",
       "  #{CMD} --version",
       "  #{CMD} --help",
       '',
       'Commands:',
       '']

  cmds.sort.each do |k, v|
    s.<< format('  %-18<command>s %<desc>s',
                command: k,
                desc: v.description)
  end

  s.<< ''
  s.<< "Use '#{CMD} <command> --help' for further information."
  s.join("\n")
end

#docopt_hashHash

Returns command descriptions for docopt.

Returns:

  • (Hash)

    command descriptions for docopt.



71
72
73
74
75
# File 'lib/wavefront-cli/controller.rb', line 71

def docopt_hash
  cmds.each_with_object(default: default_help) do |(k, v), ret|
    ret[k.to_sym] = v.docopt
  end
end

#handle_missing_credentials(error) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
# File 'lib/wavefront-cli/controller.rb', line 193

def handle_missing_credentials(error)
  if DEFAULT_CONFIG.exist? && DEFAULT_CONFIG.file?
    abort "Credential error. #{error.message}"
  else
    puts 'No credentials supplied on the command line or via ' \
         'environment variables, and no configuration file found. ' \
         "Please run 'wf config setup' to create configuration."
      .fold(TW, 0)
    exit 1
  end
end

#import_command(path) ⇒ Object

Load a command description from a file. Each is in its own class

return [Class] new class object defining command.

Parameters:

  • f (Pathname)

    path of file to load



221
222
223
224
225
226
227
# File 'lib/wavefront-cli/controller.rb', line 221

def import_command(path)
  return if path.extname != '.rb' || path.basename.to_s == 'base.rb'

  k_name = path.basename.to_s[0..-4]
  require(CMD_DIR + k_name)
  Object.const_get("WavefrontCommand#{k_name.capitalize}").new
end

#load_cli_class(cmd, opts) ⇒ Object



119
120
121
122
# File 'lib/wavefront-cli/controller.rb', line 119

def load_cli_class(cmd, opts)
  require_relative File.join('.', cmds[cmd].sdk_file)
  Object.const_get('WavefrontCli').const_get(cmds[cmd].sdk_class).new(opts)
end

#load_commandsHash

Each command is defined in its own file. Dynamically load all those commands.

Returns:

  • (Hash)

    :command => CommandClass



209
210
211
212
213
214
# File 'lib/wavefront-cli/controller.rb', line 209

def load_commands
  CMD_DIR.children.each_with_object({}) do |f, ret|
    k = import_command(f)
    ret[k.word.to_sym] = k if k
  end
end

#parse_argsObject

Parse the input. The first Docopt.docopt handles the default options, the second works on the command.



80
81
82
83
84
85
86
87
# File 'lib/wavefront-cli/controller.rb', line 80

def parse_args
  Docopt.docopt(usage[:default], version: WF_CLI_VERSION, argv: args)
rescue Docopt::Exit => e
  cmd = args.empty? ? nil : args.first.to_sym

  abort e.message unless usage.key?(cmd)
  parse_cmd(cmd)
end

#parse_cmd(cmd) ⇒ Object

Parse a command.

Parameters:

  • cmd (String)

    given command



92
93
94
95
96
97
98
# File 'lib/wavefront-cli/controller.rb', line 92

def parse_cmd(cmd)
  [cmd, sanitize_keys(Docopt.docopt(usage[cmd], argv: args))]
rescue Docopt::DocoptLanguageError => e
  abort "Mangled command description:\n#{e.message}"
rescue Docopt::Exit => e
  abort e.message
end

#parse_opts(options) ⇒ Object



100
101
102
# File 'lib/wavefront-cli/controller.rb', line 100

def parse_opts(options)
  WavefrontCli::OptHandler.new(options).opts
end

#run_command(cli_class_obj) ⇒ Object

rubocop:disable Metrics/MethodLength rubocop:disable Metrics/AbcSize



126
127
128
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/wavefront-cli/controller.rb', line 126

def run_command(cli_class_obj)
  cli_class_obj.validate_opts
  cli_class_obj.run
rescue Interrupt
  abort "\nOperation aborted at user request."
rescue WavefrontCli::Exception::ConfigFileNotFound => e
  abort "Configuration file #{e}' not found."
rescue WavefrontCli::Exception::CredentialError => e
  handle_missing_credentials(e)
rescue WavefrontCli::Exception::MandatoryValue
  abort 'A value must be supplied.'
rescue Wavefront::Exception::InvalidPermission => e
  abort "'#{e}' is not a valid privilege."
rescue Wavefront::Exception::InvalidUserGroupId => e
  abort "'#{e}' is not a valid user group id."
rescue WavefrontCli::Exception::InvalidValue => e
  abort "Invalid value for #{e}."
rescue WavefrontCli::Exception::ProfileExists => e
  abort "Profile '#{e}' already exists."
rescue WavefrontCli::Exception::ProfileNotFound => e
  abort "Profile '#{e}' not found."
rescue WavefrontCli::Exception::FileNotFound
  abort 'File not found.'
rescue WavefrontCli::Exception::InsufficientData => e
  abort "Insufficient data. #{e.message}"
rescue WavefrontCli::Exception::InvalidQuery => e
  abort "Invalid query. API message: '#{e.message}'."
rescue WavefrontCli::Exception::SystemError => e
  abort "Host system error. #{e.message}"
rescue WavefrontCli::Exception::UnparseableInput => e
  abort "Cannot parse input. #{e.message}"
rescue WavefrontCli::Exception::UnparseableSearchPattern
  abort 'Searches require a key, a value, and a match operator.'
rescue WavefrontCli::Exception::UnsupportedFileFormat
  abort 'Unsupported file format.'
rescue WavefrontCli::Exception::UnsupportedOperation => e
  abort "Unsupported operation.\n#{e.message}"
rescue WavefrontCli::Exception::UnsupportedOutput => e
  abort e.message
rescue WavefrontCli::Exception::UnsupportedNoop
  abort 'Multiple API call operations cannot be performed as no-ops.'
rescue WavefrontCli::Exception::UserGroupNotFound => e
  abort "Cannot find user group '#{e.message}'."
rescue Wavefront::Exception::UnsupportedWriter => e
  abort "Unsupported writer '#{e.message}'."
rescue WavefrontCli::Exception::UserError => e
  abort "User error: #{e.message}."
rescue WavefrontCli::Exception::ImpossibleSearch
  abort 'Search on non-existent key. Please use a top-level field.'
rescue StandardError => e
  warn "general error: #{e}"
  backtrace_message(e)
  abort
end

#sanitize_keys(options) ⇒ Object

Symbolize, and remove dashes from option keys

return [Hash] h with modified keys

Parameters:

  • h (Hash)

    options hash



234
235
236
237
238
# File 'lib/wavefront-cli/controller.rb', line 234

def sanitize_keys(options)
  options.each_with_object({}) do |(k, v), r|
    r[k.to_s.delete('-').to_sym] = v
  end
end