wavefront-sdk 3.6.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +43 -2
  3. data/.travis.yml +5 -5
  4. data/HISTORY.md +32 -0
  5. data/README.md +4 -3
  6. data/lib/wavefront-sdk/account.rb +303 -0
  7. data/lib/wavefront-sdk/api_mixins/user.rb +20 -0
  8. data/lib/wavefront-sdk/core/api_caller.rb +50 -7
  9. data/lib/wavefront-sdk/core/exception.rb +6 -0
  10. data/lib/wavefront-sdk/core/response.rb +2 -1
  11. data/lib/wavefront-sdk/defs/version.rb +1 -3
  12. data/lib/wavefront-sdk/ingestionpolicy.rb +85 -0
  13. data/lib/wavefront-sdk/paginator/base.rb +21 -15
  14. data/lib/wavefront-sdk/query.rb +0 -1
  15. data/lib/wavefront-sdk/role.rb +104 -0
  16. data/lib/wavefront-sdk/spy.rb +126 -0
  17. data/lib/wavefront-sdk/stdlib/array.rb +1 -1
  18. data/lib/wavefront-sdk/stdlib/time.rb +13 -0
  19. data/lib/wavefront-sdk/support/mixins.rb +1 -1
  20. data/lib/wavefront-sdk/unstable/README.md +4 -0
  21. data/lib/wavefront-sdk/unstable/chart.rb +90 -0
  22. data/lib/wavefront-sdk/unstable/unstable.rb +9 -0
  23. data/lib/wavefront-sdk/usage.rb +31 -0
  24. data/lib/wavefront-sdk/user.rb +41 -0
  25. data/lib/wavefront-sdk/usergroup.rb +17 -16
  26. data/lib/wavefront-sdk/validators.rb +65 -7
  27. data/lib/wavefront-sdk/write.rb +13 -3
  28. data/spec/.rubocop.yml +42 -1
  29. data/spec/spec_helper.rb +4 -0
  30. data/spec/support/minitest_assertions.rb +4 -4
  31. data/spec/wavefront-sdk/account_spec.rb +238 -0
  32. data/spec/wavefront-sdk/core/api_caller_spec.rb +43 -0
  33. data/spec/wavefront-sdk/ingestionpolicy_spec.rb +43 -0
  34. data/spec/wavefront-sdk/metric_helper_spec.rb +1 -1
  35. data/spec/wavefront-sdk/role_spec.rb +68 -0
  36. data/spec/wavefront-sdk/spy_spec.rb +113 -0
  37. data/spec/wavefront-sdk/unstable/chart_spec.rb +39 -0
  38. data/spec/wavefront-sdk/usage_spec.rb +33 -0
  39. data/spec/wavefront-sdk/user_spec.rb +20 -0
  40. data/spec/wavefront-sdk/usergroup_spec.rb +21 -11
  41. data/spec/wavefront-sdk/validators_spec.rb +52 -6
  42. data/wavefront-sdk.gemspec +4 -4
  43. metadata +30 -9
@@ -25,6 +25,26 @@ module Wavefront
25
25
 
26
26
  list.each { |id| wf_usergroup_id?(id) }
27
27
  end
28
+
29
+ # Validate a list of accounts.
30
+ # @param list [Array[String]] list of account IDs
31
+ # @raise Wavefront::Exception::InvalidAccount
32
+ #
33
+ def validate_account_list(list)
34
+ raise ArgumentError unless list.is_a?(Array)
35
+
36
+ list.each { |id| wf_account_id?(id) }
37
+ end
38
+
39
+ # Validate a list of roles
40
+ # @param list [Array[String]] list of role IDs
41
+ # @raise Wavefront::Exception::InvalidRole
42
+ #
43
+ def validate_role_list(list)
44
+ raise ArgumentError unless list.is_a?(Array)
45
+
46
+ list.each { |id| wf_role_id?(id) }
47
+ end
28
48
  end
29
49
  end
30
50
  end
