bosh-director 1.3178.0 → 1.3181.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 50418ce75a28cf747cafa90bd0233596b8d2fe1c
4
- data.tar.gz: c4866efc86924bd8295b6707f97ebcd41ee36331
3
+ metadata.gz: c97780b3dff8eed73c5d5be903e89db7379d5783
4
+ data.tar.gz: b44b5e4a56222f11ebbd158edd38a4ee12d931c8
5
5
  SHA512:
6
- metadata.gz: dabe39be4831b8f56640c70904dfc40b834d81e0fb8d28280654a411613a88402913385382a719b6f217d5938f1e6018313344669362f59e8add41a2104c6395
7
- data.tar.gz: 855d438e111d8bc3412ebf9b9a5a52741a623c26061886a38ba3d80f9e0279713e78355f04b9151f91eff409b95fe1b61b5ef1919746afbdc514c5e5b6f4488e
6
+ metadata.gz: 9906d59e3d0f2b08d4dad7c5bc3de47fb01dc528506a7b8d662cb0b0c4e6724dbb2c5a90871d893bef2f8cbbd8c560545c9b6149e69753b7510a4a6ee9b846d2
7
+ data.tar.gz: 6c32ef7e74cb561aec71c44bd28ddb66bb178bca559cf9c092417bc18bece0d008b62c3bb67f49bb523cb01efa34d345fb35be0fcced1e753cf48fc10d280c02
data/lib/bosh/director.rb CHANGED
@@ -92,6 +92,10 @@ require 'bosh/director/transactor'
92
92
  require 'bosh/director/sequel'
93
93
  require 'common/thread_pool'
94
94
 
95
+ require 'bosh/director/manifest/manifest'
96
+ require 'bosh/director/manifest/changeset'
97
+ require 'bosh/director/manifest/diff_lines'
98
+
95
99
  require 'bosh/director/log_bundles_cleaner'
96
100
  require 'bosh/director/logs_fetcher'
97
101
 
@@ -18,6 +18,10 @@ module Bosh
18
18
  list(1).first
19
19
  end
20
20
 
21
+ def find_by_id(id)
22
+ Bosh::Director::Models::CloudConfig.find(id: id)
23
+ end
24
+
21
25
  private
22
26
 
23
27
  def validate_manifest!(cloud_config)
@@ -205,7 +205,7 @@ module Bosh::Director
205
205
 
206
206
  post '/:deployment/properties', :consumes => [:json] do
207
207
  payload = json_decode(request.body)
208
- @property_manager.create_property(params[:deployment], payload['name'], payload['value'])
208
+ @property_manager.create_property(params[:deployment], payload['name'], payload['value'] )
209
209
  status(204)
210
210
  end
211
211
 
@@ -267,12 +267,44 @@ module Bosh::Director
267
267
  options = {}
268
268
  options['recreate'] = true if params['recreate'] == 'true'
269
269
  options['skip_drain'] = params['skip_drain'] if params['skip_drain']
270
- latest_cloud_config = Bosh::Director::Api::CloudConfigManager.new.latest
270
+ if params['context']
271
+ @logger.debug("Deploying with context #{params['context']}")
272
+ context = JSON.parse(params['context'])
273
+ cloud_config = Api::CloudConfigManager.new.find_by_id(context['cloud_config_id'])
274
+ else
275
+ cloud_config =Api::CloudConfigManager.new.latest
276
+ end
271
277
 
272
- task = @deployment_manager.create_deployment(current_user, request.body, latest_cloud_config, options)
278
+ task = @deployment_manager.create_deployment(current_user, request.body, cloud_config, options)
273
279
  redirect "/tasks/#{task.id}"
274
280
  end
275
281
 
