bosh-template 1.3262.24.0 → 2.0.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: 1412630af5e936ad0684c3a26de987c2a51d4895
4
- data.tar.gz: 3f61c19de023c59e19a81af462b6af46518bebfc
3
+ metadata.gz: 2378abe7a780eedfde0662cecff0c3431262493b
4
+ data.tar.gz: 0d007a700bb85ba2bef9a076e63ae45a29dd447b
5
5
  SHA512:
6
- metadata.gz: 5022ecdd77d1c0783f0b0ca18062703bf83ae9a8ce33a7b80439aa016fc3ace3569fdec26bb757247e574e53ce1b672c0e8726f620b0a1fe786e8ae93163b74f
7
- data.tar.gz: 2cc3308812842ba9362bcc7e0579f4e6771e9691485a24207ff436029c481393ea1c69970f53f564bc0453308c911cd839a832ad3f189abd9bf147651c99e61c
6
+ metadata.gz: f96497f3c1d86cd259ef7fb1d1b70f02c5196553660d39c95be450c431e7eebbe076fc246ef409fd5bd056d1611203f07bbef7284625279848334c593abb689d
7
+ data.tar.gz: e3b70178abec3413f4dbd0008d8883534860a05f88011b0201ac20cbe996428219e4d9648c309c89b21e3097a22e8caa92c60771b9369eb5ef37f03f1cc8f508
data/README.md CHANGED
@@ -9,3 +9,31 @@ gem install bosh-template
9
9
 
10
10
  bosh-template <template.erb> --context '{ "...": ... }'
