topological_inventory-providers-common 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|