module Ridley # @author Jamie Winsor # @api private class SandboxUploader class << self # Return the checksum of the contents of the file at the given filepath # # @param [String] path # file to checksum # # @return [String] # the binary checksum of the contents of the file def checksum(path) File.open(path, 'rb') { |f| checksum_io(f, Digest::MD5.new) } end # Return a base64 encoded checksum of the contents of the given file. This is the expected # format of sandbox checksums given to the Chef Server. # # @param [String] path # # @return [String] # a base64 encoded checksum def checksum64(path) Base64.encode64([checksum(path)].pack("H*")).strip end # @param [String] io # @param [Object] digest # # @return [String] def checksum_io(io, digest) while chunk = io.read(1024 * 8) digest.update(chunk) end digest.hexdigest end end include Celluloid attr_reader :client_name attr_reader :client_key attr_reader :options def initialize(client_name, client_key, options = {}) @client_name = client_name @client_key = client_key @options = options end # Upload one file into the sandbox for the given checksum id # # @param [Ridley::SandboxObject] sandbox # @param [String] chk_id # checksum of the file being uploaded # @param [String] path # path to the file to upload # # @return [Hash, nil] def upload(sandbox, chk_id, path) checksum = sandbox.checksum(chk_id) unless checksum[:needs_upload] return nil end headers = { 'Content-Type' => 'application/x-binary', 'content-md5' => self.class.checksum64(path) } contents = File.open(path, 'rb') { |f| f.read } url = URI(checksum[:url]) upload_path = url.path url.path = "" # versions prior to OSS Chef 11 will strip the port to upload the file to in the checksum # url returned. This will ensure we are uploading to the proper location. if sandbox.send(:resource).connection.foss? url.port = URI(sandbox.send(:resource).connection.server_url).port end begin Faraday.new(url, self.options) do |c| c.response :chef_response c.response :follow_redirects c.request :chef_auth, self.client_name, self.client_key c.adapter :net_http end.put(upload_path, contents, headers) rescue Ridley::Errors::HTTPError => ex abort(ex) end end end end