scooter 3.2.19 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -83,7 +83,7 @@ module Scooter
83
83
  if !host.is_a?(Unix::Host)
84
84
  raise 'Can only acquire SSL certs if the host is a Unix::Host'
85
85
  end
86
- acquire_ca_cert(host)
86
+ acquire_ca_cert(host)
87
87
  end
88
88
  end
89
89
 
@@ -93,28 +93,14 @@ module Scooter
93
93
  request.body = "username=#{login}&password=#{CGI.escape(password)}"
94
94
  connection.port = 443
95
95
  end
96
- #return the response if the status code was not 200
97
96
  return response if response.status != 200
98
- # try to be helpful and acquire the xcsrf; catch any error that occurs
99
- # in the acquire_xcsrf method
100
- begin
101
- acquire_xcsrf
102
- rescue
103
- # do nothing in the rescue
104
- end
97
+ # This just had to be a string...*sigh*
98
+ header_array = response.headers['Set-Cookie'].split(';')
99
+ pl_ssti = header_array.select{|s| s =~ /pl_ssti/}
100
+ pl_ssti_value = pl_ssti[0].partition('pl_ssti=').last
101
+ @connection.headers['Cookie'] = response.headers['Set-Cookie']
102
+ @connection.headers['X-Authentication'] = pl_ssti_value
105
103
  # Reset the connection port, since we have to hardcode it to 443 signin
106
- # here
107
- set_url_prefix
108
- end
109
-
110
- def acquire_xcsrf
111
- # This simply makes a call to the base_uri and extracts out an
112
- # anti-forgery-token and adds that token to the headers for the
113
- # connection object
114
- response_body = @connection.get('/') { |req| connection.port = 443 }.env.body
115
- parsed_body = Nokogiri::HTML(response_body)
116
- token = parsed_body.css("meta[name='__anti-forgery-token']")[0].attributes['content'].value
117
- @connection.headers['X-CSRF-Token'] = token
118
104
  set_url_prefix
119
105
  end
120
106
 
@@ -11,10 +11,10 @@ module Scooter
11
11
  # low level functionality. Otherwise, the primary function of this class
12
12
  # is to allow more specific Dispatchers, such as the ConsoleDispatcher, to
13
13
  # extend it and write higher level functionality.
14
- class HttpDispatcher
14
+ class HttpDispatcher < Beaker::Http::Connection
15
15
 
16
16
 
17
- attr_accessor :connection, :host, :ssl, :token, :send_auth_token_as_query_param, :faraday_logger
17
+ attr_accessor :connection, :host, :token, :send_auth_token_as_query_param, :faraday_logger
18
18
  # The only required parameter for the HttpDispatcher is the host, which
19
19
  # could either be a beaker Unix::Host or a String. HttpDispatchers offer
20
20
  # support for automatically generating the required SSL components for the
@@ -30,17 +30,20 @@ module Scooter
30
30
  # @param log_level(Int) The desired log level
31
31
  # @param log_body(Boolean) Whether to log the body of responses
32
32
  def initialize(host, log_level=Logger::DEBUG, log_body=true)
33
+ @connection = create_default_connection(host.options, log_body)
33
34
  @log_body = log_body
34
- @ssl = {}
35
35
  @host = host
36
- if @host.is_a?(Unix::Host)
37
- @connection = create_default_connection_with_beaker_host
38
- elsif @host.is_a?(String)
39
- @connection = create_default_connection(log_level)
40
- set_url_prefix
41
- else
42
- raise "Argument host must be Unix::Host or String"
36
+ configure_private_key_and_cert_with_puppet(host)
37
+
38
+ set_url_prefix
39
+ # In this conditional, if we are unable to resolve the hostname, we get the public IP address;
40
+ # because public IP addresses will fail ssl verification, we explicitly turn that off. There
41
+ # should be a better solution, but this has worked so far...
42
+ if !is_resolvable
43
+ connection.url_prefix.host = Scooter::Utilities::BeakerUtilities.get_public_ip(host)
44
+ connection.ssl['verify'] = false
43
45
  end
46
+
44
47
  # The http-cookie library that the cookie-jar wraps requires that a
45
48
  # URI object be specifically a URI::HTTPS object. This changes the
46
49
  # default url_prefix in Faraday to be sub-classed from HTTPS, not plain
@@ -49,20 +52,7 @@ module Scooter
49
52
  @connection.url_prefix = URI.parse(@connection.url_prefix.to_s)
50
53
  end
51
54
 
52
- def initialize_connection
53
- if @host.is_a?(Unix::Host)
54
- @connection = create_default_connection_with_beaker_host
55
- elsif @host.is_a?(String)
56
- @connection = create_default_connection
57
- set_url_prefix
58
- add_ssl_components_to_connection
59
- else
60
- raise "Argument host must be Unix::Host or String"
61
- end
62
-
63
- end
64
-
65
- def create_default_connection(log_level=Logger::DEBUG)
55
+ def create_default_connection(options, log_body)
66
56
  Faraday.new do |conn|
67
57
  conn.request :rbac_auth_token, self
68
58
  conn.request :json
@@ -70,10 +60,7 @@ module Scooter
70
60
  conn.response :follow_redirects
71
61
  conn.response :raise_error
72
62
  conn.response :json, :content_type => /\bjson$/
73
- @faraday_logger ||= Logger.new $stderr
74
- # If log level is not set by Beaker, set faraday log level to debug.
75
- @faraday_logger.level ||= log_level if defined? @faraday_logger.level
76
- conn.response :logger, @faraday_logger, bodies: @log_body
63
+ conn.response :faraday_beaker_logger, options[:logger], { :bodies => log_body }
77
64
 
78
65
  conn.use :cookie_jar
79
66
 
@@ -89,21 +76,6 @@ module Scooter
89
76
  end
90
77
  end
91
78
 
92
- def create_default_connection_with_beaker_host
93
- if host.logger
94
- log_level = Logger::ERROR if host.logger.log_level == :error
95
- log_level = Logger.WARN if host.logger.log_level == :warn
96
- log_level = Logger::INFO if (host.logger.log_level == :info || host.logger.log_level == :notify)
97
- log_level ||= Logger::DEBUG
98
- end
99
-
100
- connection = create_default_connection(log_level)
101
- set_url_prefix(connection)
102
- acquire_ssl_components if ssl.empty?
103
- add_ssl_components_to_connection(connection)
104
- connection
105
- end
106
-
107
79
  # See if we can reach the host by hostname
108
80
  def is_resolvable(host=self.host)
109
81
  begin
@@ -113,56 +85,6 @@ module Scooter
113
85
  false
114
86
  end
115
87
  end
116
-
117
- # If you would like to run tests that expect 400 or even 500 responses,
118
- # apply this method to remove the <tt>:raise_error</tt> middleware.
119
- def remove_error_checking(connection=self.connection)
120
- connection.builder.delete(Faraday::Response::RaiseError)
121
- end
122
-
123
- def acquire_ssl_components(host=self.host)
124
- if !host.is_a?(Unix::Host)
125
- raise 'Can only acquire SSL certs if the host is a Unix::Host'
126
- end
127
- acquire_ca_cert(host)
128
- acquire_cert_and_key(host)
129
- end
130
-
131
- def acquire_ca_cert(host=self.host)
132
- @ssl['ca_file'] = Scooter::Utilities::BeakerUtilities.pe_ca_cert_file(host)
133
- end
134
-
135
- def acquire_cert_and_key(host=self.host)
136
- client_key = Scooter::Utilities::BeakerUtilities.pe_private_key(host)
137
- client_cert = Scooter::Utilities::BeakerUtilities.pe_hostcert(host)
138
- @ssl['client_key'] = OpenSSL::PKey.read(client_key)
139
- @ssl['client_cert'] = OpenSSL::X509::Certificate.new(client_cert)
140
- end
141
-
142
- def add_ssl_components_to_connection(connection=self.connection)
143
- # return immediately if the ssl object is empty
144
- if ssl.empty?
145
- warn 'no ssl keys defined, the connection object will not be modified'
146
- return
147
- end
148
- # enforce the scheme to be https, since we are adding ssl components to
149
- # the connection object
150
- connection.url_prefix.scheme = 'https'
151
-
152
- @ssl.each do |k, v|
153
- connection.ssl[k] = v
154
- end
155
-
156
- if host.is_a?(Unix::Host)
157
- if connection.url_prefix.host == Scooter::Utilities::BeakerUtilities.get_public_ip(host) && connection.ssl['verify'] == nil
158
- # Because we are connecting to the dashboard by IP address, SSL verification
159
- # against the CA will fail. Disable verifying against it for now until a better
160
- # fix can be found.
161
- connection.ssl['verify'] = false
162
- end
163
- end
164
- end
165
-
166
88
  end
