topological_inventory-providers-common 1.0.2 → 1.0.7
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 +49 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +3 -0
- data/.rubocop_cc.yml +4 -0
- data/.rubocop_local.yml +2 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +29 -1
- data/Gemfile +0 -3
- data/lib/topological_inventory/providers/common/logging.rb +8 -0
- data/lib/topological_inventory/providers/common/operations/endpoint_client.rb +3 -0
- data/lib/topological_inventory/providers/common/operations/source.rb +191 -0
- data/lib/topological_inventory/providers/common/operations/sources_api_client.rb +15 -6
- data/lib/topological_inventory/providers/common/save_inventory/saver.rb +13 -4
- 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 +236 -0
- data/spec/topological_inventory/providers/common/collector_spec.rb +171 -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 +7 -1
- metadata +104 -3
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,236 @@
|
|
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(:application_id) { '345' }
|
15
|
+
let(:authentication_id) { '345' }
|
16
|
+
let(:payload) do
|
17
|
+
{
|
18
|
+
'params' => {
|
19
|
+
'source_id' => source_id,
|
20
|
+
'external_tenant' => external_tenant,
|
21
|
+
'timestamp' => Time.now.utc
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
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}\"}]}" }
|
27
|
+
let(:list_endpoint_authentications_response) { "{\"data\":[{\"authtype\":\"username_password\",\"id\":\"#{authentication_id}\",\"resource_id\":\"#{endpoint_id}\",\"resource_type\":\"Endpoint\",\"username\":\"admin\",\"tenant\":\"#{external_tenant}\"}]}" }
|
28
|
+
let(:list_endpoint_authentications_response_empty) { "{\"data\":[]}" }
|
29
|
+
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\"}" }
|
30
|
+
let(:list_applications_response) { {:data => [{:id => "345", :availability_status => "available"}]}.to_json }
|
31
|
+
let(:list_applications_unavailable_response) { {:data => [{:id => "345", :availability_status => "unavailable"}]}.to_json }
|
32
|
+
|
33
|
+
subject { described_class.new(payload["params"]) }
|
34
|
+
|
35
|
+
context "when not checked recently" do
|
36
|
+
before do
|
37
|
+
allow(subject).to receive(:checked_recently?).and_return(false)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "updates Source and Endpoint when available" do
|
41
|
+
# GET
|
42
|
+
stub_get(:endpoint, list_endpoints_response)
|
43
|
+
stub_get(:authentication, list_endpoint_authentications_response)
|
44
|
+
stub_get(:password, internal_api_authentication_response)
|
45
|
+
stub_get(:application, "[]")
|
46
|
+
|
47
|
+
# PATCH
|
48
|
+
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
|
49
|
+
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
|
50
|
+
|
51
|
+
stub_patch(:source, source_patch_body)
|
52
|
+
stub_patch(:endpoint, endpoint_patch_body)
|
53
|
+
|
54
|
+
# Check ---
|
55
|
+
expect(subject).to receive(:connection_check).and_return([described_class::STATUS_AVAILABLE, nil])
|
56
|
+
|
57
|
+
subject.availability_check
|
58
|
+
|
59
|
+
assert_patch(:source, source_patch_body)
|
60
|
+
assert_patch(:endpoint, endpoint_patch_body)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "updates Source and Endpoint when unavailable" do
|
64
|
+
# GET
|
65
|
+
stub_get(:endpoint, list_endpoints_response)
|
66
|
+
stub_get(:authentication, list_endpoint_authentications_response)
|
67
|
+
stub_get(:password, internal_api_authentication_response)
|
68
|
+
stub_get(:application, "[]")
|
69
|
+
|
70
|
+
# PATCH
|
71
|
+
connection_error_message = "Some connection error"
|
72
|
+
source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
|
73
|
+
endpoint_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'availability_status_error' => connection_error_message, 'last_checked_at' => subject.send(:check_time)}.to_json
|
74
|
+
|
75
|
+
stub_patch(:source, source_patch_body)
|
76
|
+
stub_patch(:endpoint, endpoint_patch_body)
|
77
|
+
|
78
|
+
# Check ---
|
79
|
+
expect(subject).to receive(:connection_check).and_return([described_class::STATUS_UNAVAILABLE, connection_error_message])
|
80
|
+
|
81
|
+
subject.availability_check
|
82
|
+
|
83
|
+
assert_patch(:source, source_patch_body)
|
84
|
+
assert_patch(:endpoint, endpoint_patch_body)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "updates only Source to 'unavailable' status if Endpoint not found" do
|
88
|
+
# GET
|
89
|
+
stub_get(:endpoint, '')
|
90
|
+
stub_get(:application, "[]")
|
91
|
+
|
92
|
+
# PATCH
|
93
|
+
source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
|
94
|
+
stub_patch(:source, source_patch_body)
|
95
|
+
|
96
|
+
# Check
|
97
|
+
api_client = subject.send(:api_client)
|
98
|
+
expect(api_client).not_to receive(:update_endpoint)
|
99
|
+
|
100
|
+
subject.availability_check
|
101
|
+
|
102
|
+
assert_patch(:source, source_patch_body)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "updates Source and Endpoint to 'unavailable' if Authentication not found" do
|
106
|
+
# GET
|
107
|
+
stub_get(:endpoint, list_endpoints_response)
|
108
|
+
stub_get(:authentication, list_endpoint_authentications_response_empty)
|
109
|
+
stub_get(:application, "[]")
|
110
|
+
|
111
|
+
# PATCH
|
112
|
+
source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
|
113
|
+
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
|
114
|
+
|
115
|
+
stub_patch(:source, source_patch_body)
|
116
|
+
stub_patch(:endpoint, endpoint_patch_body)
|
117
|
+
|
118
|
+
# Check
|
119
|
+
expect(subject).not_to receive(:connection_check)
|
120
|
+
subject.availability_check
|
121
|
+
|
122
|
+
assert_patch(:source, source_patch_body)
|
123
|
+
assert_patch(:endpoint, endpoint_patch_body)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "when checked recently" do
|
128
|
+
before do
|
129
|
+
allow(subject).to receive(:checked_recently?).and_return(true)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "doesn't do connection check" do
|
133
|
+
expect(subject).not_to receive(:connection_check)
|
134
|
+
expect(WebMock).not_to have_requested(:patch, "#{sources_api_url}/sources/#{source_id}")
|
135
|
+
expect(WebMock).not_to have_requested(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
|
136
|
+
|
137
|
+
subject.availability_check
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when there is an application" do
|
142
|
+
context "when it is available" do
|
143
|
+
it "updates the availability status to available" do
|
144
|
+
# GET
|
145
|
+
stub_get(:endpoint, "[]")
|
146
|
+
stub_get(:application, list_applications_response)
|
147
|
+
# PATCH
|
148
|
+
application_patch_body = {'last_available_at' => subject.send(:check_time), 'last_checked_at' => subject.send(:check_time)}.to_json
|
149
|
+
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
|
150
|
+
|
151
|
+
stub_patch(:source, source_patch_body)
|
152
|
+
stub_patch(:application, application_patch_body)
|
153
|
+
|
154
|
+
# Check
|
155
|
+
expect(subject).not_to receive(:connection_check)
|
156
|
+
subject.availability_check
|
157
|
+
|
158
|
+
assert_patch(:source, source_patch_body)
|
159
|
+
assert_patch(:application, application_patch_body)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "when it is unavailable" do
|
164
|
+
it "updates the availability status to unavailable" do
|
165
|
+
# GET
|
166
|
+
stub_get(:endpoint, "[]")
|
167
|
+
stub_get(:application, list_applications_unavailable_response)
|
168
|
+
# PATCH
|
169
|
+
application_patch_body = {'last_checked_at' => subject.send(:check_time)}.to_json
|
170
|
+
source_patch_body = {'availability_status' => described_class::STATUS_UNAVAILABLE, 'last_checked_at' => subject.send(:check_time)}.to_json
|
171
|
+
|
172
|
+
stub_patch(:source, source_patch_body)
|
173
|
+
stub_patch(:application, application_patch_body)
|
174
|
+
|
175
|
+
# Check
|
176
|
+
expect(subject).not_to receive(:connection_check)
|
177
|
+
subject.availability_check
|
178
|
+
|
179
|
+
assert_patch(:source, source_patch_body)
|
180
|
+
assert_patch(:application, application_patch_body)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def stub_get(object_type, response)
|
186
|
+
case object_type
|
187
|
+
when :endpoint
|
188
|
+
stub_request(:get, "#{sources_api_url}/sources/#{source_id}/endpoints")
|
189
|
+
.with(:headers => headers)
|
190
|
+
.to_return(:status => 200, :body => response, :headers => {})
|
191
|
+
when :authentication
|
192
|
+
stub_request(:get, "#{sources_api_url}/endpoints/#{endpoint_id}/authentications")
|
193
|
+
.with(:headers => headers)
|
194
|
+
.to_return(:status => 200, :body => response, :headers => {})
|
195
|
+
when :password
|
196
|
+
stub_request(:get, "#{host_url}#{sources_internal_api_path}/authentications/#{authentication_id}?expose_encrypted_attribute%5B%5D=password")
|
197
|
+
.with(:headers => headers)
|
198
|
+
.to_return(:status => 200, :body => response, :headers => {})
|
199
|
+
when :application
|
200
|
+
stub_request(:get, "#{sources_api_url}/sources/#{source_id}/applications")
|
201
|
+
.with(:headers => headers)
|
202
|
+
.to_return(:status => 200, :body => response, :headers => {})
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def stub_patch(object_type, data)
|
207
|
+
case object_type
|
208
|
+
when :source
|
209
|
+
stub_request(:patch, "#{sources_api_url}/sources/#{source_id}")
|
210
|
+
.with(:body => data, :headers => headers)
|
211
|
+
.to_return(:status => 200, :body => "", :headers => {})
|
212
|
+
when :endpoint
|
213
|
+
stub_request(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
|
214
|
+
.with(:body => data, :headers => headers)
|
215
|
+
.to_return(:status => 200, :body => "", :headers => {})
|
216
|
+
when :application
|
217
|
+
stub_request(:patch, "#{sources_api_url}/applications/#{application_id}")
|
218
|
+
.with(:body => data, :headers => headers)
|
219
|
+
.to_return(:status => 200, :body => "", :headers => {})
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def assert_patch(object_type, data)
|
224
|
+
case object_type
|
225
|
+
when :source
|
226
|
+
expect(WebMock).to have_requested(:patch, "#{sources_api_url}/sources/#{source_id}")
|
227
|
+
.with(:body => data, :headers => headers).once
|
228
|
+
when :endpoint
|
229
|
+
expect(WebMock).to have_requested(:patch, "#{sources_api_url}/endpoints/#{endpoint_id}")
|
230
|
+
.with(:body => data, :headers => headers).once
|
231
|
+
when :application
|
232
|
+
expect(WebMock).to have_requested(:patch, "#{sources_api_url}/applications/#{application_id}")
|
233
|
+
.with(:body => data, :headers => headers).once
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
RSpec.describe TopologicalInventory::Providers::Common::Collector do
|
2
|
+
let(:collector) do
|
3
|
+
collector = described_class.new(source)
|
4
|
+
|
5
|
+
allow(collector).to receive(:ingress_api_client).and_return(client)
|
6
|
+
allow(collector).to receive(:logger).and_return(logger)
|
7
|
+
allow(logger).to receive(:error)
|
8
|
+
|
9
|
+
collector
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:parser) { TopologicalInventory::Providers::Common::Collector::Parser.new }
|
13
|
+
|
14
|
+
let(:source) { "source_uid" }
|
15
|
+
let(:client) { double }
|
16
|
+
let(:logger) { double }
|
17
|
+
let(:refresh_state_uuid) { SecureRandom.uuid }
|
18
|
+
let(:refresh_state_part_uuid) { SecureRandom.uuid }
|
19
|
+
# based on the default, we can tell how many chunks the saver will break the payload up into
|
20
|
+
let(:max_size) { TopologicalInventory::Providers::Common::SaveInventory::Saver::KAFKA_PAYLOAD_MAX_BYTES_DEFAULT }
|
21
|
+
let(:multiplier) { 0.75 }
|
22
|
+
|
23
|
+
context "#save_inventory" do
|
24
|
+
it "does nothing with empty collections" do
|
25
|
+
parts = collector.send(:save_inventory, [], collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
|
26
|
+
|
27
|
+
expect(parts).to eq 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it "saves 1 part if it fits" do
|
31
|
+
(multiplier * 1000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
|
32
|
+
|
33
|
+
expect(inventory_size(parser.collections.values) / max_size).to eq(0)
|
34
|
+
|
35
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
|
36
|
+
parts = collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
|
37
|
+
expect(parts).to eq 1
|
38
|
+
end
|
39
|
+
|
40
|
+
it "saves 2 parts if over limit with 1 collection" do
|
41
|
+
(multiplier * 2000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
|
42
|
+
|
43
|
+
expect(inventory_size(parser.collections.values) / max_size).to eq(1)
|
44
|
+
|
45
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(2).times
|
46
|
+
parts = collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
|
47
|
+
expect(parts).to eq 2
|
48
|
+
end
|
49
|
+
|
50
|
+
it "saves 2 parts if over limit with 2 collections" do
|
51
|
+
(multiplier * 1000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
|
52
|
+
(multiplier * 1000).floor.times { parser.collections.container_nodes.build(:source_ref => "a" * 950) }
|
53
|
+
|
54
|
+
expect(inventory_size(parser.collections.values) / max_size).to eq(1)
|
55
|
+
|
56
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(2).times
|
57
|
+
parts = collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
|
58
|
+
expect(parts).to eq 2
|
59
|
+
end
|
60
|
+
|
61
|
+
it "saves many parts" do
|
62
|
+
(multiplier * 1500).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
|
63
|
+
(multiplier * 2000).floor.times { parser.collections.container_nodes.build(:source_ref => "a" * 950) }
|
64
|
+
|
65
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(4).times
|
66
|
+
parts = collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid)
|
67
|
+
expect(parts).to eq 4
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'raises exception when entity to save is too big' do
|
71
|
+
parser.collections.container_groups.build(:source_ref => "a" * (1_000_000 * multiplier))
|
72
|
+
|
73
|
+
expect(inventory_size(parser.collections.values) / max_size).to eq(1)
|
74
|
+
# in this case, we first save empty inventory, then the size check fails saving the rest of data
|
75
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
|
76
|
+
|
77
|
+
expect { collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid) }.to(
|
78
|
+
raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'raises exception when entity of second collection is too big' do
|
83
|
+
(multiplier * 1000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
|
84
|
+
parser.collections.container_nodes.build(:source_ref => "a" * (1_000_000 * multiplier))
|
85
|
+
|
86
|
+
expect(inventory_size(parser.collections.values) / max_size).to eq(1)
|
87
|
+
# We save the first collection then it fails on saving the second collection
|
88
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
|
89
|
+
|
90
|
+
expect { collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid) }.to(
|
91
|
+
raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'raises exception when entity of second collection is too big then continues with smaller' do
|
96
|
+
(multiplier * 1000).floor.times { parser.collections.container_groups.build(:source_ref => "a" * 950) }
|
97
|
+
parser.collections.container_nodes.build(:source_ref => "a" * (1_000_000 * multiplier))
|
98
|
+
(multiplier * 1000).floor.times { parser.collections.container_nodes.build(:source_ref => "a" * 950) }
|
99
|
+
|
100
|
+
expect(inventory_size(parser.collections.values) / max_size).to eq(2)
|
101
|
+
# We save the first collection then it fails on saving the second collection
|
102
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
|
103
|
+
|
104
|
+
expect { collector.send(:save_inventory, parser.collections.values, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, refresh_state_part_uuid) }.to(
|
105
|
+
raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "#sweep_inventory" do
|
111
|
+
it "with nil total parts" do
|
112
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(0).times
|
113
|
+
|
114
|
+
collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, nil, [])
|
115
|
+
end
|
116
|
+
|
117
|
+
it "with empty scope " do
|
118
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(0).times
|
119
|
+
|
120
|
+
collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, [])
|
121
|
+
end
|
122
|
+
|
123
|
+
it "with normal scope " do
|
124
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
|
125
|
+
|
126
|
+
collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, [:container_groups])
|
127
|
+
end
|
128
|
+
|
129
|
+
it "with normal targeted scope " do
|
130
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(1).times
|
131
|
+
|
132
|
+
collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, {:container_groups => [{:source_ref => "a"}]})
|
133
|
+
end
|
134
|
+
|
135
|
+
it "fails with scope entity too large " do
|
136
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(0).times
|
137
|
+
|
138
|
+
sweep_scope = {:container_groups => [{:source_ref => "a" * (1_000_002 * multiplier)}]}
|
139
|
+
|
140
|
+
expect { collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, sweep_scope) }.to(
|
141
|
+
raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "fails when scope is too big " do
|
146
|
+
# We should have also sweep scope chunking, that is if we'll do big targeted refresh and sweeping
|
147
|
+
expect(client).to receive(:save_inventory_with_http_info).exactly(0).times
|
148
|
+
|
149
|
+
sweep_scope = {:container_groups => (0..1001 * multiplier).map { {:source_ref => "a" * 1_000} } }
|
150
|
+
|
151
|
+
expect { collector.send(:sweep_inventory, collector.send(:inventory_name), collector.send(:schema_name), refresh_state_uuid, 1, sweep_scope) }.to(
|
152
|
+
raise_error(TopologicalInventory::Providers::Common::SaveInventory::Exception::EntityTooLarge)
|
153
|
+
)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def build_inventory(collections)
|
158
|
+
TopologicalInventoryIngressApiClient::Inventory.new(
|
159
|
+
:name => collector.send(:inventory_name),
|
160
|
+
:schema => TopologicalInventoryIngressApiClient::Schema.new(:name => collector.send(:schema_name)),
|
161
|
+
:source => source,
|
162
|
+
:collections => collections,
|
163
|
+
:refresh_state_uuid => refresh_state_uuid,
|
164
|
+
:refresh_state_part_uuid => refresh_state_part_uuid,
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
def inventory_size(collections)
|
169
|
+
JSON.generate(build_inventory(collections).to_hash).size
|
170
|
+
end
|
171
|
+
end
|