wavefront-cli 2.7.0 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6829c82ead73104270f19e4f3c06084719720afdc64233f25683940155cef12
4
- data.tar.gz: 40530d75b0b5ceb505a5afe9268bcb26036c92cc25114f664ba97662a5639506
3
+ metadata.gz: 397350925432eb596a2268652590b0690ee115d0f351f4406e807eec465db61c
4
+ data.tar.gz: e85b47160b148fc9067ea9d14b6265c8fa27004661a65a62dd4bdca67645c4e1
5
5
  SHA512:
6
- metadata.gz: 072f0f0bbd20bce99729bf3ff4e1a0a64fa34599677be8972b00c7ce48c8ac0fa88c6e0eb45f95b8f17e7e77039d1c70c4e03aff37f0354e1e587063a5ae4bbb
7
- data.tar.gz: 88c1dcd21de080393d131c381c28d98da6624c4a866e4d84ee4694e16ff555f067df52c5993ac33599088015d8e8d70506fd1dbcd59d4095a6a7a38947c5a060
6
+ metadata.gz: 0f9d17a9f4ceebbadc18e41813b6766b86c07f6a881a1b30814ab747e82782f67ef97d6a5f876d28719c8182b8b6e1ae14b514cfc649efe80f265bdefc623c0f
7
+ data.tar.gz: 7071d861a7da1bad3322fac62601cc05ed773898b04833384d16178ce67d260861603d36ff669f36dff43d333e9805608d0c6e520513fdf179f9ae074286b93d
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Wavefront CLI
2
2
  [![Build Status](https://travis-ci.org/snltd/wavefront-cli.svg?branch=master)](https://travis-ci.org/snltd/wavefront-cli) [![Maintainability](https://api.codeclimate.com/v1/badges/9b712047af0b2dafc146/maintainability)](https://codeclimate.com/github/snltd/wavefront-cli/maintainability) [![Gem Version](https://badge.fury.io/rb/wavefront-cli.svg)](https://badge.fury.io/rb/wavefront-cli) ![](http://ruby-gem-downloads-badge.herokuapp.com/wavefront-cli?type=total)
3
3
 
4
- This package provides a command-line interface to
5
- [Wavefront](https://www.wavefront.com/)'s API. Each API path
6
- is covered by a different command keyword.
4
+ This package provides a complete command-line interface to
5
+ [Wavefront](https://www.wavefront.com/)'s API. It also provides easy
6
+ ways to write data through a proxy.
7
7
 
8
8
  The gem is hosted [on
9
9
  Rubygems](https://rubygems.org/gems/wavefront-cli) and can be
@@ -13,12 +13,12 @@ installed with
13
13
  $ gem install wavefront-cli
14
14
  ```
15
15
 
16
- It is built on [the Wavefront Ruby
16
+ It is built on [our Wavefront Ruby
17
17
  SDK](https://github.com/snltd/wavefront-sdk) and requires Ruby >=
18
18
  2.2. It has no "native extension" dependencies.
19
19
 
20
- I also maintain [a reasonably thorough
21
- tutorial](http://sysdef.xyz/post/2017-07-26-wavefront-cli).
20
+ For a far more comprehensive overview/tutorial, please read [this
21
+ article](http://sysdef.xyz/post/2017-07-26-wavefront-cli).
22
22
 
23
23
  ```
24
24
  $ wf --help
@@ -30,21 +30,25 @@ Usage:
30
30
  wf --help
31
31
 
32
32
  Commands:
33
- alert view and manage alerts
34
- integration view and manage cloud integrations
35
- dashboard view and manage dashboards
36
- event view, manage, open, and close events
37
- link view and manage external links
38
- message view and mark as read user messages
39
- metric view metric details
40
- proxy view and manage Wavefront proxies
41
- query run timeseries queries
42
- savedsearch view and manage saved searches
43
- source view and manage source tags and descriptions
44
- user view and manage Wavefront users
45
- window view and manage maintenance windows
46
- webhook view and manage webhooks
47
- write send data points to a Wavefront proxy
33
+ alert view and manage alerts
34
+ cloudintegration view and manage cloud integrations
35
+ dashboard view and manage dashboards
36
+ derivedmetric view and manage derived metrics
37
+ event open, close, view, and manage events
38
+ integration view and manage Wavefront integrations
39
+ link view and manage external links
40
+ message read and mark user messages
41
+ metric view metrics
42
+ notificant view and manage Wavefront notification targets
43
+ proxy view and manage Wavefront proxies
44
+ query query the Wavefront API
45
+ report send data directly to Wavefront
46
+ savedsearch view and manage saved searches
47
+ source view and manage source tags and descriptions
48
+ user view and manage Wavefront users
49
+ webhook view and manage webhooks
50
+ window view and manage maintenance windows
51
+ write send data to a Wavefront proxy
48
52
 
49
53
  Use 'wf <command> --help' for further information.
50
54
  ```
@@ -55,10 +59,10 @@ Use 'wf <command> --help' for further information.
55
59
 
56
60
  You can pass in your Wavefront API and token with command-line
57
61
  options `-E` and `-t`; with the environment variables
58
- `WAVEFRONT_ENDPOINT` and `WAVEFRONT_TOKEN`,
59
- or by putting them in a configuration file at `${HOME}/.wavefront`. This is an
60
- ini-style file, with a section for each Wavefront account you wish to use. (None
61
- of the tokens shown here are real, of course!)
62
+ `WAVEFRONT_ENDPOINT` and `WAVEFRONT_TOKEN`, or by putting them in a
63
+ configuration file at `${HOME}/.wavefront`. This is an ini-style
64
+ file, with a section for each Wavefront account you wish to use.
65
+ (None of the tokens shown here are real, of course!)
62
66
 
63
67
  ```
64
68
  [default]
@@ -73,14 +77,15 @@ endpoint = company.wavefront.com
73
77
  format = yaml
74
78
  ```
75
79
 
76
- You can override the config file location with `-c`, and select a profile with
77
- `-P`. If you don't supply `-P`, the `default` profile is used.
80
+ You can override the config file location with `-c`, and select a
81
+ profile with `-P`. If you don't supply `-P`, the `default` profile
82
+ is used.
78
83
 
79
84
  ### Listing Things
80
85
 
81
86
  Most commands have a `list` subcommand, which will produce brief
82
- "one thing per line" output. The unique ID of the "thing" is in the first
83
- column.
87
+ "one thing per line" output. The unique ID of the "thing" is in the
88
+ first column.
84
89
 
85
90
  ```
86
91
  $ wf proxy list
@@ -114,23 +119,30 @@ ephemeral false
114
119
  deleted false
115
120
  ```
116
121
 
117
- Most timestamps come back from the API as epoch seconds or epoch milliseconds.
118
- The CLI, in its human-readable descriptions, will convert those to
119
- `YYYY-MM-DD HH:mm:ss` when it `describe`s something.
122
+ Most timestamps come back from the API as epoch seconds or epoch
123
+ milliseconds. The CLI, in its human-readable descriptions, will
124
+ convert those to `YYYY-MM-DD HH:mm:ss` when it `describe`s
125
+ something.
120
126
 
121
127
  ### Formats, Importing, and Exporting
122
128
 
123
- Most commands and sub-commands support the `-f` option. This takes one of
124
- `json`, `yaml`, `human` and `raw`, and tells the CLI to present the information
125
- it fetches from the Wavefront API in that format. (`raw` is the raw Ruby
126
- representation, which, for instance, you could paste into `irb`.)
129
+ Most commands and sub-commands support the `-f` option. This takes
130
+ one of `json`, `yaml`, `human` and `raw`, and tells the CLI to
131
+ present the information it fetches from the Wavefront API in that
132
+ format. (`raw` is the raw Ruby representation, which, for instance,
133
+ you could paste into `irb`.) Some object types can be exported in
134
+ HCL, for easy integration with Space Ape's Wavefront Terraform
135
+ provider.
127
136
 
128
- Human output can be selective. As well as the time formatting mentioned above,
129
- human-readable listings and desctiptions may omit data which is not likely to be
130
- useful, or which is extremely hard to present in a readable way.
137
+ Human output can be selective. As well as the time formatting
138
+ mentioned above, human-readable listings and desctiptions may omit
139
+ data which is not likely to be useful, or which is extremely hard to
140
+ present in a readable way.
131
141
 
132
- If you `describe` an object like a dashboard, user, webhook etc as `json` or
133
- `yaml`, and send the output to a file, you can re-import that data. The format of the file to be imported is automatically detected.
142
+ If you `describe` an object like a dashboard, user, webhook etc as
143
+ `json` or `yaml`, and send the output to a file, you can re-import
144
+ that data. The format of the file to be imported is automatically
145
+ detected.
134
146
 
135
147
  ```
136
148
  $ wf user list
@@ -213,11 +225,12 @@ or force a timestamp:
213
225
  $ wf write point -t 16:53:14 cli.example 8
214
226
  ```
215
227
 
216
- More usefully, you can write from a file. Your file must contain multiple
217
- columns: metric name (`m`), metric value (`v`), timestamp(`t`), and point tags
218
- (`T`). `v` is mandatory, `m` can be filled in with the `-m` flag, `t` can be
219
- filled in with the current timestamp, and `T` is optional, but if used, must be
220
- last. You then tell the CLI what order your fields are in.
228
+ More usefully, you can write from a file. Your file must contain
229
+ multiple columns: metric name (`m`), metric value (`v`),
230
+ timestamp(`t`), and point tags (`T`). `v` is mandatory, `m` can be
231
+ filled in with the `-m` flag, `t` can be filled in with the current
232
+ timestamp, and `T` is optional, but if used, must be last. You then
233
+ tell the CLI what order your fields are in.
221
234
 
222
235
  ```
223
236
  $ cat datafile
@@ -233,6 +246,9 @@ If you set the file to `-`, you can read from standard in:
233
246
  $ while true; do echo $RANDOM; sleep 1; done | wf write file -m cli.demo -Fv -
234
247
  ```
235
248
 
249
+ If you wish to write points directly via the API, and you have the
250
+ "direct ingestion" privilege, just swap `write` for `report`.
251
+
236
252
  Due to limitations in [docopt](https://github.com/docopt/docopt.rb),
237
253
  writing negative values is a bit of a mess.
238
254
 
@@ -240,7 +240,10 @@ module WavefrontCli
240
240
  format.to_s.capitalize))
241
241
  oclass.new(resp, options).run
242
242
  rescue LoadError
243
- raise "Unsupported output format '#{format}'."
243
+ raise WavefrontCli::Exception::UnsupportedOutput.new(
244
+ format("The '%s' command does not support '%s' output.",
245
+ options[:class], format)
246
+ )
244
247
  end
245
248
 
246
249
  def hcl_fields
@@ -3,9 +3,11 @@ require_relative 'base'
3
3
  # Define the Alert command
4
4
  #
5
5
  class WavefrontCommandAlert < WavefrontCommandBase
6
+
6
7
  def description
7
8
  'view and manage alerts'
8
9
  end
10
+
9
11
  def _commands
10
12
  ["list #{CMN} [-l] [-f format] [-o offset] [-L limit]",
11
13
  "firing #{CMN} [-o offset] [-L limit]",
@@ -34,4 +34,10 @@ class WavefrontCommandQuery < WavefrontCommandBase
34
34
  '-k, --nospark do not show sparkline',
35
35
  '-W, --nowarn do not show API warning messages']
36
36
  end
37
+
38
+ def postscript
39
+ 'The query command has an additional output format. Using ' \
40
+ "'-f wavefront' produces output suitable for feeding back into a " \
41
+ 'proxy.'.cmd_fold(TW, 0)
42
+ end
37
43
  end
@@ -92,6 +92,8 @@ class WavefrontCliController
92
92
  def run_command(hook)
93
93
  hook.validate_opts
94
94
  hook.run
95
+ rescue WavefrontCli::Exception::UnsupportedOutput => e
96
+ abort e.message
95
97
  rescue StandardError => e
96
98
  $stderr.puts "general error: #{e}"
97
99
  $stderr.puts "re-run with '-D' for stack trace." unless opts[:debug]
@@ -1,5 +1,6 @@
1
1
  module WavefrontCli
2
2
  class Exception
3
3
  class UnhandledCommand < RuntimeError; end
4
+ class UnsupportedOutput < RuntimeError; end
4
5
  end
5
6
  end
@@ -1,10 +1,51 @@
1
1
  module WavefrontOutput
2
+ #
3
+ # WavefrontCli::Base looks for a class WavefrontOutput::Format
4
+ # where 'Format' is something like 'Json', or 'Yaml'. If it finds
5
+ # that class it creates a new instance, passing through the
6
+ # response object (@resp) and options hash (@options), then calls
7
+ # the #run method.
8
+ #
9
+ # All those classes are an extension of this one. Some, like Json
10
+ # or Yaml, are generic, and dump a straight translation of the
11
+ # response object. Others, like Hcl or Wavefront, have subclasses
12
+ # which deal with the output of specific commands.
13
+ #
2
14
  class Base
3
- attr_reader :resp, :options
15
+ attr_reader :resp, :options, :cmd
4
16
 
5
- def initialize(resp, options)
17
+ def initialize(resp = {}, options = {})
18
+ @cmd = options[:class]
6
19
  @resp = resp
7
20
  @options = options
8
21
  end
22
+
23
+ # We used to call #run directly, but now we use this wrapper to
24
+ # make it easier to test the #_run methods.
25
+ #
26
+ def run
27
+ puts _run
28
+ end
29
+
30
+ def _run
31
+ command_class.run
32
+ end
33
+
34
+ def my_format
35
+ self.class.name.split('::').last.downcase
36
+ end
37
+
38
+ def command_class_name
39
+ format('Wavefront%sOutput::%s', my_format.capitalize, cmd.capitalize)
40
+ end
41
+
42
+ def command_file
43
+ File.join(my_format, cmd)
44
+ end
45
+
46
+ def command_class
47
+ require_relative command_file
48
+ Object.const_get(command_class_name).new(resp, options)
49
+ end
9
50
  end
10
51
  end
@@ -7,14 +7,5 @@ module WavefrontOutput
7
7
  # different resource types need various amounts of massaging. Args
8
8
  # are passed through to the child class.
9
9
  #
10
- class Hcl < Base
11
- def run
12
- require_relative File.join('hcl', options[:class])
13
- oclass = Object.const_get(format('WavefrontHclOutput::%s',
14
- options[:class].to_s.capitalize))
15
- oclass.new(resp, options).run
16
- rescue LoadError
17
- abort "no HCL output for #{options[:class]}."
18
- end
19
- end
10
+ class Hcl < Base; end
20
11
  end
@@ -5,8 +5,8 @@ module WavefrontOutput
5
5
  # Display as JSON
6
6
  #
7
7
  class Json < Base
8
- def run
9
- puts resp.to_json
8
+ def _run
9
+ resp.to_json
10
10
  end
11
11
  end
12
12
  end
@@ -6,7 +6,11 @@ module WavefrontOutput
6
6
  #
7
7
  class Ruby < Base
8
8
  def run
9
- p resp
9
+ p _run
10
+ end
11
+
12
+ def _run
13
+ resp
10
14
  end
11
15
  end
12
16
  end
@@ -0,0 +1,10 @@
1
+ require_relative 'base'
2
+
3
+ module WavefrontOutput
4
+ #
5
+ # Display query results in native Wavefront format. The idea is
6
+ # that timeseries can be extracted, modified, and fed back in via
7
+ # a proxy.
8
+ #
9
+ class Wavefront < Base; end
10
+ end
@@ -0,0 +1,14 @@
1
+ module WavefrontWavefrontOutput
2
+ class Base
3
+ attr_reader :resp, :options
4
+
5
+ def initialize(resp, options)
6
+ @resp = resp
7
+ @options = options
8
+ end
9
+
10
+ def run
11
+ puts _run
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,48 @@
1
+ require 'wavefront-sdk/stdlib/hash'
2
+ require_relative 'base'
3
+
4
+ module WavefrontWavefrontOutput
5
+ #
6
+ # Display query results in Wavefront wire format. We have to
7
+ # handle raw and normal output in different ways.
8
+ #
9
+ class Query < Base
10
+ def _run
11
+ if options[:raw]
12
+ raw_output
13
+ else
14
+ query_output
15
+ end
16
+ end
17
+
18
+ def raw_output
19
+ resp.each_with_object('') do |point, a|
20
+ point[:points].each do |p|
21
+ a.<< wavefront_format(options[:'<metric>'],
22
+ p[:value],
23
+ p[:timestamp],
24
+ options[:host],
25
+ point[:tags]) + "\n"
26
+ end
27
+ end
28
+ end
29
+
30
+ def query_output
31
+ resp[:timeseries].each_with_object('') do |ts, a|
32
+ ts[:data].each do |point|
33
+ a.<< wavefront_format(ts[:label],
34
+ point[1],
35
+ point[0],
36
+ ts[:host],
37
+ ts[:tags]) + "\n"
38
+ end
39
+ end
40
+ end
41
+
42
+ def wavefront_format(path, value, ts, source, tags = nil)
43
+ arr = [path, value, ts, format('source=%s', source)]
44
+ arr.<< tags.to_wf_tag if tags && !tags.empty?
45
+ arr.join(' ')
46
+ end
47
+ end
48
+ end
@@ -8,8 +8,8 @@ module WavefrontOutput
8
8
  # We don't want the YAML keys to be symbols, so we load it as
9
9
  # JSON and turn *that* into YAML.
10
10
  #
11
- def run
12
- puts JSON.parse(resp.to_json).to_yaml
11
+ def _run
12
+ JSON.parse(resp.to_json).to_yaml
13
13
  end
14
14
  end
15
15
  end
@@ -1 +1 @@
1
- WF_CLI_VERSION = '2.7.0'.freeze
1
+ WF_CLI_VERSION = '2.8.0'.freeze
@@ -7,6 +7,13 @@ require 'minitest/spec'
7
7
  require 'pathname'
8
8
  require_relative '../lib/wavefront-cli/controller'
9
9
 
10
+ def all_commands
11
+ (Pathname.new(__FILE__).dirname.parent + 'lib' + 'wavefront-cli' +
12
+ 'commands').children.each.with_object([]) do |c, a|
13
+ a.<< c.basename.to_s.chomp('.rb') unless c.basename.to_s == 'base.rb'
14
+ end
15
+ end
16
+
10
17
  unless defined?(CMD)
11
18
  CMD = 'wavefront'.freeze
12
19
  ENDPOINT = 'metrics.wavefront.com'.freeze
@@ -17,10 +24,7 @@ unless defined?(CMD)
17
24
  JSON_POST_HEADERS = {
18
25
  'Content-Type': 'application/json', Accept: 'application/json'
19
26
  }.freeze
20
-
21
- CMDS = %w[alert integration dashboard event link message metric
22
- proxy query savedsearch source user window webhook write].freeze
23
-
27
+ CMDS = all_commands.freeze
24
28
  BAD_TAG = '*BAD_TAG*'.freeze
25
29
  TW = 80
26
30
  end
@@ -188,6 +192,18 @@ def tag_tests(cmd, id, bad_id, pth = nil, k = nil)
188
192
  invalid_tags(cmd, ["tag add #{id} #{BAD_TAG}", "tag delete #{id} #{BAD_TAG}"])
189
193
  end
190
194
 
195
+ # Load in a canned query response
196
+ #
197
+ def load_query_response
198
+ JSON.parse(IO.read(RES_DIR + 'sample_query_response.json'),
199
+ symbolize_names: true)
200
+ end
201
+
202
+ def load_raw_query_response
203
+ JSON.parse(IO.read(RES_DIR + 'sample_raw_query_response.json'),
204
+ symbolize_names: true)
205
+ end
206
+
191
207
  # stdlib extensions
192
208
  #
193
209
  class Hash