wavefront-cli 5.1.0 → 7.1.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +37 -1
  3. data/HISTORY.md +29 -0
  4. data/README.md +2 -4
  5. data/lib/wavefront-cli/account.rb +119 -0
  6. data/lib/wavefront-cli/alert.rb +29 -0
  7. data/lib/wavefront-cli/base.rb +13 -121
  8. data/lib/wavefront-cli/commands/.rubocop.yml +34 -0
  9. data/lib/wavefront-cli/commands/account.rb +61 -0
  10. data/lib/wavefront-cli/commands/alert.rb +1 -0
  11. data/lib/wavefront-cli/commands/base.rb +1 -1
  12. data/lib/wavefront-cli/commands/proxy.rb +2 -1
  13. data/lib/wavefront-cli/commands/query.rb +4 -1
  14. data/lib/wavefront-cli/commands/role.rb +44 -0
  15. data/lib/wavefront-cli/commands/spy.rb +0 -5
  16. data/lib/wavefront-cli/commands/usergroup.rb +7 -11
  17. data/lib/wavefront-cli/commands/write.rb +7 -2
  18. data/lib/wavefront-cli/controller.rb +5 -63
  19. data/lib/wavefront-cli/display/account.rb +122 -0
  20. data/lib/wavefront-cli/display/alert.rb +8 -0
  21. data/lib/wavefront-cli/display/base.rb +1 -1
  22. data/lib/wavefront-cli/display/cloudintegration.rb +3 -2
  23. data/lib/wavefront-cli/display/printer/long.rb +2 -1
  24. data/lib/wavefront-cli/display/proxy.rb +16 -0
  25. data/lib/wavefront-cli/display/role.rb +66 -0
  26. data/lib/wavefront-cli/display/settings.rb +1 -0
  27. data/lib/wavefront-cli/display/usergroup.rb +18 -14
  28. data/lib/wavefront-cli/exception_handler.rb +87 -0
  29. data/lib/wavefront-cli/helpers/load_file.rb +80 -0
  30. data/lib/wavefront-cli/output/hcl/base.rb +1 -1
  31. data/lib/wavefront-cli/output/hcl/dashboard.rb +1 -1
  32. data/lib/wavefront-cli/proxy.rb +5 -0
  33. data/lib/wavefront-cli/query.rb +13 -7
  34. data/lib/wavefront-cli/role.rb +54 -0
  35. data/lib/wavefront-cli/serviceaccount.rb +0 -6
  36. data/lib/wavefront-cli/spy.rb +0 -8
  37. data/lib/wavefront-cli/subcommands/import.rb +78 -0
  38. data/lib/wavefront-cli/usergroup.rb +8 -8
  39. data/lib/wavefront-cli/version.rb +1 -1
  40. data/lib/wavefront-cli/write.rb +29 -5
  41. data/spec/.rubocop.yml +34 -0
  42. data/spec/test_mixins/delete.rb +1 -2
  43. data/spec/test_mixins/import.rb +9 -3
  44. data/spec/wavefront-cli/account_spec.rb +303 -0
  45. data/spec/wavefront-cli/alert_spec.rb +28 -0
  46. data/spec/wavefront-cli/commands/write_spec.rb +1 -1
  47. data/spec/wavefront-cli/event_spec.rb +1 -1
  48. data/spec/wavefront-cli/output/csv/query_spec.rb +1 -1
  49. data/spec/wavefront-cli/output/wavefront/query_spec.rb +2 -2
  50. data/spec/wavefront-cli/query_spec.rb +20 -3
  51. data/spec/wavefront-cli/role_spec.rb +187 -0
  52. data/spec/wavefront-cli/serviceaccount_spec.rb +3 -3
  53. data/spec/wavefront-cli/usergroup_spec.rb +48 -43
  54. data/spec/wavefront-cli/write_spec.rb +44 -0
  55. data/wavefront-cli.gemspec +3 -3
  56. metadata +32 -32
  57. data/lib/wavefront-cli/commands/cluster.rb +0 -44
  58. data/lib/wavefront-cli/commands/user.rb +0 -54
  59. data/lib/wavefront-cli/display/monitoredcluster.rb +0 -14
  60. data/lib/wavefront-cli/display/user.rb +0 -103
  61. data/lib/wavefront-cli/monitoredcluster.rb +0 -50
  62. data/lib/wavefront-cli/user.rb +0 -92
  63. data/spec/wavefront-cli/monitoredcluster_spec.rb +0 -85
  64. data/spec/wavefront-cli/resources/responses/user-list.json +0 -1
  65. data/spec/wavefront-cli/user_spec.rb +0 -311
