Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

one_session branch working with win7 #14

Open
wants to merge 5 commits into
base: Transport
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/kitchen/busser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ def diagnose
result
end

# Returns an array of all files to be copied to the instance
#
# @return [Array<String>] array of local payload files
def local_payload
local_suite_files.concat(helper_files)
end

# Returns a command string which installs Busser, and installs all
# required Busser plugins for the suite.
#
Expand Down Expand Up @@ -132,6 +139,26 @@ def setup_cmd
Util.wrap_command(cmd, shell)
end

# Returns a command string which removes all suite test files on the
# instance.
#
# If no work needs to be performed, for example if there are no tests for
# the given suite, then `nil` will be returned.
#
# @return [String] a command string to remove all suite test files, or
# nil if no work needs to be performed.
def cleanup_cmd
return if local_suite_files.empty?

cmd = <<-CMD.gsub(/^ {8}/, "")
#{busser_setup_env}

#{sudo(config[:busser_bin])} suite cleanup

CMD
Util.wrap_command(cmd, shell)
end

# Returns a command string which transfers all suite test files to the
# instance.
#
Expand Down
4 changes: 3 additions & 1 deletion lib/kitchen/driver/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ def setup(state)
# @raise [ActionFailed] if the action could not be completed
def verify(state)
transport.connection(state) do |conn|
conn.execute(busser.sync_cmd)
conn.execute(busser.cleanup_cmd)
dirs = busser.local_payload.map {|f| File.dirname(f)}.uniq
conn.upload!(dirs, "/tmp/busser/suites")
conn.execute(busser.run_cmd)
end
end
Expand Down
177 changes: 17 additions & 160 deletions lib/kitchen/transport/winrm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@

require "kitchen/errors"
require "kitchen/login_command"
require "zip"
require 'kitchen/transport/winrm_file_transfer/remote_file'
require 'kitchen/transport/winrm_file_transfer/remote_zip_file'

module Kitchen

Expand Down Expand Up @@ -91,20 +92,13 @@ def wql(query)
run(query, :wql)
end

# (see Base#upload!)
def upload!(local, remote)
logger.debug("Upload: #{local} -> #{remote}")
local = Array.new(1) { local } if local.is_a? String
shell_id = session.open_shell
local.each do |path|
if File.directory?(path)
upload_directory(shell_id, path, remote)
else
upload_file(path, File.join(remote, File.basename(path)), shell_id)
end
end
# # (see Base#upload!)
def upload!(local_path, remote_path, &block)
local_path = [local_path] if local_path.is_a? String
file = create_remote_file(local_path, remote_path)
file.upload(&block)
ensure
session.close_shell(shell_id)
file.close unless file.nil?
end

# Convert a complex CLIXML Error to a human readable format
Expand Down Expand Up @@ -183,6 +177,15 @@ def default_port

private

def create_remote_file(local_paths, remote_path)
if local_paths.count == 1 && !File.directory?(local_paths[0])
return WinRMFileTransfer::RemoteFile.new(logger, session, local_paths[0], remote_path)
end
zip_file = WinRMFileTransfer::RemoteZipFile.new(logger, session, remote_path)
local_paths.each { |path| zip_file.add_file(path) }
zip_file
end

