topological_inventory-providers-common 1.0.10 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +31 -0
  3. data/.rubocop.yml +1 -1
  4. data/.yamllint +8 -0
  5. data/CHANGELOG.md +25 -3
  6. data/lib/topological_inventory/providers/common.rb +1 -2
  7. data/lib/topological_inventory/providers/common/collectors_pool.rb +2 -1
  8. data/lib/topological_inventory/providers/common/logging.rb +6 -3
  9. data/lib/topological_inventory/providers/common/messaging_client.rb +40 -0
  10. data/lib/topological_inventory/providers/common/metrics.rb +84 -0
  11. data/lib/topological_inventory/providers/common/mixins/sources_api.rb +61 -0
  12. data/lib/topological_inventory/providers/common/mixins/statuses.rb +19 -0
  13. data/lib/topological_inventory/providers/common/mixins/topology_api.rb +26 -0
  14. data/lib/topological_inventory/providers/common/mixins/x_rh_headers.rb +24 -0
  15. data/lib/topological_inventory/providers/common/operations/async_worker.rb +56 -0
  16. data/lib/topological_inventory/providers/common/operations/processor.rb +46 -104
  17. data/lib/topological_inventory/providers/common/operations/source.rb +183 -144
  18. data/lib/topological_inventory/providers/common/sources_api_client.rb +92 -0
  19. data/lib/topological_inventory/providers/common/topology_api_client.rb +43 -0
  20. data/lib/topological_inventory/providers/common/version.rb +1 -1
  21. data/spec/support/shared/availability_check.rb +254 -90
  22. data/spec/topological_inventory/providers/common/operations/async_worker_spec.rb +36 -0
  23. data/spec/topological_inventory/providers/common/operations/processor_spec.rb +52 -83
  24. data/topological_inventory-providers-common.gemspec +14 -10
  25. metadata +74 -9
  26. data/lib/topological_inventory/providers/common/operations/endpoint_client.rb +0 -65
  27. data/lib/topological_inventory/providers/common/operations/sources_api_client.rb +0 -94
  28. 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: cd89d895ea401fd0692588fb0f7dc86d6df4a4ec189e73ec4453eafc2be13c7e
4
- data.tar.gz: 933d448f102d927a62153b75eadd2fb835d1f6fa5a72496ddbaf830a040ff1cc
3
+ metadata.gz: faf7ab98d098c6dfed7cac60aa40dca8e84a26721eac35ed8a487c84c57d4375
4
+ data.tar.gz: 122a119549a916b7d908c7655a94ed855af4ddb49220e05cedd11e89972abcd3
5
5
  SHA512:
6
- metadata.gz: 012ad9a5e0f75a7a9970c31b269fdd7f0ef740ec7626c585ff003ba7cc784ba32995208ebef39de05666e1e442b2104aa44685d2b0d2855c012744e7553c7ea1
7
- data.tar.gz: 73590bf64ce08ae60a3693f0214a60f31a2f5b82780542180bf7d6a5e8b32d156b2e4fa2e85cea8088b8ef0178737ca365b5e932117b52becf8777cc5916acd1
6
+ metadata.gz: 90d69186763762d8d62c41282f27b926b6aee15fbd2b916d2f911eab519f586650e97e804ea07e3c2c1121b953a305f949912648e05ffe016ef27cd4273b0747
7
+ data.tar.gz: f8864ca924b2e5b494d3e0fe2403bcd0f1c03450fe9ce755fb6bf80c832e8a1dafa0c788a9998ae50c2e63eb414852bdeadd593ca2ebfd8389ae1858e6517129
@@ -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
@@ -1,3 +1,3 @@
1
1
  inherit_from:
2
- - https://raw.githubusercontent.com/ManageIQ/guides/master/.rubocop_base.yml
2
+ - https://raw.githubusercontent.com/RedHatInsights/insights-api-common-rails/master/.rubocop_base.yml
3
3
  - .rubocop_local.yml
@@ -0,0 +1,8 @@
1
+ ---
2
+ ignore: |
3
+
4
+ extends: relaxed
5
+
6
+ rules:
7
+ line-length:
8
+ max: 120
@@ -4,11 +4,29 @@ 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.10]
7
+ ## [2.1.1] - 2020-11-04
8
+ MessagingClient and Source fix #61
9
+ Fix availability_check in app check #62
10
+
11
+ ## [2.1.0] - 2020-11-02
12
+ Add Availability checks for sources via Kafka #54
13
+ Common Metrics exporter #57
14
+ Rubocop rules from insights-api-common + yamllint #59
15
+
16
+ ## [2.0.0] - 2020-10-20
17
+ Operations/API clients refactoring
18
+
19
+ ## [1.0.12] - 2020-10-05
20
+ Add Operations Async Worker class #55
21
+
22
+ ## [1.0.11] - 2020-09-04
23
+ Make Collector Poll Time a parameter so we can tweak the collection interval #51
24
+
25
+ ## [1.0.10] - 2020-08-26
8
26
  Add HealthCheck class for operations workers #48
9
27
  Set the LOG_LEVEL if present #50
10
28
 
11
- ## [1.0.9]
29
+ ## [1.0.9] - 2020-08-17
12
30
  Added refresh-type to save and sweep inventory #45
13
31
 
14
32
  ## [1.0.8] - 2020-08-12
@@ -53,7 +71,11 @@ manageiq-loggers to >= 0.4.2 #20
53
71
  ## [1.0.0] - 2020-03-19
54
72
  ### Initial release to rubygems.org
55
73
 
56
- [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.10...HEAD
74
+ [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.0...HEAD
75
+ [2.1.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.0.0...v2.1.0
76
+ [2.0.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.12...v2.0.0
77
+ [1.0.12]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.11...v1.0.12
78
+ [1.0.11]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.10...v1.0.11
57
79
  [1.0.10]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.9...v1.0.10
58
80
  [1.0.9]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.8...v1.0.9
59
81
  [1.0.8]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.7...v1.0.8
@@ -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
@@ -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: 60, thread_pool_size: 2)
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,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,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("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