summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/chef/provider/remote_file.rb87
-rw-r--r--lib/chef/resource/remote_file.rb9
2 files changed, 64 insertions, 32 deletions
diff --git a/lib/chef/provider/remote_file.rb b/lib/chef/provider/remote_file.rb
index f985682441..6d61930ab3 100644
--- a/lib/chef/provider/remote_file.rb
+++ b/lib/chef/provider/remote_file.rb
@@ -17,10 +17,11 @@
#
require 'chef/provider/file'
-require 'chef/rest'
+require 'rest_client'
require 'uri'
require 'tempfile'
require 'net/https'
+require 'net/ftp'
class Chef
class Provider
@@ -42,8 +43,14 @@ class Chef
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), {})
+ uri = URI.parse(source)
+ if URI::HTTP === uri
+ #HTTP or HTTPS
+ raw_file = RestClient::Request.execute(:method => :get, :url => source, :raw_response => true).file
+ else
+ #FTP
+ raw_file = ftp_fetch(uri)
+ end
rescue SocketError, Errno::ECONNREFUSED, Timeout::Error, Net::HTTPFatalError => e
Chef::Log.debug("#{@new_resource} cannot be downloaded from #{source}")
if source = sources.shift
@@ -102,40 +109,56 @@ 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
+ def ftp_fetch(uri)
+ # Shamelessly stolen from open-uri
+ # Fetches the file using Net::FTP, returning a Tempfile
+ path = uri.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: #{uri.inspect}"
+ end
+ directories.each {|d|
+ if /[\r\n]/ =~ d
+ raise ArgumentError, "invalid directory: #{d.inspect}"
+ end
+ }
+ if /[\r\n]/ =~ filename
+ raise ArgumentError, "invalid filename: #{filename.inspect}"
+ end
+ typecode = uri.typecode
+ if typecode && /\A[aid]\z/ !~ typecode
+ raise ArgumentError, "invalid typecode: #{typecode.inspect}"
end
- opts
- end
- private
+ tempfile = Tempfile.new(filename)
- def absolute_uri?(source)
- URI.parse(source).absolute?
- rescue URI::InvalidURIError
- false
- end
+ # The access sequence is defined by RFC 1738
+ ftp = Net::FTP.new
+ ftp.connect(uri.hostname, uri.port)
+ ftp.passive = true if !@new_resource.ftp_active_mode
+ # todo: extract user/passwd from .netrc.
+ user = 'anonymous'
+ passwd = nil
+ user, passwd = uri.userinfo.split(/:/) if uri.userinfo
+ ftp.login(user, passwd)
+ directories.each {|cwd|
+ ftp.voidcmd("CWD #{cwd}")
+ }
+ if typecode
+ # xxx: typecode D is not handled.
+ ftp.voidcmd("TYPE #{typecode.upcase}")
+ end
+ ftp.getbinaryfile(filename, tempfile.path)
+ ftp.close
+ tempfile
+ end
end
end
end
diff --git a/lib/chef/resource/remote_file.rb b/lib/chef/resource/remote_file.rb
index 2798cba3f2..21e1a2680f 100644
--- a/lib/chef/resource/remote_file.rb
+++ b/lib/chef/resource/remote_file.rb
@@ -33,6 +33,7 @@ class Chef
@resource_name = :remote_file
@action = "create"
@source = nil
+ @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