# (see Base#establish_connection)
def establish_connection
rescue_exceptions = [
Expand Down Expand Up @@ -306,152 +309,6 @@ def build_winrm_options

[endpoint, :plaintext, opts]
end

def upload_file(local, remote, shell_id)
logger.debug("Upload: #{local} -> #{remote}")
remote = full_remote_path(remote, shell_id)
if should_upload_file?(shell_id, local, remote)
upload_to_remote(shell_id, local, remote)
decode_remote_file(shell_id, local, remote)
end
end

def full_remote_path(path, shell_id)
command = <<-EOH
$dest_file_path = [System.IO.Path]::GetFullPath('#{path}')

if (!(Test-Path $dest_file_path)) {
$dest_dir = ([System.IO.Path]::GetDirectoryName($dest_file_path))
New-Item -ItemType directory -Force -Path $dest_dir | Out-Null
}

$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("#{path}")
EOH

powershell(command, shell_id)[:data][0][:stdout].chomp
end

# Checks to see if the target file on the guest is missing or out of date.
#
# @param [String] The id of a shell instance to run the command from
# @param [String] The source file path on the host
# @param [String] The destination file path on the guest
# @return [Boolean] True if the file is missing or out of date
def should_upload_file?(shell_id, local, remote)
logger.debug("comparing #{local} to #{remote}")
local_md5 = Digest::MD5.file(local).hexdigest
command = <<-EOH
$dest_file_path = [System.IO.Path]::GetFullPath('#{remote}')

if (Test-Path $dest_file_path) {
$crypto_prov = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
try {
$file = [System.IO.File]::Open($dest_file_path,
[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
$guest_md5 = ([System.BitConverter]::ToString($crypto_prov.ComputeHash($file)))
$guest_md5 = $guest_md5.Replace("-","").ToLower()
}
finally {
$file.Dispose()
}
if ($guest_md5 -eq '#{local_md5}') {
exit 0
}
}
remove-item $dest_file_path -Force
exit 1
EOH
powershell(command, shell_id)[:exitcode] == 1
end

# Uploads the given file to the guest
#
# @param [String] The id of a shell instance to run the command from
# @param [String] The source file path on the host
# @return [String] The temp file path on the guest
def upload_to_remote(shell_id, local, remote)
logger.debug("Uploading '#{local}' to temp file '#{remote}'")
base64_host_file = Base64.encode64(IO.binread(local)).gsub("\n", "")
base64_host_file.chars.to_a.each_slice(8000 - remote.size) do |chunk|
output = cmd("echo #{chunk.join} >> \"#{remote}\"", shell_id)
raise_upload_error_if_failed(output, local, remote)
end
end

# Recursively uploads the given directory from the host to the guest
#
# @param [String] The id of a shell instance to run the command from
# @param [String] The source file or directory path on the host
# @param [String] The destination file or directory path on the host
def upload_directory(shell_id, local, remote)
zipped = zip_path(local)
return if !File.exist?(zipped)
remote_zip = File.join(remote, File.basename(zipped))
logger.debug("uploading #{zipped} to #{remote_zip}")
upload_file(zipped, remote_zip, shell_id)
extract_zip(remote_zip, local, shell_id)
end

def zip_path(path)
path.sub!(%r[/$],'')
archive = File.join(path,File.basename(path))+'.zip'
FileUtils.rm archive, :force=>true

Zip::File.open(archive, 'w') do |zipfile|
Dir["#{path}/**/**"].reject{|f|f==archive}.each do |file|
entry = Zip::Entry.new(archive, file.sub(path+'/',''), nil, nil, nil, nil, nil, nil, ::Zip::DOSTime.new(2000))
zipfile.add(entry,file)
end
end

archive
end

def extract_zip(remote_zip, local, shell_id)
logger.debug("extracting #{remote_zip} to #{remote_zip.gsub('/','\\').gsub('.zip','')}")
command = <<-EOH
$shellApplication = new-object -com shell.application
$zip_path = "$($env:systemDrive)#{remote_zip.gsub('/','\\')}"

$zipPackage = $shellApplication.NameSpace($zip_path)
$dest_path = "$($env:systemDrive)#{remote_zip.gsub('/','\\').gsub('.zip','')}"
mkdir $dest_path -ErrorAction SilentlyContinue
$destinationFolder = $shellApplication.NameSpace($dest_path)
$destinationFolder.CopyHere($zipPackage.Items(),0x10)
EOH

output = powershell(command, shell_id)
raise_upload_error_if_failed(output, local, remote_zip)
end

# Moves and decodes the given file temp file on the guest to its
# permanent location
#
# @param [String] The id of a shell instance to run the command from
# @param [String] The source base64 encoded temp file path on the guest
# @param [String] The destination file path on the guest
def decode_remote_file(shell_id, local, remote)
logger.debug("Decoding temp file '#{remote}'")
command = <<-EOH
$tmp_file_path = [System.IO.Path]::GetFullPath('#{remote}')

$dest_dir = ([System.IO.Path]::GetDirectoryName($tmp_file_path))
New-Item -ItemType directory -Force -Path $dest_dir

$base64_string = Get-Content $tmp_file_path
$bytes = [System.Convert]::FromBase64String($base64_string)
[System.IO.File]::WriteAllBytes($tmp_file_path, $bytes)
EOH
output = powershell(command, shell_id)
raise_upload_error_if_failed(output, local, remote)
end

def raise_upload_error_if_failed(output, from, to)
raise TransportFailed,
:from => from,
:to => to,
:message => output.inspect unless output[:exitcode].zero?
end
end
end
end
Loading