bosh-director 1.2168.0 → 1.2175.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/bosh/director.rb CHANGED
@@ -66,6 +66,7 @@ require 'bosh/director/vm_data'
66
66
  require 'bosh/director/vm_reuser'
67
67
  require 'bosh/director/deployment_plan'
68
68
  require 'bosh/director/deployment_plan/assembler'
69
+ require 'bosh/director/errand'
69
70
  require 'bosh/director/duration'
70
71
  require 'bosh/director/hash_string_vals'
71
72
  require 'bosh/director/instance_deleter'
@@ -12,6 +12,7 @@ require 'bosh/director/deployment_plan/network_subnet'
12
12
  require 'bosh/director/deployment_plan/compiled_package'
13
13
  require 'bosh/director/deployment_plan/preparer'
14
14
  require 'bosh/director/deployment_plan/resource_pools'
15
+ require 'bosh/director/deployment_plan/instance_vm_binder'
15
16
  require 'bosh/director/deployment_plan/multi_job_updater'
16
17
  require 'bosh/director/deployment_plan/updater'
17
18
  require 'bosh/director/deployment_plan/release_version'
@@ -23,3 +24,5 @@ require 'bosh/director/deployment_plan/dynamic_network'
23
24
  require 'bosh/director/deployment_plan/manual_network'
24
25
  require 'bosh/director/deployment_plan/vip_network'
25
26
  require 'bosh/director/deployment_plan/planner'
27
+ require 'bosh/director/deployment_plan/dns_binder'
28
+
@@ -2,7 +2,6 @@ module Bosh::Director
2
2
  # DeploymentPlan::Assembler is used to populate deployment plan with information
3
3
  # about existing deployment and information from director DB
4
4
  class DeploymentPlan::Assembler
5
- include DnsHelper
6
5
  include LockHelper
7
6
  include IpUtil
8
7
 
@@ -266,27 +265,11 @@ module Bosh::Director
266
265
  # resource pool if necessary)
267
266
  # @return [void]
268
267
  def bind_unallocated_vms
269
- @deployment_plan.jobs.each do |job|
270
- job.instances.each do |instance|
271
- instance.bind_unallocated_vm
272
- # Now that we know every VM has been allocated and instance models are
273
- # bound, we can sync the state.
274
- instance.sync_state_with_db
275
- end
276
- end
268
+ @deployment_plan.jobs_starting_on_deploy.each(&:bind_unallocated_vms)
277
269
  end
278
270
 
279
271
  def bind_instance_networks
280
- @deployment_plan.jobs.each do |job|
281
- job.instances.each do |instance|
282
- instance.network_reservations.each do |name, reservation|
283
- unless reservation.reserved?
284
- network = @deployment_plan.network(name)
285
- network.reserve!(reservation, "`#{job.name}/#{instance.index}'")
286
- end
287
- end
288
- end
289
- end
272
+ @deployment_plan.jobs_starting_on_deploy.each(&:bind_instance_networks)
290
273
  end
291
274
 
292
275
  # Binds template models for each release spec in the deployment plan
@@ -328,84 +311,22 @@ module Bosh::Director
328
311
  # Calculates configuration checksums for all jobs in this deployment plan
329
312
  # @return [void]
330
313
  def bind_configuration
331
- @deployment_plan.jobs.each do |job|
314
+ @deployment_plan.jobs_starting_on_deploy.each do |job|
332
315
  JobRenderer.new(job).render_job_instances(@blobstore)
333
316
  end
334
317
  end
335
318
 
336
319
  def bind_dns
337
- domain = Models::Dns::Domain.find_or_create(:name => dns_domain_name,
338
- :type => 'NATIVE')
339
- @deployment_plan.dns_domain = domain
340
-
341
- soa_record = Models::Dns::Record.find_or_create(:domain_id => domain.id,
342
- :name => dns_domain_name,
343
- :type => 'SOA')
344
- soa_record.content = SOA
345
- soa_record.ttl = 300
346
- soa_record.save
347
-
348
- # add NS record
349
- Models::Dns::Record.find_or_create(:domain_id => domain.id,
350
- :name => dns_domain_name,
351
- :type =>'NS', :ttl => TTL_4H,
352
- :content => dns_ns_record)
353
- # add A record for name server
354
- Models::Dns::Record.find_or_create(:domain_id => domain.id,
355
- :name => dns_ns_record,
356
- :type =>'A', :ttl => TTL_4H,
357
- :content => Config.dns['address'])
320
+ binder = DeploymentPlan::DnsBinder.new(@deployment_plan)
321
+ binder.bind_deployment
358
322
  end
359
323
 
360
324
  def bind_instance_vms
