bosh-template 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 09977444d10e0c549c9b8bf1308937c64af24044
4
+ data.tar.gz: 170dd775f88c18db31cabdd6e4f04dc0154b42ac
5
+ SHA512:
6
+ metadata.gz: 5d5790815e3644abd9fbe7ada1e3f750eee5e00c7a883bbf900f8bf30853ac71f26b9cb9ccf7556e73e03d9eee9c941efc39cba3b56fa7daec1ca0f6669550a7
7
+ data.tar.gz: b50b9726628cda738bc2b615c984ecff936c4ea7c15149422543568e1a800cf277a482d19759f748d7392e447caaa10379729ef11489092cd468caab7c5a1dc7
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # bosh-template
2
+
3
+ Renders BOSH templates
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ gem install bosh-template
9
+
10
+ bosh-template <template.erb> --context '{ "...": ... }'
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.
data/bin/bosh-template ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bosh/template/renderer'
4
+ require 'optparse'
5
+
6
+ template_name = ARGV[0]
7
+
8
+ options = {}
9
+ OptionParser.new do |opts|
10
+ opts.banner = 'Usage: bosh-templates template.erb [options]'
11
+ opts.separator('Command-line renderer BOSH templates')
12
+
13
+ opts.separator('')
14
+ opts.separator('Options:')
15
+
16
+ opts.on('-C', '--context STRING', 'JSON string containing property values for template context') do |v|
17
+ options[:context] = v
18
+ end
19
+ end.parse!
20
+
21
+ raise OptionParser::MissingArgument.new('--context') if options[:context].nil?
22
+
23
+ renderer = Bosh::Template::Renderer.new(options)
24
+ puts renderer.render(template_name)
@@ -0,0 +1,7 @@
1
+ module Bosh
2
+ module Template
3
+ end
4
+ end
5
+
6
+ require 'bosh/template/evaluation_context'
7
+ require 'bosh/template/test'
@@ -0,0 +1,218 @@
1
+ require 'ostruct'
2
+ require 'bosh/template/evaluation_failed'
3
+ require 'bosh/template/unknown_property'
4
+ require 'bosh/template/unknown_link'
5
+ require 'bosh/template/property_helper'
6
+ require 'bosh/template/evaluation_link_instance'
7
+ require 'bosh/template/evaluation_link'
8
+ require 'bosh/template/manual_link_dns_encoder'
9
+
10
+ module Bosh
11
+ module Template
12
+ # Helper class to evaluate templates. Used by Director, CLI and Agent.
13
+ class EvaluationContext
14
+ include PropertyHelper
15
+
16
+ # @return [String] Template name
17
+ attr_reader :name
18
+
19
+ # @return [Integer] Template instance index
20
+ attr_reader :index
21
+
22
+ # @return [Hash] Template properties
23
+ attr_reader :properties
24
+
25
+ # @return [Hash] Raw template properties (no openstruct)
26
+ attr_reader :raw_properties
27
+
28
+ # @return [Hash] Template spec
29
+ attr_reader :spec
30
+
31
+ # @param [Hash] spec Template spec
32
+ def initialize(spec, dns_encoder)
33
+ unless spec.is_a?(Hash)
34
+ raise EvaluationFailed,
35
+ 'Invalid spec provided for template evaluation context, ' +
36
+ "Hash expected, #{spec.class} given"
37
+ end
38
+
39
+ if spec['job'].is_a?(Hash)
40
+ @name = spec['job']['name']
41
+ else
42
+ @name = nil
43
+ end
44
+
45
+ @index = spec['index']
46
+ @spec = openstruct(spec, BackCompatOpenStruct)
47
+ @raw_properties = spec['properties'] || {}
48
+ @properties = openstruct(@raw_properties)
49
+ @dns_encoder = dns_encoder
50
+
51
+ @links = spec['links'] || {}
52
+ end
53
+
54
+ # @return [Binding] Template binding
55
+ def get_binding
56
+ binding.taint
57
+ end
58
+
59
+ # Property lookup helper
60
+ #
61
+ # @overload p(name, default_value)
62
+ # Returns property value or default value if property not set
63
+ # @param [String] name Property name
64
+ # @param [Object] default_value Default value
65
+ # @return [Object] Property value
66
+ #
67
+ # @overload p(names, default_value)
68
+ # Returns first property from the list that is set or default value if
69
+ # none of them are set
70
+ # @param [Array<String>] names Property names
71
+ # @param [Object] default_value Default value
72
+ # @return [Object] Property value
73
+ #
74
+ # @overload p(names)
75
+ # Looks up first property from the list that is set, raises an error
76
+ # if none of them are set.
77
+ # @param [Array<String>] names Property names
78
+ # @return [Object] Property value
79
+ # @raise [Bosh::Common::UnknownProperty]
80
+ #
81
+ # @overload p(name)
82
+ # Looks up property and raises an error if it's not set
83
+ # @param [String] name Property name
84
+ # @return [Object] Property value
85
+ # @raise [Bosh::Common::UnknownProperty]
86
+ def p(*args)
87
+ names = Array(args[0])
88
+
89
+ names.each do |name|
90
+ result = lookup_property(@raw_properties, name)
91
+ return result unless result.nil?
92
+ end
93
+
94
+ return args[1] if args.length == 2
95
+ raise UnknownProperty.new(names)
96
+ end
97
+
98
+ def link(name)
99
+ link_spec = lookup_property(@links, name)
100
+ raise UnknownLink.new(name) if link_spec.nil?
101
+
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'])
105
+ end
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
+ )
122
+ end
123
+ raise UnknownLink.new(name)
124
+ end
125
+
126
+ # Run a block of code if all given properties are defined
127
+ # @param [Array<String>] names Property names
128
+ # @yield [Object] property values
129
+ def if_p(*names)
130
+ values = names.map do |name|
131
+ value = lookup_property(@raw_properties, name)
132
+ return ActiveElseBlock.new(self) if value.nil?
133
+ value
134
+ end
135
+
136
+ yield *values
137
+ InactiveElseBlock.new
138
+ end
139
+
140
+ # Run a block of code if the link given exists
141
+ # @param [String] name of the link
142
+ # @yield [Object] link, which is an array of instances
143
+ def if_link(name)
144
+ link_spec = lookup_property(@links, name)
145
+ if link_spec.nil? || !link_spec.has_key?('instances')
146
+ return ActiveElseBlock.new(self)
147
+ else
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'])
150
+ end
151
+
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)
153
+ InactiveElseBlock.new
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ # @return [Object] Object representation where all hashes are unrolled
160
+ # into OpenStruct objects. This exists mostly for backward
161
+ # compatibility, as it doesn't provide good error reporting.
162
+ def openstruct(object, open_struct_klass=OpenStruct)
163
+ case object
164
+ when Hash
165
+ mapped = object.inject({}) do |h, (k, v)|
166
+ h[k] = openstruct(v, open_struct_klass); h
167
+ end
168
+ open_struct_klass.new(mapped)
169
+ when Array
170
+ object.map { |item| openstruct(item, open_struct_klass) }
171
+ else
172
+ object
173
+ end
174
+ end
175
+
176
+ class BackCompatOpenStruct < OpenStruct
177
+ def methods(regular=true)
178
+ if regular
179
+ super(regular)
180
+ else
181
+ marshal_dump.keys.map(&:to_sym)
182
+ end
183
+ end
184
+ end
185
+
186
+ class ActiveElseBlock
187
+ def initialize(template_context)
188
+ @context = template_context
189
+ end
190
+
191
+ def else
192
+ yield
193
+ end
194
+
195
+ def else_if_p(*names, &block)
196
+ @context.if_p(*names, &block)
197
+ end
198
+
199
+ def else_if_link(name, &block)
200
+ @context.if_link(name, &block)
201
+ end
202
+ end
203
+
204
+ class InactiveElseBlock
205
+ def else
206
+ end
207
+
208
+ def else_if_p(*names)
209
+ InactiveElseBlock.new
210
+ end
211
+
212
+ def else_if_link(name)
213
+ InactiveElseBlock.new
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,6 @@
1
+ module Bosh
2
+ module Template
3
+ class EvaluationFailed < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,58 @@
1
+ require 'bosh/template/property_helper'
2
+
3
+ module Bosh
4
+ module Template
5
+ class EvaluationLink
6
+ include PropertyHelper
7
+
8
+ attr_reader :instances
9
+ attr_reader :properties
10
+
11
+ def initialize(instances, properties, instance_group, default_network, deployment_name, root_domain, dns_encoder)
12
+ @instances = instances
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
19
+ end
20
+
21
+ def p(*args)
22
+ names = Array(args[0])
23
+
24
+ names.each do |name|
25
+ result = lookup_property(@properties, name)
26
+ return result unless result.nil?
27
+ end
28
+
29
+ return args[1] if args.length == 2
30
+ raise UnknownProperty.new(names)
31
+ end
32
+
33
+ def if_p(*names)
34
+ values = names.map do |name|
35
+ value = lookup_property(@properties, name)
36
+ return Bosh::Template::EvaluationContext::ActiveElseBlock.new(self) if value.nil?
37
+ value
38
+ end
39
+
40
+ yield *values
41
+ Bosh::Template::EvaluationContext::InactiveElseBlock.new
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
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,50 @@
1
+ require 'bosh/template/property_helper'
2
+
3
+ module Bosh
4
+ module Template
5
+ class EvaluationLinkInstance
6
+ include PropertyHelper
7
+
8
+ attr_reader :name
9
+ attr_reader :index
10
+ attr_reader :id
11
+ attr_reader :az
12
+ attr_reader :address
13
+ attr_reader :properties
14
+ attr_reader :bootstrap
15
+
16
+ def initialize(name, index, id, az, address, properties, bootstrap)
17
+ @name = name
18
+ @index = index
19
+ @id = id
20
+ @az = az
21
+ @address = address
22
+ @properties = properties
23
+ @bootstrap = bootstrap
24
+ end
25
+
26
+ def p(*args)
27
+ names = Array(args[0])
28
+
29
+ names.each do |name|
30
+ result = lookup_property(@properties, name)
31
+ return result unless result.nil?
32
+ end
33
+
34
+ return args[1] if args.length == 2
35
+ raise UnknownProperty.new(names)
36
+ end
37
+
38
+ def if_p(*names)
39
+ values = names.map do |name|
40
+ value = lookup_property(@properties, name)
41
+ return ActiveElseBlock.new(self) if value.nil?
42
+ value
43
+ end
44
+
45
+ yield *values
46
+ InactiveElseBlock.new
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,6 @@
1
+ module Bosh
2
+ module Template
3
+ class InvalidPropertyType < StandardError
4
+ end
5
+ end
6
+ 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
@@ -0,0 +1,90 @@
1
+ require 'bosh/template/invalid_property_type'
2
+
3
+ module Bosh
4
+ module Template
5
+ module PropertyHelper
6
+ # Copies property with a given name from src to dst.
7
+ # @param [Hash] dst Property destination
8
+ # @param [Hash] src Property source
9
+ # @param [String] name Property name (dot-separated)
10
+ # @param [Object] default Default value (if property is not in src)
11
+ def copy_property(dst, src, name, default = nil)
12
+ keys = name.split(".")
13
+ src_ref = src
14
+ dst_ref = dst
15
+
16
+ keys.each do |key|
17
+ unless src_ref.is_a?(Hash)
18
+ raise Bosh::Template::InvalidPropertyType,
19
+ "Property '#{name}' expects a hash, but received '#{src_ref.class}'"
20
+ end
21
+ src_ref = src_ref[key]
22
+ break if src_ref.nil? # no property with this name is src
23
+ end
24
+
25
+ keys[0..-2].each do |key|
26
+ dst_ref[key] ||= {}
27
+ dst_ref = dst_ref[key]
28
+ end
29
+
30
+ dst_ref[keys[-1]] ||= {}
31
+ dst_ref[keys[-1]] = src_ref.nil? ? default : src_ref
32
+ end
33
+
34
+ # @param [Hash] collection Property collection
35
+ # @param [String] name Dot-separated property name
36
+ def lookup_property(collection, name)
37
+ return nil if collection.nil?
38
+ keys = name.split(".")
39
+ ref = collection
40
+
41
+ keys.each do |key|
42
+ ref = ref[key]
43
+ return nil if ref.nil?
44
+ end
45
+
46
+ ref
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
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,20 @@
1
+ require 'erb'
2
+ require 'json'
3
+ require 'bosh/template/evaluation_context'
4
+
5
+ module Bosh
6
+ module Template
7
+ class Renderer
8
+ def initialize(options={})
9
+ @context = options.fetch(:context)
10
+ end
11
+
12
+ def render(template_name)
13
+ spec = JSON.parse(@context)
14
+ evaluation_context = EvaluationContext.new(spec, nil)
15
+ template = ERB.new(File.read(template_name), safe_level = nil, trim_mode = "-")
16
+ template.result(evaluation_context.get_binding)
17
+ end
18
+ end
19
+ end
20
+ 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
@@ -0,0 +1,9 @@
1
+ module Bosh
2
+ module Template
3
+ class UnknownLink < StandardError
4
+ def initialize(name)
5
+ super("Can't find link '#{name}'")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Bosh
2
+ module Template
3
+ class UnknownProperty < StandardError
4
+ attr_reader :name
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ super("Can't find property '#{name}'")
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module Bosh
2
+ module Template
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bosh-template
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pivotal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: semi_semantic
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.0
27
+ description: Renders bosh templates
28
+ email: support@cloudfoundry.com
29
+ executables:
30
+ - bosh-template
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - bin/bosh-template
36
+ - lib/bosh/template.rb
37
+ - lib/bosh/template/evaluation_context.rb
38
+ - lib/bosh/template/evaluation_failed.rb
39
+ - lib/bosh/template/evaluation_link.rb
40
+ - lib/bosh/template/evaluation_link_instance.rb
41
+ - lib/bosh/template/invalid_property_type.rb
42
+ - lib/bosh/template/manual_link_dns_encoder.rb
43
+ - lib/bosh/template/property_helper.rb
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
52
+ - lib/bosh/template/unknown_link.rb
53
+ - lib/bosh/template/unknown_property.rb
54
+ - lib/bosh/template/version.rb
55
+ homepage: https://github.com/cloudfoundry/bosh
56
+ licenses:
57
+ - Apache-2.0
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 1.9.3
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 2.5.1
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Renders bosh templates
79
+ test_files: []