wavefront-sdk 4.0.0 → 5.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -1
  3. data/HISTORY.md +28 -0
  4. data/README.md +3 -2
  5. data/lib/wavefront-sdk/account.rb +104 -3
  6. data/lib/wavefront-sdk/api_mixins/user.rb +10 -0
  7. data/lib/wavefront-sdk/cloudintegration.rb +27 -0
  8. data/lib/wavefront-sdk/core/exception.rb +3 -0
  9. data/lib/wavefront-sdk/credentials.rb +28 -9
  10. data/lib/wavefront-sdk/defs/version.rb +3 -1
  11. data/lib/wavefront-sdk/distribution.rb +2 -0
  12. data/lib/wavefront-sdk/paginator/base.rb +21 -15
  13. data/lib/wavefront-sdk/query.rb +0 -1
  14. data/lib/wavefront-sdk/role.rb +128 -0
  15. data/lib/wavefront-sdk/spy.rb +126 -0
  16. data/lib/wavefront-sdk/stdlib/array.rb +1 -1
  17. data/lib/wavefront-sdk/user.rb +31 -0
  18. data/lib/wavefront-sdk/usergroup.rb +17 -16
  19. data/lib/wavefront-sdk/validators.rb +31 -7
  20. data/lib/wavefront-sdk/writers/core.rb +2 -2
  21. data/spec/.rubocop.yml +13 -3
  22. data/spec/support/minitest_assertions.rb +5 -11
  23. data/spec/wavefront-sdk/account_spec.rb +107 -1
  24. data/spec/wavefront-sdk/cloudintegration_spec.rb +38 -0
  25. data/spec/wavefront-sdk/core/logger_spec.rb +3 -3
  26. data/spec/wavefront-sdk/credentials_spec.rb +5 -4
  27. data/spec/wavefront-sdk/metric_helper_spec.rb +1 -1
  28. data/spec/wavefront-sdk/role_spec.rb +96 -0
  29. data/spec/wavefront-sdk/{unstable/spy_spec.rb → spy_spec.rb} +3 -3
  30. data/spec/wavefront-sdk/user_spec.rb +8 -0
  31. data/spec/wavefront-sdk/usergroup_spec.rb +21 -11
  32. data/spec/wavefront-sdk/validators_spec.rb +16 -0
  33. data/spec/wavefront-sdk/writers/core_spec.rb +1 -1
  34. data/wavefront-sdk.gemspec +6 -6
  35. metadata +23 -24
  36. data/lib/wavefront-sdk/monitoredcluster.rb +0 -93
  37. data/lib/wavefront-sdk/unstable/spy.rb +0 -134
  38. data/spec/wavefront-sdk/monitoredcluster_spec.rb +0 -55
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 578159c0ff0dac09b94caa4dac4e37f03436cb3e2b4cdda54886ce91b9ce4c9b
4
- data.tar.gz: 93eac0630e97d53e3b26d222fd09df61f8cd7966c62e39520dbf27525982cb1b
3
+ metadata.gz: 5e623522f872c4204c1e723ee5a399c513251933e64445f6f5b65ff66d2fbffc
4
+ data.tar.gz: 6f55f44177169c24d4c0aa89490464097a6fe2278e24a1e3b83e5f7520757dfd
5
5
  SHA512:
6
- metadata.gz: e3f09ae6d818b0acc7e81e2e4da585c10a59efba18a4ab373679783d62b5a5ccc300e14435b7cbe8a0f2d64adec361e080c55f089732ffbb6c171f619524440e
7
- data.tar.gz: d558a6d0eafe0d09120317113758ee70807cae035a7e9677a22cf643e537103ff66d671e7e6a031151cd03572f6b62a78e9aa92a86a2fb052f0cb31ba92c4f17
6
+ metadata.gz: 58bb998cc92b36c369497631f766af8844a692fbd10508962ee7bd30f4fbe65d222bc512d06b63feb19ca94cdcc4e8429b8c61f6227e4ff1041cd6ffff141758
7
+ data.tar.gz: 198dfadd7561b5c3772211716d23f9653ace3bbf472e41d12fa8b64d744ed16567bb11796ef3aff252054bc0945200ea71e454b5989824656c178e74503b4675
@@ -1,7 +1,18 @@
1
1
  ---
