wavefront-cli 6.1.0 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -7
  3. data/.travis.yml +4 -5
  4. data/HISTORY.md +34 -1
  5. data/README.md +3 -4
  6. data/lib/wavefront-cli/account.rb +119 -0
  7. data/lib/wavefront-cli/alert.rb +29 -0
  8. data/lib/wavefront-cli/base.rb +10 -12
  9. data/lib/wavefront-cli/cloudintegration.rb +12 -0
  10. data/lib/wavefront-cli/commands/.rubocop.yml +12 -6
  11. data/lib/wavefront-cli/commands/account.rb +61 -0
  12. data/lib/wavefront-cli/commands/alert.rb +1 -0
  13. data/lib/wavefront-cli/commands/base.rb +1 -1
  14. data/lib/wavefront-cli/commands/cloudintegration.rb +4 -1
  15. data/lib/wavefront-cli/commands/query.rb +4 -1
  16. data/lib/wavefront-cli/commands/role.rb +44 -0
  17. data/lib/wavefront-cli/commands/spy.rb +0 -5
  18. data/lib/wavefront-cli/commands/usergroup.rb +7 -11
  19. data/lib/wavefront-cli/commands/write.rb +7 -2
  20. data/lib/wavefront-cli/config.rb +3 -1
  21. data/lib/wavefront-cli/controller.rb +6 -66
  22. data/lib/wavefront-cli/display/account.rb +122 -0
  23. data/lib/wavefront-cli/display/alert.rb +8 -0
  24. data/lib/wavefront-cli/display/base.rb +1 -1
  25. data/lib/wavefront-cli/display/cloudintegration.rb +15 -2
  26. data/lib/wavefront-cli/display/printer/long.rb +7 -2
  27. data/lib/wavefront-cli/display/query.rb +24 -0
  28. data/lib/wavefront-cli/display/role.rb +66 -0
  29. data/lib/wavefront-cli/display/settings.rb +1 -0
  30. data/lib/wavefront-cli/display/usergroup.rb +18 -14
  31. data/lib/wavefront-cli/event.rb +2 -0
  32. data/lib/wavefront-cli/exception_handler.rb +87 -0
  33. data/lib/wavefront-cli/externallink.rb +4 -6
  34. data/lib/wavefront-cli/helpers/load_file.rb +1 -1
  35. data/lib/wavefront-cli/opt_handler.rb +5 -3
  36. data/lib/wavefront-cli/output/hcl/base.rb +1 -1
  37. data/lib/wavefront-cli/query.rb +13 -7
  38. data/lib/wavefront-cli/role.rb +54 -0
  39. data/lib/wavefront-cli/serviceaccount.rb +0 -6
  40. data/lib/wavefront-cli/spy.rb +0 -8
  41. data/lib/wavefront-cli/usergroup.rb +8 -8
  42. data/lib/wavefront-cli/version.rb +1 -1
  43. data/lib/wavefront-cli/write.rb +33 -11
  44. data/spec/.rubocop.yml +13 -6
  45. data/spec/support/minitest_assertions.rb +2 -4
  46. data/spec/test_mixins/delete.rb +1 -2
  47. data/spec/wavefront-cli/account_spec.rb +303 -0
  48. data/spec/wavefront-cli/alert_spec.rb +28 -0
  49. data/spec/wavefront-cli/cloudintegration_spec.rb +19 -6
  50. data/spec/wavefront-cli/commands/write_spec.rb +1 -1
  51. data/spec/wavefront-cli/config_spec.rb +1 -1
  52. data/spec/wavefront-cli/controller_spec.rb +2 -0
  53. data/spec/wavefront-cli/event_spec.rb +1 -1
  54. data/spec/wavefront-cli/output/csv/query_spec.rb +1 -1
  55. data/spec/wavefront-cli/output/wavefront/query_spec.rb +2 -2
  56. data/spec/wavefront-cli/query_spec.rb +20 -3
  57. data/spec/wavefront-cli/role_spec.rb +187 -0
  58. data/spec/wavefront-cli/serviceaccount_spec.rb +3 -3
  59. data/spec/wavefront-cli/usergroup_spec.rb +48 -43
  60. data/spec/wavefront-cli/write_spec.rb +44 -0
  61. data/wavefront-cli.gemspec +6 -6
  62. metadata +27 -36
  63. data/lib/wavefront-cli/commands/user.rb +0 -54
  64. data/lib/wavefront-cli/display/user.rb +0 -103
  65. data/lib/wavefront-cli/user.rb +0 -92
  66. data/spec/wavefront-cli/resources/responses/user-list.json +0 -1
  67. data/spec/wavefront-cli/user_spec.rb +0 -311