167
89
  end
168
90
  end
@@ -10,9 +10,13 @@ module Scooter
10
10
  end
11
11
 
12
12
  #jobs endpoints
13
- def get_last_jobs(n_jobs)
14
- @connection.get("#{@version}/jobs") do |req|
15
- req.body = {:limit => n_jobs} if n_jobs
13
+ def get_last_jobs(limit=nil, offset=nil, order_by=nil, order=nil)
14
+ path = "#{@version}/jobs"
15
+ @connection.get(path) do |request|
16
+ request.params['limit'] = limit if limit
17
+ request.params['offset'] = offset if offset
18
+ request.params['order_by'] = order_by if order_by
19
+ request.params['order'] = order if order
16
20
  end
17
21
  end
18
22
 
@@ -33,6 +37,10 @@ module Scooter
33
37
  end
34
38
 
35
39
  #environments endpoints
40
+ def get_environments
41
+ @connection.get("v1/environments")
42
+ end
43
+
36
44
  def get_environment(environment)
37
45
  @connection.get("#{@version}/environments/#{environment}")
38
46
  end
@@ -14,62 +14,28 @@ module Scooter
14
14
  connection.url_prefix.port = 8081
15
15
  end
16
16
 
17
- # Used to compare replica puppetdb to master. Raises exception if it does not match.
18
- # @param [BeakerHost] host_name
19
- def database_matches_self?(host_name)
20
- original_host_name = self.host
21
- begin
22
- self.host = host_name
23
- initialize_connection
24
- other_nodes = query_nodes.body
25
- other_catalogs = query_catalogs.body
26
- other_facts = query_facts.body
27
- other_reports = query_reports.body
28
- ensure
29
- self.host = original_host_name
30
- initialize_connection
31
- end
32
-
33
- self_nodes = query_nodes.body
34
- self_catalogs = query_catalogs.body
35
- self_facts = query_facts.body
36
- self_reports = query_reports.body
37
-
38
- nodes_match = nodes_match?(other_nodes, self_nodes)
39
- catalogs_match = catalogs_match?(other_catalogs, self_catalogs)
40
- facts_match = facts_match?(other_facts, self_facts)
41
- reports_match = reports_match?(other_reports, self_reports)
42
-
43
- errors = ''
44
- errors << "Nodes do not match\r\n" unless nodes_match
45
- errors << "Catalogs do not match\r\n" unless catalogs_match
46
- errors << "Facts do not match\r\n" unless facts_match
47
- errors << "Reports do not match\r\n" unless reports_match
48
-
49
- @faraday_logger.warn(errors.chomp) unless errors.empty?
50
- errors.empty?
51
- end
52
-
53
17
  # Compares Replica PuppetDB with Master PuppetDB, to make sure Master PuppetDB has synced to Replica PuppetDB.
54
18
  #
55
19
  # N.B.: this uses a weird definition of "synced". We're NOT making sure the two PuppetDBs are exactly the same.
56
20
  # We're just checking that the replica DB doesn't contain any records that aren't also in the master, and that
57
21
  # the replica has at least one report from each node. We do this because there's a race condition-y window where
58
22
  # an agent may have delivered a report to the Master PuppetDB, but the Replica PuppetDB hasn't picked it up yet.
