wavefront-cli 3.2.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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