@@ -8,11 +8,12 @@ module WavefrontDisplay
8
8
  #
9
9
  class CloudIntegration < Base
10
10
  def do_list_brief
11
- multicolumn(:id, :service)
11
+ multicolumn(:id, :service, :name)
12
12
  end
13
13
 
14
14
  def do_describe
15
- readable_time(:lastReceivedDataPointMs, :lastProcessingTimestamp)
15
+ readable_time(:lastReceivedDataPointMs, :lastProcessingTimestamp,
16
+ :createdEpochMillis, :updatedEpochMillis)
16
17
  drop_fields(:forceSave, :inTrash, :deleted)
17
18
  long_output
18
19
  end
@@ -24,5 +25,17 @@ module WavefrontDisplay
24
25
  def do_disable
25
26
  puts "Disabled '#{options[:'<id>']}'."
26
27
  end
28
+
29
+ def do_awsid_generate
30
+ puts data
31
+ end
32
+
33
+ def do_awsid_delete
34
+ puts "Deleted external ID '#{options[:'<external_id>']}'."
35
+ end
36
+
37
+ def do_awsid_confirm
38
+ puts "'#{data}' is a registered external ID."
39
+ end
27
40
  end
28
41
  end
@@ -8,6 +8,7 @@ module WavefrontDisplayPrinter
8
8
  #
9
9
  class Long
10
10
  attr_reader :opts, :list, :kw
11
+
11
12
  #
12
13
  # @param data [Hash] of data to display
13
14
  # @param fields [Array[Symbol]] requred fields
@@ -55,7 +56,7 @@ module WavefrontDisplayPrinter
55
56
  def preened_value(value)
56
57
  return value unless value.is_a?(String) && value =~ /<.*>/
57
58
 
58
- value.gsub(%r{<\/?[^>]*>}, '').delete("\n")
59
+ value.gsub(%r{</?[^>]*>}, '').delete("\n")
59
60
  end
60
61
 
61
62
  # A recursive function which takes a structure, most likely a
@@ -74,6 +75,7 @@ module WavefrontDisplayPrinter
74
75
  #
75
76
  # Make an array of hashes: { key, value, depth }
76
77
  #
78
+ # rubocop:disable Style/CaseLikeIf
77
79
  def make_list(data, aggr = [], depth = 0, last_key = nil)
78
80
  if data.is_a?(Hash)
79
81
  append_hash(data, aggr, depth)
@@ -83,6 +85,7 @@ module WavefrontDisplayPrinter
83
85
  aggr.<< ['', preened_value(data), depth]
84
86
  end
85
87
  end
88
+ # rubocop:enable Style/CaseLikeIf
86
89
 
87
90
  def smart_value(val)
88
91
  val.to_s.empty? && opts[:none] ? '<none>' : preened_value(val)
@@ -149,6 +152,7 @@ module WavefrontDisplayPrinter
149
152
  # @param depth [Integer]
150
153
  # @return [Array[Array]]
151
154
  #
155
+ # rubocop:disable Style/CaseLikeIf
152
156
  def append_hash(data, aggr, depth)
153
157
  data.each_pair do |k, v|
154
158
  if v.is_a?(Hash)
@@ -162,6 +166,7 @@ module WavefrontDisplayPrinter
162
166
 
163
167
  aggr
164
168
  end
169
+ # rubocop:enable Style/CaseLikeIf
165
170
 
166
171
  # Part of the #make_list recursion. Deals with arrays.
167
172
  #
@@ -174,7 +179,7 @@ module WavefrontDisplayPrinter
174
179
  data.each.with_index(1) do |element, i|
175
180
  aggr = make_list(element, aggr, depth, last_key)
176
181
 
177
- if opts[:separator] && element.is_a?(Hash) && i < data.size
182
+ if opts[:separator] && element.is_a?(Hash) && i < data.size && depth < 3
178
183
  aggr.<< ['', :separator, depth]
179
184
  end
180
185
  end
@@ -20,6 +20,7 @@ module WavefrontDisplay
20
20
  { name: data.name,
21
21
  query: data.query,
22
22
  timeseries: mk_timeseries(data),
23
+ traces: mk_traces(data),
23
24
  events: mk_events(data) }.tap do |d|
24
25
  d[:warnings] = data[:warnings] if show_warnings?
25
26
  end
@@ -54,6 +55,12 @@ module WavefrontDisplay
54
55
  data[:events].map { |s| humanize_event(s) }
55
56
  end
56
57
 
58
+ def mk_traces(data)
59
+ return [] unless data.key?(:traces)
60
+
61
+ data[:traces].map { |t| humanize_trace(t) }
62
+ end
63
+
57
64
  def do_run
58
65
  do_default
59
66
  end
@@ -99,6 +106,23 @@ module WavefrontDisplay
99
106
  end
100
107
  end
101
108
 
109
+ def humanize_trace(data)
110
+ data.tap do |t|
111
+ t[:start] = human_time(t[:start_ms])
112
+ t[:end] = human_time(t[:end_ms])
113
+ t.delete(:start_ms)
114
+ t.delete(:end_ms)
115
+ t.delete(:startMs)
116
+ t.spans = t.spans.map { |s| humanize_span(s) }
117
+ end
118
+ end
119
+
120
+ def humanize_span(span)
121
+ span.tap do |s|
122
+ s[:startMs] = human_time(s[:startMs])
123
+ end
124
+ end
125
+
102
126
  def row_time_and_val(row)
103
127
  if row.is_a?(Hash)
104
128
  [human_time(row[:timestamp]), row[:value]]
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module WavefrontDisplay
6
+ #
7
+ # Format human-readable output for role command
8
+ #
9
+ class Role < Base
10
+ def do_list_brief
11
+ data.map! do |d|
12
+ d.merge(acct_count: "#{d[:linkedAccountsCount]} accounts",
13
+ group_count: "#{d[:linkedGroupsCount]} groups")
14
+ end
15
+ multicolumn(:id, :name, :acct_count, :group_count)
16
+ end
17
+
18
+ def do_accounts
19
+ if data.empty?
20
+ puts "No accounts have role '#{options[:'<id>']}'."
21
+ else
22
+ multicolumn(:identifier)
23
+ end
24
+ end
25
+
26
+ def do_groups
27
+ if data.empty?
28
+ puts "No groups have role '#{options[:'<id>']}'."
29
+ else
30
+ multicolumn(:id, :name)
31
+ end
32
+ end
33
+
34
+ def do_permissions
35
+ if data[:permissions].empty?
36
+ puts "Role '#{options[:'<id>']}' has no permissions."
37
+ else
38
+ puts data[:permissions]
39
+ end
40
+ end
41
+
42
+ def do_grant
43
+ puts format("Granted '%<perm>s' permission to '%<id>s'.",
44
+ perm: options[:'<permission>'],
45
+ id: options[:'<id>'])
46
+ end
47
+
48
+ def do_revoke
49
+ puts format("Revoked '%<perm>s' permission from '%<id>s'.",
50
+ perm: options[:'<permission>'],
51
+ id: options[:'<id>'])
52
+ end
53
+
54
+ def do_give_to
55
+ puts format("Gave '%<role>s' to %<members>s.",
56
+ members: quoted(options[:'<member>']),
57
+ role: options[:'<id>']).fold(TW, 0)
58
+ end
59
+
60
+ def do_take_from
61
+ puts format("Took '%<role>s' from %<members>s.",
62
+ members: quoted(options[:'<member>']),
63
+ role: options[:'<id>']).fold(TW, 0)
64
+ end
65
+ end
66
+ end
@@ -8,6 +8,7 @@ module WavefrontDisplay
8
8
  #
