diff options
-rw-r--r-- | RELEASE_NOTES.md | 2 | ||||
-rw-r--r-- | lib/chef/config.rb | 76 | ||||
-rw-r--r-- | lib/chef/exceptions.rb | 7 | ||||
-rw-r--r-- | lib/chef/file_content_management/tempfile.rb | 38 | ||||
-rw-r--r-- | lib/chef/shell.rb | 12 | ||||
-rw-r--r-- | spec/unit/config_spec.rb | 6 | ||||
-rw-r--r-- | spec/unit/provider/file/content_spec.rb | 25 |
7 files changed, 108 insertions, 58 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6faeacdde8..c20b486f43 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -52,7 +52,7 @@ when using `knife.rb`. Once third-party application developers have had sufficient time to adapt to the change, `knife.rb` will become deprecated and config.rb will be preferred. -## Boostrap Changes +## Bootstrap Changes Chef Client 12 introduces a set of changes to `knife bootstrap`. Here is the list of changes: diff --git a/lib/chef/config.rb b/lib/chef/config.rb index d3871c38e8..08ce1a8ff6 100644 --- a/lib/chef/config.rb +++ b/lib/chef/config.rb @@ -560,10 +560,12 @@ class Chef # used to update files. default :file_atomic_update, true - # If false file staging is will be done via tempfiles that are - # created under ENV['TMP'] otherwise tempfiles will be created in - # the directory that files are going to reside. - default :file_staging_uses_destdir, true + # There are 3 possible values for this configuration setting. + # true => file staging is done in the destination directory + # false => file staging is done via tempfiles under ENV['TMP'] + # :auto => file staging will try using destination directory if possible and + # will fall back to ENV['TMP'] if destination directory is not usable. + default :file_staging_uses_destdir, :auto # Exit if another run is in progress and the chef-client is unable to # get the lock before time expires. If nil, no timeout is enforced. (Exits @@ -623,44 +625,44 @@ class Chef # # If there is no 'locale -a' then we return 'en_US.UTF-8' since that is the most commonly # available English UTF-8 locale. However, all modern POSIXen should support 'locale -a'. - default :internal_locale do - begin - # https://github.com/opscode/chef/issues/2181 - # Some systems have the `locale -a` command, but the result has - # invalid characters for the default encoding. - # - # For example, on CentOS 6 with ENV['LANG'] = "en_US.UTF-8", - # `locale -a`.split fails with ArgumentError invalid UTF-8 encoding. - locales = shell_out_with_systems_locale("locale -a").stdout.split - case - when locales.include?('C.UTF-8') - 'C.UTF-8' - when locales.include?('en_US.UTF-8'), locales.include?('en_US.utf8') - 'en_US.UTF-8' - when locales.include?('en.UTF-8') - 'en.UTF-8' - else - # Will match en_ZZ.UTF-8, en_ZZ.utf-8, en_ZZ.UTF8, en_ZZ.utf8 - guesses = locales.select { |l| l =~ /^en_.*UTF-?8$/i } - unless guesses.empty? - guessed_locale = guesses.first - # Transform into the form en_ZZ.UTF-8 - guessed_locale.gsub(/UTF-?8$/i, "UTF-8") - else - Chef::Log.warn "Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support." - 'C' - end - end - rescue - if Chef::Platform.windows? - Chef::Log.debug "Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else." + def self.guess_internal_locale + # https://github.com/opscode/chef/issues/2181 + # Some systems have the `locale -a` command, but the result has + # invalid characters for the default encoding. + # + # For example, on CentOS 6 with ENV['LANG'] = "en_US.UTF-8", + # `locale -a`.split fails with ArgumentError invalid UTF-8 encoding. + locales = shell_out_with_systems_locale("locale -a").stdout.split + case + when locales.include?('C.UTF-8') + 'C.UTF-8' + when locales.include?('en_US.UTF-8'), locales.include?('en_US.utf8') + 'en_US.UTF-8' + when locales.include?('en.UTF-8') + 'en.UTF-8' + else + # Will match en_ZZ.UTF-8, en_ZZ.utf-8, en_ZZ.UTF8, en_ZZ.utf8 + guesses = locales.select { |l| l =~ /^en_.*UTF-?8$/i } + unless guesses.empty? + guessed_locale = guesses.first + # Transform into the form en_ZZ.UTF-8 + guessed_locale.gsub(/UTF-?8$/i, "UTF-8") else - Chef::Log.debug "No usable locale -a command found, assuming you have en_US.UTF-8 installed." + Chef::Log.warn "Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support." + 'C' end - 'en_US.UTF-8' end + rescue + if Chef::Platform.windows? + Chef::Log.debug "Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else." + else + Chef::Log.debug "No usable locale -a command found, assuming you have en_US.UTF-8 installed." + end + 'en_US.UTF-8' end + default :internal_locale, guess_internal_locale + # Force UTF-8 Encoding, for when we fire up in the 'C' locale or other strange locales (e.g. # japanese windows encodings). If we do not do this, then knife upload will fail when a cookbook's # README.md has UTF-8 characters that do not encode in whatever surrounding encoding we have been diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 25f08455fc..93fdd414e4 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -126,6 +126,13 @@ class Chef class CannotDetermineHomebrewOwner < Package; end + # Can not create staging file during file deployment + class FileContentStagingError < RuntimeError + def initialize(errors) + super "Staging tempfile can not be created during file deployment.\n Errors: #{errors.join('\n')}!" + end + end + # A different version of a cookbook was added to a # VersionedRecipeList than the one already there. class CookbookVersionConflict < ArgumentError ; end diff --git a/lib/chef/file_content_management/tempfile.rb b/lib/chef/file_content_management/tempfile.rb index 61a5ce2a7c..2dde0ce21b 100644 --- a/lib/chef/file_content_management/tempfile.rb +++ b/lib/chef/file_content_management/tempfile.rb @@ -35,7 +35,22 @@ class Chef private def tempfile_open - tf = ::Tempfile.open(tempfile_basename, tempfile_dirname) + tf = nil + errors = [ ] + + tempfile_dirnames.each do |tempfile_dirname| + begin + tf = ::Tempfile.open(tempfile_basename, tempfile_dirname) + break + rescue SystemCallError => e + message = "Creating temp file under '#{tempfile_dirname}' failed with: '#{e.message}'" + Chef::Log.debug(message) + errors << message + end + end + + raise Chef::Exceptions::FileContentStagingError(errors) if tf.nil? + # We always process the tempfile in binmode so that we # preserve the line endings of the content. tf.binmode @@ -53,16 +68,29 @@ class Chef basename end - def tempfile_dirname + # Returns the possible directories for the tempfile to be created in. + def tempfile_dirnames # in why-run mode we need to create a Tempfile to compare against, which we will never # wind up deploying, but our enclosing directory for the destdir may not exist yet, so # instead we can reliably always create a Tempfile to compare against in Dir::tmpdir - if Chef::Config[:file_staging_uses_destdir] && !Chef::Config[:why_run] - ::File.dirname(@new_resource.path) + if Chef::Config[:why_run] + [ Dir.tmpdir ] else - Dir::tmpdir + case Chef::Config[:file_staging_uses_destdir] + when :auto + # In auto mode we try the destination directory first and fallback to ENV['TMP'] if + # that doesn't work. + [ ::File.dirname(@new_resource.path), Dir.tmpdir ] + when true + [ ::File.dirname(@new_resource.path) ] + when false + [ Dir.tmpdir ] + else + raise Chef::Exceptions::ConfigurationError, "Unknown setting '#{Chef::Config[:file_staging_uses_destdir]}' for Chef::Config[:file_staging_uses_destdir]. Possible values are :auto, true or false." + end end end + end end end diff --git a/lib/chef/shell.rb b/lib/chef/shell.rb index c5b8296c63..fed32b3795 100644 --- a/lib/chef/shell.rb +++ b/lib/chef/shell.rb @@ -308,17 +308,9 @@ FOOTER elsif ENV['HOME'] && ::File.exist?(File.join(ENV['HOME'], '.chef', 'chef_shell.rb')) File.join(ENV['HOME'], '.chef', 'chef_shell.rb') elsif config[:solo] - if Chef::Platform.windows? - "C:\\chef\\solo.rb" - else - "/etc/chef/solo.rb" - end + Chef::Config.platform_specific_path("/etc/chef/solo.rb") elsif config[:client] - if Chef::Platform.windows? - "C:\\chef\\client.rb" - else - "/etc/chef/client.rb" - end + Chef::Config.platform_specific_path("/etc/chef/client.rb") else nil end diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb index fb782da2c7..58fb229c96 100644 --- a/spec/unit/config_spec.rb +++ b/spec/unit/config_spec.rb @@ -434,7 +434,7 @@ describe Chef::Config do expect(Chef::Log).to_not receive(:warn).with(/Please install an English UTF-8 locale for Chef to use/) expect(Chef::Log).to_not receive(:debug).with(/Defaulting to locale en_US.UTF-8 on Windows/) expect(Chef::Log).to_not receive(:debug).with(/No usable locale -a command found/) - expect(Chef::Config[:internal_locale]).to eq expected_locale + expect(Chef::Config.guess_internal_locale).to eq expected_locale end end @@ -485,7 +485,7 @@ describe Chef::Config do it "should fall back to C locale" do expect(Chef::Log).to receive(:warn).with("Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.") - expect(Chef::Config[:internal_locale]).to eq 'C' + expect(Chef::Config.guess_internal_locale).to eq 'C' end end @@ -502,7 +502,7 @@ describe Chef::Config do else expect(Chef::Log).to receive(:debug).with("No usable locale -a command found, assuming you have en_US.UTF-8 installed.") end - expect(Chef::Config[:internal_locale]).to eq "en_US.UTF-8" + expect(Chef::Config.guess_internal_locale).to eq "en_US.UTF-8" end end end diff --git a/spec/unit/provider/file/content_spec.rb b/spec/unit/provider/file/content_spec.rb index db0753bf8c..0a45d15bc9 100644 --- a/spec/unit/provider/file/content_spec.rb +++ b/spec/unit/provider/file/content_spec.rb @@ -70,12 +70,34 @@ describe Chef::Provider::File::Content do expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be_falsey end - it "returns a tempfile in the destdir when :file_desployment_uses_destdir is not set" do + it "returns a tempfile in the destdir when :file_deployment_uses_destdir is set" do Chef::Config[:file_staging_uses_destdir] = true expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be_falsey expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be_truthy end + context "when creating a tempfiles in destdir fails" do + let(:enclosing_directory) { + canonicalize_path("/nonexisting/path") + } + + it "returns a tempfile in the tempdir when :file_deployment_uses_destdir is set to :auto" do + Chef::Config[:file_staging_uses_destdir] = :auto + expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be_truthy + expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be_falsey + end + + it "fails when :file_desployment_uses_destdir is set" do + Chef::Config[:file_staging_uses_destdir] = true + expect{content.tempfile}.to raise_error + end + + it "returns a tempfile in the tempdir when :file_desployment_uses_destdir is not set" do + expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be_truthy + expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be_falsey + end + end + end describe "when the resource does not have a content attribute set" do @@ -90,4 +112,3 @@ describe Chef::Provider::File::Content do end end - |