59
- # @param [BeakerHost] replica_host_name
23
+ # @param [BeakerHost] replica_host
60
24
  # @param [Array] agents all the agents in the SUT, in the form of BeakerHost instances
61
- def replica_db_synced_with_master_db?(replica_host_name, agents)
62
- master_host_name = self.host
25
+ def replica_db_synced_with_master_db?(replica_host, agents)
26
+ # Save a beaker host_hash[:vmhostname], set it to the supplied host_name param,
27
+ # and then set it back to the original at the end of the ensure. The :vmhostname
28
+ #overrides the host.hostname, and nothing should win out over it.
29
+ original_host_name = host.host_hash[:vmhostname]
63
30
  begin
64
- self.host = replica_host_name
65
- initialize_connection
31
+ host.host_hash[:vmhostname] = replica_host.hostname
32
+
66
33
  replica_nodes = query_nodes.body
67
34
  replica_catalogs = query_catalogs.body
68
35
  replica_facts = query_facts.body
69
36
  replica_reports = query_reports.body
70
37
  ensure
71
- self.host = master_host_name
72
- initialize_connection
38
+ host.host_hash[:vmhostname] = original_host_name
73
39
  end
74
40
  master_nodes = query_nodes.body
75
41
  master_catalogs = query_catalogs.body
@@ -87,7 +53,7 @@ module Scooter
87
53
  errors << "Facts not synced\r\n" unless facts_synced
88
54
  errors << "Reports not synced\r\n" unless reports_synced
89
55
 
90
- @faraday_logger.warn(errors.chomp) unless errors.empty?
56
+ host.logger.warn(errors.chomp) unless errors.empty?
91
57
  errors.empty?
92
58
  end
93
59
 
@@ -289,7 +255,7 @@ module Scooter
289
255
  master_facts.each do |master_fact|
290
256
  return true if ['certname', 'name', 'environment'].all? { |key| replica_fact[key] == master_fact[key] }
291
257
  end
292
- @faraday_logger.warn("*** fact sync failure: no Master fact matches Replica fact: #{replica_fact}")
258
+ host.logger.warn("*** fact sync failure: no Master fact matches Replica fact: #{replica_fact}")
293
259
  false
294
260
  end
295
261
 
@@ -321,7 +287,7 @@ module Scooter
321
287
  # @return [Boolean]
322
288
  def master_has_catalog?(replica_catalog, master_catalogs)
323
289
  master_catalogs.each { |master_catalog| return true if replica_catalog['certname'] == master_catalog['certname'] }
324
- @faraday_logger.warn("master doesn't have catalog with hash '#{replica_catalog['certname']}', which is on replica")
290
+ host.logger.warn("master doesn't have catalog with hash '#{replica_catalog['certname']}', which is on replica")
325
291
  false
326
292
  end
327
293
 
@@ -337,7 +303,7 @@ module Scooter
337
303
  # @return [Boolean]
338
304
  def master_has_node?(replica_node, master_nodes)
339
305
  master_nodes.each { |master_node| return true if replica_node['certname'] == master_node['certname'] }
340
- @faraday_logger.warn("master doesn't have node with certname '#{replica_node['certname']}', which is on replica")
306
+ host.logger.warn("master doesn't have node with certname '#{replica_node['certname']}', which is on replica")
341
307
  false
342
308
  end
343
309
 
@@ -352,7 +318,7 @@ module Scooter
352
318
  same_contents = same_contents?(replica_report, master_report, keys_with_expected_diffs)
353
319
  return true if same_hash && same_contents
354
320
  end
355
- @faraday_logger.warn("master doesn't have report with hash '#{replica_report['hash']}', which is on replica")
321
+ host.logger.warn("master doesn't have report with hash '#{replica_report['hash']}', which is on replica")
356
322
  false
357
323
  end
358
324
 
@@ -362,7 +328,7 @@ module Scooter
362
328
  # @return [Boolean]
363
329
  def replica_has_node_for_agent?(replica_nodes, agent)
