wavefront-cli 3.2.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8904f27867cfabaaaf9c47132f52a439fa5fdef900745986789c901a0f61ef0
4
- data.tar.gz: f30e315802c139c36f53c7d9e462e7b684bac5ab0ddebbead90210edbe317385
3
+ metadata.gz: 0cbeefd7b752bca0b1a5a3b886219b8ff0df8d6407ac1c3589e21f04faa1df7e
4
+ data.tar.gz: 36bbb2c1753d13d5911f91c0959af065d250e2d582b503d5731bd067ea1eebc9
5
5
  SHA512:
6
- metadata.gz: 9fbc2243bf64547d80d6082c1e0c6308dfd1327d112b67897afaf98b715841aed1323bf2aa3394656eb357c17404354c80e315e26d641c30289a2466fc0dce93
7
- data.tar.gz: 21808c3a636e72bbcf056fad0572e96d313f2e87823e9c04d88fc5934ac308686f1cb1f7a96306cef81ae61a4a65453228265a1d4eff6636a46d9081485ef6e2
6
+ metadata.gz: c9a1cb2a6a26f242fb5a992497e4346d4844395d34c74958c03575116a6cdcbe712a4364bb6bf7a23fd8bb73d356b3905e4aa722305e3df228cdf84d28bf4613
7
+ data.tar.gz: 4f48260461f4222092f383dbd175d90ce4e0a1c58b7ff78260035b106abd14e86e71a6cf98f3d4e3b34bcefd7532db938f8c770fcdbf506f78ef3ca524a9daea
data/HISTORY.md CHANGED
@@ -1,7 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.2.1 (09/05/2019)
4
+ * Fix for new API ACL format.
5
+ * Require 3.3.0 of [the SDK](https://github.com/snltd/wavefront-sdk).
6
+
3
7
  ## 3.2.0 (30/04/2019)
4
8
  * New `apitoken` command lets you manage your own API tokens.
9
+ * Support for alert ACLs.
5
10
  * Require 3.2.0 of [the SDK](https://github.com/snltd/wavefront-sdk).
6
11
 
7
12
  ## 3.1.4 (02/05/2019)
@@ -1,13 +1,18 @@
1
1
  require_relative 'base'
2
+ require_relative 'command_mixins/tag'
3
+ require_relative 'command_mixins/acl'
2
4
 
3
5
  module WavefrontCli
4
6
  #
5
7
  # CLI coverage for the v2 'alert' API.
6
8
  #
7
9
  class Alert < WavefrontCli::Base
10
+ include WavefrontCli::Mixin::Tag
11
+ include WavefrontCli::Mixin::Acl
12
+
8
13
  def import_fields
9
14
  %w[name condition minutes target severity displayExpression
10
- tags additionalInformation]
15
+ tags additionalInformation resolveAfterMinutes]
11
16
  end
12
17
 
13
18
  def do_describe
@@ -22,25 +27,22 @@ module WavefrontCli
22
27
  wf.unsnooze(options[:'<id>'])
23
28
  end
24
29
 
25
- # rubocop:disable Metrics/AbcSize
26
30
  def do_delete
27
- cannot_noop!
28
-
29
- word = if wf.describe(options[:'<id>']).status.code == 200
30
- 'Soft'
31
- else
32
- 'Permanently'
33
- end
31
+ smart_delete
32
+ end
34
33
 
35
- puts "#{word} deleting alert '#{options[:'<id>']}'."
36
- wf.delete(options[:'<id>'])
34
+ def do_clone
35
+ wf.clone(options[:'<id>'], options[:version]&.to_i)
37
36
  end
38
- # rubocop:enable Metrics/AbcSize
39
37
 
40
38
  def do_summary
41
39
  wf.summary
42
40
  end
43
41
 
42
+ def do_latest
43
+ wf.versions(options[:'<id>'])
44
+ end
45
+
44
46
  def do_history
45
47
  wf.history(options[:'<id>'], options[:offset], options[:limit])
46
48
  end
@@ -394,6 +394,21 @@ module WavefrontCli
394
394
  wf.delete(options[:'<id>'])
395
395
  end
396
396
 
397
+ # Some objects support soft deleting. To handle that, call this
398
+ # method from do_delete
399
+ #
400
+ def smart_delete(object_type = klass_word)
401
+ cannot_noop!
402
+ puts smart_delete_message(object_type)
403
+ wf.delete(options[:'<id>'])
404
+ end
405
+
406
+ def smart_delete_message(object_type)
407
+ desc = wf.describe(options[:'<id>'])
408
+ word = desc.ok? ? 'Soft' : 'Permanently'
409
+ format("%s deleting %s '%s'", word, object_type, options[:'<id>'])
410
+ end
411
+
397
412
  def do_undelete
398
413
  wf.undelete(options[:'<id>'])
399
414
  end
@@ -445,26 +460,6 @@ module WavefrontCli
445
460
  end
446
461
  end
447
462
 
448
- def do_tags
449
- wf.tags(options[:'<id>'])
450
- end
451
-
452
- def do_tag_add
453
- wf.tag_add(options[:'<id>'], options[:'<tag>'].first)
454
- end
455
-
456
- def do_tag_delete
457
- wf.tag_delete(options[:'<id>'], options[:'<tag>'].first)
458
- end
459
-
460
- def do_tag_set
461
- wf.tag_set(options[:'<id>'], options[:'<tag>'])
462
- end
463
-
464
- def do_tag_clear
465
- wf.tag_set(options[:'<id>'], [])
466
- end
467
-
468
463
  # Most things will re-import with the POST method if you remove
469
464
  # the ID.
470
465
  #
@@ -0,0 +1,84 @@
1
+ module WavefrontCli
2
+ module Mixin
3
+ #
4
+ # Standard ACL commands. Mix this module in to get ACL support.
5
+ #
6
+ module Acl
7
+ def do_acls
8
+ wf.acls([options[:'<id>']])
9
+ end
10
+
11
+ def do_acl_clear
12
+ wf.acl_set(options[:'<id>'], [], [everyone_id])
13
+ do_acls
14
+ end
15
+
16
+ def do_acl_grant
17
+ return grant_view if options[:view]
18
+ return grant_modify if options[:modify]
19
+
20
+ raise WavefrontCli::Exception::InsufficientData
21
+ end
22
+
23
+ def do_acl_revoke
24
+ return revoke_view if options[:view]
25
+ return revoke_modify if options[:modify]
26
+
27
+ raise WavefrontCli::Exception::InsufficientData
28
+ end
29
+
30
+ # @return [String] UUID of 'Everyone' group
31
+ # @raise WavefrontCli::Exception::UserGroupNotFound if group
32
+ # does not exist. This is caught in the controller.
33
+ #
34
+ def everyone_id
35
+ require 'wavefront-sdk/search'
36
+ wfs = Wavefront::Search.new(mk_creds, mk_opts)
37
+ query = conds_to_query(['name=Everyone'])
38
+ wfs.search(:usergroup, query).response.items.first.id
39
+ rescue RuntimeError
40
+ raise WavefrontCli::Exception::UserGroupNotFound, 'Everyone'
41
+ end
42
+
43
+ def grant_modify
44
+ wf.acl_add(options[:'<id>'], [], options[:'<name>'])
45
+ do_acls
46
+ end
47
+
48
+ def grant_view
49
+ wf.acl_add(options[:'<id>'], options[:'<name>'], [])
50
+ do_acls
51
+ end
52
+
53
+ def revoke_view
54
+ wf.acl_delete(options[:'<id>'], options[:'<name>'], [])
55
+ do_acls
56
+ end
57
+
58
+ def revoke_modify
59
+ wf.acl_delete(options[:'<id>'], [], options[:'<name>'])
60
+ do_acls
61
+ end
62
+
63
+ # @param action [Symbol] :grant_to or :revoke_from
64
+ # @return [Wavefront::Response]
65
+ #
66
+ def _acl_action(action)
67
+ entity_type, entities = acl_entities
68
+
69
+ resp = send(format('%s_%s', action, entity_type),
70
+ options[:'<id>'],
71
+ entities)
72
+
73
+ print_status(resp.status)
74
+ do_acls
75
+ end
76
+
77
+ def print_status(status)
78
+ puts status.message unless status.message.empty?
79
+ rescue NoMethodError
80
+ nil
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,28 @@
1
+ module WavefrontCli
2
+ module Mixin
3
+ #
4
+ # Standard tag commands
5
+ #
6
+ module Tag
7
+ def do_tags
8
+ wf.tags(options[:'<id>'])
9
+ end
10
+
11
+ def do_tag_add
12
+ wf.tag_add(options[:'<id>'], options[:'<tag>'].first)
13
+ end
14
+
15
+ def do_tag_delete
16
+ wf.tag_delete(options[:'<id>'], options[:'<tag>'].first)
17
+ end
18
+
19
+ def do_tag_set
20
+ wf.tag_set(options[:'<id>'], options[:'<tag>'])
21
+ end
22
+
23
+ def do_tag_clear
24
+ wf.tag_set(options[:'<id>'], [])
25
+ end
26
+ end
27
+ end
28
+ end
@@ -13,8 +13,11 @@ class WavefrontCommandAlert < WavefrontCommandBase
13
13
  "snoozed #{CMN} [-o offset] [-L limit]",
14
14
  "describe #{CMN} [-v version] <id>",
15
15
  "delete #{CMN} <id>",
16
+ "clone #{CMN} [-v version] <id>",
16
17
  "undelete #{CMN} <id>",
17
18
  "history #{CMN} [-o offset] [-L limit] <id>",
19
+ "clone #{CMN} [-v version] <id>",
20
+ "latest #{CMN} <id>",
18
21
  "import #{CMN} <file>",
19
22
  "snooze #{CMN} [-T time] <id>",
20
23
  "update #{CMN} <key=value> <id>",
@@ -29,6 +32,7 @@ class WavefrontCommandAlert < WavefrontCommandBase
29
32
  "queries #{CMN} [-b] [<id>]",
30
33
  "install #{CMN} <id>",
31
34
  "uninstall #{CMN} <id>",
35
+ acl_commands,
32
36
  "summary #{CMN} [-a]"]
33
37
  end
34
38
 
@@ -50,10 +50,8 @@ class WavefrontCommandBase
50
50
  def acl_commands
51
51
  ["acls #{CMN} <id>",
52
52
  "acl #{CMN} clear <id>",
53
- "acl #{CMN} grant (view | modify) on <id> to " \
54
- '(user | group) <name>...',
55
- "acl #{CMN} revoke (view | modify) on <id> from " \
56
- '(user | group) <name>...']
53
+ "acl #{CMN} grant (view | modify) on <id> to <name>...",
54
+ "acl #{CMN} revoke (view | modify) on <id> from <name>..."]
57
55
  end
58
56
 
59
57
  # Inheriting classes must override this method
@@ -142,8 +142,7 @@ class WavefrontCliController
142
142
  abort "Unsupported writer '#{e.message}'."
143
143
  rescue StandardError => e
144
144
  warn "general error: #{e}"
145
- warn "re-run with '-D' for stack trace." unless opts[:debug]
146
- warn "Backtrace:\n\t#{e.backtrace.join("\n\t")}" if opts[:debug]
145
+ warn "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
147
146
  abort
148
147
  end
149
148
  # rubocop:enable Metrics/MethodLength
@@ -1,10 +1,15 @@
1
1
  require_relative 'base'
2
+ require_relative 'command_mixins/tag'
3
+ require_relative 'command_mixins/acl'
2
4
 
3
5
  module WavefrontCli
