topological_inventory-providers-common 1.0.11 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) 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 +28 -4
  6. data/lib/topological_inventory/providers/common.rb +1 -2
  7. data/lib/topological_inventory/providers/common/logging.rb +6 -3
  8. data/lib/topological_inventory/providers/common/messaging_client.rb +40 -0
  9. data/lib/topological_inventory/providers/common/metrics.rb +84 -0
  10. data/lib/topological_inventory/providers/common/mixins/sources_api.rb +61 -0
  11. data/lib/topological_inventory/providers/common/mixins/statuses.rb +19 -0
  12. data/lib/topological_inventory/providers/common/mixins/topology_api.rb +26 -0
  13. data/lib/topological_inventory/providers/common/mixins/x_rh_headers.rb +24 -0
  14. data/lib/topological_inventory/providers/common/operations/async_worker.rb +61 -0
  15. data/lib/topological_inventory/providers/common/operations/processor.rb +46 -104
  16. data/lib/topological_inventory/providers/common/operations/source.rb +183 -144
  17. data/lib/topological_inventory/providers/common/sources_api_client.rb +92 -0
  18. data/lib/topological_inventory/providers/common/topology_api_client.rb +43 -0
  19. data/lib/topological_inventory/providers/common/version.rb +1 -1
  20. data/spec/support/shared/availability_check.rb +254 -90
  21. data/spec/topological_inventory/providers/common/operations/async_worker_spec.rb +45 -0
  22. data/spec/topological_inventory/providers/common/operations/processor_spec.rb +52 -83
  23. data/topological_inventory-providers-common.gemspec +14 -10
  24. metadata +74 -9
  25. data/lib/topological_inventory/providers/common/operations/endpoint_client.rb +0 -65
  26. data/lib/topological_inventory/providers/common/operations/sources_api_client.rb +0 -94
  27. 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: d2593d36aee528450e29282d903ddcc2d74c6593e9579c69ec2a510c9fcc15ab
4
- data.tar.gz: 47b0675fcab7eaab9c2b3899837b597295a0e7c6c737f1ce39ca49c4a7968cc5
3
+ metadata.gz: 661072299fccb0117c3dcbe1fba36e4836a82ac1a6d98abbaab1808ea437e6d7
4
+ data.tar.gz: a1a45e527dc14cb4b09004d2df98f075818fb35d616d84d954839c289dac5c8a
5
5
  SHA512:
6
- metadata.gz: 03f98f6334065f62a4462e30633c6a6d852552f49414be2293b4864d3971372ce66ab52d47e23b4a7ad2f8c0277d9d2352f38519e2621a80f6c9b2b011a7cdd4
7
- data.tar.gz: b801478114b42cc897329a12eca067cc707306f488ed7e84eddf7eddd91c0098dbbdf5d6d3fcb116734b39cd08d25dcabdd6731b60057fc900ca2c39a5008bd8
6
+ metadata.gz: ba101d97cca75814a073eb70f8528e3debb0f4270fb4cfb98cc9db55f53ff790efcd99380158e8c808b317603a9ce1a7a30d13f791e03741b699e06f0556965e
7
+ data.tar.gz: 3e4340ad6743693f104d0c847995bb866860ef2911c57865ce042389cb3872badf05f2b46119664650214285f509495f656dc5cf82f77cec9cf5e2a253a0f2cc
@@ -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,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.0.11]
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/v1.0.11...HEAD
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
- 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('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