361
- unbound_instances = []
362
-
363
- @deployment_plan.jobs.each do |job|
364
- job.instances.each do |instance|
365
- # Don't allocate resource pool VMs to instances in detached state
366
- next if instance.state == 'detached'
367
- # Skip bound instances
368
- next if instance.model.vm
369
- unbound_instances << instance
370
- end
371
- end
325
+ jobs = @deployment_plan.jobs_starting_on_deploy
326
+ instances = jobs.map(&:instances).flatten
372
327
 
373
- return if unbound_instances.empty?
374
-
375
- @event_log.begin_stage('Binding instance VMs', unbound_instances.size)
376
-
377
- ThreadPool.new(:max_threads => Config.max_threads).wrap do |pool|
378
- unbound_instances.each do |instance|
379
- pool.process do
380
- bind_instance_vm(instance)
381
- end
382
- end
383
- end
384
- end
385
-
386
- # @param [DeploymentPlan::Instance]
387
- def bind_instance_vm(instance)
388
- @event_log.track("#{instance.job.name}/#{instance.index}") do
389
- idle_vm = instance.idle_vm
390
-
391
- # Apply the assignment to the VM
392
- agent = AgentClient.with_defaults(idle_vm.vm.agent_id)
393
- state = idle_vm.current_state
394
- state['job'] = instance.job.spec
395
- state['index'] = instance.index
396
- agent.apply(state)
397
-
398
- # Our assumption here is that director database access
399
- # is much less likely to fail than VM agent communication
400
- # so we only update database after we see a successful agent apply.
401
- # If database update fails subsequent deploy will try to
402
- # assign a new VM to this instance which is ok.
403
- idle_vm.vm.db.transaction do
404
- idle_vm.vm.update(:apply_spec => state)
405
- instance.model.update(:vm => idle_vm.vm)
406
- end
407
- instance.current_state = state
408
- end
328
+ binder = DeploymentPlan::InstanceVmBinder.new(@event_log)
329
+ binder.bind_instance_vms(instances)
409
330
  end
410
331
 
411
332
  def delete_unneeded_vms
@@ -0,0 +1,45 @@
1
+ module Bosh::Director
2
+ class DeploymentPlan::DnsBinder
3
+ include DnsHelper
4
+
5
+ def initialize(deployment)
6
+ @deployment = deployment
7
+ @config = Config
8
+ end
9
+
10
+ def bind_deployment
11
+ return unless @config.dns_enabled?
12
+
13
+ domain = Models::Dns::Domain.find_or_create(
14
+ :name => dns_domain_name,
15
+ :type => 'NATIVE',
16
+ )
17
+ @deployment.dns_domain = domain
18
+
19
+ soa_record = Models::Dns::Record.find_or_create(
20
+ :domain_id => domain.id,
21
+ :name => dns_domain_name,
22
+ :type => 'SOA',
23
+ )
24
+ soa_record.content = SOA
25
+ soa_record.ttl = 300
26
+ soa_record.save
27
+
28
+ # add NS record
29
+ Models::Dns::Record.find_or_create(
30
+ :domain_id => domain.id,
31
+ :name => dns_domain_name,
32
+ :type =>'NS', :ttl => TTL_4H,
33
+ :content => dns_ns_record,
34
+ )
35
+
36
+ # add A record for name server
37
+ Models::Dns::Record.find_or_create(
38
+ :domain_id => domain.id,
39
+ :name => dns_ns_record,
40
+ :type =>'A', :ttl => TTL_4H,
41
+ :content => @config.dns['address'],
42
+ )
43
+ end
44
+ end
45
+ end
@@ -162,7 +162,18 @@ module Bosh::Director
162
162
  @network_reservations.each do |name, reservation|
163
163
  network = @job.deployment.network(name)
164
164
  network_settings[name] = network.network_settings(reservation, default_properties[name])
165
- network_settings[name]['dns_record_name'] = dns_record_name(name)
165
+
166
+ # Temporary hack for running errands.
167
+ # We need to avoid RunErrand task thinking that
168
+ # network configuration for errand VM differs
169
+ # from network configuration for its Instance.
170
+ #
171
+ # Obviously this does not account for other changes
172
+ # in network configuration that errand job might need.
173
+ # (e.g. errand job desires static ip)
174
+ if @job.starts_on_deploy?
175
+ network_settings[name]['dns_record_name'] = dns_record_name(name)
176
+ end
166
177
 
167
178
  # Somewhat of a hack: for dynamic networks we might know IP address, Netmask & Gateway
168
179
  # if they're featured in agent state, in that case we put them into network spec to satisfy
