wavefront-cli 5.1.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
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