topological_inventory-providers-common 1.0.11 → 2.1.2
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 +4 -4
- data/.codeclimate.yml +31 -0
- data/.rubocop.yml +1 -1
- data/.yamllint +8 -0
- data/CHANGELOG.md +28 -4
- data/lib/topological_inventory/providers/common.rb +1 -2
- data/lib/topological_inventory/providers/common/logging.rb +6 -3
- data/lib/topological_inventory/providers/common/messaging_client.rb +40 -0
- data/lib/topological_inventory/providers/common/metrics.rb +84 -0
- data/lib/topological_inventory/providers/common/mixins/sources_api.rb +61 -0
- data/lib/topological_inventory/providers/common/mixins/statuses.rb +19 -0
- data/lib/topological_inventory/providers/common/mixins/topology_api.rb +26 -0
- data/lib/topological_inventory/providers/common/mixins/x_rh_headers.rb +24 -0
- data/lib/topological_inventory/providers/common/operations/async_worker.rb +61 -0
- data/lib/topological_inventory/providers/common/operations/processor.rb +46 -104
- data/lib/topological_inventory/providers/common/operations/source.rb +183 -144
- data/lib/topological_inventory/providers/common/sources_api_client.rb +92 -0
- data/lib/topological_inventory/providers/common/topology_api_client.rb +43 -0
- data/lib/topological_inventory/providers/common/version.rb +1 -1
- data/spec/support/shared/availability_check.rb +254 -90
- data/spec/topological_inventory/providers/common/operations/async_worker_spec.rb +45 -0
- data/spec/topological_inventory/providers/common/operations/processor_spec.rb +52 -83
- data/topological_inventory-providers-common.gemspec +14 -10
- metadata +74 -9
- data/lib/topological_inventory/providers/common/operations/endpoint_client.rb +0 -65
- data/lib/topological_inventory/providers/common/operations/sources_api_client.rb +0 -94
- data/lib/topological_inventory/providers/common/operations/topology_api_client.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 661072299fccb0117c3dcbe1fba36e4836a82ac1a6d98abbaab1808ea437e6d7
|
4
|
+
data.tar.gz: a1a45e527dc14cb4b09004d2df98f075818fb35d616d84d954839c289dac5c8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba101d97cca75814a073eb70f8528e3debb0f4270fb4cfb98cc9db55f53ff790efcd99380158e8c808b317603a9ce1a7a30d13f791e03741b699e06f0556965e
|
7
|
+
data.tar.gz: 3e4340ad6743693f104d0c847995bb866860ef2911c57865ce042389cb3872badf05f2b46119664650214285f509495f656dc5cf82f77cec9cf5e2a253a0f2cc
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
---
|
2
|
+
version: '2'
|
3
|
+
prepare:
|
4
|
+
fetch:
|
5
|
+
- url: https://raw.githubusercontent.com/RedHatInsights/insights-api-common-rails/master/.rubocop_base.yml
|
6
|
+
path: ".rubocop_base.yml"
|
7
|
+
- url: https://raw.githubusercontent.com/RedHatInsights/insights-api-common-rails/master/.rubocop_cc_base.yml
|
8
|
+
path: ".rubocop_cc_base.yml"
|
9
|
+
checks:
|
10
|
+
argument-count:
|
11
|
+
enabled: false
|
12
|
+
complex-logic:
|
13
|
+
enabled: false
|
14
|
+
file-lines:
|
15
|
+
enabled: false
|
16
|
+
method-complexity:
|
17
|
+
config:
|
18
|
+
threshold: 11
|
19
|
+
method-count:
|
20
|
+
enabled: false
|
21
|
+
method-lines:
|
22
|
+
enabled: false
|
23
|
+
nested-control-flow:
|
24
|
+
enabled: false
|
25
|
+
return-statements:
|
26
|
+
enabled: false
|
27
|
+
plugins:
|
28
|
+
rubocop:
|
29
|
+
enabled: true
|
30
|
+
config: ".rubocop_cc.yml"
|
31
|
+
channel: rubocop-1-0
|
data/.rubocop.yml
CHANGED
data/.yamllint
ADDED
data/CHANGELOG.md
CHANGED
@@ -4,14 +4,33 @@ 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.
|
7
|
+
## [2.1.2] - 2020-11-24
|
8
|
+
Custom Metrics for AsyncWorker #60
|
9
|
+
Change error metric name to errors_total #64
|
10
|
+
|
11
|
+
## [2.1.1] - 2020-11-04
|
12
|
+
MessagingClient and Source fix #61
|
13
|
+
Fix availability_check in app check #62
|
14
|
+
|
15
|
+
## [2.1.0] - 2020-11-02
|
16
|
+
Add Availability checks for sources via Kafka #54
|
17
|
+
Common Metrics exporter #57
|
18
|
+
Rubocop rules from insights-api-common + yamllint #59
|
19
|
+
|
20
|
+
## [2.0.0] - 2020-10-20
|
21
|
+
Operations/API clients refactoring
|
22
|
+
|
23
|
+
## [1.0.12] - 2020-10-05
|
24
|
+
Add Operations Async Worker class #55
|
25
|
+
|
26
|
+
## [1.0.11] - 2020-09-04
|
8
27
|
Make Collector Poll Time a parameter so we can tweak the collection interval #51
|
9
28
|
|
10
|
-
## [1.0.10]
|
29
|
+
## [1.0.10] - 2020-08-26
|
11
30
|
Add HealthCheck class for operations workers #48
|
12
31
|
Set the LOG_LEVEL if present #50
|
13
32
|
|
14
|
-
## [1.0.9]
|
33
|
+
## [1.0.9] - 2020-08-17
|
15
34
|
Added refresh-type to save and sweep inventory #45
|
16
35
|
|
17
36
|
## [1.0.8] - 2020-08-12
|
@@ -56,7 +75,12 @@ manageiq-loggers to >= 0.4.2 #20
|
|
56
75
|
## [1.0.0] - 2020-03-19
|
57
76
|
### Initial release to rubygems.org
|
58
77
|
|
59
|
-
[Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/
|
78
|
+
[Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.2...HEAD
|
79
|
+
[2.1.2]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.1...v2.1.2
|
80
|
+
[2.1.1]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.0...v2.1.1
|
81
|
+
[2.1.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.0.0...v2.1.0
|
82
|
+
[2.0.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.12...v2.0.0
|
83
|
+
[1.0.12]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.11...v1.0.12
|
60
84
|
[1.0.11]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.10...v1.0.11
|
61
85
|
[1.0.10]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.9...v1.0.10
|
62
86
|
[1.0.9]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.8...v1.0.9
|
@@ -1,10 +1,9 @@
|
|
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"
|
6
|
+
require "topological_inventory/providers/common/metrics"
|
8
7
|
|
9
8
|
module TopologicalInventory
|
10
9
|
module Providers
|
@@ -23,11 +23,14 @@ module TopologicalInventory
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def availability_check(message, severity = :info)
|
26
|
-
|
26
|
+
send("#{severity}_ext", "Source#availability_check", message)
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
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,40 @@
|
|
1
|
+
require "more_core_extensions/core_ext/module/cache_with_timeout"
|
2
|
+
require "manageiq-messaging"
|
3
|
+
|
4
|
+
module TopologicalInventory
|
5
|
+
module Providers
|
6
|
+
module Common
|
7
|
+
class MessagingClient
|
8
|
+
# Kafka host name
|
9
|
+
attr_accessor :queue_host
|
10
|
+
# Kafka port
|
11
|
+
attr_accessor :queue_port
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
self.queue_host = ENV['QUEUE_HOST'] || 'localhost'
|
15
|
+
self.queue_port = (ENV['QUEUE_PORT'] || 9092).to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.default
|
19
|
+
@@default ||= new
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.configure
|
23
|
+
if block_given?
|
24
|
+
yield(default)
|
25
|
+
else
|
26
|
+
default
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
cache_with_timeout(:client) do
|
31
|
+
ManageIQ::Messaging::Client.open(:protocol => :Kafka, :host => default.queue_host, :port => default.queue_port)
|
32
|
+
end
|
33
|
+
|
34
|
+
def client
|
35
|
+
self.class.client
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
require "prometheus_exporter"
|
3
|
+
require "prometheus_exporter/server"
|
4
|
+
require "prometheus_exporter/client"
|
5
|
+
require "prometheus_exporter/instrumentation"
|
6
|
+
|
7
|
+
module TopologicalInventory
|
8
|
+
module Providers
|
9
|
+
module Common
|
10
|
+
class Metrics
|
11
|
+
ERROR_COUNTER_MESSAGE = "total number of errors".freeze
|
12
|
+
|
13
|
+
def initialize(port = 9394)
|
14
|
+
return if port == 0
|
15
|
+
|
16
|
+
configure_server(port)
|
17
|
+
configure_metrics
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop_server
|
21
|
+
@server&.stop
|
22
|
+
end
|
23
|
+
|
24
|
+
def record_error(type = :general)
|
25
|
+
@error_counter&.observe(1, :type => type.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
def record_operation(name, labels = {})
|
29
|
+
@status_counter&.observe(1, (labels || {}).merge(:name => name))
|
30
|
+
end
|
31
|
+
|
32
|
+
def record_operation_time(name, labels = {}, &block)
|
33
|
+
record_time(@duration_seconds, (labels || {}).merge(:name => name), &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Common method for gauge
|
37
|
+
def record_gauge(metric, opt, value: nil, labels: {})
|
38
|
+
case opt
|
39
|
+
when :set then
|
40
|
+
metric&.observe(value.to_i, labels)
|
41
|
+
when :add then
|
42
|
+
metric&.increment(labels)
|
43
|
+
when :remove then
|
44
|
+
metric&.decrement(labels)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Common method for histogram
|
49
|
+
def record_time(metric, labels = {})
|
50
|
+
result = nil
|
51
|
+
time = Benchmark.realtime { result = yield }
|
52
|
+
metric&.observe(time, labels)
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def configure_server(port)
|
59
|
+
@server = PrometheusExporter::Server::WebServer.new(:port => port)
|
60
|
+
@server.start
|
61
|
+
|
62
|
+
PrometheusExporter::Client.default = PrometheusExporter::LocalClient.new(:collector => @server.collector)
|
63
|
+
end
|
64
|
+
|
65
|
+
def configure_metrics
|
66
|
+
PrometheusExporter::Instrumentation::Process.start
|
67
|
+
PrometheusExporter::Metric::Base.default_prefix = default_prefix
|
68
|
+
|
69
|
+
@duration_seconds = PrometheusExporter::Metric::Histogram.new('duration_seconds', 'Duration of processed operation')
|
70
|
+
@error_counter = PrometheusExporter::Metric::Counter.new('errors_total', ERROR_COUNTER_MESSAGE)
|
71
|
+
@status_counter = PrometheusExporter::Metric::Counter.new('status_counter', 'number of processed operations')
|
72
|
+
|
73
|
+
[@duration_seconds, @error_counter, @status_counter].each do |metric|
|
74
|
+
@server.collector.register_metric(metric)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def default_prefix
|
79
|
+
raise NotImplementedError, "#{__method__} must be implemented in a subclass"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,61 @@
|
|
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
|
+
metrics&.record_error(:sources_api)
|
18
|
+
logger.error_ext(operation, "Failed to fetch Endpoint for Source #{source_id}: #{e.message}")
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def authentication
|
23
|
+
@authentication ||= if endpoint.receptor_node.present?
|
24
|
+
AUTH_NOT_NECESSARY
|
25
|
+
else
|
26
|
+
sources_api.fetch_authentication(source_id, endpoint)
|
27
|
+
end
|
28
|
+
rescue => e
|
29
|
+
metrics&.record_error(:sources_api)
|
30
|
+
logger.error_ext(operation, "Failed to fetch Authentication for Source #{source_id}: #{e.message}")
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def application
|
35
|
+
@application ||= sources_api.fetch_application(source_id)
|
36
|
+
rescue => e
|
37
|
+
metrics&.record_error(:sources_api)
|
38
|
+
logger.error_ext(operation, "Failed to fetch Application for Source #{source_id}: #{e.message}")
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_premise?
|
43
|
+
@on_premise ||= endpoint&.receptor_node.to_s.strip.present?
|
44
|
+
end
|
45
|
+
|
46
|
+
def verify_ssl_mode
|
47
|
+
endpoint&.verify_ssl ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
48
|
+
end
|
49
|
+
|
50
|
+
def full_hostname(endpoint)
|
51
|
+
if on_premise?
|
52
|
+
"receptor://#{endpoint.receptor_node}"
|
53
|
+
else
|
54
|
+
endpoint.host.tap { |host| host << ":#{endpoint.port}" if endpoint.port }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module TopologicalInventory
|
2
|
+
module Providers
|
3
|
+
module Common
|
4
|
+
module Mixins
|
5
|
+
module Statuses
|
6
|
+
def operation_status
|
7
|
+
return @statuses if @statuses.present?
|
8
|
+
|
9
|
+
@statuses = {}
|
10
|
+
%i[success error skipped not_implemented].each do |status|
|
11
|
+
@statuses[status] = status.to_s
|
12
|
+
end
|
13
|
+
@statuses
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
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
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "topological_inventory/providers/common/logging"
|
2
|
+
require "topological_inventory/providers/common/mixins/statuses"
|
3
|
+
require "topological_inventory/providers/common/operations/health_check"
|
4
|
+
|
5
|
+
module TopologicalInventory
|
6
|
+
module Providers
|
7
|
+
module Common
|
8
|
+
module Operations
|
9
|
+
class AsyncWorker
|
10
|
+
include Logging
|
11
|
+
include TopologicalInventory::Providers::Common::Mixins::Statuses
|
12
|
+
|
13
|
+
def initialize(processor, queue: nil, metrics: nil)
|
14
|
+
@processor = processor
|
15
|
+
@queue = queue || Queue.new
|
16
|
+
@metrics = metrics
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
return if thread.present?
|
21
|
+
|
22
|
+
@thread = Thread.new { listen }
|
23
|
+
end
|
24
|
+
|
25
|
+
def stop
|
26
|
+
thread&.exit
|
27
|
+
end
|
28
|
+
|
29
|
+
def enqueue(msg)
|
30
|
+
queue << msg
|
31
|
+
end
|
32
|
+
|
33
|
+
def listen
|
34
|
+
loop do
|
35
|
+
# the queue thread waits for a message to come during `Queue#pop`
|
36
|
+
msg = queue.pop
|
37
|
+
process_message(msg)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :thread, :queue, :processor, :metrics
|
44
|
+
|
45
|
+
def process_message(message)
|
46
|
+
result = processor.process!(message, metrics)
|
47
|
+
metrics&.record_operation(message.message, :status => result)
|
48
|
+
rescue => err
|
49
|
+
model, method = message.message.to_s.split(".")
|
50
|
+
logger.error("#{model}##{method}: async worker failure: #{err.cause}\n#{err}\n#{err.backtrace.join("\n")}")
|
51
|
+
metrics&.record_operation(message.message, :status => operation_status[:error])
|
52
|
+
ensure
|
53
|
+
message.ack
|
54
|
+
TopologicalInventory::Providers::Common::Operations::HealthCheck.touch_file
|
55
|
+
logger.debug("Operations::AsyncWorker queue length: #{queue.length}") if queue.length >= 20 && queue.length % 5 == 0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|