topological_inventory-providers-common 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aafb37efd571f568a887c8c11b31bc87a6141a556ae06b970bf4acfcbdea3d08
4
- data.tar.gz: 2d1024891611a96df454a56819a5f6f921e195605d3867ca05c595c336f9a699
3
+ metadata.gz: 868c8f764cd11d9888ed99698a309cc199fdf92823963f4e5cae71208d506ff7
4
+ data.tar.gz: 39c352e8b1306bdb87675bf0ccc1fe08f02b0c13555b2466c4c38d7a76979aca
5
5
  SHA512:
6
- metadata.gz: daff9b2dc2388297dba831994c74e048d608256c9fd3aadc06446c16abfa4143f5fcbed2e6dd3eff66cb6fbb9e6637abe8f47c1eda452b99d5557c9424c20849
7
- data.tar.gz: ece193799a908c7ca96aee73de80e9a6dc7cf17346b549ebf2d802cd3ed42b11aec7b46837a4078c1569fe608ef0241f7ac64a7daf43bd2905df1d53e21c8e01
6
+ metadata.gz: c2a8af390114548a89c7dc5bb43f1a6e3784a5a59ebd2ce1c7451c15726015df1353e3dcbc3755d7637d696707b0d6db93e5d4907920a1c4b09d3f6410130a77
7
+ data.tar.gz: 46e6596954367336de4049f8024da8b5e63e5be4c60636c77f91f22c3e407229b38aca604137907d1f5cd6411201d9c3b57e4ad5bab2cfb749b2c04e9ca8eeab
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.0.7] - 2020-07-27
8
+ Update operations/source model for receptor-enabled availability checks #36
9
+ Add check for Application subresource under a Source during Availability check #40
10
+ Remove infinite loop in error messages #43
11
+
7
12
  ## [1.0.6] - 2020-07-06
8
13
  Add some error handling if Sources does not have endpoints/authentications for a source #38
9
14
  Specs for Collector #35
@@ -38,7 +43,8 @@ manageiq-loggers to >= 0.4.2 #20
38
43
  ## [1.0.0] - 2020-03-19
39
44
  ### Initial release to rubygems.org
40
45
 