9
9
  class Settings < Base
10
10
  def do_list_permissions
11
+ data.sort_by! { |p| p[:groupName] }
11
12
  options[:long] ? long_output : multicolumn(:groupName)
12
13
  end
13
14
 
@@ -15,28 +15,28 @@ module WavefrontDisplay
15
15
  puts "Deleted user group '#{options[:'<id>']}'."
16
16
  end
17
17
 
18
- def do_add_user
18
+ def do_add_to
19
19
  puts format("Added %<quoted_user>s to '%<group_id>s'.",
20
20
  quoted_user: quoted(options[:'<user>']),
21
21
  group_id: options[:'<id>']).fold(TW, 0)
22
22
  end
23
23
 
24
- def do_remove_user
24
+ def do_remove_from
25
25
  puts format("Removed %<quoted_user>s from '%<group_id>s'.",
26
26
  quoted_user: quoted(options[:'<user>']),
27
27
  group_id: options[:'<id>']).fold(TW, 0)
28
28
  end
29
29
 
30
- def do_grant
31
- puts format("Granted '%<perm>s' permission to '%<group_id>s'.",
32
- perm: options[:'<permission>'],
33
- group_id: options[:'<id>'])
30
+ def do_add_role
31
+ puts format("Added %<quoted_role>s to '%<group_id>s'.",
32
+ quoted_role: quoted(options[:'<role>']),
33
+ group_id: options[:'<id>']).fold(TW, 0)
34
34
  end
35
35
 
36
- def do_revoke
37
- puts format("Revoked '%<perm>s' permission from '%<group_id>s'.",
38
- perm: options[:'<permission>'],
39
- group_id: options[:'<id>'])
36
+ def do_remove_role
37
+ puts format("Removed %<quoted_role>s from '%<group_id>s'.",
38
+ quoted_role: quoted(options[:'<role>']),
39
+ group_id: options[:'<id>']).fold(TW, 0)
40
40
  end
41
41
 
42
42
  def do_users
@@ -47,12 +47,16 @@ module WavefrontDisplay
47
47
  end)
48
48
  end
49
49
 