4
6
  #
5
7
  # CLI coverage for the v2 'dashboard' API.
6
8
  #
7
9
  class Dashboard < WavefrontCli::Base
10
+ include WavefrontCli::Mixin::Tag
11
+ include WavefrontCli::Mixin::Acl
12
+
8
13
  def list_filter(list)
9
14
  return list unless options[:nosystem]
10
15
  list.tap { |l| l.response.items.delete_if { |d| d[:systemOwned] } }
@@ -14,20 +19,9 @@ module WavefrontCli
14
19
  wf.describe(options[:'<id>'], options[:version])
15
20
  end
16
21
 
17
- # rubocop:disable Metrics/AbcSize
18
22
  def do_delete
19
- cannot_noop!
20
-
21
- word = if wf.describe(options[:'<id>']).status.code == 200
22
- 'Soft'
23
- else
24
- 'Permanently'
25
- end
26
-
27
- puts "#{word} deleting dashboard '#{options[:'<id>']}'."
28
- wf.delete(options[:'<id>'])
23
+ smart_delete
29
24
  end
30
- # rubocop:enable Metrics/AbcSize
31
25
 
32
26
  def do_history
33
27
  wf.history(options[:'<id>'])
@@ -59,136 +53,5 @@ module WavefrontCli
59
53
  wf.unfavorite(options[:'<id>'])
60
54
  do_favs
61
55
  end
62
-
63
- def do_acls
64
- wf.acls([options[:'<id>']])
65
- end
66
-
67
- def do_acl_clear
68
- wf.acl_set(options[:'<id>'], [], [id: everyone_id, name: 'Everyone'])
69
- do_acls
70
- end
71
-
72
- def do_acl_grant
73
- acl_action(:grant_to)
74
- end
75
-
76
- def do_acl_revoke
77
- acl_action(:revoke_from)
78
- end
79
-
80
- # Based on command-line options, return an array describing the
81
- # users or groups (entities) which will be granted or revoked a
82
- # privilege.
83
- # @return [Array] [type_of_entity, [Hash]...]
84
- #
85
- def acl_entities
86
- acl_type = options[:modify] ? :modify : :view
87
-
88
- if options[:user]
89
- [:users, user_lists(acl_type, options[:'<name>'])]
90
- else
91
- [:groups, group_lists(acl_type, options[:'<name>'])]
92
- end
93
- end
94
-
95
- # Make a list of users to be given to the SDK ACL methods. Users
96
- # are defined as a Hash, with keys :id and :name.
97
- # @param acl_type [Symbol] :view or :modify
98
- # @param users [Array] user names
99
- # @return [Array[Hash]]
100
- #
101
- def user_lists(acl_type, users)
102
- { view: [], modify: [] }.tap do |l|
103
- l[acl_type] = users.map { |u| { id: u, name: u } }
104
- end
105
- end
106
-
107
- # Generate arrays ready for passing to the SDK acl methods
108
- # @return see #user_lists, but name and id are not the same.
109
- #
110
- def group_lists(acl_type, groups)
111
- { view: [], modify: [] }.tap do |l|
112
- l[acl_type] = groups.each_with_object([]) do |g, a|
113
- name = group_name(g)
114
-
115
- if name.nil?
116
- puts "Cannot find group with id '#{g}'."
117
- next
118
- end
119
-
120
- a.<< ({ id: g, name: name })
121
- end
122
- end
123
- end
124
-
125
- private
126
-
127
- # When given an ACL action (grant or revoke), call the right
128
- # method with the right arguments.
129
- # @param action [Symbol] :grant_to or :revoke_from
130
- # @return [Wavefront::Response]
131
- #
132
- def acl_action(action)
133
- entity_type, entities = acl_entities
134
-
135
- resp = send(format('%s_%s', action, entity_type),
136
- options[:'<id>'],
137
- entities)
138
-
139
- print_status(resp.status)
140
- do_acls
141
- end
142
-
143
- # The #grant_to_ and #revoke_from_ methods are called by
144
- # #acl_action, and speak to the SDK. They all return a
145
- # Wavefront::Response object.
146
- #
147
- def grant_to_users(id, lists)
148
- wf.acl_add(id, lists[:view], lists[:modify])
149
- end
150
-
151
- def revoke_from_users(id, lists)
152
- wf.acl_delete(id, lists[:view], lists[:modify])
153
- end
154
-
155
- def grant_to_groups(id, lists)
156
- wf.acl_add(id, lists[:view], lists[:modify])
157
- end
158
-
159
- def revoke_from_groups(id, lists)
160
- wf.acl_delete(id, lists[:view], lists[:modify])
161
- end
162
-
163
- def print_status(status)
164
- puts status.message unless status.message.empty?
165
- rescue NoMethodError
166
- nil
167
- end
168
-
169
- # Get the name of a user group, given the ID.
170
- # @param group_id [String] UUID of a group
171
- # @return [String, Nil] name of group, nil if it does not exist
172
- #
173
- def group_name(group_id)
174
- require 'wavefront-sdk/usergroup'
175
- wfs = Wavefront::UserGroup.new(mk_creds, mk_opts)
176
- wfs.describe(group_id).response.name
177
- rescue RuntimeError
178
- nil
179
- end
180
-
181
- # @return [String] UUID of 'Everyone' group
182
- # @raise WavefrontCli::Exception::UserGroupNotFound if group
183
- # does not exist. This is caught in the controller.
184
- #
185
- def everyone_id
186
- require 'wavefront-sdk/search'
187
- wfs = Wavefront::Search.new(mk_creds, mk_opts)
188
- query = conds_to_query(['name=Everyone'])
189
- wfs.search(:usergroup, query).response.items.first.id
190
- rescue RuntimeError
191
- raise WavefrontCli::Exception::UserGroupNotFound, 'Everyone'
192
- end
193
56
  end
194
57
  end
