vmpooler 0.14.2 → 0.14.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b0e071edb016f25c0379ebc2d7768e945096333d9be2f1f31ce66fc32474fd1
4
- data.tar.gz: 1bbac7250090c48a74bf671d316cb9aa45d1dc55af1f69351e9afd27f7c4ed9d
3
+ metadata.gz: aa377464c0b9bfa6ac09d96064dceb410417ddcfc81e2523e5d32e691f7e8091
4
+ data.tar.gz: d61f1c0ea6fe636b593912cbe26954edd253b07952b999205adf6b9271c8ae3b
5
5
  SHA512:
6
- metadata.gz: 21bc1b2a3c83e82a2e4f3ad6f54a41a380a76a331ca8613b49acf0f7cd2df6ff69162e600f7d449e482e110d1ac7e3e5d9534cd1479d990dd4ff4f8c2c079b74
7
- data.tar.gz: 109ae1cbfa75cc80a3afff19938677856f9ac0fd858cb546d6ea42b8dbbddf515cad3cdb6cfcb340addbe863c5ee1fc9c4025704bdb298584a7ab8696dd27de9
6
+ metadata.gz: 1f5c68048317e9374725e4c5cf71c53d32c28ad3e287b7f82c54a2bb852e039f21b8326fa62160f77dcf7572ed39b4035a005bf214097634844052bab59c828a
7
+ data.tar.gz: 877db96232be8617987096b227f390a643b788d50971ebc629de7f5aca720cd96202d8d0292ecffa8a7fe2e366ee649f9e2eda59202423929abd94f90beae1cd
@@ -2,29 +2,39 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'vmpooler'
5
+ require 'vmpooler/version'
5
6
 
6
7
  config = Vmpooler.config
8
+ logger_file = config[:config]['logfile']
9
+ prefix = config[:config]['prefix']
7
10
  redis_host = config[:redis]['server']
8
11
  redis_port = config[:redis]['port']
9
12
  redis_password = config[:redis]['password']
10
13
  redis_connection_pool_size = config[:redis]['connection_pool_size']
11
14
  redis_connection_pool_timeout = config[:redis]['connection_pool_timeout']
12
- logger_file = config[:config]['logfile']
15
+ redis_reconnect_attempts = config[:redis]['reconnect_attempts']
16
+ tracing_enabled = config[:tracing]['enabled']
17
+ tracing_jaeger_host = config[:tracing]['jaeger_host']
13
18
 
14
19
  logger = Vmpooler::Logger.new logger_file
15
20
  metrics = Vmpooler::Metrics.init(logger, config)
16
21
 
22
+ version = Vmpooler::VERSION
23
+
24
+ startup_args = ARGV
25
+ Vmpooler.configure_tracing(startup_args, prefix, tracing_enabled, tracing_jaeger_host, version)
26
+
17
27
  torun_threads = []
18
28
  if ARGV.count == 0
19
29
  torun = %i[api manager]
20
30
  else
21
31
  torun = []
22
- torun << :api if ARGV.include? 'api'
23
- torun << :manager if ARGV.include? 'manager'
32
+ torun << :api if ARGV.include?('api')
33
+ torun << :manager if ARGV.include?('manager')
24
34
  exit(2) if torun.empty?
25
35
  end
26
36
 
27
- if torun.include? :api
37
+ if torun.include?(:api)
28
38
  api = Thread.new do
29
39
  redis = Vmpooler.new_redis(redis_host, redis_port, redis_password)
30
40
  Vmpooler::API.execute(torun, config, redis, metrics, logger)
@@ -38,12 +48,12 @@ elsif metrics.respond_to?(:setup_prometheus_metrics)
38
48
  torun_threads << prometheus_only_api
39
49
  end
40
50
 
41
- if torun.include? :manager
51
+ if torun.include?(:manager)
42
52
  manager = Thread.new do
43
53
  Vmpooler::PoolManager.new(
44
54
  config,
45
55
  logger,
46
- Vmpooler.redis_connection_pool(redis_host, redis_port, redis_password, redis_connection_pool_size, redis_connection_pool_timeout, metrics),
56
+ Vmpooler.redis_connection_pool(redis_host, redis_port, redis_password, redis_connection_pool_size, redis_connection_pool_timeout, metrics, redis_reconnect_attempts),
47
57
  metrics
48
58
  ).execute!
49
59
  end
@@ -15,6 +15,15 @@ module Vmpooler
15
15
  require 'timeout'
16
16
  require 'yaml'
17
17
 
18
+ # Dependencies for tracing
19
+ require 'opentelemetry-api'
20
+ require 'opentelemetry-instrumentation-concurrent_ruby'
21
+ require 'opentelemetry-instrumentation-redis'
22
+ require 'opentelemetry-instrumentation-sinatra'
23
+ require 'opentelemetry-sdk'
24
+ require 'opentelemetry/exporter/jaeger'
25
+ require 'opentelemetry/resource/detectors'
26
+
18
27
  %w[api metrics logger pool_manager generic_connection_pool].each do |lib|
19
28
  require "vmpooler/#{lib}"
20
29
  end
@@ -58,59 +67,62 @@ module Vmpooler
58
67
  end
59
68
 
60
69
  # Set some configuration defaults
61
- parsed_config[:config]['task_limit'] = string_to_int(ENV['TASK_LIMIT']) || parsed_config[:config]['task_limit'] || 10
62
- parsed_config[:config]['ondemand_clone_limit'] = string_to_int(ENV['ONDEMAND_CLONE_LIMIT']) || parsed_config[:config]['ondemand_clone_limit'] || 10
70
+ parsed_config[:config]['task_limit'] = string_to_int(ENV['TASK_LIMIT']) || parsed_config[:config]['task_limit'] || 10
71
+ parsed_config[:config]['ondemand_clone_limit'] = string_to_int(ENV['ONDEMAND_CLONE_LIMIT']) || parsed_config[:config]['ondemand_clone_limit'] || 10
63
72
  parsed_config[:config]['max_ondemand_instances_per_request'] = string_to_int(ENV['MAX_ONDEMAND_INSTANCES_PER_REQUEST']) || parsed_config[:config]['max_ondemand_instances_per_request'] || 10
64
- parsed_config[:config]['migration_limit'] = string_to_int(ENV['MIGRATION_LIMIT']) if ENV['MIGRATION_LIMIT']
65
- parsed_config[:config]['vm_checktime'] = string_to_int(ENV['VM_CHECKTIME']) || parsed_config[:config]['vm_checktime'] || 1
66
- parsed_config[:config]['vm_lifetime'] = string_to_int(ENV['VM_LIFETIME']) || parsed_config[:config]['vm_lifetime'] || 24
67
- parsed_config[:config]['max_lifetime_upper_limit'] = string_to_int(ENV['MAX_LIFETIME_UPPER_LIMIT']) || parsed_config[:config]['max_lifetime_upper_limit']
68
- parsed_config[:config]['ready_ttl'] = string_to_int(ENV['READY_TTL']) || parsed_config[:config]['ready_ttl'] || 60
69
- parsed_config[:config]['ondemand_request_ttl'] = string_to_int(ENV['ONDEMAND_REQUEST_TTL']) || parsed_config[:config]['ondemand_request_ttl'] || 5
70
- parsed_config[:config]['prefix'] = ENV['PREFIX'] || parsed_config[:config]['prefix'] || ''
71
-
72
- parsed_config[:config]['logfile'] = ENV['LOGFILE'] if ENV['LOGFILE']
73
-
74
- parsed_config[:config]['site_name'] = ENV['SITE_NAME'] if ENV['SITE_NAME']
75
- parsed_config[:config]['domain'] = ENV['DOMAIN'] if ENV['DOMAIN']
76
- parsed_config[:config]['clone_target'] = ENV['CLONE_TARGET'] if ENV['CLONE_TARGET']
77
- parsed_config[:config]['timeout'] = string_to_int(ENV['TIMEOUT']) if ENV['TIMEOUT']
78
- parsed_config[:config]['vm_lifetime_auth'] = string_to_int(ENV['VM_LIFETIME_AUTH']) if ENV['VM_LIFETIME_AUTH']
79
- parsed_config[:config]['max_tries'] = string_to_int(ENV['MAX_TRIES']) if ENV['MAX_TRIES']
80
- parsed_config[:config]['retry_factor'] = string_to_int(ENV['RETRY_FACTOR']) if ENV['RETRY_FACTOR']
81
- parsed_config[:config]['create_folders'] = true?(ENV['CREATE_FOLDERS']) if ENV['CREATE_FOLDERS']
82
- parsed_config[:config]['create_template_delta_disks'] = ENV['CREATE_TEMPLATE_DELTA_DISKS'] if ENV['CREATE_TEMPLATE_DELTA_DISKS']
73
+ parsed_config[:config]['migration_limit'] = string_to_int(ENV['MIGRATION_LIMIT']) if ENV['MIGRATION_LIMIT']
74
+ parsed_config[:config]['vm_checktime'] = string_to_int(ENV['VM_CHECKTIME']) || parsed_config[:config]['vm_checktime'] || 1
75
+ parsed_config[:config]['vm_lifetime'] = string_to_int(ENV['VM_LIFETIME']) || parsed_config[:config]['vm_lifetime'] || 24
76
+ parsed_config[:config]['max_lifetime_upper_limit'] = string_to_int(ENV['MAX_LIFETIME_UPPER_LIMIT']) || parsed_config[:config]['max_lifetime_upper_limit']
77
+ parsed_config[:config]['ready_ttl'] = string_to_int(ENV['READY_TTL']) || parsed_config[:config]['ready_ttl'] || 60
78
+ parsed_config[:config]['ondemand_request_ttl'] = string_to_int(ENV['ONDEMAND_REQUEST_TTL']) || parsed_config[:config]['ondemand_request_ttl'] || 5
79
+ parsed_config[:config]['prefix'] = ENV['PREFIX'] || parsed_config[:config]['prefix'] || ''
80
+ parsed_config[:config]['logfile'] = ENV['LOGFILE'] if ENV['LOGFILE']
81
+ parsed_config[:config]['site_name'] = ENV['SITE_NAME'] if ENV['SITE_NAME']
82
+ parsed_config[:config]['domain'] = ENV['DOMAIN'] if ENV['DOMAIN']
83
+ parsed_config[:config]['clone_target'] = ENV['CLONE_TARGET'] if ENV['CLONE_TARGET']
84
+ parsed_config[:config]['timeout'] = string_to_int(ENV['TIMEOUT']) if ENV['TIMEOUT']
85
+ parsed_config[:config]['vm_lifetime_auth'] = string_to_int(ENV['VM_LIFETIME_AUTH']) if ENV['VM_LIFETIME_AUTH']
86
+ parsed_config[:config]['max_tries'] = string_to_int(ENV['MAX_TRIES']) if ENV['MAX_TRIES']
87
+ parsed_config[:config]['retry_factor'] = string_to_int(ENV['RETRY_FACTOR']) if ENV['RETRY_FACTOR']
88
+ parsed_config[:config]['create_folders'] = true?(ENV['CREATE_FOLDERS']) if ENV['CREATE_FOLDERS']
89
+ parsed_config[:config]['experimental_features'] = ENV['EXPERIMENTAL_FEATURES'] if ENV['EXPERIMENTAL_FEATURES']
90
+ parsed_config[:config]['purge_unconfigured_folders'] = ENV['PURGE_UNCONFIGURED_FOLDERS'] if ENV['PURGE_UNCONFIGURED_FOLDERS']
91
+ parsed_config[:config]['usage_stats'] = ENV['USAGE_STATS'] if ENV['USAGE_STATS']
92
+ parsed_config[:config]['request_logger'] = ENV['REQUEST_LOGGER'] if ENV['REQUEST_LOGGER']
93
+ parsed_config[:config]['create_template_delta_disks'] = ENV['CREATE_TEMPLATE_DELTA_DISKS'] if ENV['CREATE_TEMPLATE_DELTA_DISKS']
83
94
  set_linked_clone(parsed_config)
84
- parsed_config[:config]['experimental_features'] = ENV['EXPERIMENTAL_FEATURES'] if ENV['EXPERIMENTAL_FEATURES']
85
- parsed_config[:config]['purge_unconfigured_folders'] = ENV['PURGE_UNCONFIGURED_FOLDERS'] if ENV['PURGE_UNCONFIGURED_FOLDERS']
86
- parsed_config[:config]['usage_stats'] = ENV['USAGE_STATS'] if ENV['USAGE_STATS']
87
- parsed_config[:config]['request_logger'] = ENV['REQUEST_LOGGER'] if ENV['REQUEST_LOGGER']
88
-
89
- parsed_config[:redis] = parsed_config[:redis] || {}
90
- parsed_config[:redis]['server'] = ENV['REDIS_SERVER'] || parsed_config[:redis]['server'] || 'localhost'
91
- parsed_config[:redis]['port'] = string_to_int(ENV['REDIS_PORT']) if ENV['REDIS_PORT']
92
- parsed_config[:redis]['password'] = ENV['REDIS_PASSWORD'] if ENV['REDIS_PASSWORD']
93
- parsed_config[:redis]['data_ttl'] = string_to_int(ENV['REDIS_DATA_TTL']) || parsed_config[:redis]['data_ttl'] || 168
94
- parsed_config[:redis]['connection_pool_size'] = string_to_int(ENV['REDIS_CONNECTION_POOL_SIZE']) || parsed_config[:redis]['connection_pool_size'] || 10
95
+
96
+ parsed_config[:redis] = parsed_config[:redis] || {}
97
+ parsed_config[:redis]['server'] = ENV['REDIS_SERVER'] || parsed_config[:redis]['server'] || 'localhost'
98
+ parsed_config[:redis]['port'] = string_to_int(ENV['REDIS_PORT']) if ENV['REDIS_PORT']
99
+ parsed_config[:redis]['password'] = ENV['REDIS_PASSWORD'] if ENV['REDIS_PASSWORD']
100
+ parsed_config[:redis]['data_ttl'] = string_to_int(ENV['REDIS_DATA_TTL']) || parsed_config[:redis]['data_ttl'] || 168
101
+ parsed_config[:redis]['connection_pool_size'] = string_to_int(ENV['REDIS_CONNECTION_POOL_SIZE']) || parsed_config[:redis]['connection_pool_size'] || 10
95
102
  parsed_config[:redis]['connection_pool_timeout'] = string_to_int(ENV['REDIS_CONNECTION_POOL_TIMEOUT']) || parsed_config[:redis]['connection_pool_timeout'] || 5
103
+ parsed_config[:redis]['reconnect_attempts'] = string_to_int(ENV['REDIS_RECONNECT_ATTEMPTS']) || parsed_config[:redis]['reconnect_attempts'] || 10
96
104
 
97
- parsed_config[:statsd] = parsed_config[:statsd] || {} if ENV['STATSD_SERVER']
105
+ parsed_config[:statsd] = parsed_config[:statsd] || {} if ENV['STATSD_SERVER']
98
106
  parsed_config[:statsd]['server'] = ENV['STATSD_SERVER'] if ENV['STATSD_SERVER']
99
107
  parsed_config[:statsd]['prefix'] = ENV['STATSD_PREFIX'] if ENV['STATSD_PREFIX']
100
- parsed_config[:statsd]['port'] = string_to_int(ENV['STATSD_PORT']) if ENV['STATSD_PORT']
108
+ parsed_config[:statsd]['port'] = string_to_int(ENV['STATSD_PORT']) if ENV['STATSD_PORT']
101
109
 
102
- parsed_config[:graphite] = parsed_config[:graphite] || {} if ENV['GRAPHITE_SERVER']
110
+ parsed_config[:graphite] = parsed_config[:graphite] || {} if ENV['GRAPHITE_SERVER']
103
111
  parsed_config[:graphite]['server'] = ENV['GRAPHITE_SERVER'] if ENV['GRAPHITE_SERVER']
104
112
  parsed_config[:graphite]['prefix'] = ENV['GRAPHITE_PREFIX'] if ENV['GRAPHITE_PREFIX']
105
- parsed_config[:graphite]['port'] = string_to_int(ENV['GRAPHITE_PORT']) if ENV['GRAPHITE_PORT']
113
+ parsed_config[:graphite]['port'] = string_to_int(ENV['GRAPHITE_PORT']) if ENV['GRAPHITE_PORT']
114
+
115
+ parsed_config[:tracing] = parsed_config[:tracing] || {}
116
+ parsed_config[:tracing]['enabled'] = ENV['VMPOOLER_TRACING_ENABLED'] || parsed_config[:tracing]['enabled'] || 'false'
117
+ parsed_config[:tracing]['jaeger_host'] = ENV['VMPOOLER_TRACING_JAEGER_HOST'] || parsed_config[:tracing]['jaeger_host'] || 'http://localhost:14268/api/traces'
106
118
 
107
119
  parsed_config[:auth] = parsed_config[:auth] || {} if ENV['AUTH_PROVIDER']
108
120
  if parsed_config.key? :auth
109
- parsed_config[:auth]['provider'] = ENV['AUTH_PROVIDER'] if ENV['AUTH_PROVIDER']
110
- parsed_config[:auth][:ldap] = parsed_config[:auth][:ldap] || {} if parsed_config[:auth]['provider'] == 'ldap'
111
- parsed_config[:auth][:ldap]['host'] = ENV['LDAP_HOST'] if ENV['LDAP_HOST']
112
- parsed_config[:auth][:ldap]['port'] = string_to_int(ENV['LDAP_PORT']) if ENV['LDAP_PORT']
113
- parsed_config[:auth][:ldap]['base'] = ENV['LDAP_BASE'] if ENV['LDAP_BASE']
121
+ parsed_config[:auth]['provider'] = ENV['AUTH_PROVIDER'] if ENV['AUTH_PROVIDER']
122
+ parsed_config[:auth][:ldap] = parsed_config[:auth][:ldap] || {} if parsed_config[:auth]['provider'] == 'ldap'
123
+ parsed_config[:auth][:ldap]['host'] = ENV['LDAP_HOST'] if ENV['LDAP_HOST']
124
+ parsed_config[:auth][:ldap]['port'] = string_to_int(ENV['LDAP_PORT']) if ENV['LDAP_PORT']
125
+ parsed_config[:auth][:ldap]['base'] = ENV['LDAP_BASE'] if ENV['LDAP_BASE']
114
126
  parsed_config[:auth][:ldap]['user_object'] = ENV['LDAP_USER_OBJECT'] if ENV['LDAP_USER_OBJECT']
115
127
  end
116
128
 
@@ -164,7 +176,7 @@ module Vmpooler
164
176
  pools
165
177
  end
166
178
 
167
- def self.redis_connection_pool(host, port, password, size, timeout, metrics)
179
+ def self.redis_connection_pool(host, port, password, size, timeout, metrics, redis_reconnect_attempts = 0)
168
180
  Vmpooler::PoolManager::GenericConnectionPool.new(
169
181
  metrics: metrics,
170
182
  connpool_type: 'redis_connection_pool',
@@ -173,13 +185,14 @@ module Vmpooler
173
185
  timeout: timeout
174
186
  ) do
175
187
  connection = Concurrent::Hash.new
176
- redis = new_redis(host, port, password)
188
+ redis = new_redis(host, port, password, redis_reconnect_attempts)
177
189
  connection['connection'] = redis
178
190
  end
179
191
  end
180
192
 
181
- def self.new_redis(host = 'localhost', port = nil, password = nil)
182
- Redis.new(host: host, port: port, password: password)
193
+ def self.new_redis(host = 'localhost', port = nil, password = nil, redis_reconnect_attempts = 10)
194
+ Redis.new(host: host, port: port, password: password, reconnect_attempts: redis_reconnect_attempts, reconnect_delay: 1.5,
195
+ reconnect_delay_max: 10.0)
183
196
  end
184
197
 
185
198
  def self.pools(conf)
@@ -213,4 +226,44 @@ module Vmpooler
213
226
  parsed_config[:config]['create_linked_clones'] = ENV['CREATE_LINKED_CLONES'] if ENV['CREATE_LINKED_CLONES'] =~ /true|false/
214
227
  parsed_config[:config]['create_linked_clones'] = true?(parsed_config[:config]['create_linked_clones']) if parsed_config[:config]['create_linked_clones']
215
228
  end
229
+
230
+ def self.configure_tracing(startup_args, prefix, tracing_enabled, tracing_jaeger_host, version)
231
+ if startup_args.length == 1 && startup_args.include?('api')
232
+ service_name = 'vmpooler-api'
233
+ elsif startup_args.length == 1 && startup_args.include?('manager')
234
+ service_name = 'vmpooler-manager'
235
+ else
236
+ service_name = 'vmpooler'
237
+ end
238
+
239
+ service_name += "-#{prefix}" unless prefix.empty?
240
+
241
+ if tracing_enabled.eql?('false')
242
+ puts "Exporting of traces has been disabled so the span processor has been se to a 'NoopSpanExporter'"
243
+ span_processor = OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
244
+ exporter: OpenTelemetry::SDK::Trace::Export::NoopSpanExporter.new
245
+ )
246
+ else
247
+ puts "Exporting of traces will be done over HTTP in binary Thrift format to #{tracing_jaeger_host}"
248
+ span_processor = OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
249
+ exporter: OpenTelemetry::Exporter::Jaeger::CollectorExporter.new(endpoint: tracing_jaeger_host)
250
+ )
251
+ end
252
+
253
+ OpenTelemetry::SDK.configure do |c|
254
+ c.use 'OpenTelemetry::Instrumentation::Sinatra'
255
+ c.use 'OpenTelemetry::Instrumentation::ConcurrentRuby'
256
+ c.use 'OpenTelemetry::Instrumentation::Redis'
257
+
258
+ c.add_span_processor(span_processor)
259
+
260
+ c.resource = OpenTelemetry::Resource::Detectors::AutoDetector.detect
261
+ c.resource = OpenTelemetry::SDK::Resources::Resource.create(
262
+ {
263
+ OpenTelemetry::SDK::Resources::Constants::SERVICE_RESOURCE[:name] => service_name,
264
+ OpenTelemetry::SDK::Resources::Constants::SERVICE_RESOURCE[:version] => version
265
+ }
266
+ )
267
+ end
268
+ end
216
269
  end
@@ -38,8 +38,8 @@ module Vmpooler
38
38
  # Using customised collector that filters out hostnames on API paths
39
39
  require 'vmpooler/metrics/promstats/collector_middleware'
40
40
  require 'prometheus/middleware/exporter'
41
- use Vmpooler::Metrics::Promstats::CollectorMiddleware, metrics_prefix: "#{metrics.metrics_prefix}_http"
42
- use Prometheus::Middleware::Exporter, path: metrics.endpoint
41
+ use Vmpooler::Metrics::Promstats::CollectorMiddleware, metrics_prefix: "#{metrics.prometheus_prefix}_http"
42
+ use Prometheus::Middleware::Exporter, path: metrics.prometheus_endpoint
43
43
  end
44
44
 
45
45
  if torun.include? :api
@@ -89,18 +89,16 @@ module Vmpooler
89
89
  template_backends += aliases
90
90
  weighted_pools = get_pool_weights(template_backends)
91
91
 
92
- pickup = Pickup.new(weighted_pools) if weighted_pools.count == template_backends.count
93
- count.to_i.times do
94
- if pickup
92
+ if weighted_pools.count > 1 && weighted_pools.count == template_backends.count
93
+ pickup = Pickup.new(weighted_pools)
94
+ count.to_i.times do
95
95
  selection << pickup.pick
96
- else
96
+ end
97
+ else
98
+ count.to_i.times do
97
99
  selection << template_backends.sample
98
100
  end
99
101
  end
100
- else
101
- count.to_i.times do
102
- selection << template
103
- end
104
102
  end
105
103
 
106
104
  count_selection(selection)
@@ -809,7 +807,7 @@ module Vmpooler
809
807
 
810
808
  post "#{api_prefix}/ondemandvm/?" do
811
809
  content_type :json
812
- metrics.increment('api_vm.post.ondemand.requestid')
810
+ metrics.increment('http_requests_vm_total.post.ondemand.requestid')
813
811
 
814
812
  need_token! if Vmpooler::API.settings.config[:auth]
815
813
 
@@ -847,7 +845,7 @@ module Vmpooler
847
845
  post "#{api_prefix}/ondemandvm/:template/?" do
848
846
  content_type :json
849
847
  result = { 'ok' => false }
850
- metrics.increment('api_vm.delete.ondemand.template')
848
+ metrics.increment('http_requests_vm_total.delete.ondemand.template')
851
849
 
852
850
  need_token! if Vmpooler::API.settings.config[:auth]
853
851
 
@@ -874,7 +872,7 @@ module Vmpooler
874
872
 
875
873
  get "#{api_prefix}/ondemandvm/:requestid/?" do