@@ -0,0 +1,58 @@
1
+ module Bosh::Director
2
+ class DeploymentPlan::InstanceVmBinder
3
+ def initialize(event_log)
4
+ @event_log = event_log
5
+ end
6
+
7
+ # @param [Array<DeploymentPlan::Instance>]
8
+ # instances All instances to consider for binding to a VM
9
+ def bind_instance_vms(instances)
10
+ unbound_instances = []
11
+
12
+ instances.each do |instance|
13
+ # Don't allocate resource pool VMs to instances in detached state
14
+ next if instance.state == 'detached'
15
+
16
+ # Skip bound instances
17
+ next if instance.model.vm
18
+
19
+ unbound_instances << instance
20
+ end
21
+
22
+ return if unbound_instances.empty?
23
+
24
+ @event_log.begin_stage('Binding instance VMs', unbound_instances.size)
25
+
26
+ ThreadPool.new(:max_threads => Config.max_threads).wrap do |pool|
27
+ unbound_instances.each do |instance|
28
+ pool.process { bind_instance_vm(instance) }
29
+ end
30
+ end
31
+ end
32
+
33
+ # @param [DeploymentPlan::Instance] instance
34
+ def bind_instance_vm(instance)
35
+ @event_log.track("#{instance.job.name}/#{instance.index}") do
36
+ idle_vm = instance.idle_vm
37
+
38
+ # Apply the assignment to the VM
39
+ agent = AgentClient.with_defaults(idle_vm.vm.agent_id)
40
+ state = idle_vm.current_state
41
+ state['job'] = instance.job.spec
42
+ state['index'] = instance.index
43
+ agent.apply(state)
44
+
45
+ # Our assumption here is that director database access
46
+ # is much less likely to fail than VM agent communication
47
+ # so we only update database after we see a successful agent apply.
48
+ # If database update fails subsequent deploy will try to
49
+ # assign a new VM to this instance which is ok.
50
+ idle_vm.vm.db.transaction do
51
+ idle_vm.vm.update(:apply_spec => state)
52
+ instance.model.update(:vm => idle_vm.vm)
53
+ end
54
+ instance.current_state = state
55
+ end
56
+ end
57
+ end
58
+ end
@@ -5,6 +5,9 @@ module Bosh::Director
5
5
  class Job
6
6
  include Bosh::Common::PropertyHelper
7
7
 
8
+ VALID_LIFECYCLE_PROFILES = %w(service errand)
9
+ DEFAULT_LIFECYCLE_PROFILE = 'service'
10
+
8
11
  # started, stopped and detached are real states
9
12
  # (persisting in DB and reflecting target instance state)
10
13
  # recreate and restart are two virtual states
@@ -15,6 +18,9 @@ module Bosh::Director
15
18
  # @return [String] Job name
16
19
  attr_accessor :name
17
20
 
21
+ # @return [String] Lifecycle profile
22
+ attr_accessor :lifecycle
23
+
18
24
  # @return [String] Job canonical name (mostly for DNS)
19
25
  attr_accessor :canonical_name
20
26
 
@@ -229,6 +235,35 @@ module Bosh::Director
229
235
  end
230
236
  end
231
237
 
238
+ def bind_unallocated_vms
239
+ instances.each do |instance|
240
+ instance.bind_unallocated_vm
241
+
242
+ # Now that we know every VM has been allocated and
243
+ # instance models are bound, we can sync the state.
244
+ instance.sync_state_with_db
245
+ end
246
+ end
247
+
248
+ def bind_instance_networks
249
+ instances.each do |instance|
250
+ instance.network_reservations.each do |net_name, reservation|
251
+ unless reservation.reserved?
252
+ network = @deployment.network(net_name)
253
+ network.reserve!(reservation, "`#{name}/#{instance.index}'")
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ def starts_on_deploy?
260
+ @lifecycle == 'service'
261
+ end
262
+
263
+ def can_run_as_errand?
264
+ @lifecycle == 'errand'
265
+ end
266
+
232
267
  private
233
268
 
234
269
  # @param [Hash] collection All properties collection
@@ -21,6 +21,8 @@ module Bosh::Director
21
21
  @job = Job.new(@deployment)
22
22
 
23
23
  parse_name
24
+ parse_lifecycle
25
+
24
26
  parse_release
25
27
  parse_template
26
28
  parse_templates
@@ -45,6 +47,22 @@ module Bosh::Director
45
47
  @job.canonical_name = canonical(@job.name)
46
48
  end
47
49
 