@@ -1,10 +1,13 @@
1
1
  require_relative 'base'
2
+ require_relative 'command_mixins/tag'
2
3
 
3
4
  module WavefrontCli
4
5
  #
5
6
  # CLI coverage for the v2 'derivedmetric' API.
6
7
  #
7
8
  class DerivedMetric < WavefrontCli::Base
9
+ include WavefrontCli::Mixin::Tag
10
+
8
11
  def validator_exception
9
12
  Wavefront::Exception::InvalidDerivedMetricId
10
13
  end
@@ -13,22 +16,9 @@ module WavefrontCli
13
16
  wf.describe(options[:'<id>'], options[:version])
14
17
  end
15
18
 
16
- # rubocop:disable Metrics/AbcSize
17
19
  def do_delete
18
- cannot_noop!
19
-
20
- word = if wf.describe(options[:'<id>']).status.code == 200
21
- 'Soft'
22
- else
23
- 'Permanently'
24
- end
25
-
26
- puts format('%s deleting derived metric definition %s', word,
27
- options[:'<id>'])
28
-
29
- wf.delete(options[:'<id>'])
20
+ smart_delete('derived metric')
30
21
  end
31
- # rubocop:enable Metrics/AbcSize
32
22
 
33
23
  def do_history
34
24
  wf.history(options[:'<id>'])
@@ -48,6 +48,10 @@ module WavefrontDisplay
48
48
  puts "Unsnoozed alert '#{options[:'<id>']}'."
49
49
  end
50
50
 
51
+ def do_latest
52
+ puts data.max
53
+ end
54
+
51
55
  # rubocop:disable Metrics/AbcSize
52
56
  def do_summary
53
57
  kw = data.keys.map(&:size).max + 2
@@ -63,5 +67,9 @@ module WavefrontDisplay
63
67
  multicolumn(:id, :condition)
64
68
  end
65
69
  end
70
+
71
+ def do_version
72
+ puts data.max
73
+ end
66
74
  end
67
75
  end
@@ -2,6 +2,7 @@ require 'fileutils'
2
2
  require 'open3'
3
3
  require 'wavefront-sdk/support/mixins'
4
4
  require_relative 'base'
5
+ require_relative 'command_mixins/tag'
5
6
 
6
7
  EVENT_STATE_DIR = Pathname.new('/var/tmp/wavefront')
7
8
 
@@ -12,6 +13,7 @@ module WavefrontCli
12
13
  class Event < Base
13
14
  attr_reader :state_dir
14
15
  include Wavefront::Mixins
16
+ include WavefrontCli::Mixin::Tag
15
17
 
16
18
  def post_initialize(_options)
17
19
  @state_dir = EVENT_STATE_DIR + (Etc.getlogin || 'notty')
@@ -1,10 +1,13 @@
1
1
  require_relative 'base'
2
+ require_relative 'command_mixins/tag'
2
3
 
3
4
  module WavefrontCli
4
5
  #
5
6
  # CLI coverage for the v2 'source' API.
6
7
  #
7
8
  class Source < WavefrontCli::Base
9
+ include WavefrontCli::Mixin::Tag
10
+
8
11
  def do_list
9
12
  wf.list(options[:limit], options[:cursor])
10
13
  end
@@ -1 +1 @@
1
- WF_CLI_VERSION = '3.2.0'.freeze
1
+ WF_CLI_VERSION = '3.2.1'.freeze
data/spec/.rubocop.yml CHANGED
@@ -10,7 +10,7 @@ Metrics/BlockLength:
10
10
  Max: 120
11
11
 
12
12
  Metrics/MethodLength:
13
- Max: 80
13
+ Max: 100
14
14
 
15
15
  Metrics/AbcSize:
16
16
  Max: 45
data/spec/spec_helper.rb CHANGED
@@ -64,6 +64,8 @@ class DummyResponse
64
64
  def empty?
65
65
  false
66
66
  end
67
+
68
+ def status; end
67
69
  end
68
70
 
69
71
  CANNED_RESPONSE = DummyResponse.new
@@ -75,9 +77,11 @@ CANNED_RESPONSE = DummyResponse.new
75
77
  # @param cmd [String] command line args to supply to the Wavefront
76
78
  # command
77
79
  # @param call [Hash]
80
+ # @param spies [Array[Hash]] array of spies to set up, for stubbing
81
+ # methods in any class. Hash has keys :class, :method, :return.
78
82
  # rubocop:disable Metrics/AbcSize
79
83
  # rubocop:disable Metrics/PerceivedComplexity
80
- def cmd_to_call(word, args, call, sdk_class = nil, extra_method = nil)
84
+ def cmd_to_call(word, args, call, sdk_class = nil, spies = [])
81
85
  headers = { 'Accept': /.*/,
82
86
  'Accept-Encoding': /.*/,
83
87
  'Authorization': 'Bearer 0123456789-ABCDEF',
@@ -115,16 +119,10 @@ def cmd_to_call(word, args, call, sdk_class = nil, extra_method = nil)
115
119
  end
116
120
 
117
121
  require "wavefront-sdk/#{sdk_class.name.split('::').last.downcase}"
118
- # This lets us mock out methods which happen after the
119
- # thing we're interested in. For instance when you
120
- # favourite a dashboard, the CLI calls do_favs to display
121
- # the new favourite list, sending another API call, which
122
- # has been tested elsewhere.
123
- #
124
- if extra_method
122
+ spies.each do |spy|
125
123
  Spy.on_instance_method(
126
- Object.const_get(extra_method.first), extra_method.last
127
- ).and_return(true)
124
+ Object.const_get(spy[:class]), spy[:method]
125
+ ).and_return(spy[:return])
128
126
  end
129
127
 
130
128
  Spy.on_instance_method(
@@ -315,11 +313,75 @@ def tag_tests(cmd, id, bad_id, pth = nil, klass = nil)
315
313
  "tag delete #{id} #{BAD_TAG}"])