876
874
  content_type :json
877
- metrics.increment('api_vm.get.ondemand.request')
875
+ metrics.increment('http_requests_vm_total.get.ondemand.request')
878
876
 
879
877
  status 404
880
878
  result = check_ondemand_request(params[:requestid])
@@ -885,7 +883,7 @@ module Vmpooler
885
883
  delete "#{api_prefix}/ondemandvm/:requestid/?" do
886
884
  content_type :json
887
885
  need_token! if Vmpooler::API.settings.config[:auth]
888
- metrics.increment('api_vm.delete.ondemand.request')
886
+ metrics.increment('http_requests_vm_total.delete.ondemand.request')
889
887
 
890
888
  status 404
891
889
  result = delete_ondemand_request(params[:requestid])
@@ -896,7 +894,7 @@ module Vmpooler
896
894
  post "#{api_prefix}/vm/?" do
897
895
  content_type :json
898
896
  result = { 'ok' => false }
899
- metrics.increment('api_vm.post.vm.checkout')
897
+ metrics.increment('http_requests_vm_total.post.vm.checkout')
900
898
 
901
899
  payload = JSON.parse(request.body.read)
902
900
 
@@ -1051,7 +1049,7 @@ module Vmpooler
1051
1049
  post "#{api_prefix}/vm/:template/?" do
1052
1050
  content_type :json
1053
1051
  result = { 'ok' => false }
1054
- metrics.increment('api_vm.get.vm.template')
1052
+ metrics.increment('http_requests_vm_total.get.vm.template')
1055
1053
 
1056
1054
  payload = extract_templates_from_query_params(params[:template])
1057
1055
 
@@ -1075,7 +1073,7 @@ module Vmpooler
1075
1073
 
1076
1074
  get "#{api_prefix}/vm/:hostname/?" do
1077
1075
  content_type :json
1078
- metrics.increment('api_vm.get.vm.hostname')
1076
+ metrics.increment('http_requests_vm_total.get.vm.hostname')
1079
1077
 
1080
1078
  result = {}
1081
1079
 
@@ -1148,7 +1146,7 @@ module Vmpooler
1148
1146
 
1149
1147
  delete "#{api_prefix}/vm/:hostname/?" do
1150
1148
  content_type :json
1151
- metrics.increment('api_vm.delete.vm.hostname')
1149
+ metrics.increment('http_requests_vm_total.delete.vm.hostname')
1152
1150
 
1153
1151
  result = {}
1154
1152
 
@@ -1177,7 +1175,7 @@ module Vmpooler
1177
1175
 
1178
1176
  put "#{api_prefix}/vm/:hostname/?" do
1179
1177
  content_type :json
1180
- metrics.increment('api_vm.put.vm.modify')
1178
+ metrics.increment('http_requests_vm_total.put.vm.modify')
1181
1179
 
1182
1180
  status 404
1183
1181
  result = { 'ok' => false }
@@ -1254,7 +1252,7 @@ module Vmpooler
1254
1252
 
1255
1253
  post "#{api_prefix}/vm/:hostname/disk/:size/?" do
1256
1254
  content_type :json
1257
- metrics.increment('api_vm.post.vm.disksize')
1255
+ metrics.increment('http_requests_vm_total.post.vm.disksize')
1258
1256
 
1259
1257
  need_token! if Vmpooler::API.settings.config[:auth]
1260
1258
 
@@ -1278,7 +1276,7 @@ module Vmpooler
1278
1276
 
1279
1277
  post "#{api_prefix}/vm/:hostname/snapshot/?" do
1280
1278
  content_type :json
1281
- metrics.increment('api_vm.post.vm.snapshot')
1279
+ metrics.increment('http_requests_vm_total.post.vm.snapshot')
1282
1280
 
1283
1281
  need_token! if Vmpooler::API.settings.config[:auth]
1284
1282
 
@@ -1304,7 +1302,7 @@ module Vmpooler
1304
1302
 
1305
1303
  post "#{api_prefix}/vm/:hostname/snapshot/:snapshot/?" do
1306
1304
  content_type :json
1307
- metrics.increment('api_vm.post.vm.disksize')
1305
+ metrics.increment('http_requests_vm_total.post.vm.disksize')
1308
1306
 
1309
1307
  need_token! if Vmpooler::API.settings.config[:auth]
1310
1308
 
@@ -5,7 +5,7 @@ require 'prometheus/client'
5
5
  module Vmpooler
6
6
  class Metrics
7
7
  class Promstats < Metrics
8
- attr_reader :prefix, :endpoint, :metrics_prefix
8
+ attr_reader :prefix, :prometheus_endpoint, :prometheus_prefix
9
9
 
10
10
  # Constants for Metric Types
11
11
  M_COUNTER = 1
@@ -21,25 +21,139 @@ module Vmpooler
21
21
  REDIS_CONNECT_BUCKETS = [1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 18.0, 23.0].freeze
22
22
 
23
23
  @p_metrics = {}
24
+ @torun = []
24
25
 
25
26
  def initialize(logger, params = {})
26
27
  @prefix = params['prefix'] || 'vmpooler'
27
- @metrics_prefix = params['metrics_prefix'] || 'vmpooler'
28
- @endpoint = params['endpoint'] || '/prometheus'
28
+ @prometheus_prefix = params['prometheus_prefix'] || 'vmpooler'
29
+ @prometheus_endpoint = params['prometheus_endpoint'] || '/prometheus'
29
30
  @logger = logger
30
31
 
31
32
  # Setup up prometheus registry and data structures
32
33
  @prometheus = Prometheus::Client.registry
33
34
  end
34
35
 
