wavefront-cli 6.0.0 → 8.0.1

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -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/proxy.rb +2 -1
  16. data/lib/wavefront-cli/commands/query.rb +4 -1
  17. data/lib/wavefront-cli/commands/role.rb +44 -0
  18. data/lib/wavefront-cli/commands/spy.rb +0 -5
  19. data/lib/wavefront-cli/commands/usergroup.rb +7 -11
  20. data/lib/wavefront-cli/commands/write.rb +7 -2
  21. data/lib/wavefront-cli/config.rb +3 -1
  22. data/lib/wavefront-cli/controller.rb +6 -66
  23. data/lib/wavefront-cli/display/account.rb +122 -0
  24. data/lib/wavefront-cli/display/alert.rb +8 -0
  25. data/lib/wavefront-cli/display/base.rb +1 -1
  26. data/lib/wavefront-cli/display/cloudintegration.rb +15 -2
  27. data/lib/wavefront-cli/display/printer/long.rb +6 -1
  28. data/lib/wavefront-cli/display/proxy.rb +16 -0
  29. data/lib/wavefront-cli/display/role.rb +66 -0
  30. data/lib/wavefront-cli/display/settings.rb +1 -0
  31. data/lib/wavefront-cli/display/usergroup.rb +18 -14
  32. data/lib/wavefront-cli/event.rb +2 -0
  33. data/lib/wavefront-cli/exception_handler.rb +87 -0
  34. data/lib/wavefront-cli/externallink.rb +4 -6
  35. data/lib/wavefront-cli/helpers/load_file.rb +1 -1
  36. data/lib/wavefront-cli/opt_handler.rb +5 -3
  37. data/lib/wavefront-cli/output/hcl/base.rb +1 -1
  38. data/lib/wavefront-cli/proxy.rb +5 -0
  39. data/lib/wavefront-cli/query.rb +13 -7
  40. data/lib/wavefront-cli/role.rb +54 -0
  41. data/lib/wavefront-cli/serviceaccount.rb +0 -6
  42. data/lib/wavefront-cli/spy.rb +0 -8
  43. data/lib/wavefront-cli/usergroup.rb +8 -8
  44. data/lib/wavefront-cli/version.rb +1 -1
  45. data/lib/wavefront-cli/write.rb +33 -11
  46. data/spec/.rubocop.yml +13 -6
  47. data/spec/support/minitest_assertions.rb +2 -4
  48. data/spec/test_mixins/delete.rb +1 -2
  49. data/spec/wavefront-cli/account_spec.rb +303 -0
  50. data/spec/wavefront-cli/alert_spec.rb +28 -0
  51. data/spec/wavefront-cli/cloudintegration_spec.rb +19 -6
  52. data/spec/wavefront-cli/commands/write_spec.rb +1 -1
  53. data/spec/wavefront-cli/config_spec.rb +1 -1
  54. data/spec/wavefront-cli/controller_spec.rb +2 -0
  55. data/spec/wavefront-cli/event_spec.rb +1 -1
  56. data/spec/wavefront-cli/output/csv/query_spec.rb +1 -1
  57. data/spec/wavefront-cli/output/wavefront/query_spec.rb +2 -2
  58. data/spec/wavefront-cli/query_spec.rb +20 -3
  59. data/spec/wavefront-cli/role_spec.rb +187 -0
  60. data/spec/wavefront-cli/serviceaccount_spec.rb +3 -3
  61. data/spec/wavefront-cli/usergroup_spec.rb +48 -43
  62. data/spec/wavefront-cli/write_spec.rb +44 -0
  63. data/wavefront-cli.gemspec +6 -6
  64. metadata +27 -36
  65. data/lib/wavefront-cli/commands/user.rb +0 -54
  66. data/lib/wavefront-cli/display/user.rb +0 -103
  67. data/lib/wavefront-cli/user.rb +0 -92
  68. data/spec/wavefront-cli/resources/responses/user-list.json +0 -1
  69. data/spec/wavefront-cli/user_spec.rb +0 -311
@@ -112,7 +112,7 @@ module WavefrontDisplay
112
112
  # long listing objects. Subclasses may define their own.
113
113
  #
114
114
  def priority_keys
115
- %i[id name]
115
+ %i[id name identifier]
116
116
  end
117
117
 
118
118
  def prioritize_keys(data, keys)
@@ -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
  #
@@ -7,6 +7,16 @@ module WavefrontDisplay
7
7
  # Format human-readable output for proxies.
8
8
  #
9
9
  class Proxy < Base
10
+ def do_list
11
+ filter_inactive_proxies! if options[:active]
12
+ super
13
+ end
14
+
15
+ def do_list_brief
16
+ filter_inactive_proxies! if options[:active]
17
+ super
18
+ end
19
+
10
20
  def do_describe
11
21
  readable_time(:lastCheckInTime)
12
22
  long_output
@@ -15,5 +25,11 @@ module WavefrontDisplay
15
25
  def do_versions
16
26
  multicolumn(:id, :version, :name)
17
27
  end
28
+
29
+ private
30
+
31
+ def filter_inactive_proxies!
32
+ data.delete_if { |p| p[:status] != 'ACTIVE' }
33
+ end
18
34
  end
19
35
  end
@@ -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
@@ -7,6 +7,11 @@ module WavefrontCli
7
7
  # CLI coverage for the v2 'proxy' API.
8
8
  #
9
9
  class Proxy < WavefrontCli::Base
10
+ def do_list
11
+ options[:all] = true if options[:active]
12
+ super
13
+ end
14
+
10
15
  def no_api_response
11
16
  %w[do_versions]
12
17
  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