316
314
  end
317
315
 
316
+ def acl_tests(cmd, id, bad_id, pth = nil, klass = nil)
317
+ gid1 = '2659191e-aad4-4302-a94e-9667e1517127'
318
+ pth ||= cmd
319
+
320
+ cmd_to_call(cmd, "acls #{id}", { path: "/api/v2/#{pth}/acl?id=#{id}" },
321
+ klass)
322
+
323
+ spies = [{ class: "WavefrontCli::#{cmd.capitalize}",
324
+ method: :do_acls,
325
+ return: true }]
326
+
327
+ cmd_to_call(cmd, "acl clear #{id}",
328
+ { method: :put,
329
+ path: "/api/v2/#{pth}/acl/set",
330
+ body: [{ entityId: id,
331
+ viewAcl: [],
332
+ modifyAcl: %w[abcd-1234] }].to_json,
333
+ headers: JSON_POST_HEADERS }, klass, [
334
+ { class: "WavefrontCli::#{cmd.capitalize}",
335
+ method: :everyone_id,
336
+ return: 'abcd-1234' },
337
+ { class: "WavefrontCli::#{cmd.capitalize}",
338
+ method: :do_acls,
339
+ return: true }
340
+ ])
341
+
342
+ cmd_to_call(cmd, "acl grant view on #{id} to testuser1 testuser2",
343
+ { method: :post,
344
+ path: "/api/v2/#{pth}/acl/add",
345
+ body: [{ entityId: id,
346
+ viewAcl: %w[testuser1 testuser2],
347
+ modifyAcl: [] }].to_json,
348
+ headers: JSON_POST_HEADERS }, klass, spies)
349
+ cmd_to_call(cmd, "acl revoke view on #{id} from testuser1",
350
+ { method: :post,
351
+ path: "/api/v2/#{pth}/acl/remove",
352
+ body: [{ entityId: id,
353
+ viewAcl: %w[testuser1],
354
+ modifyAcl: [] }].to_json,
355
+ headers: JSON_POST_HEADERS }, klass, spies)
356
+
357
+ cmd_to_call(cmd, "acl grant modify on #{id} to testuser1",
358
+ { method: :post,
359
+ path: "/api/v2/#{pth}/acl/add",
360
+ body: [{ entityId: id,
361
+ viewAcl: [],
362
+ modifyAcl: %w[testuser1] }].to_json,
363
+ headers: JSON_POST_HEADERS }, klass, spies)
364
+ cmd_to_call(cmd, "acl revoke modify on #{id} from testuser1",
365
+ { method: :post,
366
+ path: "/api/v2/#{pth}/acl/remove",
367
+ body: [{ entityId: id,
368
+ viewAcl: [],
369
+ modifyAcl: %w[testuser1] }].to_json,
370
+ headers: JSON_POST_HEADERS }, klass, spies)
371
+
372
+ invalid_ids(cmd, ["acls #{bad_id}",
373
+ "acl clear #{bad_id}",
374
+ "acl grant modify on #{bad_id} to testuser1",
375
+ "acl revoke view on #{bad_id} from #{gid1}"])
376
+ end
377
+
378
+ # Unit tests
379
+ #
318
380
  class CliMethodTest < MiniTest::Test
319
381
  attr_reader :wf
320
382
 
321
383
  def setup
322
- @wf = CliClass.new({})
384
+ @wf = cliclass.new({})
323
385
  end
324
386
 
325
387
  def import_tester(word, have_fields, do_not_have_fields = [])
@@ -19,16 +19,29 @@ require_relative '../spec_helper'
19
19
  require_relative "../../lib/wavefront-cli/#{word}"
20
20
 
21
21
  describe "#{word} command" do
22
- missing_creds(word, ['list', "describe #{id}", "snooze #{id}",
23
- 'queries', 'snoozed', "install #{id}",
24
- "uninstall #{id}", 'firing',
25
- 'currently firing', 'summary',
26
- "delete #{id}", "undelete #{id}", "history #{id}"])
22
+ missing_creds(word, ['list',
23
+ "describe #{id}",
24
+ "snooze #{id}",
25
+ 'queries',
26
+ 'snoozed',
27
+ "clone #{id}",
28
+ "install #{id}",
29
+ "uninstall #{id}",
30
+ 'firing',
31
+ 'currently firing',
32
+ 'summary',
33
+ "acls #{id}",
34
+ "acl grant view on #{id} to testuser1",
35
+ "acl revoke modify on #{id} from group1",
36
+ "delete #{id}",
37
+ "undelete #{id}",
38
+ "history #{id}"])
27
39
  list_tests(word)
28
40
  cmd_to_call(word, "describe #{id}", path: "/api/v2/#{word}/#{id}")
29
41
  cmd_to_call(word, "describe -v 7 #{id}",
30
42
  path: "/api/v2/#{word}/#{id}/history/7")
31
43
  cmd_to_call(word, "history #{id}", path: "/api/v2/#{word}/#{id}/history")
44
+ cmd_to_call(word, "latest #{id}", path: "/api/v2/#{word}/#{id}/history")
32
45
 
33
46
  it 'deletes with a check on inTrash' do
34
47
  stub_request(:get,
@@ -43,6 +56,12 @@ describe "#{word} command" do
43
56
  method: :delete, path: "/api/v2/#{word}/#{id}")
44
57
  end
45
58
 
59
+ cmd_to_call(word, "clone #{id}",
60
+ method: :post, path: "/api/v2/#{word}/#{id}/clone")
61
+ cmd_to_call(word, "clone #{id} -v 5",
62
+ method: :post,
63
+ path: "/api/v2/#{word}/#{id}/clone",
64
+ body: { id: id, v: 5, name: nil })
46
65
  cmd_to_call(word, "undelete #{id}",
47
66
  method: :post, path: "/api/v2/#{word}/#{id}/undelete")
