bosh-director 1.1750.0 → 1.1761.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -403,10 +403,12 @@ module Bosh::Director
403
403
 
404
404
  def delete_unneeded_vms
405
405
  unneeded_vms = @deployment_plan.unneeded_vms
406
- return if unneeded_vms.empty?
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
- return if unneeded_instances.empty?
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).delete_instances(unneeded_instances)
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 'common/deep_copy'
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
- job = new(deployment, job_spec)
73
- job.parse
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
- # @param [Hash] job_spec Raw job spec from the deployment manifest
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
- return collection if @templates.none? { |template| template.properties }
442
- return extract_template_properties(collection) if @templates.all? { |template| template.properties }
443
- raise JobIncompatibleSpecs, "Job `#{name}' has specs with conflicting property definition styles between" +
444
- " its job spec templates. This may occur if colocating jobs, one of which has a spec file including" +
445
- " `properties' and one which doesn't."
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
- domain.destroy # cascaded - all records are removed
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
@@ -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.has_key?(property)
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 #{self.class.name}"
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
- "Property `#{property}' (value #{value.inspect}) did not match the required type `#{klass}'"
51
+ "Property `#{property}' (value #{value.inspect}) did not match the required type `#{klass}'"
49
52
  end
50
53
  end
51
54
  end
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Director
3
- VERSION = '1.1750.0'
3
+ VERSION = '1.1761.0'
4
4
  end
5
5
  end
@@ -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 blobstore_uri
35
- properties = @options['agent']['blobstore']['options']
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
- agent_cmd = %W[bosh_agent -a #{agent_id} -s #{blobstore_uri} -p simple -b #{agent_base_dir} -n #{nats_uri} -r #{root_dir} --no-alerts]
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.1750.0
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-08 00:00:00.000000000 Z
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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.1750.0
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
- 5212a9'
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: 2997654396825944747
716
+ hash: 857401901605478284
716
717
  requirements: []
717
718
  rubyforge_project:
718
719
  rubygems_version: 1.8.23