bosh-director 1.3048.0 → 1.3050.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -32,14 +32,10 @@ module Bosh::Director
32
32
  @release_version_model = nil
33
33
 
34
34
  @rebase = !!options['rebase']
35
- @skip_if_exists = !!options['skip_if_exists']
36
35
 
37
36
  @manifest = nil
38
37
  @name = nil
39
38
  @version = nil
40
-
41
- @packages_unchanged = false
42
- @jobs_unchanged = false
43
39
  end
44
40
 
45
41
  # Extracts release tarball, verifies release manifest and saves release in DB
@@ -81,9 +77,7 @@ module Bosh::Director
81
77
 
82
78
  result = Bosh::Exec.sh("tar -C #{release_dir} -xzf #{@release_path} 2>&1", :on_error => :return)
83
79
  if result.failed?
84
- logger.error("Failed to extract release archive '#{@release_path}' into dir '#{release_dir}', " +
85
- "tar returned #{result.exit_status}, " +
86
- "output: #{result.output}")
80
+ logger.error("Failed to extract release archive '#{@release_path}' into dir '#{release_dir}', tar returned #{result.exit_status}, output: #{result.output})")
87
81
  FileUtils.rm_rf(release_dir)
88
82
  raise ReleaseInvalidArchive, "Extracting release archive failed. Check task debug log for details."
89
83
  end
@@ -95,19 +89,13 @@ module Bosh::Director
95
89
  # @return [void]
96
90
  def verify_manifest(release_dir)
97
91
  manifest_file = File.join(release_dir, "release.MF")
98
- unless File.file?(manifest_file)
99
- raise ReleaseManifestNotFound, "Release manifest not found"
100
- end
92
+ raise ReleaseManifestNotFound, "Release manifest not found" unless File.file?(manifest_file)
101
93
 
102
94
  @manifest = Psych.load_file(manifest_file)
103
95
 
104
96
  #handle compiled_release case
105
97
  @compiled_release = !!@manifest["compiled_packages"]
106
- if @compiled_release
107
- @packages_folder = "compiled_packages"
108
- else
109
- @packages_folder = "packages"
110
- end
98
+ @packages_folder = @compiled_release ? "compiled_packages" : "packages"
111
99
 
112
100
  normalize_manifest
113
101
 
@@ -126,6 +114,15 @@ module Bosh::Director
126
114
  @uncommitted_changes = @manifest.fetch("uncommitted_changes", nil)
127
115
  end
128
116
 
117
+ def compiled_release
118
+ raise "Don't know what kind of release we have until verify_release is called" unless @manifest
119
+ @compiled_release
120
+ end
121
+
122
+ def source_release
123
+ !compiled_release
124
+ end
125
+
129
126
  # Processes uploaded release, creates jobs and packages in DB if needed
130
127
  # @param [String] release_dir local path to the unpacked release
131
128
  # @return [void]
@@ -136,29 +133,14 @@ module Bosh::Director
136
133
  @version = next_release_version
137
134
  end
138
135
 
139
- version_attrs = {
140
- :release => @release_model,
141
- :version => @version.to_s
142
- }
136
+ version_attrs = { :release => @release_model, :version => @version.to_s }
143
137
  version_attrs[:uncommitted_changes] = @uncommitted_changes if @uncommitted_changes
144
138
  version_attrs[:commit_hash] = @commit_hash if @commit_hash
145
139
 
146
- @release_version_model = Models::ReleaseVersion.new(version_attrs)
147
- unless @release_version_model.valid?
148
- if @release_version_model.errors[:version] == [:format]
149
- raise ReleaseVersionInvalid,
150
- "Release version invalid `#{@name}/#{@version}'"
151
- elsif @skip_if_exists
152
- event_log.begin_stage("Release already exists", 1)
153
- event_log.track("#{@name}/#{@version}") {}
154
- return
155
- else
156
- raise ReleaseAlreadyExists,
157
- "Release `#{@name}/#{@version}' already exists"
158
- end
159
- end
160
-
161
- @release_version_model.save
140
+ @release_is_new = false
141
+ @release_version_model = Models::ReleaseVersion.find_or_create(version_attrs) {
142
+ @release_is_new = true
143
+ }
162
144
 
163
145
  single_step_stage("Resolving package dependencies") do
