topological_inventory-providers-common 1.0.12 → 2.0.0

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: b81c523169c3ca87e678e3e7d046b4daee863200cf4eadd695e9022d17a11e5a
4
- data.tar.gz: 1fc2a47cedf6f8d3b41f0df728a5ac8ceceaea03b58d43ef67da90680861c0eb
3
+ metadata.gz: 134e945b201072b8bdd7fc6af73b04a6f60698da831ef3989ab973453a1a5159
4
+ data.tar.gz: 20e4f260c411551572b557f4e4454fdc03c9e3b2ac9c8f5997e6600780298c1a
5
5
  SHA512:
6
- metadata.gz: 9afb8ba326d632ba59245d93837f32bd8edbcb34b3d82e029c351f904bb75f196592eb4724116a48caf0b7df8fc7f4101f5b59bd0e23b65d6fca25ada3ac4cb6
7
- data.tar.gz: '00924d04d1fc4623cd4e0f0a719c5471dbd39635820375b4957278db1c6e3ecbdf2b58c25758bca22e91e979511286708279922919f6303118401e58ac9cc5c4'
6
+ metadata.gz: 0a847566f6fb20e9518faef660c626788e6afd20d26cec28a5899e645c68cedd7c017974d0cb1c3c2524253007386b9728a1bb67a0f0a9101fe62bd388f03ccb
7
+ data.tar.gz: 6e786659b1bf913fe1e22d9c0618986d03dc4bc169d782e446ecd089b97a136b639f83be85c562006dccde83a8d025b102863b565aa571746767fa292e4dae89
@@ -4,17 +4,20 @@ 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
+ ## [2.0.0]
8
+ Operations/API clients refactoring
9
+
7
10
  ## [1.0.12] - 2020-10-01
8
11
  Add Operations Async Worker class #55
9
12
 
10
- ## [1.0.11]
13
+ ## [1.0.11] - 2020-09-04
11
14
  Make Collector Poll Time a parameter so we can tweak the collection interval #51
12
15
 
13
- ## [1.0.10]
16
+ ## [1.0.10] - 2020-08-26
14
17
  Add HealthCheck class for operations workers #48
15
18
  Set the LOG_LEVEL if present #50
16
19
 
17
- ## [1.0.9]
20
+ ## [1.0.9] - 2020-08-17
18
21
  Added refresh-type to save and sweep inventory #45
19
22
 
20
23
  ## [1.0.8] - 2020-08-12
@@ -59,7 +62,8 @@ manageiq-loggers to >= 0.4.2 #20
59
62
  ## [1.0.0] - 2020-03-19
60
63
  ### Initial release to rubygems.org
61
64
 