@@ -6,6 +6,7 @@ require 'addressable'
6
6
  require_relative 'response'
7
7
  require_relative '../defs/version'
8
8
  require_relative '../support/mixins'
9
+ require_relative '../stdlib/time'
9
10
 
10
11
  module Wavefront
11
12
  #
@@ -78,14 +79,47 @@ module Wavefront
78
79
  # was cleaner to add this method. Parameters are same as #get.
79
80
  #
80
81
  def get_flat_params(path, query = {})
81
- conn = mk_conn(path,
82
- {},
83
- request: {
84
- params_encoder: Faraday::FlatParamsEncoder
85
- },
86
- params: query)
82
+ make_call(flat_param_conn(path, query), :get)
83
+ end
84
+
85
+ # This is used by the Wavefront::Unstable::Spy methods to stream data.
86
+ # It prints to standard out.
87
+ # @param path [String] path to be appended to the net[:api_base] path.
88
+ # @param query [Hash] optional key-value pairs with will be made into a
89
+ # query string
90
+ # @param opts [Hash] keys:
91
+ # timestamp_chunks -- prints a timestamp before each chunk of streamed
92
+ # data
93
+ # timeout -- after approximately this many seconds, return. It will be
94
+ # the first chunk *after* the given time
95
+ # @return
96
+ #
97
+ def get_stream(path, query = {}, opts = {})
98
+ conn = flat_param_conn(path, query)
99
+ verbosity(conn, :get, query)
100
+ stream_connection(conn, query, opts)
101
+ rescue Faraday::TimeoutError
102
+ raise Wavefront::Exception::NetworkTimeout
103
+ rescue StopIteration
104
+ nil
105
+ end
106
+
107
+ def stream_connection(conn, query, opts)
108
+ t_end = end_time(opts)
109
+
110
+ conn.get do |req|
111
+ req.params = query
112
+ req.options.on_data = proc do |chunk, _size|
113
+ raise StopIteration if t_end && Time.right_now >= t_end
87
114
 
88
- make_call(conn, :get)
115
+ puts Time.now if opts[:timestamp_chunks]
116
+ puts chunk
117
+ end
118
+ end
119
+ end
120
+
121
+ def end_time(opts)
122
+ Time.right_now + opts[:timeout] if opts[:timeout]&.positive?
89
123
  end
90
124
 
91
125
  # Make a POST call to the Wavefront API and return the result as
@@ -240,5 +274,14 @@ module Wavefront
240
274
  end
241
275
  end
242
276
  end
277
+
278
+ def flat_param_conn(path, query)
279
+ mk_conn(path,
280
+ {},
281
+ request: {
282
+ params_encoder: Faraday::FlatParamsEncoder
283
+ },
284
+ params: query)
285
+ end
243
286
  end
244
287
  end
@@ -8,6 +8,7 @@ module Wavefront
8
8
  class CredentialError < RuntimeError; end
9
9
  class EmptyMetricName < RuntimeError; end
10
10
  class EnumerableError < RuntimeError; end
11
+ class InvalidAccountId < RuntimeError; end
11
12
  class InvalidAlertId < RuntimeError; end
12
13
  class InvalidAlertSeverity < RuntimeError; end
13
14
  class InvalidApiTokenId < RuntimeError; end
@@ -23,9 +24,11 @@ module Wavefront
23
24
  class InvalidExternalLinkId < RuntimeError; end
24
25
  class InvalidGranularity < RuntimeError; end
25
26
  class InvalidHostname < RuntimeError; end
27
+ class InvalidIngestionPolicyId < RuntimeError; end
26
28
  class InvalidIntegrationId < RuntimeError; end
27
29
  class InvalidLinkTemplate < RuntimeError; end
28
30
  class InvalidMaintenanceWindowId < RuntimeError; end
31
+ class InvalidMonitoredClusterId < RuntimeError; end
29
32
  class InvalidMessageId < RuntimeError; end
30
33
  class InvalidMetricName < RuntimeError; end
31
34
  class InvalidMetricValue < RuntimeError; end
