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.
@@ -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