50
+ def parse_lifecycle
51
+ lifecycle = safe_property(@job_spec, "lifecycle",
52
+ :class => String,
53
+ :optional => true,
54
+ :default => Job::DEFAULT_LIFECYCLE_PROFILE,
55
+ )
56
+
57
+ unless Job::VALID_LIFECYCLE_PROFILES.include?(lifecycle)
58
+ raise JobInvalidLifecycle,
59
+ "Invalid lifecycle `#{lifecycle}' for `#{@job.name}', " +
60
+ "valid lifecycle profiles are: #{Job::VALID_LIFECYCLE_PROFILES.join(', ')}"
61
+ end
62
+
63
+ @job.lifecycle = lifecycle
64
+ end
65
+
48
66
  def parse_release
49
67
  release_name = safe_property(@job_spec, "release", :class => String, :optional => true)
50
68
 
@@ -227,6 +227,10 @@ module Bosh::Director
227
227
  @jobs_name_index[name]
228
228
  end
229
229
 
230
+ def jobs_starting_on_deploy
231
+ @jobs.select(&:starts_on_deploy?)
232
+ end
233
+
230
234
  def rename_in_progress?
231
235
  @job_rename['old_name'] && @job_rename['new_name']
232
236
  end
@@ -1,12 +1,14 @@
1
1
  module Bosh::Director
2
2
  module DeploymentPlan
3
3
  class Preparer
4
- def initialize(job, compiler)
4
+ def initialize(job, assembler)
5
5
  @job = job
6
- @assembler = compiler
6
+ @assembler = assembler
7
7
  end
8
8
 
9
9
  def prepare
10
+ job.begin_stage('Preparing deployment', 9)
11
+
10
12
  job.track_and_log('Binding deployment') do
11
13
  @assembler.bind_deployment
12
14
  end
@@ -14,9 +14,7 @@ module Bosh::Director
14
14
  def update
15
15
  @event_log.begin_stage('Preparing DNS', 1)
16
16
  @base_job.track_and_log('Binding DNS') do
17
- if Config.dns_enabled?
18
- @assembler.bind_dns
19
- end
17
+ @assembler.bind_dns
20
18
  end
21
19
 
22
20
  @logger.info('Updating resource pools')
@@ -38,7 +36,11 @@ module Bosh::Director
38
36
  @assembler.delete_unneeded_instances
39
37
 
40
38
  @logger.info('Updating jobs')
41
- @multi_job_updater.run(@base_job, @deployment_plan, @deployment_plan.jobs)
39
+ @multi_job_updater.run(
40
+ @base_job,
41
+ @deployment_plan,
42
+ @deployment_plan.jobs_starting_on_deploy,
43
+ )
42
44
 
43
45
  @logger.info('Refilling resource pools')
44
46
  @resource_pools.refill
