bosh-director 1.2583.0 → 1.2596.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,6 +23,7 @@ require 'zlib'
23
23
 
24
24
  require 'common/exec'
25
25
  require 'common/properties'
26
+ require 'common/version/release_version_list'
26
27
 
27
28
  require 'bcrypt'
28
29
  require 'blobstore_client'
@@ -48,7 +49,6 @@ require 'bosh/director/validation_helper'
48
49
  require 'bosh/director/download_helper'
49
50
 
50
51
  require 'bosh/director/version'
51
- require 'bosh/director/next_rebase_version'
52
52
  require 'bosh/director/config'
53
53
  require 'bosh/director/event_log'
54
54
  require 'bosh/director/task_result_file'
@@ -72,10 +72,12 @@ require 'bosh/director/hash_string_vals'
72
72
  require 'bosh/director/instance_deleter'
73
73
  require 'bosh/director/instance_updater'
74
74
  require 'bosh/director/instance_updater/preparer'
75
- require 'bosh/director/instance_updater/network_updater'
76
75
  require 'bosh/director/instance_updater/stopper'
76
+ require 'bosh/director/instance_updater/network_updater'
77
+ require 'bosh/director/instance_updater/vm_updater'
77
78
  require 'bosh/director/job_runner'
78
79
  require 'bosh/director/job_updater'
80
+ require 'bosh/director/job_updater_factory'
79
81
  require 'bosh/director/job_queue'
80
82
  require 'bosh/director/lock'
81
83
  require 'bosh/director/nats_rpc'
@@ -5,25 +5,20 @@ module Bosh::Director
5
5
  class PackagesController < BaseController
6
6
  post '/packages/matches', :consumes => :yaml do
7
7
  manifest = Psych.load(request.body)
8
+
8
9
  unless manifest.is_a?(Hash) && manifest['packages'].is_a?(Array)
9
10
  raise BadManifest, "Manifest doesn't have a usable packages section"
10
11
  end
11
12
 
12
- fp_list = []
13
- sha1_list = []
13
+ fingerprint_list = []
14
14
 
15
15
  manifest['packages'].each do |package|
16
- fp_list << package['fingerprint'] if package['fingerprint']
17
- sha1_list << package['sha1'] if package['sha1']
16
+ fingerprint_list << package['fingerprint'] if package['fingerprint']
18
17
  end
19
18
 
20
- filter = {:fingerprint => fp_list, :sha1 => sha1_list}.sql_or
21
-
22
- result = Models::Package.where(filter).all.map { |package|
23
- [package.sha1, package.fingerprint]
24
- }.flatten.compact.uniq
19
+ matching_packages = Models::Package.where(fingerprint: fingerprint_list).all
25
20
 
26
- json_encode(result)
21
+ json_encode(matching_packages.map(&:fingerprint).compact.uniq)
27
22
  end
28
23
  end
29
24
  end
@@ -27,19 +27,22 @@ module Bosh::Director
27
27
  release_version = dataset.filter(:version => version).first
28
28
  if release_version.nil?
29
29
  begin
30
- # specified version not found, try formatted version
31
- formatted_version = Bosh::Common::VersionNumber.parse(version).to_s
32
- unless version == formatted_version
33
- # only check db if the formatted version is different
34
- release_version = dataset.filter(:version => formatted_version).first
35
- end
36
- if release_version.nil?
37
- raise ReleaseVersionNotFound,
38
- "Release version `#{release.name}/#{version}' doesn't exist"
39
- end
30
+ new_formatted_version = Bosh::Common::Version::ReleaseVersion.parse(version)
40
31
  rescue SemiSemantic::ParseError
41
32
  raise ReleaseVersionInvalid, "Release version invalid: #{version}"
42
33
  end
34
+ if version == new_formatted_version.to_s
35
+ old_formatted_version = new_formatted_version.to_old_format
36
+ if old_formatted_version
37
+ release_version = dataset.filter(:version => old_formatted_version).first
38
+ end
39
+ else
40
+ release_version = dataset.filter(:version => new_formatted_version.to_s).first
41
+ end
42
+ if release_version.nil?
43
+ raise ReleaseVersionNotFound,
44
+ "Release version `#{release.name}/#{version}' doesn't exist"
45
+ end
43
46
  end