41
- [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.6...HEAD
46
+ [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.7...HEAD
47
+ [1.0.6]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.6...v1.0.7
42
48
  [1.0.6]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.5...v1.0.6
43
49
  [1.0.5]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.4...v1.0.5
44
50
  [1.0.4]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.3...v1.0.4
@@ -12,18 +12,20 @@ module TopologicalInventory
12
12
  STATUS_AVAILABLE, STATUS_UNAVAILABLE = %w[available unavailable].freeze
13
13
 
14
14
  ERROR_MESSAGES = {
15
- :authentication_not_found => "Authentication not found in Sources API",
16
- :endpoint_not_found => "Endpoint not found in Sources API",
15
+ :authentication_not_found => "Authentication not found in Sources API",
16
+ :endpoint_or_application_not_found => "Endpoint or Application not found in Sources API",
17
17
  }.freeze
18
18
 
19
19
  LAST_CHECKED_AT_THRESHOLD = 5.minutes.freeze
20
+ AUTH_NOT_NECESSARY = "n/a".freeze
20
21
 
21
- attr_accessor :params, :request_context, :source_id
22
+ attr_accessor :params, :request_context, :source_id, :account_number
22
23
 
23
24
  def initialize(params = {}, request_context = nil)
24
25
  self.params = params
25
26
  self.request_context = request_context
26
27
  self.source_id = params['source_id']
28
+ self.account_number = params['external_tenant']
27
29
  end
28
30
 
29
31
  def availability_check
@@ -33,7 +35,7 @@ module TopologicalInventory
33
35
 
34
36
  status, error_message = connection_status
35
37
 
36
- update_source_and_endpoint(status, error_message)
38
+ update_source_and_subresources(status, error_message)
37
39
 
38
40
  logger.availability_check("Completed: Source #{source_id} is #{status}")
39
41
  end
@@ -57,32 +59,57 @@ module TopologicalInventory
57
59
  end
58
60
 
59
61
  def checked_recently?
60
- return false if endpoint.nil?
62
+ checked_recently = if endpoint.present?
63
+ endpoint.last_checked_at.present? && endpoint.last_checked_at >= LAST_CHECKED_AT_THRESHOLD.ago
64
+ elsif application.present?
65
+ application.last_checked_at.present? && application.last_checked_at >= LAST_CHECKED_AT_THRESHOLD.ago
66
+ end
61
67
 
62
- checked_recently = endpoint.last_checked_at.present? && endpoint.last_checked_at >= LAST_CHECKED_AT_THRESHOLD.ago
63
- logger.availability_check("Skipping, last check at #{endpoint.last_checked_at} [Source ID: #{source_id}] ") if checked_recently
68
+ logger.availability_check("Skipping, last check at #{endpoint.last_checked_at || application.last_checked_at} [Source ID: #{source_id}] ") if checked_recently
64
69
 
65
70
  checked_recently
66
71
  end
67
72
 
68
73
  def connection_status
69
- return [STATUS_UNAVAILABLE, ERROR_MESSAGES[:endpoint_not_found]] unless endpoint
70
- return [STATUS_UNAVAILABLE, ERROR_MESSAGES[:authentication_not_found]] unless authentication
74
+ # we need either an endpoint or application to check the source.
75
+ return [STATUS_UNAVAILABLE, ERROR_MESSAGES[:endpoint_or_application_not_found]] unless endpoint || application
71
76
 
72
77
  check_time
78
+ if endpoint
79
+ endpoint_connection_check
80
+ elsif application
81
+ application_connection_check
82
+ end
83
+ end
84
+
85
+ def endpoint_connection_check
86
+ return [STATUS_UNAVAILABLE, ERROR_MESSAGES[:authentication_not_found]] unless authentication
87
+
88
+ # call down into the operations pod implementation of `Source#connection_check`
73
89
  connection_check
74
90
  end
75
91
 
92
+ def application_connection_check
93
+ case application.availability_status
94
+ when "available"
95
+ [STATUS_AVAILABLE, nil]
96
+ when "unavailable"
97
+ [STATUS_UNAVAILABLE, "Application id #{application.id} unavailable"]
98
+ end
99
+ end
100
+
76
101
  # @return [Array<String, String|nil] - STATUS_[UN]AVAILABLE, error message
77
102
  def connection_check
78
103
  raise NotImplementedError, "#{__method__} must be implemented in a subclass"
79
104
  end
80
105
 
81
- def update_source_and_endpoint(status, error_message = nil)
106
+ def update_source_and_subresources(status, error_message = nil)
82
107
  logger.availability_check("Updating source [#{source_id}] status [#{status}] message [#{error_message}]")
83
108
 
84
109
  update_source(status)
85
- update_endpoint(status, error_message)
110
+
111
+ update_endpoint(status, error_message) if endpoint
112
+ update_application(status) if application
86
113
  end
87
114
 
88
115
  def update_source(status)
@@ -114,18 +141,36 @@ module TopologicalInventory
114
141
  logger.availability_check("Failed to update Endpoint(ID: #{endpoint.id}) - #{e.message}", :error)
115
142
  end
116
143
 
144
+ def update_application(status)
145
+ application_update = ::SourcesApiClient::Application.new
146
+ application_update.last_checked_at = check_time
147
+ application_update.last_available_at = check_time if status == STATUS_AVAILABLE
148
+
149
+ api_client.update_application(application.id, application_update)
150
+ rescue ::SourcesApiClient::ApiError => e
151
+ logger.availability_check("Failed to update Application id: #{application.id} - #{e.message}", :error)
152
+ end
153
+
117
154
  def endpoint
118
155
  @endpoint ||= api_client.fetch_default_endpoint(source_id)
119
- rescue StandardError => e
120
- logger.availability_check("Failed to fetch Endpoint for Source #{source_id}, Endpoint #{endpoint}: #{e.message}", :error)
121
- return
156
+ rescue e
157
+ logger.availability_check("Failed to fetch Endpoint for Source #{source_id}: #{e.message}", :error)
122
158
  end
123
159
 
124
160
  def authentication
125
- @authentication ||= api_client.fetch_authentication(source_id, endpoint)
126
- rescue StandardError => e
127
- logger.availability_check("Failed to fetch Authentication for Source #{source_id}, Endpoint #{endpoint}: #{e.message}", :error)
128
- return
161
+ @authentication ||= if endpoint.receptor_node.present?
162
+ AUTH_NOT_NECESSARY
163
+ else
164
+ api_client.fetch_authentication(source_id, endpoint)
165
+ end
166
+ rescue e
167
+ logger.availability_check("Failed to fetch Authentication for Source #{source_id}: #{e.message}", :error)
168
+ end
169
+
170
+ def application
171
+ @application ||= api_client.fetch_application(source_id)
172
+ rescue e
173
+ logger.availability_check("Failed to fetch Application for Source #{source_id}: #{e.message}", :error)
129
174
  end
130
175
 
131
176
  def check_time
@@ -133,7 +178,7 @@ module TopologicalInventory
133
178
  end
134
179
 
135
180
  def identity
136
- @identity ||= {"x-rh-identity" => Base64.strict_encode64({"identity" => {"account_number" => params["external_tenant"], "user" => {"is_org_admin" => true}}}.to_json)}
181
+ @identity ||= {"x-rh-identity" => Base64.strict_encode64({"identity" => {"account_number" => account_number, "user" => {"is_org_admin" => true}}}.to_json)}
137
182
  end
138
183
 
139
184
  def api_client
@@ -5,7 +5,7 @@ module TopologicalInventory
5
5
  module Common
6
6
  module Operations
7
7
  class SourcesApiClient < ::SourcesApiClient::ApiClient
8
- delegate :update_source, :update_endpoint, :to => :api
8
+ delegate :update_source, :update_endpoint, :update_application, :to => :api
9
9
 
10
10
  INTERNAL_API_PATH = '//internal/v1.0'.freeze
11
11
 
@@ -25,6 +25,11 @@ module TopologicalInventory
25
25
  endpoints.find(&:default)
26
26
  end
27
27
 
28
+ def fetch_application(source_id)
29
+ applications = api.list_source_applications(source_id)&.data || []
30
+ applications.first
31
+ end
32
+
28
33
  def fetch_authentication(source_id, default_endpoint = nil, authtype = nil)
29
34
  endpoint = default_endpoint || fetch_default_endpoint(source_id)
30
35
  return if endpoint.nil?
@@ -1,7 +1,7 @@
1
1
  module TopologicalInventory
2
2
  module Providers
3
3
  module Common
4
- VERSION = "1.0.6"
4
+ VERSION = "1.0.7"
5
5
  end
6
6
  end
7
7
  end
@@ -11,6 +11,7 @@ RSpec.shared_examples "availability_check" do
11
11
  let(:headers) { {'Content-Type' => 'application/json'}.merge(identity) }
12
12
  let(:source_id) { '123' }
13
13
  let(:endpoint_id) { '234' }
14
+ let(:application_id) { '345' }
14
15
  let(:authentication_id) { '345' }
15
16
  let(:payload) do
16
17
  {
@@ -26,6 +27,8 @@ RSpec.shared_examples "availability_check" do
26
27
  let(:list_endpoint_authentications_response) { "{\"data\":[{\"authtype\":\"username_password\",\"id\":\"#{authentication_id}\",\"resource_id\":\"#{endpoint_id}\",\"resource_type\":\"Endpoint\",\"username\":\"admin\",\"tenant\":\"#{external_tenant}\"}]}" }
27
28
  let(:list_endpoint_authentications_response_empty) { "{\"data\":[]}" }
28
29
  let(:internal_api_authentication_response) { "{\"authtype\":\"username_password\",\"id\":\"#{authentication_id}\",\"resource_id\":\"#{endpoint_id}\",\"resource_type\":\"Endpoint\",\"username\":\"admin\",\"tenant\":\"#{external_tenant}\",\"password\":\"xxx\"}" }
30
+ let(:list_applications_response) { {:data => [{:id => "345", :availability_status => "available"}]}.to_json }
31
+ let(:list_applications_unavailable_response) { {:data => [{:id => "345", :availability_status => "unavailable"}]}.to_json }
29
32
 
30
33
  subject { described_class.new(payload["params"]) }
31
34
 
@@ -39,6 +42,7 @@ RSpec.shared_examples "availability_check" do
39
42
  stub_get(:endpoint, list_endpoints_response)
40
43
  stub_get(:authentication, list_endpoint_authentications_response)
41
44
  stub_get(:password, internal_api_authentication_response)
45
+ stub_get(:application, "[]")
42
46
 
43
47
  # PATCH
44
48
  source_patch_body = {'availability_status' => described_class::STATUS_AVAILABLE, 'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
@@ -61,6 +65,7 @@ RSpec.shared_examples "availability_check" do
61
65
  stub_get(:endpoint, list_endpoints_response)
62
66
  stub_get(:authentication, list_endpoint_authentications_response)
63
67
  stub_get(:password, internal_api_authentication_response)
68
+ stub_get(:application, "[]")
64
69
 
65
70
  # PATCH
66
71
  connection_error_message = "Some connection error"
@@ -82,6 +87,7 @@ RSpec.shared_examples "availability_check" do
82
87
  it "updates only Source to 'unavailable' status if Endpoint not found" do
83
88
  # GET
84
89
  stub_get(:endpoint, '')
90
+ stub_get(:application, "[]")
85
91
 
86
92
  # PATCH
87
93
  source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
@@ -100,6 +106,7 @@ RSpec.shared_examples "availability_check" do
100
106
  # GET
101
107
  stub_get(:endpoint, list_endpoints_response)
102
108
  stub_get(:authentication, list_endpoint_authentications_response_empty)
109
+ stub_get(:application, "[]")
103
110
 
104
111
  # PATCH
105
112
  source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
@@ -131,6 +138,49 @@ RSpec.shared_examples "availability_check" do
131
138
  end
132
139
  end
133
140
 
141
+ context "when there is an application" do
142
+ context "when it is available" do
143
+ it "updates the availability status to available" do
144
+ # GET
145
+ stub_get(:endpoint, "[]")
146
+ stub_get(:application, list_applications_response)
147
+ # PATCH
148
+ application_patch_body = {'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
149
+ source_patch_body = {'availability_status' => described_class::STATUS_AVAILABLE, 'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
150
+
151
+ stub_patch(:source, source_patch_body)
152
+ stub_patch(:application, application_patch_body)
153
+
154
+ # Check
155
+ expect(subject).not_to receive(:connection_check)
156
+ subject.availability_check
157
+
158
+ assert_patch(:source, source_patch_body)
159
+ assert_patch(:application, application_patch_body)
160
+ end
161
+ end
162
+
163
+ context "when it is unavailable" do
164
+ it "updates the availability status to unavailable" do
165
+ # GET
166
+ stub_get(:endpoint, "[]")
167
+ stub_get(:application, list_applications_unavailable_response)
168
+ # PATCH
169
+ application_patch_body = {'last_checked_at' => subject.send(:check_time)}.to_json
170
+ source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
171
+
172
+ stub_patch(:source, source_patch_body)
173
+ stub_patch(:application, application_patch_body)
174
+
175
+ # Check
176
+ expect(subject).not_to receive(:connection_check)
177
+ subject.availability_check
178
+
179
+ assert_patch(:source, source_patch_body)
180
+ assert_patch(:application, application_patch_body)
181
+ end
182
+ end
183
+ end
134
184
 
135
185
  def stub_get(object_type, response)
136
186
  case object_type
@@ -146,6 +196,10 @@ RSpec.shared_examples "availability_check" do
146
196
  stub_request(:get, "#{host_url}#{sources_internal_api_path}/authentications/#{authentication_id}?expose_encrypted_attribute%5B%5D=password")
147
197
  .with(:headers => headers)
148
198
  .to_return(:status => 200, :body => response, :headers => {})
199
+ when :application
200
+ stub_request(:get, "#{sources_api_url}/sources/#{source_id}/applications")
201
+ .with(:headers => headers)
202
+ .to_return(:status => 200, :body => response, :headers => {})
149
203
  end
150
204
  end
151
205
 
@@ -159,6 +213,10 @@ RSpec.shared_examples "availability_check" do
159
213
  stub_request(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
160
214
  .with(:body => data, :headers => headers)
161
215
  .to_return(:status => 200, :body => "", :headers => {})
216
+ when :application
217
+ stub_request(:patch, "#{sources_api_url}/applications/#{application_id}")
218
+ .with(:body => data, :headers => headers)
219
+ .to_return(:status => 200, :body => "", :headers => {})
162
220
  end
163
221
  end
164
222
 
@@ -166,10 +224,13 @@ RSpec.shared_examples "availability_check" do
166
224
  case object_type
167
225
  when :source
168
226
  expect(WebMock).to have_requested(:patch, "#{sources_api_url}/sources/#{source_id}")
169
- .with(:body => data, :headers => headers).once
227
+ .with(:body => data, :headers => headers).once
170
228
  when :endpoint
171
229
  expect(WebMock).to have_requested(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
172
- .with(:body => data, :headers => headers).once
230
+ .with(:body => data, :headers => headers).once
231
+ when :application
232
+ expect(WebMock).to have_requested(:patch, "#{sources_api_url}/applications/#{application_id}")
233
+ .with(:body => data, :headers => headers).once
173
234
  end
174
235
  end
175
236
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: topological_inventory-providers-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Slemr
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-09 00:00:00.000000000 Z
11
+ date: 2020-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport