summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan McLellan <btm@opscode.com>2013-02-27 07:53:37 -0800
committerBryan McLellan <btm@opscode.com>2013-02-27 07:53:37 -0800
commita82935b4a8e1e8174d02e83259f07ea9926dd007 (patch)
treedaefb2c6df08276e822378e1fd5c9d6820f1d294
parent464087c7a6da4ed51237cbb6ff39e45feeae0ecd (diff)
parent6f6fff817866e1fb5ab3c90862bfaa627b5378fd (diff)
downloadchef-a82935b4a8e1e8174d02e83259f07ea9926dd007.tar.gz
Merge branch 'CHEF-1031'
-rw-r--r--lib/chef/provider/remote_file.rb93
-rw-r--r--lib/chef/provider/remote_file/ftp.rb95
-rw-r--r--lib/chef/providers.rb2
-rw-r--r--lib/chef/resource/remote_file.rb11
-rw-r--r--spec/data/remote_file/nyan_cat.png.gzbin0 -> 14944 bytes
-rw-r--r--spec/functional/resource/remote_file_spec.rb43
-rw-r--r--spec/tiny_server.rb21
-rw-r--r--spec/unit/provider/remote_file/ftp_spec.rb117
-rw-r--r--spec/unit/provider/remote_file_spec.rb65
-rw-r--r--spec/unit/resource/remote_file_spec.rb18
10 files changed, 375 insertions, 90 deletions
diff --git a/lib/chef/provider/remote_file.rb b/lib/chef/provider/remote_file.rb
index f985682441..4d1e696d0e 100644
--- a/lib/chef/provider/remote_file.rb
+++ b/lib/chef/provider/remote_file.rb
@@ -1,4 +1,5 @@
#
+# Author:: Jesse Campbell (<hikeit@gmail.com>)
# Author:: Adam Jacob (<adam@opscode.com>)
# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
@@ -17,10 +18,9 @@
#
require 'chef/provider/file'
-require 'chef/rest'
+require 'rest_client'
require 'uri'
require 'tempfile'
-require 'net/https'
class Chef
class Provider
@@ -40,24 +40,12 @@ class Chef
Chef::Log.debug("#{@new_resource} checksum matches target checksum (#{@new_resource.checksum}) - not updating")
else
sources = @new_resource.source
- source = sources.shift
- begin
- rest = Chef::REST.new(source, nil, nil, http_client_opts(source))
- raw_file = rest.streaming_request(rest.create_url(source), {})
- rescue SocketError, Errno::ECONNREFUSED, Timeout::Error, Net::HTTPFatalError => e
- Chef::Log.debug("#{@new_resource} cannot be downloaded from #{source}")
- if source = sources.shift
- Chef::Log.debug("#{@new_resource} trying to download from another mirror")
- retry
- else
- raise e
- end
- end
+ raw_file, raw_file_source = try_multiple_sources(sources)
if matches_current_checksum?(raw_file)
Chef::Log.debug "#{@new_resource} target and source checksums are the same - not updating"
else
description = []
- description << "copy file downloaded from #{@new_resource.source} into #{@new_resource.path}"
+ description << "copy file downloaded from #{raw_file_source} into #{@new_resource.path}"
description << diff_current(raw_file.path)
converge_by(description) do
backup_new_resource
@@ -102,38 +90,55 @@ class Chef
end
end
- def source_file(source, current_checksum, &block)
- if absolute_uri?(source)
- fetch_from_uri(source, &block)
- elsif !Chef::Config[:solo]
- fetch_from_chef_server(source, current_checksum, &block)
- else
- fetch_from_local_cookbook(source, &block)
- end
- end
+ private
- def http_client_opts(source)
- opts={}
- # CHEF-3140
- # 1. If it's already compressed, trying to compress it more will
- # probably be counter-productive.
- # 2. Some servers are misconfigured so that you GET $URL/file.tgz but
- # they respond with content type of tar and content encoding of gzip,
- # which tricks Chef::REST into decompressing the response body. In this
- # case you'd end up with a tar archive (no gzip) named, e.g., foo.tgz,
- # which is not what you wanted.
- if @new_resource.path =~ /gz$/ or source =~ /gz$/
- opts[:disable_gzip] = true
+ # Given an array of source uris, iterate through them until one does not fail
+ def try_multiple_sources(sources)
+ sources = sources.dup
+ source = sources.shift
+ begin
+ uri = URI.parse(source)
+ raw_file = grab_file_from_uri(uri)
+ rescue ArgumentError => e
+ raise e
+ rescue => e
+ if e.is_a?(RestClient::Exception)
+ error = "Request returned #{e.message}"
+ else
+ error = e.to_s
+ end
+ Chef::Log.debug("#{@new_resource} cannot be downloaded from #{source}: #{error}")
+ if source = sources.shift
+ Chef::Log.debug("#{@new_resource} trying to download from another mirror")
+ retry
+ else
+ raise e
+ end
end
- opts
+ if uri.userinfo
+ uri.password = "********"
+ end
+ return raw_file, uri.to_s
end
- private
-
- def absolute_uri?(source)
- URI.parse(source).absolute?
- rescue URI::InvalidURIError
- false
+ # Given a source uri, return a Tempfile, or a File that acts like a Tempfile (close! method)
+ def grab_file_from_uri(uri)
+ if URI::HTTP === uri
+ #HTTP or HTTPS
+ raw_file = RestClient::Request.execute(:method => :get, :url => uri.to_s, :raw_response => true).file
+ elsif URI::FTP === uri
+ #FTP
+ raw_file = FTP::fetch(uri, @new_resource.ftp_active_mode)
+ elsif uri.scheme == "file"
+ #local/network file
+ raw_file = ::File.new(uri.path, "r")
+ def raw_file.close!
+ self.close
+ end
+ else
+ raise ArgumentError, "Invalid uri. Only http(s), ftp, and file are currently supported"
+ end
+ raw_file
end
end
diff --git a/lib/chef/provider/remote_file/ftp.rb b/lib/chef/provider/remote_file/ftp.rb
new file mode 100644
index 0000000000..3c5d3e0a91
--- /dev/null
+++ b/lib/chef/provider/remote_file/ftp.rb
@@ -0,0 +1,95 @@
+#
+# Author:: Jesse Campbell (<hikeit@gmail.com>)
+# Copyright:: Copyright (c) 2013 Jesse Campbell
+# 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 'uri'
+require 'tempfile'
+require 'net/ftp'
+require 'chef/provider/remote_file'
+
+class Chef
+ class Provider
+ class RemoteFile
+ class FTP
+
+ # Fetches the file at uri using Net::FTP, returning a Tempfile
+ def self.fetch(uri, ftp_active_mode)
+ self.new(uri, ftp_active_mode).fetch()
+ end
+
+ # Parse the uri into instance variables
+ def initialize(uri, ftp_active_mode)
+ @directories, @filename = parse_path(uri.path)
+ @typecode = uri.typecode
+ # Only support ascii and binary types
+ if @typecode && /\A[ai]\z/ !~ @typecode
+ raise ArgumentError, "invalid typecode: #{@typecode.inspect}"
+ end
+ @ftp_active_mode = ftp_active_mode
+ @hostname = uri.hostname
+ @port = uri.port
+ if uri.userinfo
+ @user = URI.unescape(uri.user)
+ @pass = URI.unescape(uri.password)
+ else
+ @user = 'anonymous'
+ @pass = nil
+ end
+ end
+
+ # Fetches using Net::FTP, returns a Tempfile with the content
+ def fetch()
+ tempfile = Tempfile.new(@filename)
+
+ # The access sequence is defined by RFC 1738
+ ftp = Net::FTP.new
+ ftp.connect(@hostname, @port)
+ ftp.passive = !@ftp_active_mode
+ ftp.login(@user, @pass)
+ @directories.each do |cwd|
+ ftp.voidcmd("CWD #{cwd}")
+ end
+ if @typecode
+ ftp.voidcmd("TYPE #{@typecode.upcase}")
+ end
+ ftp.getbinaryfile(@filename, tempfile.path)
+ ftp.close
+
+ tempfile
+ end
+
+ private
+
+ def parse_path(path)
+ path = path.sub(%r{\A/}, '%2F') # re-encode the beginning slash because uri library decodes it.
+ directories = path.split(%r{/}, -1)
+ directories.each {|d|
+ d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
+ }
+ unless filename = directories.pop
+ raise ArgumentError, "no filename: #{path.inspect}"
+ end
+ if filename.length == 0 || filename.end_with?( "/" )
+ raise ArgumentError, "no filename: #{path.inspect}"
+ end
+ return directories, filename
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index ae95632eaa..3a40c542ec 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -101,5 +101,7 @@ require 'chef/provider/mount/windows'
require 'chef/provider/deploy/revision'
require 'chef/provider/deploy/timestamped'
+require 'chef/provider/remote_file/ftp'
+
require "chef/provider/lwrp_base"
require 'chef/provider/registry_key'
diff --git a/lib/chef/resource/remote_file.rb b/lib/chef/resource/remote_file.rb
index 2798cba3f2..524e00186b 100644
--- a/lib/chef/resource/remote_file.rb
+++ b/lib/chef/resource/remote_file.rb
@@ -32,7 +32,8 @@ class Chef
super
@resource_name = :remote_file
@action = "create"
- @source = nil
+ @source = []
+ @ftp_active_mode = false
@provider = Chef::Provider::RemoteFile
end
@@ -54,6 +55,14 @@ class Chef
)
end
+ def ftp_active_mode(args=nil)
+ set_or_return(
+ :ftp_active_mode,
+ args,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
def after_created
validate_source(@source)
end
diff --git a/spec/data/remote_file/nyan_cat.png.gz b/spec/data/remote_file/nyan_cat.png.gz
new file mode 100644
index 0000000000..efa9d4427a
--- /dev/null
+++ b/spec/data/remote_file/nyan_cat.png.gz
Binary files differ
diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb
index 57a5321ea2..fbb921d48c 100644
--- a/spec/functional/resource/remote_file_spec.rb
+++ b/spec/functional/resource/remote_file_spec.rb
@@ -23,14 +23,6 @@ describe Chef::Resource::RemoteFile do
include_context Chef::Resource::File
let(:file_base) { "remote_file_spec" }
- let(:source) { 'http://localhost:9000/nyan_cat.png' }
- let(:expected_content) do
- content = File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png'), "rb") do |f|
- f.read
- end
- content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding)
- content
- end
def create_resource
node = Chef::Node.new
@@ -71,13 +63,44 @@ describe Chef::Resource::RemoteFile do
f.read
end
}
+ @api.get("/nyan_cat.png.gz", 200, nil, { 'Content-Type' => 'application/gzip', 'Content-Encoding' => 'gzip' } ) {
+ File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png.gz'), "rb") do |f|
+ f.read
+ end
+ }
end
after(:all) do
@server.stop
end
- it_behaves_like "a file resource"
+ context "when using normal encoding" do
+ let(:source) { 'http://localhost:9000/nyan_cat.png' }
+ let(:expected_content) do
+ content = File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png'), "rb") do |f|
+ f.read
+ end
+ content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding)
+ content
+ end
+
+ it_behaves_like "a file resource"
- it_behaves_like "a securable resource with reporting"
+ it_behaves_like "a securable resource with reporting"
+ end
+
+ context "when using gzip encoding" do
+ let(:source) { 'http://localhost:9000/nyan_cat.png.gz' }
+ let(:expected_content) do
+ content = File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png.gz'), "rb") do |f|
+ f.read
+ end
+ content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding)
+ content
+ end
+
+ it_behaves_like "a file resource"
+
+ it_behaves_like "a securable resource with reporting"
+ end
end
diff --git a/spec/tiny_server.rb b/spec/tiny_server.rb
index 9eecf13cec..eb5f5c0fc0 100644
--- a/spec/tiny_server.rb
+++ b/spec/tiny_server.rb
@@ -127,20 +127,20 @@ module TinyServer
@routes = {GET => [], PUT => [], POST => [], DELETE => []}
end
- def get(path, response_code, data=nil, &block)
- @routes[GET] << Route.new(path, Response.new(response_code,data, &block))
+ def get(path, response_code, data=nil, headers=nil, &block)
+ @routes[GET] << Route.new(path, Response.new(response_code, data, headers, &block))
end
- def put(path, response_code, data=nil, &block)
- @routes[PUT] << Route.new(path, Response.new(response_code,data, &block))
+ def put(path, response_code, data=nil, headers=nil, &block)
+ @routes[PUT] << Route.new(path, Response.new(response_code, data, headers, &block))
end
- def post(path, response_code, data=nil, &block)
- @routes[POST] << Route.new(path, Response.new(response_code,data, &block))
+ def post(path, response_code, data=nil, headers=nil, &block)
+ @routes[POST] << Route.new(path, Response.new(response_code, data, headers, &block))
end
- def delete(path, response_code, data=nil, &block)
- @routes[DELETE] << Route.new(path, Response.new(response_code,data, &block))
+ def delete(path, response_code, data=nil, headers=nil, &block)
+ @routes[DELETE] << Route.new(path, Response.new(response_code, data, headers, &block))
end
def call(env)
@@ -183,14 +183,15 @@ module TinyServer
class Response
HEADERS = {'Content-Type' => 'application/json'}
- def initialize(response_code=200,data=nil, &block)
+ def initialize(response_code=200, data=nil, headers=nil, &block)
@response_code, @data = response_code, data
+ @response_headers = headers ? HEADERS.merge(headers) : HEADERS
@block = block_given? ? block : nil
end
def call
data = @data || @block.call
- [@response_code, HEADERS, Array(data)]
+ [@response_code, @response_headers, Array(data)]
end
def to_s
diff --git a/spec/unit/provider/remote_file/ftp_spec.rb b/spec/unit/provider/remote_file/ftp_spec.rb
new file mode 100644
index 0000000000..03ef9424dd
--- /dev/null
+++ b/spec/unit/provider/remote_file/ftp_spec.rb
@@ -0,0 +1,117 @@
+#
+# Author:: Jesse Campbell (<hikeit@gmail.com>)
+# Copyright:: Copyright (c) 2013 Jesse Campbell
+# 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'
+
+describe Chef::Provider::RemoteFile::FTP, "fetch" do
+ before(:each) do
+ @ftp = mock(Net::FTP, { })
+ Net::FTP.stub!(:new).and_return(@ftp)
+ @ftp.stub!(:connect)
+ @ftp.stub!(:login)
+ @ftp.stub!(:voidcmd)
+ @ftp.stub!(:getbinaryfile)
+ @ftp.stub!(:close)
+ @ftp.stub!(:passive=)
+ @tempfile = Tempfile.new("chef-rspec-ftp_spec-line#{__LINE__}--")
+ Tempfile.stub!(:new).and_return(@tempfile)
+ @uri = URI.parse("ftp://opscode.com/seattle.txt")
+ end
+
+ describe "when parsing the uri" do
+ it "throws an argument exception when no path is given" do
+ @uri.path = ""
+ lambda { Chef::Provider::RemoteFile::FTP.new(@uri, false) }.should raise_error(ArgumentError)
+ end
+
+ it "throws an argument exception when only a / is given" do
+ @uri.path = "/"
+ lambda { Chef::Provider::RemoteFile::FTP.new(@uri, false) }.should raise_error(ArgumentError)
+ end
+
+ it "throws an argument exception when no filename is given" do
+ @uri.path = "/the/whole/path/"
+ lambda { Chef::Provider::RemoteFile::FTP.new(@uri, false) }.should raise_error(ArgumentError)
+ end
+
+ it "throws an argument exception when the typecode is invalid" do
+ @uri.typecode = "d"
+ lambda { Chef::Provider::RemoteFile::FTP.new(@uri, false) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "when connecting to the remote" do
+ it "should connect to the host from the uri on the default port 21" do
+ @ftp.should_receive(:connect).with("opscode.com", 21)
+ Chef::Provider::RemoteFile::FTP.fetch(@uri, false).close!
+ end
+
+ it "should connect on an alternate port when one is provided" do
+ @ftp.should_receive(:connect).with("opscode.com", 8021)
+ Chef::Provider::RemoteFile::FTP.fetch(URI.parse("ftp://opscode.com:8021/seattle.txt"), false).close!
+ end
+
+ it "should set passive true when ftp_active_mode is false" do
+ @ftp.should_receive(:passive=).with(true)
+ Chef::Provider::RemoteFile::FTP.fetch(@uri, false).close!
+ end
+
+ it "should set passive false when ftp_active_mode is false" do
+ @ftp.should_receive(:passive=).with(false)
+ Chef::Provider::RemoteFile::FTP.fetch(@uri, true).close!
+ end
+
+ it "should use anonymous ftp when no userinfo is provided" do
+ @ftp.should_receive(:login).with("anonymous", nil)
+ Chef::Provider::RemoteFile::FTP.fetch(@uri, false).close!
+ end
+
+ it "should use authenticated ftp when userinfo is provided" do
+ @ftp.should_receive(:login).with("the_user", "the_password")
+ Chef::Provider::RemoteFile::FTP.fetch(URI.parse("ftp://the_user:the_password@opscode.com/seattle.txt"), false).close!
+ end
+
+ it "should accept ascii for the typecode" do
+ @uri.typecode = "a"
+ @ftp.should_receive(:voidcmd).with("TYPE A").once
+ Chef::Provider::RemoteFile::FTP.fetch(@uri, false).close!
+ end
+
+ it "should accept image for the typecode" do
+ @uri.typecode = "i"
+ @ftp.should_receive(:voidcmd).with("TYPE I").once
+ Chef::Provider::RemoteFile::FTP.fetch(@uri, false).close!
+ end
+
+ it "should fetch the file from the correct path" do
+ @ftp.should_receive(:voidcmd).with("CWD the").once
+ @ftp.should_receive(:voidcmd).with("CWD whole").once
+ @ftp.should_receive(:voidcmd).with("CWD path").once
+ @ftp.should_receive(:getbinaryfile).with("seattle.txt", @tempfile.path)
+ Chef::Provider::RemoteFile::FTP.fetch(URI.parse("ftp://opscode.com/the/whole/path/seattle.txt"), false).close!
+ end
+ end
+
+ describe "when it finishes downloading" do
+ it "should return a tempfile" do
+ ftpfile = Chef::Provider::RemoteFile::FTP.fetch(@uri, false)
+ ftpfile.should equal @tempfile
+ ftpfile.close!
+ end
+ end
+end
diff --git a/spec/unit/provider/remote_file_spec.rb b/spec/unit/provider/remote_file_spec.rb
index 78d7e77121..149f0462c3 100644
--- a/spec/unit/provider/remote_file_spec.rb
+++ b/spec/unit/provider/remote_file_spec.rb
@@ -46,11 +46,9 @@ describe Chef::Provider::RemoteFile, "action_create" do
describe "when fetching the file from the remote" do
before(:each) do
@tempfile = Tempfile.new("chef-rspec-remote_file_spec-line#{__LINE__}--")
+ @rawresp = RestClient::RawResponse.new(@tempfile, nil, nil)
- @rest = mock(Chef::REST, { })
- Chef::REST.stub!(:new).and_return(@rest)
- @rest.stub!(:streaming_request).and_return(@tempfile)
- @rest.stub!(:create_url) { |url| url }
+ RestClient::Request.stub!(:execute).and_return(@rawresp)
@resource.cookbook_name = "monkey"
@provider.stub!(:checksum).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa")
@@ -81,19 +79,19 @@ describe Chef::Provider::RemoteFile, "action_create" do
shared_examples_for "source specified with multiple URIs" do
it "should try to download the next URI when the first one fails" do
- @rest.should_receive(:streaming_request).with("http://foo", {}).once.and_raise(SocketError)
- @rest.should_receive(:streaming_request).with("http://bar", {}).once.and_return(@tempfile)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://foo", :raw_response => true).once.and_raise(SocketError)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://bar", :raw_response => true).once.and_return(@rawresp)
@provider.run_action(:create)
end
it "should raise an exception when all the URIs fail" do
- @rest.should_receive(:streaming_request).with("http://foo", {}).once.and_raise(SocketError)
- @rest.should_receive(:streaming_request).with("http://bar", {}).once.and_raise(SocketError)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://foo", :raw_response => true).once.and_raise(SocketError)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://bar", :raw_response => true).once.and_raise(SocketError)
lambda { @provider.run_action(:create) }.should raise_error(SocketError)
end
it "should download from only one URI when the first one works" do
- @rest.should_receive(:streaming_request).once.and_return(@tempfile)
+ RestClient::Request.should_receive(:execute).once.and_return(@rawresp)
@provider.run_action(:create)
end
@@ -123,7 +121,7 @@ describe Chef::Provider::RemoteFile, "action_create" do
end
it "does not download the file" do
- @rest.should_not_receive(:fetch).with("http://opscode.com/seattle.txt").and_return(@tempfile)
+ RestClient::Request.should_not_receive(:execute).with("http://opscode.com/seattle.txt").and_return(@tempfile)
@provider.run_action(:create)
end
@@ -140,7 +138,7 @@ describe Chef::Provider::RemoteFile, "action_create" do
end
it "should not download the file if the checksum is a partial match from the beginning" do
- @rest.should_not_receive(:fetch).with("http://opscode.com/seattle.txt").and_return(@tempfile)
+ @rawresp.should_not_receive(:fetch).with("http://opscode.com/seattle.txt").and_return(@tempfile)
@provider.run_action(:create)
end
@@ -154,7 +152,7 @@ describe Chef::Provider::RemoteFile, "action_create" do
describe "and the existing file doesn't match the given checksum" do
it "downloads the file" do
@resource.checksum("this hash doesn't match")
- @rest.should_receive(:streaming_request).with("http://opscode.com/seattle.txt", {}).and_return(@tempfile)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://opscode.com/seattle.txt", :raw_response => true).and_return(@rawresp)
@provider.stub!(:update_new_file_state)
@provider.run_action(:create)
end
@@ -162,7 +160,7 @@ describe Chef::Provider::RemoteFile, "action_create" do
it "does not consider the checksum a match if the matching string is offset" do
# i.e., the existing file is "0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa"
@resource.checksum("fd012fd")
- @rest.should_receive(:streaming_request).with("http://opscode.com/seattle.txt", {}).and_return(@tempfile)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://opscode.com/seattle.txt", :raw_response => true).and_return(@rawresp)
@provider.stub!(:update_new_file_state)
@provider.run_action(:create)
end
@@ -173,7 +171,7 @@ describe Chef::Provider::RemoteFile, "action_create" do
describe "and the resource doesn't specify a checksum" do
it "should download the file from the remote URL" do
@resource.checksum(nil)
- @rest.should_receive(:streaming_request).with("http://opscode.com/seattle.txt", {}).and_return(@tempfile)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://opscode.com/seattle.txt", :raw_response => true).and_return(@rawresp)
@provider.run_action(:create)
end
end
@@ -190,7 +188,7 @@ describe Chef::Provider::RemoteFile, "action_create" do
context "and the target file is a tarball" do
before do
@resource.path(File.expand_path(File.join(CHEF_SPEC_DATA, "seattle.tar.gz")))
- Chef::REST.should_receive(:new).with("http://opscode.com/seattle.txt", nil, nil, :disable_gzip => true).and_return(@rest)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://opscode.com/seattle.txt", :raw_response => true).and_return(@rawresp)
end
it "disables gzip in the http client" do
@@ -202,7 +200,7 @@ describe Chef::Provider::RemoteFile, "action_create" do
context "and the source appears to be a tarball" do
before do
@resource.source("http://example.com/tarball.tgz")
- Chef::REST.should_receive(:new).with("http://example.com/tarball.tgz", nil, nil, :disable_gzip => true).and_return(@rest)
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => "http://example.com/tarball.tgz", :raw_response => true).and_return(@rawresp)
end
it "disables gzip in the http client" do
@@ -210,17 +208,45 @@ describe Chef::Provider::RemoteFile, "action_create" do
end
end
+ context "and the uri scheme is ftp" do
+ before do
+ @resource.source("ftp://opscode.com/seattle.txt")
+ end
+
+ it "should fetch with ftp in passive mode" do
+ Chef::Provider::RemoteFile::FTP.should_receive(:fetch).with(URI.parse("ftp://opscode.com/seattle.txt"), false).and_return(@tempfile)
+ @provider.run_action(:create)
+ end
+
+ it "should fetch with ftp in active mode" do
+ @resource.ftp_active_mode true
+ Chef::Provider::RemoteFile::FTP.should_receive(:fetch).with(URI.parse("ftp://opscode.com/seattle.txt"), true).and_return(@tempfile)
+ @provider.run_action(:create)
+ end
+ end
+
+ context "and the uri scheme is file" do
+ before do
+ @resource.source("file:///nyan_cat.png")
+ end
+
+ it "should load the local file" do
+ File.should_receive(:new).with("/nyan_cat.png", "r").and_return(File.open(File.join(CHEF_SPEC_DATA, "remote_file", "nyan_cat.png"), "r"))
+ @provider.run_action(:create)
+ end
+ end
+
it "should raise an exception if it's any other kind of retriable response than 304" do
r = Net::HTTPMovedPermanently.new("one", "two", "three")
e = Net::HTTPRetriableError.new("301", r)
- @rest.stub!(:streaming_request).and_raise(e)
+ RestClient::Request.stub!(:execute).and_raise(e)
lambda { @provider.run_action(:create) }.should raise_error(Net::HTTPRetriableError)
end
it "should raise an exception if anything else happens" do
r = Net::HTTPBadRequest.new("one", "two", "three")
e = Net::HTTPServerException.new("fake exception", r)
- @rest.stub!(:streaming_request).and_raise(e)
+ RestClient::Request.stub!(:execute).and_raise(e)
lambda { @provider.run_action(:create) }.should raise_error(Net::HTTPServerException)
end
@@ -316,9 +342,6 @@ describe Chef::Provider::RemoteFile, "action_create" do
@provider.should_receive(:set_all_access_controls).and_return(true)
@provider.run_action(:create)
end
-
-
end
-
end
end
diff --git a/spec/unit/resource/remote_file_spec.rb b/spec/unit/resource/remote_file_spec.rb
index d91f80d1a7..064c97fc27 100644
--- a/spec/unit/resource/remote_file_spec.rb
+++ b/spec/unit/resource/remote_file_spec.rb
@@ -42,7 +42,7 @@ describe Chef::Resource::RemoteFile do
describe "source" do
it "does not have a default value for 'source'" do
- @resource.source.should be_nil
+ @resource.source.should eql([])
end
it "should accept a URI for the remote file source" do
@@ -64,7 +64,7 @@ describe Chef::Resource::RemoteFile do
lambda { @resource.source("not-a-uri") }.should raise_error(Chef::Exceptions::InvalidRemoteFileURI)
end
- it "should raise and exception when source is an empty array" do
+ it "should raise an exception when source is an empty array" do
lambda { @resource.source([]) }.should raise_error(ArgumentError)
end
@@ -80,7 +80,18 @@ describe Chef::Resource::RemoteFile do
@resource.checksum.should == nil
end
end
-
+
+ describe "ftp_active_mode" do
+ it "should accept a boolean for the ftp_active_mode object" do
+ @resource.ftp_active_mode true
+ @resource.ftp_active_mode.should be_true
+ end
+
+ it "should default to false" do
+ @resource.ftp_active_mode.should be_false
+ end
+ end
+
describe "when it has group, mode, owner, source, and checksum" do
before do
if Chef::Platform.windows?
@@ -119,5 +130,4 @@ describe Chef::Resource::RemoteFile do
end
end
end
-
end