beaker 2.18.0 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +699 -2
  3. data/acceptance/lib/beaker/acceptance/install_utils.rb +58 -0
  4. data/acceptance/pre_suite/puppet_gem/install.rb +1 -8
  5. data/acceptance/pre_suite/puppet_git/install.rb +6 -65
  6. data/acceptance/pre_suite/puppet_pkg/install.rb +1 -1
  7. data/acceptance/tests/foss_utils/clone_git_repo_on.rb +49 -0
  8. data/beaker.gemspec +2 -0
  9. data/lib/beaker/command.rb +1 -1
  10. data/lib/beaker/dsl/helpers/puppet_helpers.rb +8 -6
  11. data/lib/beaker/dsl/helpers/web_helpers.rb +2 -1
  12. data/lib/beaker/dsl/install_utils/aio_defaults.rb +0 -3
  13. data/lib/beaker/dsl/install_utils/foss_defaults.rb +19 -0
  14. data/lib/beaker/dsl/install_utils/foss_utils.rb +164 -67
  15. data/lib/beaker/dsl/install_utils/pe_defaults.rb +9 -11
  16. data/lib/beaker/dsl/install_utils/pe_utils.rb +48 -64
  17. data/lib/beaker/dsl/install_utils/puppet_utils.rb +43 -0
  18. data/lib/beaker/dsl/install_utils/windows_utils.rb +144 -0
  19. data/lib/beaker/dsl/roles.rb +20 -3
  20. data/lib/beaker/dsl/structure.rb +14 -3
  21. data/lib/beaker/host/freebsd/pkg.rb +18 -0
  22. data/lib/beaker/host/freebsd.rb +2 -0
  23. data/lib/beaker/host/unix/exec.rb +3 -3
  24. data/lib/beaker/host/unix/pkg.rb +37 -0
  25. data/lib/beaker/host/windows/exec.rb +3 -0
  26. data/lib/beaker/host.rb +38 -9
  27. data/lib/beaker/host_prebuilt_steps.rb +21 -11
  28. data/lib/beaker/hypervisor/aws_sdk.rb +22 -18
  29. data/lib/beaker/hypervisor/docker.rb +7 -0
  30. data/lib/beaker/hypervisor/openstack.rb +1 -0
  31. data/lib/beaker/hypervisor/vagrant_virtualbox.rb +10 -5
  32. data/lib/beaker/hypervisor/vmpooler.rb +4 -0
  33. data/lib/beaker/logger.rb +12 -1
  34. data/lib/beaker/options/command_line_parser.rb +9 -0
  35. data/lib/beaker/options/options_hash.rb +3 -296
  36. data/lib/beaker/options/parser.rb +12 -0
  37. data/lib/beaker/options/presets.rb +0 -1
  38. data/lib/beaker/platform.rb +3 -1
  39. data/lib/beaker/ssh_connection.rb +48 -23
  40. data/lib/beaker/test_case.rb +1 -1
  41. data/lib/beaker/version.rb +1 -1
  42. data/spec/beaker/dsl/helpers/puppet_helpers_spec.rb +0 -1
  43. data/spec/beaker/dsl/helpers/web_helpers_spec.rb +10 -1
  44. data/spec/beaker/dsl/install_utils/foss_utils_spec.rb +247 -49
  45. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +116 -26
  46. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +57 -0
  47. data/spec/beaker/dsl/install_utils/windows_utils_spec.rb +132 -0
  48. data/spec/beaker/dsl/roles_spec.rb +36 -5
  49. data/spec/beaker/dsl/structure_spec.rb +9 -2
  50. data/spec/beaker/host/unix/pkg_spec.rb +26 -6
  51. data/spec/beaker/host_prebuilt_steps_spec.rb +3 -2
  52. data/spec/beaker/host_spec.rb +24 -6
  53. data/spec/beaker/hypervisor/aixer_spec.rb +1 -1
  54. data/spec/beaker/hypervisor/aws_sdk_spec.rb +595 -58
  55. data/spec/beaker/hypervisor/docker_spec.rb +2 -1
  56. data/spec/beaker/hypervisor/solaris_spec.rb +1 -0
  57. data/spec/beaker/hypervisor/vagrant_spec.rb +20 -5
  58. data/spec/beaker/hypervisor/vagrant_virtualbox_spec.rb +1 -1
  59. data/spec/beaker/logger_spec.rb +39 -0
  60. data/spec/beaker/options/command_line_parser_spec.rb +2 -2
  61. data/spec/beaker/options/options_hash_spec.rb +1 -102
  62. data/spec/beaker/options/parser_spec.rb +19 -0
  63. data/spec/beaker/options/pe_version_scaper_spec.rb +11 -1
  64. data/spec/beaker/options/presets_spec.rb +8 -0
  65. data/spec/beaker/ssh_connection_spec.rb +39 -21
  66. data/spec/helpers.rb +9 -3
  67. data/spec/mocks.rb +2 -0
  68. metadata +35 -11
  69. data/lib/beaker/answers/version20.rb +0 -120
  70. data/lib/beaker/answers/version28.rb +0 -121
  71. data/lib/beaker/answers/version30.rb +0 -227
  72. data/lib/beaker/answers/version32.rb +0 -44
  73. data/lib/beaker/answers/version34.rb +0 -51
  74. data/lib/beaker/answers/version38.rb +0 -29
  75. data/lib/beaker/answers/version40.rb +0 -44
  76. data/lib/beaker/answers.rb +0 -143
  77. data/spec/beaker/answers_spec.rb +0 -547
