arborist-webservice 0.0.1.pre20180815100446 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +45 -2
- data/History.md +1 -1
- data/README.md +4 -4
- data/Rakefile +7 -7
- data/lib/arborist/monitor/webservice.rb +54 -15
- data/lib/arborist/node/webservice.rb +25 -10
- data/lib/arborist/webservice/constants.rb +76 -0
- data/spec/arborist/monitor/webservice_spec.rb +169 -0
- data/spec/arborist/node/webservice_spec.rb +59 -9
- data/spec/arborist/webservice_spec.rb +27 -0
- data/spec/spec_helper.rb +11 -0
- metadata +13 -10
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d364d3021e618267b924682c39b011d365aa486c56a37c13842a23d71e4b4c6
|
4
|
+
data.tar.gz: b6028b714764befd0d89372432d6936037eaa0b5bd54d10f735c537903fbeb38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7b5ff725bd2e08a5737e5c2d0ceb8dfdff21aec1d30633cf08fa0c310ca82c01f1524c3bdb2ce472843ca002536311cbf1eca65d1e087bd2487ecfb156741ee
|
7
|
+
data.tar.gz: 1c023a87365bc5551a3ccb3f054e5c15a671f8bff6df0e66e88b8f83232d08524d0f4f07a94a791c19a033b67d8ff71eef437ecf730a4dbebefc08773472fcf4
|
checksums.yaml.gz.sig
ADDED
data.tar.gz.sig
ADDED
Binary file
|
data/ChangeLog
CHANGED
@@ -1,12 +1,55 @@
|
|
1
|
+
2018-08-30 Michael Granger <ged@FaerieMUD.org>
|
2
|
+
|
3
|
+
* History.md, arborist-webservice.gemspec:
|
4
|
+
Update history and the gemspec
|
5
|
+
[65c19b823a1b] [tip]
|
6
|
+
|
7
|
+
* README.md, lib/arborist/monitor/webservice.rb,
|
8
|
+
lib/arborist/node/webservice.rb,
|
9
|
+
lib/arborist/webservice/constants.rb,
|
10
|
+
spec/arborist/monitor/webservice_spec.rb,
|
11
|
+
spec/arborist/node/webservice_spec.rb, spec/spec_helper.rb:
|
12
|
+
Finish up tests, flesh out settings and config for less-common
|
13
|
+
cases.
|
14
|
+
[53ff11b5ccf6]
|
15
|
+
|
16
|
+
2018-08-29 Michael Granger <ged@FaerieMUD.org>
|
17
|
+
|
18
|
+
* lib/arborist/monitor/webservice.rb,
|
19
|
+
spec/arborist/monitor/webservice_spec.rb:
|
20
|
+
Rename 'HTML' monitor callback to 'HTTP'
|
21
|
+
[c970fb9b5ccf] [github/master]
|
22
|
+
|
1
23
|
2018-08-15 Michael Granger <ged@FaerieMUD.org>
|
2
24
|
|
25
|
+
* README.md, Rakefile, arborist-webservice.gemspec:
|
26
|
+
Fix some documentation issues.
|
27
|
+
[1e226b9d9561]
|
28
|
+
|
29
|
+
* .hgignore:
|
30
|
+
Ignore built gems
|
31
|
+
[a580ef4ff61b]
|
32
|
+
|
33
|
+
* Rakefile, arborist-webservice.gemspec:
|
34
|
+
Update dependencies.
|
35
|
+
[c08c1e0bbb70]
|
36
|
+
|
37
|
+
* .gems, .hgignore, ChangeLog, Manifest.txt, Rakefile, arborist-
|
38
|
+
webservice.gemspec, lib/arborist/monitor/webservice.rb,
|
39
|
+
lib/arborist/webservice.rb, lib/arborist/webservice/connection.rb,
|
40
|
+
lib/arborist/webservice/constants.rb,
|
41
|
+
spec/arborist/monitor/webservice_spec.rb,
|
42
|
+
spec/arborist/node/webservice_spec.rb:
|
43
|
+
Switch to Typhoeus for batched parallel requests
|
44
|
+
[59e390021ccd]
|
45
|
+
|
3
46
|
* .gems, Rakefile, lib/arborist/monitor/webservice.rb,
|
4
47
|
lib/arborist/node/webservice.rb, lib/arborist/webservice.rb,
|
5
48
|
lib/arborist/webservice/connection.rb,
|
6
49
|
lib/arborist/webservice/constants.rb,
|
7
50
|
lib/arborist/webservice/monkeypatches.rb:
|
8
51
|
Checkpoint commit before switching to Typhoeus
|
9
|
-
[d2e9ed791434]
|
52
|
+
[d2e9ed791434]
|
10
53
|
|
11
54
|
2018-08-01 Mahlon E. Smith <mahlon@martini.nu>
|
12
55
|
|
@@ -18,7 +61,7 @@
|
|
18
61
|
|
19
62
|
* README.md:
|
20
63
|
Fix incorrect URLs in the README
|
21
|
-
[7bdc40158b25]
|
64
|
+
[7bdc40158b25]
|
22
65
|
|
23
66
|
* .gems, .pryrc, .ruby-gemset, .ruby-version, .rvm.gems, .rvmrc,
|
24
67
|
ChangeLog, History.md, LICENSE.txt, Manifest.txt, README.md,
|
data/History.md
CHANGED
data/README.md
CHANGED
@@ -30,7 +30,7 @@ The simplest example is an unauthenticated REST service running on port 80:
|
|
30
30
|
end
|
31
31
|
|
32
32
|
This adds a `webservice` child node to the containing host with an identifier of
|
33
|
-
`
|
33
|
+
`example-webserver-api-vi`.
|
34
34
|
|
35
35
|
The simplest monitor setup to monitor that service might look something like:
|
36
36
|
|
@@ -38,9 +38,9 @@ The simplest monitor setup to monitor that service might look something like:
|
|
38
38
|
#encoding: utf-8
|
39
39
|
|
40
40
|
require 'arborist/monitor/webservice'
|
41
|
-
Arborist::Monitor::Webservice::
|
41
|
+
Arborist::Monitor::Webservice::HTTP.default
|
42
42
|
|
43
|
-
This would check that an
|
43
|
+
This would check that an HEAD HTTP request to `http://api.example.com/v1/heartbeat` responds with a 2xx status code.
|
44
44
|
|
45
45
|
|
46
46
|
## Prerequisites
|
@@ -69,7 +69,7 @@ and generate the API documentation.
|
|
69
69
|
|
70
70
|
## License
|
71
71
|
|
72
|
-
Copyright (c) 2016, Michael Granger
|
72
|
+
Copyright (c) 2016-2018, Michael Granger
|
73
73
|
All rights reserved.
|
74
74
|
|
75
75
|
Redistribution and use in source and binary forms, with or without
|
data/Rakefile
CHANGED
@@ -29,13 +29,13 @@ hoespec = Hoe.spec 'arborist-webservice' do |spec|
|
|
29
29
|
|
30
30
|
spec.developer 'Michael Granger', 'ged@FaerieMUD.org'
|
31
31
|
|
32
|
-
spec.dependency 'arborist', '~> 0'
|
33
|
-
spec.dependency 'loggability', '~> 0.
|
32
|
+
spec.dependency 'arborist', '~> 0.2'
|
33
|
+
spec.dependency 'loggability', '~> 0.14'
|
34
34
|
spec.dependency 'typhoeus', '~> 1.3'
|
35
35
|
|
36
|
-
spec.dependency 'hoe-deveiate', '~> 0.
|
36
|
+
spec.dependency 'hoe-deveiate', '~> 0.10', :developer
|
37
37
|
spec.dependency 'simplecov', '~> 0.7', :developer
|
38
|
-
spec.dependency 'rdoc-generator-fivefish', '~> 0.
|
38
|
+
spec.dependency 'rdoc-generator-fivefish', '~> 0.4', :developer
|
39
39
|
|
40
40
|
spec.hg_sign_tags = true if spec.respond_to?( :hg_sign_tags= )
|
41
41
|
spec.check_history_on_release = true if spec.respond_to?( :check_history_on_release= )
|
@@ -69,11 +69,11 @@ if File.directory?( '.hg' )
|
|
69
69
|
|
70
70
|
Rake::Task[ 'docs' ].clear
|
71
71
|
RDoc::Task.new( 'docs' ) do |rdoc|
|
72
|
-
rdoc.main = "README.
|
72
|
+
rdoc.main = "README.md"
|
73
73
|
rdoc.markup = 'markdown'
|
74
|
-
rdoc.rdoc_files.include( "*.rdoc", "ChangeLog", "lib/**/*.rb" )
|
74
|
+
rdoc.rdoc_files.include( "*.rdoc", "*.md", "ChangeLog", "lib/**/*.rb" )
|
75
75
|
rdoc.generator = :fivefish
|
76
|
-
rdoc.title = 'Webservice Node Type for Arborist'
|
76
|
+
rdoc.title = 'Webservice Node Type and Monitor for Arborist'
|
77
77
|
rdoc.rdoc_dir = 'doc'
|
78
78
|
end
|
79
79
|
end
|
@@ -34,12 +34,20 @@ module Arborist::Monitor::Webservice
|
|
34
34
|
Integer( val )
|
35
35
|
end
|
36
36
|
|
37
|
+
##
|
38
|
+
# Whether or not to enable SSL peer certificate verification by default. The
|
39
|
+
# value is either `1` to enable it by default, or `0` to disable it by default.
|
40
|
+
# The config of each invidual webservice node can override this.
|
41
|
+
setting :ssl_verifypeer, default: 1 do |val|
|
42
|
+
Integer( val ).nonzero? ? 1 : 0
|
43
|
+
end
|
44
|
+
|
37
45
|
end
|
38
46
|
|
39
47
|
|
40
48
|
|
41
|
-
# Arborist
|
42
|
-
class
|
49
|
+
# Arborist HTTP web service monitor logic
|
50
|
+
class HTTP
|
43
51
|
extend Loggability
|
44
52
|
include Arborist::Webservice::Constants
|
45
53
|
|
@@ -53,7 +61,7 @@ module Arborist::Monitor::Webservice
|
|
53
61
|
}
|
54
62
|
|
55
63
|
# The array of node properites used by this monitor
|
56
|
-
NODE_PROPERTIES = %i[ uri http_method body body_mimetype ].freeze
|
64
|
+
NODE_PROPERTIES = %i[ uri http_method body body_mimetype config ].freeze
|
57
65
|
|
58
66
|
|
59
67
|
### Return an array of attributes to fetch from nodes for this monitor.
|
@@ -68,7 +76,7 @@ module Arborist::Monitor::Webservice
|
|
68
76
|
end
|
69
77
|
|
70
78
|
|
71
|
-
### Create a new
|
79
|
+
### Create a new HTTP webservice monitor with the specified +options+. Valid options are:
|
72
80
|
###
|
73
81
|
### +:timeout+
|
74
82
|
### Set the number of seconds to wait for a connection for each node.
|
@@ -103,7 +111,8 @@ module Arborist::Monitor::Webservice
|
|
103
111
|
request = self.request_for_node( node )
|
104
112
|
request.on_complete do |response|
|
105
113
|
self.log.debug "Handling response for %s" % [ identifier ]
|
106
|
-
results[ identifier ] =
|
114
|
+
results[ identifier ] =
|
115
|
+
self.make_response_results( response, node['expected_status'] )
|
107
116
|
end
|
108
117
|
hydra.queue( request )
|
109
118
|
end
|
@@ -116,9 +125,11 @@ module Arborist::Monitor::Webservice
|
|
116
125
|
|
117
126
|
### Return a request object built to test the specified webservice +node+.
|
118
127
|
def request_for_node( node_data )
|
128
|
+
http_version = convert_http_version( node_data['http_version'] || DEFAULT_HTTP_VERSION )
|
129
|
+
|
119
130
|
options = {
|
120
131
|
method: node_data['http_method'] || DEFAULT_HTTP_METHOD,
|
121
|
-
http_version:
|
132
|
+
http_version: http_version,
|
122
133
|
headers: self.make_headers_hash( node_data ),
|
123
134
|
body: node_data['body'],
|
124
135
|
timeout: self.timeout,
|
@@ -129,6 +140,7 @@ module Arborist::Monitor::Webservice
|
|
129
140
|
options.merge!( ssl_opts )
|
130
141
|
end
|
131
142
|
|
143
|
+
self.log.debug "Node options for %p are: %p" % [ node_data['uri'], options ]
|
132
144
|
return Typhoeus::Request.new( node_data['uri'], options )
|
133
145
|
end
|
134
146
|
|
@@ -137,10 +149,15 @@ module Arborist::Monitor::Webservice
|
|
137
149
|
def make_ssl_options( uri, node_data )
|
138
150
|
return nil unless uri.start_with?( 'https:' )
|
139
151
|
|
140
|
-
|
141
|
-
|
152
|
+
self.log.debug "Extracting valid SSL options from the node's config: %p" %
|
153
|
+
[ node_data['config'] ]
|
154
|
+
ssl_attributes = SSL_ATTRIBUTES.each_with_object({}) do |(key, desc), opts|
|
155
|
+
opts[ key ] = node_data[ 'config' ][ key.to_s ] if
|
156
|
+
node_data['config']&.key?( key.to_s )
|
142
157
|
end
|
143
158
|
|
159
|
+
ssl_attributes[ :ssl_verifypeer ] ||= Arborist::Monitor::Webservice.ssl_verifypeer
|
160
|
+
|
144
161
|
return ssl_attributes
|
145
162
|
end
|
146
163
|
|
@@ -167,17 +184,18 @@ module Arborist::Monitor::Webservice
|
|
167
184
|
|
168
185
|
|
169
186
|
### Return a Hash of results appropriate for the specified +response+.
|
170
|
-
def make_response_results( response )
|
171
|
-
if response.
|
187
|
+
def make_response_results( response, expected_status=200 )
|
188
|
+
if response.code == expected_status
|
172
189
|
return { webservice: self.success_results(response) }
|
173
190
|
elsif response.timed_out?
|
174
191
|
self.log.error "Request timed out."
|
175
192
|
return { error: 'Request timed out.' }
|
176
193
|
elsif response.code == 0
|
177
|
-
self.log.error
|
178
|
-
return { error: response.
|
194
|
+
self.log.error( response.return_message )
|
195
|
+
return { error: response.return_message }
|
179
196
|
else
|
180
|
-
self.log.error "Got
|
197
|
+
self.log.error "Got an unexpected %03d %s response; expected %03d." %
|
198
|
+
[ response.code, response.status_message, expected_status ]
|
181
199
|
return {
|
182
200
|
error: "%03d %s" % [ response.code, response.status_message ]
|
183
201
|
}
|
@@ -189,6 +207,7 @@ module Arborist::Monitor::Webservice
|
|
189
207
|
### responses.
|
190
208
|
def success_results( response )
|
191
209
|
return {
|
210
|
+
http_version: response.http_version,
|
192
211
|
status: response.code,
|
193
212
|
status_message: response.status_message,
|
194
213
|
headers: response.headers_hash,
|
@@ -203,11 +222,31 @@ module Arborist::Monitor::Webservice
|
|
203
222
|
}
|
204
223
|
end
|
205
224
|
|
206
|
-
|
225
|
+
|
226
|
+
#######
|
227
|
+
private
|
228
|
+
#######
|
229
|
+
|
230
|
+
### Convert a version string like `1.1` into the Symbol Typhoeus/Ethon expect
|
231
|
+
### (e.g., `:httpv1_1`)
|
232
|
+
def convert_http_version( version_string )
|
233
|
+
return case version_string
|
234
|
+
when '1.0'
|
235
|
+
:httpv1_0
|
236
|
+
when '1.1'
|
237
|
+
:httpv1_1
|
238
|
+
when '2.0'
|
239
|
+
:httpv2_0
|
240
|
+
else
|
241
|
+
version_string.to_sym
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
end # class HTTP
|
207
246
|
|
208
247
|
|
209
248
|
# Arborist REST webservice monitor logic
|
210
|
-
class REST < Arborist::Monitor::Webservice::
|
249
|
+
class REST < Arborist::Monitor::Webservice::HTTP
|
211
250
|
end # class REST
|
212
251
|
|
213
252
|
end # class Arborist::Monitor::Webservice
|
@@ -25,14 +25,11 @@ class Arborist::Node::Webservice < Arborist::Node::Service
|
|
25
25
|
|
26
26
|
### Create a new Webservice node.
|
27
27
|
def initialize( identifier, host, uri, attributes={}, &block )
|
28
|
-
|
29
|
-
uri_obj = URI( uri )
|
30
|
-
|
31
|
-
attributes[:app_protocol] ||= uri_obj.scheme
|
32
|
-
attributes[:port] ||= uri_obj.port
|
28
|
+
attributes[:uri] = URI( uri )
|
33
29
|
attributes[:protocol] = 'tcp'
|
34
30
|
attributes[:app_protocol] = 'http'
|
35
31
|
attributes[:http_method] ||= DEFAULT_HTTP_METHOD
|
32
|
+
attributes[:http_version] ||= DEFAULT_HTTP_VERSION
|
36
33
|
attributes[:http_headers] ||= {}
|
37
34
|
attributes[:expected_status] ||= DEFAULT_EXPECTED_STATUS
|
38
35
|
attributes[:body] ||= ''
|
@@ -47,14 +44,14 @@ class Arborist::Node::Webservice < Arborist::Node::Service
|
|
47
44
|
public
|
48
45
|
######
|
49
46
|
|
50
|
-
##
|
51
|
-
# The URI of an endpoint that can be used to monitor the webservice
|
52
|
-
dsl_accessor :uri
|
53
|
-
|
54
47
|
##
|
55
48
|
# The http_method used by the service
|
56
49
|
dsl_accessor :http_method
|
57
50
|
|
51
|
+
##
|
52
|
+
# The http version used by the service
|
53
|
+
dsl_accessor :http_version
|
54
|
+
|
58
55
|
##
|
59
56
|
# The expected_status used by the service
|
60
57
|
dsl_accessor :expected_status
|
@@ -73,6 +70,20 @@ class Arborist::Node::Webservice < Arborist::Node::Service
|
|
73
70
|
end
|
74
71
|
|
75
72
|
|
73
|
+
### Get/set the URI of the service.
|
74
|
+
def uri( new_uri=nil )
|
75
|
+
if new_uri
|
76
|
+
@uri = URI( new_uri )
|
77
|
+
@uri.host ||= self.addresses.first.to_s
|
78
|
+
|
79
|
+
self.app_protocol( @uri.scheme )
|
80
|
+
self.port( @uri.port )
|
81
|
+
end
|
82
|
+
|
83
|
+
return @uri.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
|
76
87
|
### Set node +attributes+ from a Hash.
|
77
88
|
def modify( attributes )
|
78
89
|
attributes = stringify_keys( attributes )
|
@@ -81,6 +92,7 @@ class Arborist::Node::Webservice < Arborist::Node::Service
|
|
81
92
|
|
82
93
|
self.uri( attributes['uri'] )
|
83
94
|
self.http_method( attributes['http_method'] )
|
95
|
+
self.http_version( attributes['http_version'] )
|
84
96
|
self.expected_status( attributes['expected_status'] )
|
85
97
|
self.body( attributes['body'] )
|
86
98
|
self.body_mimetype( attributes['body_mimetype'] )
|
@@ -95,6 +107,8 @@ class Arborist::Node::Webservice < Arborist::Node::Service
|
|
95
107
|
URI( self.uri ) == URI( val )
|
96
108
|
when 'http_method'
|
97
109
|
self.http_method == val
|
110
|
+
when 'http_version'
|
111
|
+
self.http_version == val
|
98
112
|
when 'expected_status'
|
99
113
|
self.expected_status == val
|
100
114
|
when 'body'
|
@@ -122,10 +136,11 @@ class Arborist::Node::Webservice < Arborist::Node::Service
|
|
122
136
|
|
123
137
|
### Return service-node-specific information for #inspect.
|
124
138
|
def node_description
|
125
|
-
desc = "%s %s %s
|
139
|
+
desc = "%s %s %s/%s" % [
|
126
140
|
self.http_method,
|
127
141
|
self.uri,
|
128
142
|
self.app_protocol.upcase,
|
143
|
+
self.http_version,
|
129
144
|
]
|
130
145
|
|
131
146
|
if body && !body.empty?
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'arborist/webservice' unless defined?( Arborist::Webservice )
|
5
|
+
|
6
|
+
|
7
|
+
module Arborist::Webservice::Constants
|
8
|
+
|
9
|
+
# The default HTTP verb to use for monitoring requests
|
10
|
+
DEFAULT_HTTP_METHOD = 'HEAD'.freeze
|
11
|
+
|
12
|
+
# The default HTTP status code to expect from responses
|
13
|
+
DEFAULT_EXPECTED_STATUS = 200
|
14
|
+
|
15
|
+
# The default Content-type to use for requests with a body
|
16
|
+
DEFAULT_BODY_MIMETYPE = 'text/plain'.freeze
|
17
|
+
|
18
|
+
# The version of HTTP to use in the request line
|
19
|
+
DEFAULT_HTTP_VERSION = '2.0'
|
20
|
+
|
21
|
+
# The headers to include with the request
|
22
|
+
DEFAULT_HTTP_HEADERS = {
|
23
|
+
'Connection' => 'close',
|
24
|
+
'Accept' => '*/*',
|
25
|
+
'User-Agent' => "Arborist-WebService/%s" % [ Arborist::Webservice::VERSION ],
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# The SSL attributes that are settable
|
29
|
+
SSL_ATTRIBUTES = {
|
30
|
+
sslcert: "Client cert.",
|
31
|
+
sslcerttype: "Client cert type.",
|
32
|
+
sslkey: "Client key.",
|
33
|
+
sslkeytype: "Client key type.",
|
34
|
+
keypasswd: "Client key password.",
|
35
|
+
ssl_enable_alpn: "Enable use of ALPN.",
|
36
|
+
ssl_enable_npn: "Enable use of NPN.",
|
37
|
+
sslengine: "Use identifier with SSL engine.",
|
38
|
+
sslengine_default: "Default SSL engine.",
|
39
|
+
ssl_falsestart: "Enable TLS False Start.",
|
40
|
+
sslversion: "SSL version to use.",
|
41
|
+
ssl_verifyhost: "Verify the host name in the SSL certificate.",
|
42
|
+
ssl_verifypeer: "Verify the SSL certificate.",
|
43
|
+
ssl_verifystatus: "Verify the SSL certificate's status.",
|
44
|
+
cainfo: "CA cert bundle.",
|
45
|
+
issuercert: "Issuer certificate.",
|
46
|
+
capath: "Path to CA cert bundle.",
|
47
|
+
crlfile: "Certificate Revocation List.",
|
48
|
+
certinfo: "Extract certificate info.",
|
49
|
+
pinnedpublickey: "Set pinned SSL public key .",
|
50
|
+
random_file: "Provide source for entropy random data.",
|
51
|
+
egdsocket: "Identify EGD socket for entropy.",
|
52
|
+
ssl_cipher_list: "Ciphers to use.",
|
53
|
+
tls13_ciphers: "TLS 1.3 cipher suites to use.",
|
54
|
+
ssl_sessionid_cache: "Disable SSL session-id cache.",
|
55
|
+
ssl_options: "Control SSL behavior.",
|
56
|
+
krblevel: "Kerberos security level.",
|
57
|
+
gssapi_delegation: "Disable GSS-API delegation.",
|
58
|
+
proxy_sslcert: "Proxy client cert.",
|
59
|
+
proxy_sslcerttype: "Proxy client cert type.",
|
60
|
+
proxy_sslkey: "Proxy client key.",
|
61
|
+
proxy_sslkeytype: "Proxy client key type.",
|
62
|
+
proxy_keypasswd: "Proxy client key password.",
|
63
|
+
proxy_sslversion: "Proxy SSL version to use.",
|
64
|
+
proxy_ssl_verifyhost: "Verify the host name in the proxy SSL certificate.",
|
65
|
+
proxy_ssl_verifypeer: "Verify the proxy SSL certificate.",
|
66
|
+
proxy_cainfo: "Proxy CA cert bundle.",
|
67
|
+
proxy_capath: "Path to proxy CA cert bundle.",
|
68
|
+
proxy_crlfile: "Proxy Certificate Revocation List.",
|
69
|
+
proxy_pinnedpublickey: "Set the proxy's pinned SSL public key.",
|
70
|
+
proxy_ssl_cipher_list: "Proxy ciphers to use.",
|
71
|
+
proxy_tls13_ciphers: "Proxy TLS 1.3 cipher suites to use.",
|
72
|
+
proxy_ssl_options: "Control proxy SSL behavior.",
|
73
|
+
}.freeze
|
74
|
+
|
75
|
+
|
76
|
+
end # module Arborist::Webservice::Constants
|
@@ -0,0 +1,169 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
|
5
|
+
require 'typhoeus'
|
6
|
+
require 'arborist/webservice'
|
7
|
+
|
8
|
+
|
9
|
+
describe Arborist::Monitor::Webservice do
|
10
|
+
|
11
|
+
using Arborist::TimeRefinements
|
12
|
+
|
13
|
+
before( :each ) do
|
14
|
+
Typhoeus::Expectation.clear
|
15
|
+
end
|
16
|
+
after( :each ) do
|
17
|
+
Typhoeus::Expectation.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
describe Arborist::Monitor::Webservice::HTTP do
|
22
|
+
|
23
|
+
let( :monitor ) { described_class.new }
|
24
|
+
let( :host_node ) do
|
25
|
+
Arborist::Node.create( :host, 'webserver' ) do
|
26
|
+
description "Test host node with a few web services"
|
27
|
+
address '10.2.18.64'
|
28
|
+
tags :testing
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
let( :webservice_node1 ) { host_node.webservice('marketing', 'https://www.acme.com/') }
|
33
|
+
let( :webservice_node2 ) { host_node.webservice('store', 'https://store.acme.com/') }
|
34
|
+
let( :webservice_node3 ) { host_node.webservice('support', 'https://int.support.acme.com/') }
|
35
|
+
|
36
|
+
let( :nodes ) {[ webservice_node1, webservice_node2, webservice_node3 ]}
|
37
|
+
let( :nodes_hash ) do
|
38
|
+
nodes.each_with_object({}) do |node, accum|
|
39
|
+
accum[ node.identifier ] = node.fetch_values
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it "is created with a default timeout" do
|
45
|
+
expect( monitor.timeout ).to be_an( Numeric )
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
it "can clone itself with a new timeout" do
|
50
|
+
new_monitor = monitor.with_timeout( 2.minutes )
|
51
|
+
expect( new_monitor ).to_not equal( monitor )
|
52
|
+
expect( new_monitor.timeout ).to eq( 2.minutes )
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
it "runs against a collection of nodes and updates the statuses of each one" do
|
57
|
+
Typhoeus.stub( /acme/i ).and_return do |req|
|
58
|
+
Typhoeus::Response.new( code: 200, body: "OK" )
|
59
|
+
end
|
60
|
+
|
61
|
+
result = monitor.run( nodes_hash )
|
62
|
+
|
63
|
+
expect( result ).to be_a( Hash )
|
64
|
+
expect( result.keys ).to contain_exactly( *nodes.map(&:identifier) )
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
it "sets an error for HTTP error response statuses" do
|
69
|
+
Typhoeus.stub( /support\.acme/i ).and_return do |req|
|
70
|
+
Typhoeus::Response.new( code: 500, status_message: 'Server Error' )
|
71
|
+
end
|
72
|
+
Typhoeus.stub( /(www|store)\.acme/i ).and_return do |req|
|
73
|
+
Typhoeus::Response.new( code: 200, body: "OK" )
|
74
|
+
end
|
75
|
+
|
76
|
+
result = monitor.run( nodes_hash )
|
77
|
+
|
78
|
+
expect( result[webservice_node3.identifier] ).to include( error: '500 Server Error' )
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
it "sets a human-readable error message for lower-layer error response statuses" do
|
83
|
+
Typhoeus.stub( /store\.acme/i ).and_return do |req|
|
84
|
+
Typhoeus::Response.new( code: 0, return_code: :couldnt_connect )
|
85
|
+
end
|
86
|
+
Typhoeus.stub( /(www|support)\.acme/i ).and_return do |req|
|
87
|
+
Typhoeus::Response.new( code: 200, body: "OK" )
|
88
|
+
end
|
89
|
+
|
90
|
+
result = monitor.run( nodes_hash )
|
91
|
+
|
92
|
+
expect( result[webservice_node2.identifier] ).to include( error: "Couldn't connect to server" )
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
it "sets an error for timeouts" do
|
97
|
+
Typhoeus.stub( /store\.acme/i ).and_return do |req|
|
98
|
+
Typhoeus::Response.new( code: 0, return_code: :operation_timedout )
|
99
|
+
end
|
100
|
+
Typhoeus.stub( /(www|support)\.acme/i ).and_return do |req|
|
101
|
+
Typhoeus::Response.new( code: 200, body: "OK" )
|
102
|
+
end
|
103
|
+
|
104
|
+
result = monitor.run( nodes_hash )
|
105
|
+
|
106
|
+
expect( result[webservice_node2.identifier] ).to include( error: 'Request timed out.' )
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
it "doesn't error if HTTP error response status matches the expected status" do
|
111
|
+
Typhoeus.stub( /store\.acme/i ).and_return do |req|
|
112
|
+
Typhoeus::Response.new( code: 401, status_message: 'Access denied' )
|
113
|
+
end
|
114
|
+
Typhoeus.stub( /(www|support)\.acme/i ).and_return do |req|
|
115
|
+
Typhoeus::Response.new( code: 200, body: "OK" )
|
116
|
+
end
|
117
|
+
|
118
|
+
webservice_node2.expected_status( 401 )
|
119
|
+
result = monitor.run( nodes_hash )
|
120
|
+
|
121
|
+
expect( result[webservice_node2.identifier] ).to_not include( :error )
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
it "posts with the provided body if one is set" do
|
126
|
+
Typhoeus.stub( /support\.acme/i ).and_return do |req|
|
127
|
+
expect( req.options[:method] ).to eq( 'POST' )
|
128
|
+
expect( req.options[:body] ).to eq( '[1, 2, 3, 4]' )
|
129
|
+
expect( req.options[:headers] ).to include( 'Content-type' => 'application/json' )
|
130
|
+
Typhoeus::Response.new( code: 201, status_message: 'Object created' )
|
131
|
+
end
|
132
|
+
Typhoeus.stub( /(www|store)\.acme/i ).and_return do |req|
|
133
|
+
Typhoeus::Response.new( code: 200, body: "OK" )
|
134
|
+
end
|
135
|
+
|
136
|
+
webservice_node3.http_method( 'POST' )
|
137
|
+
webservice_node3.body( '[1, 2, 3, 4]' )
|
138
|
+
webservice_node3.body_mimetype( 'application/json' )
|
139
|
+
webservice_node3.expected_status( 201 )
|
140
|
+
|
141
|
+
result = monitor.run( nodes_hash )
|
142
|
+
|
143
|
+
expect( result.keys ).to contain_exactly( *nodes.map(&:identifier) )
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
it "uses valid entries from the node's config as SSL configuration" do
|
148
|
+
Typhoeus.stub( /acme/i ).and_return do |req|
|
149
|
+
expect( req.options[:ssl_verifypeer] ).to eq( 0 )
|
150
|
+
Typhoeus::Response.new( code: 200, body: "OK" )
|
151
|
+
end
|
152
|
+
|
153
|
+
nodes.each do |node|
|
154
|
+
node.config( ssl_verifypeer: 0 )
|
155
|
+
end
|
156
|
+
|
157
|
+
result = monitor.run( nodes_hash )
|
158
|
+
|
159
|
+
expect( result ).to be_a( Hash )
|
160
|
+
expect( result.keys ).to contain_exactly( *nodes.map(&:identifier) )
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
describe "REST monitor logic"
|
167
|
+
|
168
|
+
end
|
169
|
+
|
@@ -4,21 +4,71 @@
|
|
4
4
|
require_relative '../../spec_helper'
|
5
5
|
|
6
6
|
require 'rspec'
|
7
|
+
require 'arborist/node/host'
|
7
8
|
require 'arborist/node/webservice'
|
8
9
|
|
9
10
|
describe Arborist::Node::Webservice do
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
let( :host_node ) do
|
13
|
+
Arborist::Host 'testhost' do
|
14
|
+
address '192.168.66.12'
|
15
|
+
address '10.2.12.68'
|
16
|
+
hostname 'example.com'
|
17
|
+
end
|
18
|
+
end
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
let( :node ) { host_node.webservice('api', 'https://example.com/api/v1') }
|
21
|
+
let( :getonly_node ) do
|
22
|
+
host_node.webservice( 'repo-api', 'https://repo.example.com/' ) do
|
23
|
+
http_method 'GET'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
let( :http10_node ) do
|
27
|
+
host_node.webservice( 'archive-site', 'http://archive.example.com/' ) do
|
28
|
+
http_version '1.0'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
it "can match on URI" do
|
34
|
+
expect( node ).to match_criteria( uri: 'https://example.com/api/v1' )
|
35
|
+
expect( node ).to_not match_criteria( uri: 'https://example.com/api/v2' )
|
36
|
+
expect( node ).to_not match_criteria( uri: 'https://bitter.com/api/v1' )
|
37
|
+
expect( node ).to_not match_criteria( uri: 'http://example.com/api/v1' )
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
it "can match on HTTP method" do
|
42
|
+
expect( node ).to match_criteria( http_method: 'HEAD' )
|
43
|
+
expect( node ).to_not match_criteria( http_method: 'GET' )
|
44
|
+
expect( getonly_node ).to match_criteria( http_method: 'GET' )
|
45
|
+
expect( getonly_node ).to_not match_criteria( http_method: 'HEAD' )
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
it "can match on HTTP version" do
|
50
|
+
expect( node ).to match_criteria( http_version: '2.0' )
|
51
|
+
expect( node ).to_not match_criteria( http_version: '1.0' )
|
52
|
+
expect( http10_node ).to match_criteria( http_version: '1.0' )
|
53
|
+
expect( http10_node ).to_not match_criteria( http_version: '1.1' )
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
it "includes the URI in the operational attributes" do
|
58
|
+
expect( node.operational_values ).to include( uri: node.uri )
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
it "includes the HTTP method in the operational attributes" do
|
63
|
+
expect( node.operational_values ).to include( http_method: node.http_method )
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
it "allows shorthand URI syntax for implicit host" do
|
68
|
+
node = host_node.webservice( 'djinn', 'http:///' )
|
69
|
+
expect( node.uri ).to eq( 'http://192.168.66.12/' )
|
70
|
+
end
|
20
71
|
|
21
|
-
it "provides a DSL declaration to disable SSL verification"
|
22
72
|
|
23
73
|
end
|
24
74
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'arborist/webservice'
|
6
|
+
|
7
|
+
|
8
|
+
describe Arborist::Webservice do
|
9
|
+
|
10
|
+
it "has a version constant" do
|
11
|
+
expect( described_class::VERSION ).to match( /^\d+(\.\d+){2}$/ )
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
it "can return a version string for itself" do
|
16
|
+
expect( described_class.version_string ).
|
17
|
+
to match( /Arborist::Webservice v\d+\.\d+\.\d+/ )
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
it "can return a version string with the build ID" do
|
22
|
+
expect( described_class.version_string(include_build: true) ).
|
23
|
+
to match( /Arborist::Webservice v\d+\.\d+\.\d+.*\(Revision: \w+\)/ )
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -6,6 +6,17 @@ require 'simplecov' if ENV['COVERAGE']
|
|
6
6
|
require 'rspec'
|
7
7
|
|
8
8
|
require 'loggability/spechelpers'
|
9
|
+
require 'arborist'
|
10
|
+
require 'arborist/mixins'
|
11
|
+
require 'arborist/webservice'
|
12
|
+
|
13
|
+
|
14
|
+
RSpec::Matchers.define( :match_criteria ) do |criteria|
|
15
|
+
match do |node|
|
16
|
+
criteria = Arborist::HashUtilities.stringify_keys( criteria )
|
17
|
+
node.matches?( criteria )
|
18
|
+
end
|
19
|
+
end
|
9
20
|
|
10
21
|
|
11
22
|
### Mock with RSpec
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arborist-webservice
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Granger
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
X0qdrKi+2aZZ0NGuFj9AItBsVmAvkBGIpX4TEKQp5haEbPpmaqO5nIIhV26PXmyT
|
36
36
|
OMKv6pWsoS81vw5KAGBmfX8nht/Py90DQrbRvakATGI=
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2018-08-
|
38
|
+
date: 2018-08-31 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: arborist
|
@@ -43,28 +43,28 @@ dependencies:
|
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '0'
|
46
|
+
version: '0.2'
|
47
47
|
type: :runtime
|
48
48
|
prerelease: false
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '0'
|
53
|
+
version: '0.2'
|
54
54
|
- !ruby/object:Gem::Dependency
|
55
55
|
name: loggability
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: '0.
|
60
|
+
version: '0.14'
|
61
61
|
type: :runtime
|
62
62
|
prerelease: false
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: '0.
|
67
|
+
version: '0.14'
|
68
68
|
- !ruby/object:Gem::Dependency
|
69
69
|
name: typhoeus
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
@@ -141,14 +141,14 @@ dependencies:
|
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: '0.
|
144
|
+
version: '0.4'
|
145
145
|
type: :development
|
146
146
|
prerelease: false
|
147
147
|
version_requirements: !ruby/object:Gem::Requirement
|
148
148
|
requirements:
|
149
149
|
- - "~>"
|
150
150
|
- !ruby/object:Gem::Version
|
151
|
-
version: '0.
|
151
|
+
version: '0.4'
|
152
152
|
- !ruby/object:Gem::Dependency
|
153
153
|
name: rdoc
|
154
154
|
requirement: !ruby/object:Gem::Requirement
|
@@ -202,7 +202,10 @@ files:
|
|
202
202
|
- lib/arborist/monitor/webservice.rb
|
203
203
|
- lib/arborist/node/webservice.rb
|
204
204
|
- lib/arborist/webservice.rb
|
205
|
+
- lib/arborist/webservice/constants.rb
|
206
|
+
- spec/arborist/monitor/webservice_spec.rb
|
205
207
|
- spec/arborist/node/webservice_spec.rb
|
208
|
+
- spec/arborist/webservice_spec.rb
|
206
209
|
- spec/spec_helper.rb
|
207
210
|
homepage: http://deveiate.org/projects/arborist-webservice
|
208
211
|
licenses:
|
@@ -221,9 +224,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
221
224
|
version: '0'
|
222
225
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
223
226
|
requirements:
|
224
|
-
- - "
|
227
|
+
- - ">="
|
225
228
|
- !ruby/object:Gem::Version
|
226
|
-
version:
|
229
|
+
version: '0'
|
227
230
|
requirements: []
|
228
231
|
rubyforge_project:
|
229
232
|
rubygems_version: 2.7.6
|
metadata.gz.sig
ADDED
Binary file
|