35
- # Metrics structure used to register the metrics and also translate/interpret the incoming metrics.
36
+ =begin # rubocop:disable Style/BlockComments
37
+ The Metrics table is used to register metrics and translate/interpret the incoming metrics.
38
+
39
+ This table describes all of the prometheus metrics that are recognised by the application.
40
+ The background documentation for defining metrics is at: https://prometheus.io/docs/introduction/
41
+ In particular, the naming practices should be adhered to: https://prometheus.io/docs/practices/naming/
42
+ The Ruby Client docs are also useful: https://github.com/prometheus/client_ruby
43
+
44
+ The table here allows the currently used stats definitions to be translated correctly for Prometheus.
45
+ The current format is of the form A.B.C, where the final fields may be actual values (e.g. poolname).
46
+ Prometheus metrics cannot use the '.' as a character, so this is either translated into '_' or
47
+ variable parameters are expressed as labels accompanying the metric.
48
+
49
+ Sample statistics are:
50
+ # Example showing hostnames (FQDN)
51
+ migrate_from.pix-jj26-chassis1-2.ops.puppetlabs.net
52
+ migrate_to.pix-jj26-chassis1-8.ops.puppetlabs.net
53
+
54
+ # Example showing poolname as a parameter
55
+ poolreset.invalid.centos-8-x86_64
56
+
57
+ # Examples showing similar sub-typed checkout stats
58
+ checkout.empty.centos-8-x86_64
59
+ checkout.invalid.centos-8-x86_64
60
+ checkout.invalid.unknown
61
+ checkout.success.centos-8-x86_64
62
+
63
+ # Stats without any final parameter.
64
+ connect.fail
65
+ connect.open
66
+ delete.failed
67
+ delete.success
68
+
69
+ # Stats with multiple param_labels
70
+ vmpooler_user.debian-8-x86_64-pixa4.john
71
+
72
+ The metrics implementation here preserves the existing framework which will continue to support
73
+ graphite and statsd (since vmpooler is used outside of puppet). Some rationalisation and renaming
74
+ of the actual metrics was done to get a more usable model to fit within the prometheus framework.
75
+ This particularly applies to the user stats collected once individual machines are terminated as
76
+ this would have challenged prometheus' ability due to multiple (8) parameters being collected
77
+ in a single measure (which has a very high cardinality).
78
+
79
+ Prometheus requires all metrics to be pre-registered (which is the primary reason for this
80
+ table) and also uses labels to differentiate the characteristics of the measurement. This
81
+ is used throughout to capture information such as poolnames. So for example, this is a sample
82
+ of the prometheus metrics generated for the "vmpooler_ready" measurement:
83
+
84
+ # TYPE vmpooler_ready gauge
85
+ # HELP vmpooler_ready vmpooler number of machines in ready State
86
+ vmpooler_ready{vmpooler_instance="vmpooler",poolname="win-10-ent-x86_64-pixa4"} 2.0
87
+ vmpooler_ready{vmpooler_instance="vmpooler",poolname="debian-8-x86_64-pixa4"} 2.0
88
+ vmpooler_ready{vmpooler_instance="vmpooler",poolname="centos-8-x86_64-pixa4"} 2.0
89
+
90
+ Prometheus supports the following metric types:
91
+ (see https://prometheus.io/docs/concepts/metric_types/)
92
+
93
+ Counter (increment):
94
+ A counter is a cumulative metric that represents a single monotonically increasing counter whose
95
+ value can only increase or be reset to zero on restart
96
+
97
+ Gauge:
98
+ A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.
99
+
100
+ Histogram:
101
+ A histogram samples observations (usually things like request durations or response sizes) and
102
+ counts them in configurable buckets. It also provides a sum of all observed values.
103
+ This replaces the timer metric supported by statsd
104
+
105
+ Summary :
106
+ Summary provides a total count of observations and a sum of all observed values, it calculates
107
+ configurable quantiles over a sliding time window.
108
+ (Summary is not used in vmpooler)
109
+
110
+ vmpooler_metrics_table is a table of hashes, where the hash key represents the first part of the
111
+ metric name, e.g. for the metric 'delete.*' (see above) the key would be 'delete:'. "Sub-metrics",
112
+ are supported, again for the 'delete.*' example, this can be subbed into '.failed' and '.success'
113
+
114
+ The entries within the hash as are follows:
115
+
116
+ mtype:
117
+ Metric type, which is one of the following constants:
118
+ M_COUNTER = 1
119
+ M_GAUGE = 2
120
+ M_SUMMARY = 3
121
+ M_HISTOGRAM = 4
122
+
123
+ torun:
124
+ Indicates which process the metric is for - within vmpooler this is either ':api' or ':manager'
125
+ (there is a suggestion that we change this to two separate tables).
126
+
127
+ docstring:
128
+ Documentation string for the metric - this is displayed as HELP text by the endpoint.
129
+
130
+ metric_suffixes:
131
+ Array of sub-metrics of the form 'sub-metric: "doc-string for sub-metric"'. This supports
132
+ the generation of individual sub-metrics for all elements in the array.
133
+
134
+ param_labels:
135
+ This is an optional array of symbols for the final labels in a metric. It should not be
136
+ specified if there are no additional parameters.
137
+
138
+ If it specified, it can either be a single symbol, or two or more symbols. The treatment
139
+ differs if there is only one symbol given as all of the remainder of the metric string
140
+ supplied is collected into a label with the symbol name. This allows the handling of
141
+ node names (FQDN).
142
+
143
+ To illustrate:
144
+ 1. In the 'connect.*' or 'delete.*' example above, it should not be specified.
145
+ 2. For the 'migrate_from.*' example above, the remainder of the measure is collected
146
+ as the 'host_name' label.
147
+ 3. For the 'vmpooler_user' example above, the first parameter is treated as the pool
148
+ name, and the second as the username.
149
+
150
+ =end
36
151
  def vmpooler_metrics_table
37
152
  {
38
153
  errors: {
39
154
  mtype: M_COUNTER,
40
155
  torun: %i[manager],
41
156
  docstring: 'Count of errors for pool',
42
- prom_metric_prefix: "#{@metrics_prefix}_errors",
43
157
  metric_suffixes: {
44
158
  markedasfailed: 'timeout waiting for instance to initialise',
45
159
  duplicatehostname: 'unable to create instance due to duplicate hostname',
@@ -51,42 +165,36 @@ module Vmpooler
51
165
  mtype: M_COUNTER,
52
166
  torun: %i[manager],
53
167
  docstring: 'Number of pool instances this user created created',
54
- prom_metric_prefix: "#{@metrics_prefix}_user",
55
168
  param_labels: %i[user poolname]
56
169
  },
57
170
  usage_litmus: {
58
171
  mtype: M_COUNTER,
59
172
  torun: %i[manager],
60
173
  docstring: 'Pools by Litmus job usage',
61
- prom_metric_prefix: "#{@metrics_prefix}_usage_litmus",
62
174
  param_labels: %i[user poolname]
63
175
  },
64
176
  usage_jenkins_instance: {
65
177
  mtype: M_COUNTER,
66
178
  torun: %i[manager],
67
179
  docstring: 'Pools by Jenkins instance usage',
68
- prom_metric_prefix: "#{@metrics_prefix}_usage_jenkins_instance",
69
180
  param_labels: %i[jenkins_instance value_stream poolname]
70
181
  },
71
182
  usage_branch_project: {
72
183
  mtype: M_COUNTER,
73
184
  torun: %i[manager],
74
185
  docstring: 'Pools by branch/project usage',
75
- prom_metric_prefix: "#{@metrics_prefix}_usage_branch_project",
76
186
  param_labels: %i[branch project poolname]
77
187
  },
78
188
  usage_job_component: {
79
189
  mtype: M_COUNTER,
80
190
  torun: %i[manager],
81
191
  docstring: 'Pools by job/component usage',
82
- prom_metric_prefix: "#{@metrics_prefix}_usage_job_component",
83
192
  param_labels: %i[job_name component_to_test poolname]
84
193
  },
85
194
  checkout: {
86
195
  mtype: M_COUNTER,
87
196
  torun: %i[api],
88
197
  docstring: 'Pool checkout counts',
89
- prom_metric_prefix: "#{@metrics_prefix}_checkout",
90
198
  metric_suffixes: {
91
199
  nonresponsive: 'checkout failed - non responsive machine',
92
200
  empty: 'checkout failed - no machine',
@@ -99,7 +207,6 @@ module Vmpooler
99
207
  mtype: M_COUNTER,
100
208
  torun: %i[api],
101
209
  docstring: 'Delete machine',
102
- prom_metric_prefix: "#{@metrics_prefix}_delete",
103
210
  metric_suffixes: {
104
211
  success: 'succeeded',
105
212
  failed: 'failed'
@@ -110,7 +217,6 @@ module Vmpooler
110
217
  mtype: M_COUNTER,
111
218
  torun: %i[api],
112
219
  docstring: 'Ondemand request',
113
- prom_metric_prefix: "#{@metrics_prefix}_ondemandrequest_generate",
114
220
  metric_suffixes: {
115
221
  duplicaterequests: 'failed duplicate request',
116
222
  success: 'succeeded'
@@ -121,7 +227,6 @@ module Vmpooler
121
227
  mtype: M_COUNTER,
122
228
  torun: %i[api],
123
229
  docstring: 'Ondemand request failure',
124
- prom_metric_prefix: "#{@metrics_prefix}_ondemandrequest_fail",
125
230
  metric_suffixes: {
126
231
  toomanyrequests: 'too many requests',
127
232
  invalid: 'invalid poolname'
@@ -132,7 +237,6 @@ module Vmpooler
132
237
  mtype: M_COUNTER,
133
238
  torun: %i[api],
134
239
  docstring: 'vmpooler pool configuration request',
135
- prom_metric_prefix: "#{@metrics_prefix}_config",
136
240
  metric_suffixes: { invalid: 'Invalid' },
137
241
  param_labels: %i[poolname]
138
242
  },
@@ -140,7 +244,6 @@ module Vmpooler
140
244
  mtype: M_COUNTER,
141
245
  torun: %i[api],
142
246
  docstring: 'Pool reset counter',
143
- prom_metric_prefix: "#{@metrics_prefix}_poolreset",
144
247
  metric_suffixes: { invalid: 'Invalid Pool' },
145
248
  param_labels: %i[poolname]
146
249
  },
@@ -148,7 +251,6 @@ module Vmpooler
148
251
  mtype: M_COUNTER,
149
252
  torun: %i[manager],
150
253
  docstring: 'vmpooler connect (to vSphere)',
151
- prom_metric_prefix: "#{@metrics_prefix}_connect",
152
254
  metric_suffixes: {
153
255
  open: 'Connect Succeeded',
154
256
  fail: 'Connect Failed'
@@ -159,42 +261,36 @@ module Vmpooler
159
261
  mtype: M_COUNTER,
160
262
  torun: %i[manager],
161
263
  docstring: 'vmpooler machine migrated from',
162
- prom_metric_prefix: "#{@metrics_prefix}_migrate_from",
163
264
  param_labels: %i[host_name]
164
265
  },
165
266
  migrate_to: {
166
267
  mtype: M_COUNTER,
167
268
  torun: %i[manager],
168
269
  docstring: 'vmpooler machine migrated to',
169
- prom_metric_prefix: "#{@metrics_prefix}_migrate_to",
170
270
  param_labels: %i[host_name]
171
271
  },
172
- api_vm: {
272
+ http_requests_vm_total: {
173
273
  mtype: M_COUNTER,
174
274
  torun: %i[api],
175
275
  docstring: 'Total number of HTTP request/sub-operations handled by the Rack application under the /vm endpoint',
176
- prom_metric_prefix: "#{@metrics_prefix}_http_requests_vm_total",
177
276
  param_labels: %i[method subpath operation]
178
277
  },
179
278
  ready: {
180
279
  mtype: M_GAUGE,
181
280
  torun: %i[manager],
182
281
  docstring: 'vmpooler number of machines in ready State',
183
- prom_metric_prefix: "#{@metrics_prefix}_ready",
184
282
  param_labels: %i[poolname]
185
283
  },
186
284
  running: {
187
285
  mtype: M_GAUGE,
188
286
  torun: %i[manager],
189
287
  docstring: 'vmpooler number of machines running',
190
- prom_metric_prefix: "#{@metrics_prefix}_running",
191
288
  param_labels: %i[poolname]
192
289
  },
193
290
  connection_available: {
194
291
  mtype: M_GAUGE,
195
292
  torun: %i[manager],
196
293
  docstring: 'vmpooler redis connections available',
197
- prom_metric_prefix: "#{@metrics_prefix}_connection_available",
198
294
  param_labels: %i[type provider]
199
295
  },
200
296
  time_to_ready_state: {
@@ -202,7 +298,6 @@ module Vmpooler
202
298
  torun: %i[manager],
203
299
  buckets: POOLER_READY_TIME_BUCKETS,
204
300
  docstring: 'Time taken for machine to read ready state for pool',
205
- prom_metric_prefix: "#{@metrics_prefix}_time_to_ready_state",
206
301
  param_labels: %i[poolname]
207
302
  },
208
303
  migrate: {
@@ -210,7 +305,6 @@ module Vmpooler
210
305
  torun: %i[manager],
211
306
  buckets: POOLER_CLONE_TIME_BUCKETS,
212
307
  docstring: 'vmpooler time taken to migrate machine for pool',
213
- prom_metric_prefix: "#{@metrics_prefix}_migrate",
214
308
  param_labels: %i[poolname]
215
309
  },
216
310
  clone: {
@@ -218,7 +312,6 @@ module Vmpooler
218
312
  torun: %i[manager],
219
313
  buckets: POOLER_CLONE_TIME_BUCKETS,
220
314
  docstring: 'vmpooler time taken to clone machine',
221
- prom_metric_prefix: "#{@metrics_prefix}_clone",
222
315
  param_labels: %i[poolname]
223
316
  },
224
317
  destroy: {
@@ -226,7 +319,6 @@ module Vmpooler
226
319
  torun: %i[manager],
227
320
  buckets: POOLER_CLONE_TIME_BUCKETS,
228
321
  docstring: 'vmpooler time taken to destroy machine',
229
- prom_metric_prefix: "#{@metrics_prefix}_destroy",
230
322
  param_labels: %i[poolname]
231
323
  },
232
324
  connection_waited: {
@@ -234,7 +326,6 @@ module Vmpooler
234
326
  torun: %i[manager],
235
327
  buckets: REDIS_CONNECT_BUCKETS,
236
328
  docstring: 'vmpooler redis connection wait time',
237
- prom_metric_prefix: "#{@metrics_prefix}_connection_waited",
238
329
  param_labels: %i[type provider]
239
330
  }
240
331
  }
@@ -278,8 +369,9 @@ module Vmpooler
278
369
  # Top level method to register all the prometheus metrics.
279
370
 
280
371
  def setup_prometheus_metrics(torun)
372
+ @torun = torun
281
373
  @p_metrics = vmpooler_metrics_table
282
- @p_metrics.each do |_name, metric_spec|
374
+ @p_metrics.each do |name, metric_spec|
283
375
  # Only register metrics appropriate to api or manager
284
376
  next if (torun & metric_spec[:torun]).empty?
285
377
 
@@ -288,7 +380,7 @@ module Vmpooler
288
380
  metric_spec[:metric_suffixes].each do |metric_suffix|
289
381
  add_prometheus_metric(
290
382
  metric_spec,
291
- "#{metric_spec[:prom_metric_prefix]}_#{metric_suffix[0]}",
383
+ "#{@prometheus_prefix}_#{name}_#{metric_suffix[0]}",
292
384
  "#{metric_spec[:docstring]} #{metric_suffix[1]}"
293
385
  )
294
386
  end
@@ -296,7 +388,7 @@ module Vmpooler
296
388
  # No Additional counter suffixes so register this as metric.
297
389
  add_prometheus_metric(
298
390
  metric_spec,
299
- metric_spec[:prom_metric_prefix],
391
+ "#{@prometheus_prefix}_#{name}",
300
392
  metric_spec[:docstring]
301
393
  )
302
394
  end
@@ -309,15 +401,18 @@ module Vmpooler
309
401
  metric_key = sublabels.shift.to_sym
310
402
  raise("Invalid Metric #{metric_key} for #{label}") unless @p_metrics.key? metric_key
311
403
 
312
- metric = @p_metrics[metric_key].clone
404
+ metric_spec = @p_metrics[metric_key]
405
+ raise("Invalid Component #{component} for #{metric_key}") if (metric_spec[:torun] & @torun).nil?
406
+
407
+ metric = metric_spec.clone
313
408
 
314
409
  if metric.key? :metric_suffixes
315
410
  metric_subkey = sublabels.shift.to_sym
316
411
  raise("Invalid Metric #{metric_key}_#{metric_subkey} for #{label}") unless metric[:metric_suffixes].key? metric_subkey.to_sym
317
412
 
318
- metric[:metric_name] = "#{metric[:prom_metric_prefix]}_#{metric_subkey}"
413
+ metric[:metric_name] = "#{@prometheus_prefix}_#{metric_key}_#{metric_subkey}"
319
414
  else
320
- metric[:metric_name] = metric[:prom_metric_prefix]
415
+ metric[:metric_name] = "#{@prometheus_prefix}_#{metric_key}"
321
416
  end
322
417
 
323
418
  # Check if we are looking for a parameter value at last element.
@@ -114,6 +114,9 @@ module Vmpooler
114
114
  path
115
115
  .gsub(%r{/vm/.+$}, '/vm')
116
116
  .gsub(%r{/ondemand/.+$}, '/ondemand')
117
+ .gsub(%r{/token/.+$}, '/token')
118
+ .gsub(%r{/lib/.+$}, '/lib')
119
+ .gsub(%r{/img/.+$}, '/img')
117
120
  end
118
121
  end
119
122
  end
@@ -368,7 +368,7 @@ module Vmpooler
368
368
  $metrics.increment("errors.duplicatehostname.#{pool_name}")
369
369
  $logger.log('s', "[!] [#{pool_name}] Generated hostname #{hostname} was not unique (attempt \##{hostname_retries} of #{max_hostname_retries})")
370
370
  elsif !dns_available
371
- $metrics.increment("errors.staledns.#{hostname}")
371
+ $metrics.increment("errors.staledns.#{pool_name}")
372
372
  $logger.log('s', "[!] [#{pool_name}] Generated hostname #{hostname} already exists in DNS records (#{dns_ip}), stale DNS")
373
373
  end
374
374
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vmpooler
4
- VERSION = '0.14.2'
4
+ VERSION = '0.14.7'
5
5
  end
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: 0.14.2
4
+ version: 0.14.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-03 00:00:00.000000000 Z
11
+ date: 2020-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pickup
@@ -84,16 +84,22 @@ dependencies:
84
84
  name: rbvmomi
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '2.1'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '4.0'
90
93
  type: :runtime
91
94
  prerelease: false
92
95
  version_requirements: !ruby/object:Gem::Requirement
93
96
  requirements:
94
- - - "~>"
97
+ - - ">="
95
98
  - !ruby/object:Gem::Version
96
99
  version: '2.1'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '4.0'
97
103
  - !ruby/object:Gem::Dependency
98
104
  name: sinatra
99
105
  requirement: !ruby/object:Gem::Requirement