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 +1 -0
- data/lib/bosh/director/deployment_plan.rb +3 -0
- data/lib/bosh/director/deployment_plan/assembler.rb +9 -88
- data/lib/bosh/director/deployment_plan/dns_binder.rb +45 -0
- data/lib/bosh/director/deployment_plan/instance.rb +12 -1
- data/lib/bosh/director/deployment_plan/instance_vm_binder.rb +58 -0
- data/lib/bosh/director/deployment_plan/job.rb +35 -0
- data/lib/bosh/director/deployment_plan/job_spec_parser.rb +18 -0
- data/lib/bosh/director/deployment_plan/planner.rb +4 -0
- data/lib/bosh/director/deployment_plan/preparer.rb +4 -2
- data/lib/bosh/director/deployment_plan/updater.rb +6 -4
- data/lib/bosh/director/errand.rb +7 -0
- data/lib/bosh/director/errand/deployment_preparer.rb +29 -0
- data/lib/bosh/director/errand/job_manager.rb +41 -0
- data/lib/bosh/director/errand/runner.rb +79 -0
- data/lib/bosh/director/errors.rb +14 -0
- data/lib/bosh/director/jobs/run_errand.rb +80 -58
- data/lib/bosh/director/jobs/update_deployment.rb +0 -2
- data/lib/bosh/director/task_result_file.rb +1 -5
- data/lib/bosh/director/version.rb +1 -1
- metadata +26 -20
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.
|
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.
|
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.
|
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
|
-
|
338
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module Bosh::Director
|
2
2
|
module DeploymentPlan
|
3
3
|
class Preparer
|
4
|
-
def initialize(job,
|
4
|
+
def initialize(job, assembler)
|
5
5
|
@job = job
|
6
|
-
@assembler =
|
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
|
-
|
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(
|
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,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
|
data/lib/bosh/director/errors.rb
CHANGED
@@ -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 '
|
1
|
+
require 'psych'
|
2
2
|
|
3
3
|
module Bosh::Director
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
44
|
-
|
36
|
+
if job.instances.empty?
|
37
|
+
raise InstanceNotFound, "Instance `#{@deployment_name}/#{@errand_name}/0' doesn't exist"
|
45
38
|
end
|
46
39
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
54
|
-
instance = @instance_manager.find_by_name(@deployment_name, @errand_name, 0)
|
48
|
+
private
|
55
49
|
|
56
|
-
|
57
|
-
|
50
|
+
def with_updated_instances(deployment, job, &blk)
|
51
|
+
logger.info('Starting to prepare for deployment')
|
52
|
+
prepare_deployment(deployment, job)
|
58
53
|
|
59
|
-
|
60
|
-
|
54
|
+
logger.info('Starting to update resource pool')
|
55
|
+
rp_manager = update_resource_pool(job)
|
61
56
|
|
62
|
-
|
63
|
-
|
57
|
+
logger.info('Starting to update job instances')
|
58
|
+
job_manager = update_instances(deployment, job)
|
64
59
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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,
|
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
|
-
|
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.
|
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-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
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: -
|
750
|
+
hash: -1777045952966921306
|
745
751
|
requirements: []
|
746
752
|
rubyforge_project:
|
747
753
|
rubygems_version: 1.8.23
|