wavefront-cli 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +20 -0
- data/.gitignore +4 -0
- data/.travis.yml +16 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +65 -0
- data/README.md +221 -0
- data/Rakefile +18 -0
- data/bin/wavefront +14 -0
- data/lib/wavefront-cli/alert.rb +60 -0
- data/lib/wavefront-cli/base.rb +320 -0
- data/lib/wavefront-cli/cloudintegration.rb +12 -0
- data/lib/wavefront-cli/commands/alert.rb +38 -0
- data/lib/wavefront-cli/commands/base.rb +105 -0
- data/lib/wavefront-cli/commands/dashboard.rb +29 -0
- data/lib/wavefront-cli/commands/event.rb +44 -0
- data/lib/wavefront-cli/commands/integration.rb +33 -0
- data/lib/wavefront-cli/commands/link.rb +34 -0
- data/lib/wavefront-cli/commands/message.rb +23 -0
- data/lib/wavefront-cli/commands/metric.rb +20 -0
- data/lib/wavefront-cli/commands/proxy.rb +25 -0
- data/lib/wavefront-cli/commands/query.rb +32 -0
- data/lib/wavefront-cli/commands/savedsearch.rb +32 -0
- data/lib/wavefront-cli/commands/source.rb +27 -0
- data/lib/wavefront-cli/commands/user.rb +24 -0
- data/lib/wavefront-cli/commands/webhook.rb +25 -0
- data/lib/wavefront-cli/commands/window.rb +33 -0
- data/lib/wavefront-cli/commands/write.rb +35 -0
- data/lib/wavefront-cli/constants.rb +17 -0
- data/lib/wavefront-cli/controller.rb +134 -0
- data/lib/wavefront-cli/dashboard.rb +27 -0
- data/lib/wavefront-cli/display/alert.rb +44 -0
- data/lib/wavefront-cli/display/base.rb +304 -0
- data/lib/wavefront-cli/display/cloudintegration.rb +18 -0
- data/lib/wavefront-cli/display/dashboard.rb +21 -0
- data/lib/wavefront-cli/display/event.rb +19 -0
- data/lib/wavefront-cli/display/externallink.rb +13 -0
- data/lib/wavefront-cli/display/maintenancewindow.rb +19 -0
- data/lib/wavefront-cli/display/message.rb +8 -0
- data/lib/wavefront-cli/display/metric.rb +22 -0
- data/lib/wavefront-cli/display/proxy.rb +13 -0
- data/lib/wavefront-cli/display/query.rb +69 -0
- data/lib/wavefront-cli/display/savedsearch.rb +17 -0
- data/lib/wavefront-cli/display/source.rb +26 -0
- data/lib/wavefront-cli/display/user.rb +16 -0
- data/lib/wavefront-cli/display/webhook.rb +24 -0
- data/lib/wavefront-cli/display/write.rb +19 -0
- data/lib/wavefront-cli/event.rb +162 -0
- data/lib/wavefront-cli/exception.rb +5 -0
- data/lib/wavefront-cli/externallink.rb +16 -0
- data/lib/wavefront-cli/maintenancewindow.rb +16 -0
- data/lib/wavefront-cli/message.rb +19 -0
- data/lib/wavefront-cli/metric.rb +24 -0
- data/lib/wavefront-cli/opt_handler.rb +62 -0
- data/lib/wavefront-cli/proxy.rb +22 -0
- data/lib/wavefront-cli/query.rb +74 -0
- data/lib/wavefront-cli/savedsearch.rb +24 -0
- data/lib/wavefront-cli/source.rb +20 -0
- data/lib/wavefront-cli/user.rb +25 -0
- data/lib/wavefront-cli/version.rb +1 -0
- data/lib/wavefront-cli/webhook.rb +8 -0
- data/lib/wavefront-cli/write.rb +244 -0
- data/spec/spec_helper.rb +197 -0
- data/spec/wavefront-cli/alert_spec.rb +44 -0
- data/spec/wavefront-cli/base_spec.rb +47 -0
- data/spec/wavefront-cli/cli_help_spec.rb +47 -0
- data/spec/wavefront-cli/cloudintegration_spec.rb +24 -0
- data/spec/wavefront-cli/dashboard_spec.rb +37 -0
- data/spec/wavefront-cli/event_spec.rb +19 -0
- data/spec/wavefront-cli/externallink_spec.rb +18 -0
- data/spec/wavefront-cli/maintanancewindow_spec.rb +19 -0
- data/spec/wavefront-cli/message_spec.rb +28 -0
- data/spec/wavefront-cli/metric_spec.rb +22 -0
- data/spec/wavefront-cli/proxy_spec.rb +26 -0
- data/spec/wavefront-cli/query_spec.rb +63 -0
- data/spec/wavefront-cli/resources/conf.yaml +10 -0
- data/spec/wavefront-cli/savedsearch_spec.rb +18 -0
- data/spec/wavefront-cli/source_spec.rb +18 -0
- data/spec/wavefront-cli/user_spec.rb +31 -0
- data/spec/wavefront-cli/webhook_spec.rb +17 -0
- data/wavefront-cli.gemspec +36 -0
- metadata +279 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cd9a4217e92b817721a7437e19ec3733bd06cd3c
|
4
|
+
data.tar.gz: '019e98583f888391f948e7f82266ff2cdb902a3b'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fb04299e0ad36f4f4cc6c587483574859a93794a317e9c54a127b1821894dad3d25a34eade8ad81ce290aedc8b8df24d44b9853d062433d8e3a6c7d1343bb8f4
|
7
|
+
data.tar.gz: 926c982c2fccb55b043c8780662ab37e4f12481c91f24a3fc51d81c8e9a0226a4172522d46bef8db1d18769a7b5fb521c3d1332a22ce0f868a887c87bd5d2386
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
duplication:
|
4
|
+
enabled: true
|
5
|
+
checks:
|
6
|
+
Similar code:
|
7
|
+
enabled: false
|
8
|
+
config:
|
9
|
+
languages:
|
10
|
+
- ruby
|
11
|
+
fixme:
|
12
|
+
enabled: true
|
13
|
+
rubocop:
|
14
|
+
enabled: true
|
15
|
+
ratings:
|
16
|
+
paths:
|
17
|
+
- "**.rb"
|
18
|
+
exclude_paths:
|
19
|
+
- spec/
|
20
|
+
- lib/_wavefront
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
rvm:
|
4
|
+
- 2.2.7
|
5
|
+
- 2.3.4
|
6
|
+
- 2.4.1
|
7
|
+
before_install: gem install bundler --no-rdoc --no-ri
|
8
|
+
deploy:
|
9
|
+
provider: rubygems
|
10
|
+
api_key:
|
11
|
+
secure: dfmL5JwBn+u3cUmyAaDsApDa7ljGajGNz3GDcKd2J8FOt7+a758/lmL8EQ34sDT1ZFotrxn/y1RbgXlaDxAE1XDfrZbjckmx7a6wa2sqR3kBraJ2tx7CiXodbw3Z8XZf9WLb0kYGmlLtI73GNcuunMt/9f1cobqWISRLHw6b7amlO7GW2ZBZgzRS+N8TSS2dicIvKMo5HoMYU+uWLM4zDFBPnGNcMiWxh8ysLzJoKqA9kbBUyCVEZ03MlV7G71ObvWCLasKnZ3W5U+K1NbgU7mgMYfl9KIcA4y9hQ9hUCijk40SmT7ffy3P2gq8zblC/4x5Eefpau9X/bdLwXoRCIzqk05t4f45wstj2auHGK0HJwOYRtx8apdaLSgyJ5lQpGcbCRu40WR9mDkaM8m9n3u2o6GJmftCg3AN1QtsourmQB84x67LEbHzValMaokrbCol4XeWqlC+dCNLPixemQRBvcNfI3V9C6RqVGfjpoGlSTI+RkQqwm01PcxpeqIVfdMd1wnfUuAOywUO6UpvtK9TZaxg0NnVElXpPseQbtzulLwZ7R5Y3A4Ss8Z7w43c1KHxTkg54FWUOp065ItjAc4lmyORXq/2+F7sMvRN6dtCLaXTUlkYuU3cjFLIPlLGFYgqq4T4xQa+e5NEK1XW7nghv+IRfKfyVeZsB0WpY+uc=
|
12
|
+
gem: wavefront-cli
|
13
|
+
on:
|
14
|
+
tags: true
|
15
|
+
repo: snltd/wavefront-cli
|
16
|
+
ruby: 2.3.4
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
wavefront-cli (0.0.2)
|
5
|
+
docopt (= 0.5.0)
|
6
|
+
wavefront-sdk (~> 0.1.5)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.5.1)
|
12
|
+
public_suffix (~> 2.0, >= 2.0.2)
|
13
|
+
ast (2.3.0)
|
14
|
+
crack (0.4.3)
|
15
|
+
safe_yaml (~> 1.0.0)
|
16
|
+
docopt (0.5.0)
|
17
|
+
faraday (0.12.1)
|
18
|
+
multipart-post (>= 1.2, < 3)
|
19
|
+
hashdiff (0.3.2)
|
20
|
+
inifile (3.0.0)
|
21
|
+
map (6.6.0)
|
22
|
+
minitest (5.8.5)
|
23
|
+
multipart-post (2.0.0)
|
24
|
+
parser (2.4.0.0)
|
25
|
+
ast (~> 2.2)
|
26
|
+
powerpack (0.1.1)
|
27
|
+
public_suffix (2.0.5)
|
28
|
+
rainbow (2.2.1)
|
29
|
+
rake (12.0.0)
|
30
|
+
rubocop (0.47.1)
|
31
|
+
parser (>= 2.3.3.1, < 3.0)
|
32
|
+
powerpack (~> 0.1)
|
33
|
+
rainbow (>= 1.99.1, < 3.0)
|
34
|
+
ruby-progressbar (~> 1.7)
|
35
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
36
|
+
ruby-progressbar (1.8.1)
|
37
|
+
safe_yaml (1.0.4)
|
38
|
+
spy (0.4.5)
|
39
|
+
unicode-display_width (1.1.3)
|
40
|
+
wavefront-sdk (0.1.5)
|
41
|
+
addressable (~> 2.4)
|
42
|
+
faraday (>= 0.12.1, < 0.13)
|
43
|
+
inifile (>= 3.0.0)
|
44
|
+
map (~> 6.6.0)
|
45
|
+
webmock (2.3.2)
|
46
|
+
addressable (>= 2.3.6)
|
47
|
+
crack (>= 0.3.2)
|
48
|
+
hashdiff
|
49
|
+
yard (0.9.5)
|
50
|
+
|
51
|
+
PLATFORMS
|
52
|
+
ruby
|
53
|
+
|
54
|
+
DEPENDENCIES
|
55
|
+
bundler (~> 1.3)
|
56
|
+
minitest (~> 5.8, >= 5.8.0)
|
57
|
+
rake (~> 12.0)
|
58
|
+
rubocop (~> 0.47.0)
|
59
|
+
spy (~> 0.4.0)
|
60
|
+
wavefront-cli!
|
61
|
+
webmock (~> 2.3, >= 2.3.2)
|
62
|
+
yard (~> 0.9.5)
|
63
|
+
|
64
|
+
BUNDLED WITH
|
65
|
+
1.14.6
|
data/README.md
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
# Wavefront CLI [![Build Status](https://travis-ci.org/snltd/wavefront-cli.svg?branch=master)](https://travis-ci.org/snltd/wavefront-cli) [![Code Climate](https://codeclimate.com/github/snltd/wavefront-cli/badges/gpa.svg)](https://codeclimate.com/github/snltd/wavefront-cli) [![Issue Count](https://codeclimate.com/github/snltd/wavefront-cli/badges/issue_count.svg)](https://codeclimate.com/github/snltd/wavefront-cli) [![Known Vulnerabilities](https://snyk.io/test/github/snltd/wavefront-cli/badge.svg)](https://snyk.io/test/github/snltd/wavefront-cli)
|
2
|
+
|
3
|
+
|
4
|
+
This package provides a command-line interface to Wavefront's API. Each API path
|
5
|
+
is covered by a different command keyword.
|
6
|
+
|
7
|
+
It is built on [the Wavefront Ruby
|
8
|
+
SDK](https://github.com/snltd/wavefront-sdk) and requires Ruby >= 2.2.
|
9
|
+
|
10
|
+
```
|
11
|
+
$ wavefront --help
|
12
|
+
Wavefront CLI
|
13
|
+
|
14
|
+
Usage:
|
15
|
+
wavefront [options] command [options]
|
16
|
+
wavefront --version
|
17
|
+
wavefront --help
|
18
|
+
|
19
|
+
Commands:
|
20
|
+
alert view and manage alerts
|
21
|
+
integration view and manage cloud integrations
|
22
|
+
dashboard view and manage dashboards
|
23
|
+
event view, manage, open, and close events
|
24
|
+
link view and manage external links
|
25
|
+
message view and mark as read user messages
|
26
|
+
metric view metric details
|
27
|
+
proxy view and manage Wavefront proxies
|
28
|
+
query run timeseries queries
|
29
|
+
savedsearch view and manage saved searches
|
30
|
+
source view and manage source tags and descriptions
|
31
|
+
user view and manage Wavefront users
|
32
|
+
window view and manage maintenance windows
|
33
|
+
webhook view and manage webhooks
|
34
|
+
write send data points to a Wavefront proxy
|
35
|
+
|
36
|
+
Use 'wavefront <command> --help' for further information.
|
37
|
+
```
|
38
|
+
|
39
|
+
## General Rules
|
40
|
+
|
41
|
+
### Credentials and the Config File
|
42
|
+
|
43
|
+
You can pass in your Wavefront API and token with command-line
|
44
|
+
options `-E` and `-t`; with the environment variables
|
45
|
+
`WAVEFRONT_ENDPOINT` and `WAVEFRONT_TOKEN`,
|
46
|
+
or by putting them in a configuration file at `${HOME}/.wavefront`. This is an
|
47
|
+
ini-style file, with a section for each Wavefront account you wish to use. (None
|
48
|
+
of the tokens shown here are real, of course!)
|
49
|
+
|
50
|
+
```
|
51
|
+
[default]
|
52
|
+
token = 106ba476-e3bd-c14c-4a3d-391cd4c11def
|
53
|
+
endpoint = metrics.wavefront.com
|
54
|
+
proxy = wavefront.localnet
|
55
|
+
format = human
|
56
|
+
|
57
|
+
[company]
|
58
|
+
token = 9ac40b15-f47f-a168-a5d3-271ab5bad617
|
59
|
+
endpoint = company.wavefront.com
|
60
|
+
format = yaml
|
61
|
+
```
|
62
|
+
|
63
|
+
You can override the config file location with `-c`, and select a profile with
|
64
|
+
`-P`. If you don't supply `-P`, the `default` profile is used.
|
65
|
+
|
66
|
+
### Listing Things
|
67
|
+
|
68
|
+
Most commands have a `list` subcommand, which will produce brief
|
69
|
+
"one thing per line" output. The unique ID of the "thing" is in the first
|
70
|
+
column.
|
71
|
+
|
72
|
+
```
|
73
|
+
$ wavefront proxy list
|
74
|
+
457d6cf3-5171-45e0-8d31-5c980be889ea test agent
|
75
|
+
917102d1-a10e-997b-ba63-95058f98d4fb Agent on wavefront-2017-03-13-02
|
76
|
+
926dfb4c-23c6-4fb9-8c8d-833625ab8f6f Agent on shark-wavefront
|
77
|
+
```
|
78
|
+
|
79
|
+
You can get more verbose listings with the `-l` flag.
|
80
|
+
|
81
|
+
### Describing Things
|
82
|
+
|
83
|
+
Most commands have a `describe` subcommand which will tell you more about the
|
84
|
+
object.
|
85
|
+
|
86
|
+
```
|
87
|
+
$ wavefront proxy describe 917102d1-a10e-497b-ba63-95058f98d4fb
|
88
|
+
name Agent on wavefront-2017-03-13-02
|
89
|
+
id 917102d1-a10e-497b-ba63-95058f98d4fb
|
90
|
+
version 4.7
|
91
|
+
customerId sysdef
|
92
|
+
inTrash false
|
93
|
+
lastCheckInTime 2017-06-06 14:47:20
|
94
|
+
hostname wavefront-2017-03-13-02
|
95
|
+
timeDrift -751
|
96
|
+
bytesLeftForBuffer 1536094720
|
97
|
+
bytesPerMinuteForBuffer 280109
|
98
|
+
localQueueSize 0
|
99
|
+
sshAgent false
|
100
|
+
ephemeral false
|
101
|
+
deleted false
|
102
|
+
```
|
103
|
+
|
104
|
+
Most timestamps come back from the API as epoch seconds or epoch milliseconds.
|
105
|
+
The CLI, in its human-readable descriptions, will convert those to
|
106
|
+
`YYYY-MM-DD HH:mm:ss` when it `describe`s something.
|
107
|
+
|
108
|
+
### Formats, Importing, and Exporting
|
109
|
+
|
110
|
+
Most commands and sub-commands support the `-f` option. This takes one of
|
111
|
+
`json`, `yaml`, `human` and `raw`, and tells the CLI to present the information
|
112
|
+
it fetches from the Wavefront API in that format. (`raw` is the raw Ruby
|
113
|
+
representation, which, for instance, you could paste into `irb`.)
|
114
|
+
|
115
|
+
Human output can be selective. As well as the time formatting mentioned above,
|
116
|
+
human-readable listings and desctiptions may omit data which is not likely to be
|
117
|
+
useful, or which is extremely hard to present in a readable way.
|
118
|
+
|
119
|
+
If you `describe` an object like a dashboard, user, webhook etc as `json` or
|
120
|
+
`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.
|
121
|
+
|
122
|
+
```
|
123
|
+
$ wavefront user list
|
124
|
+
slackboy@gmail.com
|
125
|
+
sysdef.limited@gmail.com
|
126
|
+
$ wavefront user describe -f json sysdef.limited@gmail.com > user.json
|
127
|
+
$ cat user.json
|
128
|
+
{"identifier":"sysdef.limited@gmail.com","customer":"sysdef","groups":["agent_management"]}
|
129
|
+
$ wavefront user delete sysdef.limited@gmail.com
|
130
|
+
Deleted user 'sysdef.limited@gmail.com'.
|
131
|
+
$ wavefront user list
|
132
|
+
slackboy@gmail.com
|
133
|
+
$ wavefront user import user.json
|
134
|
+
Imported user.
|
135
|
+
identifier sysdef.limited@gmail.com
|
136
|
+
customer sysdef
|
137
|
+
groups agent_management
|
138
|
+
$ wavefront user list
|
139
|
+
slackboy@gmail.com
|
140
|
+
sysdef.limited@gmail.com
|
141
|
+
```
|
142
|
+
|
143
|
+
You could, of course, modify certain aspects of the exported data before
|
144
|
+
re-importing.
|
145
|
+
|
146
|
+
### Time Windows
|
147
|
+
|
148
|
+
Commands which operate on a time window, such as `query` or `event`
|
149
|
+
will expect that window to be defined with `-s` and `-e` (or
|
150
|
+
`--start` and `--end`). Times can be in seconds since the epoch, or
|
151
|
+
any format which [Ruby's `strptime`
|
152
|
+
method](https://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/DateTime.html#method-c-strptime)
|
153
|
+
method can parse unaided. For instance:
|
154
|
+
|
155
|
+
```
|
156
|
+
$ wavefront --start 12:15 --end 12:20 ...
|
157
|
+
```
|
158
|
+
|
159
|
+
will define a window between 12:15 and 12:20pm today. If you ran
|
160
|
+
that in the morning, the time would be invalid, and you would get a
|
161
|
+
400 error from Wavefront, so something of the form
|
162
|
+
`2016-04-17T12:25:00` would remove all ambiguity.
|
163
|
+
|
164
|
+
There is no need to include a timezone in your time: the `wavefront`
|
165
|
+
CLI will automatically use your local timezone when it parses the
|
166
|
+
string.
|
167
|
+
|
168
|
+
The following options are valid in almost all contexts.
|
169
|
+
|
170
|
+
```
|
171
|
+
-c, --config=FILE path to configuration file [default: ~/.wavefront]
|
172
|
+
-P, --profile=NAME profile in configuration file [default: default]
|
173
|
+
-D, --debug enable debug mode
|
174
|
+
-V, --verbose enable verbose mode
|
175
|
+
-h, --help show help for command
|
176
|
+
```
|
177
|
+
|
178
|
+
Debug mode will show you combined options, and debug output from
|
179
|
+
`faraday`. It also shows the full stack trace should a command
|
180
|
+
fail. This output can be very verbose.
|
181
|
+
|
182
|
+
## Writing Points
|
183
|
+
|
184
|
+
Writing a single point is simple:
|
185
|
+
|
186
|
+
```
|
187
|
+
$ wavefront write point cli.example 10
|
188
|
+
```
|
189
|
+
|
190
|
+
and you can add point tags, if you like.
|
191
|
+
|
192
|
+
```
|
193
|
+
$ wavefront write point cli.example 9.4 -E wavefront -T proxy=wavefront \
|
194
|
+
-T from=README
|
195
|
+
```
|
196
|
+
|
197
|
+
or force a timestamp:
|
198
|
+
|
199
|
+
```
|
200
|
+
$ wavefront write point -t 16:53:14 cli.example 8
|
201
|
+
```
|
202
|
+
|
203
|
+
More usefully, you can write from a file. Your file must contain multiple
|
204
|
+
columns: metric name (`m`), metric value (`v`), timestamp(`t`), and point tags
|
205
|
+
(`T`). `v` is mandatory, `m` can be filled in with the `-m` flag, `t` can be
|
206
|
+
filled in with the current timestamp, and `T` is optional, but if used, must be
|
207
|
+
last. You then tell the CLI what order your fields are in.
|
208
|
+
|
209
|
+
```
|
210
|
+
$ cat datafile
|
211
|
+
1496767813 dev.cli.test 12.1
|
212
|
+
1496767813 dev.cli.test 10.0
|
213
|
+
1496767813 dev.cli.test 14.5
|
214
|
+
$ wavefront write file -F tmv datafile
|
215
|
+
```
|
216
|
+
|
217
|
+
If you set the file to `-`, you can read from standard in:
|
218
|
+
|
219
|
+
```
|
220
|
+
$ while true; do echo $RANDOM; sleep 1; done | wavefront write file -m cli.demo -Fv -
|
221
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'yard'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
task default: :test
|
6
|
+
|
7
|
+
Rake::TestTask.new do |t|
|
8
|
+
t.pattern = 'spec/wavefront-cli/*_spec.rb'
|
9
|
+
t.warning = false
|
10
|
+
end
|
11
|
+
|
12
|
+
RuboCop::RakeTask.new(:rubocop) do |t|
|
13
|
+
t.options = ['--display-cop-names']
|
14
|
+
end
|
15
|
+
|
16
|
+
YARD::Rake::YardocTask.new do |t|
|
17
|
+
t.files = ['lib/wavefront-cli/*rb']
|
18
|
+
end
|
data/bin/wavefront
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'io/console'
|
5
|
+
require_relative '../lib/wavefront-cli/controller'
|
6
|
+
|
7
|
+
begin
|
8
|
+
TW = IO.console.winsize.last
|
9
|
+
rescue
|
10
|
+
TW = 80
|
11
|
+
end
|
12
|
+
|
13
|
+
CMD = Pathname.new(__FILE__).basename
|
14
|
+
WavefrontCliController.new(ARGV)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontCli
|
4
|
+
#
|
5
|
+
# CLI coverage for the v2 'alert' API.
|
6
|
+
#
|
7
|
+
class Alert < WavefrontCli::Base
|
8
|
+
def do_describe
|
9
|
+
wf.describe(options[:'<id>'], options[:version])
|
10
|
+
end
|
11
|
+
|
12
|
+
def do_snooze
|
13
|
+
wf.snooze(options[:'<id>'], options[:time])
|
14
|
+
end
|
15
|
+
|
16
|
+
def do_unsnooze
|
17
|
+
wf.unsnooze(options[:'<id>'])
|
18
|
+
end
|
19
|
+
|
20
|
+
def do_delete
|
21
|
+
print (if wf.describe(options[:'<id>']).status.code == 200
|
22
|
+
'Soft'
|
23
|
+
else
|
24
|
+
'Permanently'
|
25
|
+
end)
|
26
|
+
|
27
|
+
puts " deleting alert '#{options[:'<id>']}'."
|
28
|
+
wf.delete(options[:'<id>'])
|
29
|
+
end
|
30
|
+
|
31
|
+
def do_summary
|
32
|
+
wf.summary
|
33
|
+
end
|
34
|
+
|
35
|
+
def do_history
|
36
|
+
wf.history(options[:'<id>'], options[:offset], options[:limit])
|
37
|
+
end
|
38
|
+
|
39
|
+
# Take a previously exported alert, and construct a hash which
|
40
|
+
# create() can use to re-create it.
|
41
|
+
#
|
42
|
+
# @param raw [Hash] Ruby hash of imported data
|
43
|
+
#
|
44
|
+
def import_to_create(raw)
|
45
|
+
ret = %w(name condition minutes target severity displayExpression
|
46
|
+
additionalInformation).each_with_object({}) do |k, aggr|
|
47
|
+
aggr[k.to_sym] = raw[k]
|
48
|
+
end
|
49
|
+
|
50
|
+
if raw.key?('resolveAfterMinutes')
|
51
|
+
ret[:resolveMinutes] = raw['resolveAfterMinutes']
|
52
|
+
end
|
53
|
+
|
54
|
+
if raw.key?('customerTagsWithCounts')
|
55
|
+
ret[:sharedTags] = raw['customerTagsWithCounts'].keys
|
56
|
+
end
|
57
|
+
ret
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,320 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
require 'json'
|
4
|
+
require 'wavefront-sdk/validators'
|
5
|
+
# require_relative './constants'
|
6
|
+
require_relative './exception'
|
7
|
+
|
8
|
+
module WavefrontCli
|
9
|
+
#
|
10
|
+
# Parent of all the CLI classes. This class uses metaprogramming
|
11
|
+
# techniques to try to make adding new CLI commands and
|
12
|
+
# sub-commands as simple as possible.
|
13
|
+
#
|
14
|
+
# To define a subcommand 'cmd', you only need add it to the
|
15
|
+
# `docopt` description in the relevant section, and create a
|
16
|
+
# method 'do_cmd'. The WavefrontCli::Base::dispatch() method will
|
17
|
+
# find it, and call it. If your subcommand has multiple words,
|
18
|
+
# like 'delete tag', your do method would be called
|
19
|
+
# `do_delete_tag`. The `do_` methods are able to access the
|
20
|
+
# Wavefront SDK object as `wf`, and all docopt options as
|
21
|
+
# `options`.
|
22
|
+
#
|
23
|
+
class Base
|
24
|
+
attr_accessor :wf, :options, :klass, :klass_word
|
25
|
+
|
26
|
+
include Wavefront::Validators
|
27
|
+
|
28
|
+
def initialize(options)
|
29
|
+
@options = options
|
30
|
+
sdk_class = self.class.name.sub(/Cli/, '')
|
31
|
+
@klass_word = sdk_class.split('::').last.downcase
|
32
|
+
validate_input
|
33
|
+
|
34
|
+
if options.include?(:help) && options[:help]
|
35
|
+
puts options
|
36
|
+
exit 0
|
37
|
+
end
|
38
|
+
|
39
|
+
require File.join('wavefront-sdk', @klass_word)
|
40
|
+
@klass = Object.const_get(sdk_class)
|
41
|
+
|
42
|
+
send(:post_initialize, options) if respond_to?(:post_initialize)
|
43
|
+
end
|
44
|
+
|
45
|
+
def run
|
46
|
+
@wf = klass.new(mk_creds, mk_opts)
|
47
|
+
dispatch
|
48
|
+
end
|
49
|
+
|
50
|
+
# We normally validate with a predictable method name. Alert IDs are
|
51
|
+
# validated with #wf_alert_id? etc. If you need to change that, override
|
52
|
+
# this method.
|
53
|
+
#
|
54
|
+
def validator_method
|
55
|
+
"wf_#{klass_word}_id?".to_sym
|
56
|
+
end
|
57
|
+
|
58
|
+
def validator_exception
|
59
|
+
Object.const_get(
|
60
|
+
"Wavefront::Exception::Invalid#{klass_word.capitalize}Id"
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_input
|
65
|
+
validate_id if options[:'<id>']
|
66
|
+
validate_tags if options[:'<tag>']
|
67
|
+
send(:extra_validation) if respond_to?(:extra_validation)
|
68
|
+
end
|
69
|
+
|
70
|
+
def validate_tags
|
71
|
+
Array(options[:'<tag>']).each do |t|
|
72
|
+
begin
|
73
|
+
send(:wf_tag?, t)
|
74
|
+
rescue Wavefront::Exception::InvalidTag
|
75
|
+
abort "'#{t}' is not a valid tag."
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate_id
|
81
|
+
send(validator_method, options[:'<id>'])
|
82
|
+
rescue validator_exception
|
83
|
+
abort "'#{options[:'<id>']}' is not a valid #{klass_word} ID."
|
84
|
+
end
|
85
|
+
|
86
|
+
# Make a wavefront-sdk credentials object from standard
|
87
|
+
# options.
|
88
|
+
#
|
89
|
+
# @return [Hash] containing `token` and `endpoint`.
|
90
|
+
#
|
91
|
+
def mk_creds
|
92
|
+
{ token: options[:token], endpoint: options[:endpoint] }
|
93
|
+
end
|
94
|
+
|
95
|
+
# Make a common wavefront-sdk options object from standard CLI
|
96
|
+
# options.
|
97
|
+
#
|
98
|
+
# @return [Hash] containing `debug`, `verbose`, and `noop`.
|
99
|
+
#
|
100
|
+
def mk_opts
|
101
|
+
{ debug: options[:debug], verbose: options[:verbose],
|
102
|
+
noop: options[:noop] }
|
103
|
+
end
|
104
|
+
|
105
|
+
# To allow a user to default to different output formats for
|
106
|
+
# different object, we define a format for each class. For
|
107
|
+
# instance, `alertformat` or `agentformat`. This method returns
|
108
|
+
# such a string appropriate for the inheriting class.
|
109
|
+
#
|
110
|
+
# @return [Symbol] name of the option or config-file key which
|
111
|
+
# sets the default output format for this class
|
112
|
+
#
|
113
|
+
def format_var
|
114
|
+
options[:format].to_sym
|
115
|
+
# (self.class.name.split('::').last.downcase + 'format').to_sym
|
116
|
+
end
|
117
|
+
|
118
|
+
# Works out the user's command by matching any options docopt has
|
119
|
+
# set to 'true' with any 'do_' method in the class. Then calls that
|
120
|
+
# method, and displays whatever it returns.
|
121
|
+
#
|
122
|
+
# @return [nil]
|
123
|
+
# @raise 'unsupported command', if the command does not match a
|
124
|
+
# `do_` method.
|
125
|
+
#
|
126
|
+
def dispatch
|
127
|
+
#
|
128
|
+
# Take a list of do_ methods, remove the 'do_' from their name,
|
129
|
+
# and break them into arrays of '_' separated words.
|
130
|
+
#
|
131
|
+
m_list = methods.select { |m| m.to_s.start_with?('do_') }.map do |m|
|
132
|
+
m.to_s.split('_')[1..-1]
|
133
|
+
end
|
134
|
+
|
135
|
+
# Sort that array of arrays by length, longest first. Then look
|
136
|
+
# through each deconstructed method name and see if the user
|
137
|
+
# supplied an option for each component. Call the first one that
|
138
|
+
# matches. The order will ensure we match "do_delete_tags" before
|
139
|
+
# we match "do_delete".
|
140
|
+
#
|
141
|
+
m_list.sort_by(&:length).reverse.each do |m|
|
142
|
+
if m.reject { |w| options[w.to_sym] }.empty?
|
143
|
+
method = (%w(do) + m).join('_')
|
144
|
+
return display(public_send(method), method)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
if respond_to?(:do_default)
|
149
|
+
return display(public_send(:do_default), :do_default)
|
150
|
+
end
|
151
|
+
|
152
|
+
raise WavefrontCli::Exception::UnhandledCommand
|
153
|
+
end
|
154
|
+
|
155
|
+
# Display a Ruby object as JSON, YAML, or human-readable. We
|
156
|
+
# provide a default method to format human-readable output, but
|
157
|
+
# you can override it by creating your own
|
158
|
+
# `humanize_command_output` method
|
159
|
+
# control how its output is handled by setting the `response`
|
160
|
+
# instance variable.
|
161
|
+
#
|
162
|
+
# @param data [WavefrontResponse] an object returned by a
|
163
|
+
# Wavefront SDK method. This will contain a 'response'
|
164
|
+
# and 'status' structures.
|
165
|
+
# @param method [String] the name of the method which produced
|
166
|
+
# this output. Used to find a suitable humanize method.
|
167
|
+
#
|
168
|
+
def display(data, method)
|
169
|
+
[:status, :response].each do |b|
|
170
|
+
abort "no #{b} block in API response" unless data.respond_to?(b)
|
171
|
+
end
|
172
|
+
|
173
|
+
unless check_status(data.status)
|
174
|
+
handle_error(method, data.status.code) if format_var == :human
|
175
|
+
abort "API #{data.status.code}: #{data.status.message}."
|
176
|
+
end
|
177
|
+
|
178
|
+
resp = if data.response.respond_to?(:items)
|
179
|
+
data.response.items
|
180
|
+
else
|
181
|
+
data.response
|
182
|
+
end
|
183
|
+
|
184
|
+
handle_response(resp, format_var, method)
|
185
|
+
end
|
186
|
+
|
187
|
+
def check_status(status)
|
188
|
+
status.respond_to?(:result) && status.result == 'OK'
|
189
|
+
end
|
190
|
+
|
191
|
+
# This gives us a chance to catch different errors in
|
192
|
+
# WavefrontDisplay classes. If nothing catches, them abort.
|
193
|
+
#
|
194
|
+
def handle_error(method, code)
|
195
|
+
k = load_display_class
|
196
|
+
k.new({}, options).run_error([method, code].join('_'))
|
197
|
+
end
|
198
|
+
|
199
|
+
def handle_response(resp, format, method)
|
200
|
+
case format
|
201
|
+
when :json
|
202
|
+
puts resp.to_json
|
203
|
+
when :yaml # We don't want the YAML keys to be symbols.
|
204
|
+
puts JSON.parse(resp.to_json).to_yaml
|
205
|
+
when :ruby
|
206
|
+
p resp
|
207
|
+
when :human
|
208
|
+
k = load_display_class
|
209
|
+
k.new(resp, options).run(method)
|
210
|
+
else
|
211
|
+
raise "Unknown output format '#{format}'."
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def load_display_class
|
216
|
+
require_relative File.join('display', klass_word)
|
217
|
+
Object.const_get(klass.name.sub('Wavefront', 'WavefrontDisplay'))
|
218
|
+
end
|
219
|
+
|
220
|
+
# There are things we need to have. If we don't have them, stop
|
221
|
+
# the user right now. Also, if we're in debug mode, print out a
|
222
|
+
# hash of options, which can be very useful when doing actual
|
223
|
+
# debugging. Some classes may have to override this method. The
|
224
|
+
# writer, for instance, uses a proxy and has no token.
|
225
|
+
#
|
226
|
+
def validate_opts
|
227
|
+
raise 'Please supply an API token.' unless options[:token]
|
228
|
+
raise 'Please supply an API endpoint.' unless options[:endpoint]
|
229
|
+
end
|
230
|
+
|
231
|
+
# Give it a path to a file (as a string) and it will return the
|
232
|
+
# contents of that file as a Ruby object. Automatically detects
|
233
|
+
# JSON and YAML. Raises an exception if it doesn't look like
|
234
|
+
# either.
|
235
|
+
#
|
236
|
+
# @param path [String] the file to load
|
237
|
+
# @return [Hash] a Ruby object of the loaded file
|
238
|
+
# @raise 'Unsupported file format.' if the filetype is unknown.
|
239
|
+
# @raise pass through any error loading or parsing the file
|
240
|
+
#
|
241
|
+
def load_file(path)
|
242
|
+
file = Pathname.new(path)
|
243
|
+
raise 'Import file does not exist.' unless file.exist?
|
244
|
+
|
245
|
+
if file.extname == '.json'
|
246
|
+
JSON.parse(IO.read(file))
|
247
|
+
elsif file.extname == '.yaml' || file.extname == '.yml'
|
248
|
+
YAML.safe_load(IO.read(file))
|
249
|
+
else
|
250
|
+
raise 'Unsupported file format.'
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Below here are common methods. Most are used by most classes,
|
255
|
+
# but if they don't match a command described in the docopt
|
256
|
+
# text, the dispatcher will never call them. So, there's no
|
257
|
+
# harm inheriting unneeded things. Some classes override them.
|
258
|
+
#
|
259
|
+
def do_list
|
260
|
+
wf.list(options[:offset] || 0, options[:limit] || 100)
|
261
|
+
end
|
262
|
+
|
263
|
+
def do_describe
|
264
|
+
wf.describe(options[:'<id>'])
|
265
|
+
end
|
266
|
+
|
267
|
+
def do_import
|
268
|
+
raw = load_file(options[:'<file>'])
|
269
|
+
|
270
|
+
begin
|
271
|
+
prepped = import_to_create(raw)
|
272
|
+
rescue => e
|
273
|
+
puts e if options[:debug]
|
274
|
+
raise 'could not parse input.'
|
275
|
+
end
|
276
|
+
|
277
|
+
wf.create(prepped)
|
278
|
+
end
|
279
|
+
|
280
|
+
def do_delete
|
281
|
+
wf.delete(options[:'<id>'])
|
282
|
+
end
|
283
|
+
|
284
|
+
def do_undelete
|
285
|
+
wf.undelete(options[:'<id>'])
|
286
|
+
end
|
287
|
+
|
288
|
+
def do_update
|
289
|
+
k, v = options[:'<key=value>'].split('=')
|
290
|
+
wf.update(options[:'<id>'], k => v)
|
291
|
+
end
|
292
|
+
|
293
|
+
def do_tags
|
294
|
+
wf.tags(options[:'<id>'])
|
295
|
+
end
|
296
|
+
|
297
|
+
def do_tag_add
|
298
|
+
wf.tag_add(options[:'<id>'], options[:'<tag>'].first)
|
299
|
+
end
|
300
|
+
|
301
|
+
def do_tag_delete
|
302
|
+
wf.tag_delete(options[:'<id>'], options[:'<tag>'].first)
|
303
|
+
end
|
304
|
+
|
305
|
+
def do_tag_set
|
306
|
+
wf.tag_set(options[:'<id>'], options[:'<tag>'])
|
307
|
+
end
|
308
|
+
|
309
|
+
def do_tag_clear
|
310
|
+
wf.tag_set(options[:'<id>'], [])
|
311
|
+
end
|
312
|
+
|
313
|
+
# Most things will re-import with the POST method if you remove
|
314
|
+
# the ID.
|
315
|
+
#
|
316
|
+
def import_to_create(raw)
|
317
|
+
raw.delete_if { |k, _v| k == 'id' }
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|