11
11
  ```
12
+
13
+ ## Unit-testing your release ERB templates
14
+
15
+ The `Bosh::Template::Test` package provides classes to unit-test your templates. These classes can be used to mock out different combinations of links, instances, etc., without the need to script a `create-release` and `deploy` against a running director.
16
+
17
+ Examples of usage are provided in `src/spec/config.erb_spec.rb` in `template-test-release`.
18
+
19
+ When you create your own release, you can likewise create a tests folder in your release and use these classes. A release author can create a test and call the template rendering like such:
20
+
21
+ ```ruby
22
+ let(:job) {release.job('JOB-NAME')} # e.g. 'web-server;
23
+ let(:template) {job.template('PATH-TO-TEMPLATE')} # e.g. 'config/config-with-nested'
24
+ let(:manifest) {
25
+ 'cert' => '----- BEGIN ... -----',
26
+ 'port' => 42,
27
+ }
28
+ let(:instance) { InstanceSpec.new(name:'instance-name', az: 'az1', bootstrap: true) }
29
+ let(:link_instance) { InstanceSpec.new(name:'link-instance-name', az: 'az2') }
30
+ let(:link_properties){
31
+ 'link-key' => 'link-value',
32
+ }
33
+ let(:link) { Link.new(name:'link-name', instances:[link_instance], properties: link_properties)}
34
+
35
+
36
+ rendered_template = JSON.parse(template.render(manifest, spec: instance, consumes: [link]))
37
+ ```
38
+
39
+ And then check that their template rendered as they expected.
@@ -4,3 +4,4 @@ module Bosh
4
4
  end
5
5
 
6
6
  require 'bosh/template/evaluation_context'
7
+ require 'bosh/template/test'
@@ -5,6 +5,7 @@ require 'bosh/template/unknown_link'
5
5
  require 'bosh/template/property_helper'
6
6
  require 'bosh/template/evaluation_link_instance'
7
7
  require 'bosh/template/evaluation_link'
8
+ require 'bosh/template/manual_link_dns_encoder'
8
9
 
9
10
  module Bosh
10
11
  module Template
@@ -28,7 +29,7 @@ module Bosh
28
29
  attr_reader :spec
29
30
 
30
31
  # @param [Hash] spec Template spec
31
- def initialize(spec)
32
+ def initialize(spec, dns_encoder)
32
33
  unless spec.is_a?(Hash)
33
34
  raise EvaluationFailed,
34
35
  'Invalid spec provided for template evaluation context, ' +
@@ -45,6 +46,7 @@ module Bosh
45
46
  @spec = openstruct(spec, BackCompatOpenStruct)
46
47
  @raw_properties = spec['properties'] || {}
47
48
  @properties = openstruct(@raw_properties)
49
+ @dns_encoder = dns_encoder
48
50
 
49
51
  @links = spec['links'] || {}
50
52
  end
@@ -94,14 +96,29 @@ module Bosh
94
96
  end
95
97
 
96
98
  def link(name)
97
- result = lookup_property(@links, name)
98
- raise UnknownLink.new(name) if result.nil?
99
+ link_spec = lookup_property(@links, name)
100
+ raise UnknownLink.new(name) if link_spec.nil?
99
101
 
100
- if result.has_key?("instances")
101
- instance_array = result["instances"].map do |link_spec|
102
- EvaluationLinkInstance.new(link_spec["name"], link_spec["index"], link_spec["id"], link_spec["az"], link_spec["address"], link_spec["properties"], link_spec["bootstrap"])
102
+ if link_spec.has_key?('instances')
103
+ link_instances = link_spec['instances'].map do |instance_link_spec|
104
+ EvaluationLinkInstance.new(instance_link_spec['name'], instance_link_spec['index'], instance_link_spec['id'], instance_link_spec['az'], instance_link_spec['address'], instance_link_spec['properties'], instance_link_spec['bootstrap'])
103
105
  end
104
- return EvaluationLink.new(instance_array, result["properties"])
106
+
107
+ if link_spec.has_key?('address')
108
+ encoder_to_inject = ManualLinkDnsEncoder.new(link_spec['address'])
109
+ else
110
+ encoder_to_inject = @dns_encoder
111
+ end
112
+
113
+ return EvaluationLink.new(
114
+ link_instances,
115
+ link_spec['properties'],
116
+ link_spec['instance_group'],
117
+ link_spec['default_network'],
118
+ link_spec['deployment_name'],
119
+ link_spec['domain'],
120
+ encoder_to_inject,
121
+ )
105
122
  end
106
123
  raise UnknownLink.new(name)
107
124
  end
@@ -124,15 +141,15 @@ module Bosh
124
141
  # @param [String] name of the link
125
142
  # @yield [Object] link, which is an array of instances
126
143
  def if_link(name)
127
- link_found = lookup_property(@links, name)
128
- if link_found.nil? || !link_found.has_key?("instances")
144
+ link_spec = lookup_property(@links, name)
145
+ if link_spec.nil? || !link_spec.has_key?('instances')
129
146
  return ActiveElseBlock.new(self)
130
147
  else
131
- instance_array = link_found["instances"].map do |link_spec|
132
- EvaluationLinkInstance.new(link_spec["name"], link_spec["index"], link_spec["id"], link_spec["az"], link_spec["address"], link_spec["properties"], link_spec["bootstrap"])
148
+ link_instances = link_spec['instances'].map do |instance_link_spec|
149
+ EvaluationLinkInstance.new(instance_link_spec['name'], instance_link_spec['index'], instance_link_spec['id'], instance_link_spec['az'], instance_link_spec['address'], instance_link_spec['properties'], instance_link_spec['bootstrap'])
133
150
  end
134
151
 
135
- yield EvaluationLink.new(instance_array, link_found["properties"])
152
+ yield EvaluationLink.new(link_instances, link_spec['properties'], link_spec['instance_group'], link_spec['default_network'], link_spec['deployment_name'], link_spec['root_domain'], @dns_encoder)
136
153
  InactiveElseBlock.new
137
154
  end
138
155
  end
@@ -1,3 +1,5 @@
1
+ require 'bosh/template/property_helper'
2
+
1
3
  module Bosh
2
4
  module Template
3
5
  class EvaluationLink
@@ -6,9 +8,14 @@ module Bosh
6
8
  attr_reader :instances
7
9
  attr_reader :properties
8
10
 
9
- def initialize(instances, properties)
11
+ def initialize(instances, properties, instance_group, default_network, deployment_name, root_domain, dns_encoder)
10
12
  @instances = instances
11
13
  @properties = properties
14
+ @instance_group = instance_group
15
+ @default_network = default_network
16
+ @deployment_name = deployment_name
17
+ @root_domain = root_domain
18
+ @dns_encoder = dns_encoder
12
19
  end
13
20
 
14
21
  def p(*args)
@@ -33,6 +40,19 @@ module Bosh
33
40
  yield *values
34
41
  Bosh::Template::EvaluationContext::InactiveElseBlock.new
35
42
  end
43
+
44
+ def address(criteria = {})
45
+ raise NotImplementedError.new('link.address requires bosh director') if @dns_encoder.nil?
46
+
47
+ full_criteria = criteria.merge(
48
+ instance_group: @instance_group,
49
+ default_network: @default_network,
50
+ deployment_name: @deployment_name,
51
+ root_domain: @root_domain,
52
+ )
53
+
54
+ @dns_encoder.encode_query(full_criteria)
55
+ end
36
56
  end
37
57
  end
38
- end
58
+ end
@@ -0,0 +1,13 @@
1
+ module Bosh
2
+ module Template
3
+ class ManualLinkDnsEncoder
4
+ def initialize(manual_link_address)
5
+ @manual_link_address = manual_link_address
6
+ end
7
+
8
+ def encode_query(_)
9
+ @manual_link_address
10
+ end
11
+ end
12
+ end
13
+ end
@@ -45,6 +45,46 @@ module Bosh
45
45
 
46
46
  ref
47
47
  end
48
+
49
+ def sort_property(property)
50
+ if property.is_a?(Hash)
51
+ property.each do |k, v|
52
+ property[k] = sort_property(v)
53
+ end.sort.to_h
54
+ else
55
+ property
56
+ end
57
+ end
58
+
59
+ # Inject property with a given name and value to dst.
60
+ # @param [Hash] dst Property destination
61
+ # @param [String] name Property name (dot-separated)
62
+ # @param [Object] value Property value to be set
63
+ def set_property(dst, name, value)
64
+ keys = name.split('.')
65
+ dst_ref = dst
66
+
67
+ keys[0..-2].each do |key|
68
+ dst_ref[key] ||= {}
69
+ dst_ref = dst_ref[key]
70
+ end
71
+
72
+ dst_ref[keys[-1]] = value
73
+ end
74
+
75
+ def validate_properties_format(properties, name)
76
+ keys = name.split('.')
77
+ properties_ref = properties
78
+
79
+ keys.each do |key|
80
+ unless properties_ref.is_a?(Hash)
81
+ raise Bosh::Template::InvalidPropertyType,
82
+ "Property '#{name}' expects a hash, but received '#{properties_ref.class}'"
83
+ end
84
+ properties_ref = properties_ref[key]
85
+ break if properties_ref.nil? # no property with this name is src
86
+ end
87
+ end
48
88
  end
49
89
  end
50
90
  end
@@ -11,7 +11,7 @@ module Bosh
11
11
 
12
12
  def render(template_name)
13
13
  spec = JSON.parse(@context)
14
- evaluation_context = EvaluationContext.new(spec)
14
+ evaluation_context = EvaluationContext.new(spec, nil)
15
15
  template = ERB.new(File.read(template_name), safe_level = nil, trim_mode = "-")
16
16
  template.result(evaluation_context.get_binding)
17
17
  end
@@ -0,0 +1,14 @@
1
+ module Bosh
2
+ module Template
3
+ module Test
4
+
5
+ end
6
+ end
7
+ end
8
+
9
+ require 'bosh/template/test/job'
10
+ require 'bosh/template/test/link'
11
+ require 'bosh/template/test/link_instance'
12
+ require 'bosh/template/test/release_dir'
13
+ require 'bosh/template/test/instance_spec'
14
+ require 'bosh/template/test/template'
@@ -0,0 +1,40 @@
1
+ module Bosh::Template::Test
2
+ class InstanceSpec
3
+ def initialize(
4
+ address: 'my.bosh.com',
5
+ az: 'az1',
6
+ bootstrap: false,
7
+ deployment: 'my-deployment',
8
+ id: 'xxxxxx-xxxxxxxx-xxxxx',
9
+ index: 0,
10
+ ip: '192.168.0.0',
11
+ name: 'me',
12
+ networks: {'network1' => {'foo' => 'bar', 'ip' => '192.168.0.0'}}
13
+ )
14
+ @address = address
15
+ @az = az
16
+ @bootstrap = bootstrap
17
+ @deployment = deployment
18
+ @id = id
19
+ @index = index
20
+ @ip = ip
21
+ @name = name
22
+ @networks = networks
23
+ end
24
+
25
+ def to_h
26
+ {
27
+ 'address' => @address,
28
+ 'az' => @az,
29
+ 'bootstrap' => @bootstrap,
30
+ 'deployment' => @deployment,
31
+ 'id' => @id,
32
+ 'index' => @index,
33
+ 'ip' => @ip,
34
+ 'name' => @name,
35
+ 'networks' => @networks,
36
+ 'job' => {'name' => @name}
37
+ }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ module Bosh::Template::Test
2
+ class Job
3
+ def initialize(release_path, name)
4
+ @release_path = release_path
5
+ @name = name
6
+ @job_path = File.join(@release_path, 'jobs', @name)
7
+ # raise "No such job at path: #{@job_path}" if !File.exist?(@job_path)
8
+ spec_path = File.join(@job_path, 'spec')
9
+ @spec = YAML.load(File.read(spec_path))
10
+ @templates = @spec['templates']
11
+ end
12
+
13
+ def template(rendered_file_name)
14
+ @templates.each_pair do |k, v|
15
+ return Template.new(@spec, File.join(@job_path, 'templates', k)) if v == rendered_file_name
16
+ end
17
+ raise "Template for rendered path filename not found: #{rendered_file_name}. Possible values are: [#{@templates.values.join(', ')}]"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module Bosh::Template::Test
2
+ class Link
3
+ attr_reader :instances, :name, :properties
4
+
5
+ def initialize(name:, instances: [], properties: {})
6
+ @instances = instances
7
+ @name = name
8
+ @properties = properties
9
+ end
10
+
11
+ def to_h
12
+ {
13
+ 'instances' => instances.map(&:to_h),
14
+ 'name' => name,
15
+ 'properties' => properties,
16
+ }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ module Bosh::Template::Test
2
+ class LinkInstance
3
+ attr_reader :name, :id, :index, :az, :address, :bootstrap
4
+
5
+ def initialize(
6
+ name: 'i-name',
7
+ id: 'jkl8098',
8
+ index: 4,
9
+ az: 'az4',
10
+ address: 'link.instance.address.com',
11
+ bootstrap: false)
12
+ @bootstrap = bootstrap
13
+ @address = address
14
+ @az = az
15
+ @index = index
16
+ @id = id
17
+ @name = name
18
+ end
19
+
20
+ def to_h
21
+ {
22
+ 'name' => name,
23
+ 'id' => id,
24
+ 'index' => index,
25
+ 'az' => az,
26
+ 'address' => address,
27
+ 'bootstrap' => bootstrap
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ module Bosh::Template::Test
2
+ class ReleaseDir
3
+ def initialize(path)
4
+ @path = path
5
+ end
6
+
7
+ def job(name)
8
+ Bosh::Template::Test::Job.new(@path, name)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,77 @@
1
+ require 'bosh/template/evaluation_context'
2
+ require 'bosh/template/evaluation_context'
3
+
4
+ module Bosh::Template
5
+ module Test
6
+ class Template
7
+ include PropertyHelper
8
+
9
+ def initialize(job_spec_hash, template_path)
10
+ @job_spec_hash = job_spec_hash
11
+ @template_path = template_path
12
+ end
13
+
14
+ def render(manifest_properties_hash, spec: InstanceSpec.new, consumes: [])
15
+ spec_hash = {}
16
+ spec_hash['properties'] = hash_with_defaults(manifest_properties_hash)
17
+ sanitized_hash_with_spec = spec_hash.merge(spec.to_h)
18
+ sanitized_hash_with_spec['links'] = links_hash(consumes)
19
+
20
+ binding = Bosh::Template::EvaluationContext.new(sanitized_hash_with_spec, nil).get_binding
21
+ raise "No such file at #{@template_path}" unless File.exist?(@template_path)
22
+ ERB.new(File.read(@template_path)).result(binding)
23
+ end
24
+
25
+ private
26
+
27
+ def hash_with_defaults(manifest_properties_hash)
28
+ hash_properties = {}
29
+ spec_properties = @job_spec_hash['properties']
30
+
31
+ spec_properties.each_pair do |dotted_spec_key, property_def|
32
+ property_val = lookup_property(manifest_properties_hash, dotted_spec_key)
33
+ if property_val.nil? && !property_def['default'].nil?
34
+ property_val = property_def['default']
35
+ end
36
+ insert_property(hash_properties, dotted_spec_key, property_val)
37
+ end
38
+
39
+ hash_properties
40
+ end
41
+
42
+ def links_hash(links)
43
+ links_hash = {}
44
+ known_links = []
45
+
46
+ consumes = @job_spec_hash.fetch('consumes', [])
47
+ consumes.each do |consume|
48
+ known_links << consume['name']
49
+ link = links.find {|l| l.name == consume['name']}
50
+ links_hash[link.name] = link.to_h unless link.nil?
51
+ end
52
+
53
+ links.each do |link|
54
+ unless known_links.include?(link.name)
55
+ raise "Link '#{link.name}' is not declared as a consumed link in this job."
56
+ end
57
+ end
58
+
59
+ links_hash
60
+ end
61
+
62
+ def insert_property(nested_hash, dotted_key, value)
63
+ property_segments = dotted_key.split('.')
64
+ current_level = nested_hash
65
+
66
+ property_segments.each_with_index do |property_segment, i|
67
+ if i == property_segments.count - 1
68
+ current_level[property_segment] = value
69
+ else
70
+ current_level[property_segment] ||= {}
71
+ current_level = current_level[property_segment]
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Template
3
- VERSION = '1.3262.24.0'
3
+ VERSION = '2.0.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bosh-template
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3262.24.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pivotal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-02 00:00:00.000000000 Z
11
+ date: 2017-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: semi_semantic
@@ -24,48 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.2.0
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rspec-its
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
27
  description: Renders bosh templates
70
28
  email: support@cloudfoundry.com
71
29
  executables:
@@ -81,14 +39,22 @@ files:
81
39
  - lib/bosh/template/evaluation_link.rb
82
40
  - lib/bosh/template/evaluation_link_instance.rb
83
41
  - lib/bosh/template/invalid_property_type.rb
42
+ - lib/bosh/template/manual_link_dns_encoder.rb
84
43
  - lib/bosh/template/property_helper.rb
85
44
  - lib/bosh/template/renderer.rb
45
+ - lib/bosh/template/test.rb
46
+ - lib/bosh/template/test/instance_spec.rb
47
+ - lib/bosh/template/test/job.rb
48
+ - lib/bosh/template/test/link.rb
49
+ - lib/bosh/template/test/link_instance.rb
50
+ - lib/bosh/template/test/release_dir.rb
51
+ - lib/bosh/template/test/template.rb
86
52
  - lib/bosh/template/unknown_link.rb
87
53
  - lib/bosh/template/unknown_property.rb
88
54
  - lib/bosh/template/version.rb
89
55
  homepage: https://github.com/cloudfoundry/bosh
90
56
  licenses:
91
- - Apache 2.0
57
+ - Apache-2.0
92
58
  metadata: {}
93
59
  post_install_message:
94
60
  rdoc_options: []
@@ -106,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
72
  version: '0'
107
73
  requirements: []
108
74
  rubyforge_project:
109
- rubygems_version: 2.2.2
75
+ rubygems_version: 2.5.1
110
76
  signing_key:
111
77
  specification_version: 4
112
78
  summary: Renders bosh templates