# encoding: UTF-8 # # Copyright 2015 Shawn Neal # # 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_relative 'temp_zip_file' require_relative 'file_uploader' require_relative 'command_executor' require_relative '../scripts/scripts' module WinRM module FS module Core # Orchestrates the upload of a file or directory class UploadOrchestrator def initialize(service) @service = service @logger = Logging.logger[self] end def upload_file(local_path, remote_path) # If the src has a file extension and the destination does not # we can assume the caller specified the dest as a directory if File.extname(local_path) != '' && File.extname(remote_path) == '' remote_path = File.join(remote_path, File.basename(local_path)) end temp_path = temp_file_path(local_path) with_command_executor do |cmd_executor| return 0 unless out_of_date?(cmd_executor, local_path, remote_path) do_file_upload(cmd_executor, local_path, temp_path, remote_path) end end def upload_directory(local_path, remote_path) with_local_zip(local_path) do |local_zip| temp_path = temp_file_path(local_zip.path) with_command_executor do |cmd_executor| return 0 unless out_of_date?(cmd_executor, local_zip.path, temp_path) do_file_upload(cmd_executor, local_zip.path, temp_path, remote_path) end end end private def do_file_upload(cmd_executor, local_path, temp_path, remote_path) file_uploader = WinRM::FS::Core::FileUploader.new(cmd_executor) bytes = file_uploader.upload(local_path, temp_path) do |bytes_copied, total_bytes| yield bytes_copied, total_bytes, local_path, remote_path if block_given? end cmd_executor.run_powershell( WinRM::FS::Scripts.render('decode_file', src: temp_path, dest: remote_path)) bytes end def with_command_executor cmd_executor = WinRM::FS::Core::CommandExecutor.new(@service) cmd_executor.open yield cmd_executor ensure cmd_executor.close end def with_local_zip(local_path) local_zip = create_temp_zip_file(local_path) yield local_zip ensure local_zip.delete if local_zip end def out_of_date?(cmd_executor, local_path, remote_path) local_checksum = local_checksum(local_path) remote_checksum = remote_checksum(cmd_executor, remote_path) if remote_checksum == local_checksum @logger.debug("#{remote_path} is up to date") return false end true end def remote_checksum(cmd_executor, remote_path) script = WinRM::FS::Scripts.render('checksum', path: remote_path) cmd_executor.run_powershell(script).chomp end def local_checksum(local_path) Digest::MD5.file(local_path).hexdigest end def temp_file_path(local_path) ext = '.tmp' ext = '.zip' if File.extname(local_path) == '.zip' "$env:TEMP/winrm-upload/#{local_checksum(local_path)}#{ext}" end def create_temp_zip_file(local_path) zip = WinRM::FS::Core::TempZipFile.new(local_path, recurse_paths: true) zip.add(local_path) zip.build zip end end end end end