diff --git a/assets/no_properties.yml.erb b/assets/no_properties.yml.erb new file mode 100644 index 0000000..a788388 --- /dev/null +++ b/assets/no_properties.yml.erb @@ -0,0 +1 @@ +no_properties diff --git a/assets/no_properties_job.MF b/assets/no_properties_job.MF new file mode 100644 index 0000000..adee5e8 --- /dev/null +++ b/assets/no_properties_job.MF @@ -0,0 +1,10 @@ +--- +name: no_properties +packages: + - foopackage +templates: + no_properties.yml.erb: no_properties.yml + +consumes: +- name: barprovider + type: barprovider diff --git a/bosh_template_go.go b/bosh_template_go.go index 229e45f..325b153 100644 --- a/bosh_template_go.go +++ b/bosh_template_go.go @@ -1,3 +1,5 @@ +//go:generate rice embed-go + package boshgotemplate import ( @@ -6,8 +8,9 @@ import ( "os/exec" "path/filepath" + rice "github.com/GeertJohan/go.rice" "github.com/pkg/errors" - "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v2" ) const ( @@ -20,103 +23,7 @@ var ( // RubyBinary is the name of the ruby binary. Can be an absolute path. RubyBinary = "ruby" // RubyGemBinary is the name of the ruby gem binary. Can be an absolute path. - RubyGemBinary = "gem" - templateEvaluationContextRb = []byte(` -require "erb" -require "yaml" -require "bosh/template" -require 'fileutils' - -if $0 == __FILE__ - context_path, spec_path, instance_path, src_path, dst_path = *ARGV - - puts "Context file: #{context_path}" - puts "Instance file: #{instance_path}" - puts "Spec file: #{spec_path}" - puts "Template file: #{src_path}" - puts "Output file: #{dst_path}" - - # Load the context hash - context_hash = YAML.load_file(context_path) - - # Load the job spec - job_spec = YAML.load_file(spec_path) - - # Load the instace info - instance_info = YAML.load_file(instance_path) - - # Read the erb template - begin - perms = File.stat(src_path).mode - template = Bosh::Template::Test::Template.new(job_spec, src_path) - rescue Errno::ENOENT - raise "failed to read template file #{src_path}" - end - - # Build links - links = [] - if context_hash['properties'] && context_hash['properties']['bosh_containerization'] && context_hash['properties']['bosh_containerization']['consumes'] - context_hash['properties']['bosh_containerization']['consumes'].each_pair do |name, link| - next if link['instances'].empty? - - instances = [] - link['instances'].each do |link_instance| - instances << Bosh::Template::Test::InstanceSpec.new( - address: link_instance['address'], - az: link_instance['az'], - id: link_instance['id'], - index: link_instance['index'], - name: link_instance['name'], - bootstrap: link_instance['index'] == '0', - ) - end - links << Bosh::Template::Test::Link.new(name: name, instances: instances, properties: link['properties']) - end - end - - # Build instance - instance = Bosh::Template::Test::InstanceSpec.new( - address: instance_info['address'], - az: instance_info['az'], - bootstrap: instance_info['index'] == '0', - deployment: instance_info['deployment'], - id: instance_info['id'], - index: instance_info['index'], - ip: instance_info['ip'], - name: instance_info['name'], - networks: {'default' => {'ip' => instance_info['ip'], - 'dns_record_name' => instance_info['address'], - # TODO: Do we need more, like netmask and gateway? - # https://github.com/cloudfoundry/bosh-agent/blob/master/agent/applier/applyspec/v1_apply_spec_test.go - }}, - ) - - # Process the Template - output = template.render(context_hash['properties'], spec: instance, consumes: links) - - begin - # Open the output file - output_dir = File.dirname(dst_path) - FileUtils.mkdir_p(output_dir) - out_file = File.open(dst_path, 'w') - - # Write results to the output file - out_file.write(output) - - # Set the appropriate permissions on the output file - if File.basename(File.dirname(dst_path)) == 'bin' - out_file.chmod(0755) - else - out_file.chmod(perms) - end - rescue Errno::ENOENT, Errno::EACCES => e - out_file = nil - raise "failed to open output file #{dst_path}: #{e}" - ensure - out_file.close unless out_file.nil? - end -end -`) + RubyGemBinary = "gem" ) // EvaluationContext is the context passed to the erb renderer @@ -174,6 +81,12 @@ func (e *ERBRenderer) Render(inputFilePath, outputFilePath string) (returnErr er // Write the ruby class to a file rbClassFilePath := filepath.Join(tmpDir, rbClassFileName) + templateEvaluationContextRb, err := rice. + MustFindBox("rb"). + Bytes("template_evaluation_context.rb") + if err != nil { + return errors.Wrap(err, "failed to load ruby class") + } err = ioutil.WriteFile(rbClassFilePath, templateEvaluationContextRb, 0600) if err != nil { return errors.Wrap(err, "failed to write the rendering ruby class file") diff --git a/bosh_template_go_test.go b/bosh_template_go_test.go index ca04ec7..87b79b5 100644 --- a/bosh_template_go_test.go +++ b/bosh_template_go_test.go @@ -296,3 +296,28 @@ func TestRenderWithLinkProperty(t *testing.T) { assert.NoError(err) assert.Equal("toaster", string(output)) } + +func TestWithNilContext(t *testing.T) { + // Arrange + assert := assert.New(t) + erbFile := filepath.Join(testDir(), "assets", "no_properties.yml.erb") + jobSpecFile := filepath.Join(testDir(), "assets", "no_properties_job.MF") + + erbRenderer := NewERBRenderer( + &EvaluationContext{}, + &InstanceInfo{}, + jobSpecFile) + outDir, err := ioutil.TempDir("", "bosh-erb-render") + assert.NoError(err) + outFile := filepath.Join(outDir, "output") + + // Act + err = erbRenderer.Render(erbFile, outFile) + assert.NoError(err) + + output, err := ioutil.ReadFile(outFile) + + // Assert + assert.NoError(err) + assert.Equal("no_properties\n", string(output)) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5a9b7ee --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module github.com/viovanov/bosh-template-go + +go 1.12 + +require ( + github.com/GeertJohan/go.rice v1.0.0 + github.com/pkg/errors v0.8.1 + github.com/stretchr/testify v1.3.0 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..18e1a88 --- /dev/null +++ b/go.sum @@ -0,0 +1,25 @@ +github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= +github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ= +github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= +github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= +github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/rb/template_evaluation_context.rb b/rb/template_evaluation_context.rb new file mode 100644 index 0000000..f661375 --- /dev/null +++ b/rb/template_evaluation_context.rb @@ -0,0 +1,95 @@ +require "erb" +require "yaml" +require "bosh/template" +require "fileutils" + +if $0 == __FILE__ + context_path, spec_path, instance_path, src_path, dst_path = *ARGV + + puts "Context file: #{context_path}" + puts "Instance file: #{instance_path}" + puts "Spec file: #{spec_path}" + puts "Template file: #{src_path}" + puts "Output file: #{dst_path}" + + # Load the context hash + context_hash = YAML.load_file(context_path) + + # Load the job spec + job_spec = YAML.load_file(spec_path) + job_spec['properties'] = {} if job_spec['properties'].nil? + + # Load the instace info + instance_info = YAML.load_file(instance_path) + + # Read the erb template + begin + perms = File.stat(src_path).mode + template = Bosh::Template::Test::Template.new(job_spec, src_path) + rescue Errno::ENOENT + raise "failed to read template file #{src_path}" + end + + # Build links + links = [] + if context_hash['properties'] && context_hash['properties']['bosh_containerization'] && context_hash['properties']['bosh_containerization']['consumes'] + context_hash['properties']['bosh_containerization']['consumes'].each_pair do |name, link| + next if link['instances'].empty? + + instances = [] + link['instances'].each do |link_instance| + instances << Bosh::Template::Test::InstanceSpec.new( + address: link_instance['address'], + az: link_instance['az'], + id: link_instance['id'], + index: link_instance['index'], + name: link_instance['name'], + bootstrap: link_instance['index'] == '0', + ) + end + links << Bosh::Template::Test::Link.new(name: name, instances: instances, properties: link['properties']) + end + end + + # Build instance + instance = Bosh::Template::Test::InstanceSpec.new( + address: instance_info['address'], + az: instance_info['az'], + bootstrap: instance_info['index'] == '0', + deployment: instance_info['deployment'], + id: instance_info['id'], + index: instance_info['index'], + ip: instance_info['ip'], + name: instance_info['name'], + networks: {'default' => {'ip' => instance_info['ip'], + 'dns_record_name' => instance_info['address'], + # TODO: Do we need more, like netmask and gateway? + # https://github.com/cloudfoundry/bosh-agent/blob/master/agent/applier/applyspec/v1_apply_spec_test.go + }}, + ) + + # Process the Template + output = template.render(context_hash['properties'], spec: instance, consumes: links) + + begin + # Open the output file + output_dir = File.dirname(dst_path) + FileUtils.mkdir_p(output_dir) + out_file = File.open(dst_path, 'w') + + # Write results to the output file + out_file.write(output) + + # Set the appropriate permissions on the output file + if File.basename(File.dirname(dst_path)) == 'bin' + out_file.chmod(0755) + else + out_file.chmod(perms) + end + rescue Errno::ENOENT, Errno::EACCES => e + out_file = nil + raise "failed to open output file #{dst_path}: #{e}" + ensure + out_file.close unless out_file.nil? + end +end diff --git a/rice-box.go b/rice-box.go new file mode 100644 index 0000000..c2c8cd8 --- /dev/null +++ b/rice-box.go @@ -0,0 +1,43 @@ +package boshgotemplate + +import ( + "time" + + "github.com/GeertJohan/go.rice/embedded" +) + +func init() { + + // define files + file2 := &embedded.EmbeddedFile{ + Filename: "template_evaluation_context.rb", + FileModTime: time.Unix(1559256632, 0), + + Content: string("require \"erb\"\nrequire \"yaml\"\nrequire \"bosh/template\"\nrequire \"fileutils\"\n\nif $0 == __FILE__\n context_path, spec_path, instance_path, src_path, dst_path = *ARGV\n\n puts \"Context file: #{context_path}\"\n puts \"Instance file: #{instance_path}\"\n puts \"Spec file: #{spec_path}\"\n puts \"Template file: #{src_path}\"\n puts \"Output file: #{dst_path}\"\n\n # Load the context hash\n context_hash = YAML.load_file(context_path)\n\n # Load the job spec\n job_spec = YAML.load_file(spec_path)\n job_spec['properties'] = {} if job_spec['properties'].nil?\n\n # Load the instace info\n instance_info = YAML.load_file(instance_path)\n\n # Read the erb template\n begin\n perms = File.stat(src_path).mode\n template = Bosh::Template::Test::Template.new(job_spec, src_path)\n rescue Errno::ENOENT\n raise \"failed to read template file #{src_path}\"\n end\n\n # Build links\n links = []\n if context_hash['properties'] && context_hash['properties']['bosh_containerization'] && context_hash['properties']['bosh_containerization']['consumes']\n context_hash['properties']['bosh_containerization']['consumes'].each_pair do |name, link|\n next if link['instances'].empty?\n\n instances = []\n link['instances'].each do |link_instance|\n instances << Bosh::Template::Test::InstanceSpec.new(\n address: link_instance['address'],\n az: link_instance['az'],\n id: link_instance['id'],\n index: link_instance['index'],\n name: link_instance['name'],\n bootstrap: link_instance['index'] == '0',\n )\n end\n links << Bosh::Template::Test::Link.new(name: name, instances: instances, properties: link['properties'])\n end\n end\n\n # Build instance\n instance = Bosh::Template::Test::InstanceSpec.new(\n address: instance_info['address'],\n az: instance_info['az'],\n bootstrap: instance_info['index'] == '0',\n deployment: instance_info['deployment'],\n id: instance_info['id'],\n index: instance_info['index'],\n ip: instance_info['ip'],\n name: instance_info['name'],\n networks: {'default' => {'ip' => instance_info['ip'],\n 'dns_record_name' => instance_info['address'],\n # TODO: Do we need more, like netmask and gateway?\n # https://github.com/cloudfoundry/bosh-agent/blob/master/agent/applier/applyspec/v1_apply_spec_test.go\n }},\n )\n\n # Process the Template\n output = template.render(context_hash['properties'], spec: instance, consumes: links)\n\n begin\n # Open the output file\n output_dir = File.dirname(dst_path)\n FileUtils.mkdir_p(output_dir)\n out_file = File.open(dst_path, 'w')\n\n # Write results to the output file\n out_file.write(output)\n\n # Set the appropriate permissions on the output file\n if File.basename(File.dirname(dst_path)) == 'bin'\n out_file.chmod(0755)\n else\n out_file.chmod(perms)\n end\n rescue Errno::ENOENT, Errno::EACCES => e\n out_file = nil\n raise \"failed to open output file #{dst_path}: #{e}\"\n ensure\n out_file.close unless out_file.nil?\n end\nend\n"), + } + + // define dirs + dir1 := &embedded.EmbeddedDir{ + Filename: "", + DirModTime: time.Unix(1559254477, 0), + ChildFiles: []*embedded.EmbeddedFile{ + file2, // "template_evaluation_context.rb" + + }, + } + + // link ChildDirs + dir1.ChildDirs = []*embedded.EmbeddedDir{} + + // register embeddedBox + embedded.RegisterEmbeddedBox(`rb`, &embedded.EmbeddedBox{ + Name: `rb`, + Time: time.Unix(1559254477, 0), + Dirs: map[string]*embedded.EmbeddedDir{ + "": dir1, + }, + Files: map[string]*embedded.EmbeddedFile{ + "template_evaluation_context.rb": file2, + }, + }) +}