bosh-template 1.3262.24.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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