50
- def do_permissions
51
- puts(if !data.include?(:permissions) || data[:permissions].empty?
52
- "Group '#{options[:'<id>']}' has no permissions."
50
+ def do_roles
51
+ puts(if !data.include?(:roles) || data[:roles].empty?
52
+ "Group '#{options[:'<id>']}' has no roles attached."
53
53
  else
54
- data[:permissions]
54
+ data[:roles].map { |r| r[:id] }
55
55
  end)
56
56
  end
57
+
58
+ def do_permissions
59
+ puts data[:roles].map { |r| r[:permissions] }.flatten.sort.uniq
60
+ end
57
61
  end
58
62
  end
@@ -97,6 +97,7 @@ module WavefrontCli
97
97
  # return [Hash] body for #create() method
98
98
  #
99
99
  # rubocop:disable Metrics/MethodLength
100
+ # rubocop:disable Metrics/AbcSize
100
101
  def create_body(opts, t_start)
101
102
  { name: opts[:'<event>'],
102
103
  startTime: t_start,
@@ -111,6 +112,7 @@ module WavefrontCli
111
112
  end
112
113
  end
113
114
  end
115
+ # rubocop:enable Metrics/AbcSize
114
116
  # rubocop:enable Metrics/MethodLength
115
117
 
116
118
  def annotations(opts)
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WavefrontCli
4
+ #
5
+ # Handle fatal errors.
6
+ #
7
+ module ExceptionMixins
8
+ # rubocop:disable Metrics/MethodLength
9
+ # rubocop:disable Metrics/AbcSize
10
+ # rubocop:disable Metrics/CyclomaticComplexity
11
+ def exception_handler(exception)
12
+ case exception
13
+ when WavefrontCli::Exception::UnhandledCommand
14
+ abort 'Fatal error. Unsupported command. Please open a Github issue.'
15
+ when WavefrontCli::Exception::InvalidInput
16
+ abort "Invalid input. #{exception.message}"
17
+ when Interrupt
18
+ abort "\nOperation aborted at user request."
19
+ when WavefrontCli::Exception::ConfigFileNotFound
20
+ abort "Configuration file #{exception}' not found."
21
+ when WavefrontCli::Exception::CredentialError
22
+ handle_missing_credentials(exception)
23
+ when WavefrontCli::Exception::MandatoryValue
24
+ abort 'A value must be supplied.'
25
+ when Wavefront::Exception::NetworkTimeout
26
+ abort 'Connection timed out.'
27
+ when Wavefront::Exception::InvalidPermission
28
+ abort "'#{exception}' is not a valid Wavefront permission."
29
+ when Wavefront::Exception::InvalidUserGroupId
30
+ abort "'#{exception}' is not a valid user group ID."
31
+ when Wavefront::Exception::InvalidAccountId
32
+ abort "'#{exception}' is not a valid system or user account ID."
33
+ when Wavefront::Exception::InvalidAwsExternalId
34
+ abort "'#{exception}' is not a valid AWS external ID."
35
+ when Wavefront::Exception::InvalidRoleId
36
+ abort "'#{exception}' is not a valid role ID."
37
+ when Wavefront::Exception::InvalidApiTokenId
38
+ abort "'#{exception}' is not a valid API token ID."
39
+ when Wavefront::Exception::InvalidIngestionPolicyId
40
+ abort "'#{exception}' is not a valid ingestion policy ID."
41
+ when WavefrontCli::Exception::InvalidValue
42
+ abort "Invalid value for #{exception}."
43
+ when WavefrontCli::Exception::ProfileExists
44
+ abort "Profile '#{exception}' already exists."
45
+ when WavefrontCli::Exception::ProfileNotFound
46
+ abort "Profile '#{exception}' not found."
47
+ when WavefrontCli::Exception::FileNotFound
48
+ abort 'File not found.'
49
+ when WavefrontCli::Exception::InsufficientData
50
+ abort "Insufficient data. #{exception.message}"
51
+ when WavefrontCli::Exception::InvalidQuery
52
+ abort "Invalid query. API message: '#{exception.message}'."
53
+ when WavefrontCli::Exception::SystemError
54
+ abort "Host system error. #{exception.message}"
55
+ when WavefrontCli::Exception::UnparseableInput
56
+ abort "Cannot parse input. #{exception.message}"
57
+ when WavefrontCli::Exception::UnparseableSearchPattern
58
+ abort 'Searches require a key, a value, and a match operator.'
59
+ when WavefrontCli::Exception::UnsupportedFileFormat
60
+ abort 'Unsupported file format.'
61
+ when WavefrontCli::Exception::UnsupportedOperation
62
+ abort "Unsupported operation.\n#{exception.message}"
63
+ when WavefrontCli::Exception::UnsupportedOutput
64
+ abort exception.message
65
+ when WavefrontCli::Exception::UnsupportedNoop
66
+ abort 'Multiple API call operations cannot be performed as no-ops.'
67
+ when WavefrontCli::Exception::UserGroupNotFound
68
+ abort "Cannot find user group '#{exception.message}'."
69
+ when Wavefront::Exception::UnsupportedWriter
70
+ abort "Unsupported writer '#{exception.message}'."
71
+ when WavefrontCli::Exception::UserError
72
+ abort "User error: #{exception.message}."
73
+ when WavefrontCli::Exception::ImpossibleSearch
74
+ abort 'Search on non-existent key. Please use a top-level field.'
75
+ when Wavefront::Exception::InvalidSamplingValue
76
+ abort 'Sampling rates must be between 0 and 0.05.'
77
+ else
78
+ warn "general error: #{exception}"
79
+ backtrace_message(exception)
80
+ abort
81
+ end
82
+ end
83
+ # rubocop:enable Metrics/MethodLength
84
+ # rubocop:enable Metrics/AbcSize
85
+ # rubocop:enable Metrics/CyclomaticComplexity
86
+ end
87
+ end
@@ -38,12 +38,10 @@ module WavefrontCli
38
38
 
39
39
  def point_filter_regexes
40
40
  ret = options[:pointregex].each_with_object({}) do |r, a|
41
- begin
42
- k, v = r.split('=', 2)
43
- a[k.to_sym] = v
44
- rescue StandardError
45
- puts "cannot parse point regex '#{r}'. Skipping."
46
- end
41
+ k, v = r.split('=', 2)
42
+ a[k.to_sym] = v
43
+ rescue StandardError
44
+ puts "cannot parse point regex '#{r}'. Skipping."
47
45
  end
48
46
 
49
47
  ret.empty? ? nil : ret
@@ -57,7 +57,7 @@ module WavefrontCli
57
57
  # does not parse
58
58
  #
59
59
  def load_from_stdin
60
- raw = STDIN.read
60
+ raw = $stdin.read
61
61
 
62
62
  if raw.start_with?('---')
63
63
  read_yaml(raw)
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'pathname'
4
4
  require 'wavefront-sdk/credentials'
5
- require_relative 'constants.rb'
5
+ require_relative 'constants'
6
6
 
7
7
  module WavefrontCli
8
8
  #
@@ -35,6 +35,8 @@ module WavefrontCli
35
35
  abort "Configuration file '#{e}' not found."
36
36
  rescue Wavefront::Exception::InvalidConfigFile => e
37
37
  abort "Could not load configuration file '#{e.message}'."
38
+ rescue Wavefront::Exception::MissingConfigProfile => e
39
+ abort "Cannot find profile '#{e}'."
38
40
  end
39
41
 
40
42
  # Create an options hash to pass to the Wavefront::Credentials
@@ -45,7 +47,7 @@ module WavefrontCli
45
47
  # :profile
46
48
  #
47
49
  def setup_cred_opts(cli_opts)
48
- cred_opts = {}
50
+ cred_opts = { raise_on_no_profile: true }
49
51
 
50
52
  if cli_opts[:config]
51
53
  cred_opts[:file] = Pathname.new(cli_opts[:config])
@@ -69,7 +71,7 @@ module WavefrontCli
69
71
  #
70
72
  def load_profile(cred_opts)
71
73
  creds = Wavefront::Credentials.new(cred_opts).config
72
- Hash[creds.map { |k, v| [k.to_sym, v] }]
74
+ creds.transform_keys(&:to_sym)
73
75
  end
74
76
  end
75
77
  end
@@ -92,7 +92,7 @@ module WavefrontHclOutput
92
92
  def quote_value(val)
93
93
  case val.class.to_s.to_sym
94
94
  when :String
95
- format('"%<value>s"', value: val.gsub(/\"/, '\"'))
95
+ format('"%<value>s"', value: val.gsub(/"/, '\"'))
96
96
  else
97
97
  val
98
98
  end
@@ -51,20 +51,26 @@ module WavefrontCli
51
51
 
52
52
  # @return [Hash] options for the SDK query method
53
53
  #
54
- # rubocop:disable Metrics/AbcSize
55
54
  def q_opts
55
+ basic_q_opts.tap do |o|
56
+ o[:n] = options[:name]
57
+ o[:p] = options[:points]
58
+ o[:view] = 'HISTOGRAM' if options[:histogramview]
59
+ o[:cached] = false if options[:nocache]
60
+ end.compact
61
+ end
62
+
63
+ # Every query gets these options. They're modified by q_opts
64
+ #
65
+ def basic_q_opts
56
66
  { autoEvents: options[:events],
57
67
  i: options[:inclusive],
58
68
  summarization: options[:summarize] || 'mean',
59
69
  listMode: true,
60
- strict: true,
70
+ strict: !options[:nostrict],
61
71
  includeObsoleteMetrics: options[:obsolete],
62
- sorted: true }.tap do |o|
63
- o[:n] = options[:name] if options[:name]
64
- o[:p] = options[:points] if options[:points]
65
- end
72
+ sorted: true }
66
73
  end
67
- # rubocop:enable Metrics/AbcSize
68
74
 
69
75
  # @return [Integer] start of query window. If one has been
70
76
  # given, that; if not, ten minutes ago