@@ -0,0 +1,7 @@
1
+ module Bosh::Director
2
+ module Errand; end
3
+ end
4
+
5
+ require 'bosh/director/errand/deployment_preparer'
6
+ require 'bosh/director/errand/job_manager'
7
+ require 'bosh/director/errand/runner'
@@ -0,0 +1,29 @@
1
+ module Bosh::Director
2
+ class Errand::DeploymentPreparer
3
+ # @param [Bosh::Director::DeploymentPlan::Planner] deployment
4
+ # @param [Bosh::Director::DeploymentPlan::Job] job
5
+ # @param [Bosh::Director::EventLog::Log] event_log
6
+ # @param [Bosh::Blobstore::Jobs::BaseJob] base_job
7
+ def initialize(deployment, job, event_log, base_job)
8
+ @deployment = deployment
9
+ @job = job
10
+ @event_log = event_log
11
+ @base_job = base_job
12
+ end
13
+
14
+ def prepare_deployment
15
+ assembler = DeploymentPlan::Assembler.new(@deployment)
16
+
17
+ preparer = DeploymentPlan::Preparer.new(@base_job, assembler)
18
+ preparer.prepare
19
+
20
+ compiler = PackageCompiler.new(@deployment)
21
+ compiler.compile
22
+ end
23
+
24
+ def prepare_job
25
+ @job.bind_unallocated_vms
26
+ @job.bind_instance_networks
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ module Bosh::Director
2
+ class Errand::JobManager
3
+ # @param [Bosh::Director::DeploymentPlan::Planner] deployment
4
+ # @param [Bosh::Director::DeploymentPlan::Job] job
5
+ # @param [Bosh::Blobstore::Client] blobstore
6
+ # @param [Bosh::Director::EventLog::Log] event_log
7
+ def initialize(deployment, job, blobstore, event_log)
8
+ @deployment = deployment
9
+ @job = job
10
+ @blobstore = blobstore
11
+ @event_log = event_log
12
+ end
13
+
14
+ # Creates/updates all job instances
15
+ # @return [void]
16
+ def update_instances
17
+ dns_binder = DeploymentPlan::DnsBinder.new(@deployment)
18
+ dns_binder.bind_deployment
19
+
20
+ instance_vm_binder = DeploymentPlan::InstanceVmBinder.new(@event_log)
21
+ instance_vm_binder.bind_instance_vms(@job.instances)
22
+
23
+ job_renderer = JobRenderer.new(@job)
24
+ job_renderer.render_job_instances(@blobstore)
25
+
26
+ job_updater = JobUpdater.new(@deployment, @job)
27
+ job_updater.update
28
+ end
29
+
30
+ # Deletes created all job instances
31
+ # @return [void]
32
+ def delete_instances
33
+ event_log_stage = @event_log.begin_stage(
34
+ 'Deleting instances', @job.instances.size, [@job.name])
35
+
36
+ instance_deleter = InstanceDeleter.new(@deployment)
37
+ instance_deleter.delete_instances(
38
+ @job.instances.map(&:model), event_log_stage)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,79 @@
1
+ require 'membrane'
2
+
3
+ module Bosh::Director
4
+ class Errand::Runner
5
+ class ErrandResult
6
+ attr_reader :exit_code
7
+
8
+ AGENT_TASK_RESULT_SCHEMA = ::Membrane::SchemaParser.parse do
9
+ {
10
+ 'exit_code' => Integer,
11
+ 'stdout' => String,
12
+ 'stderr' => String,
13
+ }
14
+ end
15
+
16
+ # Explicitly write out schema of the director task result
17
+ # to avoid accidently leaking agent task result extra fields.
18
+ def self.from_agent_task_result(agent_task_result)
19
+ AGENT_TASK_RESULT_SCHEMA.validate(agent_task_result)
20
+ new(*agent_task_result.values_at('exit_code', 'stdout', 'stderr'))
21
+ rescue Membrane::SchemaValidationError => e
22
+ raise AgentInvalidTaskResult, e.message
23
+ end
24
+
25
+ def initialize(exit_code, stdout, stderr)
26
+ @exit_code = exit_code
27
+ @stdout = stdout
28
+ @stderr = stderr
29
+ end
30
+
31
+ def to_hash
32
+ {
33
+ 'exit_code' => @exit_code,
34
+ 'stdout' => @stdout,
35
+ 'stderr' => @stderr,
36
+ }
37
+ end
38
+ end
39
+
40
+ # @param [Bosh::Director::DeploymentPlan::Job] job
41
+ # @param [Bosh::Director::TaskResultFile] result_file
42
+ # @param [Bosh::Director::EventLog::Log] event_log
43
+ def initialize(job, result_file, instance_manager, event_log)
44
+ @job = job
45
+ @result_file = result_file
46
+ @instance_manager = instance_manager
47
+ @event_log = event_log
48
+ end
49
+
50
+ # Runs errand on job instances
51
+ # @return [String] short description of the errand result
52
+ def run
53
+ instance = @job.instances.first
54
+ unless instance
55
+ raise DirectorError, 'Must have at least one job instance to run an errand'
56
+ end
57
+
58
+ agent_task_result = nil
59
+
60
+ event_log_stage = @event_log.begin_stage('Running errand', 1)
61
+ event_log_stage.advance_and_track("#{@job.name}/#{instance.index}") do
62
+ agent = @instance_manager.agent_client_for(instance.model)
63
+ agent_task_result = agent.run_errand
64
+ end
65
+
66
+ errand_result = ErrandResult.from_agent_task_result(agent_task_result)
67
+ @result_file.write(JSON.dump(errand_result.to_hash) + "\n")
68
+
69
+ title_prefix = "Errand `#{@job.name}' completed"
70
+ exit_code_suffix = "(exit code #{errand_result.exit_code})"
71
+
72
+ if errand_result.exit_code == 0
73
+ "#{title_prefix} successfully #{exit_code_suffix}"
74
+ else
75
+ "#{title_prefix} with error #{exit_code_suffix}"
76
+ end
77
+ end
78
+ end
79
+ end
@@ -54,6 +54,7 @@ module Bosh::Director
54
54
  TaskNotFound = err(10000, NOT_FOUND)
55
55
  TaskCancelled = err(10001, OK)
56
56
 
57
+ # User management
57
58
  UserNotFound = err(20000, NOT_FOUND)
58
59
  UserImmutableUsername = err(20001)
59
60
  UserInvalid = err(20002)
@@ -83,6 +84,7 @@ module Bosh::Director
83
84
 
84
85
  PackageInvalidArchive = err(60000)
85
86
 
87
+ # Models
86
88
  DeploymentNotFound = err(70000, NOT_FOUND)
87
89
  InstanceNotFound = err(70001, NOT_FOUND)
88
90
  InstanceInvalidIndex = err(70002)
@@ -93,7 +95,9 @@ module Bosh::Director
93
95
  VmInstanceOutOfSync = err(70006)
