Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge pull request #126 from joshcooper/ticket/master/11944-separate-…

…host-logic

(#11944) Separate native command logic from acceptance tests
  • Loading branch information...
commit f9cb52b2463c255a417c78ff7820ae09911b4ec9 2 parents e36f0d2 + 64e3330
Dominic  Maraglia authored February 03, 2012
21  lib/command.rb
... ...
@@ -1,6 +1,8 @@
1 1
 # An immutable data structure representing a task to run on a remote
2 2
 # machine.
3 3
 class Command
  4
+  include Test::Unit::Assertions
  5
+
4 6
   def initialize(command_string)
5 7
     @command_string = command_string
6 8
   end
@@ -12,7 +14,24 @@ def cmd_line(host_info)
12 14
   end
13 15
 
14 16
   def exec(host, options={})
15  
-    host.exec(cmd_line(host), options)
  17
+    options[:acceptable_exit_codes] ||= [0]
  18
+    options[:failing_exit_codes]    ||= [1]
  19
+
  20
+    cmdline = cmd_line(host)
  21
+    result = host.exec(cmdline, options)
  22
+
  23
+    unless options[:silent] then
  24
+      result.log
  25
+      if options[:acceptable_exit_codes].include?(result.exit_code)
  26
+        # cool.
  27
+      elsif options[:failing_exit_codes].include?(result.exit_code)
  28
+        assert( false, "Host '#{host} exited with #{result.exit_code} running: #{cmdline}" )
  29
+      else
  30
+        raise "Host '#{host}' exited with #{result.exit_code} running: #{cmdline}"
  31
+      end
  32
+    end
  33
+
  34
+    result
16 35
   end
17 36
 
18 37
   # Determine the appropriate puppet env command for the given host.
19  lib/command_factory.rb
... ...
@@ -0,0 +1,19 @@
  1
+module CommandFactory
  2
+  include Test::Unit::Assertions
  3
+
  4
+  def execute(command, options={}, &block)
  5
+    command = Command.new(command)
  6
+
  7
+    result = command.exec(self, options)
  8
+
  9
+    if block_given?
  10
+      yield result
  11
+    else
  12
+      result.stdout.chomp
  13
+    end
  14
+  end
  15
+
  16
+  def fail_test(msg)
  17
+    assert(false, msg)
  18
+  end
  19
+end
114  lib/host.rb
... ...
@@ -0,0 +1,114 @@
  1
+class Host
  2
+  def self.create(name, overrides, defaults)
  3
+    case overrides['platform']
  4
+    when /windows/;
  5
+      Windows::Host.new(name, overrides, defaults)
  6
+    else
  7
+      Unix::Host.new(name, overrides, defaults)
  8
+    end
  9
+  end
  10
+
  11
+  # A cache for active SSH connections to our execution nodes.
  12
+  def initialize(name, overrides, defaults)
  13
+    @name,@overrides,@defaults = name,overrides,defaults
  14
+  end
  15
+  def []=(k,v)
  16
+    @overrides[k] = v
  17
+  end
  18
+  def [](k)
  19
+    @overrides.has_key?(k) ? @overrides[k] : @defaults[k]
  20
+  end
  21
+  def to_str
  22
+    @name
  23
+  end
  24
+  def to_s
  25
+    @name
  26
+  end
  27
+  def +(other)
  28
+    @name+other
  29
+  end
  30
+
  31
+  attr_reader :name, :overrides
  32
+
  33
+  # Wrap up the SSH connection process; this will cache the connection and
  34
+  # allow us to reuse it for each operation without needing to reauth every
  35
+  # single time.
  36
+  def ssh
  37
+    tries = 1
  38
+    @ssh ||= begin
  39
+               Net::SSH.start(self, self['user'], self['ssh'])
  40
+             rescue
  41
+               tries += 1
  42
+               if tries < 4
  43
+                 puts "Try #{tries} -- Host Unreachable"
  44
+                 puts 'Trying again in 20 seconds'
  45
+                 sleep 20
  46
+                 retry
  47
+               end
  48
+             end
  49
+  end
  50
+
  51
+  def close
  52
+    if @ssh
  53
+      @ssh.close
  54
+    end
  55
+  end
  56
+
  57
+  def do_action(verb,*args)
  58
+    result = Result.new(self,args,'','',0)
  59
+    Log.debug "#{self}: #{verb}(#{args.inspect})"
  60
+    yield result unless $dry_run
  61
+    result
  62
+  end
  63
+
  64
+  def exec(command, options)
  65
+    do_action('RemoteExec',command) do |result|
  66
+      ssh.open_channel do |channel|
  67
+        if options[:pty] then
  68
+          channel.request_pty do |ch, success|
  69
+            if success
  70
+              puts "Allocated a PTY on #{@name} for #{command.inspect}"
  71
+            else
  72
+              abort "FAILED: could not allocate a pty when requested on " +
  73
+                "#{@name} for #{command.inspect}"
  74
+            end
  75
+          end
  76
+        end
  77
+
  78
+        channel.exec(command) do |terminal, success|
  79
+          abort "FAILED: to execute command on a new channel on #{@name}" unless success
  80
+          terminal.on_data                   { |ch, data|       result.stdout << data }
  81
+          terminal.on_extended_data          { |ch, type, data| result.stderr << data if type == 1 }
  82
+          terminal.on_request("exit-status") { |ch, data|       result.exit_code = data.read_long  }
  83
+
  84
+          # queue stdin data, force it to packets, and signal eof: this
  85
+          # triggers action in many remote commands, notably including
  86
+          # 'puppet apply'.  It must be sent at some point before the rest
  87
+          # of the action.
  88
+          terminal.send_data(options[:stdin].to_s)
  89
+          terminal.process
  90
+          terminal.eof!
  91
+        end
  92
+      end
  93
+      # Process SSH activity until we stop doing that - which is when our
  94
+      # channel is finished with...
  95
+      ssh.loop
  96
+    end
  97
+  end
  98
+
  99
+  def do_scp(source, target)
  100
+    do_action("ScpFile",source,target) { |result|
  101
+      # Net::Scp always returns 0, so just set the return code to 0 Setting
  102
+      # these values allows reporting via result.log(test_name)
  103
+      result.stdout = "SCP'ed file #{source} to #{@host}:#{target}"
  104
+      result.stderr=nil
  105
+      result.exit_code=0
  106
+      recursive_scp='false'
  107
+      recursive_scp='true' if File.directory? source
  108
+      ssh.scp.upload!(source, target, :recursive => recursive_scp)
  109
+    }
  110
+  end
  111
+end
  112
+
  113
+require 'lib/host/windows'
  114
+require 'lib/host/unix'
37  lib/host/unix.rb
... ...
@@ -0,0 +1,37 @@
  1
+require 'lib/host'
  2
+require 'lib/command_factory'
  3
+
  4
+module Unix
  5
+  class Host < Host
  6
+    require 'lib/host/unix/user'
  7
+    require 'lib/host/unix/group'
  8
+    require 'lib/host/unix/file'
  9
+    require 'lib/host/unix/exec'
  10
+
  11
+    include Unix::User
  12
+    include Unix::Group
  13
+    include Unix::File
  14
+    include Unix::Exec
  15
+
  16
+    PE_DEFAULTS = {
  17
+      'user'         => 'root',
  18
+      'puppetpath'   => '/etc/puppetlabs/puppet',
  19
+      'puppetbin'    => '/usr/local/bin/puppet',
  20
+      'puppetbindir' => '/opt/puppet/bin'
  21
+    }
  22
+
  23
+    DEFAULTS = {
  24
+      'user'         => 'root',
  25
+      'puppetpath'   => '/etc/puppet',
  26
+      'puppetvardir' => '/var/lib/puppet',
  27
+      'puppetbin'    => '/usr/bin/puppet',
  28
+      'puppetbindir' => '/usr/bin'
  29
+    }
  30
+
  31
+    def initialize(name, overrides, defaults)
  32
+      super(name, overrides, defaults)
  33
+
  34
+      @defaults = defaults.merge(TestConfig.puppet_enterprise_version ? PE_DEFAULTS : DEFAULTS)
  35
+    end
  36
+  end
  37
+end
15  lib/host/unix/exec.rb
... ...
@@ -0,0 +1,15 @@
  1
+module Unix::Exec
  2
+  include CommandFactory
  3
+
  4
+  def echo(msg, abs=true)
  5
+    (abs ? '/bin/echo' : 'echo') + " #{msg}"
  6
+  end
  7
+
  8
+  def touch(file, abs=true)
  9
+    (abs ? '/bin/touch' : 'touch') + " #{file}"
  10
+  end
  11
+
  12
+  def path
  13
+    '/bin:/usr/bin'
  14
+  end
  15
+end
15  lib/host/unix/file.rb
... ...
@@ -0,0 +1,15 @@
  1
+module Unix::File
  2
+  include CommandFactory
  3
+
  4
+  def tmpfile(name)
  5
+    execute("mktemp -t #{name}.XXXXXX")
  6
+  end
  7
+
  8
+  def tmpdir(name)
  9
+    execute("mktemp -td #{name}.XXXXXX")
  10
+  end
  11
+
  12
+  def path_split(paths)
  13
+    paths.split(':')
  14
+  end
  15
+end
32  lib/host/unix/group.rb
... ...
@@ -0,0 +1,32 @@
  1
+module Unix::Group
  2
+  include CommandFactory
  3
+
  4
+  def group_list(&block)
  5
+    execute("getent group") do |result|
  6
+      groups = []
  7
+      result.stdout.each_line do |line|
  8
+        groups << (line.match(/^([^:]+)/) or next)[1]
  9
+      end
  10
+
  11
+      yield result if block_given?
  12
+
  13
+      groups
  14
+    end
  15
+  end
  16
+
  17
+  def group_get(name, &block)
  18
+    execute("getent group #{name}") do |result|
  19
+      fail_test "failed to get group #{name}" unless result.stdout =~ /^#{name}:.*:[0-9]+:/
  20
+
  21
+      yield result if block_given?
  22
+    end
  23
+  end
  24
+
  25
+  def group_present(name, &block)
  26
+    execute("if ! getent group #{name}; then groupadd #{name}; fi", {}, &block)
  27
+  end
  28
+
  29
+  def group_absent(name, &block)
  30
+    execute("if getent group #{name}; then groupdel #{name}; fi", {}, &block)
  31
+  end
  32
+end
32  lib/host/unix/user.rb
... ...
@@ -0,0 +1,32 @@
  1
+module Unix::User
  2
+  include CommandFactory
  3
+
  4
+  def user_list(&block)
  5
+    execute("getent passwd") do |result|
  6
+      users = []
  7
+      result.stdout.each_line do |line|
  8
+        users << (line.match( /^([^:]+)/) or next)[1]
  9
+      end
  10
+
  11
+      yield result if block_given?
  12
+
  13
+      users
  14
+    end
  15
+  end
  16
+
  17
+  def user_get(name, &block)
  18
+    execute("getent passwd #{name}") do |result|
  19
+      fail_test "failed to get user #{name}" unless result.stdout =~  /^#{name}:/
  20
+
  21
+      yield result if block_given?
  22
+    end
  23
+  end
  24
+
  25
+  def user_present(name, &block)
  26
+    execute("if ! getent passwd #{name}; then useradd #{name}; fi", {}, &block)
  27
+  end
  28
+
  29
+  def user_absent(name, &block)
  30
+    execute("if getent passwd #{name}; then userdel #{name}; fi", {}, &block)
  31
+  end
  32
+end
29  lib/host/windows.rb
... ...
@@ -0,0 +1,29 @@
  1
+require 'lib/host'
  2
+require 'lib/command_factory'
  3
+
  4
+module Windows
  5
+  class Host < Host
  6
+    require 'lib/host/windows/user'
  7
+    require 'lib/host/windows/group'
  8
+    require 'lib/host/windows/file'
  9
+    require 'lib/host/windows/exec'
  10
+
  11
+    include Windows::User
  12
+    include Windows::Group
  13
+    include Windows::File
  14
+    include Windows::Exec
  15
+
  16
+    DEFAULTS = {
  17
+      'user'         => 'Administrator',
  18
+      'group'        => 'Administrators',
  19
+      'puppetpath'   => '"`cygpath -F 35`/PuppetLabs/puppet/etc"',
  20
+      'puppetvardir' => '"`cygpath -F 35`/PuppetLabs/puppet/var"'
  21
+    }
  22
+
  23
+    def initialize(name, overrides, defaults)
  24
+      super(name, overrides, defaults)
  25
+
  26
+      @defaults = defaults.merge(DEFAULTS)
  27
+    end
  28
+  end
  29
+end
18  lib/host/windows/exec.rb
... ...
@@ -0,0 +1,18 @@
  1
+module Windows::Exec
  2
+  include CommandFactory
  3
+
  4
+  ABS_CMD = 'c:\\\\windows\\\\system32\\\\cmd.exe'
  5
+  CMD = 'cmd.exe'
  6
+
  7
+  def echo(msg, abs=true)
  8
+    (abs ? ABS_CMD : CMD) + " /c echo #{msg}"
  9
+  end
  10
+
  11
+  def touch(file, abs=true)
  12
+    (abs ? ABS_CMD : CMD) + " /c echo. 2> #{file}"
  13
+  end
  14
+
  15
+  def path
  16
+    'c:/windows/system32;c:/windows'
  17
+  end
  18
+end
15  lib/host/windows/file.rb
... ...
@@ -0,0 +1,15 @@
  1
+module Windows::File
  2
+  include CommandFactory
  3
+
  4
+  def tmpfile(name)
  5
+    execute("cygpath -m $(mktemp -t #{name}.XXXXXX)")
  6
+  end
  7
+
  8
+  def tmpdir(name)
  9
+    execute("cygpath -m $(mktemp -td #{name}.XXXXXX)")
  10
+  end
  11
+
  12
+  def path_split(paths)
  13
+    paths.split(';')
  14
+  end
  15
+end
32  lib/host/windows/group.rb
... ...
@@ -0,0 +1,32 @@
  1
+module Windows::Group
  2
+  include CommandFactory
  3
+
  4
+  def group_list(&block)
  5
+    execute('wmic group where localaccount="true" get name /format:value') do |result|
  6
+      groups = []
  7
+      result.stdout.each_line do |line|
  8
+        groups << (line.match(/^Name=([\w ]+)/) or next)[1]
  9
+      end
  10
+
  11
+      yield result if block_given?
  12
+
  13
+      groups
  14
+    end
  15
+  end
  16
+
  17
+  def group_get(name, &block)
  18
+    execute("net localgroup \"#{name}\"") do |result|
  19
+      fail_test "failed to get group #{name}" unless result.stdout =~ /^Alias name\s+#{name}/
  20
+
  21
+      yield result if block_given?
  22
+    end
  23
+  end
  24
+
  25
+  def group_present(name, &block)
  26
+    execute("net localgroup /add \"#{name}\"", {:acceptable_exit_codes => [0,2]}, &block)
  27
+  end
  28
+
  29
+  def group_absent(name, &block)
  30
+    execute("net localgroup /delete \"#{name}\"", {:acceptable_exit_codes => [0,2]}, &block)
  31
+  end
  32
+end
32  lib/host/windows/user.rb
... ...
@@ -0,0 +1,32 @@
  1
+module Windows::User
  2
+  include CommandFactory
  3
+
  4
+  def user_list(&block)
  5
+    execute('wmic useraccount where localaccount="true" get name /format:value') do |result|
  6
+      users = []
  7
+      result.stdout.each_line do |line|
  8
+        users << (line.match(/^Name=([\w ]+)/) or next)[1]
  9
+      end
  10
+
  11
+      yield result if block_given?
  12
+
  13
+      users
  14
+    end
  15
+  end
  16
+
  17
+  def user_get(name, &block)
  18
+    execute("net user \"#{name}\"") do |result|
  19
+      fail_test "failed to get user #{name}" unless result.stdout =~ /^User name\s+#{name}/
  20
+
  21
+      yield result if block_given?
  22
+    end
  23
+  end
  24
+
  25
+  def user_present(name, &block)
  26
+    execute("net user /add \"#{name}\"", {:acceptable_exit_codes => [0,2]}, &block)
  27
+  end
  28
+
  29
+  def user_absent(name, &block)
  30
+    execute("net user /delete \"#{name}\"", {:acceptable_exit_codes => [0,2]}, &block)
  31
+  end
  32
+end
15  lib/test_case.rb
... ...
@@ -1,5 +1,5 @@
1 1
 class TestCase
2  
-  require 'lib/test_case/host'
  2
+  require 'lib/host'
3 3
   require 'tempfile'
4 4
   require 'benchmark'
5 5
   require 'stringio'
@@ -90,8 +90,6 @@ def test_name(test_name,&block)
90 90
   #
91 91
   attr_reader :result
92 92
   def on(host, command, options={}, &block)
93  
-    options[:acceptable_exit_codes] ||= [0]
94  
-    options[:failing_exit_codes]    ||= [1]
95 93
     if command.is_a? String
96 94
       command = Command.new(command)
97 95
     end
@@ -100,17 +98,6 @@ def on(host, command, options={}, &block)
100 98
     else
101 99
       @result = command.exec(host, options)
102 100
 
103  
-      unless options[:silent] then
104  
-        result.log
105  
-        if options[:acceptable_exit_codes].include?(exit_code)
106  
-          # cool.
107  
-        elsif options[:failing_exit_codes].include?(exit_code)
108  
-          assert( false, "Host '#{host} exited with #{exit_code} running: #{command.cmd_line('')}" )
109  
-        else
110  
-          raise "Host '#{host}' exited with #{exit_code} running: #{command.cmd_line('')}"
111  
-        end
112  
-      end
113  
-
114 101
       # Also, let additional checking be performed by the caller.
115 102
       yield if block_given?
116 103
 
148  lib/test_case/host.rb
... ...
@@ -1,148 +0,0 @@
1  
-class TestCase
2  
-  class Host
3  
-    def self.create(name, overrides, defaults)
4  
-      case overrides['platform']
5  
-      when /windows/;
6  
-        WindowsHost.new(name, overrides, defaults)
7  
-      else
8  
-        UnixHost.new(name, overrides, defaults)
9  
-      end
10  
-    end
11  
-
12  
-    # A cache for active SSH connections to our execution nodes.
13  
-    def initialize(name, overrides, defaults)
14  
-      @name,@overrides,@defaults = name,overrides,defaults
15  
-    end
16  
-    def []=(k,v)
17  
-      @overrides[k] = v
18  
-    end
19  
-    def [](k)
20  
-      @overrides.has_key?(k) ? @overrides[k] : @defaults[k]
21  
-    end
22  
-    def to_str
23  
-      @name
24  
-    end
25  
-    def to_s
26  
-      @name
27  
-    end
28  
-    def +(other)
29  
-      @name+other
30  
-    end
31  
-
32  
-    attr_reader :name, :overrides
33  
-
34  
-    # Wrap up the SSH connection process; this will cache the connection and
35  
-    # allow us to reuse it for each operation without needing to reauth every
36  
-    # single time.
37  
-    def ssh
38  
-      tries = 1
39  
-      @ssh ||= begin
40  
-                 Net::SSH.start(self, self['user'] || "root", self['ssh'])
41  
-               rescue
42  
-                 tries += 1
43  
-                 if tries < 4
44  
-                   puts "Try #{tries} -- Host Unreachable"
45  
-                   puts 'Trying again in 20 seconds'
46  
-                   sleep 20
47  
-                   retry
48  
-                 end
49  
-               end
50  
-    end
51  
-
52  
-    def close
53  
-      if @ssh
54  
-        @ssh.close
55  
-      end
56  
-    end
57  
-
58  
-    def do_action(verb,*args)
59  
-      result = Result.new(self,args,'','',0)
60  
-      Log.debug "#{self}: #{verb}(#{args.inspect})"
61  
-      yield result unless $dry_run
62  
-      result
63  
-    end
64  
-
65  
-    def exec(command, options)
66  
-      do_action('RemoteExec',command) do |result|
67  
-        ssh.open_channel do |channel|
68  
-          if options[:pty] then
69  
-            channel.request_pty do |ch, success|
70  
-              if success
71  
-                puts "Allocated a PTY on #{@name} for #{command.inspect}"
72  
-              else
73  
-                abort "FAILED: could not allocate a pty when requested on " +
74  
-                  "#{@name} for #{command.inspect}"
75  
-              end
76  
-            end
77  
-          end
78  
-
79  
-          channel.exec(command) do |terminal, success|
80  
-            abort "FAILED: to execute command on a new channel on #{@name}" unless success
81  
-            terminal.on_data                   { |ch, data|       result.stdout << data }
82  
-            terminal.on_extended_data          { |ch, type, data| result.stderr << data if type == 1 }
83  
-            terminal.on_request("exit-status") { |ch, data|       result.exit_code = data.read_long  }
84  
-
85  
-            # queue stdin data, force it to packets, and signal eof: this
86  
-            # triggers action in many remote commands, notably including
87  
-            # 'puppet apply'.  It must be sent at some point before the rest
88  
-            # of the action.
89  
-            terminal.send_data(options[:stdin].to_s)
90  
-            terminal.process
91  
-            terminal.eof!
92  
-          end
93  
-        end
94  
-        # Process SSH activity until we stop doing that - which is when our
95  
-        # channel is finished with...
96  
-        ssh.loop
97  
-      end
98  
-    end
99  
-
100  
-    def do_scp(source, target)
101  
-      do_action("ScpFile",source,target) { |result|
102  
-        # Net::Scp always returns 0, so just set the return code to 0 Setting
103  
-        # these values allows reporting via result.log(test_name)
104  
-        result.stdout = "SCP'ed file #{source} to #{@host}:#{target}"
105  
-        result.stderr=nil
106  
-        result.exit_code=0
107  
-        recursive_scp='false'
108  
-        recursive_scp='true' if File.directory? source
109  
-        ssh.scp.upload!(source, target, :recursive => recursive_scp)
110  
-      }
111  
-    end
112  
-  end
113  
-
114  
-  class UnixHost < Host
115  
-    PE_DEFAULTS = {
116  
-      'puppetpath'   => '/etc/puppetlabs/puppet',
117  
-      'puppetbin'    => '/usr/local/bin/puppet',
118  
-      'puppetbindir' => '/opt/puppet/bin'
119  
-    }
120  
-
121  
-    DEFAULTS = {
122  
-      'puppetpath'   => '/etc/puppet',
123  
-      'puppetvardir' => '/var/lib/puppet',
124  
-      'puppetbin'    => '/usr/bin/puppet',
125  
-      'puppetbindir' => '/usr/bin'
126  
-    }
127  
-
128  
-    def initialize(name, overrides, defaults)
129  
-      super(name, overrides, defaults)
130  
-
131  
-      @defaults = defaults.merge(TestConfig.puppet_enterprise_version ? PE_DEFAULTS : DEFAULTS)
132  
-    end
133  
-  end
134  
-
135  
-  class WindowsHost < Host
136  
-    DEFAULTS = {
137  
-      'user'         => 'Administrator',
138  
-      'puppetpath'   => '"`cygpath -F 35`/PuppetLabs/puppet/etc"',
139  
-      'puppetvardir' => '"`cygpath -F 35`/PuppetLabs/puppet/var"'
140  
-    }
141  
-
142  
-    def initialize(name, overrides, defaults)
143  
-      super(name, overrides, defaults)
144  
-
145  
-      @defaults = defaults.merge(DEFAULTS)
146  
-    end
147  
-  end
148  
-end
4  lib/test_suite.rb
@@ -43,10 +43,12 @@ def run
43 43
     @test_files.each do |test_file|
44 44
       Log.notify
45 45
       Log.notify "Begin #{test_file}"
  46
+      start = Time.now
46 47
       test_case = TestCase.new(@hosts, config, options, test_file).run_test
  48
+      duration = Time.now - start
47 49
       @test_cases << test_case
48 50
 
49  
-      msg = "#{test_file} #{test_case.test_status == :skip ? 'skipp' : test_case.test_status}ed"
  51
+      msg = "#{test_file} #{test_case.test_status == :skip ? 'skipp' : test_case.test_status}ed in %.2f seconds" % duration.to_f
50 52
       case test_case.test_status
51 53
       when :pass
52 54
         Log.success msg
4  systest.rb
@@ -9,7 +9,7 @@
9 9
 require 'systemu'
10 10
 require 'test/unit'
11 11
 require 'yaml'
12  
-require 'lib/test_case/host'
  12
+require 'lib/host'
13 13
 
14 14
 Test::Unit.run = true
15 15
 Dir.glob(File.dirname(__FILE__) + '/lib/*.rb') {|file| require file}
@@ -52,7 +52,7 @@
52 52
 end
53 53
 
54 54
 # Generate hosts
55  
-hosts = config['HOSTS'].collect { |name,overrides| TestCase::Host.create(name,overrides,config['CONFIG']) }
  55
+hosts = config['HOSTS'].collect { |name,overrides| Host.create(name,overrides,config['CONFIG']) }
56 56
 begin
57 57
   # Run the harness for install
58 58
   TestSuite.new('setup', hosts, setup_options, config, TRUE).run_and_exit_on_failure

0 notes on commit f9cb52b

Please sign in to comment.
Something went wrong with that request. Please try again.