48
67
  cmd_to_call(word, "snooze #{id}",
@@ -90,15 +109,18 @@ describe "#{word} command" do
90
109
  tag_tests(word, id, bad_id)
91
110
  noop_tests(word, id, true)
92
111
  test_list_output(word)
112
+ acl_tests(word, id, bad_id)
93
113
  end
94
114
 
95
- CliClass = WavefrontCli::Alert
96
-
97
115
  class TestAlertMethods < CliMethodTest
98
116
  def test_import_method
99
117
  import_tester(:window,
100
- %i[startTimeInSeconds endTimeInSeconds
101
- relevantCustomerTags title relevantHostTags],
118
+ %i[condition displayExpression resolveAfterMinutes
119
+ minutes severity tags target name],
102
120
  %i[id])
103
121
  end
122
+
123
+ def cliclass
124
+ WavefrontCli::Alert
125
+ end
104
126
  end
@@ -85,59 +85,97 @@ class WavefrontCliConfigTest < MiniTest::Test
85
85
  input_list = %w[2501b9c3-61e3-4f07-bee2-250aa06a9cab
86
86
  test.wavefront.com myproxy json]
87
87
 
88
- x = wfo.stub(:read_input, proc { input_list.shift }) do
89
- wfo.create_profile('prof')
88
+ out, err = capture_io do
89
+ x = wfo.stub(:read_input, proc { input_list.shift }) do
90
+ wfo.create_profile('prof')
91
+ end
92
+
93
+ assert_instance_of(IniFile, x)
94
+ assert_equal('2501b9c3-61e3-4f07-bee2-250aa06a9cab',
95
+ x[:prof]['token'])
96
+ assert_equal('test.wavefront.com', x[:prof]['endpoint'])
97
+ assert_equal('myproxy', x[:prof]['proxy'])
98
+ assert_equal('json', x[:prof]['format'])
90
99
  end
91
100
 
92
- assert_instance_of(IniFile, x)
93
- assert_equal('2501b9c3-61e3-4f07-bee2-250aa06a9cab', x[:prof]['token'])
94
- assert_equal('test.wavefront.com', x[:prof]['endpoint'])
95
- assert_equal('myproxy', x[:prof]['proxy'])
96
- assert_equal('json', x[:prof]['format'])
101
+ assert_match(/Creating profile 'prof'./, out)
102
+ assert_match(/Wavefront API token/, out)
103
+ assert_match(/Wavefront API endpoint/, out)
104
+ assert_match(/Wavefront proxy endpoint/, out)
105
+ assert_match(/default output format/, out)
106
+ assert_empty(err)
97
107
  end
98
108
 
99
109
  def test_create_profile_2
100
110
  input_list = ['2501b9c3-61e3-4f07-bee2-250aa06a9cab', '', '', '']
101
111
 
102
- x = wfo.stub(:read_input, proc { input_list.shift }) do
103
- wfo.create_profile('prof')
112
+ out, err = capture_io do
113
+ x = wfo.stub(:read_input, proc { input_list.shift }) do
114
+ wfo.create_profile('prof')
115
+ end
116
+
117
+ assert_instance_of(IniFile, x)
118
+ assert_equal('2501b9c3-61e3-4f07-bee2-250aa06a9cab',
119
+ x[:prof]['token'])
120
+ assert_equal('metrics.wavefront.com', x[:prof]['endpoint'])
121
+ assert_equal('wavefront', x[:prof]['proxy'])
122
+ assert_equal('human', x[:prof]['format'])
104
123
  end
105
124
 
106
- assert_instance_of(IniFile, x)
107
- assert_equal('2501b9c3-61e3-4f07-bee2-250aa06a9cab', x[:prof]['token'])
108
- assert_equal('metrics.wavefront.com', x[:prof]['endpoint'])
109
- assert_equal('wavefront', x[:prof]['proxy'])
110
- assert_equal('human', x[:prof]['format'])
125
+ assert_match(/Creating profile 'prof'./, out)
126
+ assert_match(/Wavefront API token/, out)
127
+ assert_match(/Wavefront API endpoint/, out)
128
+ assert_match(/Wavefront proxy endpoint/, out)
129
+ assert_match(/default output format/, out)
130
+ assert_empty(err)
111
131
  end
112
132
 
113
133
  def test_create_profile_3
114
134
  input_list = ['X501b9c3-61e3-4f07-bee2-250aa06a9cab', '', '', '']
115
135
 
116
- assert_raises(WavefrontCli::Exception::InvalidValue) do
117
- wfo.stub(:read_input, proc { input_list.shift }) do
118
- wfo.create_profile('prof')
136
+ out, err = capture_io do
137
+ assert_raises(WavefrontCli::Exception::InvalidValue) do
138
+ wfo.stub(:read_input, proc { input_list.shift }) do
139
+ wfo.create_profile('prof')
140
+ end
119
141
  end
120
142
  end
143
+
144
+ assert_match(/Creating profile 'prof'./, out)
145
+ assert_match(/Wavefront API token/, out)
146
+ assert_empty(err)
121
147
  end
122
148
 
123
149
  def test_create_profile_4
124
150
  input_list = ['2501b9c3-61e3-4f07-bee2-250aa06a9cab', 'end', '', '']
125
151
 
126
- assert_raises(WavefrontCli::Exception::InvalidValue) do
127
- wfo.stub(:read_input, proc { input_list.shift }) do
128
- wfo.create_profile('prof')
152
+ out, err = capture_io do
153
+ assert_raises(WavefrontCli::Exception::InvalidValue) do
154
+ wfo.stub(:read_input, proc { input_list.shift }) do
155
+ wfo.create_profile('prof')
156
+ end
129
157
  end
130
158
  end
159
+
160
+ assert_match(/Creating profile 'prof'./, out)
161
+ assert_match(/Wavefront API token/, out)
162
+ assert_empty(err)
131
163
  end
132
164
 
133
165
  def test_create_profile_5
134
166
  input_list = ['', '', '', '']
135
167
 
136
- assert_raises(WavefrontCli::Exception::MandatoryValue) do
137
- wfo.stub(:read_input, proc { input_list.shift }) do
138
- wfo.create_profile('prof')
168
+ out, err = capture_io do
169
+ assert_raises(WavefrontCli::Exception::MandatoryValue) do
170
+ wfo.stub(:read_input, proc { input_list.shift }) do
171
+ wfo.create_profile('prof')
172
+ end
139
173
  end
140
174
  end
175
+
176
+ assert_match(/Creating profile 'prof'./, out)
177
+ assert_match(/Wavefront API token/, out)
178
+ assert_empty(err)
141
179
  end
142
180
 
143
181
  def test_do_setup; end
@@ -7,7 +7,7 @@ require_relative '../../lib/wavefront-cli/controller'
7
7
  #
8
8
  class WavefrontCliHelpTest < MiniTest::Test
9
9
  def test_no_args
10
- WavefrontCliController.new([])
10
+ capture_io { WavefrontCliController.new([]) }
11
11
  rescue SystemExit => e
12
12
  assert_equal(1, e.status)
13
13
  assert_match(/^Usage/, e.message)
@@ -16,14 +16,14 @@ class WavefrontCliHelpTest < MiniTest::Test
16
16
  end
17
17
 
18
18
  def test_version
19
- WavefrontCliController.new(%w[--version])
19
+ capture_io { WavefrontCliController.new(%w[--version]) }
20
20
  rescue SystemExit => e
21
21
  assert_equal(1, e.status)
22
22
  assert_match(/^\d+\.\d+\.\d+$/, e.message)
23
23
  end
24
24
 
25
25
  def test_help
26
- WavefrontCliController.new(%w[--help])
26
+ capture_io { WavefrontCliController.new(%w[--help]) }
27
27
  rescue SystemExit => e
28
28
  assert_equal(1, e.status)
29
29
  assert_match(/^Commands:$/, e.message)
@@ -33,7 +33,7 @@ class WavefrontCliHelpTest < MiniTest::Test
33
33
  def test_command_help
34
34
  CMDS.each do |cmd|
35
35
  begin
36
- WavefrontCliController.new([cmd, '--help'])
36
+ capture_io { WavefrontCliController.new([cmd, '--help']) }
37
37
  rescue SystemExit => e
38
38
  assert(e.message.split("\n").map(&:size).max <= TW)
39
39
  assert_equal(1, e.status)
@@ -46,15 +46,19 @@ class WavefrontCliHelpTest < MiniTest::Test
46
46
  end
47
47
 
48
48
  def test_malformed_config
49
- WavefrontCliController.new(['alert', 'list',
50
- "--config=#{RES_DIR}/malformed.conf"])
49
+ capture_io do
50
+ WavefrontCliController.new(['alert', 'list',
51
+ "--config=#{RES_DIR}/malformed.conf"])
52
+ end
51
53
  rescue SystemExit => e
52
54
  assert_equal(1, e.status)
53
55
  assert e.message.start_with?('Could not load configuration file')
54
56
  end
55
57
 
56
58
  def test_missing_config
57
- WavefrontCliController.new(%w[alert list --config=/no/such/file])
59
+ capture_io do
60
+ WavefrontCliController.new(%w[alert list --config=/no/such/file])
61
+ end
58
62
  rescue SystemExit => e
59
63
  assert_equal(1, e.status)
60
64
  assert_equal("Configuration file '/no/such/file' not found.", e.message)
@@ -7,28 +7,6 @@ word = 'dashboard'
7
7
  require_relative '../spec_helper'
8
8
  require_relative "../../lib/wavefront-cli/#{word}"
9
9
 
10
- # Method tests. CLI tests follow
11
- #
12
- class WavefrontCliDashboardTest < MiniTest::Test
13
- attr_reader :wf
14
-
15
- def setup
16
- @wf = WavefrontCli::Dashboard.new({})
17
- end
18
-
19
- def test_user_lists
20
- assert_equal({ modify: [],
21
- view: [{ name: 'a@bc.com', id: 'a@bc.com' },
22
- { name: 'x@yz.com', id: 'x@yz.com' }] },
23
- wf.user_lists(:view, %w[a@bc.com x@yz.com]))
24
-
25
- assert_equal({ view: [],
26
- modify: [{ name: 'a@bc.com', id: 'a@bc.com' },
27
- { name: 'x@yz.com', id: 'x@yz.com' }] },
28
- wf.user_lists(:modify, %w[a@bc.com x@yz.com]))
29
- end
30
- end
31
-
32
10
  describe "#{word} command" do
33
11
  missing_creds(word, ['list', "describe #{id}", "delete #{id}",
34
12
  "undelete #{id}", "history #{id}"])
@@ -73,30 +51,38 @@ describe "#{word} command" do
73
51
  sort: { field: 'id',
74
52
  ascending: true } }.to_json)
