summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoradamedx <adamed@opscode.com>2013-08-02 10:01:15 -0700
committeradamedx <adamed@opscode.com>2013-08-02 10:01:15 -0700
commit491ddbbbe8e5037ec3321f26a0872e36b47bb665 (patch)
tree073048eedb7ed1e0d40744203b9d0752f72c2cca
parent324caf8614b356dbe6a0e3a6028c1e2cd3af33d7 (diff)
downloadchef-491ddbbbe8e5037ec3321f26a0872e36b47bb665.tar.gz
CHEF-4422 Truncate cache paths for remote files to stay within file system path length limits
-rw-r--r--lib/chef/provider/remote_file/cache_control_data.rb5
-rwxr-xr-xspec/functional/provider/remote_file/cache_control_data_spec.rb91
-rw-r--r--spec/unit/provider/remote_file/cache_control_data_spec.rb36
3 files changed, 131 insertions, 1 deletions
diff --git a/lib/chef/provider/remote_file/cache_control_data.rb b/lib/chef/provider/remote_file/cache_control_data.rb
index d21ad2da12..27c724620b 100644
--- a/lib/chef/provider/remote_file/cache_control_data.rb
+++ b/lib/chef/provider/remote_file/cache_control_data.rb
@@ -149,7 +149,10 @@ class Chef
end
def sanitized_cache_file_basename
- scrubbed_uri = uri.gsub(/\W/, '_')
+ # Scrub and truncate in accordance with the goals of keeping the name
+ # human-readable but within the bounds of local file system
+ # path length limits
+ scrubbed_uri = uri.gsub(/\W/, '_')[0..63]
uri_md5 = Chef::Digester.instance.generate_md5_checksum(StringIO.new(uri))
"#{scrubbed_uri}-#{uri_md5}.json"
end
diff --git a/spec/functional/provider/remote_file/cache_control_data_spec.rb b/spec/functional/provider/remote_file/cache_control_data_spec.rb
new file mode 100755
index 0000000000..20e0172252
--- /dev/null
+++ b/spec/functional/provider/remote_file/cache_control_data_spec.rb
@@ -0,0 +1,91 @@
+#
+# Author:: Adam Edwards (<adamed@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 'spec_helper'
+require 'uri'
+
+describe Chef::Provider::RemoteFile::CacheControlData do
+
+ before(:each) do
+ Chef::Config[:file_cache_path] = Dir.mktmpdir
+ end
+
+ after(:each) do
+ FileUtils.rm_rf(Chef::Config[:file_cache_path])
+ end
+
+ let(:uri) { URI.parse("http://www.bing.com/robots.txt") }
+
+ describe "when the cache control data save method is invoked" do
+
+ subject(:cache_control_data) do
+ Chef::Provider::RemoteFile::CacheControlData.load_and_validate(uri, file_checksum)
+ end
+
+ # the checksum of the file last we fetched it.
+ let(:file_checksum) { "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }
+
+ let(:etag) { "\"a-strong-identifier\"" }
+ let(:mtime) { "Thu, 01 Aug 2013 08:16:32 GMT" }
+
+ before do
+ cache_control_data.etag = etag
+ cache_control_data.mtime = mtime
+ cache_control_data.checksum = file_checksum
+ end
+
+ it "writes data to the cache" do
+ cache_control_data.save
+ end
+
+ it "writes the data to the cache and the same data can be read back" do
+ cache_control_data.save
+ saved_cache_control_data = Chef::Provider::RemoteFile::CacheControlData.load_and_validate(uri, file_checksum)
+ saved_cache_control_data.etag.should == cache_control_data.etag
+ saved_cache_control_data.mtime.should == cache_control_data.mtime
+ saved_cache_control_data.checksum.should == cache_control_data.checksum
+ end
+
+ # Cover the very long remote file path case -- see CHEF-4422 where
+ # local cache file names generated from the long uri exceeded
+ # local file system path limits resulting in exceptions from
+ # file system API's on both Windows and Unix systems.
+ context "when the length of the uri exceeds the path length limits for the local file system" do
+ let(:uri_exceeds_file_system_limit) do
+ URI.parse("http://www.bing.com/" + ('0' * 1024))
+ end
+
+ let(:uri) { uri_exceeds_file_system_limit }
+
+ it "writes data to the cache" do
+ cache_control_data.save
+ end
+
+ it "writes the data to the cache and the same data can be read back" do
+ cache_control_data.save
+ saved_cache_control_data = Chef::Provider::RemoteFile::CacheControlData.load_and_validate(uri, file_checksum)
+ saved_cache_control_data.etag.should == cache_control_data.etag
+ saved_cache_control_data.mtime.should == cache_control_data.mtime
+ saved_cache_control_data.checksum.should == cache_control_data.checksum
+ end
+
+ end
+ end
+
+end
+
diff --git a/spec/unit/provider/remote_file/cache_control_data_spec.rb b/spec/unit/provider/remote_file/cache_control_data_spec.rb
index ee29e21890..2baf86bcf6 100644
--- a/spec/unit/provider/remote_file/cache_control_data_spec.rb
+++ b/spec/unit/provider/remote_file/cache_control_data_spec.rb
@@ -164,6 +164,42 @@ describe Chef::Provider::RemoteFile::CacheControlData do
cache_control_data.save
end
end
+
+ # Cover the very long remote file path case -- see CHEF-4422 where
+ # local cache file names generated from the long uri exceeded
+ # local file system path limits resulting in exceptions from
+ # file system API's on both Windows and Unix systems.
+ context "and the URI results in a file cache path that exceeds 102 characters in length" do
+ let(:truncated_friendly_file_name_length) { 64 }
+ let(:md5_hex_length) { 32 }
+ let(:json_file_extension_length) { 5 }
+ let(:cache_file_path_limit) { truncated_friendly_file_name_length + 1 + md5_hex_length + json_file_extension_length } # {friendly}-{md5hex}.json == 102
+ let(:long_remote_path) { "http://www.bing.com/" + ('0' * (truncated_friendly_file_name_length * 2 )) }
+ let(:uri) { URI.parse(long_remote_path) }
+ let(:truncated_remote_uri) { URI.parse(long_remote_path[0...truncated_friendly_file_name_length]) }
+ let(:truncated_file_cache_path) do
+ cache_control_data_truncated = Chef::Provider::RemoteFile::CacheControlData.load_and_validate(truncated_remote_uri, current_file_checksum)
+ cache_control_data_truncated.send('sanitized_cache_file_basename')[0...truncated_friendly_file_name_length]
+ end
+
+ it "truncates the file cache path to 102 characters" do
+ normalized_cache_path = cache_control_data.send('sanitized_cache_file_basename')
+
+ Chef::FileCache.should_receive(:store).with("remote_file/" + normalized_cache_path, cache_control_data.json_data)
+
+ cache_control_data.save
+
+ normalized_cache_path.length.should == cache_file_path_limit
+ end
+
+ it "uses a file cache path that starts with the first 64 characters of the URI" do
+ normalized_cache_path = cache_control_data.send('sanitized_cache_file_basename')
+
+ truncated_file_cache_path.length.should == truncated_friendly_file_name_length
+ normalized_cache_path.start_with?(truncated_file_cache_path).should == true
+ end
+ end
+
end
end