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,29 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Director
|
4
|
+
class Blobstores
|
5
|
+
|
6
|
+
attr_reader :blobstore
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@blobstore = create_client(config.hash.fetch('blobstore'))
|
10
|
+
|
11
|
+
bd_config = config.hash['backup_destination']
|
12
|
+
@backup_destination = create_client(bd_config) if bd_config
|
13
|
+
end
|
14
|
+
|
15
|
+
def backup_destination
|
16
|
+
raise 'No backup destination configured' unless @backup_destination
|
17
|
+
@backup_destination
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_client(hash)
|
23
|
+
provider = hash.fetch('provider')
|
24
|
+
options = hash.fetch('options')
|
25
|
+
|
26
|
+
Bosh::Blobstore::Client.create(provider, options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Director
|
4
|
+
class Client
|
5
|
+
|
6
|
+
attr_accessor :id
|
7
|
+
|
8
|
+
def initialize(service_name, client_id, options = {})
|
9
|
+
@service_name = service_name
|
10
|
+
@client_id = client_id
|
11
|
+
@nats_rpc = Config.nats_rpc
|
12
|
+
@timeout = options[:timeout] || 45
|
13
|
+
@logger = Config.logger
|
14
|
+
@retry_methods = options[:retry_methods] || {}
|
15
|
+
|
16
|
+
if options[:credentials]
|
17
|
+
@encryption_handler =
|
18
|
+
Bosh::Core::EncryptionHandler.new(@client_id, options[:credentials])
|
19
|
+
end
|
20
|
+
|
21
|
+
@resource_manager = Api::ResourceManager.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method_name, *args)
|
25
|
+
retries = @retry_methods[method_name] || 0
|
26
|
+
begin
|
27
|
+
handle_method(method_name, args)
|
28
|
+
rescue RpcTimeout
|
29
|
+
if retries > 0
|
30
|
+
retries -= 1
|
31
|
+
retry
|
32
|
+
end
|
33
|
+
raise
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def wait_until_ready(deadline = 600)
|
38
|
+
old_timeout = @timeout
|
39
|
+
@timeout = 1.0
|
40
|
+
@deadline = Time.now.to_i + deadline
|
41
|
+
|
42
|
+
begin
|
43
|
+
ping
|
44
|
+
rescue RpcTimeout
|
45
|
+
retry if @deadline - Time.now.to_i > 0
|
46
|
+
raise RpcTimeout, "Timed out pinging to #{@client_id} after #{deadline} seconds"
|
47
|
+
rescue RpcRemoteException => e
|
48
|
+
retry if e.message =~ /^restarting agent/ && @deadline - Time.now.to_i > 0
|
49
|
+
raise e
|
50
|
+
ensure
|
51
|
+
@timeout = old_timeout
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def handle_method(method_name, args)
|
56
|
+
result = {}
|
57
|
+
result.extend(MonitorMixin)
|
58
|
+
|
59
|
+
cond = result.new_cond
|
60
|
+
timeout_time = Time.now.to_f + @timeout
|
61
|
+
|
62
|
+
request = {:method => method_name, :arguments => args}
|
63
|
+
|
64
|
+
if @encryption_handler
|
65
|
+
@logger.info("Request: #{request}")
|
66
|
+
request = {"encrypted_data" => @encryption_handler.encrypt(request)}
|
67
|
+
request["session_id"] = @encryption_handler.session_id
|
68
|
+
end
|
69
|
+
|
70
|
+
recipient = "#{@service_name}.#{@client_id}"
|
71
|
+
|
72
|
+
request_id = @nats_rpc.send_request(recipient, request) do |response|
|
73
|
+
if @encryption_handler
|
74
|
+
begin
|
75
|
+
response = @encryption_handler.decrypt(response["encrypted_data"])
|
76
|
+
rescue Bosh::Core::EncryptionHandler::CryptError => e
|
77
|
+
response["exception"] = "CryptError: #{e.inspect} #{e.backtrace}"
|
78
|
+
end
|
79
|
+
@logger.info("Response: #{response}")
|
80
|
+
end
|
81
|
+
|
82
|
+
result.synchronize do
|
83
|
+
inject_compile_log(response)
|
84
|
+
result.merge!(response)
|
85
|
+
cond.signal
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
result.synchronize do
|
90
|
+
while result.empty?
|
91
|
+
timeout = timeout_time - Time.now.to_f
|
92
|
+
unless timeout > 0
|
93
|
+
@nats_rpc.cancel_request(request_id)
|
94
|
+
raise RpcTimeout,
|
95
|
+
"Timed out sending `#{method_name}' to #{@client_id} " +
|
96
|
+
"after #{@timeout} seconds"
|
97
|
+
end
|
98
|
+
cond.wait(timeout)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
if result.has_key?("exception")
|
103
|
+
raise RpcRemoteException, format_exception(result["exception"])
|
104
|
+
end
|
105
|
+
|
106
|
+
result["value"]
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns formatted exception information
|
110
|
+
# @param [Hash|#to_s] exception Serialized exception
|
111
|
+
# @return [String]
|
112
|
+
def format_exception(exception)
|
113
|
+
return exception.to_s unless exception.is_a?(Hash)
|
114
|
+
|
115
|
+
msg = exception["message"].to_s
|
116
|
+
|
117
|
+
if exception["backtrace"]
|
118
|
+
msg += "\n"
|
119
|
+
msg += Array(exception["backtrace"]).join("\n")
|
120
|
+
end
|
121
|
+
|
122
|
+
if exception["blobstore_id"]
|
123
|
+
blob = download_and_delete_blob(exception["blobstore_id"])
|
124
|
+
msg += "\n"
|
125
|
+
msg += blob.to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
msg
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
# the blob is removed from the blobstore once we have fetched it,
|
134
|
+
# but if there is a crash before it is injected into the response
|
135
|
+
# and then logged, there is a chance that we lose it
|
136
|
+
def inject_compile_log(response)
|
137
|
+
if response["value"] && response["value"].is_a?(Hash) &&
|
138
|
+
response["value"]["result"].is_a?(Hash) &&
|
139
|
+
blob_id = response["value"]["result"]["compile_log_id"]
|
140
|
+
compile_log = download_and_delete_blob(blob_id)
|
141
|
+
response["value"]["result"]["compile_log"] = compile_log
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Downloads blob and ensures it's deleted from the blobstore
|
146
|
+
# @param [String] blob_id Blob id
|
147
|
+
# @return [String] Blob contents
|
148
|
+
def download_and_delete_blob(blob_id)
|
149
|
+
blob = @resource_manager.get_resource(blob_id)
|
150
|
+
blob
|
151
|
+
ensure
|
152
|
+
@resource_manager.delete_resource(blob_id)
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Director
|
4
|
+
module CloudcheckHelper
|
5
|
+
# Helper functions that come in handy for
|
6
|
+
# cloudcheck:
|
7
|
+
# 1. VM/agent interactions
|
8
|
+
# 2. VM lifecycle operations (from cloudcheck POV)
|
9
|
+
# 3. Error handling
|
10
|
+
|
11
|
+
# This timeout has been made pretty short mainly
|
12
|
+
# to avoid long cloudchecks, however 10 seconds should
|
13
|
+
# still be pretty generous interval for agent to respond.
|
14
|
+
DEFAULT_AGENT_TIMEOUT = 10
|
15
|
+
|
16
|
+
def cloud
|
17
|
+
Bosh::Director::Config.cloud
|
18
|
+
end
|
19
|
+
|
20
|
+
def handler_error(message)
|
21
|
+
raise Bosh::Director::ProblemHandlerError, message
|
22
|
+
end
|
23
|
+
|
24
|
+
def instance_name(vm)
|
25
|
+
instance = vm.instance
|
26
|
+
return "Unknown VM" if instance.nil?
|
27
|
+
|
28
|
+
job = instance.job || "unknown job"
|
29
|
+
index = instance.index || "unknown index"
|
30
|
+
"#{job}/#{index}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def agent_client(vm, timeout = DEFAULT_AGENT_TIMEOUT, retries = 0)
|
34
|
+
options = {
|
35
|
+
:timeout => timeout,
|
36
|
+
:retry_methods => { :get_state => retries }
|
37
|
+
}
|
38
|
+
@clients ||= {}
|
39
|
+
@clients[vm.agent_id] ||= AgentClient.new(vm.agent_id, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def agent_timeout_guard(vm, &block)
|
43
|
+
yield agent_client(vm)
|
44
|
+
rescue Bosh::Director::RpcTimeout
|
45
|
+
handler_error("VM `#{vm.cid}' is not responding")
|
46
|
+
end
|
47
|
+
|
48
|
+
def reboot_vm(vm)
|
49
|
+
cloud.reboot_vm(vm.cid)
|
50
|
+
begin
|
51
|
+
agent_client(vm).wait_until_ready
|
52
|
+
rescue Bosh::Director::RpcTimeout
|
53
|
+
handler_error("Agent still unresponsive after reboot")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_vm(vm)
|
58
|
+
# Paranoia: don't blindly delete VMs with persistent disk
|
59
|
+
disk_list = agent_timeout_guard(vm) { |agent| agent.list_disk }
|
60
|
+
if disk_list.size != 0
|
61
|
+
handler_error("VM has persistent disk attached")
|
62
|
+
end
|
63
|
+
|
64
|
+
cloud.delete_vm(vm.cid)
|
65
|
+
vm.db.transaction do
|
66
|
+
vm.instance.update(:vm => nil) if vm.instance
|
67
|
+
vm.destroy
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_vm_reference(vm, options={})
|
72
|
+
if vm.cid && !options[:skip_cid_check]
|
73
|
+
handler_error("VM has a CID")
|
74
|
+
end
|
75
|
+
|
76
|
+
vm.db.transaction do
|
77
|
+
vm.instance.update(:vm => nil) if vm.instance
|
78
|
+
vm.destroy
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def recreate_vm(vm)
|
83
|
+
# Best we can do without any feedback from the agent
|
84
|
+
# is to use the spec persisted in the DB at the time
|
85
|
+
# of last apply call.
|
86
|
+
# This method is somewhat similar in its nature to what
|
87
|
+
# InstanceUpdater is doing in case of the stemcell update,
|
88
|
+
# however we don't need to handle some advanced scenarios
|
89
|
+
# such as disk migration.
|
90
|
+
|
91
|
+
spec = validate_spec(vm)
|
92
|
+
env = validate_env(vm)
|
93
|
+
|
94
|
+
resource_pool_spec = spec.fetch("resource_pool", {})
|
95
|
+
stemcell = find_stemcell(resource_pool_spec.fetch("stemcell", {}))
|
96
|
+
|
97
|
+
deployment = vm.deployment
|
98
|
+
handler_error("VM doesn't belong to any deployment") unless deployment
|
99
|
+
|
100
|
+
instance = vm.instance
|
101
|
+
disk_cid = instance ? instance.persistent_disk_cid : nil
|
102
|
+
|
103
|
+
# One situation where this handler is actually useful is when
|
104
|
+
# VM has already been deleted but something failed after that
|
105
|
+
# and it is still referenced in DB. In that case it makes sense
|
106
|
+
# to ignore "VM not found" errors in `delete_vm' and let the method
|
107
|
+
# proceed creating a new VM. Other errors are not forgiven.
|
108
|
+
begin
|
109
|
+
cloud.delete_vm(vm.cid)
|
110
|
+
rescue Bosh::Clouds::VMNotFound => e
|
111
|
+
@logger.warn("VM '#{vm.cid}' might have already been deleted from the cloud")
|
112
|
+
end
|
113
|
+
|
114
|
+
vm.db.transaction do
|
115
|
+
instance.update(:vm => nil) if instance
|
116
|
+
vm.destroy
|
117
|
+
end
|
118
|
+
|
119
|
+
cloud_properties = resource_pool_spec.fetch("cloud_properties", {})
|
120
|
+
networks = spec["networks"]
|
121
|
+
new_vm = VmCreator.create(deployment, stemcell, cloud_properties, networks, Array(disk_cid), env)
|
122
|
+
new_vm.apply_spec = spec
|
123
|
+
new_vm.save
|
124
|
+
|
125
|
+
if instance
|
126
|
+
instance.update(:vm => new_vm)
|
127
|
+
|
128
|
+
# refresh metadata after new instance has been set
|
129
|
+
VmMetadataUpdater.build.update(new_vm, {})
|
130
|
+
end
|
131
|
+
|
132
|
+
agent_client(new_vm).wait_until_ready
|
133
|
+
|
134
|
+
# After this point agent is actually responding to
|
135
|
+
# pings, so if the rest of this handler fails
|
136
|
+
# bcck won't find this type of problem again
|
137
|
+
# but regular deployment will fail with "out-of-sync"
|
138
|
+
# error (as we now have an instance that points to
|
139
|
+
# VM that reports empty state). This problem
|
140
|
+
# should be handled by "out-of-sync VM" problem handler.
|
141
|
+
|
142
|
+
if disk_cid
|
143
|
+
# N.B. attach_disk might fail if disk image is no longer
|
144
|
+
# there or for some other reason. Generally it means
|
145
|
+
# the data has been lost (e.g. someone deleted VM from vCenter
|
146
|
+
# along with the disk.
|
147
|
+
cloud.attach_disk(new_vm.cid, disk_cid)
|
148
|
+
agent_client(new_vm).mount_disk(disk_cid)
|
149
|
+
end
|
150
|
+
|
151
|
+
agent_client(new_vm).apply(spec)
|
152
|
+
|
153
|
+
if instance && instance.state == "started"
|
154
|
+
agent_client(new_vm).start
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def validate_spec(vm)
|
161
|
+
handler_error("Unable to look up VM apply spec") unless vm.apply_spec
|
162
|
+
|
163
|
+
spec = vm.apply_spec
|
164
|
+
|
165
|
+
unless spec.kind_of?(Hash)
|
166
|
+
handler_error("Invalid apply spec format")
|
167
|
+
end
|
168
|
+
|
169
|
+
spec
|
170
|
+
end
|
171
|
+
|
172
|
+
def validate_env(vm)
|
173
|
+
handler_error("Unable to look up VM environment") unless vm.env
|
174
|
+
|
175
|
+
env = vm.env
|
176
|
+
|
177
|
+
unless env.kind_of?(Hash)
|
178
|
+
handler_error("Invalid VM environment format")
|
179
|
+
end
|
180
|
+
|
181
|
+
env
|
182
|
+
end
|
183
|
+
|
184
|
+
def find_stemcell(stemcell_spec)
|
185
|
+
stemcell_name = stemcell_spec['name']
|
186
|
+
stemcell_version = stemcell_spec['version']
|
187
|
+
|
188
|
+
unless stemcell_name && stemcell_version
|
189
|
+
handler_error('Unknown stemcell name and/or version')
|
190
|
+
end
|
191
|
+
|
192
|
+
stemcell = Models::Stemcell.find(:name => stemcell_name, :version => stemcell_version)
|
193
|
+
|
194
|
+
handler_error("Unable to find stemcell '#{stemcell_name} #{stemcell_version}'") unless stemcell
|
195
|
+
|
196
|
+
stemcell
|
197
|
+
end
|
198
|
+
|
199
|
+
def generate_agent_id
|
200
|
+
SecureRandom.uuid
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Director
|
4
|
+
class CompileTask
|
5
|
+
# @return [Models::Package] What package is being compiled
|
6
|
+
attr_reader :package
|
7
|
+
|
8
|
+
# @return [Models::Stemcell] What stemcell package is compiled for
|
9
|
+
attr_reader :stemcell
|
10
|
+
|
11
|
+
# @return [Array<DeploymentPlan::Job>] Jobs interested in this package
|
12
|
+
attr_reader :jobs
|
13
|
+
|
14
|
+
# @return [Models::CompiledPackage] Compiled package DB model
|
15
|
+
attr_reader :compiled_package
|
16
|
+
|
17
|
+
# @return [String] Dependency key (changing it will trigger recompilation
|
18
|
+
# even when package bits haven't changed)
|
19
|
+
attr_accessor :dependency_key
|
20
|
+
|
21
|
+
# @return [Array<CompileTask>] Tasks this task depends on
|
22
|
+
attr_reader :dependencies
|
23
|
+
|
24
|
+
# @return [Array<CompileTask>] Tasks depending on this task
|
25
|
+
attr_reader :dependent_tasks
|
26
|
+
|
27
|
+
# @return [String] A unique checksum based on the dependencies in this task
|
28
|
+
attr_reader :cache_key
|
29
|
+
|
30
|
+
# @param [Models::Package] package What package is being compiled
|
31
|
+
# by this task
|
32
|
+
# @param [Models::Stemcell] stemcell What stemcell package is compiled for
|
33
|
+
# @param [Array<Models::Package>] dependent_packages Package models that this task depends on
|
34
|
+
# @param [DeploymentPlan::Job] initial_job The first job that this task is associate with
|
35
|
+
def initialize(package, stemcell, dependent_packages, initial_job = nil)
|
36
|
+
@package = package
|
37
|
+
@stemcell = stemcell
|
38
|
+
|
39
|
+
@jobs = []
|
40
|
+
add_job(initial_job) if initial_job
|
41
|
+
@dependencies = []
|
42
|
+
@dependent_tasks = []
|
43
|
+
|
44
|
+
@dependency_key = generate_dependency_key(dependent_packages)
|
45
|
+
|
46
|
+
@cache_key = generate_cache_key(dependent_packages)
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Boolean] Whether this task is ready to be compiled
|
50
|
+
def ready_to_compile?
|
51
|
+
!compiled? && all_dependencies_compiled?
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Boolean]
|
55
|
+
def all_dependencies_compiled?
|
56
|
+
@dependencies.all? { |task| task.compiled? }
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Boolean]
|
60
|
+
def compiled?
|
61
|
+
!@compiled_package.nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Makes compiled package available to all jobs waiting for it
|
65
|
+
# @param [Models::CompiledPackage] compiled_package Compiled package
|
66
|
+
# @return [void]
|
67
|
+
def use_compiled_package(compiled_package)
|
68
|
+
@compiled_package = compiled_package
|
69
|
+
|
70
|
+
@jobs.each do |job|
|
71
|
+
job.use_compiled_package(@compiled_package)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Adds job to a list of job requiring this compiled package
|
76
|
+
# @note Cycle detection is done elsewhere
|
77
|
+
# @param [DeploymentPlan::Job] job Job to be added
|
78
|
+
# @return [void]
|
79
|
+
def add_job(job)
|
80
|
+
return if @jobs.include?(job)
|
81
|
+
@jobs << job
|
82
|
+
if @compiled_package
|
83
|
+
# If package is already compiled we can make it available to job
|
84
|
+
# immediately, otherwise it will be done by #use_compiled_package
|
85
|
+
job.use_compiled_package(@compiled_package)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Adds a compilation task to the list of dependencies
|
90
|
+
# @note Cycle detection performed elsewhere
|
91
|
+
# @param [CompileTask] task Compilation task
|
92
|
+
# @param [Boolean] reciprocate If true, add self as dependent task to other
|
93
|
+
# @return [void]
|
94
|
+
def add_dependency(task, reciprocate=true)
|
95
|
+
@dependencies << task
|
96
|
+
if reciprocate
|
97
|
+
task.add_dependent_task(self, false)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Adds a compilation task to the list of dependent tasks
|
102
|
+
# @note Cycle detection performed elsewhere
|
103
|
+
# @param [CompileTask] task Compilation task
|
104
|
+
# @param [Boolean] reciprocate If true, add self as dependency to to other
|
105
|
+
# @return [void]
|
106
|
+
def add_dependent_task(task, reciprocate=true)
|
107
|
+
@dependent_tasks << task
|
108
|
+
if reciprocate
|
109
|
+
task.add_dependency(self, false)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# This call only makes sense if all dependencies have already been compiled,
|
114
|
+
# otherwise it raises an exception
|
115
|
+
# @return [Hash] Hash representation of all package dependencies. Agent uses
|
116
|
+
# that to download package dependencies before compiling the package on a
|
117
|
+
# compilation VM.
|
118
|
+
def dependency_spec
|
119
|
+
spec = {}
|
120
|
+
|
121
|
+
@dependencies.each do |dep_task|
|
122
|
+
unless dep_task.compiled?
|
123
|
+
raise DirectorError,
|
124
|
+
"Cannot generate package dependency spec " +
|
125
|
+
"for `#{@package.name}', " +
|
126
|
+
"`#{dep_task.package.name}' hasn't been compiled yet"
|
127
|
+
end
|
128
|
+
|
129
|
+
compiled_package = dep_task.compiled_package
|
130
|
+
|
131
|
+
spec[compiled_package.name] = {
|
132
|
+
"name" => compiled_package.name,
|
133
|
+
"version" => "#{compiled_package.version}.#{compiled_package.build}",
|
134
|
+
"sha1" => compiled_package.sha1,
|
135
|
+
"blobstore_id" => compiled_package.blobstore_id
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
spec
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
def generate_dependency_key(packages)
|
144
|
+
key = packages.sort { |a, b|
|
145
|
+
a.name <=> b.name
|
146
|
+
}.map { |p| [p.name, p.version]}
|
147
|
+
|
148
|
+
Yajl::Encoder.encode(key)
|
149
|
+
end
|
150
|
+
|
151
|
+
def generate_cache_key(dependent_packages)
|
152
|
+
dependency_fingerprints = dependent_packages.sort_by(&:name).map {|p| p.fingerprint }
|
153
|
+
hash_input = ([package.fingerprint, stemcell.sha1]+dependency_fingerprints).join("")
|
154
|
+
Digest::SHA1.hexdigest(hash_input)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|