75
53
 
54
+ spies = [{ class: 'WavefrontCli::Dashboard',
55
+ method: :do_favs,
56
+ return: nil }]
57
+
76
58
  cmd_to_call(word,
77
59
  "fav #{id}",
78
60
  { method: :post,
79
61
  path: "/api/v2/#{word}/#{id}/favorite" },
80
62
  nil,
81
- ['WavefrontCli::Dashboard', :do_favs])
63
+ spies)
82
64
 
83
65
  cmd_to_call(word,
84
66
  "unfav #{id}",
85
67
  { method: :post,
86
68
  path: "/api/v2/#{word}/#{id}/unfavorite" },
87
69
  nil,
88
- ['WavefrontCli::Dashboard', :do_favs])
70
+ spies)
71
+
89
72
  cmd_to_call(word, "undelete #{id}",
90
73
  method: :post, path: "/api/v2/#{word}/#{id}/undelete")
91
74
  invalid_ids(word, ["describe #{bad_id}", "delete #{bad_id}",
92
75
  "undelete #{bad_id}"])
93
76
  tag_tests(word, id, bad_id)
94
77
  test_list_output(word)
78
+ acl_tests(word, id, bad_id)
95
79
  end
96
80
 
97
- CliClass = WavefrontCli::Dashboard
98
-
99
81
  class TestAlertMethods < CliMethodTest
