topological_inventory-providers-common 1.0.12 → 2.1.3

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 +30 -5
  6. data/README.md +1 -1
  7. data/lib/topological_inventory/providers/common.rb +1 -2
  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 +12 -7
  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 +14 -5
  23. data/spec/topological_inventory/providers/common/operations/processor_spec.rb +52 -83
  24. data/topological_inventory-providers-common.gemspec +14 -10
  25. metadata +72 -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: b81c523169c3ca87e678e3e7d046b4daee863200cf4eadd695e9022d17a11e5a
4
- data.tar.gz: 1fc2a47cedf6f8d3b41f0df728a5ac8ceceaea03b58d43ef67da90680861c0eb
3
+ metadata.gz: 9848e297a81db270e52355b3caf500b38391f1c35e118ca31cadbec35c3f0c5e
4
+ data.tar.gz: ad23c8d4f9a46b6824b20e2d870611d4b3a9c570da00f00a262513934b2349ab
5
5
  SHA512:
6
- metadata.gz: 9afb8ba326d632ba59245d93837f32bd8edbcb34b3d82e029c351f904bb75f196592eb4724116a48caf0b7df8fc7f4101f5b59bd0e23b65d6fca25ada3ac4cb6
7
- data.tar.gz: '00924d04d1fc4623cd4e0f0a719c5471dbd39635820375b4957278db1c6e3ecbdf2b58c25758bca22e91e979511286708279922919f6303118401e58ac9cc5c4'
6
+ metadata.gz: b9e92c54114310f529eb78c8faa1ec64fd56350c82086ea2fbe126594afa596757e3a39e6f068929c093b0a607e60cf6ca46f2671dea867a5c87b1f59cae6de7
7
+ data.tar.gz: 567e5f95a9138dbc8ac7dd52e26a7fe923075552c89d24c3c4e6111f072c0b1b0fb61c5a1fd463bec86d87f00e359b9ca55cbe728fff5b4a0a2fc7a07e4853ba
@@ -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,17 +4,37 @@ 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.12] - 2020-10-01
7
+ ## [2.1.3] - 2020-12-14
8
+ Update travis URL in README file #67
9
+ Kafka availability_checks fix #68
10
+
11
+ ## [2.1.2] - 2020-11-24
12
+ Custom Metrics for AsyncWorker #60
13
+ Change error metric name to errors_total #64
14
+
15
+ ## [2.1.1] - 2020-11-04
16
+ MessagingClient and Source fix #61
17
+ Fix availability_check in app check #62
18
+
19
+ ## [2.1.0] - 2020-11-02
20
+ Add Availability checks for sources via Kafka #54
21
+ Common Metrics exporter #57
22
+ Rubocop rules from insights-api-common + yamllint #59
23
+
24
+ ## [2.0.0] - 2020-10-20
25
+ Operations/API clients refactoring
26
+
27
+ ## [1.0.12] - 2020-10-05
8
28
  Add Operations Async Worker class #55
9
29
 
10
- ## [1.0.11]
30
+ ## [1.0.11] - 2020-09-04
11
31
  Make Collector Poll Time a parameter so we can tweak the collection interval #51
12
32
 
13
- ## [1.0.10]
33
+ ## [1.0.10] - 2020-08-26
14
34
  Add HealthCheck class for operations workers #48
15
35
  Set the LOG_LEVEL if present #50
16
36
 
17
- ## [1.0.9]
37
+ ## [1.0.9] - 2020-08-17
18
38
  Added refresh-type to save and sweep inventory #45
19
39
 
20
40
  ## [1.0.8] - 2020-08-12
@@ -59,7 +79,12 @@ manageiq-loggers to >= 0.4.2 #20
59
79
  ## [1.0.0] - 2020-03-19
60
80
  ### Initial release to rubygems.org
61
81
 
62
- [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.12...HEAD
82
+ [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.3...HEAD
83
+ [2.1.3]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.2...v2.1.3
84
+ [2.1.2]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.1...v2.1.2
85
+ [2.1.1]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.1.0...v2.1.1
86
+ [2.1.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v2.0.0...v2.1.0
87
+ [2.0.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.12...v2.0.0
63
88
  [1.0.12]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.11...v1.0.12
64
89
  [1.0.11]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.10...v1.0.11
65
90
  [1.0.10]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.9...v1.0.10
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TopologicalInventory::Providers::Common
2
2
 
3
- [![Build Status](https://travis-ci.org/RedHatInsights/topological_inventory-providers-common.svg?branch=master)](https://travis-ci.org/RedHatInsights/topological_inventory-providers-common)
3
+ [![Build Status](https://travis-ci.com/RedHatInsights/topological_inventory-providers-common.svg?branch=master)](https://travis-ci.com/RedHatInsights/topological_inventory-providers-common)
4
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/df747492b802bfea3c83/maintainability)](https://codeclimate.com/github/RedHatInsights/topological_inventory-providers-common/maintainability)
5
5
  [![Test Coverage](https://api.codeclimate.com/v1/badges/df747492b802bfea3c83/test_coverage)](https://codeclimate.com/github/RedHatInsights/topological_inventory-providers-common/test_coverage)
6
6
  [![security](https://hakiri.io/github/RedHatInsights/topological_inventory-providers-common/master.svg)](https://hakiri.io/github/RedHatInsights/topological_inventory-providers-common/master)
@@ -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
@@ -1,4 +1,5 @@
1
1
  require "topological_inventory/providers/common/logging"
2
+ require "topological_inventory/providers/common/mixins/statuses"
2
3
  require "topological_inventory/providers/common/operations/health_check"
3
4
 
4
5
  module TopologicalInventory
@@ -7,10 +8,12 @@ module TopologicalInventory
7
8
  module Operations
8
9
  class AsyncWorker
9
10
  include Logging
11
+ include TopologicalInventory::Providers::Common::Mixins::Statuses
10
12
 
11
- def initialize(processor, queue = nil)
13
+ def initialize(processor, queue: nil, metrics: nil)
12
14
  @processor = processor
13
- @queue = queue || Queue.new
15
+ @queue = queue || Queue.new
16
+ @metrics = metrics
14
17
  end
15
18
 
16
19
  def start
@@ -37,15 +40,17 @@ module TopologicalInventory
37
40
 
38
41
  private
39
42
 
40
- attr_reader :thread, :queue, :processor
43
+ attr_reader :thread, :queue, :processor, :metrics
41
44
 
42
- def process_message(msg)
43
- processor.process!(msg)
45
+ def process_message(message)
46
+ result = processor.process!(message, metrics)
47
+ metrics&.record_operation(message.message, :status => result)
44
48
  rescue => err
45
- model, method = msg.message.to_s.split(".")
49
+ model, method = message.message.to_s.split(".")
46
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])
47
52
  ensure
48
- msg.ack
53
+ message.ack
49
54
  TopologicalInventory::Providers::Common::Operations::HealthCheck.touch_file
50
55
  logger.debug("Operations::AsyncWorker queue length: #{queue.length}") if queue.length >= 20 && queue.length % 5 == 0
51
56
  end