@@ -35,7 +38,9 @@ module Wavefront
35
38
  class InvalidPoint < RuntimeError; end
36
39
  class InvalidPrefixLength < RuntimeError; end
37
40
  class InvalidProxyId < RuntimeError; end
41
+ class InvalidRoleId < RuntimeError; end
38
42
  class InvalidRelativeTime < RuntimeError; end
43
+ class InvalidSamplingValue < RuntimeError; end
39
44
  class InvalidSavedSearchEntity < RuntimeError; end
40
45
  class InvalidSavedSearchId < RuntimeError; end
41
46
  class InvalidServiceAccountId < RuntimeError; end
@@ -49,6 +54,7 @@ module Wavefront
49
54
  class InvalidUserGroupId < RuntimeError; end
50
55
  class InvalidVersion < RuntimeError; end
51
56
  class InvalidWebhookId < RuntimeError; end
57
+ class NetworkTimeout < RuntimeError; end
52
58
  class NotImplemented < RuntimeError; end
53
59
  class SocketError < RuntimeError; end
54
60
  class UnparseableResponse < RuntimeError; end
@@ -25,7 +25,8 @@ module Wavefront
25
25
  #
26
26
  class Response
27
27
  include Wavefront::Mixins
28
- attr_reader :status, :response, :opts, :logger
28
+ attr_reader :status, :opts, :logger
29
+ attr_accessor :response
29
30
 
30
31
  # Create and return a Wavefront::Response object
31
32
  # @param json [String] a raw response body from the Wavefront API
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
4
-
5
- WF_SDK_VERSION = '3.6.0'
3
+ WF_SDK_VERSION = '5.0.0'
6
4
  WF_SDK_LOCATION = Pathname.new(__dir__).parent.parent.parent
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core/api'
4
+
5
+ module Wavefront
6
+ #
7
+ # View and manage Wavefront ingestion policies.
8
+ #
9
+ # These use the Usage API path.
10
+ #
11
+ class IngestionPolicy < CoreApi
12
+ def api_base
13
+ '/usage/ingestionpolicy'
14
+ end
15
+
16
+ # GET /api/v2/usage/ingestionpolicy
17
+ # Get all ingestion policies for a customer
18
+ #
19
+ # @return [Wavefront::Response]
20
+ #
21
+ # @param offset [Int] ingestion policy at which the list begins
22
+ # @param limit [Int] the number of ingestion policies to return
23
+ # @return [Wavefront::Response]
24
+ #
25
+ def list(offset = 0, limit = 100)
26
+ api.get('', offset: offset, limit: limit)
27
+ end
28
+
29
+ # POST /api/v2/usage/ingestionpolicy
30
+ # Create a specific ingestion policy
31
+ #
32
+ # @param body [Hash] description of ingestion policy
33
+ # @return [Wavefront::Response]
34
+ #
35
+ def create(body)
36
+ raise ArgumentError unless body.is_a?(Hash)
37
+
38
+ api.post('', body, 'application/json')
39
+ end
40
+
41
+ # DELETE /api/v2/usage/ingestionpolicy/{id}
42
+ # Delete a specific ingestion policy
43
+ #
44
+ # @param id [String] ID of the alert
45
+ # @return [Wavefront::Response]
46
+ #
47
+ def delete(id)
48
+ wf_ingestionpolicy_id?(id)
49
+ api.delete(id)
50
+ end
51
+
52
+ # GET /api/v2/usage/ingestionpolicy/{id}
53
+ # Get a specific ingestion policy
54
+ #
55
+ # @return [Wavefront::Response]
56
+ # @param id [String] ID of the proxy
57
+ # @return [Wavefront::Response]
58
+ #
59
+ def describe(id)
60
+ wf_ingestionpolicy_id?(id)
61
+ api.get(id)
62
+ end
63
+
64
+ # PUT /api/v2/usage/ingestionpolicy/{id}
65
+ # Update a specific ingestion policy
66
+ #
67
+ # @param id [String] a Wavefront alert ID
68
+ # @param body [Hash] key-value hash of the parameters you wish
69
+ # to change
70
+ # @param modify [true, false] if true, use {#describe()} to get
71
+ # a hash describing the existing object, and modify that with
72
+ # the new body. If false, pass the new body straight through.
73
+ # @return [Wavefront::Response]
74
+ #
75
+ def update(id, body, modify = true)
76
+ wf_ingestionpolicy_id?(id)
77
+ raise ArgumentError unless body.is_a?(Hash)
78
+
79
+ return api.put(id, body, 'application/json') unless modify
80
+
81
+ api.put(id, hash_for_update(describe(id).response, body),
82
+ 'application/json')
83
+ end
84
+ end
85
+ end
@@ -5,23 +5,29 @@ require_relative '../defs/constants'
5
5
  module Wavefront