82
+ def cliclass
83
+ WavefrontCli::Dashboard
84
+ end
85
+
100
86
  def test_import_method
101
87
  import_tester(:dashboard,
102
88
  %i[description name parameters tags url creatorId
@@ -80,12 +80,14 @@ describe "#{word} command" do
80
80
  test_list_output(word, k)
81
81
  end
82
82
 
83
- CliClass = WavefrontCli::DerivedMetric
84
-
85
83
  class TestDerivedMetricMethods < CliMethodTest
86
84
  def test_import_method
87
85
  import_tester(:derivedmetric,
88
86
  %i[tags minutes name query metricsUsed hostsUsed],
89
87
  %i[id])
90
88
  end
89
+
90
+ def cliclass
91
+ WavefrontCli::DerivedMetric
92
+ end
91
93
  end
@@ -37,9 +37,11 @@ describe "#{word} command" do
37
37
  test_list_output(word, k)
38
38
  end
39
39
 
40
- CliClass = WavefrontCli::MaintenanceWindow
41
-
42
40
  class TestMaintenanceWindowMethods < CliMethodTest
41
+ def cliclass
42
+ WavefrontCli::MaintenanceWindow
43
+ end
44
+
43
45
  def test_import_method
44
46
  import_tester(:window,
45
47
  %i[startTimeInSeconds endTimeInSeconds
@@ -34,12 +34,14 @@ describe "#{word} command" do
34
34
  test_list_output(word)
35
35
  end
36
36
 
37
- CliClass = WavefrontCli::Notificant
38
-
39
37
  class TestNotificantMethods < CliMethodTest
40
38
  def test_import_method
41
39
  import_tester(:notificant,
42
40
  %i[method title creatorId triggers template],
43
41
  %i[id])
44
42
  end
43
+
44
+ def cliclass
45
+ WavefrontCli::Notificant
46
+ end
45
47
  end
@@ -20,7 +20,7 @@ class OptHandlerTest < MiniTest::Test
20
20
  end
21
21
 
22
22
  def test_missing_config
23
- WavefrontCli::OptHandler.new(config: '/no/such/file')
23
+ capture_io { WavefrontCli::OptHandler.new(config: '/no/such/file') }
24
24
  rescue SystemExit => e
25
25
  assert_equal(1, e.status)
26
26
  assert_match("Configuration file '/no/such/file' not found.", e.message)
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require_relative '../../spec_helper'
3
4
  require_relative '../../../lib/wavefront-cli/stdlib/array'
4
5
 
5
6
  # Test extensions to stlib Array
@@ -24,13 +24,13 @@ Gem::Specification.new do |gem|
24
24
 
25
25
  gem.add_runtime_dependency 'docopt', '~> 0.6.0'
26
26
  gem.add_runtime_dependency 'inifile', '~> 3.0'
27
- gem.add_runtime_dependency 'wavefront-sdk', '~> 3.2', '>= 3.2.0'
27
+ gem.add_runtime_dependency 'wavefront-sdk', '~> 3.3', '>= 3.3.0'
28
28
 
29
29
  gem.add_development_dependency 'minitest', '~> 5.11', '>= 5.11.0'
30
30
  gem.add_development_dependency 'rake', '~> 12.0'
31
31
  gem.add_development_dependency 'rubocop', '~> 0.54.0'
32
- gem.add_development_dependency 'spy', '~> 0.4.0'
33
- gem.add_development_dependency 'webmock', '~> 3.0'
32
+ gem.add_development_dependency 'spy', '~> 1.0.0'
33
+ gem.add_development_dependency 'webmock', '~> 3.5'
34
34
  gem.add_development_dependency 'yard', '~> 0.9.5'
35
35
 
36
36
  gem.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wavefront-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Fisher
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-07 00:00:00.000000000 Z
11
+ date: 2019-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docopt
@@ -44,20 +44,20 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 3.2.0
47
+ version: 3.3.0
48
48
  - - "~>"
49
49
  - !ruby/object:Gem::Version
50
- version: '3.2'
50
+ version: '3.3'
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - ">="
56
56
  - !ruby/object:Gem::Version
57
- version: 3.2.0
57
+ version: 3.3.0
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '3.2'
60
+ version: '3.3'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: minitest
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -112,28 +112,28 @@ dependencies:
112
112
  requirements:
113
113
  - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: 0.4.0
115
+ version: 1.0.0
116
116
  type: :development
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: 0.4.0
122
+ version: 1.0.0
123
123
  - !ruby/object:Gem::Dependency
124
124
  name: webmock
125
125
  requirement: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: '3.0'
129
+ version: '3.5'
130
130
  type: :development
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: '3.0'
136
+ version: '3.5'
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: yard
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -169,6 +169,8 @@ files:
169
169
  - lib/wavefront-cli/apitoken.rb
170
170
  - lib/wavefront-cli/base.rb
171
171
  - lib/wavefront-cli/cloudintegration.rb
172
+ - lib/wavefront-cli/command_mixins/acl.rb
173
+ - lib/wavefront-cli/command_mixins/tag.rb
172
174
  - lib/wavefront-cli/commands/.rubocop.yml
173
175
  - lib/wavefront-cli/commands/alert.rb
174
176
  - lib/wavefront-cli/commands/apitoken.rb