364
330
  replica_nodes.each { |replica_node| return true if replica_node['certname'] == agent.hostname }
365
- @faraday_logger.warn("replica doesn't have any nodes for certname '#{agent.hostname}'")
331
+ host.logger.warn("replica doesn't have any nodes for certname '#{agent.hostname}'")
366
332
  false
367
333
  end
368
334
 
@@ -372,7 +338,7 @@ module Scooter
372
338
  # @return [Boolean]
373
339
  def replica_has_report_for_agent?(replica_reports, agent)
374
340
  replica_reports.each { |replica_report| return true if replica_report['certname'] == agent.hostname }
375
- @faraday_logger.warn("replica doesn't have any reports for certname '#{agent.hostname}'")
341
+ host.logger.warn("replica doesn't have any reports for certname '#{agent.hostname}'")
376
342
  false
377
343
  end
378
344
 
@@ -382,7 +348,7 @@ module Scooter
382
348
  # @return [Boolean]
383
349
  def replica_has_catalog_for_agent?(replica_catalogs, agent)
384
350
  replica_catalogs.each { |replica_catalog| return true if replica_catalog['certname'] == agent.hostname }
385
- @faraday_logger.warn("replica doesn't have any catalogs for certname '#{agent.hostname}'")
351
+ host.logger.warn("replica doesn't have any catalogs for certname '#{agent.hostname}'")
386
352
  false
387
353
  end
388
354
  end
@@ -170,17 +170,19 @@ module Scooter
170
170
  @token = acquire_token(credentials.login, credentials.password, lifetime)
171
171
  end
172
172
 
173
- def rbac_database_matches_self?(host_name)
174
- original_host_name = self.host
173
+ def rbac_database_matches_self?(replica_host)
174
+ # Save a beaker host_hash[:vmhostname], set it to the supplied host_name param,
175
+ # and then set it back to the original at the end of the ensure. The :vmhostname
176
+ #overrides the host.hostname, and nothing should win out over it.
177
+ original_host_name = host.host_hash[:vmhostname]
175
178
  begin
176
- self.host = host_name.to_s
177
- initialize_connection
179
+ host.host_hash[:vmhostname] = replica_host.hostname
180
+
178
181
  other_users = get_list_of_users
179
182
  other_groups = get_list_of_groups
180
183
  other_roles = get_list_of_roles
181
184
  ensure
182
- self.host = original_host_name
183
- initialize_connection
185
+ host.host_hash[:vmhostname] = original_host_name
184
186
  end
185
187
 
186
188
  self_users = get_list_of_users
@@ -192,7 +194,7 @@ module Scooter
192
194
  errors << "Groups do not match\r\n" unless groups_match?(self_groups, other_groups)
193
195
  errors << "Roles do not match\r\n" unless roles_match?(self_roles, other_roles)
194
196
 
195
- @faraday_logger.warn(errors.chomp) unless errors.empty?
197
+ host.logger.warn(errors.chomp) unless errors.empty?
196
198
  errors.empty?
197
199
  end
198
200
 
@@ -1,5 +1,5 @@
1
1
  module Scooter
2
2
  module Version
3
- STRING = '3.2.19'
3
+ STRING = '4.1.0'
4
4
  end
5
5
  end
data/scooter.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'activesupport', '4.2.6'
30
30
 
31
31
  #Run time dependencies
32
+ spec.add_runtime_dependency 'beaker-http', '~> 0.1'
32
33
  spec.add_runtime_dependency 'json', '~> 1.8'
33
34
  spec.add_runtime_dependency 'test-unit'
34
35
  spec.add_runtime_dependency 'net-ldap', '~> 0.6', '>= 0.6.1', '<= 0.12.1'
@@ -36,5 +37,4 @@ Gem::Specification.new do |spec|
36
37
  spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9.1'
37
38
  spec.add_runtime_dependency 'faraday_middleware', '~> 0.9'
38
39
  spec.add_runtime_dependency 'faraday-cookie_jar', '~> 0.0', '>= 0.0.6'
