bosh-director 1.5.0.pre.1113
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +34 -0
- data/bin/bosh-director +36 -0
- data/bin/bosh-director-console +84 -0
- data/bin/bosh-director-drain-workers +42 -0
- data/bin/bosh-director-migrate +58 -0
- data/bin/bosh-director-scheduler +27 -0
- data/bin/bosh-director-worker +76 -0
- data/db/migrations/README +1 -0
- data/db/migrations/director/20110209010747_initial.rb +118 -0
- data/db/migrations/director/20110406055800_add_task_user.rb +9 -0
- data/db/migrations/director/20110518225809_remove_cid_constrain.rb +13 -0
- data/db/migrations/director/20110617211923_add_deployments_release_versions.rb +32 -0
- data/db/migrations/director/20110622212607_add_task_checkpoint_timestamp.rb +9 -0
- data/db/migrations/director/20110628023039_add_state_to_instances.rb +21 -0
- data/db/migrations/director/20110709012332_add_disk_size_to_instances.rb +9 -0
- data/db/migrations/director/20110906183441_add_log_bundles.rb +11 -0
- data/db/migrations/director/20110907194830_add_logs_json_to_templates.rb +9 -0
- data/db/migrations/director/20110915205610_add_persistent_disks.rb +51 -0
- data/db/migrations/director/20111005180929_add_properties.rb +14 -0
- data/db/migrations/director/20111110024617_add_deployment_problems.rb +24 -0
- data/db/migrations/director/20111216214145_recreate_support_for_vms.rb +9 -0
- data/db/migrations/director/20120102084027_add_credentials_to_vms.rb +7 -0
- data/db/migrations/director/20120427235217_allow_multiple_releases_per_deployment.rb +36 -0
- data/db/migrations/director/20120524175805_add_task_type.rb +44 -0
- data/db/migrations/director/20120614001930_delete_redundant_deployment_release_relation.rb +34 -0
- data/db/migrations/director/20120822004528_add_fingerprint_to_templates_and_packages.rb +17 -0
- data/db/migrations/director/20120830191244_add_properties_to_templates.rb +9 -0
- data/db/migrations/director/20121106190739_persist_vm_env.rb +9 -0
- data/db/migrations/director/20130222232131_add_sha1_to_stemcells.rb +9 -0
- data/db/migrations/director/20130312211407_add_commit_hash_to_release_versions.rb +19 -0
- data/db/migrations/director/20130409235338_snapshot.rb +15 -0
- data/db/migrations/director/20130530164918_add_paused_flag_to_instance.rb +14 -0
- data/db/migrations/director/20130531172604_add_director_attributes.rb +13 -0
- data/db/migrations/dns/20120123234908_initial.rb +27 -0
- data/lib/bosh/director.rb +133 -0
- data/lib/bosh/director/agent_client.rb +78 -0
- data/lib/bosh/director/api.rb +29 -0
- data/lib/bosh/director/api/api_helper.rb +81 -0
- data/lib/bosh/director/api/backup_manager.rb +15 -0
- data/lib/bosh/director/api/controller.rb +639 -0
- data/lib/bosh/director/api/controller_helpers.rb +34 -0
- data/lib/bosh/director/api/deployment_lookup.rb +13 -0
- data/lib/bosh/director/api/deployment_manager.rb +60 -0
- data/lib/bosh/director/api/http_constants.rb +16 -0
- data/lib/bosh/director/api/instance_lookup.rb +44 -0
- data/lib/bosh/director/api/instance_manager.rb +63 -0
- data/lib/bosh/director/api/problem_manager.rb +40 -0
- data/lib/bosh/director/api/property_manager.rb +69 -0
- data/lib/bosh/director/api/release_manager.rb +59 -0
- data/lib/bosh/director/api/resource_manager.rb +69 -0
- data/lib/bosh/director/api/resurrector_manager.rb +15 -0
- data/lib/bosh/director/api/snapshot_manager.rb +94 -0
- data/lib/bosh/director/api/stemcell_manager.rb +50 -0
- data/lib/bosh/director/api/task_helper.rb +46 -0
- data/lib/bosh/director/api/task_manager.rb +64 -0
- data/lib/bosh/director/api/user_manager.rb +72 -0
- data/lib/bosh/director/api/vm_state_manager.rb +11 -0
- data/lib/bosh/director/app.rb +35 -0
- data/lib/bosh/director/blob_util.rb +87 -0
- data/lib/bosh/director/blobstores.rb +29 -0
- data/lib/bosh/director/client.rb +156 -0
- data/lib/bosh/director/cloudcheck_helper.rb +204 -0
- data/lib/bosh/director/compile_task.rb +157 -0
- data/lib/bosh/director/config.rb +370 -0
- data/lib/bosh/director/configuration_hasher.rb +114 -0
- data/lib/bosh/director/cycle_helper.rb +36 -0
- data/lib/bosh/director/db_backup.rb +22 -0
- data/lib/bosh/director/db_backup/adapter.rb +3 -0
- data/lib/bosh/director/db_backup/adapter/mysql2.rb +27 -0
- data/lib/bosh/director/db_backup/adapter/postgres.rb +36 -0
- data/lib/bosh/director/db_backup/adapter/sqlite.rb +17 -0
- data/lib/bosh/director/db_backup/error.rb +10 -0
- data/lib/bosh/director/deployment_plan.rb +26 -0
- data/lib/bosh/director/deployment_plan/assembler.rb +430 -0
- data/lib/bosh/director/deployment_plan/compilation_config.rb +54 -0
- data/lib/bosh/director/deployment_plan/compiled_package.rb +35 -0
- data/lib/bosh/director/deployment_plan/dynamic_network.rb +91 -0
- data/lib/bosh/director/deployment_plan/idle_vm.rb +109 -0
- data/lib/bosh/director/deployment_plan/instance.rb +413 -0
- data/lib/bosh/director/deployment_plan/job.rb +470 -0
- data/lib/bosh/director/deployment_plan/manual_network.rb +137 -0
- data/lib/bosh/director/deployment_plan/network.rb +74 -0
- data/lib/bosh/director/deployment_plan/network_subnet.rb +167 -0
- data/lib/bosh/director/deployment_plan/planner.rb +288 -0
- data/lib/bosh/director/deployment_plan/preparer.rb +52 -0
- data/lib/bosh/director/deployment_plan/release.rb +126 -0
- data/lib/bosh/director/deployment_plan/resource_pool.rb +143 -0
- data/lib/bosh/director/deployment_plan/resource_pools.rb +68 -0
- data/lib/bosh/director/deployment_plan/stemcell.rb +56 -0
- data/lib/bosh/director/deployment_plan/template.rb +94 -0
- data/lib/bosh/director/deployment_plan/update_config.rb +80 -0
- data/lib/bosh/director/deployment_plan/updater.rb +55 -0
- data/lib/bosh/director/deployment_plan/vip_network.rb +79 -0
- data/lib/bosh/director/dns_helper.rb +204 -0
- data/lib/bosh/director/download_helper.rb +44 -0
- data/lib/bosh/director/duration.rb +36 -0
- data/lib/bosh/director/encryption_helper.rb +10 -0
- data/lib/bosh/director/errors.rb +198 -0
- data/lib/bosh/director/event_log.rb +136 -0
- data/lib/bosh/director/ext.rb +64 -0
- data/lib/bosh/director/hash_string_vals.rb +13 -0
- data/lib/bosh/director/instance_deleter.rb +109 -0
- data/lib/bosh/director/instance_updater.rb +506 -0
- data/lib/bosh/director/ip_util.rb +67 -0
- data/lib/bosh/director/job_queue.rb +16 -0
- data/lib/bosh/director/job_runner.rb +162 -0
- data/lib/bosh/director/job_updater.rb +121 -0
- data/lib/bosh/director/jobs/backup.rb +86 -0
- data/lib/bosh/director/jobs/base_job.rb +66 -0
- data/lib/bosh/director/jobs/cloud_check/apply_resolutions.rb +46 -0
- data/lib/bosh/director/jobs/cloud_check/scan.rb +38 -0
- data/lib/bosh/director/jobs/cloud_check/scan_and_fix.rb +73 -0
- data/lib/bosh/director/jobs/create_snapshot.rb +23 -0
- data/lib/bosh/director/jobs/delete_deployment.rb +183 -0
- data/lib/bosh/director/jobs/delete_deployment_snapshots.rb +34 -0
- data/lib/bosh/director/jobs/delete_release.rb +219 -0
- data/lib/bosh/director/jobs/delete_snapshots.rb +23 -0
- data/lib/bosh/director/jobs/delete_stemcell.rb +102 -0
- data/lib/bosh/director/jobs/fetch_logs.rb +99 -0
- data/lib/bosh/director/jobs/scheduled_backup.rb +38 -0
- data/lib/bosh/director/jobs/snapshot_deployment.rb +61 -0
- data/lib/bosh/director/jobs/snapshot_deployments.rb +23 -0
- data/lib/bosh/director/jobs/snapshot_self.rb +43 -0
- data/lib/bosh/director/jobs/ssh.rb +59 -0
- data/lib/bosh/director/jobs/update_deployment.rb +110 -0
- data/lib/bosh/director/jobs/update_release.rb +672 -0
- data/lib/bosh/director/jobs/update_stemcell.rb +109 -0
- data/lib/bosh/director/jobs/vm_state.rb +89 -0
- data/lib/bosh/director/lock.rb +133 -0
- data/lib/bosh/director/lock_helper.rb +92 -0
- data/lib/bosh/director/models.rb +29 -0
- data/lib/bosh/director/models/compiled_package.rb +33 -0
- data/lib/bosh/director/models/deployment.rb +22 -0
- data/lib/bosh/director/models/deployment_problem.rb +49 -0
- data/lib/bosh/director/models/deployment_property.rb +21 -0
- data/lib/bosh/director/models/director_attribute.rb +9 -0
- data/lib/bosh/director/models/dns.rb +9 -0
- data/lib/bosh/director/models/dns/domain.rb +9 -0
- data/lib/bosh/director/models/dns/record.rb +7 -0
- data/lib/bosh/director/models/helpers/model_helper.rb +7 -0
- data/lib/bosh/director/models/instance.rb +28 -0
- data/lib/bosh/director/models/log_bundle.rb +10 -0
- data/lib/bosh/director/models/package.rb +30 -0
- data/lib/bosh/director/models/persistent_disk.rb +13 -0
- data/lib/bosh/director/models/release.rb +17 -0
- data/lib/bosh/director/models/release_version.rb +16 -0
- data/lib/bosh/director/models/snapshot.rb +13 -0
- data/lib/bosh/director/models/stemcell.rb +18 -0
- data/lib/bosh/director/models/task.rb +10 -0
- data/lib/bosh/director/models/template.rb +44 -0
- data/lib/bosh/director/models/user.rb +11 -0
- data/lib/bosh/director/models/vm.rb +42 -0
- data/lib/bosh/director/nats_rpc.rb +54 -0
- data/lib/bosh/director/network_reservation.rb +121 -0
- data/lib/bosh/director/next_rebase_version.rb +20 -0
- data/lib/bosh/director/package_compiler.rb +423 -0
- data/lib/bosh/director/problem_handlers/base.rb +153 -0
- data/lib/bosh/director/problem_handlers/inactive_disk.rb +112 -0
- data/lib/bosh/director/problem_handlers/invalid_problem.rb +28 -0
- data/lib/bosh/director/problem_handlers/missing_vm.rb +34 -0
- data/lib/bosh/director/problem_handlers/mount_info_mismatch.rb +62 -0
- data/lib/bosh/director/problem_handlers/out_of_sync_vm.rb +64 -0
- data/lib/bosh/director/problem_handlers/unbound_instance_vm.rb +85 -0
- data/lib/bosh/director/problem_handlers/unresponsive_agent.rb +78 -0
- data/lib/bosh/director/problem_resolver.rb +103 -0
- data/lib/bosh/director/problem_scanner.rb +268 -0
- data/lib/bosh/director/resource_pool_updater.rb +216 -0
- data/lib/bosh/director/scheduler.rb +57 -0
- data/lib/bosh/director/sequel.rb +13 -0
- data/lib/bosh/director/tar_gzipper.rb +47 -0
- data/lib/bosh/director/task_result_file.rb +19 -0
- data/lib/bosh/director/thread_pool.rb +8 -0
- data/lib/bosh/director/validation_helper.rb +55 -0
- data/lib/bosh/director/version.rb +7 -0
- data/lib/bosh/director/vm_creator.rb +80 -0
- data/lib/bosh/director/vm_data.rb +63 -0
- data/lib/bosh/director/vm_metadata_updater.rb +29 -0
- data/lib/bosh/director/vm_reuser.rb +63 -0
- data/lib/cloud/dummy.rb +149 -0
- metadata +664 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
module Bosh::Director
|
2
|
+
module Jobs
|
3
|
+
class SnapshotDeployment < BaseJob
|
4
|
+
@queue = :normal
|
5
|
+
|
6
|
+
attr_reader :deployment
|
7
|
+
|
8
|
+
def self.job_type
|
9
|
+
:snapshot_deployment
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(deployment_name, options = {})
|
13
|
+
@deployment = deployment_manager.find_by_name(deployment_name)
|
14
|
+
@options = options
|
15
|
+
@errors = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def deployment_manager
|
19
|
+
@deployment_manager ||= Bosh::Director::Api::DeploymentManager.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def perform
|
23
|
+
logger.info("taking snapshot of: #{deployment.name}")
|
24
|
+
deployment.job_instances.each do |instance|
|
25
|
+
snapshot(instance)
|
26
|
+
end
|
27
|
+
|
28
|
+
msg = "snapshots of deployment '#{deployment.name}' created"
|
29
|
+
msg += ", with #{@errors} failure(s)" unless @errors == 0
|
30
|
+
msg
|
31
|
+
end
|
32
|
+
|
33
|
+
def snapshot(instance)
|
34
|
+
logger.info("taking snapshot of: #{instance.job}/#{instance.index} (#{instance.vm.cid})")
|
35
|
+
Bosh::Director::Api::SnapshotManager.take_snapshot(instance, @options)
|
36
|
+
rescue Bosh::Clouds::CloudError => e
|
37
|
+
@errors += 1
|
38
|
+
logger.error("failed to take snapshot of: #{instance.job}/#{instance.index} (#{instance.vm.cid}) - #{e.inspect}")
|
39
|
+
send_alert(instance, e.inspect)
|
40
|
+
end
|
41
|
+
|
42
|
+
ERROR = 3
|
43
|
+
|
44
|
+
def send_alert(instance, message)
|
45
|
+
nats = Bosh::Director::Config.nats
|
46
|
+
payload = Yajl::Encoder.encode(
|
47
|
+
{
|
48
|
+
"id" => 'director',
|
49
|
+
"severity" => ERROR,
|
50
|
+
"title" => "director - snapshot failure",
|
51
|
+
"summary" => "failed to snapshot #{instance.job}/#{instance.index}: #{message}",
|
52
|
+
"created_at" => Time.now.to_i
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
nats.publish('hm.director.alert', payload)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Bosh::Director
|
2
|
+
module Jobs
|
3
|
+
class SnapshotDeployments < BaseJob
|
4
|
+
@queue = :normal
|
5
|
+
|
6
|
+
def self.job_type
|
7
|
+
:snapshot_deployments
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(options={})
|
11
|
+
@snapshot_manager = options.fetch(:snapshot_manager) { Bosh::Director::Api::SnapshotManager.new }
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform
|
15
|
+
tasks = Models::Deployment.all.map do |deployment|
|
16
|
+
@snapshot_manager.create_deployment_snapshot_task('scheduler', deployment)
|
17
|
+
end
|
18
|
+
|
19
|
+
"Enqueued snapshot tasks [#{tasks.map(&:id).join(', ')}]"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Bosh::Director
|
2
|
+
module Jobs
|
3
|
+
class SnapshotSelf < BaseJob
|
4
|
+
@queue = :normal
|
5
|
+
|
6
|
+
def self.job_type
|
7
|
+
:snapshot_self
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(options={})
|
11
|
+
@cloud = options.fetch(:cloud) { Config.cloud }
|
12
|
+
@director_uuid = options.fetch(:director_uuid) { Config.uuid }
|
13
|
+
@director_name = options.fetch(:director_name) { Config.name }
|
14
|
+
@enable_snapshots = options.fetch(:enable_snapshots) { Config.enable_snapshots }
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform
|
18
|
+
unless @enable_snapshots
|
19
|
+
logger.info('Snapshots are disabled; skipping')
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
vm_id = @cloud.current_vm_id
|
24
|
+
disks = @cloud.get_disks(vm_id)
|
25
|
+
metadata = {
|
26
|
+
deployment: 'self',
|
27
|
+
job: 'director',
|
28
|
+
index: 0,
|
29
|
+
director_name: @director_name,
|
30
|
+
director_uuid: @director_uuid,
|
31
|
+
agent_id: 'self',
|
32
|
+
instance_id: vm_id
|
33
|
+
}
|
34
|
+
|
35
|
+
disks.each { |disk| @cloud.snapshot_disk(disk, metadata) }
|
36
|
+
|
37
|
+
"Snapshot director disks [#{disks.join(', ')}]"
|
38
|
+
rescue Bosh::Clouds::NotImplemented
|
39
|
+
logger.info('CPI does not support disk snapshots; skipping')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Director
|
4
|
+
module Jobs
|
5
|
+
class Ssh < BaseJob
|
6
|
+
DEFAULT_SSH_DATA_LIFETIME = 300
|
7
|
+
SSH_TAG = "ssh"
|
8
|
+
@queue = :normal
|
9
|
+
|
10
|
+
def self.job_type
|
11
|
+
:ssh
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(deployment_id, options = {})
|
15
|
+
@deployment_id = deployment_id
|
16
|
+
@target = options["target"]
|
17
|
+
@command = options["command"]
|
18
|
+
@params = options["params"]
|
19
|
+
@blobstore = options.fetch(:blobstore) { App.instance.blobstores.blobstore }
|
20
|
+
@instance_manager = Api::InstanceManager.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform
|
24
|
+
job = @target["job"]
|
25
|
+
indexes = @target["indexes"]
|
26
|
+
|
27
|
+
filter = {
|
28
|
+
:deployment_id => @deployment_id
|
29
|
+
}
|
30
|
+
|
31
|
+
if indexes && indexes.size > 0
|
32
|
+
filter[:index] = indexes
|
33
|
+
end
|
34
|
+
|
35
|
+
if job
|
36
|
+
filter[:job] = job
|
37
|
+
end
|
38
|
+
|
39
|
+
instances = @instance_manager.filter_by(filter)
|
40
|
+
|
41
|
+
ssh_info = instances.map do |instance|
|
42
|
+
agent = @instance_manager.agent_client_for(instance)
|
43
|
+
|
44
|
+
logger.info("ssh #{@command} `#{instance.job}/#{instance.index}'")
|
45
|
+
result = agent.ssh(@command, @params)
|
46
|
+
result["index"] = instance.index
|
47
|
+
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
result_file.write(Yajl::Encoder.encode(ssh_info))
|
52
|
+
result_file.write("\n")
|
53
|
+
|
54
|
+
# task result
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Director
|
4
|
+
module Jobs
|
5
|
+
class UpdateDeployment < BaseJob
|
6
|
+
include LockHelper
|
7
|
+
|
8
|
+
@queue = :normal
|
9
|
+
|
10
|
+
def self.job_type
|
11
|
+
:update_deployment
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [String] manifest_file Path to deployment manifest
|
15
|
+
# @param [Hash] options Deployment options
|
16
|
+
def initialize(manifest_file, options = {})
|
17
|
+
logger.info('Reading deployment manifest')
|
18
|
+
@manifest_file = manifest_file
|
19
|
+
@manifest = File.read(@manifest_file)
|
20
|
+
logger.debug("Manifest:\n#{@manifest}")
|
21
|
+
|
22
|
+
logger.info('Creating deployment plan')
|
23
|
+
logger.info("Deployment plan options: #{options.pretty_inspect}")
|
24
|
+
|
25
|
+
plan_options = {
|
26
|
+
'recreate' => !!options['recreate'],
|
27
|
+
'job_states' => options['job_states'] || {},
|
28
|
+
'job_rename' => options['job_rename'] || {}
|
29
|
+
}
|
30
|
+
|
31
|
+
manifest_as_hash = Psych.load(@manifest)
|
32
|
+
@deployment_plan = DeploymentPlan::Planner.new(manifest_as_hash, plan_options)
|
33
|
+
@deployment_plan.parse
|
34
|
+
logger.info('Created deployment plan')
|
35
|
+
|
36
|
+
resource_pools = @deployment_plan.resource_pools
|
37
|
+
@resource_pool_updaters = resource_pools.map do |resource_pool|
|
38
|
+
ResourcePoolUpdater.new(resource_pool)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def prepare
|
43
|
+
@assembler = DeploymentPlan::Assembler.new(@deployment_plan)
|
44
|
+
preparer = DeploymentPlan::Preparer.new(self, @assembler)
|
45
|
+
|
46
|
+
begin_stage('Preparing deployment', 9)
|
47
|
+
preparer.prepare
|
48
|
+
|
49
|
+
logger.info('Compiling and binding packages')
|
50
|
+
PackageCompiler.new(@deployment_plan).compile
|
51
|
+
end
|
52
|
+
|
53
|
+
def update
|
54
|
+
resource_pools = DeploymentPlan::ResourcePools.new(event_log, @resource_pool_updaters)
|
55
|
+
|
56
|
+
updater = DeploymentPlan::Updater.new(self, event_log, resource_pools, @assembler, @deployment_plan)
|
57
|
+
updater.update
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_stemcell_references
|
61
|
+
current_stemcells = Set.new
|
62
|
+
@deployment_plan.resource_pools.each do |resource_pool|
|
63
|
+
current_stemcells << resource_pool.stemcell.model
|
64
|
+
end
|
65
|
+
|
66
|
+
deployment = @deployment_plan.model
|
67
|
+
stemcells = deployment.stemcells
|
68
|
+
stemcells.each do |stemcell|
|
69
|
+
unless current_stemcells.include?(stemcell)
|
70
|
+
stemcell.remove_deployment(deployment)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def perform
|
76
|
+
with_deployment_lock(@deployment_plan) do
|
77
|
+
logger.info('Preparing deployment')
|
78
|
+
prepare
|
79
|
+
begin
|
80
|
+
deployment = @deployment_plan.model
|
81
|
+
logger.info('Finished preparing deployment')
|
82
|
+
logger.info('Updating deployment')
|
83
|
+
update
|
84
|
+
|
85
|
+
with_release_locks(@deployment_plan) do
|
86
|
+
deployment.db.transaction do
|
87
|
+
deployment.remove_all_release_versions
|
88
|
+
# Now we know that deployment has succeeded and can remove
|
89
|
+
# previous partial deployments release version references
|
90
|
+
# to be able to delete these release versions later.
|
91
|
+
@deployment_plan.releases.each do |release|
|
92
|
+
deployment.add_release_version(release.model)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
deployment.manifest = @manifest
|
98
|
+
deployment.save
|
99
|
+
logger.info('Finished updating deployment')
|
100
|
+
"/deployments/#{deployment.name}"
|
101
|
+
ensure
|
102
|
+
update_stemcell_references
|
103
|
+
end
|
104
|
+
end
|
105
|
+
ensure
|
106
|
+
FileUtils.rm_rf(@manifest_file)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,672 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
require 'common/version_number'
|
4
|
+
|
5
|
+
module Bosh::Director
|
6
|
+
module Jobs
|
7
|
+
class UpdateRelease < BaseJob
|
8
|
+
include LockHelper
|
9
|
+
include DownloadHelper
|
10
|
+
|
11
|
+
@queue = :normal
|
12
|
+
|
13
|
+
attr_accessor :release_model
|
14
|
+
attr_accessor :tmp_release_dir
|
15
|
+
|
16
|
+
def self.job_type
|
17
|
+
:update_release
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [String] tmp_release_dir Directory containing release bundle
|
21
|
+
# @param [Hash] options Release update options
|
22
|
+
def initialize(tmp_release_dir, options = {})
|
23
|
+
@tmp_release_dir = tmp_release_dir
|
24
|
+
@release_model = nil
|
25
|
+
@release_version_model = nil
|
26
|
+
|
27
|
+
@rebase = !!options["rebase"]
|
28
|
+
@package_rebase_mapping = {}
|
29
|
+
@job_rebase_mapping = {}
|
30
|
+
|
31
|
+
@manifest = nil
|
32
|
+
@name = nil
|
33
|
+
@version = nil
|
34
|
+
|
35
|
+
@packages_unchanged = false
|
36
|
+
@jobs_unchanged = false
|
37
|
+
|
38
|
+
@remote_release = options['remote'] || false
|
39
|
+
@remote_release_location = options['location'] if @remote_release
|
40
|
+
end
|
41
|
+
|
42
|
+
# Extracts release tarball, verifies release manifest and saves release
|
43
|
+
# in DB
|
44
|
+
# @return [void]
|
45
|
+
def perform
|
46
|
+
logger.info("Processing update release")
|
47
|
+
if @rebase
|
48
|
+
logger.info("Release rebase will be performed")
|
49
|
+
end
|
50
|
+
|
51
|
+
single_step_stage("Downloading remote release") { download_remote_release } if @remote_release
|
52
|
+
single_step_stage("Extracting release") { extract_release }
|
53
|
+
single_step_stage("Verifying manifest") { verify_manifest }
|
54
|
+
|
55
|
+
with_release_lock(@name) { process_release }
|
56
|
+
|
57
|
+
if @rebase && @packages_unchanged && @jobs_unchanged
|
58
|
+
raise DirectorError,
|
59
|
+
"Rebase is attempted without any job or package changes"
|
60
|
+
end
|
61
|
+
|
62
|
+
"Created release `#{@name}/#{@version}'"
|
63
|
+
rescue Exception => e
|
64
|
+
remove_release_version_model
|
65
|
+
raise e
|
66
|
+
ensure
|
67
|
+
if @tmp_release_dir && File.exists?(@tmp_release_dir)
|
68
|
+
FileUtils.rm_rf(@tmp_release_dir)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def download_remote_release
|
73
|
+
release_file = File.join(@tmp_release_dir, Api::ReleaseManager::RELEASE_TGZ)
|
74
|
+
download_remote_file('release', @remote_release_location, release_file)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Extracts release tarball
|
78
|
+
# @return [void]
|
79
|
+
def extract_release
|
80
|
+
release_tgz = File.join(@tmp_release_dir,
|
81
|
+
Api::ReleaseManager::RELEASE_TGZ)
|
82
|
+
|
83
|
+
result = Bosh::Exec.sh("tar -C #{@tmp_release_dir} -xzf #{release_tgz} 2>&1", :on_error => :return)
|
84
|
+
if result.failed?
|
85
|
+
logger.error("Extracting release archive failed in dir #{@tmp_release_dir}, " +
|
86
|
+
"tar returned #{result.exit_status}, " +
|
87
|
+
"output: #{result.output}")
|
88
|
+
raise ReleaseInvalidArchive, "Extracting release archive failed. Check task debug log for details."
|
89
|
+
end
|
90
|
+
ensure
|
91
|
+
if release_tgz && File.exists?(release_tgz)
|
92
|
+
FileUtils.rm(release_tgz)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# @return [void]
|
97
|
+
def verify_manifest
|
98
|
+
manifest_file = File.join(@tmp_release_dir, "release.MF")
|
99
|
+
unless File.file?(manifest_file)
|
100
|
+
raise ReleaseManifestNotFound, "Release manifest not found"
|
101
|
+
end
|
102
|
+
|
103
|
+
@manifest = Psych.load_file(manifest_file)
|
104
|
+
normalize_manifest
|
105
|
+
|
106
|
+
@name = @manifest["name"]
|
107
|
+
@version = @manifest["version"]
|
108
|
+
@commit_hash = @manifest.fetch("commit_hash", nil)
|
109
|
+
@uncommitted_changes = @manifest.fetch("uncommitted_changes", nil)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Processes uploaded release, creates jobs and packages in DB if needed
|
113
|
+
# @return [void]
|
114
|
+
def process_release
|
115
|
+
@release_model = Models::Release.find_or_create(:name => @name)
|
116
|
+
if @rebase
|
117
|
+
@version = next_release_version
|
118
|
+
end
|
119
|
+
|
120
|
+
version_attrs = {
|
121
|
+
:release => @release_model,
|
122
|
+
:version => @version
|
123
|
+
}
|
124
|
+
version_attrs[:uncommitted_changes] = @uncommitted_changes if @uncommitted_changes
|
125
|
+
version_attrs[:commit_hash] = @commit_hash if @commit_hash
|
126
|
+
|
127
|
+
@release_version_model = Models::ReleaseVersion.new(version_attrs)
|
128
|
+
unless @release_version_model.valid?
|
129
|
+
raise ReleaseAlreadyExists,
|
130
|
+
"Release `#{@name}/#{@version}' already exists"
|
131
|
+
end
|
132
|
+
|
133
|
+
@release_version_model.save
|
134
|
+
|
135
|
+
single_step_stage("Resolving package dependencies") do
|
136
|
+
resolve_package_dependencies(@manifest["packages"])
|
137
|
+
end
|
138
|
+
|
139
|
+
@packages = {}
|
140
|
+
process_packages
|
141
|
+
process_jobs
|
142
|
+
|
143
|
+
unless @package_rebase_mapping.empty?
|
144
|
+
event_log.begin_stage(
|
145
|
+
"Rebased packages", @package_rebase_mapping.size)
|
146
|
+
@package_rebase_mapping.each_pair do |name, transition|
|
147
|
+
event_log.track("#{name}: #{transition}") {}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
unless @job_rebase_mapping.empty?
|
152
|
+
event_log.begin_stage(
|
153
|
+
"Rebased jobs", @job_rebase_mapping.size)
|
154
|
+
@job_rebase_mapping.each_pair do |name, transition|
|
155
|
+
event_log.track("#{name}: #{transition}") {}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
event_log.begin_stage("Release has been created", 1)
|
160
|
+
event_log.track("#{@name}/#{@version}") {}
|
161
|
+
end
|
162
|
+
|
163
|
+
# Normalizes release manifest, so all names, versions, and checksums
|
164
|
+
# are Strings.
|
165
|
+
# @return [void]
|
166
|
+
def normalize_manifest
|
167
|
+
Bosh::Director.hash_string_vals(@manifest, 'name', 'version')
|
168
|
+
|
169
|
+
@manifest['packages'].each { |p| Bosh::Director.hash_string_vals(p, 'name', 'version', 'sha1') }
|
170
|
+
@manifest['jobs'].each { |j| Bosh::Director.hash_string_vals(j, 'name', 'version', 'sha1') }
|
171
|
+
end
|
172
|
+
|
173
|
+
# Resolves package dependencies, makes sure there are no cycles
|
174
|
+
# and all dependencies are present
|
175
|
+
# @return [void]
|
176
|
+
def resolve_package_dependencies(packages)
|
177
|
+
packages_by_name = {}
|
178
|
+
packages.each do |package|
|
179
|
+
packages_by_name[package["name"]] = package
|
180
|
+
package["dependencies"] ||= []
|
181
|
+
end
|
182
|
+
dependency_lookup = lambda do |package_name|
|
183
|
+
packages_by_name[package_name]["dependencies"]
|
184
|
+
end
|
185
|
+
result = CycleHelper.check_for_cycle(packages_by_name.keys,
|
186
|
+
:connected_vertices => true,
|
187
|
+
&dependency_lookup)
|
188
|
+
|
189
|
+
packages.each do |package|
|
190
|
+
name = package["name"]
|
191
|
+
dependencies = package["dependencies"]
|
192
|
+
|
193
|
+
logger.info("Resolving package dependencies for `#{name}', " +
|
194
|
+
"found: #{dependencies.pretty_inspect}")
|
195
|
+
package["dependencies"] = result[:connected_vertices][name]
|
196
|
+
logger.info("Resolved package dependencies for `#{name}', " +
|
197
|
+
"to: #{dependencies.pretty_inspect}")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Finds all package definitions in the manifest and sorts them into two
|
202
|
+
# buckets: new and existing packages, then creates new packages and points
|
203
|
+
# current release version to the existing packages.
|
204
|
+
# @return [void]
|
205
|
+
def process_packages
|
206
|
+
logger.info("Checking for new packages in release")
|
207
|
+
|
208
|
+
new_packages = []
|
209
|
+
existing_packages = []
|
210
|
+
|
211
|
+
@manifest["packages"].each do |package_meta|
|
212
|
+
filter = {:sha1 => package_meta["sha1"]}
|
213
|
+
if package_meta["fingerprint"]
|
214
|
+
filter[:fingerprint] = package_meta["fingerprint"]
|
215
|
+
filter = filter.sql_or
|
216
|
+
end
|
217
|
+
|
218
|
+
# Checking whether we might have the same bits somewhere
|
219
|
+
packages = Models::Package.where(filter).all
|
220
|
+
|
221
|
+
if packages.empty?
|
222
|
+
new_packages << package_meta
|
223
|
+
next
|
224
|
+
end
|
225
|
+
|
226
|
+
# Rebase is an interesting use case: we don't really care about
|
227
|
+
# preserving the original package/job versions, so if we have a
|
228
|
+
# checksum/fingerprint match, we can just substitute the original
|
229
|
+
# package/job version with an existing one.
|
230
|
+
if @rebase
|
231
|
+
substitutes = packages.select do |package|
|
232
|
+
package.release_id == @release_model.id &&
|
233
|
+
package.name == package_meta["name"] &&
|
234
|
+
package.dependency_set == Set.new(package_meta["dependencies"])
|
235
|
+
end
|
236
|
+
|
237
|
+
substitute = pick_best(substitutes, package_meta["version"])
|
238
|
+
|
239
|
+
if substitute
|
240
|
+
package_meta["version"] = substitute.version
|
241
|
+
package_meta["sha1"] = substitute.sha1
|
242
|
+
existing_packages << [substitute, package_meta]
|
243
|
+
next
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# We can reuse an existing package as long as it
|
248
|
+
# belongs to the same release and has the same name and version.
|
249
|
+
existing_package = packages.find do |package|
|
250
|
+
package.release_id == @release_model.id &&
|
251
|
+
package.name == package_meta["name"] &&
|
252
|
+
package.version == package_meta["version"]
|
253
|
+
# NOT checking dependencies here b/c dependency change would
|
254
|
+
# bump the package version anyway.
|
255
|
+
end
|
256
|
+
|
257
|
+
if existing_package
|
258
|
+
existing_packages << [existing_package, package_meta]
|
259
|
+
else
|
260
|
+
# We found a package with the same checksum but different
|
261
|
+
# (release, name, version) tuple, so we need to make a copy
|
262
|
+
# of the package blob and create a new db entry for it
|
263
|
+
package = packages.first
|
264
|
+
package_meta["blobstore_id"] = package.blobstore_id
|
265
|
+
new_packages << package_meta
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
create_packages(new_packages)
|
270
|
+
use_existing_packages(existing_packages)
|
271
|
+
end
|
272
|
+
|
273
|
+
# Creates packages using provided metadata
|
274
|
+
# @param [Array<Hash>] packages Packages metadata
|
275
|
+
# @return [void]
|
276
|
+
def create_packages(packages)
|
277
|
+
if packages.empty?
|
278
|
+
@packages_unchanged = true
|
279
|
+
return
|
280
|
+
end
|
281
|
+
|
282
|
+
event_log.begin_stage("Creating new packages", packages.size)
|
283
|
+
packages.each do |package_meta|
|
284
|
+
package_desc = "#{package_meta["name"]}/#{package_meta["version"]}"
|
285
|
+
event_log.track(package_desc) do
|
286
|
+
logger.info("Creating new package `#{package_desc}'")
|
287
|
+
package = create_package(package_meta)
|
288
|
+
register_package(package)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Points release DB model to existing packages described by given metadata
|
294
|
+
# @param [Array<Array>] packages Existing packages metadata
|
295
|
+
def use_existing_packages(packages)
|
296
|
+
return if packages.empty?
|
297
|
+
|
298
|
+
n_packages = packages.size
|
299
|
+
event_log.begin_stage("Processing #{n_packages} existing " +
|
300
|
+
"package#{n_packages > 1 ? "s" : ""}", 1)
|
301
|
+
|
302
|
+
event_log.track("Verifying checksums") do
|
303
|
+
packages.each do |package, package_meta|
|
304
|
+
package_desc = "#{package.name}/#{package.version}"
|
305
|
+
logger.info("Package `#{package_desc}' already exists, " +
|
306
|
+
"verifying checksum")
|
307
|
+
|
308
|
+
expected = package.sha1
|
309
|
+
received = package_meta["sha1"]
|
310
|
+
|
311
|
+
if expected != received
|
312
|
+
raise ReleaseExistingPackageHashMismatch,
|
313
|
+
"`#{package_desc}' checksum mismatch, " +
|
314
|
+
"expected #{expected} but received #{received}"
|
315
|
+
end
|
316
|
+
logger.info("Package `#{package_desc}' verified")
|
317
|
+
register_package(package)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Creates package in DB according to given metadata
|
323
|
+
# @param [Hash] package_meta Package metadata
|
324
|
+
# @return [void]
|
325
|
+
def create_package(package_meta)
|
326
|
+
name, version = package_meta["name"], package_meta["version"]
|
327
|
+
|
328
|
+
package_attrs = {
|
329
|
+
:release => @release_model,
|
330
|
+
:name => name,
|
331
|
+
:sha1 => package_meta["sha1"],
|
332
|
+
:fingerprint => package_meta["fingerprint"],
|
333
|
+
:version => version
|
334
|
+
}
|
335
|
+
|
336
|
+
if @rebase
|
337
|
+
new_version = next_package_version(name, version)
|
338
|
+
if new_version != version
|
339
|
+
transition = "#{version} -> #{new_version}"
|
340
|
+
logger.info("Package `#{name}' rebased: #{transition}")
|
341
|
+
package_attrs[:version] = new_version
|
342
|
+
version = new_version
|
343
|
+
@package_rebase_mapping[name] = transition
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
package = Models::Package.new(package_attrs)
|
348
|
+
package.dependency_set = package_meta["dependencies"]
|
349
|
+
|
350
|
+
existing_blob = package_meta["blobstore_id"]
|
351
|
+
desc = "package `#{name}/#{version}'"
|
352
|
+
|
353
|
+
if existing_blob
|
354
|
+
logger.info("Creating #{desc} from existing blob #{existing_blob}")
|
355
|
+
package.blobstore_id = BlobUtil.copy_blob(existing_blob)
|
356
|
+
else
|
357
|
+
logger.info("Creating #{desc} from provided bits")
|
358
|
+
|
359
|
+
package_tgz = File.join(@tmp_release_dir, "packages", "#{name}.tgz")
|
360
|
+
result = Bosh::Exec.sh("tar -tzf #{package_tgz} 2>&1", :on_error => :return)
|
361
|
+
if result.failed?
|
362
|
+
logger.error("Extracting #{desc} archive failed, " +
|
363
|
+
"tar returned #{result.exit_status}, " +
|
364
|
+
"output: #{result.output}")
|
365
|
+
raise PackageInvalidArchive, "Extracting #{desc} archive failed. Check task debug log for details."
|
366
|
+
end
|
367
|
+
|
368
|
+
package.blobstore_id = BlobUtil.create_blob(package_tgz)
|
369
|
+
end
|
370
|
+
|
371
|
+
package.save
|
372
|
+
end
|
373
|
+
|
374
|
+
# Marks package model as used by release version model
|
375
|
+
# @param [Models::Package] package Package model
|
376
|
+
# @return [void]
|
377
|
+
def register_package(package)
|
378
|
+
@packages[package.name] = package
|
379
|
+
@release_version_model.add_package(package)
|
380
|
+
end
|
381
|
+
|
382
|
+
# Finds job template definitions in release manifest and sorts them into
|
383
|
+
# two buckets: new and existing job templates, then creates new job
|
384
|
+
# template records in the database and points release version to existing
|
385
|
+
# ones.
|
386
|
+
# @return [void]
|
387
|
+
def process_jobs
|
388
|
+
logger.info("Checking for new jobs in release")
|
389
|
+
|
390
|
+
new_jobs = []
|
391
|
+
existing_jobs = []
|
392
|
+
|
393
|
+
@manifest["jobs"].each do |job_meta|
|
394
|
+
filter = {:sha1 => job_meta["sha1"]}
|
395
|
+
if job_meta["fingerprint"]
|
396
|
+
filter[:fingerprint] = job_meta["fingerprint"]
|
397
|
+
filter = filter.sql_or
|
398
|
+
end
|
399
|
+
|
400
|
+
# Checking whether we might have the same bits somewhere
|
401
|
+
jobs = Models::Template.where(filter).all
|
402
|
+
|
403
|
+
if @rebase
|
404
|
+
substitutes = jobs.select do |job|
|
405
|
+
job.release_id == @release_model.id &&
|
406
|
+
job.name == job_meta["name"]
|
407
|
+
end
|
408
|
+
|
409
|
+
substitute = pick_best(substitutes, job_meta["version"])
|
410
|
+
|
411
|
+
if substitute
|
412
|
+
job_meta["version"] = substitute.version
|
413
|
+
job_meta["sha1"] = substitute.sha1
|
414
|
+
existing_jobs << [substitute, job_meta]
|
415
|
+
else
|
416
|
+
new_jobs << job_meta
|
417
|
+
end
|
418
|
+
|
419
|
+
next
|
420
|
+
end
|
421
|
+
|
422
|
+
template = jobs.find do |job|
|
423
|
+
job.release_id == @release_model.id &&
|
424
|
+
job.name == job_meta["name"] &&
|
425
|
+
job.version == job_meta["version"]
|
426
|
+
end
|
427
|
+
|
428
|
+
if template.nil?
|
429
|
+
new_jobs << job_meta
|
430
|
+
else
|
431
|
+
existing_jobs << [template, job_meta]
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
create_jobs(new_jobs)
|
436
|
+
use_existing_jobs(existing_jobs)
|
437
|
+
end
|
438
|
+
|
439
|
+
def create_jobs(jobs)
|
440
|
+
if jobs.empty?
|
441
|
+
@jobs_unchanged = true
|
442
|
+
return
|
443
|
+
end
|
444
|
+
|
445
|
+
event_log.begin_stage("Creating new jobs", jobs.size)
|
446
|
+
jobs.each do |job_meta|
|
447
|
+
job_desc = "#{job_meta["name"]}/#{job_meta["version"]}"
|
448
|
+
event_log.track(job_desc) do
|
449
|
+
logger.info("Creating new template `#{job_desc}'")
|
450
|
+
template = create_job(job_meta)
|
451
|
+
register_template(template)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
def create_job(job_meta)
|
457
|
+
name, version = job_meta["name"], job_meta["version"]
|
458
|
+
|
459
|
+
template_attrs = {
|
460
|
+
:release => @release_model,
|
461
|
+
:name => name,
|
462
|
+
:sha1 => job_meta["sha1"],
|
463
|
+
:fingerprint => job_meta["fingerprint"],
|
464
|
+
:version => version
|
465
|
+
}
|
466
|
+
|
467
|
+
if @rebase
|
468
|
+
new_version = next_template_version(name, version)
|
469
|
+
if new_version != version
|
470
|
+
transition = "#{version} -> #{new_version}"
|
471
|
+
logger.info("Job `#{name}' rebased: #{transition}")
|
472
|
+
template_attrs[:version] = new_version
|
473
|
+
version = new_version
|
474
|
+
@job_rebase_mapping[name] = transition
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
logger.info("Creating job template `#{name}/#{version}' " +
|
479
|
+
"from provided bits")
|
480
|
+
template = Models::Template.new(template_attrs)
|
481
|
+
|
482
|
+
job_tgz = File.join(@tmp_release_dir, "jobs", "#{name}.tgz")
|
483
|
+
job_dir = File.join(@tmp_release_dir, "jobs", "#{name}")
|
484
|
+
|
485
|
+
FileUtils.mkdir_p(job_dir)
|
486
|
+
|
487
|
+
desc = "job `#{name}/#{version}'"
|
488
|
+
result = Bosh::Exec.sh("tar -C #{job_dir} -xzf #{job_tgz} 2>&1", :on_error => :return)
|
489
|
+
if result.failed?
|
490
|
+
logger.error("Extracting #{desc} archive failed in dir #{job_dir}, " +
|
491
|
+
"tar returned #{result.exit_status}, " +
|
492
|
+
"output: #{result.output}")
|
493
|
+
raise JobInvalidArchive, "Extracting #{desc} archive failed. Check task debug log for details."
|
494
|
+
end
|
495
|
+
|
496
|
+
manifest_file = File.join(job_dir, "job.MF")
|
497
|
+
unless File.file?(manifest_file)
|
498
|
+
raise JobMissingManifest,
|
499
|
+
"Missing job manifest for `#{template.name}'"
|
500
|
+
end
|
501
|
+
|
502
|
+
job_manifest = Psych.load_file(manifest_file)
|
503
|
+
|
504
|
+
if job_manifest["templates"]
|
505
|
+
job_manifest["templates"].each_key do |relative_path|
|
506
|
+
path = File.join(job_dir, "templates", relative_path)
|
507
|
+
unless File.file?(path)
|
508
|
+
raise JobMissingTemplateFile,
|
509
|
+
"Missing template file `#{relative_path}' " +
|
510
|
+
"for job `#{template.name}'"
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
main_monit_file = File.join(job_dir, "monit")
|
516
|
+
aux_monit_files = Dir.glob(File.join(job_dir, "*.monit"))
|
517
|
+
|
518
|
+
unless File.exists?(main_monit_file) || aux_monit_files.size > 0
|
519
|
+
raise JobMissingMonit, "Job `#{template.name}' is missing monit file"
|
520
|
+
end
|
521
|
+
|
522
|
+
template.blobstore_id = BlobUtil.create_blob(job_tgz)
|
523
|
+
|
524
|
+
package_names = []
|
525
|
+
job_manifest["packages"].each do |package_name|
|
526
|
+
package = @packages[package_name]
|
527
|
+
if package.nil?
|
528
|
+
raise JobMissingPackage,
|
529
|
+
"Job `#{template.name}' is referencing " +
|
530
|
+
"a missing package `#{package_name}'"
|
531
|
+
end
|
532
|
+
package_names << package.name
|
533
|
+
end
|
534
|
+
template.package_names = package_names
|
535
|
+
|
536
|
+
if job_manifest["logs"]
|
537
|
+
unless job_manifest["logs"].is_a?(Hash)
|
538
|
+
raise JobInvalidLogSpec,
|
539
|
+
"Job `#{template.name}' has invalid logs spec format"
|
540
|
+
end
|
541
|
+
|
542
|
+
template.logs = job_manifest["logs"]
|
543
|
+
end
|
544
|
+
|
545
|
+
if job_manifest["properties"]
|
546
|
+
unless job_manifest["properties"].is_a?(Hash)
|
547
|
+
raise JobInvalidPropertySpec,
|
548
|
+
"Job `#{template.name}' has invalid properties spec format"
|
549
|
+
end
|
550
|
+
|
551
|
+
template.properties = job_manifest["properties"]
|
552
|
+
end
|
553
|
+
|
554
|
+
template.save
|
555
|
+
end
|
556
|
+
|
557
|
+
# @param [Array<Array>] jobs Existing jobs metadata
|
558
|
+
# @return [void]
|
559
|
+
def use_existing_jobs(jobs)
|
560
|
+
return if jobs.empty?
|
561
|
+
|
562
|
+
n_jobs = jobs.size
|
563
|
+
event_log.begin_stage("Processing #{n_jobs} existing " +
|
564
|
+
"job#{n_jobs > 1 ? "s" : ""}", 1)
|
565
|
+
|
566
|
+
event_log.track("Verifying checksums") do
|
567
|
+
jobs.each do |template, job_meta|
|
568
|
+
job_desc = "#{template.name}/#{template.version}"
|
569
|
+
|
570
|
+
logger.info("Job `#{job_desc}' already exists, " +
|
571
|
+
"verifying checksum")
|
572
|
+
|
573
|
+
expected = template.sha1
|
574
|
+
received = job_meta["sha1"]
|
575
|
+
|
576
|
+
if expected != received
|
577
|
+
raise ReleaseExistingJobHashMismatch,
|
578
|
+
"`#{job_desc}' checksum mismatch, " +
|
579
|
+
"expected #{expected} but received #{received}"
|
580
|
+
end
|
581
|
+
|
582
|
+
logger.info("Job `#{job_desc}' verified")
|
583
|
+
register_template(template)
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
# Marks job template model as being used by release version
|
589
|
+
# @param [Models::Template] template Job template model
|
590
|
+
# @return [void]
|
591
|
+
def register_template(template)
|
592
|
+
@release_version_model.add_template(template)
|
593
|
+
end
|
594
|
+
|
595
|
+
private
|
596
|
+
|
597
|
+
# Returns the next release version (to be used for rebased release)
|
598
|
+
# @return [String]
|
599
|
+
def next_release_version
|
600
|
+
attrs = {
|
601
|
+
:release_id => @release_model.id
|
602
|
+
}
|
603
|
+
next_version(Models::ReleaseVersion.filter(attrs).all, @version)
|
604
|
+
end
|
605
|
+
|
606
|
+
# Returns the next package version (to be used for rebased package)
|
607
|
+
# @param [String] name Package name
|
608
|
+
# @param [String] version Package version
|
609
|
+
# @return [String]
|
610
|
+
def next_package_version(name, version)
|
611
|
+
attrs = {
|
612
|
+
:release_id => @release_model.id,
|
613
|
+
:name => name
|
614
|
+
}
|
615
|
+
|
616
|
+
next_version(Models::Package.filter(attrs).all, version)
|
617
|
+
end
|
618
|
+
|
619
|
+
# Returns the next job template version (to be used for rebased template)
|
620
|
+
# @param [String] name Template name
|
621
|
+
# @param [Fixnum] version Template version
|
622
|
+
# @return [String]
|
623
|
+
def next_template_version(name, version)
|
624
|
+
attrs = {
|
625
|
+
:release_id => @release_model.id,
|
626
|
+
:name => name
|
627
|
+
}
|
628
|
+
|
629
|
+
next_version(Models::Template.filter(attrs).all, version)
|
630
|
+
end
|
631
|
+
|
632
|
+
# Takes collection of versioned items and returns the version
|
633
|
+
# that new item should be promoted to if auto-versioning is used
|
634
|
+
# @param [Array<#version>] Collection of items
|
635
|
+
# @param [String] version Current version of item
|
636
|
+
# @return [String] Next version to be used
|
637
|
+
def next_version(collection, version)
|
638
|
+
Bosh::Director::NextRebaseVersion.new(collection).calculate(version)
|
639
|
+
end
|
640
|
+
|
641
|
+
# Removes release version model, along with all packages and templates.
|
642
|
+
# @return [void]
|
643
|
+
def remove_release_version_model
|
644
|
+
return unless @release_version_model && !@release_version_model.new?
|
645
|
+
|
646
|
+
@release_version_model.remove_all_packages
|
647
|
+
@release_version_model.remove_all_templates
|
648
|
+
@release_version_model.destroy
|
649
|
+
end
|
650
|
+
|
651
|
+
# Picks the best matching package/job from collection based on a simple
|
652
|
+
# heuristics: items with same version are preferred, then items
|
653
|
+
# with non-dev versions, then items with most compiled packages,
|
654
|
+
# then everything else.
|
655
|
+
# @param [Array<#name,#version>] collection
|
656
|
+
# @param [String] original_version
|
657
|
+
def pick_best(collection, original_version)
|
658
|
+
collection.sort_by { |item|
|
659
|
+
if item.version == original_version
|
660
|
+
1
|
661
|
+
elsif Bosh::Common::VersionNumber.new(item.version).final?
|
662
|
+
2
|
663
|
+
elsif item.is_a?(Bosh::Director::Models::Package)
|
664
|
+
3000 - [item.compiled_packages.size, 900].min
|
665
|
+
else
|
666
|
+
3000
|
667
|
+
end
|
668
|
+
}.first
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|
672
|
+
end
|