94
96
  InstanceTargetStateUndefined = err(70007)
95
97
  SnapshotNotFound = err(70008)
98
+ JobNotFound = err(70009, NOT_FOUND)
96
99
 
100
+ # Extracting job from a release
97
101
  JobInvalidArchive = err(80000)
98
102
  JobMissingManifest = err(80001)
99
103
  JobMissingTemplateFile = err(80002)
@@ -110,12 +114,14 @@ module Bosh::Director
110
114
  ResourceError = err(100001)
111
115
  ResourceNotFound = err(100002, NOT_FOUND)
112
116
 
117
+ # Director property management
113
118
  PropertyAlreadyExists = err(110001)
114
119
  PropertyInvalid = err(110002)
115
120
  PropertyNotFound = err(110003, NOT_FOUND)
116
121
 
117
122
  CompilationConfigUnknownNetwork = err(120001)
118
123
 
124
+ # Manifest parsing: network section
119
125
  NetworkReservationInvalidIp = err(130001)
120
126
  NetworkReservationMissing = err(130002)
121
127
  NetworkReservationAlreadyExists = err(130003)
@@ -128,6 +134,7 @@ module Bosh::Director
128
134
  NetworkReservationError = err(130010)
129
135
  NetworkReservationNotEnoughCapacity = err(130010)
130
136
 
137
+ # Manifest parsing: job section
131
138
  JobMissingRelease = err(140001)
132
139
  JobUnknownRelease = err(140002)
133
140
  JobUnknownResourcePool = err(140003)
@@ -136,7 +143,9 @@ module Bosh::Director
136
143
  JobInvalidJobState = err(140006)
137
144
  JobMissingNetwork = err(140007)
138
145
  JobInvalidTemplates = err(140008)
146
+ JobInvalidLifecycle = err(140009)
139
147
 
148
+ # Manifest parsing: job networks section
140
149
  JobUnknownNetwork = err(150001)
141
150
  JobNetworkInstanceIpMismatch = err(150002)
142
151
  JobNetworkInvalidDefault = err(150003)
@@ -170,6 +179,7 @@ module Bosh::Director
170
179
  CloudDiskMissing = err(390002)
171
180
  CloudNotEnoughDiskSpace = err(390003)
172
181
 
182
+ # Agent errors
173
183
  AgentTaskNoBlobstoreId = err(400001)
174
184
  AgentInvalidStateFormat = err(400002)
175
185
  AgentWrongDeployment = err(400003)
@@ -182,6 +192,7 @@ module Bosh::Director
182
192
  AgentDiskOutOfSync = err(400010)
183
193
  AgentInvalidTaskResult = err(400011)
184
194
 
195
+ # Cloud check task errors
185
196
  CloudcheckTooManySimilarProblems = err(410001)
186
197
  CloudcheckResolutionNotProvided = err(410002)
187
198
  CloudcheckInvalidResolutionFormat = err(410003)
@@ -198,4 +209,7 @@ module Bosh::Director
198
209
 
199
210
  SystemError = err(500000, INTERNAL_SERVER_ERROR)
200
211
  NotEnoughDiskSpace = err(500001, INTERNAL_SERVER_ERROR)
212
+
213
+ # Run errand errors
214
+ RunErrandError = err(510000)
201
215
  end
@@ -1,73 +1,95 @@
1
- require 'membrane'
1
+ require 'psych'
2
2
 
3
3
  module Bosh::Director
4
- module Jobs
5
- class RunErrand < BaseJob
6
- @queue = :normal
7
-
8
- class ErrandResult
9
- attr_reader :exit_code
10
-
11
- AGENT_TASK_RESULT_SCHEMA = ::Membrane::SchemaParser.parse do
12
- {
13
- 'exit_code' => Integer,
14
- 'stdout' => String,
15
- 'stderr' => String,
16
- }
17
- end
18
-
19
- # Explicitly write out schema of the director task result
20
- # to avoid accidently leaking agent task result extra fields.
21
- def self.from_agent_task_result(agent_task_result)
22
- AGENT_TASK_RESULT_SCHEMA.validate(agent_task_result)
23
- new(*agent_task_result.values_at('exit_code', 'stdout', 'stderr'))
24
- rescue Membrane::SchemaValidationError => e
25
- raise AgentInvalidTaskResult, e.message
26
- end
27
-
28
- def initialize(exit_code, stdout, stderr)
29
- @exit_code = exit_code
30
- @stdout = stdout
31
- @stderr = stderr
32
- end
33
-
34
- def to_hash
35
- {
36
- 'exit_code' => @exit_code,
37
- 'stdout' => @stdout,
38
- 'stderr' => @stderr,
39
- }
40
- end
4
+ class Jobs::RunErrand < Jobs::BaseJob
5
+ @queue = :normal
6
+
7
+ def self.job_type
8
+ :run_errand
9
+ end
10
+
11
+ def initialize(deployment_name, errand_name)
12
+ @deployment_name = deployment_name
13
+ @errand_name = errand_name
14
+ @deployment_manager = Api::DeploymentManager.new
15
+ @instance_manager = Api::InstanceManager.new
16
+ @blobstore = App.instance.blobstores.blobstore
17
+ end
18
+
19
+ def perform
20
+ deployment_model = @deployment_manager.find_by_name(@deployment_name)
21
+
22
+ manifest = Psych.load(deployment_model.manifest)
23
+ deployment = DeploymentPlan::Planner.parse(manifest, event_log, {})
24
+
25
+ job = deployment.job(@errand_name)
26
+ if job.nil?
27
+ raise JobNotFound, "Errand `#{@errand_name}' doesn't exist"
28
+ end
29
+
30
+ unless job.can_run_as_errand?
31
+ raise RunErrandError,
32
+ "Job `#{job.name}' is not an errand. To mark a job as an errand " +
33
+ "set its lifecycle to 'errand' in the deployment manifest."
41
34
  end