39
- spec.add_runtime_dependency 'nokogiri', '~> 1.5', '>= 1.5.10'
40
40
  end
@@ -127,15 +127,22 @@ module Scooter
127
127
 
128
128
  context 'with a beaker host passed in' do
129
129
 
130
+ let(:logger) { double('logger')}
130
131
  unixhost = { roles: ['test_role'],
131
132
  'platform' => 'debian-7-x86_64' }
132
- let(:host) { Beaker::Host.create('test.com', unixhost, {}) }
133
+ let(:host) { Beaker::Host.create('test.com', unixhost, {:logger => logger}) }
134
+ let(:host2) { Beaker::Host.create('test2.com', unixhost, {:logger => logger}) }
133
135
  let(:credentials) { { login: 'Ziggy', password: 'Stardust' } }
134
136
 
135
137
  before do
136
- expect(Scooter::Utilities::BeakerUtilities).to receive(:pe_ca_cert_file).and_return('cert file')
137
- expect(Scooter::Utilities::BeakerUtilities).to receive(:get_public_ip).and_return('public_ip')
138
- expect(subject).not_to be_nil
138
+ allow_any_instance_of(Beaker::Http::FaradayBeakerLogger).to receive(:info) { true }
139
+ allow_any_instance_of(Beaker::Http::FaradayBeakerLogger).to receive(:debug) { true }
140
+ allow_any_instance_of(HttpDispatchers::ConsoleDispatcher).to receive(:configure_private_key_and_cert_with_puppet) { true }
141
+ # Since we mocked the cert configuration action, we need to fixup the url_prefix
142
+ # to be the proper scheme and object class, HTTPS instead of HTTP
143
+ subject.url_prefix.scheme = 'https'
144
+ subject.url_prefix = URI.parse(subject.connection.url_prefix.to_s)
145
+ expect(subject).to be_kind_of(HttpDispatchers::ConsoleDispatcher)
139
146
  end
140
147
 
141
148
  describe '.get_classifier_events' do
@@ -195,22 +202,23 @@ module Scooter
195
202
  subject.connection.builder.swap(index, Faraday::Adapter::Test) do |stub|
196
203
  stub.get('activity-api/v1/events?service_id=rbac') { |env| env[:url].to_s == "https://test.com:4433/activity-api/v1/events?service_id=rbac" ?
197
204
  [200, [], rbac_events] :
198
- [200, [], rbac_events.dup["commits"].push('another_array_item')] }
205
+ [200, [], rbac_events['commits'].dup.push('another_array_item')] }
199
206
  stub.get('activity-api/v1/events?service_id=classifier') { |env| env[:url].to_s == "https://test.com:4433/activity-api/v1/events?service_id=classifier" ?
200
207
  [200, [], classifier_events] :
201
208
  [200, [], classifier_events.dup["commits"].push('another_array_item')] }
202
209
  end
203
- expect(subject).to receive(:create_default_connection).with(any_args).twice.and_return(subject.connection)
204
- expect(Scooter::Utilities::BeakerUtilities).to receive(:get_public_ip).and_return('public_ip')
210
+ expect(subject).to receive(:is_resolvable).exactly(4).times.and_return(true)
211
+ # expect(subject).to receive(:create_default_connection).with(any_args).twice.and_return(subject.connection)
212
+ # expect(Scooter::Utilities::BeakerUtilities).to receive(:get_public_ip).and_return('public_ip')
205
213
  end
206
214
 
207
215
  it 'compare with self' do
208
- expect(subject.activity_database_matches_self?('test.com')).to be_truthy
216
+ expect(subject.activity_database_matches_self?(host)).to be_truthy
209
217
  end
210
218
 
211
219
  it 'compare with different' do
212
- expect(subject.faraday_logger).to receive(:warn).with /Rbac events do not match/
213
- expect(subject.activity_database_matches_self?('test2.com')).to be_falsey
220
+ expect(subject.host.logger).to receive(:warn).with /Rbac events do not match/
221
+ expect(subject.activity_database_matches_self?(host2)).to be_falsey
214
222
  end
215
223
  end
216
224
  end