beaker 1.16.0 → 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +8 -8
  2. data/CONTRIBUTING.md +90 -0
  3. data/HISTORY.md +654 -2
  4. data/beaker.gemspec +1 -0
  5. data/lib/beaker/answers/version34.rb +4 -0
  6. data/lib/beaker/cli.rb +49 -2
  7. data/lib/beaker/dsl/helpers.rb +356 -196
  8. data/lib/beaker/dsl/install_utils.rb +135 -16
  9. data/lib/beaker/dsl/patterns.rb +37 -0
  10. data/lib/beaker/dsl/roles.rb +29 -0
  11. data/lib/beaker/dsl.rb +2 -1
  12. data/lib/beaker/host/unix.rb +14 -10
  13. data/lib/beaker/host/windows.rb +2 -0
  14. data/lib/beaker/host.rb +96 -1
  15. data/lib/beaker/host_prebuilt_steps.rb +41 -51
  16. data/lib/beaker/hypervisor/aws_sdk.rb +80 -16
  17. data/lib/beaker/hypervisor/ec2_helper.rb +1 -1
  18. data/lib/beaker/logger.rb +17 -0
  19. data/lib/beaker/options/command_line_parser.rb +3 -0
  20. data/lib/beaker/options/hosts_file_parser.rb +7 -4
  21. data/lib/beaker/options/options_hash.rb +2 -2
  22. data/lib/beaker/options/parser.rb +1 -1
  23. data/lib/beaker/options/presets.rb +128 -83
  24. data/lib/beaker/perf.rb +58 -0
  25. data/lib/beaker/shared/host_manager.rb +81 -0
  26. data/lib/beaker/shared.rb +2 -2
  27. data/lib/beaker/ssh_connection.rb +14 -7
  28. data/lib/beaker/test_case.rb +13 -0
  29. data/lib/beaker/test_suite.rb +23 -5
  30. data/lib/beaker/version.rb +1 -1
  31. data/lib/beaker.rb +1 -1
  32. data/spec/beaker/answers_spec.rb +13 -8
  33. data/spec/beaker/dsl/ezbake_utils_spec.rb +8 -9
  34. data/spec/beaker/dsl/helpers_spec.rb +299 -51
  35. data/spec/beaker/dsl/install_utils_spec.rb +75 -10
  36. data/spec/beaker/dsl/roles_spec.rb +36 -1
  37. data/spec/beaker/host_prebuilt_steps_spec.rb +21 -5
  38. data/spec/beaker/host_spec.rb +187 -23
  39. data/spec/beaker/hypervisor/ec2_helper_spec.rb +4 -4
  40. data/spec/beaker/hypervisor/vagrant_spec.rb +1 -1
  41. data/spec/beaker/options/hosts_file_parser_spec.rb +5 -0
  42. data/spec/beaker/options/options_hash_spec.rb +2 -2
  43. data/spec/beaker/options/parser_spec.rb +6 -0
  44. data/spec/beaker/options/presets_spec.rb +18 -2
  45. data/spec/beaker/perf_spec.rb +87 -0
  46. data/spec/beaker/shared/{host_role_parser_spec.rb → host_manager_spec.rb} +36 -5
  47. data/spec/beaker/test_suite_spec.rb +4 -3
  48. data/spec/matchers.rb +31 -3
  49. data/spec/mocks.rb +31 -25
  50. metadata +24 -5
  51. data/lib/beaker/shared/host_role_parser.rb +0 -36
@@ -172,6 +172,17 @@ module Beaker
172
172
  end
173
173
  end
174
174
 
175
+ #Create the Higgs install command string based upon the host and options settings. Installation command will be run as a
176
+ #background process. The output of the command will be stored in the provided host['higgs_file'].
177
+ # @param [Host] host The host that Higgs is to be installed on
178
+ # The host object must have the 'working_dir', 'dist' and 'pe_installer' field set correctly.
179
+ # @api private
180
+ def higgs_installer_cmd host
181
+
182
+ "cd #{host['working_dir']}/#{host['dist']} ; nohup ./#{host['pe_installer']} <<<Y > #{host['higgs_file']} 2>&1 &"
183
+
184
+ end
185
+
175
186
  #Determine is a given URL is accessible
176
187
  #@param [String] link The URL to examine
177
188
  #@return [Boolean] true if the URL has a '200' HTTP response code, false otherwise
@@ -420,8 +431,7 @@ module Beaker
420
431
  version = host['pe_ver'] || opts[:pe_ver]