2
2
 
3
3
  AllCops:
4
- TargetRubyVersion: 2.3
4
+ TargetRubyVersion: 2.4
5
+ NewCops: enable
5
6
 
6
7
  Metrics/ClassLength:
7
8
  Max: 150
9
+
10
+ # Is nothing sacred?
11
+ Layout/LineLength:
12
+ Max: 80
13
+
14
+ Style/StringConcatenation:
15
+ Enabled: false
16
+
17
+ Style/OptionalBooleanParameter:
18
+ Enabled: false
data/HISTORY.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.2.1 (2020-09-18)
4
+ * Remove necessity for user to `require 'pathname'`
5
+
6
+ ## 5.2.0 (2020-09-03)
7
+ * Add `:raise_on_no_profile` option to `Wavefront::Credentials` constructor
8
+ options. If this is true and a specific config stanza is requested but not
9
+ found, `Wavefront::Exception::MissingConfigProfile` is thrown.
10
+
11
+ ## 5.1.0 (2020-08-15)
12
+ * Add `create_aws_external_id`, `delete_aws_external_id`, and
13
+ `confirm_aws_external_id` methods to `Wavefront::CloudIntegration`.
14
+
15
+ ## 5.0.1 (2020-07-08)
16
+ * Reinstate `Wavefront::Role#grant` and `Wavefront::Role#revoke`, which were
17
+ accidentally removed prior to release of 5.0.0.
18
+
19
+ ## 5.0.0 (2020-07-08)
20
+ * Remove `Wavefront::UserGroup#grant` and `Wavefront::UserGroup#revoke` as [the
21
+ API paths they used have been
22
+ removed](https://docs.wavefront.com/2020.06.x_release_notes.html#obsolete-and-deprecated-apis).
23
+ (Breaking change.)
24
+ * Remove `Wavefront::MonitoredCluster` class, as it has been removed from the
25
+ public API.
26
+ * Deprecate `Wavefront::User` class, as [the user API is now
27
+ deprecated](https://docs.wavefront.com/2020.06.x_release_notes.html#obsolete-and-deprecated-apis)
28
+ * Add `Wavefront::Role` class, for managing roles.
29
+ * Promote `Wavefront::Spy` class from unstable. It is now an official API.
30
+
3
31
  ## 4.0.0 (2020-02-17)
4
32
  * Drop support for Ruby 2.3. (Breaking change.)
5
33
  * Add `Wavefront::MonitoredCluster` class.
data/README.md CHANGED
@@ -100,10 +100,11 @@ when you ask for a list of objects.
100
100
 
101
101
  You can set an offset and a limit when you list, but setting the
102
102
  limit to the magic value `:all` will return all items, without you
103
- having to deal with pagination.
103
+ having to deal with pagination. When you do that, `offset` is repurposed as
104
+ the number of results fetched with each call to the API.
104
105
 
105
106
  Calling a method with the limit set to `:lazy` returns a lazy
106
- enumerable.
107
+ enumerable. Again, `offset` is the chunk size.
107
108
 
108
109
  ```ruby
109
110
  wf = Wavefront::Alert.new(creds.all)
@@ -44,6 +44,18 @@ module Wavefront
44
44
  api.get(id)
45
45
  end
46
46
 
47
+ # POST /api/v2/account/{id}/addRoles
48
+ # Add specific roles to the account (user or service account)
49
+ # @param id [String] ID of the account
50
+ # @param role_list [Array[String]] list of roles to add
51
+ # @return [Wavefront::Response]
52
+ #
53
+ def add_roles(id, role_list)
54
+ wf_account_id?(id)
55
+ validate_role_list(role_list)
56
+ api.post([id, 'addRoles'].uri_concat, role_list, 'application/json')
57
+ end
58
+
47
59
  # POST /api/v2/account/{id}/addUserGroups
48
60
  # Adds specific user groups to the account (user or service account)
49
61
  # @param id [String] ID of the account
@@ -68,10 +80,22 @@ module Wavefront
68
80
  api.get([id, 'businessFunctions'].uri_concat)
69
81
  end
70
82
 
83
+ # POST /api/v2/account/{id}/removeRoles
84
+ # Removes specific roles from the account (user or service account)
85
+ # @param id [String] ID of the account
86
+ # @param role_list [Array[String]] list of roles to remove
87
+ # @return [Wavefront::Response]
88
+ #
89
+ def remove_roles(id, role_list)
90
+ wf_account_id?(id)
91
+ validate_role_list(role_list)
92
+ api.post([id, 'removeRoles'].uri_concat, role_list, 'application/json')
93
+ end
94
+
71
95
  # POST /api/v2/account/{id}/removeUserGroups
72
96
  # Removes specific user groups from the account (user or service account)
73
97
  # @param id [String] ID of the account
74
- # @param group_list [Array[String]] list of groups to add
98
+ # @param group_list [Array[String]] list of groups to remove
75
99
  # @return [Wavefront::Response]
76
100
  #
77
101
  def remove_user_groups(id, group_list)
@@ -132,7 +156,8 @@ module Wavefront
132
156
  end
133
157
 
134
158
  # POST /api/v2/account/removeingestionpolicies
135
- # Removes ingestion policies from multiple accounts
159
+ # Removes ingestion policies from multiple accounts. The API path says
160
+ # "policies" but I've made the method name "policy" for consistency.
136
161
  # @param policy_id [String] ID of the ingestion policy
137
162
  # @param id_list [Array[String]] list of accounts to be put in policy
138
163
  # @return [Wavefront::Response]
@@ -140,7 +165,7 @@ module Wavefront
140
165
  def remove_ingestion_policy(policy_id, id_list)
141
166
  wf_ingestionpolicy_id?(policy_id)
142
167
  validate_account_list(id_list)
143
- api.post('removeingestionpolicy',
168
+ api.post('removeingestionpolicies',
144
169
  { ingestionPolicyId: policy_id,
145
170
  accounts: id_list },
146
171
  'application/json')
@@ -157,6 +182,78 @@ module Wavefront
157
182
  api.post('deleteAccounts', id_list, 'application/json')
158
183
  end
159
184
 
185
+ # GET /api/v2/account/user
186
+ # Get all user accounts
187
+ # @param offset [Int] user account at which the list begins
188
+ # @param limit [Int] the number of user accounts to return
189
+ # @return [Wavefront::Response]
190
+ #
191
+ def user_list(offset = 0, limit = 100)
192
+ api.get('user', offset: offset, limit: limit)
193
+ end
194
+
195
+ def user_create(body, send_email = false)
196
+ raise ArgumentError unless body.is_a?(Hash)
197
+
198
+ uri = send_email ? "?sendEmail=#{send_email}" : 'user'
199
+
200
+ api.post(uri, body, 'application/json')
201
+ end
202
+
203
+ # POST /api/v2/account/user
204
+ # Creates or updates a user account
205
+ # @param id [String] a Wavefront user ID
206
+ # @param body [Hash] key-value hash of the parameters you wish to change
207
+ # @param modify [true, false] if true, use {#describe()} to get a hash
208
+ # describing the existing object, and modify that with the new body. If
209
+ # false, pass the new body straight through.
210
+ # @return [Wavefront::Response]
211
+ def user_update(body, modify = true)
212
+ raise ArgumentError unless body.is_a?(Hash)
213
+
214
+ return api.post('user', body, 'application/json') unless modify
215
+
216
+ api.post('user',
217
+ hash_for_update(describe(id).response, body),
218
+ 'application/json')
219
+ end
220
+
221
+ # GET /api/v2/account/user/{id}
222
+ # Retrieves a user by identifier (email address)
223
+ # @param id [String] ID of the proxy
224
+ # @return [Wavefront::Response]
225
+ #
226
+ def user_describe(id)
227
+ wf_user_id?(id)
228
+ api.get(['user', id].uri_concat)
229
+ end
230
+
231
+ # POST /api/v2/account/user/invite
232
+ # Invite user accounts with given user groups and permissions.
233
+ # @param body [Array[Hash]] array of hashes, each hash describing a user.
234
+ # See API docs for more details. It is your responsibility to validate
235
+ # the data which describes each user.
236
+ # @return [Wavefront::Response]
237
+ #
238
+ def user_invite(body)
239
+ raise ArgumentError unless body.is_a?(Array)
240
+ raise ArgumentError unless body.first.is_a?(Hash)
241
+
242
+ api.post('user/invite', body, 'application/json')
243
+ end
244
+
245
+ # POST /api/v2/account/validateAccounts
246
+ # Returns valid accounts (users and service accounts), also invalid
247
+ # identifiers from the given list
248
+ # @param id_list [Array[String]] list of user IDs
249
+ # @return [Wavefront::Response]
250
+ #
251
+ def validate_accounts(id_list)
252
+ raise ArgumentError unless id_list.is_a?(Array)
253
+
254
+ api.post('validateAccounts', id_list, 'application/json')
255
+ end
256
+
160
257
  private
161
258
 
162
259
  # @param id [String] ID of the user
@@ -198,5 +295,9 @@ module Wavefront
198
295
  wf_permission?(permission)
199
296
  api.post(['revoke', permission].uri_concat, id_list, 'application/json')
200
297
  end
298
+
299
+ def update_keys
300
+ %i[identifier groups userGroups roles ingestionPolicyId]
301
+ end
201
302
  end
202
303
  end
@@ -35,6 +35,16 @@ module Wavefront
35
35
 
36
36
  list.each { |id| wf_account_id?(id) }
37
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
38
48
  end
39
49
  end
40
50
  end
@@ -101,5 +101,32 @@ module Wavefront
101
101
  wf_cloudintegration_id?(id)
102
102
  api.post([id, 'undelete'].uri_concat)
103
103
  end
104
+
105
+ # POST /api/v2/cloudintegration/awsExternalIdCreate an external id
106
+ # @return [Wavefront::Response]
107
+ #
108
+ def create_aws_external_id
109
+ api.post('awsExternalId', nil, 'application/json')
110
+ end
111
+
112
+ # DELETE /api/v2/cloudintegration/awsExternalId/{id}DELETEs an external id
113
+ # that was created by Wavefront
114
+ # @param id [String] AWS external ID
115
+ # @return [Wavefront::Response]
116
+ #
117
+ def delete_aws_external_id(external_id)
118
+ wf_aws_external_id?(external_id)
119
+ api.delete(['awsExternalId', external_id].uri_concat)
120
+ end
121
+
122
+ # GET /api/v2/cloudintegration/awsExternalId/{id}GETs (confirms) a valid
123
+ # external id that was created by Wavefront
124
+ # @param id [String] AWS external ID
125
+ # @return [Wavefront::Response]
126
+ #
127
+ def confirm_aws_external_id(external_id)
128
+ wf_aws_external_id?(external_id)
129
+ api.get(['awsExternalId', external_id].uri_concat)
130
+ end
104
131
  end
105
132
  end
@@ -12,6 +12,7 @@ module Wavefront
12
12
  class InvalidAlertId < RuntimeError; end
13
13
  class InvalidAlertSeverity < RuntimeError; end
14
14
  class InvalidApiTokenId < RuntimeError; end
15
+ class InvalidAwsExternalId < RuntimeError; end
15
16
  class InvalidConfigFile < RuntimeError; end
16
17
  class InvalidCloudIntegrationId < RuntimeError; end
17
18
  class InvalidDashboardId < RuntimeError; end
@@ -38,6 +39,7 @@ module Wavefront
38
39
  class InvalidPoint < RuntimeError; end
39
40
  class InvalidPrefixLength < RuntimeError; end
40
41
  class InvalidProxyId < RuntimeError; end
42
+ class InvalidRoleId < RuntimeError; end
41
43
  class InvalidRelativeTime < RuntimeError; end
42
44
  class InvalidSamplingValue < RuntimeError; end
43
45
  class InvalidSavedSearchEntity < RuntimeError; end
@@ -53,6 +55,7 @@ module Wavefront
53
55
  class InvalidUserGroupId < RuntimeError; end
54
56
  class InvalidVersion < RuntimeError; end
55
57
  class InvalidWebhookId < RuntimeError; end
58
+ class MissingConfigProfile < RuntimeError; end
56
59
  class NetworkTimeout < RuntimeError; end
57
60
  class NotImplemented < RuntimeError; end
58
61
  class SocketError < RuntimeError; end
@@ -31,11 +31,16 @@ module Wavefront
31
31
  # @param options [Hash] keys may be 'file', which
32
32
  # specifies a config file which will be loaded and parsed. If
33
33
  # no file is supplied, those listed above will be used.;
34
- # and/or 'profile' which select a profile section from 'file'
34
+ # and/or 'profile' which select a profile section from 'file'.
35
+ # Specify the key :raise_noprofile to have an exception thrown
36
+ # if a given profile cannot be found. Otherwise that is ignored and
37
+ # options are built from other sources.
35
38
  #
36
39
  def initialize(options = {})
37
- raw = load_from_file(cred_files(options), options[:profile] ||
38
- 'default')
40
+ @raise_noprofile = options[:raise_on_no_profile] || false
41
+ raw = load_from_file(real_files(cred_files(options)),
42
+ options[:profile] || 'default')
43
+
39
44
  populate(env_override(raw))
40
45
  end
41
46
 
@@ -80,31 +85,45 @@ module Wavefront
80
85
  end
81
86
  end
82
87
 
88
+ def real_files(files)
89
+ files.select { |f| f.exist? && f.file? }
90
+ end
91
+
83
92
  # @param files [Array][Pathname] a list of ini-style config files
84
93
  # @param profile [String] a profile name
94
+ # @param disallow_missing [Bool] whether or not to raise an exception if
95
+ # we are given a profile but can't find it.
85
96
  # @return [Hash] the given profile from the given list of files.
86
97
  # If multiple files match, the last one will be used
87
98
  #
88
99
  def load_from_file(files, profile = 'default')
89
100
  ret = {}
101
+ profiles_found = conf_files_found = 0
90
102
 
91
103
  files.each do |f|
92
- next unless f.exist? && f.file?
93
-
94
104
  ret = load_profile(f, profile)
105
+ conf_files_found += 1
106
+ profiles_found += 1 unless ret.empty?
95
107
  ret[:file] = f
96
108
  end
97
109
 
110
+ raise_on_missing_profile(profile, profiles_found, conf_files_found)
98
111
  ret
99
112
  end
100
113
 
101
- # Load in an (optionally) given section of an ini-style
102
- # configuration file not there, we don't consider that an error.
114
+ def raise_on_missing_profile(profile, profiles_found, conf_files_found)
115
+ return true unless @raise_noprofile
116
+ return true unless profiles_found.zero? && conf_files_found.positive?
117
+
118
+ raise Wavefront::Exception::MissingConfigProfile, profile
119
+ end
120
+
121
+ # Load in an (optionally) given section of an ini-style configuration
122
+ # file. If the section is not there, we don't consider that an error.
103
123
  #
104
124
  # @param file [Pathname] the file to read
105
125
  # @param profile [String] the section in the config to read
106
- # @return [Hash] options loaded from file. Each key becomes a
107
- # symbol
126
+ # @return [Hash] options loaded from file. Each key becomes a symbol
108
127
  #
109
128
  def load_profile(file, profile = 'default')
110
129
  IniFile.load(file)[profile].each_with_object({}) do |(k, v), memo|
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- WF_SDK_VERSION = '4.0.0'
3
+ require 'pathname'
4
+
5
+ WF_SDK_VERSION = '5.2.1'
4
6
  WF_SDK_LOCATION = Pathname.new(__dir__).parent.parent.parent
@@ -65,6 +65,7 @@ module Wavefront
65
65
  raise Wavefront::Exception::InvalidDistribution
66
66
  end
67
67
 
68
+ # rubocop:disable Metrics/AbcSize
68
69
  def dist_hash(dist)
69
70
  dist.dup.tap do |d|
70
71
  d[:interval] = distribution_interval(dist)
@@ -75,6 +76,7 @@ module Wavefront
75
76
  d[:opttags] = tags_or_nothing(opts[:tags])
76
77
  end
77
78
  end
79
+ # rubocop:enable Metrics/AbcSize
78
80
 
79
81
  def distribution_timestamp(dist)
80
82
  parse_time(dist.fetch(:ts, Time.now))
@@ -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,