164
146
  resolve_package_dependencies(@manifest[@packages_folder])
@@ -166,7 +148,7 @@ module Bosh::Director
166
148
 
167
149
  @packages = {}
168
150
  process_packages(release_dir)
169
- process_jobs(release_dir)
151
+ process_jobs(release_dir) if @release_is_new
170
152
 
171
153
  event_log.begin_stage(@compiled_release ? "Compiled Release has been created" : "Release has been created", 1)
172
154
  event_log.track("#{@name}/#{@version}") {}
@@ -215,12 +197,16 @@ module Bosh::Director
215
197
 
216
198
  new_packages = []
217
199
  existing_packages = []
200
+ registered_packages = []
218
201
 
219
202
  @manifest[@packages_folder].each do |package_meta|
220
203
  # Checking whether we might have the same bits somewhere
221
- packages = Models::Package.where(fingerprint: package_meta["fingerprint"]).all
222
204
 
205
+ packages = Models::Package.where(fingerprint: package_meta["fingerprint"]).all
223
206
  if packages.empty?
207
+ unless @release_is_new
208
+ raise ReleaseInvalidPackage, "package #{package_meta['name']}/#{package_meta['version']} not part of previous upload of release #{@name}/#{@version}"
209
+ end
224
210
  new_packages << package_meta
225
211
  next
226
212
  end
@@ -240,7 +226,11 @@ module Bosh::Director
240
226
  existing_package.save
241
227
  end
242
228
 
243
- existing_packages << [existing_package, package_meta]
229
+ if existing_package.release_versions.include? @release_version_model
230
+ registered_packages << [existing_package, package_meta]
231
+ else
232
+ existing_packages << [existing_package, package_meta]
233
+ end
244
234
  else
245
235
  # We found a package with the same fingerprint but different
246
236
  # (release, name, version) tuple, so we need to make a copy
@@ -252,17 +242,55 @@ module Bosh::Director
252
242
  end
253
243
  end
254
244
 
255
- package_stemcell_hashes1 = create_packages(new_packages, release_dir)
256
- package_stemcell_hashes2 = use_existing_packages(existing_packages)
257
- consolidated_package_stemcell_hashes = Array(package_stemcell_hashes1) | Array(package_stemcell_hashes2)
245
+ did_something = false
246
+
247
+ package_stemcell_hashes1, created_package = create_packages(new_packages, release_dir)
248
+ did_something |= created_package
249
+
250
+ package_stemcell_hashes2, modified_package = use_existing_packages(existing_packages, release_dir)
251
+ did_something |= modified_package
258
252
 
259
- create_compiled_packages(consolidated_package_stemcell_hashes, release_dir)
253
+ if @compiled_release
254
+ compatible_stemcell_combos = registered_packages.flat_map do |pkg, pkg_meta|
255
+ stemcells_used_by_package(pkg_meta).map do |stemcell|
256
+ {
257
+ package: pkg,
258
+ stemcell: stemcell
259
+ }
260
+ end
261
+ end
262
+ consolidated_package_stemcell_hashes = Array(package_stemcell_hashes1) | Array(package_stemcell_hashes2) | compatible_stemcell_combos
263
+ did_something |= create_compiled_packages(consolidated_package_stemcell_hashes, release_dir)
264
+ else
265
+ did_something |= backfill_source_for_packages(registered_packages, release_dir)
266
+ end
267
+ did_something
268
+ end
269
+
270
+ # @return [boolean] true if sources were added to at least one package; false if the call had no effect.
271
+ def backfill_source_for_packages(packages, release_dir)
272
+ return false if packages.empty?
273
+
274
+ had_effect = false
275
+ single_step_stage("Processing #{packages.size} existing package#{"s" if packages.size > 1}") do
276
+ packages.each do |package, package_meta|
277
+ package_desc = "#{package.name}/#{package.version}"
278
+ logger.info("Adding source for package `#{package_desc}'")
279
+ had_effect |= save_package_source_blob(package, package_meta, release_dir)
280
+ package.save
281
+ end
282
+ end
283
+
284
+ had_effect
260
285
  end
261
286
 
262
287
  # Points release DB model to existing packages described by given metadata