62
- [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.12...HEAD
65
+ [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.0.0...HEAD
66
+ [2.0.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.12...v2.0.0
63
67
  [1.0.12]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.11...v1.0.12
64
68
  [1.0.11]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.10...v1.0.11
65
69
  [1.0.10]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.9...v1.0.10
@@ -1,7 +1,5 @@
1
1
  require "topological_inventory/providers/common/version"
2
2
  require "topological_inventory/providers/common/logging"
3
- require "topological_inventory/providers/common/operations/processor"
4
- require "topological_inventory/providers/common/operations/endpoint_client"
5
3
  require "topological_inventory/providers/common/operations/health_check"
6
4
  require "topological_inventory/providers/common/collectors_pool"
7
5
  require "topological_inventory/providers/common/collector"
@@ -23,11 +23,14 @@ module TopologicalInventory
23
23
  end
24
24
 
25
25
  def availability_check(message, severity = :info)
26
- log_with_prefix("Source#availability_check", message, severity)
26
+ send("#{severity}_ext", "Source#availability_check", message)
27
27
  end
28
28
 
29
- def log_with_prefix(prefix, message, severity)
30
- send(severity, "#{prefix} - #{message}") if respond_to?(severity)
29
+ %w[debug info warn error fatal].each do |severity|
30
+ define_method("#{severity}_ext".to_sym) do |prefix, message|
31
+ ext_message = [prefix, message].compact.join(' - ')
32
+ send(severity, ext_message)
33
+ end
31
34
  end
32
35
  end
33
36
 
@@ -0,0 +1,58 @@
1
+ require "topological_inventory/providers/common/sources_api_client"
2
+
3
+ module TopologicalInventory
4
+ module Providers
5
+ module Common
6
+ module Mixins
7
+ module SourcesApi
8
+ AUTH_NOT_NECESSARY = "n/a".freeze
9
+
10
+ def sources_api
11
+ @sources_api ||= TopologicalInventory::Providers::Common::SourcesApiClient.new(identity)
12
+ end
13
+
14
+ def endpoint
15
+ @endpoint ||= sources_api.fetch_default_endpoint(source_id)
16
+ rescue => e
17
+ logger.error_ext(operation, "Failed to fetch Endpoint for Source #{source_id}: #{e.message}")
18
+ nil
19
+ end
20
+
21
+ def authentication
22
+ @authentication ||= if endpoint.receptor_node.present?
23
+ AUTH_NOT_NECESSARY
24
+ else
25
+ sources_api.fetch_authentication(source_id, endpoint)
26
+ end
27
+ rescue => e
28
+ logger.error_ext(operation, "Failed to fetch Authentication for Source #{source_id}: #{e.message}")
29
+ nil
30
+ end
31
+
32
+ def application
33
+ @application ||= sources_api.fetch_application(source_id)
34
+ rescue => e
35
+ logger.error_ext(operation, "Failed to fetch Application for Source #{source_id}: #{e.message}")
36
+ nil
37
+ end
38
+
39
+ def on_premise?
40
+ @on_premise ||= endpoint&.receptor_node.to_s.strip.present?
41
+ end
42
+
43
+ def verify_ssl_mode
44
+ endpoint&.verify_ssl ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
45
+ end
46
+
47
+ def full_hostname(endpoint)
48
+ if on_premise?
49
+ "receptor://#{endpoint.receptor_node}"
50
+ else
51
+ endpoint.host.tap { |host| host << ":#{endpoint.port}" if endpoint.port }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,26 @@
1
+ require "topological_inventory/providers/common/topology_api_client"
2
+
3
+ module TopologicalInventory
4
+ module Providers
5
+ module Common
6
+ module Mixins
7
+ module TopologyApi
8
+ # @identity attr_reader is expected
9
+ def topology_api
10
+ @topology_api ||= TopologicalInventory::Providers::Common::TopologyApiClient.new(identity)
11
+ end
12
+
13
+ def update_task(task_id, source_id: nil, state:, status:, target_type: nil, target_source_ref: nil, context: nil)
14
+ topology_api.update_task(task_id,
15
+ :source_id => source_id,
16
+ :state => state,
17
+ :status => status,
18
+ :target_type => target_type,
19
+ :target_source_ref => target_source_ref,
20
+ :context => context)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ module TopologicalInventory
2
+ module Providers
3
+ module Common
4
+ module Mixins
5
+ module XRhHeaders
6
+ def account_number_by_identity(identity)
7
+ return @account_number if @account_number
8
+ return if identity.try(:[], 'x-rh-identity').nil?
9
+
10
+ identity_hash = JSON.parse(Base64.decode64(identity['x-rh-identity']))
11
+ @account_number = identity_hash.dig('identity', 'account_number')
12
+ rescue JSON::ParserError => e
13
+ logger.error_ext(operation, "Failed to parse identity header: #{e.message}")
14
+ nil
15
+ end
16
+
17
+ def identity_by_account_number(account_number)
18
+ @identity ||= {"x-rh-identity" => Base64.strict_encode64({"identity" => {"account_number" => account_number, "user" => {"is_org_admin" => true}}}.to_json)}
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,194 +1,169 @@
1
1
  require "topological_inventory/providers/common/logging"
2
2
  require "active_support/core_ext/numeric/time"
3
- require "topological_inventory/providers/common/operations/sources_api_client"
3
+ require "topological_inventory/providers/common/mixins/sources_api"
4
+ require "topological_inventory/providers/common/mixins/x_rh_headers"
4
5
 
5
6
  module TopologicalInventory
6
7
  module Providers
7
- module Common
8
- module Operations
9
- class Source
10
- include Logging
8
+ module Common
9
+ module Operations
10
+ class Source
11
+ include Logging
12
+ include Mixins::SourcesApi
13
+ include Mixins::XRhHeaders
11
14
 
12
- STATUS_AVAILABLE, STATUS_UNAVAILABLE = %w[available unavailable].freeze
15
+ STATUS_AVAILABLE, STATUS_UNAVAILABLE = %w[available unavailable].freeze
13
16
 
14
- ERROR_MESSAGES = {
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
- }.freeze
17
+ ERROR_MESSAGES = {
18
+ :authentication_not_found => "Authentication not found in Sources API",
19
+ :endpoint_or_application_not_found => "Endpoint or Application not found in Sources API",
20
+ }.freeze
18
21
 
19
- LAST_CHECKED_AT_THRESHOLD = 5.minutes.freeze
20
- AUTH_NOT_NECESSARY = "n/a".freeze
22
+ LAST_CHECKED_AT_THRESHOLD = 5.minutes.freeze
23
+ AUTH_NOT_NECESSARY = "n/a".freeze
21
24
 
22
- attr_accessor :params, :request_context, :source_id, :account_number
25
+ attr_accessor :identity, :operation, :params, :request_context, :source_id, :account_number
23
26
 
24
- def initialize(params = {}, request_context = nil)
25
- self.params = params
26
- self.request_context = request_context
27
- self.source_id = params['source_id']
28
- self.account_number = params['external_tenant']
29
- end
27
+ def initialize(params = {}, request_context = nil)
28
+ self.operation = 'Source'
29
+ self.params = params
30
+ self.request_context = request_context
31
+ self.source_id = params['source_id']
30
32
 
31
- def availability_check
32
- return if params_missing?
33
+ self.account_number = params['external_tenant']
34
+ self.identity = identity_by_account_number(account_number)
35
+ end
33
36
 
34
- return if checked_recently?
37
+ def availability_check
38
+ self.operation += '#availability_check'
35
39
 
36
- status, error_message = connection_status
40
+ return if params_missing?
37
41
 
38
- update_source_and_subresources(status, error_message)
42
+ return if checked_recently?
39
43
 
40
- logger.availability_check("Completed: Source #{source_id} is #{status}")
41
- end
44
+ status, error_message = connection_status
42
45
 
43
- private
44
-
45
- def required_params
46
- %w[source_id]
47
- end
46
+ update_source_and_subresources(status, error_message)
48
47
 
49
- def params_missing?
50
- is_missing = false
51
- required_params.each do |attr|
52
- if (is_missing = params[attr].blank?)
53
- logger.availability_check("Missing #{attr} for the availability_check request [Source ID: #{source_id}]", :error)
54
- break
55
- end
48
+ logger.availability_check("Completed: Source #{source_id} is #{status}")
56
49
  end
57
50
 
58
- is_missing
59
- end
60
-
61
- def checked_recently?
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
51
+ private
67
52
 
68
- logger.availability_check("Skipping, last check at #{endpoint.last_checked_at || application.last_checked_at} [Source ID: #{source_id}] ") if checked_recently
69
-
70
- checked_recently
71
- end
53
+ def required_params
54
+ %w[source_id]
55
+ end
72
56
 
73
- def connection_status
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
57
+ def params_missing?
58
+ is_missing = false
59
+ required_params.each do |attr|
60
+ if (is_missing = params[attr].blank?)
61
+ logger.availability_check("Missing #{attr} for the availability_check request [Source ID: #{source_id}]", :error)
62
+ break
63
+ end
64
+ end
76
65
 
77
- check_time
78
- if endpoint
79
- endpoint_connection_check
80
- elsif application
81
- application_connection_check
66
+ is_missing
82
67
  end
83
- end
84
68
 
85
- def endpoint_connection_check
86
- return [STATUS_UNAVAILABLE, ERROR_MESSAGES[:authentication_not_found]] unless authentication
69
+ def checked_recently?
70
+ checked_recently = if endpoint.present?
71
+ endpoint.last_checked_at.present? && endpoint.last_checked_at >= LAST_CHECKED_AT_THRESHOLD.ago
72
+ elsif application.present?
73
+ application.last_checked_at.present? && application.last_checked_at >= LAST_CHECKED_AT_THRESHOLD.ago
74
+ end
87
75
 
88
- # call down into the operations pod implementation of `Source#connection_check`
89
- connection_check
90
- end
76
+ logger.availability_check("Skipping, last check at #{endpoint.last_checked_at || application.last_checked_at} [Source ID: #{source_id}] ") if checked_recently
91
77
 
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"]
78
+ checked_recently
98
79
  end
99
- end
100
80
 
101
- # @return [Array<String, String|nil] - STATUS_[UN]AVAILABLE, error message
102
- def connection_check
103
- raise NotImplementedError, "#{__method__} must be implemented in a subclass"
104
- end
105
-
106
- def update_source_and_subresources(status, error_message = nil)
107
- logger.availability_check("Updating source [#{source_id}] status [#{status}] message [#{error_message}]")
81
+ def connection_status
82
+ # we need either an endpoint or application to check the source.
83
+ return [STATUS_UNAVAILABLE, ERROR_MESSAGES[:endpoint_or_application_not_found]] unless endpoint || application
108
84
 
109
- update_source(status)
85
+ check_time
86
+ if endpoint
87
+ endpoint_connection_check
88
+ elsif application
89
+ application_connection_check
90
+ end
91
+ end
110
92
 
111
- update_endpoint(status, error_message) if endpoint
112
- update_application(status) if application
113
- end
93
+ def endpoint_connection_check
94
+ return [STATUS_UNAVAILABLE, ERROR_MESSAGES[:authentication_not_found]] unless authentication
114
95
 
115
- def update_source(status)
116
- source = ::SourcesApiClient::Source.new
117
- source.availability_status = status
118
- source.last_checked_at = check_time
119
- source.last_available_at = check_time if status == STATUS_AVAILABLE
96
+ # call down into the operations pod implementation of `Source#connection_check`
97
+ connection_check
98
+ end
120
99
 
121
- api_client.update_source(source_id, source)
122
- rescue ::SourcesApiClient::ApiError => e
123
- logger.availability_check("Failed to update Source id:#{source_id} - #{e.message}", :error)
124
- end
100
+ def application_connection_check
101
+ case application.availability_status
102
+ when "available"
103
+ [STATUS_AVAILABLE, nil]
104
+ when "unavailable"
105
+ [STATUS_UNAVAILABLE, "Application id #{application.id} unavailable"]
106
+ end
107
+ end
125
108
 
126
- def update_endpoint(status, error_message)
127
- if endpoint.nil?
128
- logger.availability_check("Failed to update Endpoint for Source id:#{source_id}. Endpoint not found", :error)
129
- return
109
+ # @return [Array<String, String|nil] - STATUS_[UN]AVAILABLE, error message
110
+ def connection_check
111
+ raise NotImplementedError, "#{__method__} must be implemented in a subclass"
130
112
  end
131
113
 
132
- endpoint_update = ::SourcesApiClient::Endpoint.new
114
+ def update_source_and_subresources(status, error_message = nil)
115
+ logger.availability_check("Updating source [#{source_id}] status [#{status}] message [#{error_message}]")
133
116
 
134
- endpoint_update.availability_status = status
135
- endpoint_update.availability_status_error = error_message.to_s
136
- endpoint_update.last_checked_at = check_time
137
- endpoint_update.last_available_at = check_time if status == STATUS_AVAILABLE
117
+ update_source(status)
138
118
 
139
- api_client.update_endpoint(endpoint.id, endpoint_update)
140
- rescue ::SourcesApiClient::ApiError => e
141
- logger.availability_check("Failed to update Endpoint(ID: #{endpoint.id}) - #{e.message}", :error)
142
- end
119
+ update_endpoint(status, error_message) if endpoint
120
+ update_application(status) if application
121
+ end
143
122
 
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
123
+ def update_source(status)
124
+ source = ::SourcesApiClient::Source.new
125
+ source.availability_status = status
126
+ source.last_checked_at = check_time
127
+ source.last_available_at = check_time if status == STATUS_AVAILABLE
148
128
 
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
129
+ sources_api.update_source(source_id, source)
130
+ rescue ::SourcesApiClient::ApiError => e
131
+ logger.availability_check("Failed to update Source id:#{source_id} - #{e.message}", :error)
132
+ end
153
133
 
154
- def endpoint
155
- @endpoint ||= api_client.fetch_default_endpoint(source_id)
156
- rescue => e
157
- logger.availability_check("Failed to fetch Endpoint for Source #{source_id}: #{e.message}", :error)
158
- nil
159
- end
134
+ def update_endpoint(status, error_message)
135
+ if endpoint.nil?
136
+ logger.availability_check("Failed to update Endpoint for Source id:#{source_id}. Endpoint not found", :error)
137
+ return
138
+ end
160
139
 
161
- def authentication
162
- @authentication ||= if endpoint.receptor_node.present?
163
- AUTH_NOT_NECESSARY
164
- else
165
- api_client.fetch_authentication(source_id, endpoint)
166
- end
167
- rescue => e
168
- logger.availability_check("Failed to fetch Authentication for Source #{source_id}: #{e.message}", :error)
169
- nil
170
- end
140
+ endpoint_update = ::SourcesApiClient::Endpoint.new
171
141
 
172
- def application
173
- @application ||= api_client.fetch_application(source_id)
174
- rescue => e
175
- logger.availability_check("Failed to fetch Application for Source #{source_id}: #{e.message}", :error)
176
- nil
177
- end
142
+ endpoint_update.availability_status = status
143
+ endpoint_update.availability_status_error = error_message.to_s
144
+ endpoint_update.last_checked_at = check_time
145
+ endpoint_update.last_available_at = check_time if status == STATUS_AVAILABLE
178
146
 
179
- def check_time
180
- @check_time ||= Time.now.utc
181
- end
147
+ sources_api.update_endpoint(endpoint.id, endpoint_update)
148
+ rescue ::SourcesApiClient::ApiError => e
149
+ logger.availability_check("Failed to update Endpoint(ID: #{endpoint.id}) - #{e.message}", :error)
150
+ end
182
151
 
183
- def identity
184
- @identity ||= {"x-rh-identity" => Base64.strict_encode64({"identity" => {"account_number" => account_number, "user" => {"is_org_admin" => true}}}.to_json)}
185
- end
152
+ def update_application(status)
153
+ application_update = ::SourcesApiClient::Application.new
154
+ application_update.last_checked_at = check_time
155
+ application_update.last_available_at = check_time if status == STATUS_AVAILABLE
186
156
 
187
- def api_client
188
- @api_client ||= TopologicalInventory::Providers::Common::Operations::SourcesApiClient.new(identity)
157
+ sources_api.update_application(application.id, application_update)
158
+ rescue ::SourcesApiClient::ApiError => e
159
+ logger.availability_check("Failed to update Application id: #{application.id} - #{e.message}", :error)
160
+ end
161
+
162
+ def check_time
163
+ @check_time ||= Time.now.utc
164
+ end
189
165
  end
190
166
  end
191
167
  end
192
168
  end
193
- end
194
169
  end
@@ -0,0 +1,92 @@
1
+ require "sources-api-client"
2
+
3
+ module TopologicalInventory
4
+ module Providers
5
+ module Common
6
+ class SourcesApiClient < ::SourcesApiClient::ApiClient
7
+ delegate :update_source, :update_endpoint, :update_application, :to => :api
8
+
9
+ INTERNAL_API_PATH = '//internal/v1.0'.freeze
10
+
11
+ def initialize(identity = nil)
12
+ super(::SourcesApiClient::Configuration.default)
13
+ self.identity = identity
14
+ self.api = init_default_api
15
+ end
16
+
17
+ def init_default_api
18
+ default_headers.merge!(identity) if identity.present?
19
+ ::SourcesApiClient::DefaultApi.new(self)
20
+ end
21
+
22
+ def fetch_default_endpoint(source_id)
23
+ endpoints = api.list_source_endpoints(source_id)&.data || []
24
+ endpoints.find(&:default)
25
+ end
26
+
27
+ def fetch_application(source_id)
28
+ applications = api.list_source_applications(source_id)&.data || []
29
+ applications.first
30
+ end
31
+
32
+ def fetch_authentication(source_id, default_endpoint = nil, authtype = nil)
33
+ endpoint = default_endpoint || fetch_default_endpoint(source_id)
34
+ return if endpoint.nil?
35
+
36
+ endpoint_authentications = api.list_endpoint_authentications(endpoint.id.to_s).data || []
37
+ return if endpoint_authentications.empty?
38
+
39
+ auth_id = if authtype.nil?
40
+ endpoint_authentications.first&.id
41
+ else
42
+ endpoint_authentications.detect { |a| a.authtype = authtype }&.id
43
+ end
44
+ return if auth_id.nil?
45
+
46
+ fetch_authentication_with_password(auth_id)
47
+ end
48
+
49
+ private
50
+
51
+ attr_accessor :identity, :api, :custom_base_path
52
+
53
+ def fetch_authentication_with_password(auth_id)
54
+ on_internal_api do
55
+ local_var_path = "/authentications/#{auth_id}"
56
+
57
+ query_params = "expose_encrypted_attribute[]=password"
58
+
59
+ header_params = {'Accept' => select_header_accept(['application/json'])}
60
+ return_type = 'Authentication'
61
+ data, _, _ = call_api(:GET, local_var_path,
62
+ :header_params => header_params,
63
+ :query_params => query_params,
64
+ :auth_names => ['UserSecurity'],
65
+ :return_type => return_type)
66
+ data
67
+ end
68
+ end
69
+
70
+ def build_request_url(path)
71
+ # Add leading and trailing slashes to path
72
+ path = "/#{path}".gsub(/\/+/, '/')
73
+ URI.encode((custom_base_url || @config.base_url) + path)
74
+ end
75
+
76
+ def custom_base_url
77
+ return nil if custom_base_path.nil?
78
+
79
+ url = "#{@config.scheme}://#{[@config.host, custom_base_path].join('/').gsub(/\/+/, '/')}".sub(/\/+\z/, '')
80
+ URI.encode(url)
81
+ end
82
+
83
+ def on_internal_api
84
+ self.custom_base_path = INTERNAL_API_PATH
85
+ yield
86
+ ensure
87
+ self.custom_base_path = nil
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,43 @@
1
+ require "topological_inventory-api-client"
2
+
3
+ module TopologicalInventory
4
+ module Providers
5
+ module Common
6
+ class TopologyApiClient < ::TopologicalInventoryApiClient::ApiClient
7
+ attr_accessor :api
8
+
9
+ def initialize(identity = nil)
10
+ super(::TopologicalInventoryApiClient::Configuration.default)
11
+
12
+ self.identity = identity
13
+ self.api = init_default_api
14
+ end
15
+
16
+ def init_default_api
17
+ default_headers.merge!(identity) if identity.present?
18
+ ::TopologicalInventoryApiClient::DefaultApi.new(self)
19
+ end
20
+
21
+ def update_task(task_id, source_id: nil, state:, status:, target_type: nil, target_source_ref: nil, context: nil)
22
+ params = {'state' => state,
23
+ 'status' => status}
24
+ params['context'] = context if context
25
+ params['source_id'] = source_id if source_id
26
+ params['target_type'] = target_type if target_type
27
+ params['target_source_ref'] = target_source_ref if target_source_ref
28
+ task = TopologicalInventoryApiClient::Task.new(params)
29
+ api.update_task(task_id, task)
30
+ end
31
+
32
+ def svc_instance_url(service_instance)
33
+ rest_api_path = '/service_instances/{id}'.sub('{' + 'id' + '}', service_instance&.id.to_s)
34
+ build_request(:GET, rest_api_path).url
35
+ end
36
+
37
+ private
38
+
39
+ attr_accessor :identity
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,7 +1,7 @@
1
1
  module TopologicalInventory
2
2
  module Providers
3
3
  module Common
4
- VERSION = "1.0.12"
4
+ VERSION = "2.0.0".freeze
5
5
  end
6
6
  end
7
7
  end
@@ -94,7 +94,7 @@ RSpec.shared_examples "availability_check" do
94
94
  stub_patch(:source, source_patch_body)
95
95
 
96
96
  # Check
97
- api_client = subject.send(:api_client)
97
+ api_client = subject.send(:sources_api)
98
98
  expect(api_client).not_to receive(:update_endpoint)
99
99
 
100
100
  subject.availability_check
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.12
4
+ version: 2.0.0
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-10-05 00:00:00.000000000 Z
11
+ date: 2020-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -252,15 +252,16 @@ files:
252
252
  - lib/topological_inventory/providers/common/collector/parser.rb
253
253
  - lib/topological_inventory/providers/common/collectors_pool.rb
254
254
  - lib/topological_inventory/providers/common/logging.rb
255
+ - lib/topological_inventory/providers/common/mixins/sources_api.rb
256
+ - lib/topological_inventory/providers/common/mixins/topology_api.rb
257
+ - lib/topological_inventory/providers/common/mixins/x_rh_headers.rb
255
258
  - lib/topological_inventory/providers/common/operations/async_worker.rb
256
- - lib/topological_inventory/providers/common/operations/endpoint_client.rb
257
259
  - lib/topological_inventory/providers/common/operations/health_check.rb
258
- - lib/topological_inventory/providers/common/operations/processor.rb
259
260
  - lib/topological_inventory/providers/common/operations/source.rb
260
- - lib/topological_inventory/providers/common/operations/sources_api_client.rb
261
- - lib/topological_inventory/providers/common/operations/topology_api_client.rb
262
261
  - lib/topological_inventory/providers/common/save_inventory/exception.rb
263
262
  - lib/topological_inventory/providers/common/save_inventory/saver.rb
263
+ - lib/topological_inventory/providers/common/sources_api_client.rb
264
+ - lib/topological_inventory/providers/common/topology_api_client.rb
264
265
  - lib/topological_inventory/providers/common/version.rb
265
266
  - spec/spec_helper.rb
266
267
  - spec/support/inventory_helper.rb
@@ -271,7 +272,6 @@ files:
271
272
  - spec/topological_inventory/providers/common/collectors_pool_spec.rb
272
273
  - spec/topological_inventory/providers/common/logger_spec.rb
273
274
  - spec/topological_inventory/providers/common/operations/async_worker_spec.rb
274
- - spec/topological_inventory/providers/common/operations/processor_spec.rb
275
275
  - spec/topological_inventory/providers/common/operations/source_spec.rb
276
276
  - spec/topological_inventory/providers/common/save_inventory/saver_spec.rb
277
277
  - spec/topological_inventory/providers/common_spec.rb
@@ -1,65 +0,0 @@
1
- require "topological_inventory/providers/common/operations/topology_api_client"
2
- require "topological_inventory/providers/common/operations/sources_api_client"
3
-
4
- module TopologicalInventory
5
- module Providers
6
- module Common
7
- module Operations
8
- class EndpointClient
9
- include TopologyApiClient
10
-
11
- def initialize(source_id, task_id, identity = nil)
12
- self.identity = identity
13
- self.source_id = source_id
14
- self.task_id = task_id
15
- end
16
-
17
- def order_service(service_offering, service_plan, order_params)
18
- raise NotImplementedError, "#{__method__} must be implemented in a subclass"
19
- end
20
-
21
- def source_ref_of(endpoint_svc_instance)
22
- raise NotImplementedError, "#{__method__} must be implemented in a subclass"
23
- end
24
-
25
- def wait_for_provision_complete(source_id, endpoint_svc_instance, context = {})
26
- raise NotImplementedError, "#{__method__} must be implemented in a subclass"
27
- end
28
-
29
- def provisioned_successfully?(endpoint_svc_instance)
30
- raise NotImplementedError, "#{__method__} must be implemented in a subclass"
31
- end
32
-
33
- # Endpoint for conversion of provisioned service's status to
34
- # TopologicalInventory Task's status
35
- def task_status_for(endpoint_svc_instance)
36
- raise NotImplementedError, "#{__method__} must be implemented in a subclass"
37
- end
38
-
39
- private
40
-
41
- attr_accessor :identity, :task_id, :source_id
42
-
43
- def sources_api
44
- @sources_api ||= SourcesApiClient.new(identity)
45
- end
46
-
47
- def default_endpoint
48
- @default_endpoint ||= sources_api.fetch_default_endpoint(source_id)
49
- raise "Sources API: Endpoint not found! (source id: #{source_id})" if @default_endpoint.nil?
50
-
51
- @default_endpoint
52
- end
53
-
54
- def authentication
55
- @authentication ||= sources_api.fetch_authentication(source_id, default_endpoint)
56
- end
57
-
58
- def verify_ssl_mode
59
- default_endpoint.verify_ssl ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
@@ -1,135 +0,0 @@
1
- require "topological_inventory/providers/common/operations/topology_api_client"
2
-
3
- module TopologicalInventory
4
- module Providers
5
- module Common
6
- module Operations
7
- class Processor
8
- include Logging
9
- include TopologyApiClient
10
-
11
- SLEEP_POLL = 10
12
- POLL_TIMEOUT = 1800
13
-
14
- def self.process!(message)
15
- model, method = message.headers['message_type'].to_s.split(".")
16
- new(model, method, message.payload).process
17
- end
18
-
19
- # @param payload [Hash] https://github.com/ManageIQ/topological_inventory-api/blob/master/app/controllers/api/v0/service_plans_controller.rb#L32-L41
20
- def initialize(model, method, payload, metrics = nil)
21
- self.model = model
22
- self.method = method
23
- self.params = payload["params"]
24
- self.identity = payload["request_context"]
25
- self.metrics = metrics
26
- end
27
-
28
- def process
29
- logger.info("Processing #{model}##{method} [#{params}]...")
30
- result = order_service(params)
31
- logger.info("Processing #{model}##{method} [#{params}]...Complete")
32
-
33
- result
34
- end
35
-
36
- private
37
-
38
- attr_accessor :identity, :model, :method, :metrics, :params
39
-
40
- def endpoint_client(source_id, task_id, identity)
41
- raise NotImplementedError, "#{__method__} must be implemented in a subclass as kind of TopologicalInventory::Providers::Common::EndpointClient class"
42
- end
43
-
44
- def order_service(params)
45
- task_id, service_offering_id, service_plan_id, order_params = params.values_at("task_id", "service_offering_id", "service_plan_id", "order_params")
46
-
47
- service_plan = topology_api_client.show_service_plan(service_plan_id) if service_plan_id.present?
48
- service_offering_id = service_plan.service_offering_id if service_offering_id.nil? && service_plan.present?
49
- service_offering = topology_api_client.show_service_offering(service_offering_id)
50
-
51
- source_id = service_offering.source_id
52
- client = endpoint_client(source_id, task_id, identity)
53
-
54
- logger.info("Ordering #{service_offering.name}...")
55
- remote_service_instance = client.order_service(service_offering, service_plan.presence, order_params)
56
- logger.info("Ordering #{service_offering.name}...Complete")
57
-
58
- poll_order_complete_thread(task_id, source_id, remote_service_instance)
59
- rescue StandardError => err
60
- metrics&.record_error
61
- logger.error("[Task #{task_id}] Ordering error: #{err}\n#{err.backtrace.join("\n")}")
62
- update_task(task_id, :state => "completed", :status => "error", :context => {:error => err.to_s})
63
- end
64
-
65
- def poll_order_complete_thread(task_id, source_id, remote_svc_instance)
66
- Thread.new do
67
- begin
68
- poll_order_complete(task_id, source_id, remote_svc_instance)
69
- rescue StandardError => err
70
- logger.error("[Task #{task_id}] Waiting for complete: #{err}\n#{err.backtrace.join("\n")}")
71
- update_task(task_id, :state => "completed", :status => "warn", :context => {:error => err.to_s})
72
- end
73
- end
74
- end
75
-
76
- def poll_order_complete(task_id, source_id, remote_svc_instance)
77
- client = endpoint_client(source_id, task_id, identity)
78
-
79
- context = {
80
- :service_instance => {
81
- :source_id => source_id,
82
- :source_ref => client.source_ref_of(remote_svc_instance)
83
- }
84
- }
85
-
86
- remote_svc_instance = client.wait_for_provision_complete(task_id, remote_svc_instance, context)
87
-
88
- if client.provisioned_successfully?(remote_svc_instance)
89
- if (service_instance = load_topological_svc_instance(source_id, client.source_ref_of(remote_svc_instance))).present?
90
- context[:service_instance][:id] = service_instance.id
91
- context[:service_instance][:url] = svc_instance_url(service_instance)
92
- else
93
- logger.warn("Failed to get service_instance API URL (endpoint's service instance: #{remote_svc_instance.inspect})")
94
- end
95
- end
96
- update_task(task_id, :state => "completed", :status => client.task_status_for(remote_svc_instance), :context => context)
97
- end
98
-
99
- def load_topological_svc_instance(source_id, source_ref)
100
- api = topology_api_client.api_client
101
-
102
- count = 0
103
- timeout_count = POLL_TIMEOUT / SLEEP_POLL
104
-
105
- header_params = { 'Accept' => api.select_header_accept(['application/json']) }
106
- query_params = { :'source_id' => source_id, :'source_ref' => source_ref }
107
- return_type = 'ServiceInstancesCollection'
108
-
109
- service_instance = nil
110
- loop do
111
- data, _status_code, _headers = api.call_api(:GET, "/service_instances",
112
- :header_params => header_params,
113
- :query_params => query_params,
114
- :auth_names => ['UserSecurity'],
115
- :return_type => return_type)
116
-
117
- service_instance = data.data&.first if data.meta.count > 0
118
- break if service_instance.present?
119
-
120
- break if (count += 1) >= timeout_count
121
-
122
- sleep(SLEEP_POLL) # seconds
123
- end
124
-
125
- if service_instance.nil?
126
- logger.error("Failed to find service_instance by source_id [#{source_id}] source_ref [#{source_ref}]")
127
- end
128
-
129
- service_instance
130
- end
131
- end
132
- end
133
- end
134
- end
135
- end
@@ -1,94 +0,0 @@
1
- require "sources-api-client"
2
-
3
- module TopologicalInventory
4
- module Providers
5
- module Common
6
- module Operations
7
- class SourcesApiClient < ::SourcesApiClient::ApiClient
8
- delegate :update_source, :update_endpoint, :update_application, :to => :api
9
-
10
- INTERNAL_API_PATH = '//internal/v1.0'.freeze
11
-
12
- def initialize(identity = nil)
13
- super(::SourcesApiClient::Configuration.default)
14
- self.identity = identity
15
- self.api = init_default_api
16
- end
17
-
18
- def init_default_api
19
- default_headers.merge!(identity) if identity.present?
20
- ::SourcesApiClient::DefaultApi.new(self)
21
- end
22
-
23
- def fetch_default_endpoint(source_id)
24
- endpoints = api.list_source_endpoints(source_id)&.data || []
25
- endpoints.find(&:default)
26
- end
27
-
28
- def fetch_application(source_id)
29
- applications = api.list_source_applications(source_id)&.data || []
30
- applications.first
31
- end
32
-
33
- def fetch_authentication(source_id, default_endpoint = nil, authtype = nil)
34
- endpoint = default_endpoint || fetch_default_endpoint(source_id)
35
- return if endpoint.nil?
36
-
37
- endpoint_authentications = api.list_endpoint_authentications(endpoint.id.to_s).data || []
38
- return if endpoint_authentications.empty?
39
-
40
- auth_id = if authtype.nil?
41
- endpoint_authentications.first&.id
42
- else
43
- endpoint_authentications.detect { |a| a.authtype = authtype }&.id
44
- end
45
- return if auth_id.nil?
46
-
47
- fetch_authentication_with_password(auth_id)
48
- end
49
-
50
- private
51
-
52
- attr_accessor :identity, :api, :custom_base_path
53
-
54
- def fetch_authentication_with_password(auth_id)
55
- on_internal_api do
56
- local_var_path = "/authentications/#{auth_id}"
57
-
58
- query_params = "expose_encrypted_attribute[]=password"
59
-
60
- header_params = { 'Accept' => select_header_accept(['application/json']) }
61
- return_type = 'Authentication'
62
- data, _, _ = call_api(:GET, local_var_path,
63
- :header_params => header_params,
64
- :query_params => query_params,
65
- :auth_names => ['UserSecurity'],
66
- :return_type => return_type)
67
- data
68
- end
69
- end
70
-
71
- def build_request_url(path)
72
- # Add leading and trailing slashes to path
73
- path = "/#{path}".gsub(/\/+/, '/')
74
- URI.encode((custom_base_url || @config.base_url) + path)
75
- end
76
-
77
- def custom_base_url
78
- return nil if custom_base_path.nil?
79
-
80
- url = "#{@config.scheme}://#{[@config.host, custom_base_path].join('/').gsub(/\/+/, '/')}".sub(/\/+\z/, '')
81
- URI.encode(url)
82
- end
83
-
84
- def on_internal_api
85
- self.custom_base_path = INTERNAL_API_PATH
86
- yield
87
- ensure
88
- self.custom_base_path = nil
89
- end
90
- end
91
- end
92
- end
93
- end
94
- end
@@ -1,28 +0,0 @@
1
- module TopologicalInventory
2
- module Providers
3
- module Common
4
- module Operations
5
- module TopologyApiClient
6
- def topology_api_client
7
- @topology_api_client ||=
8
- begin
9
- api_client = TopologicalInventoryApiClient::ApiClient.new
10
- api_client.default_headers.merge!(identity) if identity.present?
11
- TopologicalInventoryApiClient::DefaultApi.new(api_client)
12
- end
13
- end
14
-
15
- def update_task(task_id, state:, status:, context:)
16
- task = TopologicalInventoryApiClient::Task.new("state" => state, "status" => status, "context" => context)
17
- topology_api_client.update_task(task_id, task)
18
- end
19
-
20
- def svc_instance_url(service_instance)
21
- rest_api_path = '/service_instances/{id}'.sub('{' + 'id' + '}', service_instance&.id.to_s)
22
- topology_api_client.api_client.build_request(:GET, rest_api_path).url
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,102 +0,0 @@
1
- require "topological_inventory/providers/common/operations/processor"
2
-
3
- RSpec.describe TopologicalInventory::Providers::Common::Operations::Processor do
4
- let(:topology_api_client) { double }
5
- let(:source_id) { 1 }
6
- let(:source_ref) { 1000 }
7
- let(:service_plan) { double("TopologicalInventoryApiClient::ServicePlan") }
8
- let(:service_offering) { double("TopologicalInventoryApiClient::ServiceOffering") }
9
-
10
- # Overriden in contexts
11
- let(:payload) { {} }
12
-
13
- before do
14
- @processor = described_class.new(nil, nil, payload)
15
- allow(@processor).to receive(:logger).and_return(double('null_object').as_null_object)
16
-
17
- allow(service_plan).to receive(:service_offering_id).and_return(1)
18
- allow(service_plan).to receive(:name).and_return(double)
19
-
20
- allow(service_offering).to receive(:name).and_return(double)
21
- allow(service_offering).to receive(:source_ref).and_return(source_ref)
22
- allow(service_offering).to receive(:extra).and_return({:type => 'job_template'})
23
- allow(service_offering).to receive(:source_id).and_return(source_id)
24
-
25
- @endpoint_client = double
26
- allow(@endpoint_client).to receive(:order_service)
27
-
28
- allow(@processor).to receive(:endpoint_client).and_return(@endpoint_client)
29
- allow(@processor).to receive(:topology_api_client).and_return(topology_api_client)
30
- allow(topology_api_client).to receive(:update_task)
31
- allow(topology_api_client).to receive(:show_service_plan).and_return(service_plan)
32
- allow(topology_api_client).to receive(:show_service_offering).and_return(service_offering)
33
- end
34
-
35
- context "Order by ServicePlan" do
36
- let(:payload) do
37
- {
38
- 'request_context' => {"x-rh-identity" => 'abcd'},
39
- 'params' => {
40
- 'order_params' => {
41
- 'service_plan_id' => 1,
42
- 'service_parameters' => { :name => "Job 1",
43
- :param1 => "Test Topology",
44
- :param2 => 50 },
45
- 'provider_control_parameters' => {}
46
- },
47
- 'service_plan_id' => 1,
48
- 'task_id' => 1 # in tp-inv api (Task)
49
- }
50
- }
51
- end
52
-
53
- describe "#order_service" do
54
- it "orders job" do
55
- allow(@processor).to receive(:poll_order_complete_thread).and_return(double)
56
-
57
- expect(@endpoint_client).to receive(:order_service).with(service_offering, service_plan, payload['params']['order_params'])
58
- @processor.send(:order_service, payload['params'])
59
- end
60
-
61
- it "updates task on error" do
62
- err_message = "Sample error"
63
-
64
- allow(@processor).to receive(:poll_order_complete_thread).and_return(double)
65
- allow(@processor).to receive(:update_task).and_return(double)
66
- allow(@endpoint_client).to receive(:order_service).and_raise(err_message)
67
-
68
- expect(@processor).to receive(:update_task).with(payload['params']['task_id'], :state => "completed", :status => "error", :context => { :error => err_message })
69
-
70
- @processor.send(:order_service, payload['params'])
71
- end
72
- end
73
- end
74
-
75
- context "Order by ServiceOffering" do
76
- let(:payload) do
77
- {
78
- 'request_context' => {"x-rh-identity" => 'abcd'},
79
- 'params' => {
80
- 'order_params' => {
81
- 'service_offering_id' => 1,
82
- 'service_parameters' => { :name => "Job 1",
83
- :param1 => "Test Topology",
84
- :param2 => 50 },
85
- 'provider_control_parameters' => {}
86
- },
87
- 'service_offering_id' => 1,
88
- 'task_id' => 1 # in tp-inv api (Task)
89
- }
90
- }
91
- end
92
-
93
- describe "#order_service" do
94
- it "orders job" do
95
- allow(@processor).to receive(:poll_order_complete_thread).and_return(double)
96
-
97
- expect(@endpoint_client).to receive(:order_service).with(service_offering, nil, payload['params']['order_params'])
98
- @processor.send(:order_service, payload['params'])
99
- end
100
- end
101
- end
102
- end