diff options
author | sersut <serdar@opscode.com> | 2014-03-30 13:45:00 -0700 |
---|---|---|
committer | sersut <serdar@opscode.com> | 2014-03-30 13:45:00 -0700 |
commit | 7a1778fb309423114462d578e01ba0e00108010f (patch) | |
tree | 792e1fbafdca12725edf923cdad22536f3124fc3 /lib/chef/cookbook | |
parent | 0d097217dda26ac5551d1ad24132d9e53a62e0fb (diff) | |
parent | cacf2a53a3b789829dd6b5b2956e07cc1aa42931 (diff) | |
download | chef-11.12.0.rc.0.tar.gz |
Merge branch 'master' into 11-stable11.12.0.rc.0
Merging mater branch for RC version.
Diffstat (limited to 'lib/chef/cookbook')
-rw-r--r-- | lib/chef/cookbook/chefignore.rb | 12 | ||||
-rw-r--r-- | lib/chef/cookbook/metadata.rb | 34 | ||||
-rw-r--r-- | lib/chef/cookbook/synchronizer.rb | 4 | ||||
-rw-r--r-- | lib/chef/cookbook/syntax_check.rb | 121 |
4 files changed, 154 insertions, 17 deletions
diff --git a/lib/chef/cookbook/chefignore.rb b/lib/chef/cookbook/chefignore.rb index 17c000350d..aa9345e64e 100644 --- a/lib/chef/cookbook/chefignore.rb +++ b/lib/chef/cookbook/chefignore.rb @@ -25,7 +25,11 @@ class Chef attr_reader :ignores def initialize(ignore_file_or_repo) + # Check the 'ignore_file_or_repo' path first and then look in the parent directory + # to handle both the chef repo cookbook layout and a standalone cookbook @ignore_file = find_ignore_file(ignore_file_or_repo) + @ignore_file = find_ignore_file(File.dirname(ignore_file_or_repo)) unless readable_file_or_symlink?(@ignore_file) + @ignores = parse_ignore_file end @@ -43,8 +47,7 @@ class Chef def parse_ignore_file ignore_globs = [] - if File.exist?(@ignore_file) && File.readable?(@ignore_file) && - (File.file?(@ignore_file) || File.symlink?(@ignore_file)) + if readable_file_or_symlink?(@ignore_file) File.foreach(@ignore_file) do |line| ignore_globs << line.strip unless line =~ COMMENTS_AND_WHITESPACE end @@ -61,6 +64,11 @@ class Chef File.join(path, 'chefignore') end end + + def readable_file_or_symlink?(path) + File.exist?(@ignore_file) && File.readable?(@ignore_file) && + (File.file?(@ignore_file) || File.symlink?(@ignore_file)) + end end end end diff --git a/lib/chef/cookbook/metadata.rb b/lib/chef/cookbook/metadata.rb index b9b32c8224..32597490d3 100644 --- a/lib/chef/cookbook/metadata.rb +++ b/lib/chef/cookbook/metadata.rb @@ -391,14 +391,14 @@ class Chef :description => { :kind_of => String }, :choice => { :kind_of => [ Array ], :default => [] }, :calculated => { :equal_to => [ true, false ], :default => false }, - :type => { :equal_to => [ "string", "array", "hash", "symbol" ], :default => "string" }, + :type => { :equal_to => [ "string", "array", "hash", "symbol", "boolean", "numeric" ], :default => "string" }, :required => { :equal_to => [ "required", "recommended", "optional", true, false ], :default => "optional" }, :recipes => { :kind_of => [ Array ], :default => [] }, - :default => { :kind_of => [ String, Array, Hash ] } + :default => { :kind_of => [ String, Array, Hash, Symbol, Numeric, TrueClass, FalseClass ] } } ) options[:required] = remap_required_attribute(options[:required]) unless options[:required].nil? - validate_string_array(options[:choice]) + validate_choice_array(options) validate_calculated_default_rule(options) validate_choice_default_rule(options) @@ -546,6 +546,34 @@ INVALID end end + # Validate the choice of the options hash + # + # Raise an exception if the members of the array do not match the defaults + # === Parameters + # opts<Hash>:: The options hash + def validate_choice_array(opts) + if opts[:choice].kind_of?(Array) + case opts[:type] + when "string" + validator = [ String ] + when "array" + validator = [ Array ] + when "hash" + validator = [ Hash ] + when "symbol" + validator = [ Symbol ] + when "boolean" + validator = [ TrueClass, FalseClass ] + when "numeric" + validator = [ Numeric ] + end + + opts[:choice].each do |choice| + validate( {:choice => choice}, {:choice => {:kind_of => validator}} ) + end + end + end + # For backwards compatibility, remap Boolean values to String # true is mapped to "required" # false is mapped to "optional" diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb index 4522323fac..fc5d16617c 100644 --- a/lib/chef/cookbook/synchronizer.rb +++ b/lib/chef/cookbook/synchronizer.rb @@ -92,7 +92,7 @@ class Chef # === Returns # true:: Always returns true def sync_cookbooks - Chef::Log.info("Loading cookbooks [#{cookbook_names.sort.join(', ')}]") + Chef::Log.info("Loading cookbooks [#{cookbooks.map {|ckbk| ckbk.name + '@' + ckbk.version}.join(', ')}]") Chef::Log.debug("Cookbooks detail: #{cookbooks.inspect}") clear_obsoleted_cookbooks @@ -136,7 +136,7 @@ class Chef # valid_cache_entries<Hash>:: Out-param; Added to this hash are the files that # were referred to by this cookbook def sync_cookbook(cookbook) - Chef::Log.debug("Synchronizing cookbook #{cookbook.name}") + Chef::Log.debug("Synchronizing cookbook #{cookbook.name} #{cookbook.version}") # files and templates are lazily loaded, and will be done later. diff --git a/lib/chef/cookbook/syntax_check.rb b/lib/chef/cookbook/syntax_check.rb index 59888e2ba3..effc7dd01d 100644 --- a/lib/chef/cookbook/syntax_check.rb +++ b/lib/chef/cookbook/syntax_check.rb @@ -17,6 +17,8 @@ # require 'pathname' +require 'stringio' +require 'erubis' require 'chef/mixin/shell_out' require 'chef/mixin/checksum' @@ -75,6 +77,8 @@ class Chef # validated. attr_reader :validated_files + attr_reader :chefignore + # Creates a new SyntaxCheck given the +cookbook_name+ and a +cookbook_path+. # If no +cookbook_path+ is given, +Chef::Config.cookbook_path+ is used. def self.for_cookbook(cookbook_name, cookbook_path=nil) @@ -90,11 +94,9 @@ class Chef # cookbook_path::: the (on disk) path to the cookbook def initialize(cookbook_path) @cookbook_path = cookbook_path - @validated_files = PersistentSet.new - end + @chefignore ||= Chefignore.new(cookbook_path) - def chefignore - @chefignore ||= Chefignore.new(File.dirname(cookbook_path)) + @validated_files = PersistentSet.new end def remove_ignored_files(file_list) @@ -161,28 +163,127 @@ class Chef def validate_template(erb_file) Chef::Log.debug("Testing template #{erb_file} for syntax errors...") - result = shell_out("erubis -x #{erb_file} | ruby -c") + if validate_inline? + validate_erb_file_inline(erb_file) + else + validate_erb_via_subcommand(erb_file) + end + end + + def validate_ruby_file(ruby_file) + Chef::Log.debug("Testing #{ruby_file} for syntax errors...") + if validate_inline? + validate_ruby_file_inline(ruby_file) + else + validate_ruby_by_subcommand(ruby_file) + end + end + + # Whether or not we're running on a version of ruby that can support + # inline validation. Inline validation relies on the `RubyVM` features + # introduced with ruby 1.9, so 1.8 cannot be supported. + def validate_inline? + defined?(RubyVM::InstructionSequence) + end + + # Validate the ruby code in an erb template. Uses RubyVM to do syntax + # checking, so callers should check #validate_inline? before calling. + def validate_erb_file_inline(erb_file) + old_stderr = $stderr + + engine = Erubis::Eruby.new + engine.convert!(IO.read(erb_file)) + + ruby_code = engine.src + + # Even when we're compiling the code w/ RubyVM, syntax errors just + # print to $stderr. We want to capture this and handle the printing + # ourselves, so we must temporarily swap $stderr to capture the output. + tmp_stderr = $stderr = StringIO.new + + abs_path = File.expand_path(erb_file) + RubyVM::InstructionSequence.new(ruby_code, erb_file, abs_path, 0) + + true + rescue SyntaxError + $stderr = old_stderr + invalid_erb_file(erb_file, tmp_stderr.string) + false + ensure + # be paranoid about setting stderr back to the old value. + $stderr = old_stderr if defined?(old_stderr) && old_stderr + end + + # Validate the ruby code in an erb template. Pipes the output of `erubis + # -x` to `ruby -c`, so it works with any ruby version, but is much slower + # than the inline version. + # -- + # TODO: This can be removed when ruby 1.8 support is dropped. + def validate_erb_via_subcommand(erb_file) + result = shell_out("erubis -x #{erb_file} | #{ruby} -c") result.error! true rescue Mixlib::ShellOut::ShellCommandFailed + invalid_erb_file(erb_file, result.stderr) + false + end + + # Debug a syntax error in a template. + def invalid_erb_file(erb_file, error_message) file_relative_path = erb_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1] Chef::Log.fatal("Erb template #{file_relative_path} has a syntax error:") - result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) } + error_message.each_line { |l| Chef::Log.fatal(l.chomp) } + nil + end + + # Validate the syntax of a ruby file. Uses (Ruby 1.9+ only) RubyVM to + # compile the code without evaluating it or spawning a new process. + # Callers should check #validate_inline? before calling. + def validate_ruby_file_inline(ruby_file) + # Even when we're compiling the code w/ RubyVM, syntax errors just + # print to $stderr. We want to capture this and handle the printing + # ourselves, so we must temporarily swap $stderr to capture the output. + old_stderr = $stderr + tmp_stderr = $stderr = StringIO.new + abs_path = File.expand_path(ruby_file) + file_content = IO.read(abs_path) + RubyVM::InstructionSequence.new(file_content, ruby_file, abs_path, 0) + true + rescue SyntaxError + $stderr = old_stderr + invalid_ruby_file(ruby_file, tmp_stderr.string) false + ensure + # be paranoid about setting stderr back to the old value. + $stderr = old_stderr if defined?(old_stderr) && old_stderr end - def validate_ruby_file(ruby_file) - Chef::Log.debug("Testing #{ruby_file} for syntax errors...") - result = shell_out("ruby -c #{ruby_file}") + # Validate the syntax of a ruby file by shelling out to `ruby -c`. Should + # work for all ruby versions, but is slower and uses more resources than + # the inline strategy. + def validate_ruby_by_subcommand(ruby_file) + result = shell_out("#{ruby} -c #{ruby_file}") result.error! true rescue Mixlib::ShellOut::ShellCommandFailed + invalid_ruby_file(ruby_file, result.stderr) + false + end + + # Debugs ruby syntax errors by printing the path to the file and any + # diagnostic info given in +error_message+ + def invalid_ruby_file(ruby_file, error_message) file_relative_path = ruby_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1] Chef::Log.fatal("Cookbook file #{file_relative_path} has a ruby syntax error:") - result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) } + error_message.each_line { |l| Chef::Log.fatal(l.chomp) } false end + # Returns the full path to the running ruby. + def ruby + Gem.ruby + end + end end end |