wavefront-cli 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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