44
47
 
45
48
  release_version
@@ -312,7 +312,7 @@ module Bosh::Director
312
312
  # @return [void]
313
313
  def bind_configuration
314
314
  @deployment_plan.jobs_starting_on_deploy.each do |job|
315
- JobRenderer.new(job).render_job_instances(@blobstore)
315
+ JobRenderer.new(job, @blobstore).render_job_instances
316
316
  end
317
317
  end
318
318
 
@@ -51,6 +51,7 @@ module Bosh::Director
51
51
  idle_vm.vm.update(:apply_spec => state)
52
52
  instance.model.update(:vm => idle_vm.vm)
53
53
  end
54
+
54
55
  instance.current_state = state
55
56
  end
56
57
  end
@@ -1,19 +1,27 @@
1
1
  module Bosh::Director
2
2
  module DeploymentPlan
3
3
  class SerialMultiJobUpdater
4
+ def initialize(job_updater_factory)
5
+ @job_updater_factory = job_updater_factory
6
+ end
7
+
4
8
  def run(base_job, deployment_plan, jobs)
5
9
  base_job.logger.info("Updating jobs serially: #{jobs.map(&:name).join(', ')}")
6
10
 
7
11
  jobs.each do |j|
8
12
  base_job.task_checkpoint
9
13
  base_job.logger.info("Updating job: #{j.name}")
10
- job_updater = JobUpdater.new(deployment_plan, j)
14
+ job_updater = @job_updater_factory.new_job_updater(deployment_plan, j)
11
15
  job_updater.update
12
16
  end
13
17
  end
14
18
  end
15
19
 
16
20
  class ParallelMultiJobUpdater
21
+ def initialize(job_updater_factory)
22
+ @job_updater_factory = job_updater_factory
23
+ end
24
+
17
25
  def run(base_job, deployment_plan, jobs)
18
26
  base_job.logger.info("Updating jobs in parallel: #{jobs.map(&:name).join(', ')}")
19
27
  base_job.task_checkpoint
@@ -22,7 +30,7 @@ module Bosh::Director
22
30
  jobs.each do |j|
23
31
  pool.process do
24
32
  base_job.logger.info("Updating job: #{j.name}")
25
- job_updater = JobUpdater.new(deployment_plan, j)
33
+ job_updater = @job_updater_factory.new_job_updater(deployment_plan, j)
26
34
  job_updater.update
27
35
  end
28
36
  end
@@ -31,9 +39,13 @@ module Bosh::Director
31
39
  end
32
40
 
33
41
  class BatchMultiJobUpdater
42
+ def initialize(job_updater_factory)
43
+ @job_updater_factory = job_updater_factory
44
+ end
45
+
34
46
  def run(base_job, deployment_plan, jobs)
35
- serial_updater = SerialMultiJobUpdater.new
36
- parallel_updater = ParallelMultiJobUpdater.new
47
+ serial_updater = SerialMultiJobUpdater.new(@job_updater_factory)
48
+ parallel_updater = ParallelMultiJobUpdater.new(@job_updater_factory)
37
49
 
38
50
  partition_jobs_by_serial(jobs).each do |jp|
39
51
  updater = jp.first.update.serial? ? serial_updater : parallel_updater
@@ -20,10 +20,10 @@ module Bosh::Director
20
20
  instance_vm_binder = DeploymentPlan::InstanceVmBinder.new(@event_log)
21
21
  instance_vm_binder.bind_instance_vms(@job.instances)
22
22
 
23
- job_renderer = JobRenderer.new(@job)
24
- job_renderer.render_job_instances(@blobstore)
23
+ job_renderer = JobRenderer.new(@job, @blobstore)
24
+ job_renderer.render_job_instances
25
25
 