42
35
 
43
- def self.job_type
44
- :run_errand
36
+ if job.instances.empty?
37
+ raise InstanceNotFound, "Instance `#{@deployment_name}/#{@errand_name}/0' doesn't exist"
45
38
  end
46
39
 
47
- def initialize(deployment_name, errand_name)
48
- @deployment_name = deployment_name
49
- @errand_name = errand_name
50
- @instance_manager = Api::InstanceManager.new
40
+ runner = Errand::Runner.new(job, result_file, @instance_manager, event_log)
41
+
42
+ with_updated_instances(deployment, job) do
43
+ logger.info('Starting to run errand')
44
+ runner.run
51
45
  end
46
+ end
52
47
 
53
- def perform
54
- instance = @instance_manager.find_by_name(@deployment_name, @errand_name, 0)
48
+ private
55
49
 
56
- agent = @instance_manager.agent_client_for(instance)
57
- agent_task_result = agent.run_errand
50
+ def with_updated_instances(deployment, job, &blk)
51
+ logger.info('Starting to prepare for deployment')
52
+ prepare_deployment(deployment, job)
58
53
 
59
- errand_result = ErrandResult.from_agent_task_result(agent_task_result)
60
- result_file.write(JSON.dump(errand_result.to_hash) + "\n")
54
+ logger.info('Starting to update resource pool')
55
+ rp_manager = update_resource_pool(job)
61
56
 
62
- title_prefix = "Errand `#{@errand_name}' completed"
63
- exit_code_suffix = "(exit code #{errand_result.exit_code})"
57
+ logger.info('Starting to update job instances')
58
+ job_manager = update_instances(deployment, job)
64
59
 
65
- if errand_result.exit_code == 0
66
- "#{title_prefix} successfully #{exit_code_suffix}"
67
- else
68
- "#{title_prefix} with error #{exit_code_suffix}"
69
- end
70
- end
60
+ result = blk.call
61
+
62
+ logger.info('Starting to delete job instances')
63
+ job_manager.delete_instances
64
+
65
+ logger.info('Starting to refill resource pool')
66
+ rp_manager.refill
67
+
68
+ result
69
+ end
70
+
71
+ def prepare_deployment(deployment, job)
72
+ deployment_preparer = Errand::DeploymentPreparer.new(
73
+ deployment, job, event_log, self)
74
+
75
+ deployment_preparer.prepare_deployment
76
+ deployment_preparer.prepare_job
77
+ end
78
+
79
+ def update_resource_pool(job)
80
+ rp_updaters = [ResourcePoolUpdater.new(job.resource_pool)]
81
+ rp_manager = DeploymentPlan::ResourcePools.new(event_log, rp_updaters)
82
+
83
+ rp_manager.update
84
+ rp_manager
85
+ end
86
+
87
+ def update_instances(deployment, job)
88
+ job_manager = Errand::JobManager.new(
89
+ deployment, job, @blobstore, event_log)
90
+
91
+ job_manager.update_instances
92
+ job_manager
71
93
  end
72
94
  end
73
95
  end
@@ -39,8 +39,6 @@ module Bosh::Director
39
39
  def prepare
40
40
  @assembler = DeploymentPlan::Assembler.new(@deployment_plan)
41
41
  preparer = DeploymentPlan::Preparer.new(self, @assembler)
42
-
43
- begin_stage('Preparing deployment', 9)
44
42
  preparer.prepare
45
43
 
46
44
  logger.info('Compiling and binding packages')