6
6
  module Paginator
7
7
  #
8
- # Automatically handle pagination. This is an abstract class
9
- # made concrete by an extension for each HTTP request type.
8
+ # Automatically handle pagination. This is an abstract class made concrete
9
+ # by an extension for each HTTP request type.
10
10
  #
11
- # This class and its children do slightly unpleasant things with
12
- # the HTTP request passed to us by the user, extracting and
13
- # changing values in the URI, query string, or POST/PUT body.
14
- # The POST class is particularly onerous.
11
+ # This class and its children do slightly unpleasant things with the HTTP
12
+ # request passed to us by the user, extracting and changing values in the
13
+ # URI, query string, or POST/PUT body. The POST class is particularly
14
+ # onerous.
15
15
  #
16
- # Automatic pagination works by letting the user override the
17
- # limit and offset values in API calls. Setting the limit to
18
- # :all iteratively calls the Wavefront API, returning all
19
- # requested objects an a standard Wavefront::Response wrapper;
20
- # setting limit to :lazy returns a lazy Enumerable. The number
21
- # of objects fetched in each API call, whether eager or lazy
22
- # defaults to PAGE_SIZE, but the user can override that value by
23
- # using the offset argument in conjunction with limit = :lazy |
24
- # :all.
16
+ # Automatic pagination works by letting the user override the limit and
17
+ # offset values in API calls.
18
+ #
19
+ # * Calling with limit = :all iteratively calls the Wavefront API,
20
+ # returning all requested objects in a standard Wavefront::Response
21
+ # wrapper.
22
+ #
23
+ # * Calling with limit = :lazy returns a lazy Enumerable.
24
+ #
25
+ # The number of objects fetched in each API call, eager or lazy, defaults
26
+ # to PAGE_SIZE, but the user can override that value by using the offset
27
+ # argument in conjunction with limit = :lazy | :all.
28
+ #
29
+ # So, for example, to fetch all objects in blocks of ten (which could
30
+ # require a lot of API calls) you would use { limit: :all, offset: 10 }
25
31
  #
26
32
  class Base
27
33
  attr_reader :api_caller, :conn, :method, :args, :page_size,
@@ -43,7 +43,6 @@ module Wavefront
43
43
  options[:s] = parse_time(t_start, true)
44
44
  options[:e] = parse_time(t_end, true) if t_end
45
45
 
46
- options.delete_if { |k, v| v == false && k != :i }
47
46
  api.get('api', options)
48
47
  end