26
- job_updater = JobUpdater.new(@deployment, @job)
26
+ job_updater = JobUpdater.new(@deployment, @job, job_renderer)
27
27
  job_updater.update
28
28
  end
29
29
 
@@ -4,38 +4,33 @@ module Bosh::Director
4
4
  class InstanceUpdater
5
5
  include DnsHelper
6
6
 
7
- MAX_ATTACH_DISK_TRIES = 3
8
7
  UPDATE_STEPS = 7
9
8
  WATCH_INTERVALS = 10
10
9
 
11
10
  attr_reader :current_state
12
11
 
13
12
  # @params [DeploymentPlan::Instance] instance
14
- def initialize(instance, event_log_task)
13
+ def initialize(instance, event_log_task, job_renderer)
14
+ @instance = instance
15
+ @event_log_task = event_log_task
16
+ @job_renderer = job_renderer
17
+
15
18
  @cloud = Config.cloud
16
19
  @logger = Config.logger
17
- @event_log_task = event_log_task
18
20
  @blobstore = App.instance.blobstores.blobstore
19
21
 
20
- @instance = instance
21
22
  @job = instance.job
22
-
23
23
  @target_state = @instance.state
24
24
 
25
25
  @deployment_plan = @job.deployment
26
- @resource_pool_spec = @job.resource_pool
26
+ @resource_pool = @job.resource_pool
27
27
  @update_config = @job.update
28
28
 
29
29
  @vm = @instance.model.vm
30
30
 
31
31
  @current_state = {}
32
32
 
33
- @network_updater = NetworkUpdater.new(@instance, @vm, agent, self, @cloud, @logger)
34
- @stopper = Stopper.new(@instance, agent, @target_state, Config, @logger)
35
- end
36
-
37
- def instance_name
38
- "#{@job.name}/#{@instance.index}"
33
+ @agent = AgentClient.with_defaults(@vm.agent_id)
39
34
  end
40
35
 
41
36
  def step
@@ -67,13 +62,11 @@ module Bosh::Director
67
62
  step { take_snapshot }
68
63
 
69
64
  if @target_state == "detached"
70
- detach_disk
71
- delete_vm
72
- @resource_pool_spec.add_idle_vm
65
+ vm_updater.detach
73
66
  return
74
67
  end
75
68
 
76
- step { update_resource_pool }
69
+ step { update_resource_pool(nil) }
77
70
  step { update_networks }
78
71
  step { update_dns }
79
72
  step { update_persistent_disk }
@@ -89,11 +82,11 @@ module Bosh::Director
89
82
  step { wait_until_running }
90
83
 
91
84
  if @target_state == "started" && current_state["job_state"] != "running"
92
- raise AgentJobNotRunning, "`#{instance_name}' is not running after update"
85
+ raise AgentJobNotRunning, "`#{@instance}' is not running after update"
93
86
  end
94
87
 
95
88
  if @target_state == "stopped" && current_state["job_state"] == "running"
96
- raise AgentJobNotStopped, "`#{instance_name}' is still running despite the stop command"
89
+ raise AgentJobNotStopped, "`#{@instance}' is still running despite the stop command"
97
90
  end
98
91
  end
99
92
 
@@ -103,9 +96,9 @@ module Bosh::Director
103
96
  def wait_until_running
104
97
  watch_schedule(min_watch_time, max_watch_time).each do |watch_time|
105
98
  sleep_time = watch_time.to_f / 1000
106
- @logger.info("Waiting for #{sleep_time} seconds to check #{instance_name} status")
99
+ @logger.info("Waiting for #{sleep_time} seconds to check #{@instance} status")
107
100
  sleep(sleep_time)
108
- @logger.info("Checking if #{instance_name} has been updated after #{sleep_time} seconds")
101
+ @logger.info("Checking if #{@instance} has been updated after #{sleep_time} seconds")
109
102
 
110
103
  @current_state = agent.get_state
111
104
 
@@ -141,7 +134,8 @@ module Bosh::Director
141
134
  end
142
135
 
143
136
  def stop
