bosh-director 1.2168.0 → 1.2175.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.
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