263
- # @param [Array<Array>] packages Existing packages metadata
264
- def use_existing_packages(packages)
265
- return if packages.empty?
288
+ # @param [Array<Array>] packages Existing packages metadata.
289
+ # @return [Array<Hash>] package & stemcell matching pairs that were registered. empty if no packages were changed.
290
+ def use_existing_packages(packages, release_dir)
291
+ if packages.empty?
292
+ return [], false
293
+ end
266
294
 
267
295
  package_stemcell_hashes = []
268
296
 
@@ -272,63 +300,69 @@ module Bosh::Director
272
300
  logger.info("Using existing package `#{package_desc}'")
273
301
  register_package(package)
274
302
 
275
- if @compiled_release
303
+ if compiled_release
276
304
  stemcells = stemcells_used_by_package(package_meta)
277
305
  stemcells.each do |stemcell|
278
- hash = { "package" => package, "stemcell" => stemcell}
306
+ hash = { package: package, stemcell: stemcell}
279
307
  package_stemcell_hashes << hash
280
308
  end
281
309
  end
310
+
311
+ if source_release && package.blobstore_id.nil?
312
+ save_package_source_blob(package, package_meta, release_dir)
313
+ package.save
314
+ end
282
315
  end
283
316
  end
284
317
 
285
- package_stemcell_hashes
318
+ return package_stemcell_hashes, true
286
319
  end
287
320
 
288
321
  # Creates packages using provided metadata
289
322
  # @param [Array<Hash>] packages Packages metadata
290
323
  # @param [String] release_dir local path to the unpacked release
291
- # @return [Array<Hash>] package & stemcell
324
+ # @return [Array<Hash>, boolean] array of compiled package & stemcell matching pairs that were registered, and a
325
+ # flag indicating if any changes were made to the database.
292
326
  def create_packages(package_metas, release_dir)
293
327
  if package_metas.empty?
294
- @packages_unchanged = true
295
- return
328
+ return [], false
296
329
  end
297
330
 
298
331
  package_stemcell_hashes = []
332
+
299
333
  event_log.begin_stage("Creating new packages", package_metas.size)
300
334
 
301
- package = package_metas.each do |package_meta|
335
+ package_metas.each do |package_meta|
302
336
  package_desc = "#{package_meta["name"]}/#{package_meta["version"]}"
337
+ package = nil
303
338
  event_log.track(package_desc) do
304
339
  logger.info("Creating new package `#{package_desc}'")
305
340
  package = create_package(package_meta, release_dir)
306
341
  register_package(package)
307
- package
308
342
  end
309
343
 
310
344
  if @compiled_release
311
345
  stemcells = stemcells_used_by_package(package_meta)
312
346
  stemcells.each do |stemcell|
313
- hash = { "package" => package, "stemcell" => stemcell}
347
+ hash = { package: package, stemcell: stemcell}
314
348
  package_stemcell_hashes << hash
315
349
  end
316
350
  end
317
351
  end
318
352
 
319
- package_stemcell_hashes
353
+ return package_stemcell_hashes, true
320
354
  end
321
355
 
356
+ # @return [boolean] true if at least one job was created; false if the call had no effect.
322
357
  def create_compiled_packages(all_compiled_packages, release_dir)
323
- if all_compiled_packages.nil?
324
- return
325
- end
358
+ return false if all_compiled_packages.nil?
326
359
 
327
360
  event_log.begin_stage('Creating new compiled packages', all_compiled_packages.size)
328
361
 
362
+ had_effect = false
329
363
  all_compiled_packages.each do |compiled_package_spec|
330
- package = compiled_package_spec['package']
331
- stemcell = compiled_package_spec['stemcell']
364
+ package = compiled_package_spec[:package]
365
+ stemcell = compiled_package_spec[:stemcell]
332
366
 
