vmpooler 1.1.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/vmpooler/api/helpers.rb +7 -5
- data/lib/vmpooler/api/v1.rb +110 -0
- data/lib/vmpooler/pool_manager.rb +53 -18
- data/lib/vmpooler/version.rb +1 -1
- data/lib/vmpooler.rb +16 -2
- metadata +34 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32b41fec26ba54f25cb6186a6dc1b97904a855f0e5ceeca19978c4c614b5ef55
|
4
|
+
data.tar.gz: 1937db66b56b998fe4030ed133eaad5bbabd15646d3786dfa1539a8ee6e35a95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 747e7156ec196e938a41ffcc7857d01c32d9412b28ce20bea8f52edc6d6579118c7f4d458bdca70d8765b1ce081e44926b0c5560081e6fa27d74110262ce1e2d
|
7
|
+
data.tar.gz: e77c0fe360af30347f20ada5cd34879087b4775aff81914ee41c83639d7a9c91951dc8a5cbf0f2028eabd65312d96e94a95bcc81457de3d1c897d121a79282c2
|
data/lib/vmpooler/api/helpers.rb
CHANGED
@@ -56,14 +56,11 @@ module Vmpooler
|
|
56
56
|
return false
|
57
57
|
end
|
58
58
|
|
59
|
-
def authenticate_ldap(port, host, user_object, base, username_str, password_str)
|
59
|
+
def authenticate_ldap(port, host, encryption_hash, user_object, base, username_str, password_str)
|
60
60
|
ldap = Net::LDAP.new(
|
61
61
|
:host => host,
|
62
62
|
:port => port,
|
63
|
-
:encryption =>
|
64
|
-
:method => :start_tls,
|
65
|
-
:tls_options => { :ssl_version => 'TLSv1' }
|
66
|
-
},
|
63
|
+
:encryption => encryption_hash,
|
67
64
|
:base => base,
|
68
65
|
:auth => {
|
69
66
|
:method => :simple,
|
@@ -86,6 +83,10 @@ module Vmpooler
|
|
86
83
|
ldap_port = auth[:ldap]['port'] || 389
|
87
84
|
ldap_user_obj = auth[:ldap]['user_object']
|
88
85
|
ldap_host = auth[:ldap]['host']
|
86
|
+
ldap_encryption_hash = auth[:ldap]['encryption'] || {
|
87
|
+
:method => :start_tls,
|
88
|
+
:tls_options => { :ssl_version => 'TLSv1' }
|
89
|
+
}
|
89
90
|
|
90
91
|
unless ldap_base.is_a? Array
|
91
92
|
ldap_base = ldap_base.split
|
@@ -100,6 +101,7 @@ module Vmpooler
|
|
100
101
|
result = authenticate_ldap(
|
101
102
|
ldap_port,
|
102
103
|
ldap_host,
|
104
|
+
ldap_encryption_hash,
|
103
105
|
search_user_obj,
|
104
106
|
search_base,
|
105
107
|
username_str,
|
data/lib/vmpooler/api/v1.rb
CHANGED
@@ -28,6 +28,10 @@ module Vmpooler
|
|
28
28
|
Vmpooler::API.settings.config[:pools]
|
29
29
|
end
|
30
30
|
|
31
|
+
def pools_at_startup
|
32
|
+
Vmpooler::API.settings.config[:pools_at_startup]
|
33
|
+
end
|
34
|
+
|
31
35
|
def pool_exists?(template)
|
32
36
|
Vmpooler::API.settings.config[:pool_names].include?(template)
|
33
37
|
end
|
@@ -236,12 +240,25 @@ module Vmpooler
|
|
236
240
|
result
|
237
241
|
end
|
238
242
|
|
243
|
+
def component_to_test(match, labels_string)
|
244
|
+
return if labels_string.nil?
|
245
|
+
|
246
|
+
labels_string_parts = labels_string.split(',')
|
247
|
+
labels_string_parts.each do |part|
|
248
|
+
key, value = part.split('=')
|
249
|
+
next if value.nil?
|
250
|
+
return value if key == match
|
251
|
+
end
|
252
|
+
'none'
|
253
|
+
end
|
254
|
+
|
239
255
|
def update_user_metrics(operation, vmname)
|
240
256
|
backend.multi
|
241
257
|
backend.hget("vmpooler__vm__#{vmname}", 'tag:jenkins_build_url')
|
242
258
|
backend.hget("vmpooler__vm__#{vmname}", 'token:user')
|
243
259
|
backend.hget("vmpooler__vm__#{vmname}", 'template')
|
244
260
|
jenkins_build_url, user, poolname = backend.exec
|
261
|
+
poolname = poolname.gsub('.', '_')
|
245
262
|
|
246
263
|
if user
|
247
264
|
user = user.gsub('.', '_')
|
@@ -276,6 +293,32 @@ module Vmpooler
|
|
276
293
|
puts 'd', "[!] [#{poolname}] failed while evaluating usage labels on '#{vmname}' with an error: #{e}"
|
277
294
|
end
|
278
295
|
|
296
|
+
def reset_pool_size(poolname)
|
297
|
+
result = { 'ok' => false }
|
298
|
+
|
299
|
+
pool_index = pool_index(pools)
|
300
|
+
|
301
|
+
pools_updated = 0
|
302
|
+
sync_pool_sizes
|
303
|
+
|
304
|
+
pool_size_now = pools[pool_index[poolname]]['size'].to_i
|
305
|
+
pool_size_original = pools_at_startup[pool_index[poolname]]['size'].to_i
|
306
|
+
result['pool_size_before_reset'] = pool_size_now
|
307
|
+
result['pool_size_before_overrides'] = pool_size_original
|
308
|
+
|
309
|
+
unless pool_size_now == pool_size_original
|
310
|
+
pools[pool_index[poolname]]['size'] = pool_size_original
|
311
|
+
backend.hdel('vmpooler__config__poolsize', poolname)
|
312
|
+
backend.sadd('vmpooler__pool__undo_size_override', poolname)
|
313
|
+
pools_updated += 1
|
314
|
+
status 201
|
315
|
+
end
|
316
|
+
|
317
|
+
status 200 unless pools_updated > 0
|
318
|
+
result['ok'] = true
|
319
|
+
result
|
320
|
+
end
|
321
|
+
|
279
322
|
def update_pool_size(payload)
|
280
323
|
result = { 'ok' => false }
|
281
324
|
|
@@ -296,6 +339,33 @@ module Vmpooler
|
|
296
339
|
result
|
297
340
|
end
|
298
341
|
|
342
|
+
def reset_pool_template(poolname)
|
343
|
+
result = { 'ok' => false }
|
344
|
+
|
345
|
+
pool_index_live = pool_index(pools)
|
346
|
+
pool_index_original = pool_index(pools_at_startup)
|
347
|
+
|
348
|
+
pools_updated = 0
|
349
|
+
sync_pool_templates
|
350
|
+
|
351
|
+
template_now = pools[pool_index_live[poolname]]['template']
|
352
|
+
template_original = pools_at_startup[pool_index_original[poolname]]['template']
|
353
|
+
result['template_before_reset'] = template_now
|
354
|
+
result['template_before_overrides'] = template_original
|
355
|
+
|
356
|
+
unless template_now == template_original
|
357
|
+
pools[pool_index_live[poolname]]['template'] = template_original
|
358
|
+
backend.hdel('vmpooler__config__template', poolname)
|
359
|
+
backend.sadd('vmpooler__pool__undo_template_override', poolname)
|
360
|
+
pools_updated += 1
|
361
|
+
status 201
|
362
|
+
end
|
363
|
+
|
364
|
+
status 200 unless pools_updated > 0
|
365
|
+
result['ok'] = true
|
366
|
+
result
|
367
|
+
end
|
368
|
+
|
299
369
|
def update_pool_template(payload)
|
300
370
|
result = { 'ok' => false }
|
301
371
|
|
@@ -1362,6 +1432,26 @@ module Vmpooler
|
|
1362
1432
|
JSON.pretty_generate(result)
|
1363
1433
|
end
|
1364
1434
|
|
1435
|
+
delete "#{api_prefix}/config/poolsize/:pool/?" do
|
1436
|
+
content_type :json
|
1437
|
+
result = { 'ok' => false }
|
1438
|
+
|
1439
|
+
if config['experimental_features']
|
1440
|
+
need_token! if Vmpooler::API.settings.config[:auth]
|
1441
|
+
|
1442
|
+
if pool_exists?(params[:pool])
|
1443
|
+
result = reset_pool_size(params[:pool])
|
1444
|
+
else
|
1445
|
+
metrics.increment('config.invalid.unknown')
|
1446
|
+
status 404
|
1447
|
+
end
|
1448
|
+
else
|
1449
|
+
status 405
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
JSON.pretty_generate(result)
|
1453
|
+
end
|
1454
|
+
|
1365
1455
|
post "#{api_prefix}/config/poolsize/?" do
|
1366
1456
|
content_type :json
|
1367
1457
|
result = { 'ok' => false }
|
@@ -1393,6 +1483,26 @@ module Vmpooler
|
|
1393
1483
|
JSON.pretty_generate(result)
|
1394
1484
|
end
|
1395
1485
|
|
1486
|
+
delete "#{api_prefix}/config/pooltemplate/:pool/?" do
|
1487
|
+
content_type :json
|
1488
|
+
result = { 'ok' => false }
|
1489
|
+
|
1490
|
+
if config['experimental_features']
|
1491
|
+
need_token! if Vmpooler::API.settings.config[:auth]
|
1492
|
+
|
1493
|
+
if pool_exists?(params[:pool])
|
1494
|
+
result = reset_pool_template(params[:pool])
|
1495
|
+
else
|
1496
|
+
metrics.increment('config.invalid.unknown')
|
1497
|
+
status 404
|
1498
|
+
end
|
1499
|
+
else
|
1500
|
+
status 405
|
1501
|
+
end
|
1502
|
+
|
1503
|
+
JSON.pretty_generate(result)
|
1504
|
+
end
|
1505
|
+
|
1396
1506
|
post "#{api_prefix}/config/pooltemplate/?" do
|
1397
1507
|
content_type :json
|
1398
1508
|
result = { 'ok' => false }
|
@@ -478,18 +478,6 @@ module Vmpooler
|
|
478
478
|
dereference_mutex(vm)
|
479
479
|
end
|
480
480
|
|
481
|
-
def component_to_test(match, labels_string)
|
482
|
-
return if labels_string.nil?
|
483
|
-
|
484
|
-
labels_string_parts = labels_string.split(',')
|
485
|
-
labels_string_parts.each do |part|
|
486
|
-
key, value = part.split('=')
|
487
|
-
next if value.nil?
|
488
|
-
return value if key == match
|
489
|
-
end
|
490
|
-
'none'
|
491
|
-
end
|
492
|
-
|
493
481
|
def purge_unused_vms_and_folders
|
494
482
|
global_purge = $config[:config]['purge_unconfigured_folders']
|
495
483
|
providers = $config[:providers].keys
|
@@ -796,6 +784,10 @@ module Vmpooler
|
|
796
784
|
# - Fires when a pool reset is requested
|
797
785
|
# - Additional options
|
798
786
|
# :poolname
|
787
|
+
# :undo_override
|
788
|
+
# - Fires when a pool override removal is requested
|
789
|
+
# - Additional options
|
790
|
+
# :poolname
|
799
791
|
#
|
800
792
|
def sleep_with_wakeup_events(loop_delay, wakeup_period = 5, options = {})
|
801
793
|
exit_by = Time.now + loop_delay
|
@@ -838,6 +830,11 @@ module Vmpooler
|
|
838
830
|
break if pending
|
839
831
|
end
|
840
832
|
|
833
|
+
if options[:undo_override]
|
834
|
+
break if redis.sismember('vmpooler__pool__undo_template_override', options[:poolname])
|
835
|
+
break if redis.sismember('vmpooler__pool__undo_size_override', options[:poolname])
|
836
|
+
end
|
837
|
+
|
841
838
|
if options[:pending_vm]
|
842
839
|
pending_vm_count = redis.scard("vmpooler__pending__#{options[:poolname]}")
|
843
840
|
break unless pending_vm_count == 0
|
@@ -892,7 +889,7 @@ module Vmpooler
|
|
892
889
|
loop_delay = (loop_delay * loop_delay_decay).to_i
|
893
890
|
loop_delay = loop_delay_max if loop_delay > loop_delay_max
|
894
891
|
end
|
895
|
-
sleep_with_wakeup_events(loop_delay, loop_delay_min, pool_size_change: true, poolname: pool['name'], pool_template_change: true, clone_target_change: true, pending_vm: true, pool_reset: true)
|
892
|
+
sleep_with_wakeup_events(loop_delay, loop_delay_min, pool_size_change: true, poolname: pool['name'], pool_template_change: true, clone_target_change: true, pending_vm: true, pool_reset: true, undo_override: true)
|
896
893
|
|
897
894
|
unless maxloop == 0
|
898
895
|
break if loop_count >= maxloop
|
@@ -1052,15 +1049,18 @@ module Vmpooler
|
|
1052
1049
|
return if mutex.locked?
|
1053
1050
|
|
1054
1051
|
@redis.with_metrics do |redis|
|
1055
|
-
|
1056
|
-
break if
|
1052
|
+
pool_size_requested = redis.hget('vmpooler__config__poolsize', pool['name'])
|
1053
|
+
break if pool_size_requested.nil?
|
1057
1054
|
|
1058
|
-
|
1059
|
-
|
1055
|
+
pool_size_requested = Integer(pool_size_requested)
|
1056
|
+
pool_size_currently = pool['size']
|
1057
|
+
break if pool_size_requested == pool_size_currently
|
1060
1058
|
|
1061
1059
|
mutex.synchronize do
|
1062
|
-
pool['size'] =
|
1060
|
+
pool['size'] = pool_size_requested
|
1063
1061
|
end
|
1062
|
+
|
1063
|
+
$logger.log('s', "[*] [#{pool['name']}] size updated from #{pool_size_currently} to #{pool_size_requested}")
|
1064
1064
|
end
|
1065
1065
|
end
|
1066
1066
|
|
@@ -1078,6 +1078,38 @@ module Vmpooler
|
|
1078
1078
|
end
|
1079
1079
|
end
|
1080
1080
|
|
1081
|
+
def undo_override(pool, provider)
|
1082
|
+
poolname = pool['name']
|
1083
|
+
mutex = pool_mutex(poolname)
|
1084
|
+
return if mutex.locked?
|
1085
|
+
|
1086
|
+
@redis.with_metrics do |redis|
|
1087
|
+
break unless redis.sismember('vmpooler__pool__undo_template_override', poolname)
|
1088
|
+
|
1089
|
+
redis.srem('vmpooler__pool__undo_template_override', poolname)
|
1090
|
+
template_now = pool['template']
|
1091
|
+
template_original = $config[:pools_at_startup][$config[:pool_index][poolname]]['template']
|
1092
|
+
|
1093
|
+
mutex.synchronize do
|
1094
|
+
update_pool_template(pool, provider, template_original, template_now, redis)
|
1095
|
+
end
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
@redis.with_metrics do |redis|
|
1099
|
+
break unless redis.sismember('vmpooler__pool__undo_size_override', poolname)
|
1100
|
+
|
1101
|
+
redis.srem('vmpooler__pool__undo_size_override', poolname)
|
1102
|
+
pool_size_now = pool['size']
|
1103
|
+
pool_size_original = $config[:pools_at_startup][$config[:pool_index][poolname]]['size']
|
1104
|
+
|
1105
|
+
mutex.synchronize do
|
1106
|
+
pool['size'] = pool_size_original
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
$logger.log('s', "[*] [#{poolname}] size updated from #{pool_size_now} to #{pool_size_original}")
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
|
1081
1113
|
def create_inventory(pool, provider, pool_check_response)
|
1082
1114
|
inventory = {}
|
1083
1115
|
begin
|
@@ -1312,6 +1344,9 @@ module Vmpooler
|
|
1312
1344
|
# Reset a pool when poolreset is requested from the API
|
1313
1345
|
reset_pool(pool)
|
1314
1346
|
|
1347
|
+
# Undo overrides submitted via the api
|
1348
|
+
undo_override(pool, provider)
|
1349
|
+
|
1315
1350
|
pool_check_response
|
1316
1351
|
end
|
1317
1352
|
|
data/lib/vmpooler/version.rb
CHANGED
data/lib/vmpooler.rb
CHANGED
@@ -119,6 +119,11 @@ module Vmpooler
|
|
119
119
|
parsed_config[:auth][:ldap]['port'] = string_to_int(ENV['LDAP_PORT']) if ENV['LDAP_PORT']
|
120
120
|
parsed_config[:auth][:ldap]['base'] = ENV['LDAP_BASE'] if ENV['LDAP_BASE']
|
121
121
|
parsed_config[:auth][:ldap]['user_object'] = ENV['LDAP_USER_OBJECT'] if ENV['LDAP_USER_OBJECT']
|
122
|
+
if parsed_config[:auth]['provider'] == 'ldap' && parsed_config[:auth][:ldap].key?('encryption')
|
123
|
+
parsed_config[:auth][:ldap]['encryption'] = parsed_config[:auth][:ldap]['encryption']
|
124
|
+
elsif parsed_config[:auth]['provider'] == 'ldap'
|
125
|
+
parsed_config[:auth][:ldap]['encryption'] = {}
|
126
|
+
end
|
122
127
|
end
|
123
128
|
|
124
129
|
# Create an index of pool aliases
|
@@ -128,8 +133,17 @@ module Vmpooler
|
|
128
133
|
parsed_config[:pools] = load_pools_from_redis(redis)
|
129
134
|
end
|
130
135
|
|
136
|
+
# Marshal.dump is paired with Marshal.load to create a copy that has its own memory space
|
137
|
+
# so that each can be edited independently
|
138
|
+
# rubocop:disable Security/MarshalLoad
|
139
|
+
|
140
|
+
# retain a copy of the pools that were observed at startup
|
141
|
+
serialized_pools = Marshal.dump(parsed_config[:pools])
|
142
|
+
parsed_config[:pools_at_startup] = Marshal.load(serialized_pools)
|
143
|
+
|
131
144
|
# Create an index of pools by title
|
132
145
|
parsed_config[:pool_index] = pool_index(parsed_config[:pools])
|
146
|
+
# rubocop:enable Security/MarshalLoad
|
133
147
|
|
134
148
|
parsed_config[:pools].each do |pool|
|
135
149
|
parsed_config[:pool_names] << pool['name']
|
@@ -236,12 +250,12 @@ module Vmpooler
|
|
236
250
|
if tracing_enabled.eql?('false')
|
237
251
|
puts "Exporting of traces has been disabled so the span processor has been se to a 'NoopSpanExporter'"
|
238
252
|
span_processor = OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
|
239
|
-
|
253
|
+
OpenTelemetry::SDK::Trace::Export::NoopSpanExporter.new
|
240
254
|
)
|
241
255
|
else
|
242
256
|
puts "Exporting of traces will be done over HTTP in binary Thrift format to #{tracing_jaeger_host}"
|
243
257
|
span_processor = OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
|
244
|
-
|
258
|
+
OpenTelemetry::Exporter::Jaeger::CollectorExporter.new(endpoint: tracing_jaeger_host)
|
245
259
|
)
|
246
260
|
end
|
247
261
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vmpooler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -72,84 +72,84 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - '='
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
75
|
+
version: 0.17.0
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
82
|
+
version: 0.17.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: opentelemetry-instrumentation-concurrent_ruby
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - '='
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: 0.17.0
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - '='
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
96
|
+
version: 0.17.0
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: opentelemetry-instrumentation-redis
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - '='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.
|
103
|
+
version: 0.17.0
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
110
|
+
version: 0.17.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: opentelemetry-instrumentation-sinatra
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - '='
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
117
|
+
version: 0.17.0
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - '='
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.
|
124
|
+
version: 0.17.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: opentelemetry-resource_detectors
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - '='
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
131
|
+
version: 0.17.0
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - '='
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
138
|
+
version: 0.17.0
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: opentelemetry-sdk
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - '='
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0.
|
145
|
+
version: 0.17.0
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - '='
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0.
|
152
|
+
version: 0.17.0
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: pickup
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -400,6 +400,26 @@ dependencies:
|
|
400
400
|
- - ">="
|
401
401
|
- !ruby/object:Gem::Version
|
402
402
|
version: 0.11.2
|
403
|
+
- !ruby/object:Gem::Dependency
|
404
|
+
name: thor
|
405
|
+
requirement: !ruby/object:Gem::Requirement
|
406
|
+
requirements:
|
407
|
+
- - "~>"
|
408
|
+
- !ruby/object:Gem::Version
|
409
|
+
version: '1.0'
|
410
|
+
- - ">="
|
411
|
+
- !ruby/object:Gem::Version
|
412
|
+
version: 1.0.1
|
413
|
+
type: :development
|
414
|
+
prerelease: false
|
415
|
+
version_requirements: !ruby/object:Gem::Requirement
|
416
|
+
requirements:
|
417
|
+
- - "~>"
|
418
|
+
- !ruby/object:Gem::Version
|
419
|
+
version: '1.0'
|
420
|
+
- - ">="
|
421
|
+
- !ruby/object:Gem::Version
|
422
|
+
version: 1.0.1
|
403
423
|
- !ruby/object:Gem::Dependency
|
404
424
|
name: yarjuf
|
405
425
|
requirement: !ruby/object:Gem::Requirement
|