summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsersut <serdar@opscode.com>2013-05-30 13:38:32 -0700
committersersut <serdar@opscode.com>2013-05-30 13:44:50 -0700
commit32172b48de16b884a539ee4358b52d711f86b368 (patch)
tree79613bb71025a7a46f4b8ce8b868e6d56a628e44
parentaeee4bf96582769207780311b7b882c387656567 (diff)
downloadchef-32172b48de16b884a539ee4358b52d711f86b368.tar.gz
Make sure that windows line endings are protected during template rendering.
-rw-r--r--lib/chef/deprecation/mixin/template.rb49
-rw-r--r--lib/chef/deprecation/provider/template.rb4
-rw-r--r--lib/chef/mixin/template.rb54
-rw-r--r--lib/chef/provider/template/content.rb9
-rw-r--r--spec/data/cookbooks/openldap/templates/default/all_windows_line_endings.erb4
-rw-r--r--spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb4
-rw-r--r--spec/data/cookbooks/openldap/templates/default/some_windows_line_endings.erb4
-rw-r--r--spec/functional/resource/template_spec.rb55
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb5
-rw-r--r--spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb5
-rw-r--r--spec/unit/mixin/template_spec.rb78
11 files changed, 194 insertions, 77 deletions
diff --git a/lib/chef/deprecation/mixin/template.rb b/lib/chef/deprecation/mixin/template.rb
new file mode 100644
index 0000000000..36d18ad90d
--- /dev/null
+++ b/lib/chef/deprecation/mixin/template.rb
@@ -0,0 +1,49 @@
+#
+# Author:: Serdar Sutay (<serdar@opscode.com>)
+# Copyright:: Copyright (c) 2013 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'tempfile'
+require 'erubis'
+
+class Chef
+ module Deprecation
+ module Mixin
+ # == Deprecation::Provider::Mixin::Template
+ # This module contains the deprecated functions of
+ # Chef::Mixin::Template. These functions are refactored to different
+ # components. They are frozen and will be removed in Chef 12.
+ #
+
+ module Template
+ def render_template(template, context)
+ begin
+ eruby = Erubis::Eruby.new(template)
+ output = eruby.evaluate(context)
+ rescue Object => e
+ raise TemplateError.new(e, template, context)
+ end
+ Tempfile.open("chef-rendered-template") do |tempfile|
+ tempfile.print(output)
+ tempfile.close
+ yield tempfile
+ end
+ end
+ end
+ end
+ end
+end
+
diff --git a/lib/chef/deprecation/provider/template.rb b/lib/chef/deprecation/provider/template.rb
index 0f93c637f6..3d8071f00b 100644
--- a/lib/chef/deprecation/provider/template.rb
+++ b/lib/chef/deprecation/provider/template.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/mixin/template'
+require 'chef/deprecation/mixin/template'
class Chef
module Deprecation
@@ -29,7 +29,7 @@ class Chef
#
module Template
- include Chef::Mixin::Template
+ include Chef::Deprecation::Mixin::Template
def template_finder
@template_finder ||= begin
diff --git a/lib/chef/mixin/template.rb b/lib/chef/mixin/template.rb
index 9892447ca4..d6cf564f40 100644
--- a/lib/chef/mixin/template.rb
+++ b/lib/chef/mixin/template.rb
@@ -93,14 +93,47 @@ class Chef
partial_context._extend_modules(@_extension_modules)
template_location = @template_finder.find(partial_name, options)
- eruby = Erubis::Eruby.new(IO.read(template_location))
- eruby.evaluate(partial_context)
+ _render_template(IO.binread(template_location), partial_context)
+ end
+
+ def render_template(template_location)
+ _render_template(IO.binread(template_location), self)
+ end
+
+ def render_template_from_string(template)
+ _render_template(template, self)
end
###
# INTERNAL PUBLIC API
###
+ def _render_template(template, context)
+ # CHEF-2991
+ # Erubis always emits unix line endings during template
+ # rendering. This results in automatic conversion of windows
+ # line endings to linux line endings if the original template
+ # contains windows line endings. In order to fix this we
+ # determine the line ending style of the template before
+ # rendering and convert the line endings of the output if needed
+ # If template contains any windows line endings we emit
+ # the template result with windows line endings.
+ windows_line_endings = template.include? "\r\n"
+
+ begin
+ eruby = Erubis::Eruby.new(template)
+ output = eruby.evaluate(context)
+ rescue Object => e
+ raise TemplateError.new(e, template, context)
+ end
+
+ if windows_line_endings
+ output = output.gsub("\n","\r\n")
+ end
+
+ output
+ end
+
def _extend_modules(module_names)
module_names.each do |mod|
[:node, :render].each do |core_method|
@@ -129,23 +162,6 @@ class Chef
end
end
-
- # Render a template with Erubis. Takes a template as a string, and a
- # context hash.
- def render_template(template, context)
- begin
- eruby = Erubis::Eruby.new(template)
- output = eruby.evaluate(context)
- rescue Object => e
- raise TemplateError.new(e, template, context)
- end
- Tempfile.open("chef-rendered-template") do |tempfile|
- tempfile.print(output)
- tempfile.close
- yield tempfile
- end
- end
-
class TemplateError < RuntimeError
attr_reader :original_exception, :context
SOURCE_CONTEXT_WINDOW = 2
diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb
index 9de9ad9ea2..f6cd470357 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -40,9 +40,12 @@ class Chef
context[:node] = @run_context.node
context[:template_finder] = template_finder
context._extend_modules(@new_resource.helper_modules)
- file = nil
- render_template(IO.read(template_location), context) { |t| file = t }
- file
+ output = context.render_template(template_location)
+
+ tempfile = Tempfile.open("chef-rendered-template")
+ tempfile.write(output)
+ tempfile.close
+ tempfile
end
def template_finder
diff --git a/spec/data/cookbooks/openldap/templates/default/all_windows_line_endings.erb b/spec/data/cookbooks/openldap/templates/default/all_windows_line_endings.erb
new file mode 100644
index 0000000000..727db12387
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/all_windows_line_endings.erb
@@ -0,0 +1,4 @@
+Template rendering libraries
+should support
+different line endings
+
diff --git a/spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb b/spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb
new file mode 100644
index 0000000000..e83c03b01a
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb
@@ -0,0 +1,4 @@
+Template rendering libraries
+should support
+different line endings
+
diff --git a/spec/data/cookbooks/openldap/templates/default/some_windows_line_endings.erb b/spec/data/cookbooks/openldap/templates/default/some_windows_line_endings.erb
new file mode 100644
index 0000000000..fa25999b6a
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/some_windows_line_endings.erb
@@ -0,0 +1,4 @@
+Template rendering libraries
+should support
+different line endings
+
diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb
index 6d42eeceac..f73f239b41 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -183,4 +183,59 @@ describe Chef::Resource::Template do
end
end
+
+ describe "when template source contains windows style line endings" do
+
+ include_context "diff disabled"
+
+ let (:expected_content) {
+ "Template rendering libraries\r\nshould support\r\ndifferent line endings\r\n\r\n"
+ }
+
+ context "for all lines" do
+ let!(:resource) do
+ r = create_resource
+ r.source "all_windows_line_endings.erb"
+ r
+ end
+
+ it "output should contain windows line endings" do
+ resource.run_action(:create)
+ IO.read(path).each_line do |line|
+ line.should end_with("\r\n")
+ end
+ end
+ end
+
+ context "for some lines" do
+ let!(:resource) do
+ r = create_resource
+ r.source "some_windows_line_endings.erb"
+ r
+ end
+
+ it "output should contain windows line endings" do
+ resource.run_action(:create)
+ IO.read(path).each_line do |line|
+ line.should end_with("\r\n")
+ end
+ end
+ end
+
+ context "for no lines" do
+ let!(:resource) do
+ r = create_resource
+ r.source "no_windows_line_endings.erb"
+ r
+ end
+
+ it "output should not contain windows line endings" do
+ resource.run_action(:create)
+ IO.read(path).each_line do |line|
+ line.should_not end_with("\r\n")
+ end
+ end
+ end
+ end
+
end
diff --git a/spec/unit/cookbook/syntax_check_spec.rb b/spec/unit/cookbook/syntax_check_spec.rb
index 7007d06920..f84c87a8fb 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -37,7 +37,10 @@ describe Chef::Cookbook::SyntaxCheck do
helper_test.erb
openldap_stuff.conf.erb
openldap_variable_stuff.conf.erb
- test.erb }
+ test.erb
+ some_windows_line_endings.erb
+ all_windows_line_endings.erb
+ no_windows_line_endings.erb }
@template_files = basenames.map { |f| File.join(cookbook_path, 'templates', 'default', f)}
end
diff --git a/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb b/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
index fa96845aca..b6d48d0255 100644
--- a/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
@@ -83,14 +83,15 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
@description = Chef::Formatters::ErrorDescription.new("Error Converging Resource:")
@template_class = Class.new { include Chef::Mixin::Template }
@template = @template_class.new
- @context = {:chef => "cool"}
+ @context = Chef::Mixin::Template::TemplateContext.new({})
+ @context[:chef] = "cool"
@resource = template("/tmp/foo.txt") do
mode "0644"
end
@error = begin
- @template.render_template("foo\nbar\nbaz\n<%= this_is_not_defined %>\nquin\nqunx\ndunno", @context) {|r| r}
+ @context.render_template_from_string("foo\nbar\nbaz\n<%= this_is_not_defined %>\nquin\nqunx\ndunno")
rescue Chef::Mixin::Template::TemplateError => e
e
end
diff --git a/spec/unit/mixin/template_spec.rb b/spec/unit/mixin/template_spec.rb
index 630f207732..aecbb5186c 100644
--- a/spec/unit/mixin/template_spec.rb
+++ b/spec/unit/mixin/template_spec.rb
@@ -18,33 +18,23 @@
require 'spec_helper'
-class TinyTemplateClass; include Chef::Mixin::Template; end
require 'cgi'
describe Chef::Mixin::Template, "render_template" do
before :each do
- @template = TinyTemplateClass.new
@context = Chef::Mixin::Template::TemplateContext.new({})
end
it "should render the template evaluated in the given context" do
@context[:foo] = "bar"
- @template.render_template("<%= @foo %>", @context) do |tmp|
- tmp.open.read.should == "bar"
- end
+ output = @context.render_template_from_string("<%= @foo %>")
+ output.should == "bar"
end
it "should provide a node method to access @node" do
@context[:node] = "tehShizzle"
- @template.render_template("<%= node %>", @context) do |tmp|
- tmp.open.read.should == "tehShizzle"
- end
- end
-
- it "should yield the tempfile it renders the template to" do
- @template.render_template("abcdef", {}) do |tempfile|
- tempfile.should be_kind_of(Tempfile)
- end
+ output = @context.render_template_from_string("<%= @node %>")
+ output.should == "tehShizzle"
end
describe "with a template resource" do
@@ -73,9 +63,8 @@ describe Chef::Mixin::Template, "render_template" do
end
it "should provide a render method" do
- @content_provider.render_template("before {<%= render 'test.erb' %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {We could be diving for pearls!\n} after"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'test.erb' %>} after")
+ output.should == "before {We could be diving for pearls!\n} after"
end
it "should render local files" do
@@ -84,9 +73,8 @@ describe Chef::Mixin::Template, "render_template" do
tf.puts "test"
tf.rewind
- @content_provider.render_template("before {<%= render '#{tf.path}', :local => true %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {test\n} after"
- end
+ output = @template_context.render_template_from_string("before {<%= render '#{tf.path}', :local => true %>} after")
+ output.should == "before {test\n} after"
ensure
tf.close
end
@@ -95,9 +83,8 @@ describe Chef::Mixin::Template, "render_template" do
it "should render partials from a different cookbook" do
@template_context[:template_finder] = Chef::Provider::TemplateFinder.new(@run_context, 'apache2', @node)
- @content_provider.render_template("before {<%= render 'test.erb', :cookbook => 'openldap' %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {We could be diving for pearls!\n} after"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'test.erb', :cookbook => 'openldap' %>} after")
+ output.should == "before {We could be diving for pearls!\n} after"
end
it "should render using the source argument if provided" do
@@ -106,9 +93,8 @@ describe Chef::Mixin::Template, "render_template" do
tf.puts "test"
tf.rewind
- @content_provider.render_template("before {<%= render 'something', :local => true, :source => '#{tf.path}' %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {test\n} after"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'something', :local => true, :source => '#{tf.path}' %>} after")
+ output.should == "before {test\n} after"
ensure
tf.close
end
@@ -117,49 +103,42 @@ describe Chef::Mixin::Template, "render_template" do
it "should pass the node to partials" do
@node.normal[:slappiness] = "happiness"
- @content_provider.render_template("before {<%= render 'openldap_stuff.conf.erb' %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {slappiness is happiness} after"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'openldap_stuff.conf.erb' %>} after")
+ output.should == "before {slappiness is happiness} after"
end
it "should pass the original variables to partials" do
@template_context[:secret] = 'candy'
- @content_provider.render_template("before {<%= render 'openldap_variable_stuff.conf.erb' %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {super secret is candy} after"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb' %>} after")
+ output == "before {super secret is candy} after"
end
it "should pass variables to partials" do
- @content_provider.render_template("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {:secret => 'whatever' } %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {super secret is whatever} after"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {:secret => 'whatever' } %>} after")
+ output.should == "before {super secret is whatever} after"
end
it "should pass variables to partials even if they are named the same" do
@template_context[:secret] = 'one'
- @content_provider.render_template("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {:secret => 'two' } %>} after <%= @secret %>", @template_context) do |tmp|
- tmp.open.read.should == "before {super secret is two} after one"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {:secret => 'two' } %>} after <%= @secret %>")
+ output.should == "before {super secret is two} after one"
end
it "should pass nil for missing variables in partials" do
- @content_provider.render_template("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {} %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {super secret is } after"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {} %>} after")
+ output.should == "before {super secret is } after"
- @content_provider.render_template("before {<%= render 'openldap_variable_stuff.conf.erb' %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {super secret is } after"
- end
+ output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb' %>} after")
+ output.should == "before {super secret is } after"
end
it "should render nested partials" do
path = File.expand_path(File.join(CHEF_SPEC_DATA, "partial_one.erb"))
- @content_provider.render_template("before {<%= render '#{path}', :local => true %>} after", @template_context) do |tmp|
- tmp.open.read.should == "before {partial one We could be diving for pearls!\n calling home\n} after"
- end
+ output = @template_context.render_template_from_string("before {<%= render '#{path}', :local => true %>} after")
+ output.should == "before {partial one We could be diving for pearls!\n calling home\n} after"
end
describe "when customizing the template context" do
@@ -196,8 +175,7 @@ describe Chef::Mixin::Template, "render_template" do
describe "when an exception is raised in the template" do
def do_raise
- @context = {:chef => "cool"}
- @template.render_template("foo\nbar\nbaz\n<%= this_is_not_defined %>\nquin\nqunx\ndunno", @context) {|r| r}
+ @context.render_template_from_string("foo\nbar\nbaz\n<%= this_is_not_defined %>\nquin\nqunx\ndunno")
end
it "should catch and re-raise the exception as a TemplateError" do
@@ -205,7 +183,7 @@ describe Chef::Mixin::Template, "render_template" do
end
it "should raise an error if an attempt is made to access node but it is nil" do
- lambda {@template.render_template("<%= node %>",{}) {|r| r}}.should raise_error(Chef::Mixin::Template::TemplateError)
+ lambda {@context.render_template_from_string("<%= node %>") {|r| r}}.should raise_error(Chef::Mixin::Template::TemplateError)
end
describe "the raised TemplateError" do