49
48
 
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core/api'
4
+ require_relative 'api_mixins/user'
5
+
6
+ module Wavefront
7
+ #
8
+ # Manage and query Wavefront roles
9
+ #
10
+ class Role < CoreApi
11
+ include Wavefront::Mixin::User
12
+
13
+ def update_keys
14
+ %i[id name description]
15
+ end
16
+
17
+ # GET /api/v2/role
18
+ # Get all roles for a customer
19
+ # @param offset [Int] alert at which the list begins
20
+ # @param limit [Int] the number of alerts to return
21
+ # @return [Wavefront::Response]
22
+ #
23
+ def list(offset = 0, limit = 100)
24
+ api.get('', offset: offset, limit: limit)
25
+ end
26
+
27
+ # POST /api/v2/role
28
+ # Create a specific role
29
+ # @param body [Hash] a hash of parameters describing the role. Please
30
+ # refer to the Wavefront Swagger docs for key:value information
31
+ # @return [Wavefront::Response]
32
+ #
33
+ def create(body)
34
+ raise ArgumentError unless body.is_a?(Hash)
35
+
36
+ api.post('', body, 'application/json')
37
+ end
38
+
39
+ # DELETE /api/v2/role/{id}
40
+ # Delete a specific role
41
+ # @param id [String] ID of the role
42
+ # @return [Wavefront::Response]
43
+ #
44
+ def delete(id)
45
+ wf_role_id?(id)
46
+ api.delete(id)
47
+ end
48
+
49
+ # GET /api/v2/role/{id}
50
+ # Get a specific role
51
+ # @param id [String] ID of the role
52
+ # @return [Wavefront::Response]
53
+ #
54
+ def describe(id)
55
+ wf_role_id?(id)
56
+ api.get(id)
57
+ end
58
+
59
+ # PUT /api/v2/role/{id}
60
+ # Update a specific role
61
+ # @param id [String] role ID
62
+ # @param body [Hash] key-value hash of the parameters you wish to change
63
+ # @param modify [true, false] if true, use {#describe()} to get a hash
64
+ # describing the existing object, and modify that with the new body. If
65
+ # false, pass the new body straight through.
66
+ # @return [Wavefront::Response]
67
+ #
68
+ def update(id, body, modify = true)
69
+ wf_role_id?(id)
70
+ raise ArgumentError unless body.is_a?(Hash)
71
+
72
+ return api.put(id, body, 'application/json') unless modify
73
+
74
+ api.put(id, hash_for_update(describe(id).response, body),
75
+ 'application/json')
76
+ end
77
+
78
+ # POST /api/v2/role/{id}/addAssignees
79
+ # Add multiple users and user groups to a specific role
80
+ # @param id [String] role ID
81
+ # @param assignees [Array[String]] list of roles or accounts to be added
82
+ # @return [Wavefront::Response]
83
+ #
84
+ def add_assignees(id, assignees)
85
+ wf_role_id?(id)
86
+ validate_user_list(assignees)
87
+ api.post([id, 'addAssignees'].uri_concat, assignees, 'application/json')
88
+ end
89
+
90
+ # POST /api/v2/role/{id}/removeAssignees
91
+ # Remove multiple users and user groups from a specific role
92
+ # @param id [String] role ID
93
+ # @param assignees [Array[String]] list of roles or accounts to be removed
94
+ # @return [Wavefront::Response]
95
+ #
96
+ def remove_assignees(id, assignees)
97
+ wf_role_id?(id)
98
+ validate_user_list(assignees)
99
+ api.post([id, 'removeAssignees'].uri_concat,
100
+ assignees,
101
+ 'application/json')
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'defs/constants'
4
+ require_relative 'core/api'
5
+
6
+ module Wavefront
7
+ #
8
+ # Spy on data going into Wavefront
9
+ #
10
+ class Spy < CoreApi
11
+ # GET /api/spy/points
12
+ # Gets new metric data points that are added to existing time series.
13
+ # @param sampling [Float] the amount of points to sample, from 0
14
+ # (none) to 1 (all)
15
+ # @param filter [Hash] with the following keys:
16
+ # :prefix [String] only list points whose metric name begins with this
17
+ # case-sensitive string
18
+ # :host [Array] only list points if source name begins with this
19
+ # case-sensitive string
20
+ # :tag_key [String,Array[String]] only list points with one or more of
21
+ # the given points tags
22
+ # @param options [Hash] with the following keys
23
+ # :timestamp [Boolean] prefix each block of streamed data with a
24
+ # timestamp
25
+ # :timeout [Integer] how many seconds to run the spy. After this time
26
+ # the method returns
27
+ # @raise Wavefront::Exception::InvalidSamplingValue
28
+ # @return [Nil]
29
+ #
30
+ def points(sampling = 0.01, filters = {}, options = {})
31
+ wf_sampling_value?(sampling)
32
+ api.get_stream('points', points_filter(sampling, filters), options)
33
+ end
34
+
35
+ # GET /api/spy/histograms
36
+ # Gets new histograms that are added to existing time series.
37
+ # @param sampling [Float] see #points
38
+ # @param filter [Hash] see #points
39
+ # @param options [Hash] see #points
40
+ # @raise Wavefront::Exception::InvalidSamplingValue
41
+ # @return [Nil]
42
+ #
43
+ def histograms(sampling = 0.01, filters = {}, options = {})
44
+ wf_sampling_value?(sampling)
45
+ api.get_stream('histograms',
46
+ histograms_filter(sampling, filters),
47
+ options)
48
+ end
49
+
50
+ # GET /api/spy/spans
51
+ # Gets new spans with existing source names and span tags.
52
+ # @param sampling [Float] see #points
53
+ # @param filter [Hash] see #points
54
+ # @param options [Hash] see #points
55
+ # @raise Wavefront::Exception::InvalidSamplingValue
56
+ # @return [Nil]
57
+ #
58
+ def spans(sampling = 0.01, filters = {}, options = {})
59
+ wf_sampling_value?(sampling)
60
+ api.get_stream('spans', spans_filter(sampling, filters), options)
61
+ end
62
+
63
+ # GET /api/spy/ids
64
+ # Gets newly allocated IDs that correspond to new metric names, source
65
+ # names, point tags, or span tags. A new ID generally indicates that a
66
+ # new time series has been introduced.
67
+ # @param sampling [Float] see #points
68
+ # @param filter [Hash] with keys:
69
+ # :prefix [String] only list assignments whose metric name begins with
70
+ # this case-sensitive string
71
+ # :type [String] one of METRIC, SPAN, HOST or STRING
72
+ # @param options [Hash] see #points
73
+ #
74
+ def ids(sampling = 0.01, filters = {}, options = {})
75
+ wf_sampling_value?(sampling)
76
+ api.get_stream('ids', ids_filter(sampling, filters), options)
77
+ end
78
+
79
+ def api_path
80
+ '/api/spy'
81
+ end
82
+
83
+ # We have to try to make the response we get from the API look
84
+ # like the one we get from the public API. To begin with, it's
85
+ # nothing like it.
86
+ #
87
+ # This method must be public because a #respond_to? looks for
88
+ # it.
89
+ #
90
+ def _response_shim(resp, status)
91
+ { response: parse_response(resp),
92
+ status: { result: status == 200 ? 'OK' : 'ERROR',
93
+ message: extract_api_message(status, resp),
94
+ code: status } }.to_json
95
+ end
96
+
97
+ private
98
+
99
+ def points_filter(sampling, filters)
100
+ { metric: filters.fetch(:prefix, nil),
101
+ host: filters.fetch(:host, nil),
102
+ sampling: sampling,
103
+ pointTagKey: filters.fetch(:tag_key, nil) }.compact
104
+ end
105
+
106
+ def histograms_filter(sampling, filters)
107
+ { histogram: filters.fetch(:prefix, nil),
108
+ host: filters.fetch(:host, nil),
109
+ sampling: sampling,
110
+ histogramTagKey: filters.fetch(:tag_key, nil) }.compact
111
+ end
112
+
113
+ def spans_filter(sampling, filters)
114
+ { name: filters.fetch(:prefix, nil),
115
+ host: filters.fetch(:host, nil),
116
+ sampling: sampling,
117
+ spanTagKey: filters.fetch(:tag_key, nil) }.compact
118
+ end
119
+
120
+ def ids_filter(sampling, filters)
121
+ { name: filters.fetch(:prefix, nil),
122
+ type: filters.fetch(:type, nil),
123
+ sampling: sampling }.compact
124
+ end
125
+ end
126
+ end