@@ -1,10 +1,7 @@
1
- # Copyright (c) 2009-2012 VMware, Inc.
2
-
3
1
  module Bosh::Director
4
-
5
2
  class TaskResultFile
6
3
  def initialize(file_name)
7
- @file = File.open(file_name, "w")
4
+ @file = File.open(file_name, 'w')
8
5
  @lock = Mutex.new
9
6
  end
10
7
 
@@ -16,4 +13,3 @@ module Bosh::Director
16
13
  end
17
14
  end
18
15
  end
19
-
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Director
3
- VERSION = '1.2168.0'
3
+ VERSION = '1.2175.0'
4
4
  end
5
5
  end
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.2168.0
4
+ version: 1.2175.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-03-11 00:00:00.000000000 Z
12
+ date: 2014-03-12 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.2168.0
37
+ version: 1.2175.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.2168.0
45
+ version: 1.2175.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.2168.0
53
+ version: 1.2175.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.2168.0
61
+ version: 1.2175.0
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: bosh-director-core
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.2168.0
69
+ version: 1.2175.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.2168.0
77
+ version: 1.2175.0
78
78
  - !ruby/object:Gem::Dependency
79
79
  name: bosh_common
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.2168.0
85
+ version: 1.2175.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.2168.0
93
+ version: 1.2175.0
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: bosh_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.2168.0
101
+ version: 1.2175.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.2168.0
109
+ version: 1.2175.0
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: bosh_openstack_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.2168.0
117
+ version: 1.2175.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.2168.0
125
+ version: 1.2175.0
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: bosh_aws_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.2168.0
133
+ version: 1.2175.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.2168.0
141
+ version: 1.2175.0
142
142
  - !ruby/object:Gem::Dependency
143
143
  name: bosh_vsphere_cpi
144
144
  requirement: !ruby/object:Gem::Requirement
@@ -146,7 +146,7 @@ dependencies:
146
146
  requirements:
147
147
  - - ~>
148
148
  - !ruby/object:Gem::Version
149
- version: 1.2168.0
149
+ version: 1.2175.0
150
150
  type: :runtime
151
151
  prerelease: false
152
152
  version_requirements: !ruby/object:Gem::Requirement
@@ -154,7 +154,7 @@ dependencies:
154
154
  requirements:
155
155
  - - ~>
156
156
  - !ruby/object:Gem::Version
157
- version: 1.2168.0
157
+ version: 1.2175.0
158
158
  - !ruby/object:Gem::Dependency
159
159
  name: bosh_vcloud_cpi
160
160
  requirement: !ruby/object:Gem::Requirement
@@ -493,7 +493,7 @@ dependencies:
493
493
  version: '1.0'
494
494
  description: ! 'BOSH Director
495
495
 
496
- 542f8e'
496
+ 83a657'
497
497
  email: support@cloudfoundry.com
498
498
  executables:
499
499
  - bosh-director
@@ -602,9 +602,11 @@ files:
602
602
  - lib/bosh/director/deployment_plan/compilation_config.rb
603
603
  - lib/bosh/director/deployment_plan/compiled_package.rb
604
604
  - lib/bosh/director/deployment_plan/deployment_spec_parser.rb
605
+ - lib/bosh/director/deployment_plan/dns_binder.rb
605
606
  - lib/bosh/director/deployment_plan/dynamic_network.rb
606
607
  - lib/bosh/director/deployment_plan/idle_vm.rb
607
608
  - lib/bosh/director/deployment_plan/instance.rb
609
+ - lib/bosh/director/deployment_plan/instance_vm_binder.rb
608
610
  - lib/bosh/director/deployment_plan/job.rb
609
611
  - lib/bosh/director/deployment_plan/job_spec_parser.rb
610
612
  - lib/bosh/director/deployment_plan/manual_network.rb
@@ -625,6 +627,10 @@ files:
625
627
  - lib/bosh/director/download_helper.rb
626
628
  - lib/bosh/director/duration.rb
627
629
  - lib/bosh/director/encryption_helper.rb
630
+ - lib/bosh/director/errand.rb
631
+ - lib/bosh/director/errand/deployment_preparer.rb
632
+ - lib/bosh/director/errand/job_manager.rb
633
+ - lib/bosh/director/errand/runner.rb
628
634
  - lib/bosh/director/errors.rb
629
635
  - lib/bosh/director/event_log.rb
630
636
  - lib/bosh/director/ext.rb
@@ -741,7 +747,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
741
747
  version: '0'
742
748
  segments:
743
749
  - 0
744
- hash: -4485791993473408680
750
+ hash: -1777045952966921306
745
751
  requirements: []
746
752
  rubyforge_project:
747
753
  rubygems_version: 1.8.23