421
432
  host['dist'] = "puppet-enterprise-#{version}-#{host['platform']}"
422
433
  end
423
- host['working_dir'] = "/tmp/" + Time.new.strftime("%Y-%m-%d_%H.%M.%S") #unique working dirs make me happy
424
- on host, "mkdir #{host['working_dir']}"
434
+ host['working_dir'] = host.tmpdir(Time.new.strftime("%Y-%m-%d_%H.%M.%S"))
425
435
  end
426
436
 
427
437
  fetch_puppet(hosts, opts)
@@ -493,6 +503,57 @@ module Beaker
493
503
  on install_hosts, puppet_agent('-t'), :acceptable_exit_codes => [0,2]
494
504
  end
495
505
 
506
+ #Perform a Puppet Enterprise Higgs install up until web browser interaction is required, runs on linux hosts only.
507
+ # @param [Host] host The host to install higgs on
508
+ # @param [Hash{Symbol=>Symbol, String}] opts The options
509
+ # @option opts [String] :pe_dir Default directory or URL to pull PE package from
510
+ # (Otherwise uses individual hosts pe_dir)
511
+ # @option opts [String] :pe_ver Default PE version to install
512
+ # (Otherwise uses individual hosts pe_ver)
513
+ # @raise [StandardError] When installation times out
514
+ #
515
+ # @example
516
+ # do_higgs_install(master, {:pe_dir => path, :pe_ver => version})
517
+ #
518
+ # @api private
519
+ #
520
+ def do_higgs_install host, opts
521
+ use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
522
+ platform = use_all_tar ? 'all' : host['platform']
523
+ version = host['pe_ver'] || opts[:pe_ver]
524
+ host['dist'] = "puppet-enterprise-#{version}-#{platform}"
525
+
526
+ use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
527
+ host['pe_installer'] ||= 'puppet-enterprise-installer'
528
+ host['working_dir'] = host.tmpdir(Time.new.strftime("%Y-%m-%d_%H.%M.%S"))
529
+
530
+ fetch_puppet([host], opts)
531
+
532
+ host['higgs_file'] = "higgs_#{File.basename(host['working_dir'])}.log"
533
+ on host, higgs_installer_cmd(host), opts
534
+
535
+ #wait for output to host['higgs_file']
536
+ #we're all done when we find this line in the PE installation log
537
+ higgs_re = /Please\s+go\s+to\s+https:\/\/.*\s+in\s+your\s+browser\s+to\s+continue\s+installation/m
538
+ res = Result.new(host, 'tmp cmd')
539
+ tries = 10
540
+ attempts = 0
541
+ prev_sleep = 0
542
+ cur_sleep = 1
543
+ while (res.stdout !~ higgs_re) and (attempts < tries)
544
+ res = on host, "cd #{host['working_dir']}/#{host['dist']} && cat #{host['higgs_file']}", :acceptable_exit_codes => (0..255)
545
+ attempts += 1
546
+ sleep( cur_sleep )
547
+ prev_sleep = cur_sleep
548
+ cur_sleep = cur_sleep + prev_sleep
549
+ end
550
+
551
+ if attempts >= tries
552
+ raise "Failed to kick off PE (Higgs) web installation"
553
+ end
554
+
555
+ end
556
+
496
557
  #Sort array of hosts so that it has the correct order for PE installation based upon each host's role
497
558
  # @example
498
559
  # h = sorted_hosts
@@ -551,6 +612,10 @@ module Beaker
551
612
  raise "install_puppet() called for unsupported platform '#{host['platform']}' on '#{host.name}'"
552
613
  end
553
614
  end
615
+
616
+ # Certain install paths may not create the config dirs/files needed
617
+ on host, "mkdir -p #{host['puppetpath']}"
618
+ on host, "echo '' >> #{host['hieraconf']}"
554
619
  end
555
620
  nil
556
621
  end
@@ -688,20 +753,51 @@ module Beaker
688
753
  # @raise [StandardError] if gem does not exist on target host
689
754
  # @api private
690
755
  def install_puppet_from_gem( host, opts )
