topological_inventory-providers-common 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/gem-push.yml +32 -6
- data/CHANGELOG.md +8 -2
- data/lib/topological_inventory/providers/common/version.rb +1 -1
- data/spec/spec_helper.rb +22 -0
- data/spec/support/inventory_helper.rb +14 -0
- data/spec/support/shared/availability_check.rb +175 -0
- data/spec/topological_inventory/providers/common/collectors/inventory_collection_storage_spec.rb +44 -0
- data/spec/topological_inventory/providers/common/collectors/inventory_collection_wrapper_spec.rb +9 -0
- data/spec/topological_inventory/providers/common/collectors_pool_spec.rb +150 -0
- data/spec/topological_inventory/providers/common/logger_spec.rb +38 -0
- data/spec/topological_inventory/providers/common/operations/processor_spec.rb +102 -0
- data/spec/topological_inventory/providers/common/operations/source_spec.rb +5 -0
- data/spec/topological_inventory/providers/common/save_inventory/saver_spec.rb +65 -0
- data/spec/topological_inventory/providers/common_spec.rb +3 -0
- data/topological_inventory-providers-common.gemspec +1 -1
- metadata +14 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e289728fe42a0bdf12142485a444c851948e9db8ff215c6444f8a7b7bd98c409
|
4
|
+
data.tar.gz: 54241e05b5d38f449a905b4edecd725b5110eaba96c213098ade88643601de76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14d64a1f0c4b208a43692905a3e843f6c255145cda9cc1a928b8db0a1da02a387cca940146d8044cdcdfbad15bee694e71660d318a2184cf7b1f920f1d6e86fe
|
7
|
+
data.tar.gz: 5f6d8af6dff4fe080e223a9fb2e0ae81ac8d4c3789f5ef832043ad3b08f52ba2d72590963096a02eb9efc9ce31d6a0e36929a1d89c0ad54a2985aa7871921a74
|
@@ -12,12 +12,38 @@ jobs:
|
|
12
12
|
runs-on: ubuntu-latest
|
13
13
|
|
14
14
|
steps:
|
15
|
-
- uses: actions/checkout@
|
15
|
+
- uses: actions/checkout@v2
|
16
16
|
|
17
|
-
- name:
|
18
|
-
uses:
|
17
|
+
- name: Set up Ruby 2.6
|
18
|
+
uses: actions/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: 2.6.x
|
21
|
+
|
22
|
+
- name: Read the version.rb
|
23
|
+
id: version_file
|
24
|
+
run: |
|
25
|
+
echo ::set-output name=data::$(grep VERSION lib/topological_inventory/providers/common/version.rb | awk {'print $3'} | tr -d '"')
|
26
|
+
|
27
|
+
- name: Echo the gem version
|
28
|
+
run: |
|
29
|
+
echo "v${{ steps.version_file.outputs.data }}"
|
30
|
+
|
31
|
+
- name: Publish to RubyGems
|
32
|
+
run: |
|
33
|
+
mkdir -p $HOME/.gem
|
34
|
+
touch $HOME/.gem/credentials
|
35
|
+
chmod 0600 $HOME/.gem/credentials
|
36
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
37
|
+
gem build *.gemspec
|
38
|
+
gem push *.gem
|
19
39
|
env:
|
20
|
-
|
21
|
-
RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
|
22
|
-
RELEASE_COMMAND: rake release
|
40
|
+
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
|
23
41
|
|
42
|
+
- name: Create Release
|
43
|
+
id: create_release
|
44
|
+
uses: actions/create-release@v1
|
45
|
+
env:
|
46
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
47
|
+
with:
|
48
|
+
tag_name: "v${{ steps.version_file.outputs.data }}"
|
49
|
+
release_name: "v${{ steps.version_file.outputs.data }}"
|
data/CHANGELOG.md
CHANGED
@@ -4,9 +4,14 @@ 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
|
+
## [1.0.5]
|
8
|
+
Change release workflow to do everything manually #32
|
9
|
+
Add specs to released files #33
|
10
|
+
|
11
|
+
## [1.0.4] - 2020-06-18
|
8
12
|
Common availability check operation #25
|
9
13
|
Rubocop and codecoverage #29
|
14
|
+
Add github workflow to release to rubygems automatically #31
|
10
15
|
|
11
16
|
## [1.0.3] - 2020-06-04
|
12
17
|
### Changed
|
@@ -29,7 +34,8 @@ manageiq-loggers to >= 0.4.2 #20
|
|
29
34
|
## [1.0.0] - 2020-03-19
|
30
35
|
### Initial release to rubygems.org
|
31
36
|
|
32
|
-
[Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.
|
37
|
+
[Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.5...HEAD
|
38
|
+
[1.0.5]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.4...v1.0.5
|
33
39
|
[1.0.4]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.3...v1.0.4
|
34
40
|
[1.0.3]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.2...v1.0.3
|
35
41
|
[1.0.2]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.1...v1.0.2
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
if ENV['CI']
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
end
|
5
|
+
|
6
|
+
require "bundler/setup"
|
7
|
+
require "topological_inventory/providers/common"
|
8
|
+
require "webmock/rspec"
|
9
|
+
|
10
|
+
Dir["./spec/support/**/*.rb"].each {|f| require f}
|
11
|
+
|
12
|
+
spec_path = File.dirname(__FILE__)
|
13
|
+
Dir[File.join(spec_path, "support/**/*.rb")].each { |f| require f }
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
# Enable flags like --only-failures and --next-failure
|
17
|
+
config.example_status_persistence_file_path = ".rspec_status"
|
18
|
+
|
19
|
+
config.expect_with :rspec do |c|
|
20
|
+
c.syntax = :expect
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module InventorySpecHelper
|
2
|
+
def self.big_inventory(size, chunk_size)
|
3
|
+
{
|
4
|
+
:collections => [
|
5
|
+
:name => SecureRandom.uuid,
|
6
|
+
:data => data_chunks(size, chunk_size)
|
7
|
+
]
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.data_chunks(size, chunk)
|
12
|
+
Array.new(size / chunk) { "a" * chunk }
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require "topological_inventory/providers/common/operations/source"
|
2
|
+
|
3
|
+
RSpec.shared_examples "availability_check" do
|
4
|
+
let(:host_url) { 'https://cloud.redhat.com' }
|
5
|
+
let(:sources_api_path) { '/api/sources/v3.0' }
|
6
|
+
let(:sources_internal_api_path) { '/internal/v1.0' }
|
7
|
+
let(:sources_api_url) { "#{host_url}#{sources_api_path}" }
|
8
|
+
|
9
|
+
let(:external_tenant) { '11001' }
|
10
|
+
let(:identity) { {'x-rh-identity' => Base64.strict_encode64({'identity' => {'account_number' => external_tenant, 'user' => {'is_org_admin' => true}}}.to_json)} }
|
11
|
+
let(:headers) { {'Content-Type' => 'application/json'}.merge(identity) }
|
12
|
+
let(:source_id) { '123' }
|
13
|
+
let(:endpoint_id) { '234' }
|
14
|
+
let(:authentication_id) { '345' }
|
15
|
+
let(:payload) do
|
16
|
+
{
|
17
|
+
'params' => {
|
18
|
+
'source_id' => source_id,
|
19
|
+
'external_tenant' => external_tenant,
|
20
|
+
'timestamp' => Time.now.utc
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:list_endpoints_response) { "{\"data\":[{\"default\":true,\"host\":\"10.0.0.1\",\"id\":\"#{endpoint_id}\",\"path\":\"/\",\"role\":\"ansible\",\"scheme\":\"https\",\"source_id\":\"#{source_id}\",\"tenant\":\"#{external_tenant}\"}]}" }
|
26
|
+
let(:list_endpoint_authentications_response) { "{\"data\":[{\"authtype\":\"username_password\",\"id\":\"#{authentication_id}\",\"resource_id\":\"#{endpoint_id}\",\"resource_type\":\"Endpoint\",\"username\":\"admin\",\"tenant\":\"#{external_tenant}\"}]}" }
|
27
|
+
let(:list_endpoint_authentications_response_empty) { "{\"data\":[]}" }
|
28
|
+
let(:internal_api_authentication_response) { "{\"authtype\":\"username_password\",\"id\":\"#{authentication_id}\",\"resource_id\":\"#{endpoint_id}\",\"resource_type\":\"Endpoint\",\"username\":\"admin\",\"tenant\":\"#{external_tenant}\",\"password\":\"xxx\"}" }
|
29
|
+
|
30
|
+
subject { described_class.new(payload["params"]) }
|
31
|
+
|
32
|
+
context "when not checked recently" do
|
33
|
+
before do
|
34
|
+
allow(subject).to receive(:checked_recently?).and_return(false)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "updates Source and Endpoint when available" do
|
38
|
+
# GET
|
39
|
+
stub_get(:endpoint, list_endpoints_response)
|
40
|
+
stub_get(:authentication, list_endpoint_authentications_response)
|
41
|
+
stub_get(:password, internal_api_authentication_response)
|
42
|
+
|
43
|
+
# PATCH
|
44
|
+
source_patch_body = {'availability_status' => described_class::STATUS_AVAILABLE, 'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
|
45
|
+
endpoint_patch_body = {'availability_status' => described_class::STATUS_AVAILABLE, 'availability_status_error' => '', 'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
|
46
|
+
|
47
|
+
stub_patch(:source, source_patch_body)
|
48
|
+
stub_patch(:endpoint, endpoint_patch_body)
|
49
|
+
|
50
|
+
# Check ---
|
51
|
+
expect(subject).to receive(:connection_check).and_return([described_class::STATUS_AVAILABLE, nil])
|
52
|
+
|
53
|
+
subject.availability_check
|
54
|
+
|
55
|
+
assert_patch(:source, source_patch_body)
|
56
|
+
assert_patch(:endpoint, endpoint_patch_body)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "updates Source and Endpoint when unavailable" do
|
60
|
+
# GET
|
61
|
+
stub_get(:endpoint, list_endpoints_response)
|
62
|
+
stub_get(:authentication, list_endpoint_authentications_response)
|
63
|
+
stub_get(:password, internal_api_authentication_response)
|
64
|
+
|
65
|
+
# PATCH
|
66
|
+
connection_error_message = "Some connection error"
|
67
|
+
source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
|
68
|
+
endpoint_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'availability_status_error' => connection_error_message, 'last_checked_at' => subject.send(:check_time)}.to_json
|
69
|
+
|
70
|
+
stub_patch(:source, source_patch_body)
|
71
|
+
stub_patch(:endpoint, endpoint_patch_body)
|
72
|
+
|
73
|
+
# Check ---
|
74
|
+
expect(subject).to receive(:connection_check).and_return([described_class::STATUS_UNAVAILABLE, connection_error_message])
|
75
|
+
|
76
|
+
subject.availability_check
|
77
|
+
|
78
|
+
assert_patch(:source, source_patch_body)
|
79
|
+
assert_patch(:endpoint, endpoint_patch_body)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "updates only Source to 'unavailable' status if Endpoint not found" do
|
83
|
+
# GET
|
84
|
+
stub_get(:endpoint, '')
|
85
|
+
|
86
|
+
# PATCH
|
87
|
+
source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
|
88
|
+
stub_patch(:source, source_patch_body)
|
89
|
+
|
90
|
+
# Check
|
91
|
+
api_client = subject.send(:api_client)
|
92
|
+
expect(api_client).not_to receive(:update_endpoint)
|
93
|
+
|
94
|
+
subject.availability_check
|
95
|
+
|
96
|
+
assert_patch(:source, source_patch_body)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "updates Source and Endpoint to 'unavailable' if Authentication not found" do
|
100
|
+
# GET
|
101
|
+
stub_get(:endpoint, list_endpoints_response)
|
102
|
+
stub_get(:authentication, list_endpoint_authentications_response_empty)
|
103
|
+
|
104
|
+
# PATCH
|
105
|
+
source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
|
106
|
+
endpoint_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'availability_status_error' => described_class::ERROR_MESSAGES[:authentication_not_found], 'last_checked_at' => subject.send(:check_time)}.to_json
|
107
|
+
|
108
|
+
stub_patch(:source, source_patch_body)
|
109
|
+
stub_patch(:endpoint, endpoint_patch_body)
|
110
|
+
|
111
|
+
# Check
|
112
|
+
expect(subject).not_to receive(:connection_check)
|
113
|
+
subject.availability_check
|
114
|
+
|
115
|
+
assert_patch(:source, source_patch_body)
|
116
|
+
assert_patch(:endpoint, endpoint_patch_body)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when checked recently" do
|
121
|
+
before do
|
122
|
+
allow(subject).to receive(:checked_recently?).and_return(true)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "doesn't do connection check" do
|
126
|
+
expect(subject).not_to receive(:connection_check)
|
127
|
+
expect(WebMock).not_to have_requested(:patch, "#{sources_api_url}/sources/#{source_id}")
|
128
|
+
expect(WebMock).not_to have_requested(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
|
129
|
+
|
130
|
+
subject.availability_check
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def stub_get(object_type, response)
|
136
|
+
case object_type
|
137
|
+
when :endpoint
|
138
|
+
stub_request(:get, "#{sources_api_url}/sources/#{source_id}/endpoints")
|
139
|
+
.with(:headers => headers)
|
140
|
+
.to_return(:status => 200, :body => response, :headers => {})
|
141
|
+
when :authentication
|
142
|
+
stub_request(:get, "#{sources_api_url}/endpoints/#{endpoint_id}/authentications")
|
143
|
+
.with(:headers => headers)
|
144
|
+
.to_return(:status => 200, :body => response, :headers => {})
|
145
|
+
when :password
|
146
|
+
stub_request(:get, "#{host_url}#{sources_internal_api_path}/authentications/#{authentication_id}?expose_encrypted_attribute%5B%5D=password")
|
147
|
+
.with(:headers => headers)
|
148
|
+
.to_return(:status => 200, :body => response, :headers => {})
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def stub_patch(object_type, data)
|
153
|
+
case object_type
|
154
|
+
when :source
|
155
|
+
stub_request(:patch, "#{sources_api_url}/sources/#{source_id}")
|
156
|
+
.with(:body => data, :headers => headers)
|
157
|
+
.to_return(:status => 200, :body => "", :headers => {})
|
158
|
+
when :endpoint
|
159
|
+
stub_request(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
|
160
|
+
.with(:body => data, :headers => headers)
|
161
|
+
.to_return(:status => 200, :body => "", :headers => {})
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def assert_patch(object_type, data)
|
166
|
+
case object_type
|
167
|
+
when :source
|
168
|
+
expect(WebMock).to have_requested(:patch, "#{sources_api_url}/sources/#{source_id}")
|
169
|
+
.with(:body => data, :headers => headers).once
|
170
|
+
when :endpoint
|
171
|
+
expect(WebMock).to have_requested(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
|
172
|
+
.with(:body => data, :headers => headers).once
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
data/spec/topological_inventory/providers/common/collectors/inventory_collection_storage_spec.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
describe TopologicalInventory::Providers::Common::Collector::InventoryCollectionStorage do
|
2
|
+
before do
|
3
|
+
@storage = described_class.new
|
4
|
+
end
|
5
|
+
|
6
|
+
it "should add collection to data" do
|
7
|
+
@storage.add_collection(:vms)
|
8
|
+
|
9
|
+
expect(@storage.data[:vms]).not_to be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should access the same collection through brackets, method name and data" do
|
13
|
+
@storage.add_collection(:vms)
|
14
|
+
|
15
|
+
vm_name = "My VM"
|
16
|
+
|
17
|
+
@storage[:vms].build(:name => vm_name)
|
18
|
+
|
19
|
+
expect(@storage.vms.data[0].name).to eq(vm_name)
|
20
|
+
expect(@storage[:vms].data[0].name).to eq(vm_name)
|
21
|
+
expect(@storage.data[:vms].data[0].name).to eq(vm_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should create collection automatically when api object exists" do
|
25
|
+
expect("TopologicalInventoryIngressApiClient::Vm".safe_constantize).not_to be_nil
|
26
|
+
|
27
|
+
storage = described_class.new
|
28
|
+
expect(storage.vms).to be_kind_of(TopologicalInventory::Providers::Common::Collector::InventoryCollectionWrapper)
|
29
|
+
|
30
|
+
storage = described_class.new
|
31
|
+
expect(storage[:vms]).to be_kind_of(TopologicalInventory::Providers::Common::Collector::InventoryCollectionWrapper)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should raise NameError when api object doesn't exist" do
|
35
|
+
expect("TopologicalInventoryIngressApiClient::SomethingNonexisting".safe_constantize).to be_nil
|
36
|
+
|
37
|
+
storage = described_class.new
|
38
|
+
|
39
|
+
expect { storage.add_collection(:something_nonexisting) }.to raise_error(NameError)
|
40
|
+
|
41
|
+
expect { storage.something_nonexisting.build(:name => "Vm") }.to raise_error(NameError)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/spec/topological_inventory/providers/common/collectors/inventory_collection_wrapper_spec.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
describe TopologicalInventory::Providers::Common::Collector::InventoryCollectionWrapper do
|
2
|
+
it "builds only existing ingress api client's object" do
|
3
|
+
ic = described_class.new(:name => :some_undefined_class_in_api_models)
|
4
|
+
ic_existing = described_class.new(:name => :vm)
|
5
|
+
|
6
|
+
expect { ic.build({}) }.to raise_error(NameError)
|
7
|
+
expect(ic_existing.build({})).to be_kind_of(TopologicalInventoryIngressApiClient::Vm)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require "tempfile"
|
2
|
+
require "yaml"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
RSpec.describe TopologicalInventory::Providers::Common::CollectorsPool do
|
6
|
+
let(:source1) { {:source => '42b1893c-ebbd-44e9-89b1-5c29b5fe6e10', :schema => 'http', :host => 'cloud.redhat.com', :port => 80} }
|
7
|
+
let(:source2) { {:source => 'fe8bcaea-3670-42c7-bed9-71f6e0bceadd', :schema => 'https', :host => 'cloud.redhat.com', :port => 443} }
|
8
|
+
let(:source3) { {:source => '05838743-4285-404a-b4d6-294045c0d4be', :schema => 'xxx', :host => 'cloud.redhat.com', :port => 1234} }
|
9
|
+
let(:source4) { {:source => '5ed08a3c-3de4-4a90-8ce9-e0f724b2b2e6', :schema => 'xxx', :host => 'cloud.redhat.com', :port => 1234} }
|
10
|
+
let(:sources) { [source1, source2, source3] }
|
11
|
+
|
12
|
+
before do
|
13
|
+
clear_settings
|
14
|
+
end
|
15
|
+
|
16
|
+
subject { described_class.new(nil, nil, :thread_pool_size => 2) }
|
17
|
+
|
18
|
+
context "config reload" do
|
19
|
+
it "changes settings with different configs" do
|
20
|
+
settings = [{:sources => sources},
|
21
|
+
{:sources => [ source2, source4 ]}]
|
22
|
+
|
23
|
+
2.times do |i|
|
24
|
+
config = Tempfile.new(["config#{i}", '.yml'])
|
25
|
+
begin
|
26
|
+
config.write(settings[i].to_yaml)
|
27
|
+
config.rewind
|
28
|
+
|
29
|
+
name, path = path_and_filename(config)
|
30
|
+
subject.send(:config_name=, name.split('.')[0])
|
31
|
+
allow(subject).to receive(:path_to_config).and_return(path)
|
32
|
+
|
33
|
+
subject.send(:reload_config)
|
34
|
+
|
35
|
+
expect(::Settings.sources.to_a.collect(&:to_hash)).to eq(settings[i][:sources])
|
36
|
+
ensure
|
37
|
+
config.close
|
38
|
+
config.unlink
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "secret reload" do
|
45
|
+
it "changes credentials with new secret" do
|
46
|
+
uuid = SecureRandom.uuid
|
47
|
+
|
48
|
+
secrets = [
|
49
|
+
{'updated_at' => Time.now.to_s, uuid => {'username' => 'admin1', 'password' => 'password1'}},
|
50
|
+
{'updated_at' => Time.now.to_s, uuid => {'username' => 'admin2', 'password' => 'password2'}},
|
51
|
+
]
|
52
|
+
|
53
|
+
2.times do |i|
|
54
|
+
secret = Tempfile.new(["credentials#{i}"])
|
55
|
+
begin
|
56
|
+
secret.write(secrets[i].to_json)
|
57
|
+
secret.rewind
|
58
|
+
|
59
|
+
name, path = path_and_filename(secret)
|
60
|
+
|
61
|
+
allow(subject).to receive(:path_to_secrets).and_return(path)
|
62
|
+
stub_const("#{described_class}::SECRET_FILENAME", name)
|
63
|
+
|
64
|
+
subject.send(:reload_secrets)
|
65
|
+
|
66
|
+
expect(subject.send(:secrets)).to eq(secrets[i])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "add or remove collector" do
|
73
|
+
before do
|
74
|
+
::Config.load_and_set_settings('some-value-needed.txt')
|
75
|
+
@collector = double("collector")
|
76
|
+
allow(subject).to receive(:new_collector).and_return(@collector)
|
77
|
+
end
|
78
|
+
|
79
|
+
context "without secrets check" do
|
80
|
+
before do
|
81
|
+
allow(subject).to receive(:secrets_for_source).and_return({})
|
82
|
+
end
|
83
|
+
|
84
|
+
it "adds new collectors from settings" do
|
85
|
+
allow(@collector).to receive(:collect!).and_return(nil)
|
86
|
+
expect(@collector).to receive(:collect!).exactly(sources.size).times
|
87
|
+
|
88
|
+
sources.each do |source|
|
89
|
+
stub_settings_merge(:sources => ::Settings.sources.to_a + [source])
|
90
|
+
|
91
|
+
subject.send(:queue_collectors)
|
92
|
+
end
|
93
|
+
|
94
|
+
pool = subject.send(:thread_pool)
|
95
|
+
pool.shutdown
|
96
|
+
pool.wait_for_termination
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "with secrets check" do
|
101
|
+
let(:secrets) do
|
102
|
+
{ 'updated_at' => Time.now.to_s,
|
103
|
+
source1[:source] => { 'username' => 'admin1', 'password' => 'password1' },
|
104
|
+
source2[:source] => { 'username' => 'admin2', 'password' => 'password2' },
|
105
|
+
'unknown' => { 'username' => 'admin3', 'password' => 'password3' }
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
before do
|
110
|
+
allow(@collector).to receive(:collect!).and_return(nil)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "creates only collectors found in both secret and config" do
|
114
|
+
# 4 sources in yaml config
|
115
|
+
stub_settings_merge(:sources => sources + [source4])
|
116
|
+
# 3 sources in secret
|
117
|
+
allow(subject).to receive(:secrets).and_return(secrets)
|
118
|
+
|
119
|
+
# for each source in yaml secret is searched (4x)
|
120
|
+
expect(subject).to receive(:secrets_for_source).and_call_original.exactly(4).times
|
121
|
+
# only 2 corresponding
|
122
|
+
expect(@collector).to receive(:collect!).exactly(2).times
|
123
|
+
|
124
|
+
subject.send(:queue_collectors)
|
125
|
+
|
126
|
+
pool = subject.send(:thread_pool)
|
127
|
+
pool.shutdown
|
128
|
+
pool.wait_for_termination
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def stub_settings_merge(hash)
|
134
|
+
if defined?(::Settings)
|
135
|
+
Settings.add_source!(hash)
|
136
|
+
Settings.reload!
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def clear_settings
|
141
|
+
::Settings.keys.dup.each { |k| ::Settings.delete_field(k) } if defined?(::Settings)
|
142
|
+
end
|
143
|
+
|
144
|
+
def path_and_filename(tempfile)
|
145
|
+
parts = tempfile.path.split('/')
|
146
|
+
name = parts[-1]
|
147
|
+
path = parts[0..-2].join('/')
|
148
|
+
[name, path]
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
RSpec.describe TopologicalInventory::Providers::Common::Logger do
|
2
|
+
let(:status) { :test }
|
3
|
+
let(:source) { '92844e11-17d5-4998-a33d-d886c3c7a80e' }
|
4
|
+
let(:entity_type) { 'test-entity' }
|
5
|
+
let(:refresh_state_uuid) { 'cd22ba1c-56f6-4fd4-a191-ec8eb8e993a8' }
|
6
|
+
let(:sweep_scope) { [entity_type] }
|
7
|
+
let(:total_parts) { 10 }
|
8
|
+
|
9
|
+
subject { described_class.new }
|
10
|
+
|
11
|
+
it 'receives collecting method' do
|
12
|
+
msg = "[#{status.to_s.upcase}] Collecting #{entity_type}"
|
13
|
+
msg += ", :total parts => #{total_parts}" if total_parts.present?
|
14
|
+
msg += ", :source_uid => #{source}, :refresh_state_uuid => #{refresh_state_uuid}"
|
15
|
+
expect(subject).to receive(:info).with(msg)
|
16
|
+
|
17
|
+
subject.collecting(status, source, entity_type, refresh_state_uuid, total_parts)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'receives sweeping method' do
|
21
|
+
msg = "[#{status.to_s.upcase}] Sweeping inactive records, :sweep_scope => #{sweep_scope}, :source_uid => #{source}, :refresh_state_uuid => #{refresh_state_uuid}"
|
22
|
+
expect(subject).to receive(:info).with(msg)
|
23
|
+
|
24
|
+
subject.sweeping(status, source, sweep_scope, refresh_state_uuid)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'receives collecting error method' do
|
28
|
+
begin
|
29
|
+
raise 'Test exception'
|
30
|
+
rescue => e
|
31
|
+
msg = "[ERROR] Collecting #{entity_type}, :source_uid => #{source}, :refresh_state_uuid => #{refresh_state_uuid}"
|
32
|
+
msg += ":message => #{e.message}\n#{e.backtrace.join("\n")}"
|
33
|
+
expect(subject).to receive(:error).with(msg)
|
34
|
+
|
35
|
+
subject.collecting_error(source, entity_type, refresh_state_uuid, e)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require "topological_inventory/providers/common/operations/processor"
|
2
|
+
|
3
|
+
RSpec.describe TopologicalInventory::Providers::Common::Operations::Processor do
|
4
|
+
let(:topology_api_client) { double }
|
5
|
+
let(:source_id) { 1 }
|
6
|
+
let(:source_ref) { 1000 }
|
7
|
+
let(:service_plan) { double("TopologicalInventoryApiClient::ServicePlan") }
|
8
|
+
let(:service_offering) { double("TopologicalInventoryApiClient::ServiceOffering") }
|
9
|
+
|
10
|
+
# Overriden in contexts
|
11
|
+
let(:payload) { {} }
|
12
|
+
|
13
|
+
before do
|
14
|
+
@processor = described_class.new(nil, nil, payload)
|
15
|
+
allow(@processor).to receive(:logger).and_return(double('null_object').as_null_object)
|
16
|
+
|
17
|
+
allow(service_plan).to receive(:service_offering_id).and_return(1)
|
18
|
+
allow(service_plan).to receive(:name).and_return(double)
|
19
|
+
|
20
|
+
allow(service_offering).to receive(:name).and_return(double)
|
21
|
+
allow(service_offering).to receive(:source_ref).and_return(source_ref)
|
22
|
+
allow(service_offering).to receive(:extra).and_return({:type => 'job_template'})
|
23
|
+
allow(service_offering).to receive(:source_id).and_return(source_id)
|
24
|
+
|
25
|
+
@endpoint_client = double
|
26
|
+
allow(@endpoint_client).to receive(:order_service)
|
27
|
+
|
28
|
+
allow(@processor).to receive(:endpoint_client).and_return(@endpoint_client)
|
29
|
+
allow(@processor).to receive(:topology_api_client).and_return(topology_api_client)
|
30
|
+
allow(topology_api_client).to receive(:update_task)
|
31
|
+
allow(topology_api_client).to receive(:show_service_plan).and_return(service_plan)
|
32
|
+
allow(topology_api_client).to receive(:show_service_offering).and_return(service_offering)
|
33
|
+
end
|
34
|
+
|
35
|
+
context "Order by ServicePlan" do
|
36
|
+
let(:payload) do
|
37
|
+
{
|
38
|
+
'request_context' => {"x-rh-identity" => 'abcd'},
|
39
|
+
'params' => {
|
40
|
+
'order_params' => {
|
41
|
+
'service_plan_id' => 1,
|
42
|
+
'service_parameters' => { :name => "Job 1",
|
43
|
+
:param1 => "Test Topology",
|
44
|
+
:param2 => 50 },
|
45
|
+
'provider_control_parameters' => {}
|
46
|
+
},
|
47
|
+
'service_plan_id' => 1,
|
48
|
+
'task_id' => 1 # in tp-inv api (Task)
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#order_service" do
|
54
|
+
it "orders job" do
|
55
|
+
allow(@processor).to receive(:poll_order_complete_thread).and_return(double)
|
56
|
+
|
57
|
+
expect(@endpoint_client).to receive(:order_service).with(service_offering, service_plan, payload['params']['order_params'])
|
58
|
+
@processor.send(:order_service, payload['params'])
|
59
|
+
end
|
60
|
+
|
61
|
+
it "updates task on error" do
|
62
|
+
err_message = "Sample error"
|
63
|
+
|
64
|
+
allow(@processor).to receive(:poll_order_complete_thread).and_return(double)
|
65
|
+
allow(@processor).to receive(:update_task).and_return(double)
|
66
|
+
allow(@endpoint_client).to receive(:order_service).and_raise(err_message)
|
67
|
+
|
68
|
+
expect(@processor).to receive(:update_task).with(payload['params']['task_id'], :state => "completed", :status => "error", :context => { :error => err_message })
|
69
|
+
|
70
|
+
@processor.send(:order_service, payload['params'])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "Order by ServiceOffering" do
|
76
|
+
let(:payload) do
|
77
|
+
{
|
78
|
+
'request_context' => {"x-rh-identity" => 'abcd'},
|
79
|
+
'params' => {
|
80
|
+
'order_params' => {
|
81
|
+
'service_offering_id' => 1,
|
82
|
+
'service_parameters' => { :name => "Job 1",
|
83
|
+
:param1 => "Test Topology",
|
84
|
+
:param2 => 50 },
|
85
|
+
'provider_control_parameters' => {}
|
86
|
+
},
|
87
|
+
'service_offering_id' => 1,
|
88
|
+
'task_id' => 1 # in tp-inv api (Task)
|
89
|
+
}
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#order_service" do
|
94
|
+
it "orders job" do
|
95
|
+
allow(@processor).to receive(:poll_order_complete_thread).and_return(double)
|
96
|
+
|
97
|
+
expect(@endpoint_client).to receive(:order_service).with(service_offering, nil, payload['params']['order_params'])
|
98
|
+
@processor.send(:order_service, payload['params'])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "topological_inventory/providers/common/save_inventory/saver"
|
2
|
+
|
3
|
+
RSpec.describe TopologicalInventory::Providers::Common::SaveInventory::Saver do
|
4
|
+
let(:client) { instance_double(TopologicalInventoryIngressApiClient::DefaultApi) }
|
5
|
+
let(:logger) { double }
|
6
|
+
let(:base_args) { {client: client, logger: logger} }
|
7
|
+
|
8
|
+
let(:small_json) { {:test => ["values"]} }
|
9
|
+
let(:big_json) { InventorySpecHelper.big_inventory(80_000, 1_000) }
|
10
|
+
|
11
|
+
describe "#save" do
|
12
|
+
subject { described_class.new(args).save(:inventory => inventory) }
|
13
|
+
|
14
|
+
context "when the data size is less than max_bytes" do
|
15
|
+
let(:args) { base_args }
|
16
|
+
let(:inventory) { small_json }
|
17
|
+
|
18
|
+
before do
|
19
|
+
allow(client).to receive(:save_inventory_with_http_info).with(small_json.to_json)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns that it saved one chunk" do
|
23
|
+
is_expected.to eq 1
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not split the payload into batches" do
|
27
|
+
expect(client).to receive(:save_inventory_with_http_info).with(small_json.to_json).once
|
28
|
+
subject
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when the data size is greater than specified max_bytes" do
|
33
|
+
let(:args) { base_args.merge!(:max_bytes => 19_512) }
|
34
|
+
let(:inventory) { big_json }
|
35
|
+
|
36
|
+
before do
|
37
|
+
allow(client).to receive(:save_inventory_with_http_info)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns that it saved five chunks" do
|
41
|
+
is_expected.to eq 5
|
42
|
+
end
|
43
|
+
|
44
|
+
it "splits the payload up into chunks" do
|
45
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(5).times
|
46
|
+
subject
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when the KAFKA_PAYLOAD_MAX_BYTES ENV var is set" do
|
51
|
+
let(:args) { base_args }
|
52
|
+
let(:inventory) { big_json }
|
53
|
+
|
54
|
+
before do
|
55
|
+
allow(ENV).to receive(:[]).with("KAFKA_PAYLOAD_MAX_BYTES").and_return("9_512")
|
56
|
+
allow(client).to receive(:save_inventory_with_http_info)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "splits the payload into smaller chunks" do
|
60
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(10).times
|
61
|
+
is_expected.to eq 10
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
# Specify which files should be added to the gem when it is released.
|
18
18
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
19
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
|
-
`git ls-files -z`.split("\x0")
|
20
|
+
`git ls-files -z`.split("\x0")
|
21
21
|
end
|
22
22
|
spec.bindir = "exe"
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: topological_inventory-providers-common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Slemr
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -254,6 +254,17 @@ files:
|
|
254
254
|
- lib/topological_inventory/providers/common/save_inventory/exception.rb
|
255
255
|
- lib/topological_inventory/providers/common/save_inventory/saver.rb
|
256
256
|
- lib/topological_inventory/providers/common/version.rb
|
257
|
+
- spec/spec_helper.rb
|
258
|
+
- spec/support/inventory_helper.rb
|
259
|
+
- spec/support/shared/availability_check.rb
|
260
|
+
- spec/topological_inventory/providers/common/collectors/inventory_collection_storage_spec.rb
|
261
|
+
- spec/topological_inventory/providers/common/collectors/inventory_collection_wrapper_spec.rb
|
262
|
+
- spec/topological_inventory/providers/common/collectors_pool_spec.rb
|
263
|
+
- spec/topological_inventory/providers/common/logger_spec.rb
|
264
|
+
- spec/topological_inventory/providers/common/operations/processor_spec.rb
|
265
|
+
- spec/topological_inventory/providers/common/operations/source_spec.rb
|
266
|
+
- spec/topological_inventory/providers/common/save_inventory/saver_spec.rb
|
267
|
+
- spec/topological_inventory/providers/common_spec.rb
|
257
268
|
- topological_inventory-providers-common.gemspec
|
258
269
|
homepage: https://github.com/RedHatInsights/topological_inventory-providers-common
|
259
270
|
licenses:
|
@@ -274,7 +285,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
274
285
|
- !ruby/object:Gem::Version
|
275
286
|
version: '0'
|
276
287
|
requirements: []
|
277
|
-
rubygems_version: 3.0.
|
288
|
+
rubygems_version: 3.0.3
|
278
289
|
signing_key:
|
279
290
|
specification_version: 4
|
280
291
|
summary: Common classes for topological-inventory collectors/operations
|