bosh-director 1.5.0.pre.1113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. data/CHANGELOG +34 -0
  2. data/bin/bosh-director +36 -0
  3. data/bin/bosh-director-console +84 -0
  4. data/bin/bosh-director-drain-workers +42 -0
  5. data/bin/bosh-director-migrate +58 -0
  6. data/bin/bosh-director-scheduler +27 -0
  7. data/bin/bosh-director-worker +76 -0
  8. data/db/migrations/README +1 -0
  9. data/db/migrations/director/20110209010747_initial.rb +118 -0
  10. data/db/migrations/director/20110406055800_add_task_user.rb +9 -0
  11. data/db/migrations/director/20110518225809_remove_cid_constrain.rb +13 -0
  12. data/db/migrations/director/20110617211923_add_deployments_release_versions.rb +32 -0
  13. data/db/migrations/director/20110622212607_add_task_checkpoint_timestamp.rb +9 -0
  14. data/db/migrations/director/20110628023039_add_state_to_instances.rb +21 -0
  15. data/db/migrations/director/20110709012332_add_disk_size_to_instances.rb +9 -0
  16. data/db/migrations/director/20110906183441_add_log_bundles.rb +11 -0
  17. data/db/migrations/director/20110907194830_add_logs_json_to_templates.rb +9 -0
  18. data/db/migrations/director/20110915205610_add_persistent_disks.rb +51 -0
  19. data/db/migrations/director/20111005180929_add_properties.rb +14 -0
  20. data/db/migrations/director/20111110024617_add_deployment_problems.rb +24 -0
  21. data/db/migrations/director/20111216214145_recreate_support_for_vms.rb +9 -0
  22. data/db/migrations/director/20120102084027_add_credentials_to_vms.rb +7 -0
  23. data/db/migrations/director/20120427235217_allow_multiple_releases_per_deployment.rb +36 -0
  24. data/db/migrations/director/20120524175805_add_task_type.rb +44 -0
  25. data/db/migrations/director/20120614001930_delete_redundant_deployment_release_relation.rb +34 -0
  26. data/db/migrations/director/20120822004528_add_fingerprint_to_templates_and_packages.rb +17 -0
  27. data/db/migrations/director/20120830191244_add_properties_to_templates.rb +9 -0
  28. data/db/migrations/director/20121106190739_persist_vm_env.rb +9 -0
  29. data/db/migrations/director/20130222232131_add_sha1_to_stemcells.rb +9 -0
  30. data/db/migrations/director/20130312211407_add_commit_hash_to_release_versions.rb +19 -0
  31. data/db/migrations/director/20130409235338_snapshot.rb +15 -0
  32. data/db/migrations/director/20130530164918_add_paused_flag_to_instance.rb +14 -0
  33. data/db/migrations/director/20130531172604_add_director_attributes.rb +13 -0
  34. data/db/migrations/dns/20120123234908_initial.rb +27 -0
  35. data/lib/bosh/director.rb +133 -0
  36. data/lib/bosh/director/agent_client.rb +78 -0
  37. data/lib/bosh/director/api.rb +29 -0
  38. data/lib/bosh/director/api/api_helper.rb +81 -0
  39. data/lib/bosh/director/api/backup_manager.rb +15 -0
  40. data/lib/bosh/director/api/controller.rb +639 -0
  41. data/lib/bosh/director/api/controller_helpers.rb +34 -0
  42. data/lib/bosh/director/api/deployment_lookup.rb +13 -0
  43. data/lib/bosh/director/api/deployment_manager.rb +60 -0
  44. data/lib/bosh/director/api/http_constants.rb +16 -0
  45. data/lib/bosh/director/api/instance_lookup.rb +44 -0
  46. data/lib/bosh/director/api/instance_manager.rb +63 -0
  47. data/lib/bosh/director/api/problem_manager.rb +40 -0
  48. data/lib/bosh/director/api/property_manager.rb +69 -0
  49. data/lib/bosh/director/api/release_manager.rb +59 -0
  50. data/lib/bosh/director/api/resource_manager.rb +69 -0
  51. data/lib/bosh/director/api/resurrector_manager.rb +15 -0
  52. data/lib/bosh/director/api/snapshot_manager.rb +94 -0
  53. data/lib/bosh/director/api/stemcell_manager.rb +50 -0
  54. data/lib/bosh/director/api/task_helper.rb +46 -0
  55. data/lib/bosh/director/api/task_manager.rb +64 -0
  56. data/lib/bosh/director/api/user_manager.rb +72 -0
  57. data/lib/bosh/director/api/vm_state_manager.rb +11 -0
  58. data/lib/bosh/director/app.rb +35 -0
  59. data/lib/bosh/director/blob_util.rb +87 -0
  60. data/lib/bosh/director/blobstores.rb +29 -0
  61. data/lib/bosh/director/client.rb +156 -0
  62. data/lib/bosh/director/cloudcheck_helper.rb +204 -0
  63. data/lib/bosh/director/compile_task.rb +157 -0
  64. data/lib/bosh/director/config.rb +370 -0
  65. data/lib/bosh/director/configuration_hasher.rb +114 -0
  66. data/lib/bosh/director/cycle_helper.rb +36 -0
  67. data/lib/bosh/director/db_backup.rb +22 -0
  68. data/lib/bosh/director/db_backup/adapter.rb +3 -0
  69. data/lib/bosh/director/db_backup/adapter/mysql2.rb +27 -0
  70. data/lib/bosh/director/db_backup/adapter/postgres.rb +36 -0
  71. data/lib/bosh/director/db_backup/adapter/sqlite.rb +17 -0
  72. data/lib/bosh/director/db_backup/error.rb +10 -0
  73. data/lib/bosh/director/deployment_plan.rb +26 -0
  74. data/lib/bosh/director/deployment_plan/assembler.rb +430 -0
  75. data/lib/bosh/director/deployment_plan/compilation_config.rb +54 -0
  76. data/lib/bosh/director/deployment_plan/compiled_package.rb +35 -0
  77. data/lib/bosh/director/deployment_plan/dynamic_network.rb +91 -0
  78. data/lib/bosh/director/deployment_plan/idle_vm.rb +109 -0
  79. data/lib/bosh/director/deployment_plan/instance.rb +413 -0
  80. data/lib/bosh/director/deployment_plan/job.rb +470 -0
  81. data/lib/bosh/director/deployment_plan/manual_network.rb +137 -0
  82. data/lib/bosh/director/deployment_plan/network.rb +74 -0
  83. data/lib/bosh/director/deployment_plan/network_subnet.rb +167 -0
  84. data/lib/bosh/director/deployment_plan/planner.rb +288 -0
  85. data/lib/bosh/director/deployment_plan/preparer.rb +52 -0
  86. data/lib/bosh/director/deployment_plan/release.rb +126 -0
  87. data/lib/bosh/director/deployment_plan/resource_pool.rb +143 -0
  88. data/lib/bosh/director/deployment_plan/resource_pools.rb +68 -0
  89. data/lib/bosh/director/deployment_plan/stemcell.rb +56 -0
  90. data/lib/bosh/director/deployment_plan/template.rb +94 -0
  91. data/lib/bosh/director/deployment_plan/update_config.rb +80 -0
  92. data/lib/bosh/director/deployment_plan/updater.rb +55 -0
  93. data/lib/bosh/director/deployment_plan/vip_network.rb +79 -0
  94. data/lib/bosh/director/dns_helper.rb +204 -0
  95. data/lib/bosh/director/download_helper.rb +44 -0
  96. data/lib/bosh/director/duration.rb +36 -0
  97. data/lib/bosh/director/encryption_helper.rb +10 -0
  98. data/lib/bosh/director/errors.rb +198 -0
  99. data/lib/bosh/director/event_log.rb +136 -0
  100. data/lib/bosh/director/ext.rb +64 -0
  101. data/lib/bosh/director/hash_string_vals.rb +13 -0
  102. data/lib/bosh/director/instance_deleter.rb +109 -0
  103. data/lib/bosh/director/instance_updater.rb +506 -0
  104. data/lib/bosh/director/ip_util.rb +67 -0
  105. data/lib/bosh/director/job_queue.rb +16 -0
  106. data/lib/bosh/director/job_runner.rb +162 -0
  107. data/lib/bosh/director/job_updater.rb +121 -0
  108. data/lib/bosh/director/jobs/backup.rb +86 -0
  109. data/lib/bosh/director/jobs/base_job.rb +66 -0
  110. data/lib/bosh/director/jobs/cloud_check/apply_resolutions.rb +46 -0
  111. data/lib/bosh/director/jobs/cloud_check/scan.rb +38 -0
  112. data/lib/bosh/director/jobs/cloud_check/scan_and_fix.rb +73 -0
  113. data/lib/bosh/director/jobs/create_snapshot.rb +23 -0
  114. data/lib/bosh/director/jobs/delete_deployment.rb +183 -0
  115. data/lib/bosh/director/jobs/delete_deployment_snapshots.rb +34 -0
  116. data/lib/bosh/director/jobs/delete_release.rb +219 -0
  117. data/lib/bosh/director/jobs/delete_snapshots.rb +23 -0
  118. data/lib/bosh/director/jobs/delete_stemcell.rb +102 -0
  119. data/lib/bosh/director/jobs/fetch_logs.rb +99 -0
  120. data/lib/bosh/director/jobs/scheduled_backup.rb +38 -0
  121. data/lib/bosh/director/jobs/snapshot_deployment.rb +61 -0
  122. data/lib/bosh/director/jobs/snapshot_deployments.rb +23 -0
  123. data/lib/bosh/director/jobs/snapshot_self.rb +43 -0
  124. data/lib/bosh/director/jobs/ssh.rb +59 -0
  125. data/lib/bosh/director/jobs/update_deployment.rb +110 -0
  126. data/lib/bosh/director/jobs/update_release.rb +672 -0
  127. data/lib/bosh/director/jobs/update_stemcell.rb +109 -0
  128. data/lib/bosh/director/jobs/vm_state.rb +89 -0
  129. data/lib/bosh/director/lock.rb +133 -0
  130. data/lib/bosh/director/lock_helper.rb +92 -0
  131. data/lib/bosh/director/models.rb +29 -0
  132. data/lib/bosh/director/models/compiled_package.rb +33 -0
  133. data/lib/bosh/director/models/deployment.rb +22 -0
  134. data/lib/bosh/director/models/deployment_problem.rb +49 -0
  135. data/lib/bosh/director/models/deployment_property.rb +21 -0
  136. data/lib/bosh/director/models/director_attribute.rb +9 -0
  137. data/lib/bosh/director/models/dns.rb +9 -0
  138. data/lib/bosh/director/models/dns/domain.rb +9 -0
  139. data/lib/bosh/director/models/dns/record.rb +7 -0
  140. data/lib/bosh/director/models/helpers/model_helper.rb +7 -0
  141. data/lib/bosh/director/models/instance.rb +28 -0
  142. data/lib/bosh/director/models/log_bundle.rb +10 -0
  143. data/lib/bosh/director/models/package.rb +30 -0
  144. data/lib/bosh/director/models/persistent_disk.rb +13 -0
  145. data/lib/bosh/director/models/release.rb +17 -0
  146. data/lib/bosh/director/models/release_version.rb +16 -0
  147. data/lib/bosh/director/models/snapshot.rb +13 -0
  148. data/lib/bosh/director/models/stemcell.rb +18 -0
  149. data/lib/bosh/director/models/task.rb +10 -0
  150. data/lib/bosh/director/models/template.rb +44 -0
  151. data/lib/bosh/director/models/user.rb +11 -0
  152. data/lib/bosh/director/models/vm.rb +42 -0
  153. data/lib/bosh/director/nats_rpc.rb +54 -0
  154. data/lib/bosh/director/network_reservation.rb +121 -0
  155. data/lib/bosh/director/next_rebase_version.rb +20 -0
  156. data/lib/bosh/director/package_compiler.rb +423 -0
  157. data/lib/bosh/director/problem_handlers/base.rb +153 -0
  158. data/lib/bosh/director/problem_handlers/inactive_disk.rb +112 -0
  159. data/lib/bosh/director/problem_handlers/invalid_problem.rb +28 -0
  160. data/lib/bosh/director/problem_handlers/missing_vm.rb +34 -0
  161. data/lib/bosh/director/problem_handlers/mount_info_mismatch.rb +62 -0
  162. data/lib/bosh/director/problem_handlers/out_of_sync_vm.rb +64 -0
  163. data/lib/bosh/director/problem_handlers/unbound_instance_vm.rb +85 -0
  164. data/lib/bosh/director/problem_handlers/unresponsive_agent.rb +78 -0
  165. data/lib/bosh/director/problem_resolver.rb +103 -0
  166. data/lib/bosh/director/problem_scanner.rb +268 -0
  167. data/lib/bosh/director/resource_pool_updater.rb +216 -0
  168. data/lib/bosh/director/scheduler.rb +57 -0
  169. data/lib/bosh/director/sequel.rb +13 -0
  170. data/lib/bosh/director/tar_gzipper.rb +47 -0
  171. data/lib/bosh/director/task_result_file.rb +19 -0
  172. data/lib/bosh/director/thread_pool.rb +8 -0
  173. data/lib/bosh/director/validation_helper.rb +55 -0
  174. data/lib/bosh/director/version.rb +7 -0
  175. data/lib/bosh/director/vm_creator.rb +80 -0
  176. data/lib/bosh/director/vm_data.rb +63 -0
  177. data/lib/bosh/director/vm_metadata_updater.rb +29 -0
  178. data/lib/bosh/director/vm_reuser.rb +63 -0
  179. data/lib/cloud/dummy.rb +149 -0
  180. 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