lib/beaker-rspec/helpers/serverspec.rb in beaker-rspec-5.0.0 vs lib/beaker-rspec/helpers/serverspec.rb in beaker-rspec-5.0.1

- old
+ new

@@ -1,11 +1,42 @@ require 'serverspec' require 'specinfra' +require 'specinfra/backend/powershell/script_helper' # Set specinfra backend to use our custom backend -set :backend, 'BeakerExec' +set :backend, 'BeakerDispatch' +module Specinfra + + # Accessor for current example + def cur_example + Specinfra.backend.example + end + + def get_working_node + example = cur_example + if example and example.metadata[:node] + node = example.metadata[:node] + else + node = default_node + end + node + end + + # The cygwin backend + def cygwin_backend + @cygwin_backend ||= Specinfra::Backend::BeakerCygwin.instance + end + + # Backend for everything non-cygwin + def exec_backend + @exec_backend ||= Specinfra::Backend::BeakerExec.instance + end + +end + + # Override existing specinfra configuration to avoid conflicts # with beaker's shell, stdout, stderr defines module Specinfra module Configuration class << self @@ -23,47 +54,218 @@ :request_pty, :ssh_options, :dockerfile_finalizer, ].freeze end + end end -module Specinfra::Backend - class BeakerExec < Specinfra::Backend::Base - # Run a command using serverspec. Defaults to running on the 'default' test node, otherwise uses the - # node specified in @example.metadata[:node] - # @param [String] cmd The serverspec command to executed - # @param [Hash] opt No currently supported options - # @return [Hash] Returns a hash containing :exit_status, :stdout and :stderr - def run_command(cmd, opt={}) - cmd = build_command(cmd) - cmd = add_pre_command(cmd) - ret = ssh_exec!(cmd) +module Specinfra::Helper::Os + private - if @example - @example.metadata[:command] = cmd - @example.metadata[:stdout] = ret[:stdout] + # Override detect_os to look at the node platform, short circuit discoverability + # when we know that we have a windows node + def detect_os + return Specinfra.configuration.os if Specinfra.configuration.os + backend = Specinfra.backend + node = get_working_node + if node['platform'] =~ /windows/ + return {:family => 'windows'} + end + Specinfra::Helper::DetectOs.subclasses.each do |c| + res = c.detect + if res + res[:arch] ||= Specinfra.backend.run_command('uname -m').stdout.strip + return res end + end + end +end - CommandResult.new ret +class Specinfra::CommandFactory + class << self + # Force creation of a windows command + def get_windows_cmd(meth, *args) + + action, resource_type, subaction = breakdown(meth) + method = action + method += "_#{subaction}" if subaction + + common_class = Specinfra::Command + base_class = common_class.const_get('Base') + os_class = common_class.const_get('Windows') + version_class = os_class.const_get('Base') + command_class = version_class.const_get(resource_type.to_camel_case) + + command_class = command_class.create + if command_class.respond_to?(method) + command_class.send(method, *args) + else + raise NotImplementedError.new("#{method} is not implemented in #{command_class}") + end end + end +end + +module Specinfra + # Rewrite the runner to use the appropriate backend based upon platform information + class Runner + + def self.method_missing(meth, *args) + backend = Specinfra.backend + node = get_working_node + if node['platform'] !~ /windows/ + processor = Specinfra::Processor + if processor.respond_to?(meth) + processor.send(meth, *args) + elsif backend.respond_to?(meth) + backend.send(meth, *args) + else + run(meth, *args) + end + else + if backend.respond_to?(meth) + backend.send(meth, *args) + else + run(meth, *args) + end + end + end + + private + + def self.run(meth, *args) + backend = Specinfra.backend + node = get_working_node + if node['platform'] =~ /windows/ + cmd = Specinfra.command.get_windows_cmd(meth, *args) + else + cmd = Specinfra.command.get(meth, *args) + end + backend = Specinfra.backend + ret = backend.run_command(cmd) + if meth.to_s =~ /^check/ + ret.success? + else + ret + end + end + end +end + +module Specinfra::Backend::PowerShell + class Command + # Do a better job at escaping regexes, handle both LF and CRLF (YAY!) + def convert_regexp(target) + case target + when Regexp + target.source + else + Regexp.escape(target.to_s.gsub '/', '\/').gsub('\n', '(\r\n|\n)') + end + end + end +end + +module Specinfra::Backend + class BeakerBase < Specinfra::Backend::Base + # Example accessor + def example + @example + end + # Execute the provided ssh command # @param [String] command The command to be executed # @return [Hash] Returns a hash containing :exit_status, :stdout and :stderr - def ssh_exec!(command) - if @example and @example.metadata[:node] - node = @example.metadata[:node] - else - node = default - end + def ssh_exec!(node, command) r = on node, command, { :acceptable_exit_codes => (0..127) } { :exit_status => r.exit_code, :stdout => r.stdout, :stderr => r.stderr } + end + + end +end + +# Used as a container for the two backends, dispatches as windows/nix depending on node platform +module Specinfra::Backend + class BeakerDispatch < BeakerBase + + def dispatch_method(meth, *args) + if get_working_node['platform'] =~ /windows/ + cygwin_backend.send(meth, *args) + else + exec_backend.send(meth, *args) + end + end + + def run_command(cmd, opts={}) + dispatch_method('run_command', cmd, opts) + end + + def build_command(cmd) + dispatch_method('build_command', cmd) + end + + def add_pre_command(cmd) + dispatch_method('add_pre_command', cmd) + end + end +end + +# Backend for running serverspec commands on windows test nodes +module Specinfra::Backend + class BeakerCygwin < BeakerBase + include Specinfra::Backend::PowerShell::ScriptHelper + + # Run a windows style command using serverspec. Defaults to running on the 'default_node' + # test node, otherwise uses the node specified in @example.metadata[:node] + # @param [String] cmd The serverspec command to executed + # @param [Hash] opt No currently supported options + # @return [Hash] Returns a hash containing :exit_status, :stdout and :stderr + def run_command(cmd, opt = {}) + node = get_working_node + script = create_script(cmd) + on node, "rm -f script.ps1" + create_remote_file(node, 'script.ps1', script) + # cygwin support /dev/null, if running from winrm would use < NULL + ret = ssh_exec!(node, "powershell.exe -File script.ps1 < /dev/null") + + if @example + @example.metadata[:command] = script + @example.metadata[:stdout] = ret[:stdout] + end + + CommandResult.new ret + end + end +end + +# Backend for running serverspec commands on non-windows test nodes +module Specinfra::Backend + class BeakerExec < BeakerBase + + # Run a unix style command using serverspec. Defaults to running on the 'default_node' + # test node, otherwise uses the node specified in @example.metadata[:node] + # @param [String] cmd The serverspec command to executed + # @param [Hash] opt No currently supported options + # @return [Hash] Returns a hash containing :exit_status, :stdout and :stderr + def run_command(cmd, opt = {}) + node = get_working_node + cmd = build_command(cmd) + cmd = add_pre_command(cmd) + ret = ssh_exec!(node, cmd) + + if @example + @example.metadata[:command] = cmd + @example.metadata[:stdout] = ret[:stdout] + end + + CommandResult.new ret end def build_command(cmd) useshell = '/bin/sh' cmd = cmd.shelljoin if cmd.is_a?(Array)