144
- @stopper.stop
137
+ stopper = Stopper.new(@instance, agent, @target_state, Config, @logger)
138
+ stopper.stop
145
139
  end
146
140
 
147
141
  def take_snapshot
@@ -152,59 +146,6 @@ module Bosh::Director
152
146
  Api::SnapshotManager.delete_snapshots(disk.snapshots)
153
147
  end
154
148
 
155
- def detach_disk
156
- return unless @instance.disk_currently_attached?
157
-
158
- if @instance.model.persistent_disk_cid.nil?
159
- raise AgentUnexpectedDisk,
160
- "`#{instance_name}' VM has disk attached " +
161
- "but it's not reflected in director DB"
162
- end
163
-
164
- agent.unmount_disk(@instance.model.persistent_disk_cid)
165
- @cloud.detach_disk(@vm.cid, @instance.model.persistent_disk_cid)
166
- end
167
-
168
- def attach_disk
169
- return if @instance.model.persistent_disk_cid.nil?
170
-
171
- @cloud.attach_disk(@vm.cid, @instance.model.persistent_disk_cid)
172
- agent.mount_disk(@instance.model.persistent_disk_cid)
173
- end
174
-
175
- def delete_vm
176
- @cloud.delete_vm(@vm.cid)
177
-
178
- @instance.model.db.transaction do
179
- @instance.model.vm = nil
180
- @instance.model.save
181
- @vm.destroy
182
- end
183
- end
184
-
185
- def create_vm(new_disk_id)
186
- stemcell = @resource_pool_spec.stemcell
187
- disks = [@instance.model.persistent_disk_cid, new_disk_id].compact
188
-
189
- @vm = VmCreator.create(@deployment_plan.model, stemcell.model,
190
- @resource_pool_spec.cloud_properties,
191
- @instance.network_settings, disks,
192
- @resource_pool_spec.env)
193
-
194
- begin
195
- @instance.model.vm = @vm
196
- @instance.model.save
197
-
198
- agent.wait_until_ready
199
- rescue => e
200
- if @vm
201
- @logger.error("error during create_vm(), deleting vm #{@vm.cid}")
202
- delete_vm
203
- end
204
- raise e
205
- end
206
- end
207
-
208
149
  def apply_state(state)
209
150
  @vm.update(:apply_spec => state)
210
151
  agent.apply(state)
@@ -225,6 +166,7 @@ module Bosh::Director
225
166
 
226
167
  def delete_disk(disk, vm_cid)
227
168
  disk_cid = disk.disk_cid
169
+
228
170
  # Unmount the disk only if disk is known by the agent
229
171
  if agent && disk_info.include?(disk_cid)
230
172
  agent.unmount_disk(disk_cid)
@@ -235,7 +177,7 @@ module Bosh::Director
235
177
  rescue Bosh::Clouds::DiskNotAttached
236
178
  if disk.active
237
179
  raise CloudDiskNotAttached,
238
- "`#{instance_name}' VM should have persistent disk attached " +
180
+ "`#{@instance}' VM should have persistent disk attached " +
239
181
  "but it doesn't (according to CPI)"
240
182
  end
241
183
  end
@@ -266,57 +208,12 @@ module Bosh::Director
266
208
  end
267
209
  end
268
210
 
269
- def update_resource_pool(new_disk_cid = nil)
270
- return unless @instance.resource_pool_changed? || new_disk_cid
271
-
272
- detach_disk
273
- num_retries = 0
274
- begin
275
- delete_vm
276
- create_vm(new_disk_cid)
277
- attach_disk
278
- rescue Bosh::Clouds::NoDiskSpace => e
279
- if e.ok_to_retry && num_retries < MAX_ATTACH_DISK_TRIES
280
- num_retries += 1
281
- @logger.warn("Retrying attach disk operation #{num_retries}")
282
- retry
283
- end
284
- @logger.warn("Giving up on attach disk operation")
285
- e.ok_to_retry = false
286
- raise CloudNotEnoughDiskSpace,
287
- "Not enough disk space to update `#{instance_name}'"
288
- end
289
-
290
- state = {
291
- "deployment" => @deployment_plan.name,
292
- "networks" => @instance.network_settings,
293
- "resource_pool" => @job.resource_pool.spec,
294
- "job" => @job.spec,
295
- "index" => @instance.index,
296
- }
297
-
298
- if @instance.disk_size > 0
299
- state["persistent_disk"] = @instance.disk_size
300
- end
301
-
302
- # if we have a failure above the new VM doesn't get any state,
303
- # which makes it impossible to recreate it
304
- apply_state(state)
305
- @instance.current_state = agent.get_state
306
- end
307
-
308
- def attach_missing_disk
309
- if @instance.model.persistent_disk_cid &&
310
- !@instance.disk_currently_attached?
311
- attach_disk
312
- end
313
- rescue Bosh::Clouds::NoDiskSpace => e
314
- update_resource_pool(@instance.model.persistent_disk_cid)
211
+ def update_resource_pool(new_disk_cid)
212
+ @vm, @agent = vm_updater.update(new_disk_cid)
315
213
  end
316
214
 
317
215
  # Synchronizes persistent_disks with the agent.
318
- #
319
- # NOTE: Currently assumes that we only have 1 persistent disk.
216
+ # (Currently assumes that we only have 1 persistent disk.)
320
217
  # @return [void]
321
218
  def check_persistent_disk
322
219
  return if @instance.model.persistent_disks.empty?
@@ -324,23 +221,20 @@ module Bosh::Director
324
221
 
325
222
  if agent_disk_cid != @instance.model.persistent_disk_cid
326
223
  raise AgentDiskOutOfSync,
327
- "`#{instance_name}' has invalid disks: agent reports " +
224
+ "`#{@instance}' has invalid disks: agent reports " +
328
225
  "`#{agent_disk_cid}' while director record shows " +
329
226
  "`#{@instance.model.persistent_disk_cid}'"
330
227
  end
331
228
 
332
229
  @instance.model.persistent_disks.each do |disk|
333
230
  unless disk.active
334
- @logger.warn("`#{instance_name}' has inactive disk #{disk.disk_cid}")
231
+ @logger.warn("`#{@instance}' has inactive disk #{disk.disk_cid}")
335
232
  end
336
233
  end
337
234
  end
338
235
 
339
236
  def update_persistent_disk
340
- # CLEANUP FIXME
341
- # [olegs] Error cleanup should be performed AFTER logic cleanup, I can't
342
- # event comprehend this method.
343
- attach_missing_disk
237
+ vm_updater.attach_missing_disk
344
238
  check_persistent_disk
345
239
 
346
240
  disk_cid = nil
@@ -399,22 +293,8 @@ module Bosh::Director
399
293
  end
400
294
 
401
295
  def update_networks
402
- @network_updater.update
403
- end
404
-
405
- def agent
406
- if @agent && @agent.id == @vm.agent_id
407
- @agent
408
- else
409
- if @vm.agent_id.nil?
410
- raise VmAgentIdMissing, "VM #{@vm.id} is missing agent id"
411
- end
412
- @agent = AgentClient.with_defaults(@vm.agent_id)
413
- end
414
- end
415
-
416
- def generate_agent_id
417
- SecureRandom.uuid
296
+ network_updater = NetworkUpdater.new(@instance, @vm, agent, vm_updater, @cloud, @logger)
297
+ @vm, @agent = network_updater.update
418
298
  end
419
299
 
420
300
  # Returns an array of wait times distributed
@@ -446,5 +326,13 @@ module Bosh::Director
446
326
  def canary?
447
327
  @canary
448
328
  end
329
+
330
+ attr_reader :agent
331
+
332
+ def vm_updater
333
+ # Do not memoize to avoid caching same VM and agent
334
+ # which could be replaced after updating a VM
335
+ VmUpdater.new(@instance, @vm, agent, @job_renderer, @cloud, 3, @logger)
336
+ end
449
337
  end
450
338
  end