@@ -1,44 +1,11 @@
1
+ require 'stringify-hash'
2
+
1
3
  module Beaker
2
4
  module Options
3
5
 
4
6
  # A hash that treats Symbol and String keys interchangeably
5
7
  # and recursively merges hashes
6
- class OptionsHash < Hash
7
-
8
- # The dividor between elements when OptionsHash is dumped
9
- DIV = ' '
10
-
11
- # The end of line when dumping
12
- EOL = "\n"
13
-
14
- # Get value for given key, search for both k as String and k as Symbol,
15
- # if not present return nil
16
- #
17
- # @param [Object] k The key to find, searches for both k as String
18
- # and k as Symbol
19
- #
20
- # @example Use this method to return the value for a given key
21
- # a['key'] = 'value'
22
- # a['key'] == a[:key] == 'value'
23
- #
24
- # @return [nil, Object] Return the Object found at given key,
25
- # or nil if no Object found
26
- def [] k
27
- super(k.to_s) || super(k.to_sym)
28
- end
29
-
30
- # Set Symbol key to Object value
31
- # @param [Object] k The key to associated with the value,
32
- # converted to Symbol key
33
- # @param [Object] v The value to store in the ObjectHash
34
- #
35
- # @example Use this method to set the value for a key
36
- # a['key'] = 'value'
37
- #
38
- # @return [Object] Return the Object value just stored
39
- def []=k,v
40
- super(k.to_sym, v)
41
- end
8
+ class OptionsHash < StringifyHash
42
9
 
43
10
  # Determine if type of ObjectHash is pe, defaults to true
44
11
  #
@@ -66,271 +33,11 @@ module Beaker
66
33
  :pe
67
34
  when /foss/
68
35
  :foss
69
- when /aio/
70
- :aio
71
36
  else
72
37
  :foss
73
38
  end
74
39
  end
75
40
 
76
- # Determine if key is stored in ObjectHash
77
- # @param [Object] k The key to find in ObjectHash, searches for
78
- # both k as String and k as Symbol
79
- #
80
- # @example Use this method to set the value for a key
81
- # a['key'] = 'value'
82
- # a.has_key[:key] == true
83
- #
84
- # @return [Boolean]
85
- def has_key? k
86
- super(k.to_s) || super(k.to_sym)
87
- end
88
-
89
- # Determine key=>value entry in OptionsHash, remove both value at
90
- # String key and value at Symbol key
91
- #
92
- # @param [Object] k The key to delete in ObjectHash,
93
- # deletes both k as String and k as Symbol
94
- #
95
- # @example Use this method to set the value for a key
96
- # a['key'] = 'value'
97
- # a.delete[:key] == 'value'
98
- #
99
- # @return [Object, nil] The Object deleted at value,
100
- # nil if no Object deleted
101
- def delete k
102
- super(k.to_s) || super(k.to_sym)
103
- end
104
-
105
- # Recursively merge and OptionsHash with an OptionsHash or Hash
106
- #
107
- # @param [OptionsHash] base The hash to merge into
108
- # @param [OptionsHash, Hash] hash The hash to merge from
109
- #
110
- # @example
111
- # base = { :key => { :subkey1 => 'subval', :subkey2 => 'subval' } }
112
- # hash = { :key => { :subkey1 => 'newval'} }
113
- #
114
- # rmerge(base, hash)
115
- # #=> {:key =>
116
- # {:subkey1 => 'newval',
117
- # :subkey2 => 'subval'}}
118
- #
119
- # @return [OptionsHash] The combined bash and hash
120
- def rmerge base, hash
121
- return base unless hash.is_a?(Hash) || hash.is_a?(OptionsHash)
122
- hash.each do |key, v|
123
- if (base[key].is_a?(Hash) || base[key].is_a?(OptionsHash)) && (hash[key].is_a?(Hash) || hash[key].is_a?(OptionsHash))
124
- rmerge(base[key], hash[key])
125
- elsif hash[key].is_a?(Hash)
126
- base[key] = OptionsHash.new.merge(hash[key])
127
- else
128
- base[key]= hash[key]
129
- end
130
- end
131
- base
132
- end
133
-
134
- # Create new OptionsHash from recursively merged self with an OptionsHash or Hash
135
- #
136
- # @param [OptionsHash, Hash] hash The hash to merge from
137
- #
138
- # @example
139
- # base = { :key => { :subkey1 => 'subval', :subkey2 => 'subval' } }
140
- # hash = { :key => { :subkey1 => 'newval'} }
141
- #
142
- # base.merge(hash)
143
- # #=> {:key =>
144
- # {:subkey1 => 'newval',
145
- # :subkey2 => 'subval' }
146
- #
147
- # @return [OptionsHash] The combined hash
148
- def merge hash
149
- #make a deep copy into an empty hash object
150
- merged_hash = rmerge(OptionsHash.new, self)
151
- rmerge(merged_hash, hash)
152
- end
153
-
154
- # Recursively merge self with an OptionsHash or Hash
155
- #
156
- # @param [OptionsHash, Hash] hash The hash to merge from
157
- #
158
- # @example
159
- # base = { :key => { :subkey1 => 'subval', :subkey2 => 'subval' } }
160
- # hash = { :key => { :subkey1 => 'newval'} }
161
- #
162
- # base.merge!(hash)
163
- # #=> {:key =>
164
- # {:subkey1 => 'newval',
165
- # :subkey2 => 'subval' }
166
- #
167
- # @return [OptionsHash] The combined hash
168
- def merge! hash
169
- rmerge(self, hash)
170
- end
171
-
172
- # Helper for formatting collections
173
- # Computes the indentation level for elements of the collection
174
- # Yields indentation to block to so the caller can create
175
- # map of element strings
176
- # Places delimiters in the correct location
177
- # Joins everything with correct EOL
178
- #
179
- #
180
- # !@visibility private
181
- def as_coll( opening, closing, in_lvl, in_inc, &block )
182
- delim_indent = in_inc * in_lvl
183
- elem_indent = in_inc * (in_lvl + 1)
184
-
185
- open_brace = opening
186
- close_brace = delim_indent + closing
187
-
188
- fmtd_coll = block.call( elem_indent )
189
- str_coll = fmtd_coll.join( ',' + EOL )
190
-
191
- return open_brace + EOL + str_coll + EOL + close_brace
192
- end
193
-
194
- # Pretty prints a collection
195
- #
196
- # @param [Enumerable] collection The collection to be printed
197
- # @param [Integer] in_lvl The level of indentation
198
- # @param [String] in_inc The increment to indent
199
- #
200
- # @example
201
- # base = {:key => { :subkey1 => 'subval', :subkey2 => ['subval'] }}
202
- # self.fmt_collection( base )
203
- # #=> '{
204
- # "key": {
205
- # "subkey": "subval",
206
- # "subkey2": [
207
- # "subval"
208
- # ]
209
- # }
210
- # }'
211
- #
212
- # @return [String] The collection as a pretty JSON object
213
- def fmt_collection( collection, in_lvl = 0, in_inc = DIV )
214
- if collection.respond_to? :each_pair
215
- string = fmt_assoc( collection, in_lvl, in_inc )
216
- else
217
- string = fmt_list( collection, in_lvl, in_inc )
218
- end
219
-
220
- return string
221
- end
222
-
223
- # Pretty prints an associative collection
224
- #
225
- # @param [#each_pair] coll The collection to be printed
226
- # @param [Integer] in_lvl The level of indentation
227
- # @param [String] in_inc The increment to indent
228
- #
229
- # @example
230
- # base = { :key => 'value', :key2 => 'value' }
231
- # self.fmt_assoc( base )
232
- # #=> '{
233
- # "key": "value",
234
- # "key2": "value"
235
- # }'
236
- #
237
- # @return [String] The collection as a pretty JSON object
238
- def fmt_assoc( coll, in_lvl = 0, in_inc = DIV )
239
- if coll.empty?
240
- return '{}'
241
- else
242
- as_coll '{', '}', in_lvl, in_inc do |elem_indent|
243
- coll.map do |key, value|
244
- assoc_line = elem_indent + '"' + key.to_s + '"' + ': '
245
- assoc_line += fmt_value( value, in_lvl, in_inc )
246
- end
247
- end
248
- end
249
- end
250
-
251
- # Pretty prints a list collection
252
- #
253
- # @param [#each] coll The collection to be printed
254
- # @param [Integer] in_lvl The level of indentation
255
- # @param [String] in_inc The increment to indent
256
- #
257
- # @example
258
- # base = [ 'first', 'second' ]
259
- # self.fmt_list( base )
260
- # #=> '[
261
- # "first",
262
- # "second"
263
- # ]'
264
- #
265
- # @return [String] The collection as a pretty JSON object
266
- def fmt_list( coll, in_lvl = 0, in_inc = DIV )
267
- if coll.empty?
268
- return '[]'
269
- else
270
- as_coll '[', ']', in_lvl, in_inc do |indent|
271
- coll.map do |el|
272
- indent + fmt_value( el, in_lvl, in_inc )
273
- end
274
- end
275
- end
276
- end
277
-
278
- # Chooses between collection and primitive formatting
279
- #
280
- # !@visibility private
281
- def fmt_value( value, in_lvl = 0, in_inc = DIV )
282
- if value.kind_of? Enumerable and not value.is_a? String
283
- fmt_collection( value, in_lvl + 1, in_inc )
284
- else
285
- fmt_basic( value )
286
- end
287
- end
288
-
289
- # Pretty prints primitive JSON values
290
- #
291
- # @param [Object] value The collection to be printed
292
- #
293
- # @example
294
- # self.fmt_value( 4 )
295
- # #=> '4'
296
- #
297
- # @example
298
- # self.fmt_value( true )
299
- # #=> 'true'
300
- #
301
- # @example
302
- # self.fmt_value( nil )
303
- # #=> 'null'
304
- #
305
- # @example
306
- # self.fmt_value( 'string' )
307
- # #=> '"string"'
308
- #
309
- # @return [String] The value as a valid JSON primitive
310
- def fmt_basic( value )
311
- case value
312
- when Numeric, TrueClass, FalseClass then value.to_s
313
- when NilClass then "null"
314
- else "\"#{value}\""
315
- end
316
- end
317
-
318
- # Pretty print the options as JSON
319
- #
320
- # @example
321
- # base = { :key => { :subkey1 => 'subval', :subkey2 => 'subval' } }
322
- # base.dump
323
- # #=> '{
324
- # "key": {
325
- # "subkey1": "subval",
326
- # "subkey2": 2
327
- # }
328
- # }
329
- #
330
- # @return [String] The description of self
331
- def dump
332
- fmt_collection( self, 0, DIV )
333
- end
334
41
  end
335
42
  end
336
43
  end
@@ -328,6 +328,7 @@ module Beaker
328
328
  end
329
329
 
330
330
  normalize_and_validate_tags()
331
+ resolve_symlinks()
331
332
 
332
333
  #set the default role
333
334
  set_default_host!(@options[:HOSTS])
@@ -365,6 +366,17 @@ module Beaker
365
366
  end
366
367
  end
367
368
 
369
+ # resolves all file symlinks that require it.
370
+ #
371
+ # @note doing it here allows us to not need duplicate logic, which we
372
+ # would need if we were doing it in the parser (--hosts & --config)
373
+ #
374
+ # @return nil
375
+ # @api public
376
+ def resolve_symlinks()
377
+ @options[:hosts_file] = File.realpath(@options[:hosts_file]) if @options[:hosts_file]
378
+ end
379
+
368
380
  private
369
381
 
370
382
  # @api private
@@ -178,7 +178,6 @@ module Beaker
178
178
  :ssh => {
179
179
  :config => false,
180
180
  :paranoid => false,
181
- :timeout => 300,
182
181
  :auth_methods => ["publickey"],
183
182
  :port => 22,
184
183
  :forward_agent => true,
@@ -3,7 +3,7 @@ module Beaker
3
3
  # all String methods while adding several platform-specific use cases.
4
4
  class Platform < String
5
5
  # Supported platforms
6
- PLATFORMS = /^(cisco|freebsd|osx|centos|fedora|debian|oracle|redhat|scientific|sles|ubuntu|windows|solaris|aix|el|eos|cumulus|f5)\-.+\-.+$/
6
+ PLATFORMS = /^(cisco|(free|open)bsd|osx|centos|fedora|debian|oracle|redhat|scientific|sles|ubuntu|windows|solaris|aix|el|eos|cumulus|f5)\-.+\-.+$/
7
7
 
8
8
  # Platform version numbers vs. codenames conversion hash
9
9
  PLATFORM_VERSION_CODES =
@@ -44,6 +44,8 @@ module Beaker
44
44
  # Creates the Platform object. Checks to ensure that the platform String
45
45
  # provided meets the platform formatting rules. Platforms name must be of
46
46
  # the format /^OSFAMILY-VERSION-ARCH.*$/ where OSFAMILY is one of:
47
+ # * freebsd
48
+ # * openbsd
47
49
  # * osx
48
50
  # * centos
49
51
  # * fedora
@@ -6,6 +6,7 @@ module Beaker
6
6
  class SshConnection
7
7
 
8
8
  attr_accessor :logger
9
+ attr_accessor :ip, :vmhostname, :hostname
9
10
 
10
11
  RETRYABLE_EXCEPTIONS = [
11
12
  SocketError,
@@ -21,41 +22,65 @@ module Beaker
21
22
  IOError,
22
23
  ]
23
24
 
24
- def initialize hostname, user = nil, ssh_opts = {}, options = {}
25
- @hostname = hostname
25
+ def initialize name_hash, user = nil, ssh_opts = {}, options = {}
26
+ @vmhostname = name_hash[:vmhostname]
27
+ @ip = name_hash[:ip]
28
+ @hostname = name_hash[:hostname]
26
29
  @user = user
27
30
  @ssh_opts = ssh_opts
28
31
  @logger = options[:logger]
29
32
  @options = options
30
33
  end
31
34
 
32
- def self.connect hostname, user = 'root', ssh_opts = {}, options = {}
33
- connection = new hostname, user, ssh_opts, options
35
+ def self.connect name_hash, user = 'root', ssh_opts = {}, options = {}
36
+ connection = new name_hash, user, ssh_opts, options
34
37
  connection.connect
35
38
  connection
36
39
  end
37
40
 
41
+ def connect_block host, user, ssh_opts
42
+ try = 1
43
+ last_wait = 2
44
+ wait = 3
45
+ begin
46
+ @logger.debug "Attempting ssh connection to #{host}, user: #{user}, opts: #{ssh_opts}"
47
+ Net::SSH.start(host, user, ssh_opts)
48
+ rescue *RETRYABLE_EXCEPTIONS => e
49
+ if try <= 8
50
+ @logger.warn "Try #{try} -- Host #{host} unreachable: #{e.class.name} - #{e.message}"
51
+ @logger.warn "Trying again in #{wait} seconds"
52
+ sleep wait
53
+ (last_wait, wait) = wait, last_wait + wait
54
+ try += 1
55
+ retry
56
+ else
57
+ @logger.warn "Failed to connect to #{host}, after #{try} attempts"
58
+ nil
59
+ end
60
+ end
61
+ end
62
+
38
63
  # connect to the host
39
64
  def connect
40
- try = 1
41
- last_wait = 0
42
- wait = 1
43
- @ssh ||= begin
44
- @logger.debug "Attempting ssh connection to #{@hostname}, user: #{@user}, opts: #{@ssh_opts}"
45
- Net::SSH.start(@hostname, @user, @ssh_opts)
46
- rescue *RETRYABLE_EXCEPTIONS => e
47
- if try <= 11
48
- @logger.warn "Try #{try} -- Host #{@hostname} unreachable: #{e.class.name} - #{e.message}"
49
- @logger.warn "Trying again in #{wait} seconds"
50
- sleep wait
51
- (last_wait, wait) = wait, last_wait + wait
52
- try += 1
53
- retry
54
- else
55
- @logger.error "Failed to connect to #{@hostname}"
56
- raise
57
- end
58
- end
65
+ #try three ways to connect to host (ip, vmhostname, hostname)
66
+ methods = []
67
+ if @ip
68
+ @ssh ||= connect_block(@ip, @user, @ssh_opts)
69
+ methods << "ip (#{@ip})"
70
+ end
71
+ if @vmhostname && !@ssh
72
+ @ssh ||= connect_block(@vmhostname, @user, @ssh_opts)
73
+ methods << "vmhostname (#{@vmhostname})"
74
+ end
75
+ if @hostname && !@ssh
76
+ @ssh ||= connect_block(@hostname, @user, @ssh_opts)
77
+ methods << "hostname (#{@hostname})"
78
+ end
79
+ if not @ssh
80
+ @logger.error "Failed to connect to #{@hostname}, attempted #{methods.join(', ')}"
81
+ raise RuntimeError, "Cannot connect to #{@hostname}"
82
+ end
83
+ @ssh
59
84
  end
60
85
 
61
86
  # closes this SshConnection
@@ -1,4 +1,4 @@
1
- [ 'host', 'answers', 'dsl' ].each do |lib|
1
+ [ 'host', 'dsl' ].each do |lib|
2
2
  require "beaker/#{lib}"
3
3
  end
4
4
 
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '2.18.0'
3
+ STRING = '2.19.0'
4
4
  end
5
5
  end
@@ -394,7 +394,6 @@ describe ClassMixedWithDSLHelpers do
394
394
  allow( el_agent ).to receive( :puppet ).and_return( { 'vardir' => vardir } )
395
395
 
396
396
  expect( el_agent ).to receive( :file_exist? ).with("/var/state/agent_catalog_run.lock").and_return(false)
397
- expect( el_agent ).to receive( :file_exist? ).with("/etc/init.d/pe-puppet-agent").and_return(true)
398
397
 
399
398
  expect( subject ).to receive( :puppet_resource ).with( "service", "puppet", "ensure=stopped").once
400
399
  expect( subject ).to receive( :on ).once
@@ -29,12 +29,21 @@ describe ClassMixedWithDSLHelpers do
29
29
 
30
30
  it "returns its second and third arguments concatenated." do
31
31
  create_files(['destdir/name'])
32
- result = subject.fetch_http_file "http://beaker.tool/", "name", "destdir"
32
+ result = subject.fetch_http_file "http://beaker.tool", "name", "destdir"
33
33
  expect(result).to eq("destdir/name")
34
34
  end
35
35
 
36
36
  end
37
37
 
38
+ describe 'given invalid arguments' do
39
+
40
+ it 'chomps correctly when given a URL ending with a / character' do
41
+ expect( subject ).to receive( :open ).with( 'http://beaker.tool/name', anything )
42
+ subject.fetch_http_file( "http://beaker.tool/", "name", "destdir" )
43
+ end
44
+
45
+ end
46
+
38
47
  end
39
48
 
40
49
  describe "#fetch_http_dir" do