282
+ post '/:deployment/diff', :consumes => :yaml do
283
+ deployment = Models::Deployment[name: params[:deployment]]
284
+ if deployment
285
+ before_manifest = Manifest.load_from_text(deployment.manifest, deployment.cloud_config)
286
+ before_manifest.resolve_aliases
287
+ else
288
+ before_manifest = Manifest.load_from_text(nil, nil)
289
+ end
290
+
291
+ after_cloud_config = Bosh::Director::Api::CloudConfigManager.new.latest
292
+ after_manifest = Manifest.load_from_text(
293
+ request.body,
294
+ after_cloud_config
295
+ )
296
+ after_manifest.resolve_aliases
297
+
298
+ diff = before_manifest.diff(after_manifest)
299
+
300
+ json_encode({
301
+ 'context' => {
302
+ 'cloud_config_id' => after_cloud_config ? after_cloud_config.id : nil,
303
+ },
304
+ 'diff' => diff.map { |l| [l.to_s, l.status] }
305
+ })
306
+ end
307
+
276
308
  post '/:deployment_name/errands/:errand_name/runs' do
277
309
  deployment_name = params[:deployment_name]
278
310
  errand_name = params[:errand_name]
@@ -5,23 +5,7 @@ module Bosh::Director
5
5
 
6
6
  def get_all_releases
7
7
  releases = Models::Release.order_by(:name.asc).map do |release|
