Skip to content
This repository was archived by the owner on Jul 2, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
AllCops:
TargetRubyVersion: 3.0
TargetRubyVersion: 3.1
NewCops: enable

require:
plugins:
- rubocop-rake
- rubocop-minitest
- rubocop-performance
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ gem 'rubocop-minitest', '~> 0.26', group: :development
gem 'rubocop-performance', '~> 1.15', group: :development
gem 'rubocop-rake', '~> 0.6', group: :development
gem 'spy', '~> 1.0', group: :development
gem 'webmock', '~> 3.20.0', group: :development
gem 'webmock', '~> 3.20', group: :development
gem 'yard', '~> 0.9', group: :development
156 changes: 74 additions & 82 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
# Wavefront CLI
[![Test](https://github.com/snltd/wavefront-cli/actions/workflows/test.yml/badge.svg)](https://github.com/snltd/wavefront-cli/actions/workflows/test.yml) ![Release](https://github.com/snltd/wavefront-sdk/workflows/Release/badge.svg) [![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)

[![Test](https://github.com/snltd/wavefront-cli/actions/workflows/test.yml/badge.svg)](https://github.com/snltd/wavefront-cli/actions/workflows/test.yml)
![Release](https://github.com/snltd/wavefront-sdk/workflows/Release/badge.svg)
[![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)

This is a complete command-line interface to
[Wavefront](https://www.wavefront.com/)'s API. It also provides easy
ways to get data into Wavefront.
[Wavefront](https://www.wavefront.com/)'s API. It also provides easy ways to get
data into Wavefront.

The gem is hosted [on
Rubygems](https://rubygems.org/gems/wavefront-cli) and can be
installed with
The gem is hosted [on Rubygems](https://rubygems.org/gems/wavefront-cli) and can
be installed with

```
$ gem install wavefront-cli
```

It is built on [our Wavefront Ruby
SDK](https://github.com/snltd/wavefront-sdk) and requires Ruby >=
3.0. It has no "native extension" dependencies.
It is built on [our Wavefront Ruby SDK](https://github.com/snltd/wavefront-sdk)
and requires Ruby >= 3.1. It has no "native extension" dependencies.

For a far more comprehensive overview/tutorial, please read [this
article](https://sysdef.xyz/article/wavefront-cli).
For a far more comprehensive overview/tutorial, please read
[this article](https://sysdef.xyz/article/wavefront-cli).

```
$ wf --help
Expand Down Expand Up @@ -65,12 +67,11 @@ Use 'wf <command> --help' for further information.

### Credentials and the Config File

You can pass in your Wavefront API and token with command-line
options `-E` and `-t`; with the environment variables
`WAVEFRONT_ENDPOINT` and `WAVEFRONT_TOKEN`, or by putting them in a
configuration file at `${HOME}/.wavefront`. This is an ini-style
file, with a section for each Wavefront account you wish to use.
(None of the tokens shown here are real, of course!)
You can pass in your Wavefront API and token with command-line options `-E` and
`-t`; with the environment variables `WAVEFRONT_ENDPOINT` and `WAVEFRONT_TOKEN`,
or by putting them in a configuration file at `${HOME}/.wavefront`. This is an
ini-style file, with a section for each Wavefront account you wish to use. (None
of the tokens shown here are real, of course!)

```
[default]
Expand All @@ -85,15 +86,13 @@ endpoint = company.wavefront.com
format = yaml
```

You can override the config file location with `-c`, and select a
profile with `-P`. If you don't supply `-P`, the `default` profile
is used.
You can override the config file location with `-c`, and select a profile with
`-P`. If you don't supply `-P`, the `default` profile is used.

### Listing Things

Most commands have a `list` subcommand, which will produce brief
"one thing per line" output. The unique ID of the "thing" is in the
first column.
Most commands have a `list` subcommand, which will produce brief "one thing per
line" output. The unique ID of the "thing" is in the first column.

```
$ wf proxy list
Expand All @@ -102,14 +101,14 @@ $ wf proxy list
926dfb4c-23c6-4fb9-8c8d-833625ab8f6f Agent on shark-wavefront
```

You can get more verbose listings with the `-l` flag. Results may be
paginated. You can progress through pages with the `-L` and `-o`
options, or use `--all` to get everything in one go.
You can get more verbose listings with the `-l` flag. Results may be paginated.
You can progress through pages with the `-L` and `-o` options, or use `--all` to
get everything in one go.

### Describing Things

Most commands have a `describe` subcommand which will tell you more
about the object.
Most commands have a `describe` subcommand which will tell you more about the
object.

```
$ wf proxy describe 917102d1-a10e-497b-ba63-95058f98d4fb
Expand All @@ -129,33 +128,30 @@ ephemeral false
deleted false
```

Most timestamps come back from the API as epoch seconds or epoch
milliseconds. The CLI, in its human-readable descriptions, will
convert those to `YYYY-MM-DD HH:mm:ss` when it `describe`s
something.
Most timestamps come back from the API as epoch seconds or epoch milliseconds.
The CLI, in its human-readable descriptions, will convert those to
`YYYY-MM-DD HH:mm:ss` when it `describe`s something.

### Formats, Importing, and Exporting

All commands support the `-f` option. This takes one of `json`,
`yaml`, `human` and `raw`, and tells the CLI to present the
information it fetches from the Wavefront API in that format. (`raw`
is the raw Ruby representation, which, for instance, you could paste
into `irb`.) Some object types can be exported in other formats.
Alerts, notificants and dashboards can be exported as HCL, for easy
integration with [Space Ape's Wavefront Terraform
All commands support the `-f` option. This takes one of `json`, `yaml`, `human`
and `raw`, and tells the CLI to present the information it fetches from the
Wavefront API in that format. (`raw` is the raw Ruby representation, which, for
instance, you could paste into `irb`.) Some object types can be exported in
other formats. Alerts, notificants and dashboards can be exported as HCL, for
easy integration with
[Space Ape's Wavefront Terraform
provider](https://tech.spaceapegames.com/2017/09/28/building-a-custom-terraform-provider-for-wavefront/).
Query results can be presented as CSV files, or in the native
Wavefront data format.
Query results can be presented as CSV files, or in the native Wavefront data
format.

Human output can be selective. As well as the time formatting
mentioned above, human-readable listings and desctiptions may omit
data which is not likely to be useful, or which is extremely hard to
present in a readable way.
Human output can be selective. As well as the time formatting mentioned above,
human-readable listings and desctiptions may omit data which is not likely to be
useful, or which is extremely hard to present in a readable way.

If you `describe` an object like a dashboard, account, webhook etc as
`json` or `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.
If you `describe` an object like a dashboard, account, webhook etc as `json` or
`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.

```
$ wf user list
Expand All @@ -179,31 +175,29 @@ sysdef.limited@gmail.com
```

You could, of course, modify certain aspects of the exported data before
re-importing. You can import an object over the top of an existing
one with `import --update`.
re-importing. You can import an object over the top of an existing one with
`import --update`.

### Time Windows

Commands which operate on a time window, such as `query` or `event`
will expect that window to be defined with `-s` and `-e` (or
`--start` and `--end`). Times can be in seconds since the epoch, or
any format which [Ruby's `strptime`
method](https://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/DateTime.html#method-c-strptime)
Commands which operate on a time window, such as `query` or `event` will expect
that window to be defined with `-s` and `-e` (or `--start` and `--end`). Times
can be in seconds since the epoch, or any format which
[Ruby's `strptime` method](https://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/DateTime.html#method-c-strptime)
method can parse unaided. For instance:

```
$ wf command --start 12:15 --end 12:20 ...
```

will define a window between 12:15 and 12:20pm today. If you ran
that in the morning, the time would be invalid, and you would get a
400 error from Wavefront, so something of the form
`2016-04-17T12:25:00` would remove all ambiguity.
will define a window between 12:15 and 12:20pm today. If you ran that in the
morning, the time would be invalid, and you would get a 400 error from
Wavefront, so something of the form `2016-04-17T12:25:00` would remove all
ambiguity.

There is no need to include a timezone in your time: the `wf`
CLI will automatically use your local timezone when it parses the
string. You can also specify relative times: for instance '-10m' for
the last ten minutes.
There is no need to include a timezone in your time: the `wf` CLI will
automatically use your local timezone when it parses the string. You can also
specify relative times: for instance '-10m' for the last ten minutes.

## Querying Data

Expand Down Expand Up @@ -246,11 +240,10 @@ timeseries
...
events <none>
warnings <none>

```

By default you get the last ten minutes of data, but the time
windowing rules can be used to specify any range.
By default you get the last ten minutes of data, but the time windowing rules
can be used to specify any range.

## Writing Points

Expand All @@ -272,12 +265,11 @@ or force a timestamp:
$ wf write point -t 16:53:14 cli.example 8
```

More usefully, you can write from a file. Your file must contain
multiple columns: metric name (`m`), metric value (`v`),
timestamp(`t`), and point tags (`T`). `v` is mandatory, `m` can be
filled in with the `-m` flag, `t` can be filled in with the current
timestamp, and `T` is optional, but if used, must be last. You then
tell the CLI what order your fields are in.
More usefully, you can write from a file. Your file must contain multiple
columns: metric name (`m`), metric value (`v`), timestamp(`t`), and point tags
(`T`). `v` is mandatory, `m` can be filled in with the `-m` flag, `t` can be
filled in with the current timestamp, and `T` is optional, but if used, must be
last. You then tell the CLI what order your fields are in.

```
$ cat datafile
Expand All @@ -293,28 +285,28 @@ If you set the file to `-`, you can read from standard in:
$ while true; do echo $RANDOM; sleep 1; done | wf write file -m cli.demo -Fv -
```

If you wish to write points directly via the API, and you have the
"direct ingestion" privilege, just add `-u api` to your `write`
command. To send points to a proxy over HTTP, use `-u http`, and to
write to a Unix socket use `-u socket`.
If you wish to write points directly via the API, and you have the "direct
ingestion" privilege, just add `-u api` to your `write` command. To send points
to a proxy over HTTP, use `-u http`, and to write to a Unix socket use
`-u socket`.

You can write delta metrics with `-i` (for increment).

```
$ wf write point -i counter.example 4
```

To sent negative values, you must use `--` to tell `wf` that you
have finished declaring options, or get creative with your quoting.
To sent negative values, you must use `--` to tell `wf` that you have finished
declaring options, or get creative with your quoting.

```
$ wf write point cli.example -- -10
$ wf write point cli.example "\-10"
```

You can even write distibutions. Either list every number
individually, or use `x` to specify multiples of any value. You can
mix and match within the same line.
You can even write distibutions. Either list every number individually, or use
`x` to specify multiples of any value. You can mix and match within the same
line.

```
$ wf write distribution dist.example 3 1 4 1 1 2 3 6 4 1 3 2
Expand Down
2 changes: 0 additions & 2 deletions lib/wavefront-cli/commands/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,10 @@ def global_option_text(width, term_width)
# columns. This is used to indent following lines
# @param term_width [Integer] the width of the user's terminal
#
# rubocop:disable Lint/FormatParameterMismatch
def opt_row(opt_str, width, term_width = TW)
format(" %s %-#{width}s %s",
*opt_str.split(/\s+/, 3)).opt_fold(term_width, width + 5)
end
# rubocop:enable Lint/FormatParameterMismatch

# @return [Integer] the width of the column containing short and
# long options
Expand Down
6 changes: 3 additions & 3 deletions lib/wavefront-cli/display/serviceaccount.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ def do_list_brief
end

def do_activate
puts format("Activated service account '#{options[:'<id>']}'.")
puts "Activated service account '#{options[:'<id>']}'."
end

def do_deactivate
puts format("Deactivated service account '#{options[:'<id>']}'.")
puts "Deactivated service account '#{options[:'<id>']}'."
end

def do_groups
Expand Down Expand Up @@ -73,7 +73,7 @@ def do_apitoken_list
end

def do_apitoken_delete
puts format("Deleted API token '#{options[:'<token_id>']}'.")
puts "Deleted API token '#{options[:'<token_id>']}'."
end

def do_delete
Expand Down
2 changes: 1 addition & 1 deletion lib/wavefront-cli/output/hcl/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def resource_name
def required_fields
return resp if hcl_fields.empty?

resp.select { |k, _v| hcl_fields.include?(k) }
resp.slice(*hcl_fields)
end

# Format each key-value pair
Expand Down
4 changes: 2 additions & 2 deletions spec/support/minitest_assertions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ def assert_cannot_noop(command)
"performed as no-ops.\n", err)
end

def assert_repeated_output(msg, &block)
def assert_repeated_output(msg, &)
begin
out, err = capture_io(&block)
out, err = capture_io(&)
rescue SystemExit => e
puts e.backtrace
p e
Expand Down
3 changes: 2 additions & 1 deletion spec/wavefront-cli/output/csv/query_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,14 @@ def test__run_raw
end

def check_csv_output(out)
vals = [10, 13]
out.each do |l|
c = l.split(',', 5)
assert_equal(c[0], 'solaris.network.obytes64')
assert_match(/^[\d.]+$/, c[1])
# query returns epoch s timestamp, raw returns epoch ms
assert_match(/^\d+$/, c[2])
assert(c[2].size == 10 || c[2].size == 13)
assert(vals.include?(c[2].size))
assert(c[3] =~ /.*blue$/)
end
end
Expand Down
3 changes: 2 additions & 1 deletion spec/wavefront-cli/output/wavefront/query_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ def test__run_raw
end

def check_wf_native_output(out)
vals = [10, 13]
out.each do |l|
c = l.split(' ', 5)
assert_equal(c[0], 'solaris.network.obytes64')
assert_match(/^[\d.]+$/, c[1])
# query returns epoch s timestamp, raw returns epoch ms
assert_match(/^\d+$/, c[2])
assert(c[2].size == 10 || c[2].size == 13)
assert(vals.include?(c[2].size))
assert_match(/^source=[-\w]+$/, c[3])
assert_match(/^source=[-\w]+$/, c[3])
c[4].split.each { |t| assert_match(/^\w+="[-\w]+"$/, t) }
Expand Down
4 changes: 1 addition & 3 deletions wavefront-cli.gemspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require 'pathname'
require 'date'
require_relative 'lib/wavefront-cli/version'

Gem::Specification.new do |gem|
Expand All @@ -25,6 +23,6 @@ Gem::Specification.new do |gem|
gem.add_dependency 'inifile', '~> 3.0'
gem.add_dependency 'wavefront-sdk', '~> 8.0'

gem.required_ruby_version = Gem::Requirement.new('>= 3.0.0')
gem.required_ruby_version = Gem::Requirement.new('>= 3.1.0')
gem.metadata['rubygems_mfa_required'] = 'true'
end
Loading