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,54 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ class NatsRpc
5
+
6
+ def initialize
7
+ @nats = Config.nats
8
+ @logger = Config.logger
9
+ @lock = Mutex.new
10
+ @inbox_name = "director.#{Config.process_uuid}"
11
+ @requests = {}
12
+ subscribe_inbox
13
+ end
14
+
15
+ def subscribe_inbox
16
+ @nats.subscribe("#{@inbox_name}.>") do |message, _, subject|
17
+ @logger.debug("RECEIVED: #{subject} #{message}")
18
+ begin
19
+ request_id = subject.split(".").last
20
+ callback = @lock.synchronize { @requests.delete(request_id) }
21
+ if callback
22
+ message = Yajl::Parser.new.parse(message)
23
+ callback.call(message)
24
+ end
25
+ rescue Exception => e
26
+ @logger.warn(e.message)
27
+ end
28
+ end
29
+ end
30
+
31
+ def send_request(client, request, &block)
32
+ request_id = generate_request_id
33
+ request["reply_to"] = "#{@inbox_name}.#{request_id}"
34
+ @lock.synchronize do
35
+ @requests[request_id] = block
36
+ end
37
+ message = Yajl::Encoder.encode(request)
38
+ @logger.debug("SENT: #{client} #{message}")
39
+ EM.next_tick do
40
+ @nats.publish(client, message)
41
+ end
42
+ request_id
43
+ end
44
+
45
+ def cancel_request(request_id)
46
+ @lock.synchronize { @requests.delete(request_id) }
47
+ end
48
+
49
+ def generate_request_id
50
+ SecureRandom.uuid
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,121 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ ##
5
+ # Network resolution, either existing or one to be fulfilled by {Network}
6
+ class NetworkReservation
7
+ include IpUtil
8
+
9
+ STATIC = :static
10
+ DYNAMIC = :dynamic
11
+
12
+ USED = :used
13
+ CAPACITY = :capacity
14
+ WRONG_TYPE = :wrong_type
15
+
16
+ # @return [Integer, nil] ip
17
+ attr_accessor :ip
18
+
19
+ # @return [Symbol, nil] type
20
+ attr_accessor :type
21
+
22
+ # @return [Boolean] reserved
23
+ attr_accessor :reserved
24
+
25
+ # @return [Symbol, nil] reservation error
26
+ attr_accessor :error
27
+
28
+ def self.new_dynamic(ip = nil)
29
+ new(:type => NetworkReservation::DYNAMIC, :ip => ip)
30
+ end
31
+
32
+ def self.new_static(ip = nil)
33
+ new(:type => NetworkReservation::STATIC, :ip => ip)
34
+ end
35
+
36
+ ##
37
+ # Creates a new network reservation
38
+ # @param [Hash] options the options to create the reservation from
39
+ # @option options [Integer, String, NetAddr::CIDR] :ip reservation ip
40
+ # @option options [Symbol] :type reservation type
41
+ def initialize(options = {})
42
+ @ip = options[:ip]
43
+ @type = options[:type]
44
+ @reserved = false
45
+ @error = nil
46
+
47
+ @ip = ip_to_i(@ip) if @ip
48
+ end
49
+
50
+ ##
51
+ # @return [Boolean] returns true if this is a static reservation
52
+ def static?
53
+ @type == STATIC
54
+ end
55
+
56
+ ##
57
+ # @return [Boolean] returns true if this is a dynamic reservation
58
+ def dynamic?
59
+ @type == DYNAMIC
60
+ end
61
+
62
+ ##
63
+ # @return [Boolean] returns true if this reservation was fulfilled
64
+ def reserved?
65
+ !!@reserved
66
+ end
67
+
68
+ ##
69
+ # Tries to take the provided reservation if it meets the requirements
70
+ # @return [void]
71
+ def take(other)
72
+ if other.reserved?
73
+ if @type == other.type
74
+ if dynamic? || (static? && @ip == other.ip)
75
+ @ip = other.ip
76
+ @reserved = true
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Handles network reservation error and re-raises the proper exception
84
+ # @param [String] origin Whoever tried to take the reservation
85
+ # @raise [NetworkReservationAlreadyInUse]
86
+ # @raise [NetworkReservationWrongType]
87
+ # @raise [NetworkReservationNotEnoughCapacity]
88
+ # @raise [NetworkReservationError]
89
+ # @return void
90
+ def handle_error(origin)
91
+ if static?
92
+ formatted_ip = ip_to_netaddr(@ip).ip
93
+ case @error
94
+ when NetworkReservation::USED
95
+ raise NetworkReservationAlreadyInUse,
96
+ "#{origin} asked for a static IP #{formatted_ip} " +
97
+ "but it's already reserved/in use"
98
+ when NetworkReservation::WRONG_TYPE
99
+ raise NetworkReservationWrongType,
100
+ "#{origin} asked for a static IP #{formatted_ip} " +
101
+ "but it's in the dynamic pool"
102
+ else
103
+ raise NetworkReservationError,
104
+ "#{origin} failed to reserve static IP " +
105
+ "#{formatted_ip}: #{@error}"
106
+ end
107
+ else
108
+ case @error
109
+ when NetworkReservation::CAPACITY
110
+ raise NetworkReservationNotEnoughCapacity,
111
+ "#{origin} asked for a dynamic IP " +
112
+ "but there were no more available"
113
+ else
114
+ raise NetworkReservationError,
115
+ "#{origin} failed to reserve dynamic IP " +
116
+ "#{formatted_ip}: #{@error}"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,20 @@
1
+ module Bosh::Director
2
+ class NextRebaseVersion
3
+ def initialize(existing_versions)
4
+ @existing_versions = existing_versions
5
+ end
6
+
7
+ def calculate(current_version)
8
+ current_version = Bosh::Common::VersionNumber.new(current_version)
9
+ versions = @existing_versions.map { |item| Bosh::Common::VersionNumber.new(item.version) }
10
+
11
+ return current_version.to_s if current_version.final?
12
+
13
+ latest = versions.select { |version|
14
+ version.major == current_version.major
15
+ }.max
16
+
17
+ latest ? latest.next_minor.dev.to_s : "#{current_version.major}.1-dev"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,423 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Director
4
+ class PackageCompiler
5
+ include LockHelper
6
+
7
+ attr_reader :compilations_performed
8
+
9
+ # @param [DeploymentPlan] deployment_plan Deployment plan
10
+ def initialize(deployment_plan)
11
+ @deployment_plan = deployment_plan
12
+
13
+ @cloud = Config.cloud
14
+ @event_log = Config.event_log
15
+ @logger = Config.logger
16
+ @director_job = Config.current_job
17
+
18
+ @tasks_mutex = Mutex.new
19
+ @network_mutex = Mutex.new
20
+ @counter_mutex = Mutex.new
21
+
22
+ compilation_config = @deployment_plan.compilation
23
+
24
+ @network = compilation_config.network
25
+ @compilation_resources = compilation_config.cloud_properties
26
+ @compilation_env = compilation_config.env
27
+
28
+ @vm_reuser = VmReuser.new
29
+
30
+ @compile_tasks = {}
31
+ @ready_tasks = []
32
+ @compilations_performed = 0
33
+ end
34
+
35
+ # @return [Integer] How many compile tasks are present
36
+ def compile_tasks_count
37
+ @compile_tasks.size
38
+ end
39
+
40
+ # @return [Integer] How many compile tasks are ready
41
+ def ready_tasks_count
42
+ @tasks_mutex.synchronize { @ready_tasks.size }
43
+ end
44
+
45
+ # Generates compilation tasks for all packages in all job templates included
46
+ # in the current deployment and kicks off compilation.
47
+ # @return [void]
48
+ def compile
49
+ @logger.info("Generating a list of compile tasks")
50
+ prepare_tasks
51
+
52
+ @compile_tasks.each_value do |task|
53
+ if task.ready_to_compile?
54
+ @logger.info("Package `#{task.package.desc}' is ready to be " +
55
+ "compiled for stemcell `#{task.stemcell.desc}'")
56
+ @ready_tasks << task
57
+ end
58
+ end
59
+
60
+ if @ready_tasks.empty?
61
+ @logger.info("All packages are already compiled")
62
+ else
63
+ compile_packages
64
+ director_job_checkpoint
65
+ end
66
+ end
67
+
68
+ # Generates all compilation tasks required to compile all packages
69
+ # in the jobs defined by deployment plan
70
+ # @return [void]
71
+ def prepare_tasks
72
+ @event_log.begin_stage("Preparing package compilation")
73
+
74
+ @event_log.track("Finding packages to compile") do
75
+ @deployment_plan.jobs.each do |job|
76
+ job_desc = "#{job.release.name}/#{job.name}"
77
+ stemcell = job.resource_pool.stemcell
78
+
79
+ @logger.info("Job `#{job_desc}' needs to run " +
80
+ "on stemcell `#{stemcell.model.desc}'")
81
+
82
+ job.templates.each do |template|
83
+ template.package_models.each do |package|
84
+ generate_compile_task(job, package, stemcell.model)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ # Generates compilation task for a given (package, stemcell) tuple
92
+ # @param [DeploymentPlan::Job] job Job spec
93
+ # @param [Models::Package] package Package model
94
+ # @param [Models::Stemcell] stemcell Stemcell model
95
+ # @return [CompileTask] Compilation task for this package/stemcell tuple
96
+ def generate_compile_task(job, package, stemcell)
97
+ # Our assumption here is that package dependency graph
98
+ # has no cycles: this is being enforced on release upload.
99
+ # Other than that it's a vanilla DFS.
100
+
101
+ @logger.info("Checking whether package `#{package.desc}' needs " +
102
+ "to be compiled for stemcell `#{stemcell.desc}'")
103
+ task_key = [package.id, stemcell.id]
104
+ task = @compile_tasks[task_key]
105
+
106
+ if task # We already visited this task and its dependencies
107
+ task.add_job(job) # But we still need to register this job with task
108
+ return task
109
+ end
110
+
111
+ dependencies = package.dependency_set.map do |name|
112
+ job.release.get_package_model_by_name(name)
113
+ end
114
+
115
+ task = CompileTask.new(package, stemcell, dependencies, job)
116
+
117
+ compiled_package = find_compiled_package(task)
118
+ if compiled_package
119
+ task.use_compiled_package(compiled_package)
120
+ end
121
+
122
+ @logger.info("Processing package `#{package.desc}' dependencies")
123
+ dependencies.each do |dependency|
124
+ @logger.info("Package `#{package.desc}' depends on " +
125
+ "package `#{dependency.desc}'")
126
+ dependency_task = generate_compile_task(job, dependency, stemcell)
127
+ task.add_dependency(dependency_task)
128
+ end
129
+
130
+ @compile_tasks[task_key] = task
131
+ task
132
+ end
133
+
134
+ def reserve_network
135
+ reservation = NetworkReservation.new_dynamic
136
+
137
+ @network_mutex.synchronize do
138
+ @network.reserve(reservation)
139
+ end
140
+
141
+ if !reservation.reserved?
142
+ raise PackageCompilationNetworkNotReserved,
143
+ "Could not reserve network for package compilation: " +
144
+ reservation.error.to_s
145
+ end
146
+
147
+ reservation
148
+ end
149
+
150
+ def release_network(reservation)
151
+ @network_mutex.synchronize do
152
+ @network.release(reservation)
153
+ end
154
+ end
155
+
156
+ def compile_packages
157
+ @event_log.begin_stage("Compiling packages", compilation_count)
158
+ number_of_workers = @deployment_plan.compilation.workers
159
+
160
+ begin
161
+ ThreadPool.new(:max_threads => number_of_workers).wrap do |pool|
162
+ loop do
163
+ # process as many tasks without waiting
164
+ loop do
165
+ break if director_job_cancelled?
166
+ task = @tasks_mutex.synchronize { @ready_tasks.pop }
167
+ break if task.nil?
168
+
169
+ pool.process { process_task(task) }
170
+ end
171
+
172
+ break if !pool.working? && @ready_tasks.empty?
173
+ sleep(0.1)
174
+ end
175
+ end
176
+ ensure
177
+ # Delete all of the VMs if we were reusing compilation VMs. This can't
178
+ # happen until everything was done compiling.
179
+ if @deployment_plan.compilation.reuse_compilation_vms
180
+ # Using a new ThreadPool instead of reusing the previous one,
181
+ # as if there's a failed compilation, the thread pool will stop
182
+ # processing any new thread.
183
+ ThreadPool.new(:max_threads => number_of_workers).wrap do |pool|
184
+ @vm_reuser.each do |vm_data|
185
+ pool.process { tear_down_vm(vm_data) }
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ def process_task(task)
193
+ package_desc = task.package.desc
194
+ stemcell_desc = task.stemcell.desc
195
+ task_desc = "package `#{package_desc}' for stemcell `#{stemcell_desc}'"
196
+
197
+ with_thread_name("compile_package(#{package_desc}, #{stemcell_desc})") do
198
+ if director_job_cancelled?
199
+ @logger.info("Cancelled compiling #{task_desc}")
200
+ else
201
+ @event_log.track(package_desc) do
202
+ @logger.info("Compiling #{task_desc}")
203
+ compile_package(task)
204
+ @logger.info("Finished compiling #{task_desc}")
205
+ enqueue_unblocked_tasks(task)
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ def compile_package(task)
212
+ package = task.package
213
+ stemcell = task.stemcell
214
+
215
+ with_compile_lock(package.id, stemcell.id) do
216
+ # Check if the package was compiled in a parallel deployment
217
+ compiled_package = find_compiled_package(task)
218
+ if compiled_package.nil?
219
+ build = Models::CompiledPackage.generate_build_number(package, stemcell)
220
+ task_result = nil
221
+
222
+ prepare_vm(stemcell) do |vm_data|
223
+ vm_metadata_updater.update(vm_data.vm, :compiling => package.name)
224
+ agent_task =
225
+ vm_data.agent.compile_package(package.blobstore_id,
226
+ package.sha1, package.name,
227
+ "#{package.version}.#{build}",
228
+ task.dependency_spec)
229
+ task_result = agent_task["result"]
230
+ end
231
+
232
+ compiled_package = Models::CompiledPackage.create do |p|
233
+ p.package = package
234
+ p.stemcell = stemcell
235
+ p.sha1 = task_result["sha1"]
236
+ p.build = build
237
+ p.blobstore_id = task_result["blobstore_id"]
238
+ p.dependency_key = task.dependency_key
239
+ end
240
+
241
+ if Config.use_compiled_package_cache?
242
+ if BlobUtil.exists_in_global_cache?(package, task.cache_key)
243
+ @logger.info("Already exists in global package cache, skipping upload")
244
+ else
245
+ @logger.info("Uploading to global package cache")
246
+ BlobUtil.save_to_global_cache(compiled_package, task.cache_key)
247
+ end
248
+ else
249
+ @logger.info("Global blobstore not configured, skipping upload")
250
+ end
251
+
252
+ @counter_mutex.synchronize { @compilations_performed += 1 }
253
+ end
254
+
255
+ task.use_compiled_package(compiled_package)
256
+ end
257
+ end
258
+
259
+ def enqueue_unblocked_tasks(task)
260
+ @tasks_mutex.synchronize do
261
+ @logger.info("Unblocking dependents of " +
262
+ "`#{task.package.desc}` for `#{task.stemcell.desc}`")
263
+ task.dependent_tasks.each do |dep_task|
264
+ if dep_task.ready_to_compile?
265
+ @logger.info("Package `#{dep_task.package.desc}' now ready to be " +
266
+ "compiled for `#{dep_task.stemcell.desc}'")
267
+ @ready_tasks << dep_task
268
+ end
269
+ end
270
+ end
271
+ end
272
+
273
+ # This method will create a VM for each stemcell in the stemcells array
274
+ # passed in. The VMs are yielded and their destruction is ensured.
275
+ # @param [Models::Stemcell] stemcell The stemcells that need to have
276
+ # compilation VMs created.
277
+ # @yield [VmData] Yields a VmData object that contains all the data for the
278
+ # VM that should be used for compilation. This may be a reused VM or a
279
+ # freshly created VM.
280
+ def prepare_vm(stemcell)
281
+ # If we're reusing VMs, try to just return an already-created VM.
282
+ if @deployment_plan.compilation.reuse_compilation_vms
283
+ vm_data = @vm_reuser.get_vm(stemcell)
284
+ if vm_data
285
+ @logger.info("Reusing compilation VM `#{vm_data.vm.cid}' for " +
286
+ "stemcell `#{stemcell.desc}'")
287
+ begin
288
+ yield vm_data
289
+ ensure
290
+ vm_data.release
291
+ end
292
+ return
293
+ end
294
+ # This shouldn't happen. If it does there's a bug.
295
+ if @vm_reuser.get_num_vms(stemcell) >=
296
+ @deployment_plan.compilation.workers
297
+ raise PackageCompilationNotEnoughWorkersForReuse,
298
+ "There should never be more VMs for a stemcell than the " +
299
+ "number of workers in reuse_compilation_vms mode"
300
+ end
301
+ end
302
+
303
+ @logger.info("Creating compilation VM for stemcell `#{stemcell.desc}'")
304
+
305
+ reservation = reserve_network
306
+
307
+ network_settings = {
308
+ @network.name => @network.network_settings(reservation)
309
+ }
310
+
311
+ vm = VmCreator.create(@deployment_plan.model, stemcell,
312
+ @compilation_resources, network_settings,
313
+ nil, @compilation_env)
314
+ vm_data = @vm_reuser.add_vm(reservation, vm, stemcell, network_settings)
315
+
316
+ @logger.info("Configuring compilation VM: #{vm.cid}")
317
+
318
+ begin
319
+ agent = AgentClient.new(vm.agent_id)
320
+ agent.wait_until_ready
321
+
322
+ configure_vm(vm, agent, network_settings)
323
+ vm_data.agent = agent
324
+ yield vm_data
325
+ rescue RpcTimeout => e
326
+ # if we time out waiting for the agent, we should clean up the the VM
327
+ # as it will leave us in an unrecoverable state otherwise
328
+ @vm_reuser.remove_vm(vm_data)
329
+ tear_down_vm(vm_data)
330
+ raise e
331
+ ensure
332
+ vm_data.release
333
+ unless @deployment_plan.compilation.reuse_compilation_vms
334
+ tear_down_vm(vm_data)
335
+ end
336
+ end
337
+ end
338
+
339
+ # Tears down a VM and releases the network reservations.
340
+ # @param [VmData] vm_data The VmData object for the VM to tear down.
341
+ def tear_down_vm(vm_data)
342
+ vm = vm_data.vm
343
+ if vm.exists?
344
+ reservation = vm_data.reservation
345
+ @logger.info("Deleting compilation VM: #{vm.cid}")
346
+ @cloud.delete_vm(vm.cid)
347
+ vm.destroy
348
+ release_network(reservation)
349
+ end
350
+ end
351
+
352
+ # @param [CompileTask] task
353
+ # @return [Models::CompiledPackage]
354
+ def find_compiled_package(task)
355
+ package = task.package
356
+ stemcell = task.stemcell
357
+ dependency_key = task.dependency_key
358
+
359
+ # Check if this package is already compiled
360
+ compiled_package = Models::CompiledPackage[
361
+ :package_id => package.id,
362
+ :stemcell_id => stemcell.id,
363
+ :dependency_key => dependency_key
364
+ ]
365
+ if compiled_package
366
+ @logger.info("Found compiled version of package `#{package.desc}' " +
367
+ "for stemcell `#{stemcell.desc}'")
368
+ else
369
+ if Config.use_compiled_package_cache?
370
+ if BlobUtil.exists_in_global_cache?(package, task.cache_key)
371
+ @event_log.track("Downloading '#{package.desc}' from global cache") do
372
+ # has side effect of putting CompiledPackage model in db
373
+ compiled_package = BlobUtil.fetch_from_global_cache(package, stemcell, task.cache_key, dependency_key)
374
+ end
375
+ end
376
+ end
377
+
378
+ if compiled_package
379
+ @logger.info("Package `Found compiled version of package `#{package.desc}'" +
380
+ "for stemcell `#{stemcell.desc}' in global cache")
381
+ else
382
+ @logger.info("Package `#{package.desc}' " +
383
+ "needs to be compiled on `#{stemcell.desc}'")
384
+ end
385
+ end
386
+
387
+ compiled_package
388
+ end
389
+
390
+ def director_job_cancelled?
391
+ @director_job && @director_job.task_cancelled?
392
+ end
393
+
394
+ def director_job_checkpoint
395
+ @director_job.task_checkpoint if @director_job
396
+ end
397
+
398
+ def configure_vm(vm, agent, network_settings)
399
+ state = {
400
+ "deployment" => @deployment_plan.name,
401
+ "resource_pool" => "package_compiler",
402
+ "networks" => network_settings
403
+ }
404
+
405
+ vm.update(:apply_spec => state)
406
+ agent.apply(state)
407
+ end
408
+
409
+ def compilation_count
410
+ counter = 0
411
+ @compile_tasks.each_value do |task|
412
+ counter += 1 unless task.compiled?
413
+ end
414
+ counter
415
+ end
416
+
417
+ private
418
+
419
+ def vm_metadata_updater
420
+ @vm_metadata_updater ||= VmMetadataUpdater.build
421
+ end
422
+ end
423
+ end