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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1006dfaf9b120fa2720a2637603c4f75397cbe36d62f2039c35d4a1f8c39b56
4
- data.tar.gz: 46b34fd363f89a60bcfa7cdafceeec8d2a68c00e9e0b79d2c5126b9fc4b47137
3
+ metadata.gz: 3d364d3021e618267b924682c39b011d365aa486c56a37c13842a23d71e4b4c6
4
+ data.tar.gz: b6028b714764befd0d89372432d6936037eaa0b5bd54d10f735c537903fbeb38
5
5
  SHA512:
6
- metadata.gz: 3d5b067c522b37beb004f549c98fba58ba7e64806a79baa05be708344ef9cc6801d982ec083bedfacbed75f6e30eb5fa4ef09ebf0b744f88272d032f1ea2dc9a
7
- data.tar.gz: 49457c9714ce9c128be66eb3e04a9f7bc280ebbf4c0359e2d3719e73415eb21ce62189eb07ff68e095f876f5b8b2dadf996b46dedf230d3084baa0aa4bf5d299
6
+ metadata.gz: f7b5ff725bd2e08a5737e5c2d0ceb8dfdff21aec1d30633cf08fa0c310ca82c01f1524c3bdb2ce472843ca002536311cbf1eca65d1e087bd2487ecfb156741ee
7
+ data.tar.gz: 1c023a87365bc5551a3ccb3f054e5c15a671f8bff6df0e66e88b8f83232d08524d0f4f07a94a791c19a033b67d8ff71eef437ecf730a4dbebefc08773472fcf4
@@ -0,0 +1,2 @@
1
+ �@9�]�TElMoˡ�|�g����c��R�����P����ڙ���u�k�j�U~���w�Ӿݘ��-�������!��B��u+����~mVG?�Fѯ�Q��ud�@� :0�R��L.����'�z���mrAs�(�]�`���r0 �o'M���ލ��\��Ð�Zp@9C�ͷ�3�bW8����_��~�ަ���#%����/:� �|��Z�������vs<���4��)A�P���WOE��@j$�[xs�2W
2
+ �� /0$����JY\�-2
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] [tip]
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] [github/master]
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
@@ -1,4 +1,4 @@
1
- ## v0.0.1 [YYYY-MM-DD] Michael Granger <ged@FaerieMUD.org>
1
+ ## v0.0.1 [2018-08-30] Michael Granger <ged@FaerieMUD.org>
2
2
 
3
3
  Initial release.
4
4
 
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
- `frontend-17-api-example-com-webservice`.
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::REST.default
41
+ Arborist::Monitor::Webservice::HTTP.default
42
42
 
43
- This would check that an OPTIONS HTTP request to `http://api.example.com/v1/heartbeat` responds with a 2xx status code.
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.11'
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.3', :developer
36
+ spec.dependency 'hoe-deveiate', '~> 0.10', :developer
37
37
  spec.dependency 'simplecov', '~> 0.7', :developer
38
- spec.dependency 'rdoc-generator-fivefish', '~> 0.1', :developer
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.rdoc"
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 HTML web service monitor logic
42
- class HTML
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 HTML webservice monitor with the specified +options+. Valid options are:
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 ] = self.make_response_results( response )
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: node_data['http_version'] || DEFAULT_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
- ssl_attributes = SSL_ATTRIBUTES.each_with_object({}) do |(key, ssl_key), opts|
141
- opts[ ssl_key ] = node_data[ key.to_s ] if node_data.key?( key.to_s )
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.success?
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 "Non-HTTP response: %s." % [ response.return_code ]
178
- return { error: response.return_code }
194
+ self.log.error( response.return_message )
195
+ return { error: response.return_message }
179
196
  else
180
- self.log.error "Got a %03d %s response." % [ response.code, response.status_message ]
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
- end # class HTML
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::HTML
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
- @uri = uri
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/1.1" % [
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
- it "can match on URI"
12
- it "can match on HTTP method"
13
- it "can match on request body"
14
- it "can match on request body media type"
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
- it "includes the URI in the operational attributes"
17
- it "includes the HTTP method in the operational attributes"
18
- it "includes the request body in the operational attributes"
19
- it "includes the request body media type in the operational attributes"
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
+
@@ -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.pre20180815100446
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-15 00:00:00.000000000 Z
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.11'
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.11'
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.1'
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.1'
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: 1.3.1
229
+ version: '0'
227
230
  requirements: []
228
231
  rubyforge_project:
229
232
  rubygems_version: 2.7.6
Binary file