bosh-director 1.1750.0 → 1.1761.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.
- data/lib/bosh/director/deployment_plan/assembler.rb +11 -5
- data/lib/bosh/director/deployment_plan/job.rb +25 -244
- data/lib/bosh/director/deployment_plan/job_spec_parser.rb +294 -0
- data/lib/bosh/director/dns_helper.rb +13 -7
- data/lib/bosh/director/errors.rb +1 -0
- data/lib/bosh/director/models/template.rb +6 -8
- data/lib/bosh/director/validation_helper.rb +10 -7
- data/lib/bosh/director/version.rb +1 -1
- data/lib/cloud/dummy.rb +25 -12
- metadata +19 -18
|
@@ -403,10 +403,12 @@ module Bosh::Director
|
|
|
403
403
|
|
|
404
404
|
def delete_unneeded_vms
|
|
405
405
|
unneeded_vms = @deployment_plan.unneeded_vms
|
|
406
|
-
|
|
406
|
+
if unneeded_vms.empty?
|
|
407
|
+
@logger.info("No unneeded vms to delete")
|
|
408
|
+
return
|
|
409
|
+
end
|
|
407
410
|
|
|
408
411
|
@event_log.begin_stage("Deleting unneeded VMs", unneeded_vms.size)
|
|
409
|
-
|
|
410
412
|
ThreadPool.new(:max_threads => Config.max_threads).wrap do |pool|
|
|
411
413
|
unneeded_vms.each do |vm|
|
|
412
414
|
pool.process do
|
|
@@ -422,10 +424,14 @@ module Bosh::Director
|
|
|
422
424
|
|
|
423
425
|
def delete_unneeded_instances
|
|
424
426
|
unneeded_instances = @deployment_plan.unneeded_instances
|
|
425
|
-
|
|
427
|
+
if unneeded_instances.empty?
|
|
428
|
+
@logger.info("No unneeded instances to delete")
|
|
429
|
+
return
|
|
430
|
+
end
|
|
426
431
|
|
|
427
|
-
@event_log.begin_stage("Deleting unneeded instances", unneeded_instances.size)
|
|
428
|
-
InstanceDeleter.new(@deployment_plan)
|
|
432
|
+
event_log_stage = @event_log.begin_stage("Deleting unneeded instances", unneeded_instances.size)
|
|
433
|
+
instance_deleter = InstanceDeleter.new(@deployment_plan)
|
|
434
|
+
instance_deleter.delete_instances(unneeded_instances, event_log_stage)
|
|
429
435
|
@logger.info("Deleted no longer needed instances")
|
|
430
436
|
end
|
|
431
437
|
end
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'bosh/director/deployment_plan/job_spec_parser'
|
|
2
2
|
|
|
3
3
|
module Bosh::Director
|
|
4
4
|
module DeploymentPlan
|
|
5
5
|
class Job
|
|
6
6
|
include Bosh::Common::PropertyHelper
|
|
7
7
|
|
|
8
|
-
include IpUtil
|
|
9
|
-
include DnsHelper
|
|
10
|
-
include ValidationHelper
|
|
11
|
-
|
|
12
8
|
# started, stopped and detached are real states
|
|
13
9
|
# (persisting in DB and reflecting target instance state)
|
|
14
10
|
# recreate and restart are two virtual states
|
|
@@ -51,6 +47,9 @@ module Bosh::Director
|
|
|
51
47
|
# @return [DeploymentPlan::UpdateConfig] Job update settings
|
|
52
48
|
attr_accessor :update
|
|
53
49
|
|
|
50
|
+
# @return [Array<DeploymentPlan::Instance>] All job instances
|
|
51
|
+
attr_accessor :instances
|
|
52
|
+
|
|
54
53
|
# @return [Array<Models::Instance>] List of excess instance models that
|
|
55
54
|
# are not needed for current deployment
|
|
56
55
|
attr_accessor :unneeded_instances
|
|
@@ -65,42 +64,32 @@ module Bosh::Director
|
|
|
65
64
|
# interrupted
|
|
66
65
|
attr_accessor :halt_exception
|
|
67
66
|
|
|
67
|
+
attr_accessor :all_properties
|
|
68
|
+
|
|
68
69
|
# @param [Bosh::Director::DeploymentPlan] deployment Deployment plan
|
|
69
70
|
# @param [Hash] job_spec Raw job spec from the deployment manifest
|
|
70
71
|
# @return [Bosh::Director::DeploymentPlan::Job]
|
|
71
72
|
def self.parse(deployment, job_spec)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
job
|
|
73
|
+
job_parser = JobSpecParser.new(deployment)
|
|
74
|
+
job_parser.parse(job_spec)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# @param [Bosh::Director::DeploymentPlan] deployment Deployment plan
|
|
78
|
-
|
|
79
|
-
def initialize(deployment, job_spec)
|
|
78
|
+
def initialize(deployment)
|
|
80
79
|
@deployment = deployment
|
|
81
|
-
@job_spec = job_spec
|
|
82
80
|
|
|
83
81
|
@release = nil
|
|
84
82
|
@templates = []
|
|
85
83
|
@all_properties = nil # All properties available to job
|
|
86
84
|
@properties = nil # Actual job properties
|
|
87
85
|
|
|
86
|
+
@instances = []
|
|
87
|
+
@unneeded_instances = []
|
|
88
|
+
@instance_states = {}
|
|
89
|
+
|
|
88
90
|
@error_mutex = Mutex.new
|
|
89
91
|
@packages = {}
|
|
90
92
|
@halt = false
|
|
91
|
-
@unneeded_instances = []
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def parse
|
|
95
|
-
parse_name
|
|
96
|
-
parse_release
|
|
97
|
-
parse_template
|
|
98
|
-
parse_disk
|
|
99
|
-
parse_properties
|
|
100
|
-
parse_resource_pool
|
|
101
|
-
parse_update_config
|
|
102
|
-
parse_instances
|
|
103
|
-
parse_networks
|
|
104
93
|
end
|
|
105
94
|
|
|
106
95
|
def self.is_legacy_spec?(job_spec)
|
|
@@ -173,12 +162,6 @@ module Bosh::Director
|
|
|
173
162
|
result.select { |name, _| run_time_dependencies.include? name }
|
|
174
163
|
end
|
|
175
164
|
|
|
176
|
-
# Returns all instances of this job
|
|
177
|
-
# @return [Array<DeploymentPlan::Instance>] All job instances
|
|
178
|
-
def instances
|
|
179
|
-
@instances
|
|
180
|
-
end
|
|
181
|
-
|
|
182
165
|
# Returns job instance by index
|
|
183
166
|
# @param [Integer] index
|
|
184
167
|
# @return [DeploymentPlan::Instance] index-th instance
|
|
@@ -212,215 +195,6 @@ module Bosh::Director
|
|
|
212
195
|
end
|
|
213
196
|
end
|
|
214
197
|
|
|
215
|
-
def parse_name
|
|
216
|
-
@name = safe_property(@job_spec, "name", :class => String)
|
|
217
|
-
@canonical_name = canonical(@name)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def parse_release
|
|
221
|
-
release_name = safe_property(@job_spec, "release", :class => String,
|
|
222
|
-
:optional => true)
|
|
223
|
-
|
|
224
|
-
if release_name.nil?
|
|
225
|
-
if @deployment.releases.size == 1
|
|
226
|
-
@release = @deployment.releases.first
|
|
227
|
-
else
|
|
228
|
-
raise JobMissingRelease,
|
|
229
|
-
"Cannot tell what release job `#{@name}' supposed to use, please reference an existing release"
|
|
230
|
-
end
|
|
231
|
-
else
|
|
232
|
-
@release = @deployment.release(release_name)
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
if @release.nil?
|
|
236
|
-
raise JobUnknownRelease,
|
|
237
|
-
"Job `#{@name}' references an unknown release `#{release_name}'"
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
def parse_template
|
|
242
|
-
if @release.nil?
|
|
243
|
-
raise DirectorError, "Cannot parse template before parsing release"
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
template_names = safe_property(@job_spec, "template")
|
|
247
|
-
|
|
248
|
-
if template_names.is_a?(String)
|
|
249
|
-
template_names = Array(template_names)
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
unless template_names.is_a?(Array)
|
|
253
|
-
invalid_type("template", "String or Array", template_names)
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
template_names.each do |template_name|
|
|
257
|
-
@release.use_template_named(template_name)
|
|
258
|
-
@templates << @release.template(template_name)
|
|
259
|
-
end
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def parse_disk
|
|
263
|
-
@persistent_disk = safe_property(@job_spec, "persistent_disk", :class => Integer, :default => 0)
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def parse_properties
|
|
267
|
-
# Manifest can contain global and per-job properties section
|
|
268
|
-
job_properties = safe_property(@job_spec, "properties", :class => Hash, :optional => true)
|
|
269
|
-
|
|
270
|
-
@all_properties = Bosh::Common::DeepCopy.copy(deployment.properties)
|
|
271
|
-
|
|
272
|
-
if job_properties
|
|
273
|
-
@all_properties.recursive_merge!(job_properties)
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
mappings = safe_property(@job_spec, "property_mappings", :class => Hash, :default => {})
|
|
277
|
-
|
|
278
|
-
mappings.each_pair do |to, from|
|
|
279
|
-
resolved = lookup_property(@all_properties, from)
|
|
280
|
-
|
|
281
|
-
if resolved.nil?
|
|
282
|
-
raise JobInvalidPropertyMapping,
|
|
283
|
-
"Cannot satisfy property mapping `#{to}: #{from}', as `#{from}' is not in deployment properties"
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
@all_properties[to] = resolved
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def parse_resource_pool
|
|
291
|
-
resource_pool_name = safe_property(@job_spec, "resource_pool", class: String)
|
|
292
|
-
@resource_pool = deployment.resource_pool(resource_pool_name)
|
|
293
|
-
if @resource_pool.nil?
|
|
294
|
-
raise JobUnknownResourcePool,
|
|
295
|
-
"Job `#{@name}' references an unknown resource pool `#{resource_pool_name}'"
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def parse_update_config
|
|
300
|
-
update_spec = safe_property(@job_spec, "update", class: Hash, optional: true)
|
|
301
|
-
@update = UpdateConfig.new(update_spec, @deployment.update)
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
def parse_instances
|
|
305
|
-
@instances = []
|
|
306
|
-
@instance_states = {}
|
|
307
|
-
|
|
308
|
-
@state = safe_property(@job_spec, "state", class: String, optional: true)
|
|
309
|
-
job_size = safe_property(@job_spec, "instances", class: Integer)
|
|
310
|
-
instance_states = safe_property(@job_spec, "instance_states", class: Hash, default: {})
|
|
311
|
-
|
|
312
|
-
instance_states.each_pair do |index, state|
|
|
313
|
-
begin
|
|
314
|
-
index = Integer(index)
|
|
315
|
-
rescue ArgumentError
|
|
316
|
-
raise JobInvalidInstanceIndex,
|
|
317
|
-
"Invalid job index `#{index}', integer expected"
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
unless (0...job_size).include?(index)
|
|
321
|
-
raise JobInvalidInstanceIndex,
|
|
322
|
-
"`#{@name}/#{index}' is outside of (0..#{job_size-1}) range"
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
unless VALID_JOB_STATES.include?(state)
|
|
326
|
-
raise JobInvalidInstanceState,
|
|
327
|
-
"Invalid state `#{state}' for `#{@name}/#{index}', valid states are: #{VALID_JOB_STATES.join(", ")}"
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
@instance_states[index] = state
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
if @state && !VALID_JOB_STATES.include?(@state)
|
|
334
|
-
raise JobInvalidJobState,
|
|
335
|
-
"Invalid state `#{@state}' for `#{@name}', valid states are: #{VALID_JOB_STATES.join(", ")}"
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
job_size.times do |index|
|
|
339
|
-
@instances[index] = Instance.new(self, index)
|
|
340
|
-
@resource_pool.reserve_capacity(1)
|
|
341
|
-
end
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
def parse_networks
|
|
345
|
-
@default_network = {}
|
|
346
|
-
|
|
347
|
-
network_specs = safe_property(@job_spec, "networks", :class => Array)
|
|
348
|
-
if network_specs.empty?
|
|
349
|
-
raise JobMissingNetwork,
|
|
350
|
-
"Job `#{@name}' must specify at least one network"
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
network_specs.each do |network_spec|
|
|
354
|
-
network_name = safe_property(network_spec, "name", :class => String)
|
|
355
|
-
network = @deployment.network(network_name)
|
|
356
|
-
if network.nil?
|
|
357
|
-
raise JobUnknownNetwork,
|
|
358
|
-
"Job `#{@name}' references an unknown network `#{network_name}'"
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
static_ips = nil
|
|
362
|
-
if network_spec["static_ips"]
|
|
363
|
-
static_ips = []
|
|
364
|
-
each_ip(network_spec["static_ips"]) do |ip|
|
|
365
|
-
static_ips << ip
|
|
366
|
-
end
|
|
367
|
-
if static_ips.size != @instances.size
|
|
368
|
-
raise JobNetworkInstanceIpMismatch,
|
|
369
|
-
"Job `#{@name}' has #{@instances.size} instances but was allocated #{static_ips.size} static IPs"
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
default_network = safe_property(network_spec, "default",
|
|
374
|
-
:class => Array, :optional => true)
|
|
375
|
-
if default_network
|
|
376
|
-
default_network.each do |property|
|
|
377
|
-
unless Network::VALID_DEFAULTS.include?(property)
|
|
378
|
-
raise JobNetworkInvalidDefault,
|
|
379
|
-
"Job `#{@name}' specified an invalid default network property `#{property}', " +
|
|
380
|
-
"valid properties are: " + Network::VALID_DEFAULTS.join(", ")
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
if @default_network[property]
|
|
384
|
-
raise JobNetworkMultipleDefaults,
|
|
385
|
-
"Job `#{@name}' specified more than one network to contain default #{property}"
|
|
386
|
-
else
|
|
387
|
-
@default_network[property] = network_name
|
|
388
|
-
end
|
|
389
|
-
end
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
@instances.each_with_index do |instance, index|
|
|
393
|
-
reservation = NetworkReservation.new
|
|
394
|
-
if static_ips
|
|
395
|
-
reservation.ip = static_ips[index]
|
|
396
|
-
reservation.type = NetworkReservation::STATIC
|
|
397
|
-
else
|
|
398
|
-
reservation.type = NetworkReservation::DYNAMIC
|
|
399
|
-
end
|
|
400
|
-
instance.add_network_reservation(network_name, reservation)
|
|
401
|
-
end
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
if network_specs.size > 1
|
|
405
|
-
missing_default_properties = Network::VALID_DEFAULTS.dup
|
|
406
|
-
@default_network.each_key do |key|
|
|
407
|
-
missing_default_properties.delete(key)
|
|
408
|
-
end
|
|
409
|
-
unless missing_default_properties.empty?
|
|
410
|
-
raise JobNetworkMissingDefault,
|
|
411
|
-
"Job `#{@name}' must specify which network is default for " +
|
|
412
|
-
missing_default_properties.sort.join(", ") + ", since it has more than one network configured"
|
|
413
|
-
end
|
|
414
|
-
else
|
|
415
|
-
# Set the default network to the one and only available network
|
|
416
|
-
# (if not specified already)
|
|
417
|
-
network = safe_property(network_specs[0], "name", :class => String)
|
|
418
|
-
Network::VALID_DEFAULTS.each do |property|
|
|
419
|
-
@default_network[property] ||= network
|
|
420
|
-
end
|
|
421
|
-
end
|
|
422
|
-
end
|
|
423
|
-
|
|
424
198
|
# Extracts only the properties needed by this job. This is decoupled from
|
|
425
199
|
# parsing properties because templates need to be bound to their models
|
|
426
200
|
# before 'bind_properties' is being called (as we persist job template
|
|
@@ -438,11 +212,18 @@ module Bosh::Director
|
|
|
438
212
|
raise DirectorError, "Can't extract job properties before parsing job templates"
|
|
439
213
|
end
|
|
440
214
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
215
|
+
if @templates.none? { |template| template.properties }
|
|
216
|
+
return collection
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
if @templates.all? { |template| template.properties }
|
|
220
|
+
return extract_template_properties(collection)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
raise JobIncompatibleSpecs,
|
|
224
|
+
"Job `#{name}' has specs with conflicting property definition styles between" +
|
|
225
|
+
" its job spec templates. This may occur if colocating jobs, one of which has a spec file including" +
|
|
226
|
+
" `properties' and one which doesn't."
|
|
446
227
|
end
|
|
447
228
|
|
|
448
229
|
def extract_template_properties(collection)
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
require 'common/deep_copy'
|
|
2
|
+
|
|
3
|
+
module Bosh::Director
|
|
4
|
+
module DeploymentPlan
|
|
5
|
+
class JobSpecParser
|
|
6
|
+
include DnsHelper
|
|
7
|
+
include ValidationHelper
|
|
8
|
+
include Bosh::Common::PropertyHelper
|
|
9
|
+
include IpUtil
|
|
10
|
+
|
|
11
|
+
# @param [Bosh::Director::DeploymentPlan] deployment Deployment plan
|
|
12
|
+
def initialize(deployment)
|
|
13
|
+
@deployment = deployment
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param [Hash] job_spec Raw job spec from the deployment manifest
|
|
17
|
+
# @return [DeploymentPlan::Job] Job as build from job_spec
|
|
18
|
+
def parse(job_spec)
|
|
19
|
+
@job_spec = job_spec
|
|
20
|
+
@job = Job.new(@deployment)
|
|
21
|
+
|
|
22
|
+
parse_name
|
|
23
|
+
parse_release
|
|
24
|
+
parse_template
|
|
25
|
+
parse_templates
|
|
26
|
+
|
|
27
|
+
validate_templates
|
|
28
|
+
|
|
29
|
+
check_template_uniqueness
|
|
30
|
+
parse_disk
|
|
31
|
+
parse_properties
|
|
32
|
+
parse_resource_pool
|
|
33
|
+
parse_update_config
|
|
34
|
+
parse_instances
|
|
35
|
+
parse_networks
|
|
36
|
+
|
|
37
|
+
@job
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def parse_name
|
|
43
|
+
@job.name = safe_property(@job_spec, "name", :class => String)
|
|
44
|
+
@job.canonical_name = canonical(@job.name)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def parse_release
|
|
48
|
+
release_name = safe_property(@job_spec, "release", :class => String, :optional => true)
|
|
49
|
+
|
|
50
|
+
if release_name.nil?
|
|
51
|
+
if @deployment.releases.size == 1
|
|
52
|
+
@job.release = @deployment.releases.first
|
|
53
|
+
else
|
|
54
|
+
raise JobMissingRelease,
|
|
55
|
+
"Cannot tell what release job `#{@job.name}' supposed to use, please reference an existing release"
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
@job.release = @deployment.release(release_name)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if @job.release.nil?
|
|
62
|
+
raise JobUnknownRelease,
|
|
63
|
+
"Job `#{@job.name}' references an unknown release `#{release_name}'"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def parse_template
|
|
68
|
+
if @job.release.nil?
|
|
69
|
+
raise DirectorError, "Cannot parse template before parsing release"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
template_names = safe_property(@job_spec, "template", optional: true)
|
|
73
|
+
if template_names
|
|
74
|
+
if template_names.is_a?(String)
|
|
75
|
+
template_names = Array(template_names)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
unless template_names.is_a?(Array)
|
|
79
|
+
invalid_type("template", "String or Array", template_names)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
template_names.each do |template_name|
|
|
83
|
+
@job.templates << @job.release.use_template_named(template_name)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def parse_templates
|
|
89
|
+
templates = safe_property(@job_spec, 'templates', class: Array, optional: true)
|
|
90
|
+
|
|
91
|
+
if templates
|
|
92
|
+
templates.each do |template|
|
|
93
|
+
template_name = safe_property(template, 'name', class: String)
|
|
94
|
+
release_name = safe_property(template, 'release', class: String, optional: true)
|
|
95
|
+
|
|
96
|
+
release = release_name ? @deployment.release(release_name) : @job.release
|
|
97
|
+
if release
|
|
98
|
+
@job.templates << release.use_template_named(template_name)
|
|
99
|
+
else
|
|
100
|
+
raise JobUnknownRelease,
|
|
101
|
+
"Template `#{template_name}' (job `#{@job.name}') references an unknown release `#{release_name}'"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def check_template_uniqueness
|
|
108
|
+
if @job.templates.uniq(&:name).size != @job.templates.size
|
|
109
|
+
raise JobInvalidTemplates,
|
|
110
|
+
"Job `#{@job.name}' templates must not have repeating names."
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
if @job.templates.uniq(&:release).size > 1
|
|
114
|
+
raise JobInvalidTemplates,
|
|
115
|
+
"Job `#{@job.name}' templates must come from the same release."
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def parse_disk
|
|
120
|
+
@job.persistent_disk = safe_property(@job_spec, "persistent_disk", :class => Integer, :default => 0)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def parse_properties
|
|
124
|
+
# Manifest can contain global and per-job properties section
|
|
125
|
+
job_properties = safe_property(@job_spec, "properties", :class => Hash, :optional => true)
|
|
126
|
+
|
|
127
|
+
@job.all_properties = Bosh::Common::DeepCopy.copy(@deployment.properties)
|
|
128
|
+
|
|
129
|
+
if job_properties
|
|
130
|
+
@job.all_properties.recursive_merge!(job_properties)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
mappings = safe_property(@job_spec, "property_mappings", :class => Hash, :default => {})
|
|
134
|
+
|
|
135
|
+
mappings.each_pair do |to, from|
|
|
136
|
+
resolved = lookup_property(@job.all_properties, from)
|
|
137
|
+
|
|
138
|
+
if resolved.nil?
|
|
139
|
+
raise JobInvalidPropertyMapping,
|
|
140
|
+
"Cannot satisfy property mapping `#{to}: #{from}', as `#{from}' is not in deployment properties"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
@job.all_properties[to] = resolved
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def parse_resource_pool
|
|
148
|
+
resource_pool_name = safe_property(@job_spec, "resource_pool", class: String)
|
|
149
|
+
@job.resource_pool = @deployment.resource_pool(resource_pool_name)
|
|
150
|
+
if @job.resource_pool.nil?
|
|
151
|
+
raise JobUnknownResourcePool,
|
|
152
|
+
"Job `#{@job.name}' references an unknown resource pool `#{resource_pool_name}'"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def parse_update_config
|
|
157
|
+
update_spec = safe_property(@job_spec, "update", class: Hash, optional: true)
|
|
158
|
+
@job.update = UpdateConfig.new(update_spec, @deployment.update)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def parse_instances
|
|
162
|
+
@job.state = safe_property(@job_spec, "state", class: String, optional: true)
|
|
163
|
+
job_size = safe_property(@job_spec, "instances", class: Integer)
|
|
164
|
+
instance_states = safe_property(@job_spec, "instance_states", class: Hash, default: {})
|
|
165
|
+
|
|
166
|
+
instance_states.each_pair do |index, state|
|
|
167
|
+
begin
|
|
168
|
+
index = Integer(index)
|
|
169
|
+
rescue ArgumentError
|
|
170
|
+
raise JobInvalidInstanceIndex,
|
|
171
|
+
"Invalid job index `#{index}', integer expected"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
unless (0...job_size).include?(index)
|
|
175
|
+
raise JobInvalidInstanceIndex,
|
|
176
|
+
"`#{@job.name}/#{index}' is outside of (0..#{job_size-1}) range"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
unless Job::VALID_JOB_STATES.include?(state)
|
|
180
|
+
raise JobInvalidInstanceState,
|
|
181
|
+
"Invalid state `#{state}' for `#{@job.name}/#{index}', valid states are: #{Job::VALID_JOB_STATES.join(", ")}"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
@job.instance_states[index] = state
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
if @job.state && !Job::VALID_JOB_STATES.include?(@job.state)
|
|
188
|
+
raise JobInvalidJobState,
|
|
189
|
+
"Invalid state `#{@job.state}' for `#{@job.name}', valid states are: #{Job::VALID_JOB_STATES.join(", ")}"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
job_size.times do |index|
|
|
193
|
+
@job.instances[index] = Instance.new(@job, index)
|
|
194
|
+
@job.resource_pool.reserve_capacity(1)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def parse_networks
|
|
199
|
+
@job.default_network = {}
|
|
200
|
+
|
|
201
|
+
network_specs = safe_property(@job_spec, "networks", :class => Array)
|
|
202
|
+
if network_specs.empty?
|
|
203
|
+
raise JobMissingNetwork,
|
|
204
|
+
"Job `#{@job.name}' must specify at least one network"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
network_specs.each do |network_spec|
|
|
208
|
+
network_name = safe_property(network_spec, "name", :class => String)
|
|
209
|
+
network = @deployment.network(network_name)
|
|
210
|
+
if network.nil?
|
|
211
|
+
raise JobUnknownNetwork,
|
|
212
|
+
"Job `#{@job.name}' references an unknown network `#{network_name}'"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
static_ips = nil
|
|
216
|
+
if network_spec["static_ips"]
|
|
217
|
+
static_ips = []
|
|
218
|
+
each_ip(network_spec["static_ips"]) do |ip|
|
|
219
|
+
static_ips << ip
|
|
220
|
+
end
|
|
221
|
+
if static_ips.size != @job.instances.size
|
|
222
|
+
raise JobNetworkInstanceIpMismatch,
|
|
223
|
+
"Job `#{@job.name}' has #{@job.instances.size} instances but was allocated #{static_ips.size} static IPs"
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
default_network = safe_property(network_spec, "default", :class => Array, :optional => true)
|
|
228
|
+
if default_network
|
|
229
|
+
default_network.each do |property|
|
|
230
|
+
unless Network::VALID_DEFAULTS.include?(property)
|
|
231
|
+
raise JobNetworkInvalidDefault,
|
|
232
|
+
"Job `#{@job.name}' specified an invalid default network property `#{property}', " +
|
|
233
|
+
"valid properties are: " + Network::VALID_DEFAULTS.join(", ")
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
if @job.default_network[property]
|
|
237
|
+
raise JobNetworkMultipleDefaults,
|
|
238
|
+
"Job `#{@job.name}' specified more than one network to contain default #{property}"
|
|
239
|
+
else
|
|
240
|
+
@job.default_network[property] = network_name
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
@job.instances.each_with_index do |instance, index|
|
|
246
|
+
reservation = NetworkReservation.new
|
|
247
|
+
if static_ips
|
|
248
|
+
reservation.ip = static_ips[index]
|
|
249
|
+
reservation.type = NetworkReservation::STATIC
|
|
250
|
+
else
|
|
251
|
+
reservation.type = NetworkReservation::DYNAMIC
|
|
252
|
+
end
|
|
253
|
+
instance.add_network_reservation(network_name, reservation)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if network_specs.size > 1
|
|
258
|
+
missing_default_properties = Network::VALID_DEFAULTS.dup
|
|
259
|
+
@job.default_network.each_key do |key|
|
|
260
|
+
missing_default_properties.delete(key)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
unless missing_default_properties.empty?
|
|
264
|
+
raise JobNetworkMissingDefault,
|
|
265
|
+
"Job `#{@job.name}' must specify which network is default for " +
|
|
266
|
+
missing_default_properties.sort.join(", ") + ", since it has more than one network configured"
|
|
267
|
+
end
|
|
268
|
+
else
|
|
269
|
+
# Set the default network to the one and only available network
|
|
270
|
+
# (if not specified already)
|
|
271
|
+
network = safe_property(network_specs[0], "name", :class => String)
|
|
272
|
+
Network::VALID_DEFAULTS.each do |property|
|
|
273
|
+
@job.default_network[property] ||= network
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def validate_templates
|
|
279
|
+
template_property = safe_property(@job_spec, 'template', optional: true)
|
|
280
|
+
templates_property = safe_property(@job_spec, 'templates', optional: true)
|
|
281
|
+
|
|
282
|
+
if template_property && templates_property
|
|
283
|
+
raise JobInvalidTemplates,
|
|
284
|
+
"Job `#{@job.name}' specifies both template and templates keys, only one is allowed"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
if [template_property, templates_property].compact.empty?
|
|
288
|
+
raise ValidationMissingField,
|
|
289
|
+
"Job `#{@job.name}' does not specify template or templates keys, one is required"
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
-
|
|
3
1
|
module Bosh::Director
|
|
4
2
|
module DnsHelper
|
|
5
3
|
|
|
@@ -176,12 +174,21 @@ module Bosh::Director
|
|
|
176
174
|
end
|
|
177
175
|
end
|
|
178
176
|
|
|
179
|
-
# if the count is 2, it means we only have the NS & SOA record
|
|
180
|
-
# and the domain is "empty" and can be deleted
|
|
181
177
|
def delete_empty_domain(domain)
|
|
178
|
+
# If the count is 2, it means we only have the NS & SOA record
|
|
179
|
+
# and the domain is "empty" and can be deleted
|
|
182
180
|
if domain.records.size == 2
|
|
183
181
|
@logger.info("Deleting empty reverse domain #{domain.name}")
|
|
184
|
-
|
|
182
|
+
|
|
183
|
+
# Since DNS domain can be deleted by multiple threads
|
|
184
|
+
# it's possible for database to return 0 rows modified result.
|
|
185
|
+
# In this specific case that's a valid return value
|
|
186
|
+
# but Sequel usually considers that an error.
|
|
187
|
+
# ('Attempt to delete object did not result in a single row modification')
|
|
188
|
+
domain.require_modification = false
|
|
189
|
+
|
|
190
|
+
# Cascaded - all records are removed
|
|
191
|
+
domain.destroy
|
|
185
192
|
end
|
|
186
193
|
end
|
|
187
194
|
|
|
@@ -199,6 +206,5 @@ module Bosh::Director
|
|
|
199
206
|
octets = ip.split(/\./)
|
|
200
207
|
"#{octets[0..n].reverse.join(".")}.in-addr.arpa"
|
|
201
208
|
end
|
|
202
|
-
|
|
203
209
|
end
|
|
204
|
-
end
|
|
210
|
+
end
|
data/lib/bosh/director/errors.rb
CHANGED
|
@@ -134,6 +134,7 @@ module Bosh::Director
|
|
|
134
134
|
JobInvalidInstanceState = err(140005)
|
|
135
135
|
JobInvalidJobState = err(140006)
|
|
136
136
|
JobMissingNetwork = err(140007)
|
|
137
|
+
JobInvalidTemplates = err(140008)
|
|
137
138
|
|
|
138
139
|
JobUnknownNetwork = err(150001)
|
|
139
140
|
JobNetworkInstanceIpMismatch = err(150002)
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
-
|
|
3
1
|
module Bosh::Director::Models
|
|
4
2
|
class Template < Sequel::Model(Bosh::Director::Config.db)
|
|
5
3
|
many_to_one :release
|
|
6
4
|
many_to_many :release_versions
|
|
7
5
|
|
|
6
|
+
def validate
|
|
7
|
+
validates_presence [:release_id, :name, :version, :blobstore_id, :sha1]
|
|
8
|
+
validates_unique [:release_id, :name, :version]
|
|
9
|
+
validates_format VALID_ID, [:name, :version]
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def package_names
|
|
9
13
|
result = self.package_names_json
|
|
10
14
|
result ? Yajl::Parser.parse(result) : nil
|
|
@@ -34,11 +38,5 @@ module Bosh::Director::Models
|
|
|
34
38
|
result = self.properties_json
|
|
35
39
|
result ? Yajl::Parser.parse(result) : nil
|
|
36
40
|
end
|
|
37
|
-
|
|
38
|
-
def validate
|
|
39
|
-
validates_presence [:release_id, :name, :version, :blobstore_id, :sha1]
|
|
40
|
-
validates_unique [:release_id, :name, :version]
|
|
41
|
-
validates_format VALID_ID, [:name, :version]
|
|
42
|
-
end
|
|
43
41
|
end
|
|
44
42
|
end
|
|
@@ -3,11 +3,14 @@ module Bosh::Director
|
|
|
3
3
|
def safe_property(hash, property, options = {})
|
|
4
4
|
result = nil
|
|
5
5
|
|
|
6
|
-
if hash && hash.
|
|
6
|
+
if hash && !hash.kind_of?(Hash)
|
|
7
|
+
raise Bosh::Director::ValidationInvalidType,
|
|
8
|
+
"Object (#{hash.inspect}) did not match the required type `Hash'"
|
|
9
|
+
|
|
10
|
+
elsif hash && hash.has_key?(property)
|
|
7
11
|
result = hash[property]
|
|
8
12
|
|
|
9
13
|
if options[:class]
|
|
10
|
-
|
|
11
14
|
if options[:class] == :boolean
|
|
12
15
|
unless result.kind_of?(TrueClass) || result.kind_of?(FalseClass)
|
|
13
16
|
invalid_type(property, options[:class], result)
|
|
@@ -20,17 +23,16 @@ module Bosh::Director
|
|
|
20
23
|
invalid_type(property, options[:class], result)
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
|
-
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
if options[:min] && result < options[:min]
|
|
27
29
|
raise ValidationViolatedMin,
|
|
28
|
-
"`#{property}' value (#{result}) should be greater than #{options[:min]}"
|
|
30
|
+
"`#{property}' value (#{result.inspect}) should be greater than #{options[:min].inspect}"
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
if options[:max] && result > options[:max]
|
|
32
34
|
raise ValidationViolatedMax,
|
|
33
|
-
"`#{property}' value (#{result}) should be less than #{options[:max]}"
|
|
35
|
+
"`#{property}' value (#{result.inspect}) should be less than #{options[:max].inspect}"
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
elsif options[:default]
|
|
@@ -38,14 +40,15 @@ module Bosh::Director
|
|
|
38
40
|
|
|
39
41
|
elsif !options[:optional]
|
|
40
42
|
raise ValidationMissingField,
|
|
41
|
-
"Required property `#{property}' was not specified in #{
|
|
43
|
+
"Required property `#{property}' was not specified in object (#{hash.inspect})"
|
|
42
44
|
end
|
|
45
|
+
|
|
43
46
|
result
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
def invalid_type(property, klass, value)
|
|
47
50
|
raise ValidationInvalidType,
|
|
48
|
-
|
|
51
|
+
"Property `#{property}' (value #{value.inspect}) did not match the required type `#{klass}'"
|
|
49
52
|
end
|
|
50
53
|
end
|
|
51
54
|
end
|
data/lib/cloud/dummy.rb
CHANGED
|
@@ -3,10 +3,8 @@ require 'fileutils'
|
|
|
3
3
|
require 'securerandom'
|
|
4
4
|
|
|
5
5
|
module Bosh
|
|
6
|
-
|
|
7
6
|
module Clouds
|
|
8
7
|
class Dummy
|
|
9
|
-
|
|
10
8
|
class NotImplemented < StandardError; end
|
|
11
9
|
|
|
12
10
|
def initialize(options)
|
|
@@ -16,6 +14,7 @@ module Bosh
|
|
|
16
14
|
|
|
17
15
|
@options = options
|
|
18
16
|
@base_dir = options["dir"]
|
|
17
|
+
|
|
19
18
|
FileUtils.mkdir_p(@base_dir)
|
|
20
19
|
rescue Errno::EACCES
|
|
21
20
|
raise ArgumentError, "cannot create dummy cloud base directory #{@base_dir}"
|
|
@@ -31,12 +30,8 @@ module Bosh
|
|
|
31
30
|
FileUtils.rm(stemcell_file(stemcell_cid))
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
uri = URI(properties['endpoint'])
|
|
37
|
-
uri.user = properties['user']
|
|
38
|
-
uri.password = properties['password']
|
|
39
|
-
uri.to_s
|
|
33
|
+
def blobstore
|
|
34
|
+
@options['agent']['blobstore']
|
|
40
35
|
end
|
|
41
36
|
|
|
42
37
|
def nats_uri
|
|
@@ -52,7 +47,8 @@ module Bosh
|
|
|
52
47
|
# FIXME: if there is a need to start this dummy cloud agent with alerts turned on
|
|
53
48
|
# then port should be overriden for each agent, otherwise all but first won't start
|
|
54
49
|
# (won't be able to bind to port)
|
|
55
|
-
|
|
50
|
+
write_agent_settings(agent_base_dir, agent_id, nats_uri)
|
|
51
|
+
agent_cmd = %W[bosh_agent -b #{agent_base_dir} -r #{root_dir} --no-alerts -I dummy]
|
|
56
52
|
agent_log = "#{@options['dir']}/agent.#{agent_id}.log"
|
|
57
53
|
agent_pid = Process.spawn(*agent_cmd, chdir: agent_base_dir, out: agent_log, err: agent_log)
|
|
58
54
|
|
|
@@ -64,6 +60,23 @@ module Bosh
|
|
|
64
60
|
agent_pid.to_s
|
|
65
61
|
end
|
|
66
62
|
|
|
63
|
+
def write_agent_settings(agent_base_dir, agent_id, nats_uri)
|
|
64
|
+
FileUtils.mkdir_p(File.join(agent_base_dir, 'bosh'))
|
|
65
|
+
settings = {
|
|
66
|
+
agent_id: agent_id,
|
|
67
|
+
blobstore: @options['agent']['blobstore'],
|
|
68
|
+
ntp: [],
|
|
69
|
+
disks: {
|
|
70
|
+
persistent: {},
|
|
71
|
+
},
|
|
72
|
+
vm: {
|
|
73
|
+
name: "vm-#{agent_id}"
|
|
74
|
+
},
|
|
75
|
+
mbus: nats_uri,
|
|
76
|
+
}
|
|
77
|
+
File.write(File.join(agent_base_dir, 'bosh', 'settings.json'), JSON.generate(settings))
|
|
78
|
+
end
|
|
79
|
+
|
|
67
80
|
def delete_vm(vm_name)
|
|
68
81
|
agent_pid = vm_name.to_i
|
|
69
82
|
Process.kill("INT", agent_pid)
|
|
@@ -74,7 +87,7 @@ module Bosh
|
|
|
74
87
|
end
|
|
75
88
|
|
|
76
89
|
def reboot_vm(vm)
|
|
77
|
-
raise NotImplemented, "reboot_vm"
|
|
90
|
+
raise NotImplemented, "Dummy CPI does not implement reboot_vm"
|
|
78
91
|
end
|
|
79
92
|
|
|
80
93
|
def has_vm?(vm_id)
|
|
@@ -82,7 +95,7 @@ module Bosh
|
|
|
82
95
|
end
|
|
83
96
|
|
|
84
97
|
def configure_networks(vm, networks)
|
|
85
|
-
raise NotImplemented, "configure_networks"
|
|
98
|
+
raise NotImplemented, "Dummy CPI does not implement configure_networks"
|
|
86
99
|
end
|
|
87
100
|
|
|
88
101
|
def attach_disk(vm_id, disk_id)
|
|
@@ -120,7 +133,7 @@ module Bosh
|
|
|
120
133
|
end
|
|
121
134
|
|
|
122
135
|
def validate_deployment(old_manifest, new_manifest)
|
|
123
|
-
raise NotImplemented, "validate_deployment"
|
|
136
|
+
raise NotImplemented, "Dummy CPI does not implement validate_deployment"
|
|
124
137
|
end
|
|
125
138
|
|
|
126
139
|
private
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bosh-director
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1761.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2014-01-
|
|
12
|
+
date: 2014-01-10 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bcrypt-ruby
|
|
@@ -34,7 +34,7 @@ dependencies:
|
|
|
34
34
|
requirements:
|
|
35
35
|
- - ~>
|
|
36
36
|
- !ruby/object:Gem::Version
|
|
37
|
-
version: 1.
|
|
37
|
+
version: 1.1761.0
|
|
38
38
|
type: :runtime
|
|
39
39
|
prerelease: false
|
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -42,7 +42,7 @@ dependencies:
|
|
|
42
42
|
requirements:
|
|
43
43
|
- - ~>
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: 1.
|
|
45
|
+
version: 1.1761.0
|
|
46
46
|
- !ruby/object:Gem::Dependency
|
|
47
47
|
name: bosh-core
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -50,7 +50,7 @@ dependencies:
|
|
|
50
50
|
requirements:
|
|
51
51
|
- - ~>
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 1.
|
|
53
|
+
version: 1.1761.0
|
|
54
54
|
type: :runtime
|
|
55
55
|
prerelease: false
|
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -58,7 +58,7 @@ dependencies:
|
|
|
58
58
|
requirements:
|
|
59
59
|
- - ~>
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: 1.
|
|
61
|
+
version: 1.1761.0
|
|
62
62
|
- !ruby/object:Gem::Dependency
|
|
63
63
|
name: bosh_common
|
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -66,7 +66,7 @@ dependencies:
|
|
|
66
66
|
requirements:
|
|
67
67
|
- - ~>
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: 1.
|
|
69
|
+
version: 1.1761.0
|
|
70
70
|
type: :runtime
|
|
71
71
|
prerelease: false
|
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -74,7 +74,7 @@ dependencies:
|
|
|
74
74
|
requirements:
|
|
75
75
|
- - ~>
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
|
-
version: 1.
|
|
77
|
+
version: 1.1761.0
|
|
78
78
|
- !ruby/object:Gem::Dependency
|
|
79
79
|
name: bosh_cpi
|
|
80
80
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -82,7 +82,7 @@ dependencies:
|
|
|
82
82
|
requirements:
|
|
83
83
|
- - ~>
|
|
84
84
|
- !ruby/object:Gem::Version
|
|
85
|
-
version: 1.
|
|
85
|
+
version: 1.1761.0
|
|
86
86
|
type: :runtime
|
|
87
87
|
prerelease: false
|
|
88
88
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -90,7 +90,7 @@ dependencies:
|
|
|
90
90
|
requirements:
|
|
91
91
|
- - ~>
|
|
92
92
|
- !ruby/object:Gem::Version
|
|
93
|
-
version: 1.
|
|
93
|
+
version: 1.1761.0
|
|
94
94
|
- !ruby/object:Gem::Dependency
|
|
95
95
|
name: bosh_openstack_cpi
|
|
96
96
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -98,7 +98,7 @@ dependencies:
|
|
|
98
98
|
requirements:
|
|
99
99
|
- - ~>
|
|
100
100
|
- !ruby/object:Gem::Version
|
|
101
|
-
version: 1.
|
|
101
|
+
version: 1.1761.0
|
|
102
102
|
type: :runtime
|
|
103
103
|
prerelease: false
|
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -106,7 +106,7 @@ dependencies:
|
|
|
106
106
|
requirements:
|
|
107
107
|
- - ~>
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: 1.
|
|
109
|
+
version: 1.1761.0
|
|
110
110
|
- !ruby/object:Gem::Dependency
|
|
111
111
|
name: bosh_aws_cpi
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -114,7 +114,7 @@ dependencies:
|
|
|
114
114
|
requirements:
|
|
115
115
|
- - ~>
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: 1.
|
|
117
|
+
version: 1.1761.0
|
|
118
118
|
type: :runtime
|
|
119
119
|
prerelease: false
|
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -122,7 +122,7 @@ dependencies:
|
|
|
122
122
|
requirements:
|
|
123
123
|
- - ~>
|
|
124
124
|
- !ruby/object:Gem::Version
|
|
125
|
-
version: 1.
|
|
125
|
+
version: 1.1761.0
|
|
126
126
|
- !ruby/object:Gem::Dependency
|
|
127
127
|
name: bosh_vsphere_cpi
|
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -130,7 +130,7 @@ dependencies:
|
|
|
130
130
|
requirements:
|
|
131
131
|
- - ~>
|
|
132
132
|
- !ruby/object:Gem::Version
|
|
133
|
-
version: 1.
|
|
133
|
+
version: 1.1761.0
|
|
134
134
|
type: :runtime
|
|
135
135
|
prerelease: false
|
|
136
136
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -138,7 +138,7 @@ dependencies:
|
|
|
138
138
|
requirements:
|
|
139
139
|
- - ~>
|
|
140
140
|
- !ruby/object:Gem::Version
|
|
141
|
-
version: 1.
|
|
141
|
+
version: 1.1761.0
|
|
142
142
|
- !ruby/object:Gem::Dependency
|
|
143
143
|
name: eventmachine
|
|
144
144
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -461,7 +461,7 @@ dependencies:
|
|
|
461
461
|
version: '1.0'
|
|
462
462
|
description: ! 'BOSH Director
|
|
463
463
|
|
|
464
|
-
|
|
464
|
+
826c69'
|
|
465
465
|
email: support@cloudfoundry.com
|
|
466
466
|
executables:
|
|
467
467
|
- bosh-director
|
|
@@ -570,6 +570,7 @@ files:
|
|
|
570
570
|
- lib/bosh/director/deployment_plan/idle_vm.rb
|
|
571
571
|
- lib/bosh/director/deployment_plan/instance.rb
|
|
572
572
|
- lib/bosh/director/deployment_plan/job.rb
|
|
573
|
+
- lib/bosh/director/deployment_plan/job_spec_parser.rb
|
|
573
574
|
- lib/bosh/director/deployment_plan/manual_network.rb
|
|
574
575
|
- lib/bosh/director/deployment_plan/multi_job_updater.rb
|
|
575
576
|
- lib/bosh/director/deployment_plan/network.rb
|
|
@@ -712,7 +713,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
712
713
|
version: '0'
|
|
713
714
|
segments:
|
|
714
715
|
- 0
|
|
715
|
-
hash:
|
|
716
|
+
hash: 857401901605478284
|
|
716
717
|
requirements: []
|
|
717
718
|
rubyforge_project:
|
|
718
719
|
rubygems_version: 1.8.23
|