hawkular-client 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.gitignore +2 -1
- data/.rubocop.yml +19 -6
- data/CHANGES.rdoc +13 -3
- data/README.rdoc +9 -3
- data/hawkularclient.gemspec +8 -4
- data/lib/alerts/alerts_api.rb +202 -32
- data/lib/hawkular.rb +35 -67
- data/lib/hawkular_all.rb +2 -0
- data/lib/inventory/inventory_api.rb +459 -130
- data/lib/metrics/metric_api.rb +14 -9
- data/lib/operations/operations_api.rb +247 -0
- data/lib/tokens/tokens_api.rb +33 -0
- data/lib/version.rb +1 -1
- data/spec/integration/alerts_spec.rb +272 -20
- data/spec/integration/hello-world-definitions.json +46 -0
- data/spec/integration/inventory_spec.rb +253 -89
- data/spec/integration/metric_spec.rb +36 -0
- data/spec/integration/operations_spec.rb +420 -0
- data/spec/integration/tokens_spec.rb +45 -0
- data/spec/resources/driver.jar +0 -0
- data/spec/resources/sample.war +0 -0
- data/spec/spec_helper.rb +30 -6
- data/spec/unit/base_spec.rb +22 -1
- data/spec/unit/canonical_path_spec.rb +92 -0
- data/spec/vcr/vcr_setup.rb +3 -4
- data/spec/vcr_cassettes/Alert/EndToEnd/Should_create_and_fire_a_trigger.yml +1187 -0
- data/spec/vcr_cassettes/Alert/Events/Should_list_events.yml +101 -0
- data/spec/vcr_cassettes/Alert/Events/Should_list_events_using_criteria.yml +79 -0
- data/spec/vcr_cassettes/Alert/Events/Should_not_list_events_using_criteria.yml +49 -0
- data/spec/vcr_cassettes/Alert/Triggers/Should_bulk_load_triggers.yml +225 -0
- data/spec/vcr_cassettes/Alert/Triggers/Should_create_a_basic_trigger_with_action.yml +355 -0
- data/spec/vcr_cassettes/Alert/Triggers/Should_create_an_action.yml +134 -0
- data/spec/vcr_cassettes/Alert/Triggers/Should_create_an_action_for_webhooks.yml +220 -0
- data/spec/vcr_cassettes/Alert/Triggers/Should_get_the_action_definitions.yml +218 -0
- data/spec/vcr_cassettes/Alert/Triggers/Should_not_create_an_action_for_unknown_plugin.yml +46 -0
- data/spec/vcr_cassettes/Alert/Triggers/Should_not_create_an_action_for_unknown_properties.yml +134 -0
- data/spec/vcr_cassettes/Counter_metrics/Should_get_metrics_with_limit_and_order.yml +270 -0
- data/spec/vcr_cassettes/Inventory/Client_should_listen_on_various_inventory_events.json +47 -0
- data/spec/vcr_cassettes/Inventory/Client_should_listen_on_various_inventory_events.yml +191 -0
- data/spec/vcr_cassettes/Inventory/Helpers/generate_some_events_for_websocket.yml +507 -0
- data/spec/vcr_cassettes/Inventory/Should_List_datasources_with_no_props.yml +74 -69
- data/spec/vcr_cassettes/Inventory/{Should_list_types_without_feed.yml → Should_create_a_feed.yml} +29 -32
- data/spec/vcr_cassettes/Inventory/Should_create_a_feed_again.yml +211 -0
- data/spec/vcr_cassettes/Inventory/Should_create_a_resource_.yml +355 -0
- data/spec/vcr_cassettes/Inventory/Should_create_a_resource_with_metric.yml +786 -0
- data/spec/vcr_cassettes/Inventory/Should_create_a_resourcetype.yml +205 -0
- data/spec/vcr_cassettes/Inventory/Should_create_and_delete_feed.yml +201 -0
- data/spec/vcr_cassettes/Inventory/Should_create_and_get_a_resource.yml +419 -0
- data/spec/vcr_cassettes/Inventory/Should_list_URLs.yml +39 -27
- data/spec/vcr_cassettes/Inventory/Should_list_WildFlys.yml +37 -29
- data/spec/vcr_cassettes/Inventory/Should_list_WildFlys_with_props.yml +53 -45
- data/spec/vcr_cassettes/Inventory/Should_list_all_the_resource_types.yml +177 -0
- data/spec/vcr_cassettes/Inventory/Should_list_children_of_WildFly.yml +230 -56
- data/spec/vcr_cassettes/Inventory/Should_list_feeds.yml +27 -24
- data/spec/vcr_cassettes/Inventory/Should_list_heap_metrics_for_WildFlys.yml +586 -119
- data/spec/vcr_cassettes/Inventory/Should_list_metrics_for_WildFlys.yml +220 -59
- data/spec/vcr_cassettes/Inventory/Should_list_metrics_of_given_metric_type.yml +613 -0
- data/spec/vcr_cassettes/Inventory/Should_list_metrics_of_given_resource_type.yml +333 -0
- data/spec/vcr_cassettes/Inventory/Should_list_recursive_children_of_WildFly.yml +2064 -0
- data/spec/vcr_cassettes/Inventory/Should_list_relationships_of_WildFly.yml +460 -0
- data/spec/vcr_cassettes/Inventory/Should_list_types_with_bad_feed.yml +26 -25
- data/spec/vcr_cassettes/Inventory/Should_list_types_with_feed.yml +102 -36
- data/spec/vcr_cassettes/Inventory/Should_not_find_an_unknown_resource.yml +54 -0
- data/spec/vcr_cassettes/Inventory/Tenants/Should_Get_Tenant_For_Explicit_Credentials.yml +50 -0
- data/spec/vcr_cassettes/Inventory/Tenants/Should_Get_Tenant_For_Implicit_Credentials.yml +50 -0
- data/spec/vcr_cassettes/Operation/Helpers/get_tenant.yml +50 -0
- data/spec/vcr_cassettes/Operation/Operation/Add_JDBC_driver_should_add_the_driver.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/Add_datasource_should_be_doable.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/Add_deployment_should_be_doable.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/Redeploy_can_be_run_multiple_times_in_parallel.json +40 -0
- data/spec/vcr_cassettes/Operation/Operation/Redeploy_should_be_performed_and_eventually_respond_with_success.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/Redeploy_should_not_be_performed_if_resource_path_is_wrong.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/Remove_JDBC_driver_should_be_performed_and_eventually_respond_with_success.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/Remove_datasource_should_be_performed_and_eventually_respond_with_success.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/Remove_deployment_should_be_performed_and_eventually_respond_with_success.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/Undeploy_should_be_performed_and_eventually_respond_with_success.json +26 -0
- data/spec/vcr_cassettes/Operation/Operation/should_not_be_possible_to_perform_on_closed_client.json +12 -0
- data/spec/vcr_cassettes/Operation/Websocket_connection/should_be_established.json +9 -0
- data/spec/vcr_cassettes/Tokens/Should_be_able_to_create_a_new_token_for_an_actual_user.yml +57 -0
- data/spec/vcr_cassettes/Tokens/Should_be_able_to_list_the_available_tokens.yml +49 -0
- data/spec/vcr_cassettes/Tokens/Should_get_a_401_when_attempting_to_create_a_token_with_a_wrong_password.yml +56 -0
- data/spec/vcr_cassettes/Tokens/Should_get_a_401_when_attempting_to_create_a_token_without_a_password.yml +55 -0
- metadata +175 -11
- data/lib/metrics/version.rb +0 -7
data/lib/metrics/metric_api.rb
CHANGED
@@ -43,12 +43,12 @@ module Hawkular::Metrics
|
|
43
43
|
# types (counters, gauges, availabilities).
|
44
44
|
class Metrics
|
45
45
|
# @param client [Client]
|
46
|
-
# @param
|
46
|
+
# @param metric_type [String] metric type (one of "counter", "gauge", "availability")
|
47
47
|
# @param resource [String] REST resource name for accessing metrics
|
48
48
|
# of given type (one of "counters", "gauges", "availability")
|
49
|
-
def initialize(client,
|
49
|
+
def initialize(client, metric_type, resource)
|
50
50
|
@client = client
|
51
|
-
@type =
|
51
|
+
@type = metric_type
|
52
52
|
@resource = resource
|
53
53
|
end
|
54
54
|
|
@@ -84,9 +84,9 @@ module Hawkular::Metrics
|
|
84
84
|
end
|
85
85
|
|
86
86
|
# update tags for given metric definition
|
87
|
-
# @param
|
88
|
-
def update_tags(
|
89
|
-
@client.http_put("/#{@resource}/#{
|
87
|
+
# @param metric_definition [MetricDefinition]
|
88
|
+
def update_tags(metric_definition)
|
89
|
+
@client.http_put("/#{@resource}/#{metric_definition.id}/tags", metric_definition.hash[:tags])
|
90
90
|
end
|
91
91
|
|
92
92
|
# Push metric data
|
@@ -114,10 +114,15 @@ module Hawkular::Metrics
|
|
114
114
|
# @param starts [Integer] optional timestamp (default now - 8h)
|
115
115
|
# @param ends [Integer] optional timestamp (default now)
|
116
116
|
# @param bucketDuration [String] optional interval (default no aggregation)
|
117
|
+
# @param percentiles [String] optional percentiles to calculate
|
118
|
+
# @param limit [Integer] optional limit the number of data points returned
|
119
|
+
# @param order [String] optional Data point sort order, based on timestamp (ASC, DESC)
|
117
120
|
# @return [Array[Hash]] datapoints
|
118
121
|
# @see #push_data #push_data for datapoint detail
|
119
|
-
def get_data(id, starts: nil, ends: nil, bucketDuration: nil, buckets: nil
|
120
|
-
|
122
|
+
def get_data(id, starts: nil, ends: nil, bucketDuration: nil, buckets: nil, percentiles: nil, limit: nil,
|
123
|
+
order: nil)
|
124
|
+
params = { start: starts, end: ends, bucketDuration: bucketDuration, buckets: buckets,
|
125
|
+
percentiles: percentiles, limit: limit, order: order }
|
121
126
|
resp = @client.http_get("/#{@resource}/#{ERB::Util.url_encode(id)}/data/?" +
|
122
127
|
encode_params(params))
|
123
128
|
resp.is_a?(Array) ? resp : [] # API returns no content (empty Hash) instead of empty array
|
@@ -146,7 +151,7 @@ module Hawkular::Metrics
|
|
146
151
|
end
|
147
152
|
end
|
148
153
|
|
149
|
-
# Class that
|
154
|
+
# Class that interacts with "gauge" metric types
|
150
155
|
class Gauges < Metrics
|
151
156
|
# @param client [Client]
|
152
157
|
def initialize(client)
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'hawkular'
|
2
|
+
require 'websocket-client-simple'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
# Adding a method `perform` for each block so that we can write nice callbacks for this client
|
6
|
+
class Proc
|
7
|
+
def perform(callable, result)
|
8
|
+
call(Class.new do
|
9
|
+
method_name = callable.to_sym
|
10
|
+
define_method(method_name) { |&block| block.nil? ? true : block.call(result) }
|
11
|
+
define_method("#{method_name}?") { true }
|
12
|
+
# method_missing is here because we are not forcing the client to provide both success and error callbacks
|
13
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
14
|
+
# https://github.com/bbatsov/rubocop/issues/2704
|
15
|
+
def method_missing(_method_name, *_args, &_block)
|
16
|
+
false
|
17
|
+
end
|
18
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
19
|
+
end.new)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Operations module allows invoking operation on the wildfly agent.
|
24
|
+
module Hawkular::Operations
|
25
|
+
# Client class to interact with the agent via websockets
|
26
|
+
class OperationsClient < Hawkular::BaseClient
|
27
|
+
include WebSocket::Client
|
28
|
+
|
29
|
+
attr_accessor :ws
|
30
|
+
|
31
|
+
# helper for parsing the "OperationName=json_payload" messages
|
32
|
+
class WebSocket::Frame::Data
|
33
|
+
def to_msg_hash
|
34
|
+
chunks = split('=', 2)
|
35
|
+
{
|
36
|
+
operationName: chunks[0],
|
37
|
+
data: JSON.parse(chunks[1])
|
38
|
+
}
|
39
|
+
rescue
|
40
|
+
{}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Initialize new OperationsClient
|
45
|
+
#
|
46
|
+
# @param [Hash] args Arguments for client
|
47
|
+
#
|
48
|
+
# @option args [String] :host base url of Hawkular - e.g http://localhost:8080
|
49
|
+
# @option args [Hash{String=>String}] :credentials Hash of {username, password} or token
|
50
|
+
# @option args [Fixnum] :wait_time Time in seconds describing how long the constructor should block - handshake
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# Hawkular::Operations::OperationsClient.new(credentials: {username: 'jdoe', password: 'password'})
|
54
|
+
def initialize(args)
|
55
|
+
args[:host] ||= 'localhost:8080'
|
56
|
+
args[:credentials] ||= {}
|
57
|
+
args[:wait_time] ||= 0.5
|
58
|
+
super(args[:host], args[:credentials])
|
59
|
+
# note: if we start using the secured WS, change the protocol to wss://
|
60
|
+
url = "ws://#{entrypoint}/hawkular/command-gateway/ui/ws"
|
61
|
+
@ws = Simple.connect url do |client|
|
62
|
+
client.on(:message, once: true) do |msg|
|
63
|
+
parsed_message = msg.data.to_msg_hash
|
64
|
+
puts parsed_message if ENV['HAWKULARCLIENT_LOG_RESPONSE']
|
65
|
+
case parsed_message[:operationName]
|
66
|
+
when 'WelcomeResponse'
|
67
|
+
@session_id = parsed_message[:data]['sessionId']
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
sleep args[:wait_time]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Closes the WebSocket connection
|
75
|
+
def close_connection!
|
76
|
+
@ws.close
|
77
|
+
end
|
78
|
+
|
79
|
+
# Invokes a generic operation on the Wildfly agent
|
80
|
+
# (the operation name must be specified in the hash)
|
81
|
+
# Note: if success and failure callbacks are omitted, the client will not wait for the Response message
|
82
|
+
# @param hash [Hash{String=>Object}] a hash containing: resourcePath [String] denoting the resource on
|
83
|
+
# which the operation is about to run, operationName [String]
|
84
|
+
# @param callback [Block] callback that is run after the operation is done
|
85
|
+
def invoke_generic_operation(hash, &callback)
|
86
|
+
required = [:resourcePath, :operationName]
|
87
|
+
check_pre_conditions hash, required, &callback
|
88
|
+
|
89
|
+
invoke_operation_helper(hash, &callback)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Invokes operation on the wildfly agent that has it's own message type
|
93
|
+
# @param operation_payload [Hash{String=>Object}] a hash containing: resourcePath [String] denoting
|
94
|
+
# the resource on which the operation is about to run
|
95
|
+
# @param operation_name [String] the name of the operation. This must correspond with the message type, they can be
|
96
|
+
# found here https://git.io/v2h1a (Use only the first part of the name without the Request/Response suffix), e.g.
|
97
|
+
# RemoveDatasource (and not RemoveDatasourceRequest)
|
98
|
+
# @param callback [Block] callback that is run after the operation is done
|
99
|
+
def invoke_specific_operation(operation_payload, operation_name, &callback)
|
100
|
+
fail 'Operation must be specified' if operation_name.nil?
|
101
|
+
required = [:resourcePath]
|
102
|
+
check_pre_conditions operation_payload, required, &callback
|
103
|
+
|
104
|
+
invoke_operation_helper(operation_payload, operation_name, &callback)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Deploys a war file into WildFly
|
108
|
+
#
|
109
|
+
# @param [Hash] hash Arguments for deployment
|
110
|
+
# @option hash [String] :resource_path canonical path of the WildFly server into which we deploy
|
111
|
+
# @option hash [String] :destination_file_name resulting file name
|
112
|
+
# @option hash [String] :binary_content binary content representing the war file
|
113
|
+
# @option hash [String] :enabled whether the deployment should be enabled or not
|
114
|
+
#
|
115
|
+
# @param callback [Block] callback that is run after the operation is done
|
116
|
+
def add_deployment(hash, &callback)
|
117
|
+
hash[:enabled] ||= true
|
118
|
+
required = [:resource_path, :destination_file_name, :binary_content]
|
119
|
+
check_pre_conditions hash, required, &callback
|
120
|
+
|
121
|
+
operation_payload = prepare_payload_hash([:binary_content], hash)
|
122
|
+
invoke_operation_helper(operation_payload, 'DeployApplication', hash[:binary_content], &callback)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Adds a new datasource
|
126
|
+
#
|
127
|
+
# @param [Hash] hash Arguments for the datasource
|
128
|
+
# @option hash [String] :resourcePath canonical path of the WildFly server into which we add datasource
|
129
|
+
# @option hash [String] :xaDatasource XA DS or normal
|
130
|
+
# @option hash [String] :datasourceName name of the datasource
|
131
|
+
# @option hash [String] :jndiName JNDI name
|
132
|
+
# @option hash [String] :driverName this is internal name of the driver in Hawkular
|
133
|
+
# @option hash [String] :driverClass class of driver
|
134
|
+
# @option hash [String] :connectionUrl jdbc connection string
|
135
|
+
# @option hash [String] :datasourceProperties optional properties
|
136
|
+
# @option hash [String] :username username to DB
|
137
|
+
# @option hash [String] :password password to DB
|
138
|
+
#
|
139
|
+
# @param callback [Block] callback that is run after the operation is done
|
140
|
+
def add_datasource(hash, &callback)
|
141
|
+
required = [:resourcePath, :xaDatasource, :datasourceName, :jndiName, :driverName, :driverClass, :connectionUrl]
|
142
|
+
check_pre_conditions hash, required, &callback
|
143
|
+
|
144
|
+
invoke_specific_operation(hash, 'AddDatasource', &callback)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Adds a new datasource
|
148
|
+
#
|
149
|
+
# @param [Hash] hash Arguments for the datasource
|
150
|
+
# @option hash [String] :resource_path canonical path of the WildFly server into which we add driver
|
151
|
+
# @option hash [String] :driver_jar_name name of the jar file
|
152
|
+
# @option hash [String] :driver_name name of the jdbc driver (when adding datasource, this is the driverName)
|
153
|
+
# @option hash [String] :module_name name of the JBoss module into which the driver will be installed - 'foo.bar'
|
154
|
+
# @option hash [String] :driver_class fully specified java class of the driver - e.q. 'com.mysql.jdbc.Driver'
|
155
|
+
# @option hash [String] :binary_content driver jar file bits
|
156
|
+
#
|
157
|
+
# @param callback [Block] callback that is run after the operation is done
|
158
|
+
def add_jdbc_driver(hash, &callback)
|
159
|
+
required = [:resource_path, :driver_jar_name, :driver_name, :module_name, :driver_class, :binary_content]
|
160
|
+
check_pre_conditions hash, required, &callback
|
161
|
+
|
162
|
+
operation_payload = prepare_payload_hash([:binary_content], hash)
|
163
|
+
invoke_operation_helper(operation_payload, 'AddJdbcDriver', hash[:binary_content], &callback)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Exports the JDR report
|
167
|
+
#
|
168
|
+
# @param [String] resource_path canonical path of the WildFly server
|
169
|
+
# @param callback [Block] callback that is run after the operation is done
|
170
|
+
def export_jdr(resource_path, &callback)
|
171
|
+
fail 'resource_path must be specified' if resource_path.nil?
|
172
|
+
check_pre_conditions(&callback)
|
173
|
+
|
174
|
+
invoke_specific_operation({ resourcePath: resource_path }, 'ExportJdr', &callback)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def invoke_operation_helper(operation_payload, operation_name = nil, binary_content = nil, &callback)
|
180
|
+
# fallback to generic 'ExecuteOperation' if nothing is specified
|
181
|
+
operation_name ||= 'ExecuteOperation'
|
182
|
+
add_credentials! operation_payload
|
183
|
+
|
184
|
+
handle_message(operation_name, operation_payload, &callback) unless callback.nil?
|
185
|
+
|
186
|
+
# sends a message that will actually run the operation
|
187
|
+
payload = "#{operation_name}Request=#{operation_payload.to_json}"
|
188
|
+
payload += binary_content unless binary_content.nil?
|
189
|
+
@ws.send payload, type: binary_content.nil? ? :text : :binary
|
190
|
+
end
|
191
|
+
|
192
|
+
def check_pre_conditions(hash = {}, params = [], &callback)
|
193
|
+
fail 'Handshake with server has not been done.' unless @ws.open?
|
194
|
+
fail 'Hash cannot be nil.' if hash.nil?
|
195
|
+
fail 'callback must have the perform method defined. include Hawkular::Operations' unless
|
196
|
+
callback.nil? || callback.respond_to?('perform')
|
197
|
+
params.each do |property|
|
198
|
+
fail "Hash property #{property} must be specified" if hash[property].nil?
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def add_credentials!(hash)
|
203
|
+
hash[:authentication] = @credentials.delete_if { |_, v| v.nil? }
|
204
|
+
end
|
205
|
+
|
206
|
+
def handle_message(operation_name, operation_payload, &callback)
|
207
|
+
client = @ws
|
208
|
+
# register a callback handler
|
209
|
+
@ws.on :message do |msg|
|
210
|
+
parsed = msg.data.to_msg_hash
|
211
|
+
OperationsClient.log_message(parsed)
|
212
|
+
case parsed[:operationName]
|
213
|
+
when "#{operation_name}Response"
|
214
|
+
same_path = parsed[:data]['resourcePath'] == operation_payload[:resourcePath]
|
215
|
+
# failed operations don't return the operation name from some strange reason
|
216
|
+
same_name = parsed[:data]['operationName'] == operation_payload[:operationName]
|
217
|
+
if same_path # using the resource path as a correlation id
|
218
|
+
success = same_name && parsed[:data]['status'] == 'OK'
|
219
|
+
success ? callback.perform(:success, parsed[:data]) : callback.perform(:failure, parsed[:data]['message'])
|
220
|
+
client.remove_listener :message
|
221
|
+
end
|
222
|
+
when 'GenericErrorResponse'
|
223
|
+
OperationsClient.handle_error parsed, &callback
|
224
|
+
client.remove_listener :message
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.handle_error(parsed_message, &callback)
|
230
|
+
callback.perform(:failure, parsed_message == {} ? 'error' : parsed_message[:data]['errorMessage'])
|
231
|
+
end
|
232
|
+
|
233
|
+
def self.log_message(message)
|
234
|
+
puts "\nreceived WebSocket msg: #{message}\n" if ENV['HAWKULARCLIENT_LOG_RESPONSE']
|
235
|
+
end
|
236
|
+
|
237
|
+
def prepare_payload_hash(ignored_params, hash)
|
238
|
+
# it filters out ignored params and convert keys from snake_case to camelCase
|
239
|
+
Hash[hash.select { |k, _| !ignored_params.include? k }.map { |k, v| [to_camel_case(k.to_s).to_sym, v] }]
|
240
|
+
end
|
241
|
+
|
242
|
+
def to_camel_case(str)
|
243
|
+
ret = str.split('_').collect(&:capitalize).join
|
244
|
+
ret[0, 1].downcase + ret[1..-1]
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hawkular'
|
2
|
+
|
3
|
+
# Token module provides access to the Secret Store REST API.
|
4
|
+
module Hawkular::Token
|
5
|
+
# Client class to interact with the Secret Store
|
6
|
+
class TokenClient < Hawkular::BaseClient
|
7
|
+
# Create a new Secret Store client
|
8
|
+
# @param entrypoint [String] base url of Hawkular - e.g http://localhost:8080
|
9
|
+
# @param credentials [Hash{String=>String}] Hash of username, password
|
10
|
+
def initialize(entrypoint = 'http://localhost:8080', credentials = {})
|
11
|
+
super(entrypoint, credentials)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Retrieve the tenant id for the passed credentials.
|
15
|
+
# If no credentials are passed, the ones from the constructor are used
|
16
|
+
# @param credentials [Hash{String=>String}] Hash of username, password, token(optional)
|
17
|
+
# @return [String] tenant id
|
18
|
+
def get_tokens(credentials = {})
|
19
|
+
creds = credentials.empty? ? @credentials : credentials
|
20
|
+
auth_header = { Authorization: base_64_credentials(creds) }
|
21
|
+
http_get('/secret-store/v1/tokens', auth_header)
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_token(credentials = {}, persona = nil, name = 'Token created via Hawkular Ruby Client', expires_at = nil)
|
25
|
+
creds = credentials.empty? ? @credentials : credentials
|
26
|
+
auth_header = { Authorization: base_64_credentials(creds) }
|
27
|
+
auth_header['Hawkular-Persona'] = persona if persona
|
28
|
+
|
29
|
+
token_attributes = { expiresAt: expires_at, attributes: { name: name } }
|
30
|
+
http_post('/secret-store/v1/tokens/create', token_attributes, auth_header)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/version.rb
CHANGED
@@ -5,57 +5,165 @@ module Hawkular::Alerts::RSpec
|
|
5
5
|
ALERTS_BASE = 'http://localhost:8080/hawkular/alerts'
|
6
6
|
creds = { username: 'jdoe', password: 'password' }
|
7
7
|
|
8
|
-
describe 'Alert/Triggers', :
|
9
|
-
|
10
|
-
client = Hawkular::Alerts::AlertsClient.new(ALERTS_BASE, creds)
|
8
|
+
describe 'Alert/Triggers', vcr: { decode_compressed_response: true } do
|
9
|
+
before(:each) do
|
10
|
+
@client = Hawkular::Alerts::AlertsClient.new(ALERTS_BASE, creds)
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
+
it 'Should List Triggers' do
|
14
|
+
triggers = @client.list_triggers
|
13
15
|
|
14
16
|
expect(triggers.size).to be(3)
|
15
17
|
end
|
16
18
|
|
17
19
|
it 'Should List Triggers for Tag' do
|
18
|
-
|
19
|
-
|
20
|
-
triggers = client.list_triggers [], ['resourceId|75bfdd05-d03d-481e-bf32-c724c7719d8b~Local']
|
20
|
+
triggers = @client.list_triggers [],
|
21
|
+
['resourceId|75bfdd05-d03d-481e-bf32-c724c7719d8b~Local']
|
21
22
|
|
22
23
|
expect(triggers.size).to be(7)
|
23
24
|
end
|
24
25
|
|
25
26
|
it 'Should List Triggers for Tags' do
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
'app|MyShop']
|
27
|
+
triggers = @client.list_triggers [],
|
28
|
+
['resourceId|75bfdd05-d03d-481e-bf32-c724c7719d8b~Local',
|
29
|
+
'app|MyShop']
|
30
30
|
|
31
31
|
expect(triggers.size).to be(7)
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'Should List Triggers for ID' do
|
35
|
-
|
36
|
-
|
37
|
-
triggers = client.list_triggers ['75bfdd05-d03d-481e-bf32-c724c7719d8b~Local_jvm_pheap']
|
35
|
+
triggers = @client.list_triggers ['75bfdd05-d03d-481e-bf32-c724c7719d8b~Local_jvm_pheap']
|
38
36
|
|
39
37
|
expect(triggers.size).to be(1)
|
40
38
|
end
|
41
39
|
|
42
40
|
it 'Should get a single metric Trigger' do
|
43
|
-
|
44
|
-
|
45
|
-
trigger = client.get_single_trigger('snert~Local_jvm_nheap')
|
41
|
+
trigger = @client.get_single_trigger('snert~Local_jvm_nheap')
|
46
42
|
|
47
43
|
expect(trigger).not_to be_nil
|
48
44
|
end
|
49
45
|
|
50
46
|
it 'Should get a single Trigger with conditions' do
|
51
|
-
|
52
|
-
|
53
|
-
trigger = client.get_single_trigger 'snert~Local_jvm_nheap', true
|
47
|
+
trigger = @client.get_single_trigger 'snert~Local_jvm_nheap', true
|
54
48
|
|
55
49
|
expect(trigger).not_to be_nil
|
56
50
|
expect(trigger.conditions.size).to be(1)
|
57
51
|
expect(trigger.dampenings.size).to be(1)
|
58
52
|
end
|
53
|
+
|
54
|
+
it 'Should bulk load triggers' do
|
55
|
+
json = IO.read('spec/integration/hello-world-definitions.json')
|
56
|
+
trigger_hash = JSON.parse(json)
|
57
|
+
|
58
|
+
@client.bulk_import_triggers trigger_hash
|
59
|
+
|
60
|
+
trigger = @client.get_single_trigger 'hello-world-trigger', true
|
61
|
+
expect(trigger).not_to be_nil
|
62
|
+
expect(trigger.conditions.size).to be(2)
|
63
|
+
expect(trigger.dampenings.size).to be(0)
|
64
|
+
|
65
|
+
@client.delete_trigger(trigger.id)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'Should create a basic trigger with action' do
|
69
|
+
@client.create_action :email, 'send-via-email', 'notify-to-admins' => 'joe@acme.org'
|
70
|
+
|
71
|
+
# Create the trigger
|
72
|
+
t = Hawkular::Alerts::Trigger.new({})
|
73
|
+
t.enabled = true
|
74
|
+
t.id = 'my-cool-trigger'
|
75
|
+
t.name = 'Just a trigger'
|
76
|
+
t.severity = :HIGH
|
77
|
+
t.description = 'Just a test trigger'
|
78
|
+
|
79
|
+
# Create a condition
|
80
|
+
c = Hawkular::Alerts::Trigger::Condition.new({})
|
81
|
+
c.trigger_mode = :FIRING
|
82
|
+
c.type = :THRESHOLD
|
83
|
+
c.data_id = 'my-metric-id'
|
84
|
+
c.operator = :LT
|
85
|
+
c.threshold = 5
|
86
|
+
|
87
|
+
# Reference an action definition
|
88
|
+
a = Hawkular::Alerts::Trigger::Action.new({})
|
89
|
+
a.action_plugin = :email
|
90
|
+
a.action_id = 'send-via-email'
|
91
|
+
t.actions.push a
|
92
|
+
|
93
|
+
begin
|
94
|
+
ft = @client.create_trigger t, [c], nil
|
95
|
+
|
96
|
+
expect(ft).not_to be_nil
|
97
|
+
|
98
|
+
trigger = @client.get_single_trigger t.id, true
|
99
|
+
expect(trigger).not_to be_nil
|
100
|
+
expect(trigger.conditions.size).to be(1)
|
101
|
+
expect(trigger.dampenings.size).to be(0)
|
102
|
+
ensure
|
103
|
+
# rubocop:disable Lint/HandleExceptions
|
104
|
+
begin
|
105
|
+
@client.delete_trigger(t.id)
|
106
|
+
rescue
|
107
|
+
# I am not interested
|
108
|
+
end
|
109
|
+
begin
|
110
|
+
@client.delete_action(a.action_id, a.action_plugin)
|
111
|
+
rescue
|
112
|
+
# I am not interested
|
113
|
+
end
|
114
|
+
# rubocop:enable Lint/HandleExceptions
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'Should get the action definitions' do
|
119
|
+
ret = @client.get_action_definition
|
120
|
+
expect(ret.size).to be(2)
|
121
|
+
expect(ret.key? 'email').to be_truthy
|
122
|
+
|
123
|
+
ret = @client.get_action_definition 'email'
|
124
|
+
expect(ret.size).to be(1)
|
125
|
+
expect(ret['email'].size).to be(7)
|
126
|
+
|
127
|
+
expect { @client.get_action_definition '-does-not-exist-' }
|
128
|
+
.to raise_error(Hawkular::BaseClient::HawkularException)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'Should create an action' do
|
132
|
+
@client.create_action 'email', 'my-id1', 'notify-to-admins' => 'joe@acme.org'
|
133
|
+
@client.delete_action 'email', 'my-id1'
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'Should not create an action for unknown plugin' do
|
137
|
+
expect do
|
138
|
+
@client.create_action '-does-not-exist',
|
139
|
+
'my-id2',
|
140
|
+
'notify-to-admins' => 'joe@acme.org'
|
141
|
+
end.to raise_error(Hawkular::BaseClient::HawkularException)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'Should not create an action for unknown properties' do
|
145
|
+
begin
|
146
|
+
@client.create_action :email, 'my-id3', foo: 'bar'
|
147
|
+
ensure
|
148
|
+
@client.delete_action :email, 'my-id3'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'Should create an action for webhooks' do
|
153
|
+
begin
|
154
|
+
@client.get_action_definition 'webhook'
|
155
|
+
|
156
|
+
webhook_props = { 'url' => 'http://localhost:8080/bla', 'method' => 'POST' }
|
157
|
+
@client.create_action 'webhook', 'my-id1',
|
158
|
+
webhook_props
|
159
|
+
ret = @client.get_action 'webhook', 'my-id1'
|
160
|
+
expect(ret.action_plugin).to eq('webhook')
|
161
|
+
expect(ret.action_id).to eq('my-id1')
|
162
|
+
|
163
|
+
ensure
|
164
|
+
@client.delete_action 'webhook', 'my-id1'
|
165
|
+
end
|
166
|
+
end
|
59
167
|
end
|
60
168
|
|
61
169
|
describe 'Alert/Alerts', :vcr do
|
@@ -140,4 +248,148 @@ module Hawkular::Alerts::RSpec
|
|
140
248
|
# expect(data).not_to be_nil
|
141
249
|
# end
|
142
250
|
# end
|
251
|
+
|
252
|
+
describe 'Alert/Events', :vcr do
|
253
|
+
VCR.configure do |c|
|
254
|
+
c.default_cassette_options = {
|
255
|
+
match_requests_on: [:method, VCR.request_matchers.uri_without_params(:startTime, :endTime)]
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'Should list events' do
|
260
|
+
client = Hawkular::Alerts::AlertsClient.new(ALERTS_BASE, creds)
|
261
|
+
|
262
|
+
events = client.list_events('thin' => true)
|
263
|
+
|
264
|
+
expect(events).to_not be_nil
|
265
|
+
expect(events.size).to be(12)
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'Should list events using criteria' do
|
269
|
+
now = Time.new.to_i
|
270
|
+
start_time = (now - 7_200) * 1000
|
271
|
+
end_time = now * 1000
|
272
|
+
|
273
|
+
client = Hawkular::Alerts::AlertsClient.new(ALERTS_BASE, creds)
|
274
|
+
|
275
|
+
events = client.list_events('startTime' => start_time, 'endTime' => end_time)
|
276
|
+
|
277
|
+
expect(events).to_not be_nil
|
278
|
+
expect(events.size).to be(1)
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'Should not list events using criteria' do
|
282
|
+
client = Hawkular::Alerts::AlertsClient.new(ALERTS_BASE, creds)
|
283
|
+
|
284
|
+
events = client.list_events('startTime' => 0, 'endTime' => 1000)
|
285
|
+
|
286
|
+
expect(events).to_not be_nil
|
287
|
+
expect(events.size).to be(0)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe 'Alert/EndToEnd', vcr: { decode_compressed_response: true } do
|
292
|
+
before(:each) do
|
293
|
+
@client = Hawkular::Alerts::AlertsClient.new(ALERTS_BASE, creds)
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'Should create and fire a trigger' do
|
297
|
+
email_props = { to: 'joe@acme.org',
|
298
|
+
from: 'admin@acme.org' }
|
299
|
+
begin
|
300
|
+
@client.create_action 'email', 'send-via-email',
|
301
|
+
email_props
|
302
|
+
rescue
|
303
|
+
@client.delete_action 'email', 'send-via-email'
|
304
|
+
@client.create_action 'email', 'send-via-email',
|
305
|
+
email_props
|
306
|
+
end
|
307
|
+
|
308
|
+
webhook_props = { url: 'http://172.31.7.177/',
|
309
|
+
method: 'POST' }
|
310
|
+
begin
|
311
|
+
@client.create_action 'webhook', 'send-via-webhook',
|
312
|
+
webhook_props
|
313
|
+
rescue
|
314
|
+
@client.delete_action 'webhook', 'send-via-webhook'
|
315
|
+
@client.create_action 'webhook', 'send-via-webhook',
|
316
|
+
webhook_props
|
317
|
+
end
|
318
|
+
|
319
|
+
# Create the trigger
|
320
|
+
t = Hawkular::Alerts::Trigger.new({})
|
321
|
+
t.enabled = true
|
322
|
+
t.id = 'my-cool-email-trigger'
|
323
|
+
t.name = 'Just a trigger'
|
324
|
+
t.severity = :HIGH
|
325
|
+
t.description = 'Just a test trigger'
|
326
|
+
|
327
|
+
# Create a condition
|
328
|
+
c = Hawkular::Alerts::Trigger::Condition.new({})
|
329
|
+
c.trigger_mode = :FIRING
|
330
|
+
c.type = :THRESHOLD
|
331
|
+
c.data_id = 'my-metric-id1'
|
332
|
+
c.operator = :GT
|
333
|
+
c.threshold = 5
|
334
|
+
|
335
|
+
# Reference an action definition for email
|
336
|
+
a = Hawkular::Alerts::Trigger::Action.new({})
|
337
|
+
a.action_plugin = 'email'
|
338
|
+
a.action_id = 'send-via-email'
|
339
|
+
t.actions.push a
|
340
|
+
|
341
|
+
# Reference an action definition for webhook
|
342
|
+
a = Hawkular::Alerts::Trigger::Action.new({})
|
343
|
+
a.action_plugin = 'webhook'
|
344
|
+
a.action_id = 'send-via-webhook'
|
345
|
+
t.actions.push a
|
346
|
+
|
347
|
+
begin
|
348
|
+
ft = @client.create_trigger t, [c], nil
|
349
|
+
|
350
|
+
expect(ft).not_to be_nil
|
351
|
+
|
352
|
+
trigger = @client.get_single_trigger t.id, true
|
353
|
+
expect(trigger).not_to be_nil
|
354
|
+
expect(trigger.conditions.size).to be(1)
|
355
|
+
expect(trigger.dampenings.size).to be(0)
|
356
|
+
|
357
|
+
# Trigger is set up - send a metric value to trigger it.
|
358
|
+
metric_client = Hawkular::Metrics::Client.new('http://localhost:8080/hawkular/metrics',
|
359
|
+
creds)
|
360
|
+
|
361
|
+
data_point = { timestamp: Time.now.to_i * 1000, value: 42 }
|
362
|
+
data = [{ id: 'my-metric-id1', data: [data_point] }]
|
363
|
+
|
364
|
+
metric_client.push_data(gauges: data)
|
365
|
+
|
366
|
+
# wait 2s for the alert engine to work if we are live
|
367
|
+
sleep 2 if VCR.current_cassette.recording?
|
368
|
+
|
369
|
+
# see if alert has fired
|
370
|
+
alerts = @client.get_alerts_for_trigger 'my-cool-email-trigger'
|
371
|
+
expect(alerts).to_not be(nil)
|
372
|
+
alerts.each { |al| @client.resolve_alert(al.id, 'Heiko', 'Hello Ruby World :-)') }
|
373
|
+
|
374
|
+
ensure
|
375
|
+
# rubocop:disable Lint/HandleExceptions
|
376
|
+
begin
|
377
|
+
@client.delete_trigger(t.id)
|
378
|
+
rescue
|
379
|
+
# I am not interested
|
380
|
+
end
|
381
|
+
begin
|
382
|
+
@client.delete_action('webhook', 'send-via-webhook')
|
383
|
+
rescue
|
384
|
+
# I am not interested
|
385
|
+
end
|
386
|
+
begin
|
387
|
+
@client.delete_action('email', 'send-via-email')
|
388
|
+
rescue
|
389
|
+
# I am not interested
|
390
|
+
end
|
391
|
+
# rubocop:enable Lint/HandleExceptions
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
143
395
|
end
|