Skip to content
Snippets Groups Projects
Verified Commit 91805e1d authored by Alexander Olofsson's avatar Alexander Olofsson
Browse files

Rework testing to properly stub WinRM

parent 08a4c372
No related branches found
No related tags found
No related merge requests found
...@@ -49,12 +49,9 @@ class WdsServer < ApplicationRecord ...@@ -49,12 +49,9 @@ class WdsServer < ApplicationRecord
objects = run_wql('SELECT * FROM MSFT_WdsClient', on_error: {})[:msft_wdsclient] objects = run_wql('SELECT * FROM MSFT_WdsClient', on_error: {})[:msft_wdsclient]
objects = nil if objects&.empty? objects = nil if objects&.empty?
objects ||= begin objects ||= begin
data = connection.shell(:powershell) do |s| clients = run_pwsh('Get-WdsClient').stdout
s.run('Get-WdsClient | ConvertTo-Json -Compress') clients = '[]' if clients.empty?
end.stdout underscore_result([JSON.parse(clients)].flatten)
data = '[]' if data.empty?
underscore_result([JSON.parse(data)].flatten)
end end
objects objects
...@@ -82,18 +79,14 @@ class WdsServer < ApplicationRecord ...@@ -82,18 +79,14 @@ class WdsServer < ApplicationRecord
raise NotImplementedError, 'Not finished yet' raise NotImplementedError, 'Not finished yet'
ensure_unattend(host) ensure_unattend(host)
connection.shell(:powershell) do |sh| run_pwsh("New-WdsClient -DeviceID '#{host.mac.upcase.delete ':'}' -DeviceName '#{host.name}' -WdsClientUnattend '#{unattend_file(host)}' -BootImagePath 'boot\\#{wdsify_architecture(host.architecture)}\\images\\#{(host.wds_boot_image || boot_images.first).file_name}' -PxePromptPolicy 'NoPrompt'")
sh.run("New-WdsClient -DeviceID '#{host.mac.upcase.delete ':'}' -DeviceName '#{host.name}' -WdsClientUnattend '#{unattend_file(host)}' -BootImagePath 'boot\\#{wdsify_architecture(host.architecture)}\\images\\#{(host.wds_boot_image || boot_images.first).file_name}' -PxePromptPolicy 'NoPrompt'")
end
end end
def delete_client(host) def delete_client(host)
raise NotImplementedError, 'Not finished yet' raise NotImplementedError, 'Not finished yet'
delete_unattend(host) delete_unattend(host)
connection.shell(:powershell) do |sh| run_pwsh("Remove-WdsClient -DeviceID '#{host.mac.upcase.delete ':'}'", json: false)
sh.run("Remove-WdsClient -DeviceID '#{host.mac.upcase.delete ':'}'")
end
end end
def all_images def all_images
...@@ -162,9 +155,10 @@ class WdsServer < ApplicationRecord ...@@ -162,9 +155,10 @@ class WdsServer < ApplicationRecord
def unattend_path def unattend_path
cache.cache(:unattend_path) do cache.cache(:unattend_path) do
JSON.parse(connection.shell(:powershell) do |sh| JSON.parse(
sh.run('Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\WDSServer\Providers\WDSTFTP -Name RootFolder | select RootFolder | ConvertTo-Json -Compress') run_pwsh('Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\WDSServer\Providers\WDSTFTP -Name RootFolder | select RootFolder'),
end, symbolize_names: true)[:RootFolder] symbolize_names: true
)[:RootFolder]
end end
end end
...@@ -187,6 +181,7 @@ class WdsServer < ApplicationRecord ...@@ -187,6 +181,7 @@ class WdsServer < ApplicationRecord
raise 'No provisioning interface available' unless iface raise 'No provisioning interface available' unless iface
raise NotImplementedException, 'TODO: Not implemented yet' raise NotImplementedException, 'TODO: Not implemented yet'
raise NotImplementedException, 'TODO: Not implemented yet' if SETTINGS[:wds_unattend_group]
# TODO: render template, send as heredoc # TODO: render template, send as heredoc
template = host.operatingsystem.provisioning_templates.find { |t| t.template_kind.name == 'wds_unattend' } template = host.operatingsystem.provisioning_templates.find { |t| t.template_kind.name == 'wds_unattend' }
...@@ -198,33 +193,33 @@ class WdsServer < ApplicationRecord ...@@ -198,33 +193,33 @@ class WdsServer < ApplicationRecord
template_data = host.render_template template: template template_data = host.render_template template: template
connection.shell(:powershell) do |sh| file_path = unattend_file(host)
file_path = unattend_file(host) script = []
script << "$unattend_render = @'\n#{template_data}\n'@"
sh.run("$unattend_render = @'\n#{template_data}\n'@") script << "New-Item -Path '#{file_path}' -ItemType 'file' -Value $unattend_render"
sh.run("New-Item -Path '#{file_path}' -ItemType 'file' -Value $unattend_render")
source_image = host.wds_facet.install_image
source_image = host.wds_facet.install_image target_image = target_image_for(host)
target_image = target_image_for(host)
if SETTINGS[:wds_unattend_group] if SETTINGS[:wds_unattend_group]
raise NotImplementedException, 'TODO: Not implemented yet' # New-WdsInstallImageGroup -Name #{SETTINGS[:wds_unattend_group]}
# New-WdsInstallImageGroup -Name #{SETTINGS[:wds_unattend_group]} # Export-WdsInstallImage -ImageGroup <Group> ...
# Export-WdsInstallImage -ImageGroup <Group> ... # Import-WdsInstallImage -ImageGroup #{SETTINGS[:wds_unattend_group]} -UnattendFile '#{file_path}' -OverwriteUnattend ...
# Import-WdsInstallImage -ImageGroup #{SETTINGS[:wds_unattend_group]} -UnattendFile '#{file_path}' -OverwriteUnattend ... else
else script << "Copy-WdsInstallImage -ImageGroup '#{source_image.image_group}' -FileName '#{source_image.file_name}' -ImageName '#{source_image.image_name}' -NewFileName '#{target_image.file_name}' -NewImageName '#{target_image.image_name}'"
sh.run("Copy-WdsInstallImage -ImageGroup '#{source_image.image_group}' -FileName '#{source_image.file_name}' -ImageName '#{source_image.image_name}' -NewFileName '#{target_image.file_name}' -NewImageName '#{target_image.image_name}'") script << "Set-WdsInstallImage -ImageGroup '#{target_image.image_group}' -FileName '#{target_image.file_name}' -ImageName '#{target_image.image_name}' -DisplayOrder 99999 -UnattendFile '#{file_path}' -OverwriteUnattend"
sh.run("Set-WdsInstallImage -ImageGroup '#{target_image.image_group}' -FileName '#{target_image.file_name}' -ImageName '#{target_image.image_name}' -DisplayOrder 99999 -UnattendFile '#{file_path}' -OverwriteUnattend")
end
end end
run_pwsh script.join("\n"), json: false
end end
def delete_unattend(host) def delete_unattend(host)
image = target_image_for(host) image = target_image_for(host)
connection.shell(:powershell) do |sh| command = []
sh.run("Remove-WdsInstallImage -ImageGroup '#{image.image_group}' -ImageName '#{image.image_name}' -FileName '#{image.file_name}'") command << "Remove-WdsInstallImage -ImageGroup '#{image.image_group}' -ImageName '#{image.image_name}' -FileName '#{image.file_name}'"
sh.run("Remove-Item -Path '#{unattend_file(host)}'") command << "Remove-Item -Path '#{unattend_file(host)}'"
end.errcode.zero? run_pwsh(command.join("\n"), json: false).errcode.zero?
end end
def ensure_client(_host) def ensure_client(_host)
...@@ -242,14 +237,12 @@ class WdsServer < ApplicationRecord ...@@ -242,14 +237,12 @@ class WdsServer < ApplicationRecord
objects = nil if objects.empty? objects = nil if objects.empty?
unless objects unless objects
begin result = run_pwsh "Get-WDS#{type.to_s.capitalize}Image#{" -ImageName '#{name.sub("'", "`'")}'" if name}"
result = connection.shell(:powershell) do |s|
s.run("Get-WDS#{type.to_s.capitalize}Image #{"-ImageName '#{name.sub("'", "`'")}'" if name} | ConvertTo-Json -Compress")
end
begin
objects = underscore_result([JSON.parse(result.stdout)].flatten) objects = underscore_result([JSON.parse(result.stdout)].flatten)
rescue JSON::ParserError => e rescue JSON::ParserError => e
::Rails.logger.error "#{e.class}: #{e}\n#{result}" ::Rails.logger.error "Failed to parse images - #{e.class}: #{e}, the data was;\n#{result.inspect}"
raise e raise e
end end
end end
...@@ -270,6 +263,14 @@ class WdsServer < ApplicationRecord ...@@ -270,6 +263,14 @@ class WdsServer < ApplicationRecord
end end
end end
def run_pwsh(command, json: true)
cmd_arr = [command]
cmd_arr << '| ConvertTo-Json -Compress' if json
connection.shell(:powershell) do |s|
s.run cmd_arr.join(' ')
end
end
def run_wql(wql, on_error: :raise) def run_wql(wql, on_error: :raise)
connection.run_wql(wql) connection.run_wql(wql)
rescue StandardError rescue StandardError
......
...@@ -8,7 +8,7 @@ module ForemanWds ...@@ -8,7 +8,7 @@ module ForemanWds
FactoryBot.build(:wds_server) FactoryBot.build(:wds_server)
end end
let(:host) do let(:host) do
FactoryBot.build(:host, :managed, :with_wds_facet) do FactoryBot.build(:host, :managed, :with_wds_facet) do |host|
host.wds_facet.wds_server = wds_server host.wds_facet.wds_server = wds_server
end end
end end
...@@ -23,6 +23,12 @@ module ForemanWds ...@@ -23,6 +23,12 @@ module ForemanWds
end end
context 'with WDS server' do context 'with WDS server' do
setup do
wds_server.stubs(:run_wql).returns({})
wds_server.stubs(:run_pwsh).with('Get-WDSBootImage').returns(OpenStruct.new stdout: '[]')
wds_server.stubs(:run_pwsh).with("Get-WDSInstallImage -ImageName 'install.wim'").returns(OpenStruct.new stdout: '[]')
end
it 'does not error' do it 'does not error' do
assert_nil host.wds_facet.boot_image assert_nil host.wds_facet.boot_image
assert_nil host.wds_facet.install_image assert_nil host.wds_facet.install_image
......
...@@ -21,3 +21,48 @@ ActiveSupport::TestCase.file_fixture_path = File.join(__dir__, 'fixtures') ...@@ -21,3 +21,48 @@ ActiveSupport::TestCase.file_fixture_path = File.join(__dir__, 'fixtures')
# Add plugin to FactoryBot's paths # Add plugin to FactoryBot's paths
FactoryBot.definition_file_paths << File.join(__dir__, 'factories') FactoryBot.definition_file_paths << File.join(__dir__, 'factories')
FactoryBot.reload FactoryBot.reload
class ActiveSupport::TestCase
setup :setup_winrm_stubs
def stub_winrm_powershell(command = nil, &block)
ret = if block_given?
@winrm_shell_mock[:powershell].stubs(:run).with(&block)
else
@winrm_shell_mock[:powershell].stubs(:run).with(command)
end
class << ret
def returns_pwsh(value, **params)
returns OpenStruct.new(stdout: value, **params)
end
end
ret
end
def stub_winrm_wql(query = nil)
if query
WinRM::Connection.any_instance.stubs(:run_wql).with(query)
else
WinRM::Connection.any_instance.stubs(:run_wql)
end
end
private
def setup_winrm_stubs
return if @winrm_mock
@winrm_mock = true
require 'winrm'
transport_mock = mock('winrm::http::transport')
WinRM::Connection.any_instance.stubs(:transport).returns(transport_mock)
transport_mock.stubs(:send_request).raises(StandardError, 'Real WinRM connections are not allowed')
@winrm_shell_mock = {
powershell: mock('winrm::shell::powershell')
}
WinRM::Connection.any_instance.stubs(:shell).with(:powershell).yields @winrm_shell_mock[:powershell]
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment