topological_inventory-providers-common 1.0.9 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +31 -0
- data/.rubocop.yml +1 -1
- data/.yamllint +8 -0
- data/CHANGELOG.md +25 -2
- data/lib/topological_inventory/providers/common.rb +2 -2
- data/lib/topological_inventory/providers/common/collectors_pool.rb +2 -1
- data/lib/topological_inventory/providers/common/logging.rb +10 -4
- 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 +56 -0
- data/lib/topological_inventory/providers/common/operations/health_check.rb +15 -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 +36 -0
- data/spec/topological_inventory/providers/common/operations/processor_spec.rb +52 -83
- data/topological_inventory-providers-common.gemspec +14 -10
- metadata +75 -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: aad209b97efea9bb19ce488782adb1dabfa8d650c12f8637e2b348ed4ec95a15
|
4
|
+
data.tar.gz: 69b9bbed6a2d725990206ab3bfc7b13ec25b10f7e0c1a979b48c61b67e53fd0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45722b70f009a764d8cd804424157cc98b1a225b72326b8b8971fecd093fa6260446777db76bc3502e487d27dc82b2b4325959302f4a1abe13fbbf8c015c1d54
|
7
|
+
data.tar.gz: b1a6de8e2eb93b2b26d4b609f17a1a518cf4b318dbcf117c6225fe297bb3ec97f870b944e151786977ad90f42c16746eeeadf6a3d113b648921e278cb831aadd
|
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,7 +4,25 @@ 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
|
+
## [2.1.0]
|
8
|
+
Add Availability checks for sources via Kafka #54
|
9
|
+
Common Metrics exporter #57
|
10
|
+
Rubocop rules from insights-api-common + yamllint #59
|
11
|
+
|
12
|
+
## [2.0.0] - 2020-10-20
|
13
|
+
Operations/API clients refactoring
|
14
|
+
|
15
|
+
## [1.0.12] - 2020-10-01
|
16
|
+
Add Operations Async Worker class #55
|
17
|
+
|
18
|
+
## [1.0.11] - 2020-09-04
|
19
|
+
Make Collector Poll Time a parameter so we can tweak the collection interval #51
|
20
|
+
|
21
|
+
## [1.0.10] - 2020-08-26
|
22
|
+
Add HealthCheck class for operations workers #48
|
23
|
+
Set the LOG_LEVEL if present #50
|
24
|
+
|
25
|
+
## [1.0.9] - 2020-08-17
|
8
26
|
Added refresh-type to save and sweep inventory #45
|
9
27
|
|
10
28
|
## [1.0.8] - 2020-08-12
|
@@ -49,7 +67,12 @@ manageiq-loggers to >= 0.4.2 #20
|
|
49
67
|
## [1.0.0] - 2020-03-19
|
50
68
|
### Initial release to rubygems.org
|
51
69
|
|
52
|
-
[Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/
|
70
|
+
[Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.0...HEAD
|
71
|
+
[2.1.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.0.0...v2.1.0
|
72
|
+
[2.0.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.12...v2.0.0
|
73
|
+
[1.0.12]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.11...v1.0.12
|
74
|
+
[1.0.11]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.10...v1.0.11
|
75
|
+
[1.0.10]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.9...v1.0.10
|
53
76
|
[1.0.9]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.8...v1.0.9
|
54
77
|
[1.0.8]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.7...v1.0.8
|
55
78
|
[1.0.7]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.6...v1.0.7
|
@@ -1,9 +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/
|
4
|
-
require "topological_inventory/providers/common/operations/endpoint_client"
|
3
|
+
require "topological_inventory/providers/common/operations/health_check"
|
5
4
|
require "topological_inventory/providers/common/collectors_pool"
|
6
5
|
require "topological_inventory/providers/common/collector"
|
6
|
+
require "topological_inventory/providers/common/metrics"
|
7
7
|
|
8
8
|
module TopologicalInventory
|
9
9
|
module Providers
|
@@ -5,8 +5,9 @@ module TopologicalInventory
|
|
5
5
|
module Common
|
6
6
|
class CollectorsPool
|
7
7
|
SECRET_FILENAME = "credentials".freeze
|
8
|
+
COLLECTOR_POLL_TIME = ENV['COLLECTOR_POLL_TIME']&.to_i || 300
|
8
9
|
|
9
|
-
def initialize(config_name, metrics, collector_poll_time:
|
10
|
+
def initialize(config_name, metrics, collector_poll_time: COLLECTOR_POLL_TIME, thread_pool_size: 2)
|
10
11
|
self.config_name = config_name
|
11
12
|
self.collector_status = Concurrent::Map.new
|
12
13
|
self.metrics = metrics
|
@@ -23,17 +23,23 @@ 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
|
|
34
37
|
class Logger < ManageIQ::Loggers::CloudWatch
|
35
38
|
def self.new(*args)
|
36
|
-
super.tap
|
39
|
+
super.tap do |logger|
|
40
|
+
logger.extend(TopologicalInventory::Providers::Common::LoggingFunctions)
|
41
|
+
logger.level = ENV['LOG_LEVEL'] if ENV['LOG_LEVEL']
|
42
|
+
end
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
@@ -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
|
+
@queue_host = ENV['QUEUE_HOST'] || 'localhost'
|
15
|
+
@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 => @queue_host, :port => @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("error", 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,56 @@
|
|
1
|
+
require "topological_inventory/providers/common/logging"
|
2
|
+
require "topological_inventory/providers/common/operations/health_check"
|
3
|
+
|
4
|
+
module TopologicalInventory
|
5
|
+
module Providers
|
6
|
+
module Common
|
7
|
+
module Operations
|
8
|
+
class AsyncWorker
|
9
|
+
include Logging
|
10
|
+
|
11
|
+
def initialize(processor, queue = nil)
|
12
|
+
@processor = processor
|
13
|
+
@queue = queue || Queue.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
return if thread.present?
|
18
|
+
|
19
|
+
@thread = Thread.new { listen }
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop
|
23
|
+
thread&.exit
|
24
|
+
end
|
25
|
+
|
26
|
+
def enqueue(msg)
|
27
|
+
queue << msg
|
28
|
+
end
|
29
|
+
|
30
|
+
def listen
|
31
|
+
loop do
|
32
|
+
# the queue thread waits for a message to come during `Queue#pop`
|
33
|
+
msg = queue.pop
|
34
|
+
process_message(msg)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :thread, :queue, :processor
|
41
|
+
|
42
|
+
def process_message(msg)
|
43
|
+
processor.process!(msg)
|
44
|
+
rescue => err
|
45
|
+
model, method = msg.message.to_s.split(".")
|
46
|
+
logger.error("#{model}##{method}: async worker failure: #{err.cause}\n#{err}\n#{err.backtrace.join("\n")}")
|
47
|
+
ensure
|
48
|
+
msg.ack
|
49
|
+
TopologicalInventory::Providers::Common::Operations::HealthCheck.touch_file
|
50
|
+
logger.debug("Operations::AsyncWorker queue length: #{queue.length}") if queue.length >= 20 && queue.length % 5 == 0
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|