8
- sorted_version_tuples = release.versions_dataset.all.map do |version|
9
- {
10
- provided: version,
11
- parsed: Bosh::Common::Version::ReleaseVersion.parse(version.values[:version])
12
- }
13
- end.sort_by { |rv| rv[:parsed] }
14
- release_versions = sorted_version_tuples.map do |version|
15
- provided = version[:provided]
16
- {
17
- 'version' => provided.version.to_s,
18
- 'commit_hash' => provided.commit_hash,
19
- 'uncommitted_changes' => provided.uncommitted_changes,
20
- 'currently_deployed' => !provided.deployments.empty?,
21
- 'job_names' => provided.templates.map(&:name),
22
- }
23
- end
24
-
8
+ release_versions = sorted_release_versions(release)
25
9
  {
26
10
  'name' => release.name,
27
11
  'release_versions' => release_versions
@@ -31,6 +15,27 @@ module Bosh::Director
31
15
  releases
32
16
  end
33
17
 
18
+ def sorted_release_versions(release)
19
+ sorted_version_tuples = release.versions_dataset.all.map do |version|
20
+ {
21
+ provided: version,
22
+ parsed: Bosh::Common::Version::ReleaseVersion.parse(version.values[:version])
23
+ }
24
+ end.sort_by { |rv| rv[:parsed] }
25
+ release_versions = sorted_version_tuples.map do |version|
26
+ provided = version[:provided]
27
+ {
28
+ 'version' => provided.version.to_s,
29
+ 'commit_hash' => provided.commit_hash,
30
+ 'uncommitted_changes' => provided.uncommitted_changes,
31
+ 'currently_deployed' => !provided.deployments.empty?,
32
+ 'job_names' => provided.templates.map(&:name),
33
+ }
34
+ end
35
+
36
+ release_versions
37
+ end
38
+
34
39
  def find_by_name(name)
35
40
  release = Models::Release[:name => name]
36
41
  if release.nil?
@@ -303,6 +303,25 @@ module Bosh::Director
303
303
  Thread.current[:bosh] ||= {}
304
304
  end
305
305
 
306
+ def generate_temp_dir
307
+ temp_dir = Dir.mktmpdir
308
+ ENV["TMPDIR"] = temp_dir
309
+ FileUtils.mkdir_p(temp_dir)
310
+ at_exit do
311
+ begin
312
+ if $!
313
+ status = $!.is_a?(::SystemExit) ? $!.status : 1
314
+ else
315
+ status = 0
316
+ end
317
+ FileUtils.rm_rf(temp_dir)
318
+ ensure
319
+ exit status
320
+ end
321
+ end
322
+ temp_dir
323
+ end
324
+
306
325
  def patch_sqlite
307
326
  return if @patched_sqlite
308
327
  @patched_sqlite = true
@@ -26,7 +26,12 @@ module Bosh::Director
26
26
  rescue => e
27
27
  unless instance.nil? || instance_plan.nil?
28
28
  @instance_reuser.remove_instance(instance)
29
- delete_instance(instance_plan)
29
+
30
+ if Config.keep_unreachable_vms
31
+ @logger.info('Keeping reused compilation VM for debugging')
32
+ else
33
+ delete_instance(instance_plan)
34
+ end
30
35
  end
31
36
  raise e
32
37
  end
@@ -34,11 +39,18 @@ module Bosh::Director
34
39
 
35
40
  def with_single_use_vm(stemcell)
36
41
  begin
42
+ keep_failing_vm = false
37
43
  instance_plan, instance = create_instance_plan(stemcell)
38
44
  configure_instance_plan(instance_plan)
39
45
  yield instance
46
+ rescue => e
47
+ @logger.info('Keeping single-use compilation VM for debugging')
48
+ keep_failing_vm = Config.keep_unreachable_vms
49
+ raise e
40
50
  ensure
41
- delete_instance(instance_plan) unless instance.nil?
51
+ unless instance.nil? || keep_failing_vm
52
+ delete_instance(instance_plan)
53
+ end
42
54
  end
43
55
  end
44
56
 
@@ -29,9 +29,9 @@ module Bosh::Director::DeploymentPlan
29
29
  end
30
30
 
31
31
  def allocate_dynamic_ip(reservation, subnet)
32
- item = (0...subnet.range.size).find { |i| available_for_dynamic?(subnet.range[i], subnet) }
33
-
34
32
  @mutex.synchronize do
33
+ item = (0...subnet.range.size).find { |i| available_for_dynamic?(subnet.range[i], subnet) }
34
+
35
35
  if item.nil?
36
36
  entry = @recently_released_ips.find do |entry|
37
37
  entry[:network_name] == subnet.network_name && subnet.range.contains?(entry[:ip])
@@ -72,11 +72,8 @@ module Bosh::Director::DeploymentPlan
72
72
  return false unless subnet.range.contains?(ip)
73
73
  return false if subnet.static_ips.include?(ip.to_i)
74
74
  return false if subnet.restricted_ips.include?(ip.to_i)
75
-
76
- @mutex.synchronize do
77
- return false if @recently_released_ips.include?({ip: ip.to_i, network_name: subnet.network_name})
78
- return false if @ips.include?({ip: ip.to_i, network_name: subnet.network_name})
79
- end
75
+ return false if @recently_released_ips.include?({ip: ip.to_i, network_name: subnet.network_name})
76
+ return false if @ips.include?({ip: ip.to_i, network_name: subnet.network_name})
80
77
 
81
78
  true
82
79
  end
@@ -33,21 +33,20 @@ module Bosh
33
33
  end
34
34
 
35
35
  def create_from_model(deployment_model, options={})
36
- manifest_hash = Psych.load(deployment_model.manifest)
37
- cloud_config_model = deployment_model.cloud_config
38
- create_from_manifest(manifest_hash, cloud_config_model, options)
36
+ manifest = Manifest.load_from_text(deployment_model.manifest, deployment_model.cloud_config)
37
+ create_from_manifest(manifest, deployment_model.cloud_config, options)
39
38
  end
40
39
 
41
- def create_from_manifest(manifest_hash, cloud_config, options)
42
- parse_from_manifest(manifest_hash, cloud_config, options)
40
+ def create_from_manifest(manifest, cloud_config, options)
41
+ parse_from_manifest(manifest, cloud_config, options)
43
42
  end
44
43
 
45
44
  private
46
45
 
47
- def parse_from_manifest(manifest_hash, cloud_config, options)
48
- cloud_config_hash = cloud_config.nil? ? nil : cloud_config.manifest
49
- @manifest_validator.validate(manifest_hash, cloud_config_hash)
50
- deployment_manifest, cloud_manifest = @deployment_manifest_migrator.migrate(manifest_hash, cloud_config_hash)
46
+ def parse_from_manifest(manifest, cloud_config, options)
47
+ manifest.resolve_aliases
48
+ @manifest_validator.validate(manifest.manifest_hash, manifest.cloud_config_hash)
49
+ deployment_manifest, cloud_manifest = @deployment_manifest_migrator.migrate(manifest.manifest_hash, manifest.cloud_config_hash)
51
50
  @logger.debug("Migrated deployment manifest:\n#{deployment_manifest}")
52
51
  @logger.debug("Migrated cloud config manifest:\n#{cloud_manifest}")
53
52
  name = deployment_manifest['name']
@@ -38,10 +38,6 @@ module Bosh::Director
38
38
  @model = nil
39
39
  end
40
40
 
41
- def is_using_latest_version?
42
- @version == 'latest'
43
- end
44
-
45
41
  def is_using_os?
46
42
  !@os.nil? && @name.nil?
47
43
  end
@@ -62,13 +58,9 @@ module Bosh::Director
62
58
  end
63
59
 
64
60
  def add_stemcell_model
65
- if is_using_latest_version?
66
- @model = is_using_os? ? @manager.latest_by_os(@os) : @manager.latest_by_name(@name)
67
- else
68
- @model = is_using_os? ?
69
- @manager.find_by_os_and_version(@os, @version) :
70
- @manager.find_by_name_and_version(@name, @version)
71
- end
61
+ @model = is_using_os? ?
62
+ @manager.find_by_os_and_version(@os, @version) :
63
+ @manager.find_by_name_and_version(@name, @version)
72
64
 
73
65
  @name = @model.name
74
66
  @os = @model.operating_system
@@ -23,18 +23,16 @@ module Bosh::Director
23
23
 
24
24
  def perform
25
25
  deployment_model = @deployment_manager.find_by_name(@deployment_name)
26
- deployment_manifest_hash = Psych.load(deployment_model.manifest)
27
- deployment_name = deployment_manifest_hash['name']
26
+ deployment_manifest = Manifest.load_from_text(deployment_model.manifest, deployment_model.cloud_config)
27
+ deployment_name = deployment_manifest.to_hash['name']
28
28
  with_deployment_lock(deployment_name) do
29
- cloud_config_model = deployment_model.cloud_config
30
-
31
29
  deployment = nil
32
30
  job = nil
33
31
 
34
32
  event_log.begin_stage('Preparing deployment', 1)
35
33
  event_log.track('Preparing deployment') do
36
34
  planner_factory = DeploymentPlan::PlannerFactory.create(logger)
37
- deployment = planner_factory.create_from_manifest(deployment_manifest_hash, cloud_config_model, {})
35
+ deployment = planner_factory.create_from_manifest(deployment_manifest, deployment_model.cloud_config, {})
38
36
  deployment.bind_models
39
37
  job = deployment.job(@errand_name)
40
38
 
@@ -21,8 +21,6 @@ module Bosh::Director
21
21
 
22
22
  manifest_text = File.read(@manifest_file_path)
23
23
  logger.debug("Manifest:\n#{manifest_text}")
24
- deployment_manifest_hash = Psych.load(manifest_text)
25
-
26
24
  cloud_config_model = Bosh::Director::Models::CloudConfig[@cloud_config_id]
27
25
  if cloud_config_model.nil?
28
26
  logger.debug("No cloud config uploaded yet.")
@@ -30,7 +28,8 @@ module Bosh::Director
30
28
  logger.debug("Cloud config:\n#{cloud_config_model.manifest}")
31
29
  end
32
30
 
33
- deployment_name = deployment_manifest_hash['name']
31
+ deployment_manifest = Manifest.load_from_text(manifest_text, cloud_config_model)
32
+ deployment_name = deployment_manifest.to_hash['name']
34
33
  with_deployment_lock(deployment_name) do
35
34
  @notifier = DeploymentPlan::Notifier.new(deployment_name, Config.nats_rpc, logger)
36
35
  @notifier.send_start_event
@@ -40,7 +39,7 @@ module Bosh::Director
40
39
  event_log.begin_stage('Preparing deployment', 1)
41
40
  event_log.track('Preparing deployment') do
42
41
  planner_factory = DeploymentPlan::PlannerFactory.create(logger)
43
- deployment_plan = planner_factory.create_from_manifest(deployment_manifest_hash, cloud_config_model, @options)
42
+ deployment_plan = planner_factory.create_from_manifest(deployment_manifest, cloud_config_model, @options)
44
43
  deployment_plan.bind_models
45
44
  end
46
45
 
@@ -0,0 +1,115 @@
1
+ class ::Hash
2
+ def deep_merge(second)
3
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
4
+ self.merge(second, &merger)
5
+ end
6
+ end
7
+
8
+ module Bosh::Director
9
+ class Changeset
10
+ KEY_NAME = 'name'
11
+
12
+ def initialize(before, after)
13
+ @before = before
14
+ @after = after
15
+ if @before && @after
16
+ @merged = @before.deep_merge(@after)
17
+ elsif @before
18
+ @merged = @before
19
+ else
20
+ @merged = @after
21
+ end
22
+ end
23
+
24
+ def diff(indent = 0)
25
+ lines = DiffLines.new
26
+
27
+ @merged.each_pair do |key, value|
28
+ if @before.nil? || @before[key].nil?
29
+ lines.concat(yaml_lines({key => value}, indent, 'added'))
30
+
31
+ elsif @after.nil? || @after[key].nil?
32
+ lines.concat(yaml_lines({key => value}, indent, 'removed'))
33
+
34
+ elsif @before[key].is_a?(Array) && @after[key].is_a?(Array)
35
+ lines.concat(compare_arrays(@before[key], @after[key], key, indent))
36
+
37
+ elsif value.is_a?(Hash)
38
+ changeset = Changeset.new(@before[key], @after[key])
39
+ diff_lines = changeset.diff(indent+1)
40
+ unless diff_lines.empty?
41
+ lines << Line.new(indent, "#{key}:", nil)
42
+ lines.concat(diff_lines)
43
+ end
44
+
45
+ elsif @before[key] != @after[key]
46
+ lines << Line.new(indent, "#{key}: #{@before[key]}", 'removed')
47
+ lines << Line.new(indent, "#{key}: #{@after[key]}", 'added')
48
+ end
49
+ end
50
+ lines
51
+ end
52
+
53
+ def yaml_lines(value, indent, state)
54
+ lines = DiffLines.new
55
+ value.to_yaml(indent: Line::INDENT).gsub("---\n", '').split("\n").each do |line|
56
+ lines << Line.new(indent, line, state)
57
+ end
58
+ lines
59
+ end
60
+
61
+ def compare_arrays(old_value, new_value, parent_name, indent)
62
+ added = new_value - old_value
63
+ removed = old_value - new_value
64
+
65
+ lines = DiffLines.new
66
+
67
+ added.each do |elem|
68
+ if elem.is_a?(Hash)
69
+ using_names = (added+removed).all? { |e| e['name'] }
70
+ using_ranges = (added+removed).all? { |e| e['range'] }
71
+ if using_names || using_ranges
72
+ if using_names
73
+ removed_same_name_element = removed.find { |e| e['name'] == elem['name'] }
74
+ elsif using_ranges
75
+ removed_same_name_element = removed.find { |e| e['range'] == elem['range'] }
76
+ end
77
+ removed.delete(removed_same_name_element)
78
+
79
+ if removed_same_name_element
80
+ changeset = Changeset.new(removed_same_name_element, elem)
81
+ diff_lines = changeset.diff(indent+1)
82
+
83
+ unless diff_lines.empty?
84
+ # write name if elem has been changed
85
+ if using_names
86
+ lines.concat(yaml_lines([{'name' => elem['name']}], indent, nil))
87
+ elsif using_ranges
88
+ lines.concat(yaml_lines([{'range' => elem['range']}], indent, nil))
89
+ end
90
+ lines.concat(diff_lines)
91
+ end
92
+ else
93
+ lines.concat(yaml_lines([elem], indent, 'added'))
94
+ end
95
+
96
+ else
97
+ lines.concat(yaml_lines([elem], indent, 'added'))
98
+ end
99
+ else
100
+ lines.concat(yaml_lines([elem], indent, 'added'))
101
+ end
102
+ end
103
+
104
+ unless removed.empty?
105
+ lines.concat(yaml_lines(removed, indent, 'removed'))
106
+ end
107
+
108
+ unless lines.empty?
109
+ lines.unshift(Line.new(indent, "#{parent_name}:", nil))
110
+ end
111
+
112
+ lines
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,83 @@
1
+ module Bosh::Director
2
+ class Line < Struct.new(:indent, :text, :status)
3
+ INDENT = 2
4
+
5
+ def to_s
6
+ "#{' ' * INDENT * indent}#{text}"
7
+ end
8
+
9
+ def full_indent
10
+ indent + text[/^ */].size / INDENT
11
+ end
12
+ end
13
+
14
+ class DiffLines < Array
15
+ MANIFEST_KEYS_ORDER = %w(
16
+ azs
17
+ vm_types
18
+ resource_pools
19
+ compilation
20
+ networks
21
+ disk_types
22
+ disk_pools
23
+ name
24
+ director_uuid
25
+ stemcells
26
+ releases
27
+ update
28
+ jobs
29
+ )
30
+
31
+ def order
32
+ sections = {}
33
+ key = nil
34
+
35
+ self.each do |line|
36
+ if line.indent == 0 && line.text !~ /^[ -]/
37
+ key = line.text
38
+ sections[key] = []
39
+ end
40
+
41
+ sections[key] << line
42
+ end
43
+
44
+ ordered_lines = []
45
+ MANIFEST_KEYS_ORDER.each do |manifest_key|
46
+ section_name = manifest_key + ':'
47
+ lines = sections[section_name]
48
+ ordered_lines += lines.to_a
49
+ sections.delete(section_name)
50
+ end
51
+
52
+ sections.each do |_, section_lines|
53
+ section_lines.each do |line|
54
+ ordered_lines << line
55
+ end
56
+ end
57
+
58
+ self.replace(ordered_lines)
59
+ end
60
+
61
+ def redact_properties
62
+ i = 0
63
+ while i < self.size
64
+ line = self[i]
65
+
66
+ if line.text =~ /\bproperties:/
67
+ properties_indent = line.full_indent
68
+ i += 1
69
+ line = self[i]
70
+
71
+ while line && line.full_indent > properties_indent
72
+ line.text.gsub!(/: .+/, ': <redacted>') # readact hash values
73
+ line.text.gsub!(/- [^:]+$/, '- <redacted>') # redact array values
74
+ i += 1
75
+ line = self[i]
76
+ end
77
+ end
78
+ i += 1
79
+ end
80
+ self
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,72 @@
1
+ module Bosh::Director
2
+ class Manifest
3
+ def self.load_from_text(manifest_text, cloud_config)
4
+ cloud_config_hash = cloud_config.nil? ? nil : cloud_config.manifest
5
+ manifest_hash = manifest_text.nil? ? {} : Psych.load(manifest_text)
6
+ new(manifest_hash, cloud_config_hash)
7
+ end
8
+
9
+ attr_reader :manifest_hash, :cloud_config_hash
10
+
11
+ def initialize(manifest_hash, cloud_config_hash)
12
+ @manifest_hash = manifest_hash
13
+ @cloud_config_hash = cloud_config_hash
14
+ end
15
+
16
+ def resolve_aliases
17
+ hashed = to_hash
18
+ hashed['resource_pools'].to_a.each do |rp|
19
+ rp['stemcell']['version'] = resolve_stemcell_version(rp['stemcell'])
20
+ end
21
+
22
+ hashed['stemcells'].to_a.each do |stemcell|
23
+ stemcell['version'] = resolve_stemcell_version(stemcell)
24
+ end
25
+
26
+ hashed['releases'].to_a.each do |release|
27
+ release['version'] = resolve_release_version(release)
28
+ end
29
+ end
30
+
31
+ def diff(other_manifest)
32
+ Changeset.new(to_hash, other_manifest.to_hash).diff.order.redact_properties
33
+ end
34
+
35
+ def to_hash
36
+ @manifest_hash.merge(@cloud_config_hash || {})
37
+ end
38
+
39
+ private
40
+
41
+ def resolve_stemcell_version(stemcell)
42
+ stemcell_manager = Api::StemcellManager.new
43
+
44
+ unless stemcell.is_a?(Hash)
45
+ raise 'Invalid stemcell spec in the deployment manifest'
46
+ end
47
+
48
+ if stemcell['version'] == 'latest'
49
+ if stemcell['os']
50
+ latest_stemcell = stemcell_manager.latest_by_os(stemcell['os'])
51
+ elsif stemcell['name']
52
+ latest_stemcell = stemcell_manager.latest_by_name(stemcell['name'])
53
+ else
54
+ raise 'Stemcell definition must contain either name or os'
55
+ end
56
+ return latest_stemcell[:version].to_s
57
+ end
58
+
59
+ stemcell['version'].to_s
60
+ end
61
+
62
+ def resolve_release_version(release_def)
63
+ release_manager = Api::ReleaseManager.new
64
+ if release_def['version'] == 'latest'
65
+ release = release_manager.find_by_name(release_def['name'])
66
+ return release_manager.sorted_release_versions(release).last['version']
67
+ end
68
+
69
+ release_def['version'].to_s
70
+ end
71
+ end
72
+ end
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Director
3
- VERSION = '1.3178.0'
3
+ VERSION = '1.3181.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bosh-director
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3178.0
4
+ version: 1.3181.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - VMware
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-15 00:00:00.000000000 Z
11
+ date: 2016-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bosh_common
@@ -16,98 +16,98 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.3178.0
19
+ version: 1.3181.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.3178.0
26
+ version: 1.3181.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bosh_cpi
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.3178.0
33
+ version: 1.3181.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.3178.0
40
+ version: 1.3181.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bosh-registry
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.3178.0
47
+ version: 1.3181.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.3178.0
54
+ version: 1.3181.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: blobstore_client
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.3178.0
61
+ version: 1.3181.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.3178.0
68
+ version: 1.3181.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: bosh-core
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 1.3178.0
75
+ version: 1.3181.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 1.3178.0
82
+ version: 1.3181.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: bosh-director-core
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 1.3178.0
89
+ version: 1.3181.0
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 1.3178.0
96
+ version: 1.3181.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: bosh-template
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 1.3178.0
103
+ version: 1.3181.0
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 1.3178.0
110
+ version: 1.3181.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: bosh_openstack_cpi
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -874,6 +874,9 @@ files:
874
874
  - lib/bosh/director/lock_helper.rb
875
875
  - lib/bosh/director/log_bundles_cleaner.rb
876
876
  - lib/bosh/director/logs_fetcher.rb
877
+ - lib/bosh/director/manifest/changeset.rb
878
+ - lib/bosh/director/manifest/diff_lines.rb
879
+ - lib/bosh/director/manifest/manifest.rb
877
880
  - lib/bosh/director/models.rb
878
881
  - lib/bosh/director/models/cloud_config.rb
879
882
  - lib/bosh/director/models/compiled_package.rb