kanrisuru 0.20.0 → 1.0.0.beta1
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 +6 -0
- data/README.md +39 -1
- data/lib/kanrisuru/os_package/collection.rb +4 -5
- data/lib/kanrisuru/processor_count.rb +45 -0
- data/lib/kanrisuru/remote/cluster.rb +78 -29
- data/lib/kanrisuru/version.rb +1 -1
- data/lib/kanrisuru.rb +1 -0
- data/spec/functional/processor_count_spec.rb +17 -0
- data/spec/functional/remote/cluster_spec.rb +87 -1
- data/spec/helper/simplecov.rb +2 -0
- data/spec/integration/remote/cluster/cluster_spec.rb +115 -0
- data/spec/support/shared_examples/integration/remote/host.rb +1 -3
- data/spec/unit/processor_count_spec.rb +10 -0
- data/spec/unit/remote/cluster_spec.rb +5 -0
- metadata +8 -6
- data/spec/integration/remote/cluster/ubuntu_spec.rb +0 -9
- data/spec/support/shared_examples/integration/remote/cluster.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38c3969fd06510c73c0237e611fb1b0acd31dade42489f21b9d118a456e5b834
|
4
|
+
data.tar.gz: 604ca2ef34b74dc435aa35ee24853f8f563e42cff6309d6d7d2c6b6437aa7bc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ef8d8316b07a088e88a4aad0f5d0bb48419890ba0f00895c2bbae01eb356db246104e095d4e7fe06b91d9cebeaa2c2efe973d3302a40e3eeb4714d3e394dd83
|
7
|
+
data.tar.gz: b78410887ec8b5b3e809f4e59934f7c7f6e5d0363ddf9e4a38f436bd42ca0a43de8117f837646edec963c07051f4e546a0705ae2e4581a8971ead44943f22cca
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## Kanrisuru 1.0.0.beta1 (February 27, 2022) ##
|
2
|
+
* Add parallel mode for cluster. Allows hosts to run commands concurrently, reducing time it takes due to the high I/O blocking nature of the network requests.
|
3
|
+
* Add test cases for cluster parallel mode.
|
4
|
+
* Clean up methods on cluster class to use map and each methods in a simplified manner.
|
5
|
+
* Remove `/spec` dir from codecoverage.
|
6
|
+
|
1
7
|
## Kanrisuru 0.20.0 (February 21, 2022) ##
|
2
8
|
* Allow hosts to be connected via proxy host. This is much like using a bastion / jump server.
|
3
9
|
* Add integration test cases for proxy host connection.
|
data/README.md
CHANGED
@@ -54,6 +54,27 @@ host = Kanrisuru::Remote::Host.new(
|
|
54
54
|
)
|
55
55
|
```
|
56
56
|
|
57
|
+
#### Connect with a Jump / Bastion Host
|
58
|
+
To connect to a host behind a firewall through a jump / bastion host, pass either an instance of another Kanrisuru::Remote::Host, or a hash of host config values
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
proxy = Kanrisuru::Remote::Host.new(
|
62
|
+
host: 'proxy-host',
|
63
|
+
username: 'ubuntu',
|
64
|
+
keys: ['~/.ssh/proxy.pem']
|
65
|
+
)
|
66
|
+
|
67
|
+
host = Kanrisuru::Remote::Host.new(
|
68
|
+
host: '1.2.3.4',
|
69
|
+
username: 'ubuntu',
|
70
|
+
keys: ['~/.ssh/id_rsa'],
|
71
|
+
proxy: proxy
|
72
|
+
)
|
73
|
+
|
74
|
+
host.whoami
|
75
|
+
'ubuntu'
|
76
|
+
```
|
77
|
+
|
57
78
|
#### run a simple echo command on the remote host
|
58
79
|
```ruby
|
59
80
|
host.env['VAR'] = 'world'
|
@@ -124,7 +145,24 @@ host = Kanrisuru::Remote::Host.new(host: 'remote-host-4', username: 'rhel', keys
|
|
124
145
|
cluster << host
|
125
146
|
```
|
126
147
|
|
127
|
-
|
148
|
+
#### Run cluster in parallel mode to reduce time waiting on blocking IO
|
149
|
+
```ruby
|
150
|
+
Benchmark.measure do
|
151
|
+
cluster.each do |host|
|
152
|
+
puts cluster.pwd
|
153
|
+
end
|
154
|
+
end
|
155
|
+
# => 0.198980 0.029681 0.228661 ( 5.258496)
|
156
|
+
|
157
|
+
cluster.parallel = true
|
158
|
+
|
159
|
+
Benchmark.measure do
|
160
|
+
cluster.each do |host|
|
161
|
+
puts cluster.pwd
|
162
|
+
end
|
163
|
+
end
|
164
|
+
# => 0.016478 0.007956 0.024434 ( 0.120066)
|
165
|
+
```
|
128
166
|
|
129
167
|
#### To run across all hosts with a single command, cluster will return a array of result hashes
|
130
168
|
```ruby
|
@@ -34,9 +34,8 @@ module Kanrisuru
|
|
34
34
|
os_method_names.each do |method_name|
|
35
35
|
define_method method_name do |*args, &block|
|
36
36
|
cluster = namespace_instance.instance_variable_get(:@cluster)
|
37
|
-
|
38
|
-
|
39
|
-
{ host: host_addr, result: host.send(namespace).send(method_name, *args, &block) }
|
37
|
+
cluster.map do |host|
|
38
|
+
host.send(namespace).send(method_name, *args, &block)
|
40
39
|
end
|
41
40
|
end
|
42
41
|
end
|
@@ -45,8 +44,8 @@ module Kanrisuru
|
|
45
44
|
class_eval do
|
46
45
|
os_method_names.each do |method_name|
|
47
46
|
define_method method_name do |*args, &block|
|
48
|
-
|
49
|
-
|
47
|
+
map do |host|
|
48
|
+
host.send(method_name, *args, &block)
|
50
49
|
end
|
51
50
|
end
|
52
51
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'etc'
|
3
|
+
|
4
|
+
module Kanrisuru
|
5
|
+
# https://github.com/grosser/parallel/blob/master/lib/parallel/processor_count.rb
|
6
|
+
module ProcessorCount
|
7
|
+
# Number of processors seen by the OS, used for process scheduling
|
8
|
+
def self.processor_count
|
9
|
+
@processor_count ||= Integer(ENV['PARALLEL_PROCESSOR_COUNT'] || Etc.nprocessors)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Number of physical processor cores on the current system.
|
13
|
+
def self.physical_processor_count
|
14
|
+
@physical_processor_count ||= begin
|
15
|
+
ppc =
|
16
|
+
case RbConfig::CONFIG["target_os"]
|
17
|
+
when /darwin[12]/
|
18
|
+
IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
|
19
|
+
when /linux/
|
20
|
+
cores = {} # unique physical ID / core ID combinations
|
21
|
+
phy = 0
|
22
|
+
IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
|
23
|
+
if ln.start_with?("physical")
|
24
|
+
phy = ln[/\d+/]
|
25
|
+
elsif ln.start_with?("core")
|
26
|
+
cid = "#{phy}:#{ln[/\d+/]}"
|
27
|
+
cores[cid] = true unless cores[cid]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
cores.count
|
31
|
+
when /mswin|mingw/
|
32
|
+
require 'win32ole'
|
33
|
+
result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
|
34
|
+
"select NumberOfCores from Win32_Processor"
|
35
|
+
)
|
36
|
+
result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
|
37
|
+
else
|
38
|
+
processor_count
|
39
|
+
end
|
40
|
+
# fall back to logical count if physical info is invalid
|
41
|
+
ppc > 0 ? ppc : processor_count
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'thread'
|
2
3
|
|
3
4
|
module Kanrisuru
|
4
5
|
module Remote
|
@@ -6,7 +7,12 @@ module Kanrisuru
|
|
6
7
|
extend OsPackage::Collection
|
7
8
|
include Enumerable
|
8
9
|
|
10
|
+
attr_accessor :parallel, :concurrency
|
11
|
+
|
9
12
|
def initialize(*hosts)
|
13
|
+
@parallel = false
|
14
|
+
@concurrency = local_concurrency
|
15
|
+
|
10
16
|
@hosts = {}
|
11
17
|
hosts.each do |host_opts|
|
12
18
|
add_host(host_opts)
|
@@ -30,37 +36,23 @@ module Kanrisuru
|
|
30
36
|
end
|
31
37
|
|
32
38
|
def execute(command)
|
33
|
-
|
34
|
-
## Need to evaluate each host independently for the command.
|
35
|
-
cmd = create_command(command)
|
36
|
-
|
37
|
-
{ host: host_addr, result: host.execute(cmd) }
|
38
|
-
end
|
39
|
+
map { |host| host.execute(create_command(command)) }
|
39
40
|
end
|
40
41
|
|
41
42
|
def execute_shell(command)
|
42
|
-
|
43
|
-
## Need to evaluate each host independently for the command.
|
44
|
-
cmd = create_command(command)
|
45
|
-
|
46
|
-
{ host: host_addr, result: host.execute_shell(cmd) }
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def each(&block)
|
51
|
-
@hosts.each { |_host_addr, host| block.call(host) }
|
43
|
+
map { |host| host.execute_shell(create_command(command)) }
|
52
44
|
end
|
53
45
|
|
54
46
|
def hostname
|
55
|
-
|
47
|
+
map { |host| host.send(:hostname) }
|
56
48
|
end
|
57
49
|
|
58
50
|
def ping?
|
59
|
-
|
51
|
+
map { |host| host.send(:ping?) }
|
60
52
|
end
|
61
53
|
|
62
54
|
def su(user)
|
63
|
-
|
55
|
+
each { |host| host.su(user) }
|
64
56
|
end
|
65
57
|
|
66
58
|
def chdir(path = '~')
|
@@ -68,15 +60,70 @@ module Kanrisuru
|
|
68
60
|
end
|
69
61
|
|
70
62
|
def cd(path = '~')
|
71
|
-
|
63
|
+
each { |host| host.cd(path) }
|
72
64
|
end
|
73
65
|
|
74
66
|
def disconnect
|
75
|
-
|
67
|
+
each { |host| host.disconnect }
|
68
|
+
end
|
69
|
+
|
70
|
+
def map(&block)
|
71
|
+
parallel? ? each_parallel(preserve: true, &block) : each_sequential(preserve: true, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def each(&block)
|
75
|
+
parallel? ? each_parallel(preserve: false, &block) : each_sequential(preserve: false, &block)
|
76
|
+
end
|
77
|
+
|
78
|
+
def parallel?
|
79
|
+
@parallel
|
80
|
+
end
|
81
|
+
|
82
|
+
def sequential?
|
83
|
+
!parallel?
|
76
84
|
end
|
77
85
|
|
78
86
|
private
|
79
87
|
|
88
|
+
def each_sequential(opts = {}, &block)
|
89
|
+
results = @hosts.map do |host_addr, host|
|
90
|
+
{ host: host_addr, result: block.call(host) }
|
91
|
+
end
|
92
|
+
|
93
|
+
opts[:preserve] ? results : self
|
94
|
+
end
|
95
|
+
|
96
|
+
def each_parallel(opts = {}, &block)
|
97
|
+
queue = Queue.new.tap do |q|
|
98
|
+
@hosts.each { |_, host| q << host }
|
99
|
+
end
|
100
|
+
|
101
|
+
threads = []
|
102
|
+
results = []
|
103
|
+
mutex = Mutex.new
|
104
|
+
|
105
|
+
## No need to spawn more threads then number of hosts in cluster
|
106
|
+
concurrency = queue.length < @concurrency ? queue.length : @concurrency
|
107
|
+
concurrency.times do
|
108
|
+
threads << Thread.new do
|
109
|
+
loop do
|
110
|
+
host = queue.pop(true) rescue Thread.exit
|
111
|
+
|
112
|
+
begin
|
113
|
+
result = block.call(host)
|
114
|
+
mutex.synchronize { results.push({ host: host.host, result: result }) }
|
115
|
+
rescue Exception => exception
|
116
|
+
mutex.synchronize { results.push({ host: host.host, result: exception }) }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
threads.each(&:join)
|
123
|
+
|
124
|
+
opts[:preserve] ? results : self
|
125
|
+
end
|
126
|
+
|
80
127
|
def create_command(command)
|
81
128
|
case command
|
82
129
|
when String
|
@@ -88,12 +135,6 @@ module Kanrisuru
|
|
88
135
|
end
|
89
136
|
end
|
90
137
|
|
91
|
-
def map_host_results(action)
|
92
|
-
@hosts.map do |host_addr, host|
|
93
|
-
{ host: host_addr, result: host.send(action) }
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
138
|
def remove_host(host)
|
98
139
|
if host.instance_of?(Kanrisuru::Remote::Host)
|
99
140
|
removed = false
|
@@ -119,14 +160,22 @@ module Kanrisuru
|
|
119
160
|
end
|
120
161
|
|
121
162
|
def add_host(host_opts)
|
122
|
-
|
163
|
+
case host_opts
|
164
|
+
when Hash
|
123
165
|
@hosts[host_opts[:host]] = Kanrisuru::Remote::Host.new(host_opts)
|
124
|
-
|
166
|
+
when Kanrisuru::Remote::Host
|
125
167
|
@hosts[host_opts.host] = host_opts
|
168
|
+
when Kanrisuru::Remote::Cluster
|
169
|
+
host_opts.send(:each_sequential) { |host| @hosts[host.host] = host }
|
126
170
|
else
|
127
171
|
raise ArgumentError, 'Invalid host option'
|
128
172
|
end
|
129
173
|
end
|
174
|
+
|
175
|
+
def local_concurrency
|
176
|
+
ProcessorCount.physical_processor_count
|
177
|
+
end
|
178
|
+
|
130
179
|
end
|
131
180
|
end
|
132
181
|
end
|
data/lib/kanrisuru/version.rb
CHANGED
data/lib/kanrisuru.rb
CHANGED
@@ -29,6 +29,7 @@ require_relative 'kanrisuru/util'
|
|
29
29
|
require_relative 'kanrisuru/mode'
|
30
30
|
require_relative 'kanrisuru/os_package'
|
31
31
|
require_relative 'kanrisuru/command'
|
32
|
+
require_relative 'kanrisuru/processor_count'
|
32
33
|
require_relative 'kanrisuru/remote'
|
33
34
|
require_relative 'kanrisuru/result'
|
34
35
|
require_relative 'kanrisuru/core'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Kanrisuru::ProcessorCount do
|
6
|
+
it 'gets processor count' do
|
7
|
+
expect(described_class.processor_count).to be > 0
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'gets physical processor count' do
|
11
|
+
expect(described_class.physical_processor_count).to be > 0
|
12
|
+
end
|
13
|
+
|
14
|
+
it "is even factor of logical cpus" do
|
15
|
+
expect(described_class.processor_count % described_class.physical_processor_count).to be == 0
|
16
|
+
end
|
17
|
+
end
|
@@ -27,6 +27,14 @@ RSpec.describe Kanrisuru::Remote::Cluster do
|
|
27
27
|
)
|
28
28
|
end
|
29
29
|
|
30
|
+
let(:host3) do
|
31
|
+
Kanrisuru::Remote::Host.new(
|
32
|
+
host: 'centos-host',
|
33
|
+
username: 'centos',
|
34
|
+
keys: ['id_rsa']
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
30
38
|
it 'adds host to a cluster' do
|
31
39
|
cluster = described_class.new(host1)
|
32
40
|
expect(cluster.hosts.length).to eq(1)
|
@@ -46,7 +54,7 @@ RSpec.describe Kanrisuru::Remote::Cluster do
|
|
46
54
|
username: 'centos',
|
47
55
|
keys: ['id_rsa']
|
48
56
|
}
|
49
|
-
|
57
|
+
|
50
58
|
expect(cluster.hosts.length).to eq(3)
|
51
59
|
expect(cluster.count).to eq(3)
|
52
60
|
expect(cluster.count).to eq(3)
|
@@ -55,6 +63,19 @@ RSpec.describe Kanrisuru::Remote::Cluster do
|
|
55
63
|
expect(cluster.hosts).to include(host2)
|
56
64
|
end
|
57
65
|
|
66
|
+
it 'adds a cluster to another cluster' do
|
67
|
+
cluster1 = described_class.new(host1, host2)
|
68
|
+
cluster2 = described_class.new(host3)
|
69
|
+
|
70
|
+
cluster1 << cluster2
|
71
|
+
expect(cluster1.hosts.length).to eq(3)
|
72
|
+
expect(cluster1.count).to eq(3)
|
73
|
+
expect(cluster1.count).to eq(3)
|
74
|
+
expect(cluster1['centos-host'].username).to eq('centos')
|
75
|
+
expect(cluster1.hosts).to include(host1)
|
76
|
+
expect(cluster1.hosts).to include(host2)
|
77
|
+
end
|
78
|
+
|
58
79
|
it 'fails to add host to a cluster' do
|
59
80
|
cluster = described_class.new
|
60
81
|
expect { cluster << 1 }.to raise_error(ArgumentError)
|
@@ -90,6 +111,58 @@ RSpec.describe Kanrisuru::Remote::Cluster do
|
|
90
111
|
end.to raise_error(ArgumentError)
|
91
112
|
end
|
92
113
|
|
114
|
+
it 'initializes cluster and is run in sequential mode by default' do
|
115
|
+
cluster = described_class.new
|
116
|
+
expect(cluster).to be_sequential
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'sets cluster to parallel mode' do
|
120
|
+
cluster = described_class.new
|
121
|
+
cluster.parallel = true
|
122
|
+
expect(cluster).to be_parallel
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'runs commands on a cluster sequentialy' do
|
126
|
+
cluster = described_class.new(host1, host2)
|
127
|
+
|
128
|
+
expect(cluster).to receive(:each_sequential)
|
129
|
+
|
130
|
+
command = Kanrisuru::Command.new('pwd')
|
131
|
+
cluster.execute(command)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'runs commands on a cluster in parallel across hosts' do
|
135
|
+
cluster = described_class.new(host1, host2)
|
136
|
+
cluster.parallel = true
|
137
|
+
|
138
|
+
expect(cluster).to receive(:each_parallel)
|
139
|
+
|
140
|
+
command = Kanrisuru::Command.new('pwd')
|
141
|
+
cluster.execute(command)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'gets cluster back from each method' do
|
145
|
+
cluster = described_class.new(host1, host2)
|
146
|
+
|
147
|
+
c = cluster.each { |h| h.su('root') }
|
148
|
+
expect(c).to be_instance_of(Kanrisuru::Remote::Cluster)
|
149
|
+
expect(c.object_id).to eq(cluster.object_id)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'gets results back from map method' do
|
153
|
+
cluster = described_class.new(host1, host2)
|
154
|
+
|
155
|
+
results = cluster.map do |host|
|
156
|
+
host.execute('hello')
|
157
|
+
end
|
158
|
+
|
159
|
+
expect(results).to be_instance_of(Array)
|
160
|
+
results.each do |result|
|
161
|
+
expect(result).to have_key(:host)
|
162
|
+
expect(result).to have_key(:result)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
93
166
|
it 'runs execute for a command on a cluster' do
|
94
167
|
cluster = described_class.new
|
95
168
|
cluster << host1
|
@@ -185,6 +258,19 @@ RSpec.describe Kanrisuru::Remote::Cluster do
|
|
185
258
|
StubNetwork.unstub_command!(:realpath)
|
186
259
|
end
|
187
260
|
|
261
|
+
it 'raises and catches exception in parallel mode' do
|
262
|
+
cluster = described_class.new(host1, host2, host3)
|
263
|
+
cluster.parallel = true
|
264
|
+
|
265
|
+
results = cluster.map do |host|
|
266
|
+
raise ArgumentError
|
267
|
+
end
|
268
|
+
|
269
|
+
results.each do |result|
|
270
|
+
expect(result[:result]).to be_instance_of(ArgumentError)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
188
274
|
it 'changes current working directory for each host in a cluster' do
|
189
275
|
cluster = described_class.new(host1, host2)
|
190
276
|
|
data/spec/helper/simplecov.rb
CHANGED
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
RSpec.describe Kanrisuru::Remote::Cluster do
|
7
|
+
let(:host1) do
|
8
|
+
Kanrisuru::Remote::Host.new(
|
9
|
+
host: TestHosts.host('ubuntu')['hostname'],
|
10
|
+
username: TestHosts.host('ubuntu')['username'],
|
11
|
+
keys: [TestHosts.host('ubuntu')['ssh_key']]
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:host2) do
|
16
|
+
Kanrisuru::Remote::Host.new(
|
17
|
+
host: TestHosts.host('debian')['hostname'],
|
18
|
+
username: TestHosts.host('debian')['username'],
|
19
|
+
keys: [TestHosts.host('debian')['ssh_key']]
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:host3) do
|
24
|
+
Kanrisuru::Remote::Host.new(
|
25
|
+
host: TestHosts.host('centos')['hostname'],
|
26
|
+
username: TestHosts.host('centos')['username'],
|
27
|
+
keys: [TestHosts.host('centos')['ssh_key']]
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:host4) do
|
32
|
+
Kanrisuru::Remote::Host.new(
|
33
|
+
host: TestHosts.host('opensuse')['hostname'],
|
34
|
+
username: TestHosts.host('opensuse')['username'],
|
35
|
+
keys: [TestHosts.host('opensuse')['ssh_key']]
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'gets hostname for cluster' do
|
40
|
+
cluster = described_class.new(host1, host2, host3, host4)
|
41
|
+
expect(cluster.hostname).to match([
|
42
|
+
{ host: 'ubuntu-host', result: 'ubuntu-host' },
|
43
|
+
{ host: 'debian-host', result: 'debian-host' },
|
44
|
+
{ host: 'centos-host', result: 'centos-host' },
|
45
|
+
{ host: 'opensuse-host', result: 'opensuse-host' },
|
46
|
+
])
|
47
|
+
|
48
|
+
cluster.disconnect
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'can ping host cluster' do
|
52
|
+
cluster = described_class.new(host1, host2, host3, host4)
|
53
|
+
expect(cluster.ping?).to match([
|
54
|
+
{ host: 'ubuntu-host', result: true },
|
55
|
+
{ host: 'debian-host', result: true },
|
56
|
+
{ host: 'centos-host', result: true },
|
57
|
+
{ host: 'opensuse-host', result: true },
|
58
|
+
])
|
59
|
+
|
60
|
+
cluster.disconnect
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should use specific number of threads as number of hosts' do
|
64
|
+
cluster = described_class.new(host1, host2, host3, host4)
|
65
|
+
cluster.parallel = true
|
66
|
+
|
67
|
+
## Called x2 b/c two methods invoke the parallel runner
|
68
|
+
expect(Thread).to receive(:new).exactly(cluster.count * 2).times.and_call_original
|
69
|
+
|
70
|
+
cluster.hostname
|
71
|
+
cluster.disconnect
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should respect concurrency setting' do
|
75
|
+
concurrency = 2
|
76
|
+
|
77
|
+
cluster = described_class.new(host1, host2, host3, host4)
|
78
|
+
cluster.parallel = true
|
79
|
+
cluster.concurrency = concurrency
|
80
|
+
|
81
|
+
## Called x2 b/c two methods invoke the parallel runner
|
82
|
+
expect(Thread).to receive(:new).exactly(concurrency * 2).times.and_call_original
|
83
|
+
|
84
|
+
cluster.hostname
|
85
|
+
cluster.disconnect
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'saves time running cluster in parallel mode' do
|
89
|
+
cluster = described_class.new(host1, host2, host3, host4)
|
90
|
+
time1 = Benchmark.measure {
|
91
|
+
cluster.each do |host|
|
92
|
+
expect(host.pwd).to be_success
|
93
|
+
end
|
94
|
+
}
|
95
|
+
|
96
|
+
cluster.parallel = true
|
97
|
+
time2 = Benchmark.measure {
|
98
|
+
cluster.each do |host|
|
99
|
+
expect(host.pwd).to be_success
|
100
|
+
end
|
101
|
+
}
|
102
|
+
|
103
|
+
expect(time1.total).to be > time2.total
|
104
|
+
cluster.disconnect
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'disconnects all hosts' do
|
108
|
+
cluster = described_class.new(host1, host2, host3, host4)
|
109
|
+
cluster.disconnect
|
110
|
+
|
111
|
+
cluster.each do |host|
|
112
|
+
expect(host.ssh).to be_closed
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -41,7 +41,7 @@ RSpec.shared_examples 'host' do |os_name, host_json, _spec_dir|
|
|
41
41
|
proxy: proxy
|
42
42
|
)
|
43
43
|
|
44
|
-
## Test
|
44
|
+
## Test instantiation
|
45
45
|
expect(host.proxy).to be_instance_of(Net::SSH::Gateway)
|
46
46
|
expect(host.ssh).to be_instance_of(Net::SSH::Connection::Session)
|
47
47
|
|
@@ -61,8 +61,6 @@ RSpec.shared_examples 'host' do |os_name, host_json, _spec_dir|
|
|
61
61
|
expect(result).to be_instance_of(String)
|
62
62
|
lines = result.split("\n")
|
63
63
|
expect(lines.length).to be >= 1
|
64
|
-
|
65
|
-
## Test upload
|
66
64
|
end
|
67
65
|
|
68
66
|
it 'changes directories' do
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Kanrisuru::ProcessorCount do
|
6
|
+
it 'responds to methods' do
|
7
|
+
expect(described_class).to respond_to(:processor_count)
|
8
|
+
expect(described_class).to respond_to(:physical_processor_count)
|
9
|
+
end
|
10
|
+
end
|
@@ -26,11 +26,16 @@ RSpec.describe Kanrisuru::Remote::Cluster do
|
|
26
26
|
expect(cluster).to respond_to(:execute)
|
27
27
|
expect(cluster).to respond_to(:execute_shell)
|
28
28
|
expect(cluster).to respond_to(:each)
|
29
|
+
expect(cluster).to respond_to(:map)
|
29
30
|
expect(cluster).to respond_to(:hostname)
|
30
31
|
expect(cluster).to respond_to(:ping?)
|
31
32
|
expect(cluster).to respond_to(:su)
|
32
33
|
expect(cluster).to respond_to(:chdir)
|
33
34
|
expect(cluster).to respond_to(:cd)
|
34
35
|
expect(cluster).to respond_to(:disconnect)
|
36
|
+
expect(cluster).to respond_to(:parallel)
|
37
|
+
expect(cluster).to respond_to(:concurrency)
|
38
|
+
expect(cluster).to respond_to(:parallel?)
|
39
|
+
expect(cluster).to respond_to(:sequential?)
|
35
40
|
end
|
36
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kanrisuru
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Mammina
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-02-
|
11
|
+
date: 2022-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parallel_tests
|
@@ -440,6 +440,7 @@ files:
|
|
440
440
|
- lib/kanrisuru/os_package/collection.rb
|
441
441
|
- lib/kanrisuru/os_package/define.rb
|
442
442
|
- lib/kanrisuru/os_package/include.rb
|
443
|
+
- lib/kanrisuru/processor_count.rb
|
443
444
|
- lib/kanrisuru/remote.rb
|
444
445
|
- lib/kanrisuru/remote/cluster.rb
|
445
446
|
- lib/kanrisuru/remote/cpu.rb
|
@@ -486,6 +487,7 @@ files:
|
|
486
487
|
- spec/functional/core/user_spec.rb
|
487
488
|
- spec/functional/core/yum_spec.rb
|
488
489
|
- spec/functional/core/zypper_spec.rb
|
490
|
+
- spec/functional/processor_count_spec.rb
|
489
491
|
- spec/functional/remote/cluster_spec.rb
|
490
492
|
- spec/functional/remote/cpu_spec.rb
|
491
493
|
- spec/functional/remote/env_spec.rb
|
@@ -602,7 +604,7 @@ files:
|
|
602
604
|
- spec/integration/core/zypper/opensuse_spec.rb
|
603
605
|
- spec/integration/core/zypper/sles_spec.rb
|
604
606
|
- spec/integration/os_package_spec.rb
|
605
|
-
- spec/integration/remote/cluster/
|
607
|
+
- spec/integration/remote/cluster/cluster_spec.rb
|
606
608
|
- spec/integration/remote/cpu/centos_spec.rb
|
607
609
|
- spec/integration/remote/cpu/debian_spec.rb
|
608
610
|
- spec/integration/remote/cpu/fedora_spec.rb
|
@@ -670,7 +672,6 @@ files:
|
|
670
672
|
- spec/support/shared_examples/integration/core/user.rb
|
671
673
|
- spec/support/shared_examples/integration/core/yum.rb
|
672
674
|
- spec/support/shared_examples/integration/core/zypper.rb
|
673
|
-
- spec/support/shared_examples/integration/remote/cluster.rb
|
674
675
|
- spec/support/shared_examples/integration/remote/cpu.rb
|
675
676
|
- spec/support/shared_examples/integration/remote/env_spec.rb
|
676
677
|
- spec/support/shared_examples/integration/remote/fstab.rb
|
@@ -697,6 +698,7 @@ files:
|
|
697
698
|
- spec/unit/core/zypper_spec.rb
|
698
699
|
- spec/unit/kanrisuru_spec.rb
|
699
700
|
- spec/unit/mode_spec.rb
|
701
|
+
- spec/unit/processor_count_spec.rb
|
700
702
|
- spec/unit/remote/cluster_spec.rb
|
701
703
|
- spec/unit/remote/cpu_spec.rb
|
702
704
|
- spec/unit/remote/env_spec.rb
|
@@ -722,9 +724,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
722
724
|
version: 2.5.0
|
723
725
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
724
726
|
requirements:
|
725
|
-
- - "
|
727
|
+
- - ">"
|
726
728
|
- !ruby/object:Gem::Version
|
727
|
-
version:
|
729
|
+
version: 1.3.1
|
728
730
|
requirements: []
|
729
731
|
rubygems_version: 3.3.5
|
730
732
|
signing_key:
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.shared_examples 'cluster' do |os_name, host_json, _spec_dir|
|
6
|
-
context "with #{os_name}" do
|
7
|
-
let(:host1) do
|
8
|
-
Kanrisuru::Remote::Host.new(
|
9
|
-
host: host_json['hostname'],
|
10
|
-
username: host_json['username'],
|
11
|
-
keys: [host_json['ssh_key']]
|
12
|
-
)
|
13
|
-
end
|
14
|
-
|
15
|
-
let(:host2) do
|
16
|
-
Kanrisuru::Remote::Host.new(
|
17
|
-
host: 'localhost',
|
18
|
-
username: 'ubuntu',
|
19
|
-
keys: ['~/.ssh/id_rsa']
|
20
|
-
)
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'gets hostname for cluster' do
|
24
|
-
cluster = described_class.new({
|
25
|
-
host: 'localhost',
|
26
|
-
username: 'ubuntu',
|
27
|
-
keys: ['~/.ssh/id_rsa']
|
28
|
-
}, {
|
29
|
-
host: '127.0.0.1',
|
30
|
-
username: 'ubuntu',
|
31
|
-
keys: ['~/.ssh/id_rsa']
|
32
|
-
})
|
33
|
-
|
34
|
-
expect(cluster.hostname).to match([
|
35
|
-
{ host: 'localhost', result: 'ubuntu' },
|
36
|
-
{ host: '127.0.0.1', result: 'ubuntu' }
|
37
|
-
])
|
38
|
-
|
39
|
-
cluster.disconnect
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'can ping host cluster' do
|
43
|
-
cluster = described_class.new({
|
44
|
-
host: 'localhost',
|
45
|
-
username: 'ubuntu',
|
46
|
-
keys: ['~/.ssh/id_rsa']
|
47
|
-
}, {
|
48
|
-
host: '127.0.0.1',
|
49
|
-
username: 'ubuntu',
|
50
|
-
keys: ['~/.ssh/id_rsa']
|
51
|
-
})
|
52
|
-
|
53
|
-
expect(cluster.ping?).to match([
|
54
|
-
{ host: 'localhost', result: true },
|
55
|
-
{ host: '127.0.0.1', result: true }
|
56
|
-
])
|
57
|
-
|
58
|
-
cluster.disconnect
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'disconnects all hosts' do
|
62
|
-
cluster = described_class.new(host1, host2)
|
63
|
-
cluster.disconnect
|
64
|
-
|
65
|
-
cluster.each do |host|
|
66
|
-
expect(host.ssh.closed).to be_truthy
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|