@@ -74,5 +74,13 @@ module WavefrontDisplay
74
74
  def do_version
75
75
  puts data.max
76
76
  end
77
+
78
+ def do_affected_hosts
79
+ if data == [nil]
80
+ puts 'Alert event is not attached to any hosts.'
81
+ else
82
+ long_output
83
+ end
84
+ end
77
85
  end
78
86
  end
@@ -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
@@ -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
@@ -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
@@ -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
+ # rubocop:disable Metrics/PerceivedComplexity
12
+ def exception_handler(exception)
13
+ case exception
14
+ when WavefrontCli::Exception::UnhandledCommand
15
+ abort 'Fatal error. Unsupported command. Please open a Github issue.'
16
+ when WavefrontCli::Exception::InvalidInput
17
+ abort "Invalid input. #{exception.message}"
18
+ when Interrupt
19
+ abort "\nOperation aborted at user request."
20
+ when WavefrontCli::Exception::ConfigFileNotFound
21
+ abort "Configuration file #{exception}' not found."
22
+ when WavefrontCli::Exception::CredentialError
23
+ handle_missing_credentials(exception)
24
+ when WavefrontCli::Exception::MandatoryValue
25
+ abort 'A value must be supplied.'
26
+ when Wavefront::Exception::NetworkTimeout
27
+ abort 'Connection timed out.'
28
+ when Wavefront::Exception::InvalidPermission
29
+ abort "'#{exception}' is not a valid Wavefront permission."
30
+ when Wavefront::Exception::InvalidUserGroupId
31
+ abort "'#{exception}' is not a valid user group ID."
32
+ when Wavefront::Exception::InvalidAccountId
33
+ abort "'#{exception}' is not a valid system or user account ID."
34
+ when Wavefront::Exception::InvalidRoleId
35
+ abort "'#{exception}' is not a valid role ID."
36
+ when Wavefront::Exception::InvalidApiTokenId
37
+ abort "'#{exception}' is not a valid API token ID."
38
+ when Wavefront::Exception::InvalidIngestionPolicyId
39
+ abort "'#{exception}' is not a valid ingestion policy ID."
40
+ when WavefrontCli::Exception::InvalidValue
41
+ abort "Invalid value for #{exception}."
42
+ when WavefrontCli::Exception::ProfileExists
43
+ abort "Profile '#{exception}' already exists."
44
+ when WavefrontCli::Exception::ProfileNotFound
45
+ abort "Profile '#{exception}' not found."
46
+ when WavefrontCli::Exception::FileNotFound
47
+ abort 'File not found.'
48
+ when WavefrontCli::Exception::InsufficientData
49
+ abort "Insufficient data. #{exception.message}"
50
+ when WavefrontCli::Exception::InvalidQuery
51
+ abort "Invalid query. API message: '#{exception.message}'."
52
+ when WavefrontCli::Exception::SystemError
53
+ abort "Host system error. #{exception.message}"
54
+ when WavefrontCli::Exception::UnparseableInput
55
+ abort "Cannot parse input. #{exception.message}"
56
+ when WavefrontCli::Exception::UnparseableSearchPattern
57
+ abort 'Searches require a key, a value, and a match operator.'
58
+ when WavefrontCli::Exception::UnsupportedFileFormat
59
+ abort 'Unsupported file format.'
60
+ when WavefrontCli::Exception::UnsupportedOperation
61
+ abort "Unsupported operation.\n#{exception.message}"
62
+ when WavefrontCli::Exception::UnsupportedOutput
63
+ abort exception.message
64
+ when WavefrontCli::Exception::UnsupportedNoop
65
+ abort 'Multiple API call operations cannot be performed as no-ops.'
66
+ when WavefrontCli::Exception::UserGroupNotFound
67
+ abort "Cannot find user group '#{exception.message}'."
68
+ when Wavefront::Exception::UnsupportedWriter
69
+ abort "Unsupported writer '#{exception.message}'."
70
+ when WavefrontCli::Exception::UserError
71
+ abort "User error: #{exception.message}."
72
+ when WavefrontCli::Exception::ImpossibleSearch
73
+ abort 'Search on non-existent key. Please use a top-level field.'
74
+ when Wavefront::Exception::InvalidSamplingValue
75
+ abort 'Sampling rates must be between 0 and 0.05.'
76
+ else
77
+ warn "general error: #{exception}"
78
+ backtrace_message(exception)
79
+ abort
80
+ end
81
+ end
82
+ # rubocop:enable Metrics/MethodLength
83
+ # rubocop:enable Metrics/AbcSize
84
+ # rubocop:enable Metrics/PerceivedComplexity
85
+ # rubocop:enable Metrics/CyclomaticComplexity
86
+ end
87
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../exception'
4
+
5
+ module WavefrontCli
6
+ module Helper
7
+ #
8
+ # Give it a path to a file (as a string) and it will return the
9
+ # contents of that file as a Ruby object. Automatically detects
10
+ # JSON and YAML. Raises an exception if it doesn't look like
11
+ # either. If path is '-' then it will read STDIN.
12
+ #
13
+ # @param path [String] the file to load
14
+ # @return [Hash] a Ruby object of the loaded file
15
+ # @raise WavefrontCli::Exception::UnsupportedFileFormat if the
16
+ # filetype is unknown.
17
+ # @raise pass through any error loading or parsing the file
18
+ #
19
+ class LoadFile
20
+ attr_reader :path
21
+
22
+ def initialize(path)
23
+ @path = path
24
+ end
25
+
26
+ def load
27
+ return load_from_stdin if path == '-'
28
+
29
+ file = Pathname.new(path)
30
+ extname = file.extname.downcase
31
+
32
+ raise WavefrontCli::Exception::FileNotFound unless file.exist?
33
+
34
+ return load_json(file) if extname == '.json'
35
+ return load_yaml(file) if %w[.yaml .yml].include?(extname)
36
+
37
+ raise WavefrontCli::Exception::UnsupportedFileFormat
38
+ end
39
+
40
+ private
41
+
42
+ def load_json(file)
43
+ read_json(IO.read(file))
44
+ end
45
+
46
+ def load_yaml(file)
47
+ read_yaml(IO.read(file))
48
+ end
49
+
50
+ # Read STDIN and return a Ruby object, assuming that STDIN is
51
+ # valid JSON or YAML. This is a dumb method, it does no
52
+ # buffering, so STDIN must be a single block of data. This
53
+ # appears to be a valid assumption for use-cases of this CLI.
54
+ #
55
+ # @return [Object]
56
+ # @raise Wavefront::Exception::UnparseableInput if the input
57
+ # does not parse
58
+ #
59
+ def load_from_stdin
60
+ raw = STDIN.read
61
+
62
+ if raw.start_with?('---')
63
+ read_yaml(raw)
64
+ else
65
+ read_json(raw)
66
+ end
67
+ rescue RuntimeError
68
+ raise Wavefront::Exception::UnparseableInput
69
+ end
70
+
71
+ def read_json(io)
72
+ JSON.parse(io, symbolize_names: true)
73
+ end
74
+
75
+ def read_yaml(io)
76
+ YAML.safe_load(io, symbolize_names: true)
77
+ end
78
+ end
79
+ end
80
+ 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
@@ -91,7 +91,7 @@ module WavefrontHclOutput
91
91
  end
92
92
 
93
93
  def quote_value(val)
94
- val.gsub!(/\$/, '$$') if v.is_a?(String)
94
+ val.gsub!(/\$/, '$$') if val.is_a?(String)
95
95
  super
96
96
  end
97
97
  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