333
367
  existing_compiled_package = Models::CompiledPackage.where(
334
368
  :package_id => package.id,
@@ -338,9 +372,11 @@ module Bosh::Director
338
372
  package_desc = "#{package.name}/#{package.version} for #{stemcell.name}/#{stemcell.version}"
339
373
  event_log.track(package_desc) do
340
374
  create_compiled_package(package, stemcell, release_dir)
375
+ had_effect = true
341
376
  end
342
377
  end
343
378
  end
379
+ had_effect
344
380
  end
345
381
 
346
382
  def stemcells_used_by_package(package_meta)
@@ -392,7 +428,7 @@ module Bosh::Director
392
428
  package_attrs = {
393
429
  :release => @release_model,
394
430
  :name => name,
395
- :sha1 => @compiled_release ? nil : package_meta['sha1'],
431
+ :sha1 => nil,
396
432
  :blobstore_id => nil,
397
433
  :fingerprint => package_meta['fingerprint'],
398
434
  :version => version
@@ -401,24 +437,34 @@ module Bosh::Director
401
437
  package = Models::Package.new(package_attrs)
402
438
  package.dependency_set = package_meta['dependencies']
403
439
 
404
- unless @compiled_release
405
- existing_blob = package_meta['blobstore_id']
406
- desc = "package '#{name}/#{version}'"
440
+ save_package_source_blob(package, package_meta, release_dir) unless @compiled_release
407
441
 
408
- if existing_blob
409
- logger.info("Creating #{desc} from existing blob #{existing_blob}")
410
- package.blobstore_id = BlobUtil.copy_blob(existing_blob)
442
+ package.save
443
+ end
411
444
 
412
- else
413
- logger.info("Creating #{desc} from provided bits")
445
+ # @return [boolean] true if a new blob was created; false otherwise
446
+ def save_package_source_blob(package, package_meta, release_dir)
447
+ return false unless package.blobstore_id.nil?
414
448
 
415
- package_tgz = File.join(release_dir, 'packages', "#{name}.tgz")
416
- validate_tgz(package_tgz, desc)
417
- package.blobstore_id = BlobUtil.create_blob(package_tgz)
418
- end
449
+ name, version = package_meta['name'], package_meta['version']
450
+ existing_blob = package_meta['blobstore_id']
451
+ desc = "package '#{name}/#{version}'"
452
+
453
+ package.sha1 = package_meta['sha1']
454
+
455
+ if existing_blob
456
+ logger.info("Creating #{desc} from existing blob #{existing_blob}")
457
+ package.blobstore_id = BlobUtil.copy_blob(existing_blob)
458
+
459
+ elsif package
460
+ logger.info("Creating #{desc} from provided bits")
461
+
462
+ package_tgz = File.join(release_dir, 'packages', "#{name}.tgz")
463
+ validate_tgz(package_tgz, desc)
464
+ package.blobstore_id = BlobUtil.create_blob(package_tgz)
419
465
  end
420
466
 
421
- package.save
467
+ true
422
468
  end
423
469
 
424
470
  def validate_tgz(tgz, desc)
@@ -465,15 +511,15 @@ module Bosh::Director
465
511
  end
466
512
  end
467
513
 
468
- create_jobs(new_jobs, release_dir)
469
- use_existing_jobs(existing_jobs)
514
+ did_something = create_jobs(new_jobs, release_dir)
515
+ did_something |= use_existing_jobs(existing_jobs)
516
+
517
+ did_something
470
518
  end
471
519
 
520
+ # @return [boolean] true if at least one job was created; false if the call had no effect.
472
521
  def create_jobs(jobs, release_dir)
473
- if jobs.empty?
474
- @jobs_unchanged = true
475
- return
476
- end
522
+ return false if jobs.empty?
477
523
 
478
524
  event_log.begin_stage("Creating new jobs", jobs.size)
479
525
  jobs.each do |job_meta|
@@ -484,6 +530,8 @@ module Bosh::Director
484
530
  register_template(template)
485
531
  end
486
532
  end
533
+
534
+ true
487
535
  end
488
536
 
489
537
  def create_job(job_meta, release_dir)
@@ -492,9 +540,9 @@ module Bosh::Director
492
540
  end
493
541
 
494
542
  # @param [Array<Array>] jobs Existing jobs metadata
495
- # @return [void]
543
+ # @return [boolean] true if at least one job was tied to the release version; false if the call had no effect.
496
544
  def use_existing_jobs(jobs)
497
- return if jobs.empty?
545
+ return false if jobs.empty?
498
546
 
499
547
  single_step_stage("Processing #{jobs.size} existing job#{"s" if jobs.size > 1}") do
500
548
  jobs.each do |template, _|
@@ -503,6 +551,8 @@ module Bosh::Director
503
551
  register_template(template)
504
552
  end
505
553
  end
554
+
555
+ true
506
556
  end
507
557
 
508
558
  # Marks job template model as being used by release version
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Director
3
- VERSION = '1.3048.0'
3
+ VERSION = '1.3050.0'
4
4
  end
5
5
  end
data/lib/bosh/director.rb CHANGED
@@ -151,7 +151,6 @@ require 'bosh/director/api/controllers/stemcells_controller'
151
151
  require 'bosh/director/api/controllers/tasks_controller'
152
152
  require 'bosh/director/api/controllers/task_controller'
153
153
  require 'bosh/director/api/controllers/users_controller'
154
- require 'bosh/director/api/controllers/compiled_packages_controller'
155
154
  require 'bosh/director/api/controllers/cloud_configs_controller'
156
155
  require 'bosh/director/api/controllers/locks_controller'
157
156
  require 'bosh/director/api/route_configuration'
data/lib/cloud/dummy.rb CHANGED
@@ -49,9 +49,20 @@ module Bosh
49
49
  # rubocop:enable ParameterLists
50
50
  @logger.info('Dummy: create_vm')
51
51
 
52
+ ips = []
52
53
  cmd = commands.next_create_vm_cmd
53
54
 
54
- write_agent_default_network(agent_id, cmd.ip_address) if cmd.ip_address
55
+ if cmd.ip_address
56
+ # special case used by dynamic IP assignment tests: CPI always chooses its own IP
57
+ write_agent_default_network(agent_id, cmd.ip_address)
58
+ ips << { 'network' => 'cloud', 'ip' => cmd.ip_address }
59
+ else
60
+ networks.each do |network_name, network|
61
+ ips << { 'network' => network_name, 'ip' => network['ip'] }
62
+ end
63
+ end
64
+
65
+ allocate_ips(ips)
55
66
 
56
67
  write_agent_settings(agent_id, {
57
68
  agent_id: agent_id,
@@ -67,7 +78,7 @@ module Bosh
67
78
  agent_pid = spawn_agent_process(agent_id)
68
79
 
69
80
  FileUtils.mkdir_p(@running_vms_dir)
70
- File.write(vm_file(agent_pid), agent_id)
81
+ File.write(vm_file(agent_pid), JSON.dump("agent_id" => agent_id, "ips" => ips))
71
82
 
72
83
  agent_pid.to_s
73
84
  end
@@ -79,6 +90,7 @@ module Bosh
79
90
  rescue Errno::ESRCH
80
91
  # rubocop:enable HandleExceptions
81
92
  ensure
93
+ free_ips(ips_for_vm_id(vm_name)) if has_vm?(vm_name)
82
94
  FileUtils.rm_rf(File.join(@base_dir, 'running_vms', vm_name))
83
95
  end
84
96
 
@@ -202,8 +214,32 @@ module Bosh
202
214
  agent_pid
203
215
  end
204
216
 
217
+ def allocate_ips(ips)
218
+ ips.each do |ip|
219
+ begin
220
+ network_dir = File.join(@base_dir, 'dummy_cpi_networks', ip['network'])
221
+ FileUtils.makedirs(network_dir)
222
+ open(File.join(network_dir, ip['ip']), File::WRONLY|File::CREAT|File::EXCL).close
223
+ rescue Errno::EEXIST
224
+ # at this point we should actually free all the IPs we successfully allocated before the collision,
225
+ # but in practice the tests only feed in one IP per VM so that cleanup code would never be exercised
226
+ raise "IP Address #{ip['ip']} in network '#{ip['network']}' is already in use"
227
+ end
228
+ end
229
+ end
230
+
231
+ def free_ips(ips)
232
+ ips.each do |ip|
233
+ FileUtils.rm_rf(File.join(@base_dir, 'dummy_cpi_networks', ip['network'], ip['ip']))
234
+ end
235
+ end
236
+
237
+ def ips_for_vm_id(vm_id)
238
+ JSON.parse(File.read(vm_file(vm_id)))['ips']
239
+ end
240
+
205
241
  def agent_id_for_vm_id(vm_id)
206
- File.read(vm_file(vm_id))
242
+ JSON.parse(File.read(vm_file(vm_id)))['agent_id']
207
243
  end
208
244
 
209
245
  def agent_settings_file(agent_id)