691
- if host.check_for_command( 'gem' )
692
- if opts[:facter_version]
693
- on host, "gem install facter -v#{opts[:facter_version]}"
694
- end
695
- if opts[:hiera_version]
696
- on host, "gem install hiera -v#{opts[:hiera_version]}"
697
- end
698
- ver_cmd = opts[:version] ? "-v#{opts[:version]}" : ''
699
- on host, "gem install puppet #{ver_cmd}"
700
- else
701
- raise "install_puppet() called with default_action 'gem_install' but program `gem' not installed on #{host.name}"
756
+ # Hosts may be provisioned with csw but pkgutil won't be in the
757
+ # PATH by default to avoid changing the behavior for Puppet's tests
758
+ if host['platform'] =~ /solaris-10/
759
+ on host, 'ln -s /opt/csw/bin/pkgutil /usr/bin/pkgutil'
760
+ end
761
+
762
+ # Solaris doesn't necessarily have this, but gem needs it
763
+ if host['platform'] =~ /solaris/
764
+ on host, 'mkdir -p /var/lib'
765
+ end
766
+
767
+ unless host.check_for_command( 'gem' )
768
+ gempkg = case host['platform']
769
+ when /solaris-11/ then 'ruby-18'
770
+ when /ubuntu-14/ then 'ruby'
771
+ when /solaris-10|ubuntu|debian|el-/ then 'rubygems'
772
+ else
773
+ raise "install_puppet() called with default_action " +
774
+ "'gem_install' but program `gem' is " +
775
+ "not installed on #{host.name}"
776
+ end
777
+
778
+ host.install_package gempkg
779
+ end
780
+
781
+ if host['platform'] =~ /debian|ubuntu|solaris/
782
+ gem_env = YAML.load( on( host, 'gem environment' ).stdout )
783
+ gem_paths_array = gem_env['RubyGems Environment'].find {|h| h['GEM PATHS'] != nil }['GEM PATHS']
784
+ path_with_gem = 'export PATH=' + gem_paths_array.join(':') + ':${PATH}'
785
+ on host, "echo '#{path_with_gem}' >> ~/.bashrc"
786
+ end
787
+
788
+ if opts[:facter_version]
789
+ on host, "gem install facter -v#{opts[:facter_version]} --no-ri --no-rdoc"
790
+ end
791
+
792
+ if opts[:hiera_version]
793
+ on host, "gem install hiera -v#{opts[:hiera_version]} --no-ri --no-rdoc"
702
794
  end
795
+
796
+ ver_cmd = opts[:version] ? "-v#{opts[:version]}" : ''
797
+ on host, "gem install puppet #{ver_cmd} --no-ri --no-rdoc"
703
798
  end
704
799
 
800
+
705
801
  #Install PE based upon host configuration and options
706
802
  # @example
707
803
  # install_pe
@@ -777,7 +873,7 @@ module Beaker
777
873
  on host, "rpm -ivh --force #{rpm}"
778
874
 
779
875
  when /^(debian|ubuntu)$/
780
- deb = options[:release_apt_repo_url] + "puppetlabs-release-%s.deb" % codename
876
+ deb = URI.join(options[:release_apt_repo_url], "puppetlabs-release-%s.deb" % codename)
781
877
 
782
878
  on host, "wget -O /tmp/puppet.deb #{deb}"
783
879
  on host, "dpkg -i --force-all /tmp/puppet.deb"
@@ -888,8 +984,8 @@ module Beaker
888
984
  scp_to host, list, config_dir
889
985
  scp_to host, repo_dir, "/root/#{package_name}"
890
986
 
891
- search = "'deb\\s\\+http:\\/\\/#{hostname}.*$"
892
- replace = "'deb file:\\/\\/\\/root\\/#{package_name}\\/#{codename} #{codename} main'"
987
+ search = "deb\\s\\+http:\\/\\/#{hostname}.*$"
988
+ replace = "deb file:\\/\\/\\/root\\/#{package_name}\\/#{codename} #{codename} main"
893
989
  sed_command = "sed -i 's/#{search}/#{replace}/'"
894
990
  find_and_sed = "find #{config_dir} -name \"*.list\" -exec #{sed_command} {} \\;"
895
991
 
@@ -900,6 +996,29 @@ module Beaker
900
996
  raise "No repository installation step for #{variant} yet..."
901
997
  end
902
998
  end
999
+
1000
+ #Install Higgs up till the point where you need to continue installation in a web browser, defaults to execution
1001
+ #on the master node.
1002
+ #@param [Host] higgs_host The host to install Higgs on (supported on linux platform only)
1003
+ # @example
1004
+ # install_higgs
1005
+ #
1006
+ # @note Either pe_ver and pe_dir should be set in the ENV or each host should have pe_ver and pe_dir set individually.
1007
+ # Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz).
1008
+ #
1009
+ # @api dsl
1010
+ def install_higgs( higgs_host = master )
1011
+ #process the version files if necessary
1012
+ master['pe_dir'] ||= options[:pe_dir]
1013
+ master['pe_ver'] = master['pe_ver'] || options['pe_ver'] ||
1014
+ Beaker::Options::PEVersionScraper.load_pe_version(master[:pe_dir] || options[:pe_dir], options[:pe_version_file])
1015
+ if higgs_host['platform'] =~ /osx|windows/
1016
+ raise "Attempting higgs installation on host #{higgs_host.name} with unsupported platform #{higgs_host['platform']}"
1017
+ end
1018
+ #send in the global options hash
1019
+ do_higgs_install higgs_host, options
1020
+ end
1021
+
903
1022
  end
904
1023
  end
905
1024
  end
@@ -0,0 +1,37 @@
1
+ module Beaker
2
+ module DSL
3
+ # These are simple patterns that appear frequently in beaker test
4
+ # code, and are provided to simplify test construction.
5
+ #
6
+ #
7
+ # It requires the class it is mixed into to provide the attribute
8
+ # `hosts` which contain the hosts to search, these should implement
9
+ # {Beaker::Host}'s interface. They, at least, must have #[]
10
+ # and #to_s available and provide an array when #[]('roles') is called.
11
+ #
12
+ module Patterns
13
+
14
+ #Execute a block selecting the hosts that match with the provided criteria
15
+ #@param [Array<Host>, Host, String, Symbol] hosts_or_filter A host role as a String or Symbol that can be
16
+ # used to search for a set of Hosts, a host name
17
+ # as a String that can be used to search for
18
+ # a set of Hosts, or a {Host}
19
+ # or Array<{Host}> to run the block against
20
+ #@param [Block] block This method will yield to a block of code passed by the caller
21
+ def block_on hosts_or_filter, &block
22
+ block_hosts = nil
23
+ if defined? hosts
24
+ block_hosts = hosts
25
+ end
26
+ filter = nil
27
+ if hosts_or_filter.is_a? String or hosts_or_filter.is_a? Symbol
28
+ filter = hosts_or_filter
29
+ else
30
+ block_hosts = hosts_or_filter
31
+ end
32
+ run_block_on block_hosts, filter, &block
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -88,6 +88,35 @@ module Beaker
88
88
  find_only_one :default
89
89
  end
90
90
 
91
+ #Create a new role method for a given arbitrary role name. Makes it possible to be able to run
92
+ #commands without having to refer to role by String or Symbol. Will not add a new method
93
+ #definition if the name is already in use.
94
+ # @param [String, Symbol, Array[String,Symbol]] role The role that you wish to create a definition for, either a String
95
+ # Symbol or an Array of Strings or Symbols.
96
+ # @example Basic usage
97
+ # add_role_def('myrole')
98
+ # on myrole, "run command"
99
+ def add_role_def role
100
+ if role.kind_of?(Array)
101
+ role.each do |r|
102
+ add_role_def r
103
+ end
104
+ else
105
+ if not respond_to? role
106
+ if role !~ /\A[[:alpha:]]+[a-zA-Z0-9_]*[!?=]?\Z/
107
+ raise "Role name format error for '#{role}'. Allowed characters are: \na-Z\n0-9 (as long as not at the beginning of name)\n'_'\n'?', '!' and '=' (only as individual last character at end of name)"
108
+ end
109
+ self.class.send :define_method, role.to_s do
110
+ hosts_with_role = hosts_as role.to_sym
111
+ if hosts_with_role.length == 1
112
+ hosts_with_role = hosts_with_role.pop
113
+ end
114
+ hosts_with_role
115
+ end
116
+ end
117
+ end
118
+ end
119
+
91
120
  # Determine if there is a host or hosts with the given role defined
92
121
  # @return [Boolean] True if there is a host with role, false otherwise
93
122
  #
data/lib/beaker/dsl.rb CHANGED
@@ -1,4 +1,4 @@
1
- [ 'install_utils', 'roles', 'outcomes', 'assertions',
1
+ [ 'install_utils', 'roles', 'outcomes', 'assertions', 'patterns',
2
2
  'structure', 'helpers', 'ezbake_utils', 'wrappers' ].each do |lib|
3
3
  require "beaker/dsl/#{lib}"
4
4
  end
@@ -78,5 +78,6 @@ module Beaker
78
78
  include Beaker::DSL::Helpers
79
79
  include Beaker::DSL::EZBakeUtils
80
80
  include Beaker::DSL::InstallUtils
81
+ include Beaker::DSL::Patterns
81
82
  end
82
83
  end
@@ -20,16 +20,18 @@ module Unix
20
20
  'user' => 'root',
21
21
  'group' => 'pe-puppet',
22
22
  'master-start-curl-retries' => 120,
23
- 'puppetservice' => 'pe-httpd',
24
- 'puppetpath' => '/etc/puppetlabs/puppet',
25
- 'puppetbin' => '/opt/puppet/bin/puppet',
26
- 'puppetbindir' => '/opt/puppet/bin',
27
- 'puppetvardir' => '/var/opt/lib/pe-puppet',
28
- 'hieradatadir' => '/var/lib/hiera',
29
- 'hieraconf' => '/etc/puppetlabs/puppet/hiera.yaml',
30
- 'distmoduledir' => '/etc/puppetlabs/puppet/modules',
31
- 'sitemoduledir' => '/opt/puppet/share/puppet/modules',
32
- 'pathseparator' => ':',
23
+ 'jvm-puppet-confdir' => '/etc/puppetlabs/jvm-puppet/conf.d',
24
+ 'puppetservice' => 'pe-httpd',
25
+ 'puppetpath' => '/etc/puppetlabs/puppet',
26
+ 'puppetbin' => '/opt/puppet/bin/puppet',
27
+ 'puppetbindir' => '/opt/puppet/bin',
28
+ 'puppetsbindir' => '/opt/puppet/sbin',
29
+ 'puppetvardir' => '/var/opt/lib/pe-puppet',
30
+ 'hieradatadir' => '/var/lib/hiera',
31
+ 'hieraconf' => '/etc/puppetlabs/puppet/hiera.yaml',
32
+ 'distmoduledir' => '/etc/puppetlabs/puppet/modules',
33
+ 'sitemoduledir' => '/opt/puppet/share/puppet/modules',
34
+ 'pathseparator' => ':',
33
35
  })
34
36
  end
35
37
 
@@ -39,6 +41,8 @@ module Unix
39
41
  'user' => 'root',
40
42
  'group' => 'puppet',
41
43
  'master-start-curl-retries' => 120,
44
+ 'jvm-puppet-confdir' => '/etc/jvm-puppet/conf.d',
45
+ 'puppetservice' => 'puppetmaster',
42
46
  'puppetpath' => '/etc/puppet',
43
47
  'puppetvardir' => '/var/lib/puppet',
44
48
  'puppetbin' => '/usr/bin/puppet',
@@ -21,6 +21,7 @@ module Windows
21
21
  'group' => 'Administrators',
22
22
  'puppetservice' => 'pe-httpd',
23
23
  'puppetpath' => '`cygpath -smF 35`/PuppetLabs/puppet/etc',
24
+ 'hieraconf' => '`cygpath -smF 35`/Puppetlabs/puppet/etc/hiera.yaml',
24
25
  'puppetvardir' => '`cygpath -smF 35`/PuppetLabs/puppet/var',
25
26
  'distmoduledir' => '`cygpath -smF 35`/PuppetLabs/puppet/etc/modules',
26
27
  'sitemoduledir' => 'C:/usr/share/puppet/modules',
@@ -36,6 +37,7 @@ module Windows
36
37
  'user' => 'Administrator',
37
38
  'group' => 'Administrators',
38
39
  'puppetpath' => '`cygpath -smF 35`/PuppetLabs/puppet/etc',
40
+ 'hieraconf' => '`cygpath -smF 35`/Puppetlabs/puppet/etc/hiera.yaml',
39
41
  'puppetvardir' => '`cygpath -smF 35`/PuppetLabs/puppet/var',
40
42
  'distmoduledir' => '`cygpath -smF 35`/PuppetLabs/puppet/etc/modules',
41
43
  'sitemoduledir' => 'C:/usr/share/puppet/modules',
data/lib/beaker/host.rb CHANGED
@@ -137,6 +137,43 @@ module Beaker
137
137
  @options.is_pe?
138
138
  end
139
139
 
140
+ # True if this is a pe run, or if the host has had a 'use-service' property set.
141
+ def use_service_scripts?
142
+ is_pe? || self['use-service']
143
+ end
144
+
145
+ # Mirrors the true/false value of the host's 'graceful-restarts' property,
146
+ # or falls back to the value of +is_using_passenger?+ if
147
+ # 'graceful-restarts' is nil, but only if this is not a PE run (foss only).
148
+ def graceful_restarts?
149
+ graceful =
150
+ if !self['graceful-restarts'].nil?
151
+ self['graceful-restarts']
152
+ else
153
+ !is_pe? && is_using_passenger?
154
+ end
155
+ graceful
156
+ end
157
+
158
+ # Modifies the host settings to indicate that it will be using passenger service scripts,
159
+ # (apache2) by default. Does nothing if this is a PE host, since it is already using
160
+ # passenger.
161
+ # @param [String] puppetservice Name of the service script that should be
162
+ # called to stop/startPuppet on this host. Defaults to 'apache2'.
163
+ def uses_passenger!(puppetservice = 'apache2')
164
+ if !is_pe?
165
+ self['passenger'] = true
166
+ self['puppetservice'] = puppetservice
167
+ self['use-service'] = true
168
+ end
169
+ return true
170
+ end
171
+
172
+ # True if this is a PE run, or if the host's 'passenger' property has been set.
173
+ def is_using_passenger?
174
+ is_pe? || self['passenger']
175
+ end
176
+
140
177
  def log_prefix
141
178
  if @defaults['vmhostname']
142
179
  "#{self} (#{@name})"
@@ -206,9 +243,66 @@ module Beaker
206
243
  end
207
244
  end
208
245
 
246
+ # Create the provided directory structure on the host
247
+ # @param [String] dir The directory structure to create on the host
248
+ # @return [Boolean] True, if directory construction succeeded, otherwise False
249
+ def mkdir_p dir
250
+ result = exec(Beaker::Command.new("mkdir -p #{dir}"), :acceptable_exit_codes => [0, 1])
251
+ result.exit_code == 0
252
+ end
253
+
254
+ # scp files from the localhost to this test host
255
+ # @param source [String] The path to the file/dir to upload
256
+ # @param target [String] The destination path on the host
257
+ # @param [Hash{Symbol=>String}] options Options to alter execution
258
+ # @option options [Boolean] :recursive Should we copy recursively? Defaults to 'True' in case of a directory source.
259
+ # @option options [Array<String>] :ignore An array of file/dir paths that will not be copied to the host
209
260
  def do_scp_to source, target, options
210
261
  @logger.debug "localhost $ scp #{source} #{@name}:#{target}"
211
- result = connection.scp_to(source, target, options, $dry_run)
262
+
263
+ result = Result.new(@name, [source, target])
264
+ has_ignore = options[:ignore] and not options[:ignore].empty?
265
+ # construct the regex for matching ignored files/dirs
266
+ ignore_re = nil
267
+ if has_ignore
268
+ ignore_arr = Array(options[:ignore]).map do |entry|
269
+ "((\/|\\A)#{entry}(\/|\\z))".sub(/\./, "\.")
270
+ end
271
+ ignore_re = Regexp.new(ignore_arr.join('|'))
272
+ end
273
+
274
+ # either a single file, or a directory with no ignores
275
+ if File.file?(source) or (File.directory?(source) and not has_ignore)
276
+ source_file = source
277
+ if has_ignore and (source =~ ignore_re)
278
+ @logger.debug "After rejecting ignored files/dirs, there is no file to copy"
279
+ source_file = nil
280
+ result.stdout = "No files to copy"
281
+ result.exit_code = 1
282
+ end
283
+ if source_file
284
+ result = connection.scp_to(source_file, target, options, $dry_run)
285
+ end
286
+ else # a directory with ignores
287
+ dir_source = Dir.glob("#{source}/**/*").reject do |f|
288
+ f =~ ignore_re
289
+ end
290
+ @logger.debug "After rejecting ignored files/dirs, going to scp [#{dir_source.join(", ")}]"
291
+
292
+ # create necessary directory structure on host
293
+ required_dirs = (dir_source.map{ | dir | File.dirname(dir) }).uniq
294
+ required_dirs.each do |dir|
295
+ mkdir_p( File.join(target, dir) )
296
+ end
297
+
298
+ # copy each file to the host
299
+ dir_source.each do |s|
300
+ file_path = File.join(target, s)
301
+ result = connection.scp_to(s, file_path, options, $dry_run)
302
+ end
303
+ end
304
+
305
+ @logger.debug result.stdout
212
306
  return result
213
307
  end
214
308
 
@@ -216,6 +310,7 @@ module Beaker
216
310
 
217
311
  @logger.debug "localhost $ scp #{@name}:#{source} #{target}"
218
312
  result = connection.scp_from(source, target, options, $dry_run)
313
+ @logger.debug result.stdout
219
314
  return result
220
315
  end
221
316