knapsack_pro 0.17.0 → 0.18.0
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/CHANGELOG.md +8 -0
- data/README.md +151 -2
- data/bin/knapsack_pro +1 -0
- data/knapsack_pro.gemspec +1 -1
- data/lib/knapsack_pro.rb +8 -0
- data/lib/knapsack_pro/adapters/base_adapter.rb +10 -0
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +8 -0
- data/lib/knapsack_pro/allocator.rb +28 -14
- data/lib/knapsack_pro/allocator_builder.rb +1 -29
- data/lib/knapsack_pro/base_allocator_builder.rb +35 -0
- data/lib/knapsack_pro/client/api/v1/queues.rb +27 -0
- data/lib/knapsack_pro/config/ci/base.rb +3 -0
- data/lib/knapsack_pro/config/ci/buildkite.rb +4 -0
- data/lib/knapsack_pro/config/ci/circle.rb +4 -1
- data/lib/knapsack_pro/config/ci/semaphore.rb +4 -0
- data/lib/knapsack_pro/config/ci/snap_ci.rb +4 -0
- data/lib/knapsack_pro/config/ci/travis.rb +4 -0
- data/lib/knapsack_pro/config/env.rb +22 -0
- data/lib/knapsack_pro/config/env_generator.rb +19 -0
- data/lib/knapsack_pro/queue_allocator.rb +52 -0
- data/lib/knapsack_pro/queue_allocator_builder.rb +13 -0
- data/lib/knapsack_pro/report.rb +35 -0
- data/lib/knapsack_pro/runners/queue/base_runner.rb +34 -0
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +42 -0
- data/lib/knapsack_pro/task_loader.rb +1 -1
- data/lib/knapsack_pro/version.rb +1 -1
- data/lib/tasks/queue/rspec.rake +9 -0
- data/lib/tasks/salt.rake +0 -2
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +39 -8
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +11 -0
- data/spec/knapsack_pro/allocator_builder_spec.rb +2 -12
- data/spec/knapsack_pro/base_allocator_builder_spec.rb +22 -0
- data/spec/knapsack_pro/client/api/v1/queues_spec.rb +42 -0
- data/spec/knapsack_pro/config/ci/base_spec.rb +1 -0
- data/spec/knapsack_pro/config/ci/buildkite_spec.rb +13 -0
- data/spec/knapsack_pro/config/ci/circle_spec.rb +13 -0
- data/spec/knapsack_pro/config/ci/semaphore_spec.rb +13 -0
- data/spec/knapsack_pro/config/ci/snap_ci_spec.rb +13 -0
- data/spec/knapsack_pro/config/ci/travis_spec.rb +13 -0
- data/spec/knapsack_pro/config/env_generator_spec.rb +52 -0
- data/spec/knapsack_pro/config/env_spec.rb +90 -0
- data/spec/knapsack_pro/queue_allocator_builder_spec.rb +38 -0
- data/spec/knapsack_pro/queue_allocator_spec.rb +85 -0
- data/spec/knapsack_pro/report_spec.rb +69 -0
- data/spec/knapsack_pro/runners/queue/base_runner_spec.rb +67 -0
- data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +118 -0
- metadata +26 -4
@@ -0,0 +1,27 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
module Client
|
3
|
+
module API
|
4
|
+
module V1
|
5
|
+
class Queues < Base
|
6
|
+
class << self
|
7
|
+
def queue(args)
|
8
|
+
action_class.new(
|
9
|
+
endpoint_path: '/v1/queues/queue',
|
10
|
+
http_method: :post,
|
11
|
+
request_hash: {
|
12
|
+
:can_initialize_queue => args.fetch(:can_initialize_queue),
|
13
|
+
:commit_hash => args.fetch(:commit_hash),
|
14
|
+
:branch => args.fetch(:branch),
|
15
|
+
:node_total => args.fetch(:node_total),
|
16
|
+
:node_index => args.fetch(:node_index),
|
17
|
+
:node_build_id => KnapsackPro::Config::Env.ci_node_build_id,
|
18
|
+
:test_files => args.fetch(:test_files)
|
19
|
+
}
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -14,6 +14,12 @@ module KnapsackPro
|
|
14
14
|
0).to_i
|
15
15
|
end
|
16
16
|
|
17
|
+
def ci_node_build_id
|
18
|
+
ENV['KNAPSACK_PRO_CI_NODE_BUILD_ID'] ||
|
19
|
+
ci_env_for(:node_build_id) ||
|
20
|
+
'missing-build-id'
|
21
|
+
end
|
22
|
+
|
17
23
|
def commit_hash
|
18
24
|
ENV['KNAPSACK_PRO_COMMIT_HASH'] ||
|
19
25
|
ci_env_for(:commit_hash)
|
@@ -45,6 +51,22 @@ module KnapsackPro
|
|
45
51
|
recording_enabled == 'true'
|
46
52
|
end
|
47
53
|
|
54
|
+
def queue_recording_enabled
|
55
|
+
ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED']
|
56
|
+
end
|
57
|
+
|
58
|
+
def queue_recording_enabled?
|
59
|
+
queue_recording_enabled == 'true'
|
60
|
+
end
|
61
|
+
|
62
|
+
def queue_id
|
63
|
+
ENV['KNAPSACK_PRO_QUEUE_ID'] || raise('Missing Queue ID')
|
64
|
+
end
|
65
|
+
|
66
|
+
def subset_queue_id
|
67
|
+
ENV['KNAPSACK_PRO_SUBSET_QUEUE_ID'] || raise('Missing Subset Queue ID')
|
68
|
+
end
|
69
|
+
|
48
70
|
def test_files_encrypted
|
49
71
|
ENV['KNAPSACK_PRO_TEST_FILES_ENCRYPTED']
|
50
72
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
module Config
|
3
|
+
class EnvGenerator
|
4
|
+
class << self
|
5
|
+
def set_queue_id
|
6
|
+
if ENV['KNAPSACK_PRO_QUEUE_ID']
|
7
|
+
raise 'Queue ID already generated.'
|
8
|
+
else
|
9
|
+
ENV['KNAPSACK_PRO_QUEUE_ID'] = "#{Time.now.to_i}_#{SecureRandom.uuid}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_subset_queue_id
|
14
|
+
ENV['KNAPSACK_PRO_SUBSET_QUEUE_ID'] = SecureRandom.uuid
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
class QueueAllocator
|
3
|
+
def initialize(args)
|
4
|
+
@test_files = args.fetch(:test_files)
|
5
|
+
@ci_node_total = args.fetch(:ci_node_total)
|
6
|
+
@ci_node_index = args.fetch(:ci_node_index)
|
7
|
+
@ci_node_build_id = args.fetch(:ci_node_build_id)
|
8
|
+
@repository_adapter = args.fetch(:repository_adapter)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_file_paths(can_initialize_queue)
|
12
|
+
action = build_action(can_initialize_queue)
|
13
|
+
connection = KnapsackPro::Client::Connection.new(action)
|
14
|
+
response = connection.call
|
15
|
+
if connection.success?
|
16
|
+
raise ArgumentError.new(response) if connection.errors?
|
17
|
+
prepare_test_files(response)
|
18
|
+
else
|
19
|
+
raise ArgumentError.new("Couldn't connect with Knapsack Pro API. Response: #{response}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :test_files,
|
26
|
+
:ci_node_total,
|
27
|
+
:ci_node_index,
|
28
|
+
:ci_node_build_id,
|
29
|
+
:repository_adapter
|
30
|
+
|
31
|
+
def encrypted_test_files
|
32
|
+
KnapsackPro::Crypto::Encryptor.call(test_files)
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_action(can_initialize_queue)
|
36
|
+
KnapsackPro::Client::API::V1::Queues.queue(
|
37
|
+
can_initialize_queue: can_initialize_queue,
|
38
|
+
commit_hash: repository_adapter.commit_hash,
|
39
|
+
branch: repository_adapter.branch,
|
40
|
+
node_total: ci_node_total,
|
41
|
+
node_index: ci_node_index,
|
42
|
+
node_build_id: ci_node_build_id,
|
43
|
+
test_files: encrypted_test_files,
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def prepare_test_files(response)
|
48
|
+
decrypted_test_files = KnapsackPro::Crypto::Decryptor.call(test_files, response['test_files'])
|
49
|
+
KnapsackPro::TestFilePresenter.paths(decrypted_test_files)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
class QueueAllocatorBuilder < BaseAllocatorBuilder
|
3
|
+
def allocator
|
4
|
+
KnapsackPro::QueueAllocator.new(
|
5
|
+
test_files: test_files,
|
6
|
+
ci_node_total: env.ci_node_total,
|
7
|
+
ci_node_index: env.ci_node_index,
|
8
|
+
ci_node_build_id: env.ci_node_build_id,
|
9
|
+
repository_adapter: repository_adapter,
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/knapsack_pro/report.rb
CHANGED
@@ -2,7 +2,35 @@ module KnapsackPro
|
|
2
2
|
class Report
|
3
3
|
def self.save
|
4
4
|
test_files = KnapsackPro.tracker.to_a
|
5
|
+
create_build_subset(test_files)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.save_subset_queue_to_file
|
9
|
+
test_files = KnapsackPro.tracker.to_a
|
10
|
+
subset_queue_id = KnapsackPro::Config::Env.subset_queue_id
|
11
|
+
|
12
|
+
FileUtils.mkdir_p(queue_path)
|
13
|
+
|
14
|
+
subset_queue_file_name = "#{subset_queue_id}.json"
|
15
|
+
report_path = File.join(queue_path, subset_queue_file_name)
|
16
|
+
report_json = JSON.pretty_generate(test_files)
|
17
|
+
|
18
|
+
File.open(report_path, 'w+') do |f|
|
19
|
+
f.write(report_json)
|
20
|
+
end
|
21
|
+
end
|
5
22
|
|
23
|
+
def self.save_node_queue_to_api
|
24
|
+
test_files = []
|
25
|
+
Dir.glob("#{queue_path}/*.json").each do |file|
|
26
|
+
report = JSON.parse(File.read(file))
|
27
|
+
test_files += report
|
28
|
+
end
|
29
|
+
|
30
|
+
create_build_subset(test_files)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.create_build_subset(test_files)
|
6
34
|
if test_files.empty?
|
7
35
|
KnapsackPro.logger.info("Didn't save time execution report on API server because there are no test files matching criteria on this node. Probably reason might be very narrowed tests list - you run only tests with specified tag and there are fewer test files with the tag than node total number.")
|
8
36
|
return
|
@@ -25,5 +53,12 @@ module KnapsackPro
|
|
25
53
|
KnapsackPro.logger.info('Saved time execution report on API server.')
|
26
54
|
end
|
27
55
|
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def self.queue_path
|
60
|
+
queue_id = KnapsackPro::Config::Env.queue_id
|
61
|
+
"tmp/knapsack_pro/queue/#{queue_id}"
|
62
|
+
end
|
28
63
|
end
|
29
64
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
module Runners
|
3
|
+
module Queue
|
4
|
+
class BaseRunner
|
5
|
+
def self.run(args)
|
6
|
+
raise NotImplementedError
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.run_tests(runner, can_initialize_queue, args, exitstatus)
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(adapter_class)
|
14
|
+
@allocator_builder = KnapsackPro::QueueAllocatorBuilder.new(adapter_class)
|
15
|
+
@allocator = allocator_builder.allocator
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_file_paths(args)
|
19
|
+
can_initialize_queue = args.fetch(:can_initialize_queue)
|
20
|
+
allocator.test_file_paths(can_initialize_queue)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_dir
|
24
|
+
allocator_builder.test_dir
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :allocator_builder,
|
30
|
+
:allocator
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
module Runners
|
3
|
+
module Queue
|
4
|
+
class RSpecRunner < BaseRunner
|
5
|
+
def self.run(args)
|
6
|
+
require 'rspec/core'
|
7
|
+
|
8
|
+
ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_rspec
|
9
|
+
ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED'] = 'true'
|
10
|
+
ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
|
11
|
+
|
12
|
+
runner = new(KnapsackPro::Adapters::RSpecAdapter)
|
13
|
+
|
14
|
+
cli_args = (args || '').split
|
15
|
+
run_tests(runner, true, cli_args, 0)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.run_tests(runner, can_initialize_queue, args, exitstatus)
|
19
|
+
test_file_paths = runner.test_file_paths(can_initialize_queue: can_initialize_queue)
|
20
|
+
|
21
|
+
if test_file_paths.empty?
|
22
|
+
KnapsackPro::Report.save_node_queue_to_api
|
23
|
+
exit(exitstatus)
|
24
|
+
else
|
25
|
+
subset_queue_id = KnapsackPro::Config::EnvGenerator.set_subset_queue_id
|
26
|
+
ENV['KNAPSACK_PRO_SUBSET_QUEUE_ID'] = subset_queue_id
|
27
|
+
|
28
|
+
cli_args = args + [
|
29
|
+
'--default-path', runner.test_dir,
|
30
|
+
] + test_file_paths
|
31
|
+
options = RSpec::Core::ConfigurationOptions.new(cli_args)
|
32
|
+
exit_code = RSpec::Core::Runner.new(options).run($stderr, $stdout)
|
33
|
+
exitstatus = exit_code if exit_code != 0
|
34
|
+
RSpec.world.example_groups.clear
|
35
|
+
|
36
|
+
run_tests(runner, false, args, exitstatus)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/knapsack_pro/version.rb
CHANGED
data/lib/tasks/salt.rake
CHANGED
@@ -17,31 +17,54 @@ describe KnapsackPro::Adapters::BaseAdapter do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
describe '#bind' do
|
20
|
+
let(:recording_enabled?) { false }
|
21
|
+
let(:queue_recording_enabled?) { false }
|
22
|
+
|
20
23
|
before do
|
21
24
|
expect(KnapsackPro::Config::Env).to receive(:recording_enabled?).and_return(recording_enabled?)
|
25
|
+
expect(KnapsackPro::Config::Env).to receive(:queue_recording_enabled?).and_return(queue_recording_enabled?)
|
22
26
|
end
|
23
27
|
|
28
|
+
after { subject.bind }
|
29
|
+
|
24
30
|
context 'when recording enabled' do
|
25
31
|
let(:recording_enabled?) { true }
|
26
32
|
|
33
|
+
before do
|
34
|
+
allow(subject).to receive(:bind_time_tracker)
|
35
|
+
allow(subject).to receive(:bind_save_report)
|
36
|
+
end
|
37
|
+
|
27
38
|
it do
|
28
39
|
logger = instance_double(Logger)
|
29
40
|
expect(KnapsackPro).to receive(:logger).and_return(logger)
|
30
41
|
expect(logger).to receive(:info).with('Test suite time execution recording enabled.')
|
31
|
-
expect(subject).to receive(:bind_time_tracker)
|
32
|
-
expect(subject).to receive(:bind_save_report)
|
33
|
-
subject.bind
|
34
42
|
end
|
43
|
+
it { expect(subject).to receive(:bind_time_tracker) }
|
44
|
+
it { expect(subject).to receive(:bind_save_report) }
|
35
45
|
end
|
36
46
|
|
37
|
-
context 'when recording
|
38
|
-
let(:
|
47
|
+
context 'when queue recording enabled' do
|
48
|
+
let(:queue_recording_enabled?) { true }
|
49
|
+
|
50
|
+
before do
|
51
|
+
allow(subject).to receive(:bind_time_tracker)
|
52
|
+
allow(subject).to receive(:bind_save_queue_report)
|
53
|
+
end
|
39
54
|
|
40
55
|
it do
|
41
|
-
|
42
|
-
expect(
|
43
|
-
|
56
|
+
logger = instance_double(Logger)
|
57
|
+
expect(KnapsackPro).to receive(:logger).and_return(logger)
|
58
|
+
expect(logger).to receive(:info).with('Test suite time execution queue recording enabled.')
|
44
59
|
end
|
60
|
+
it { expect(subject).to receive(:bind_time_tracker) }
|
61
|
+
it { expect(subject).to receive(:bind_save_queue_report) }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when recording disabled' do
|
65
|
+
it { expect(subject).not_to receive(:bind_time_tracker) }
|
66
|
+
it { expect(subject).not_to receive(:bind_save_report) }
|
67
|
+
it { expect(subject).not_to receive(:bind_save_queue_report) }
|
45
68
|
end
|
46
69
|
end
|
47
70
|
|
@@ -60,4 +83,12 @@ describe KnapsackPro::Adapters::BaseAdapter do
|
|
60
83
|
}.to raise_error(NotImplementedError)
|
61
84
|
end
|
62
85
|
end
|
86
|
+
|
87
|
+
describe '#bind_save_queue_report' do
|
88
|
+
it do
|
89
|
+
expect {
|
90
|
+
subject.bind_save_queue_report
|
91
|
+
}.to raise_error(NotImplementedError)
|
92
|
+
end
|
93
|
+
end
|
63
94
|
end
|