beaker 3.13.0 → 3.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/acceptance/tests/subcommands/destroy.rb +49 -0
- data/acceptance/tests/subcommands/exec.rb +33 -0
- data/acceptance/tests/subcommands/init.rb +12 -36
- data/acceptance/tests/subcommands/provision.rb +9 -35
- data/beaker.gemspec +2 -2
- data/bin/beaker +1 -1
- data/docs/how_to/cloning_private_repos.md +3 -0
- data/docs/how_to/enabling_cross_sut_access.md +23 -0
- data/docs/how_to/ssh_agent_forwarding.md +4 -0
- data/lib/beaker/cli.rb +45 -15
- data/lib/beaker/host.rb +1 -1
- data/lib/beaker/hypervisor.rb +2 -1
- data/lib/beaker/network_manager.rb +5 -1
- data/lib/beaker/options/parser.rb +8 -1
- data/lib/beaker/options/subcommand_options_file_parser.rb +20 -0
- data/lib/beaker/ssh_connection.rb +1 -1
- data/lib/beaker/subcommand.rb +140 -28
- data/lib/beaker/subcommands/subcommand_util.rb +12 -125
- data/lib/beaker/version.rb +1 -1
- data/spec/beaker/cli_spec.rb +31 -0
- data/spec/beaker/logger_spec.rb +2 -0
- data/spec/beaker/options/parser_spec.rb +14 -1
- data/spec/beaker/options/subcommand_options_parser_spec.rb +33 -0
- data/spec/beaker/subcommand/subcommand_util_spec.rb +31 -204
- data/spec/beaker/subcommand_spec.rb +146 -0
- metadata +27 -20
@@ -204,7 +204,8 @@ module Beaker
|
|
204
204
|
# 2. ARGV or provided arguments array
|
205
205
|
# 3. the 'CONFIG' section of the hosts file
|
206
206
|
# 4. options file values
|
207
|
-
# 5.
|
207
|
+
# 5. subcommand options, if executing beaker subcommands
|
208
|
+
# 6. default or preset values are given the lowest priority
|
208
209
|
#
|
209
210
|
# @param [Array] args ARGV or a provided arguments array
|
210
211
|
# @raise [ArgumentError] Raises error on bad input
|
@@ -214,6 +215,12 @@ module Beaker
|
|
214
215
|
cmd_line_options = @command_line_parser.parse(args)
|
215
216
|
cmd_line_options[:command_line] = ([$0] + args).join(' ')
|
216
217
|
@attribution = @attribution.merge(tag_sources(cmd_line_options, "flag"))
|
218
|
+
|
219
|
+
# Subcommands are the first to get merged into presets
|
220
|
+
subcommand_options = Beaker::Options::SubcommandOptionsParser.parse_subcommand_options(args)
|
221
|
+
@attribution = @attribution.merge(tag_sources(subcommand_options, 'subcommand'))
|
222
|
+
@options.merge!(subcommand_options)
|
223
|
+
|
217
224
|
file_options = Beaker::Options::OptionsFileParser.parse_options_file(cmd_line_options[:options_file])
|
218
225
|
@attribution = @attribution.merge(tag_sources(file_options, "options_file"))
|
219
226
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Beaker
|
2
|
+
module Options
|
3
|
+
#A set of functions to read options files
|
4
|
+
module SubcommandOptionsParser
|
5
|
+
|
6
|
+
# @return [OptionsHash, Hash] returns an empty OptionHash or loads subcommand options yaml
|
7
|
+
# from disk
|
8
|
+
def self.parse_subcommand_options(argv)
|
9
|
+
result = OptionsHash.new
|
10
|
+
if Beaker::Subcommands::SubcommandUtil.execute_subcommand?(argv[0])
|
11
|
+
return result if argv[0] == 'init'
|
12
|
+
if Beaker::Subcommands::SubcommandUtil::SUBCOMMAND_OPTIONS.exist?
|
13
|
+
result = YAML.load_file(Beaker::Subcommands::SubcommandUtil::SUBCOMMAND_OPTIONS)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
result
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -108,7 +108,7 @@ module Beaker
|
|
108
108
|
|
109
109
|
# Wait for the ssh connection to fail, returns true on connection failure and false otherwise
|
110
110
|
# @param [Hash{Symbol=>String}] options Options hash to control method conditionals
|
111
|
-
# @option options [Boolean] :pty Should we request a terminal when attempting
|
111
|
+
# @option options [Boolean] :pty Should we request a terminal when attempting
|
112
112
|
# to send a command over this connection?
|
113
113
|
# @option options [String] :stdin Any input to be sent along with the command
|
114
114
|
# @param [IO] stdout_callback An IO stream to send connection stdout to, defaults to nil
|
data/lib/beaker/subcommand.rb
CHANGED
@@ -6,9 +6,13 @@ module Beaker
|
|
6
6
|
class Subcommand < Thor
|
7
7
|
SubcommandUtil = Beaker::Subcommands::SubcommandUtil
|
8
8
|
|
9
|
+
|
9
10
|
def initialize(*args)
|
10
11
|
super
|
11
|
-
|
12
|
+
FileUtils.mkdir_p(SubcommandUtil::CONFIG_DIR)
|
13
|
+
FileUtils.touch(SubcommandUtil::SUBCOMMAND_OPTIONS) unless SubcommandUtil::SUBCOMMAND_OPTIONS.exist?
|
14
|
+
FileUtils.touch(SubcommandUtil::SUBCOMMAND_STATE) unless SubcommandUtil::SUBCOMMAND_STATE.exist?
|
15
|
+
@cli = Beaker::CLI.new
|
12
16
|
end
|
13
17
|
|
14
18
|
# Options listed in this group 'Beaker run' are options that can be set on subcommands
|
@@ -49,55 +53,163 @@ module Beaker
|
|
49
53
|
class_option :'exclude-tags', :type => :string, :group => 'Beaker run'
|
50
54
|
class_option :'xml-time-order', :type => :boolean, :group => 'Beaker run'
|
51
55
|
|
52
|
-
desc "init
|
53
|
-
option :help, :type => :boolean, :hide => true
|
56
|
+
desc "init BEAKER_RUN_OPTIONS", "Initializes the required configuration for Beaker subcommand execution"
|
54
57
|
long_desc <<-LONGDESC
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
Initializes the required .beaker configuration folder. This folder contains
|
59
|
+
a subcommand_options.yaml file that is user-facing; altering this file will
|
60
|
+
alter the options subcommand execution. Subsequent subcommand execution,
|
61
|
+
such as `provision`, will result in beaker making modifications to this file
|
62
|
+
as necessary.
|
60
63
|
LONGDESC
|
61
|
-
|
64
|
+
option :help, :type => :boolean, :hide => true
|
65
|
+
def init()
|
62
66
|
if options[:help]
|
63
67
|
invoke :help, [], ["init"]
|
64
68
|
return
|
65
69
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
70
|
+
|
71
|
+
@cli.parse_options
|
72
|
+
|
73
|
+
# delete unnecessary keys for saving the options
|
74
|
+
options_to_write = @cli.configured_options
|
75
|
+
# Remove keys we don't want to save
|
76
|
+
[:timestamp, :logger, :command_line, :beaker_version, :hosts_file].each do |key|
|
77
|
+
options_to_write.delete(key)
|
78
|
+
end
|
79
|
+
|
80
|
+
options_to_write = SubcommandUtil.sanitize_options_for_save(options_to_write)
|
81
|
+
|
82
|
+
@cli.logger.notify 'Writing configured options to disk'
|
83
|
+
File.open(SubcommandUtil::SUBCOMMAND_OPTIONS, 'w') do |f|
|
84
|
+
f.write(options_to_write.to_yaml)
|
85
|
+
end
|
86
|
+
@cli.logger.notify "Options written to #{SubcommandUtil::SUBCOMMAND_OPTIONS}"
|
87
|
+
|
88
|
+
state = YAML::Store.new(SubcommandUtil::SUBCOMMAND_STATE)
|
89
|
+
state.transaction do
|
90
|
+
state['provisioned'] = false
|
91
|
+
end
|
72
92
|
end
|
73
93
|
|
74
|
-
desc "provision", "Provisions the beaker test
|
75
|
-
option :validate, :type => :boolean, :default => true
|
76
|
-
option :configure, :type => :boolean, :default => true
|
77
|
-
option :help, :type => :boolean, :hide => true
|
94
|
+
desc "provision", "Provisions the beaker systems under test(SUTs)"
|
78
95
|
long_desc <<-LONGDESC
|
79
|
-
Provisions
|
96
|
+
Provisions hosts defined in your subcommand_options file. You can pass the --hosts
|
97
|
+
flag here to override any hosts provided there. Really, you can pass most any beaker
|
98
|
+
flag here to override.
|
80
99
|
LONGDESC
|
100
|
+
option :help, :type => :boolean, :hide => true
|
81
101
|
def provision()
|
82
102
|
if options[:help]
|
83
103
|
invoke :help, [], ["provision"]
|
84
104
|
return
|
85
105
|
end
|
86
106
|
|
87
|
-
|
107
|
+
state = YAML::Store.new(SubcommandUtil::SUBCOMMAND_STATE)
|
108
|
+
if state.transaction { state['provisioned']}
|
109
|
+
SubcommandUtil.error_with('Provisioned SUTs detected. Please destroy and reprovision.')
|
110
|
+
end
|
111
|
+
|
112
|
+
@cli.parse_options
|
113
|
+
@cli.provision
|
114
|
+
|
115
|
+
# Sanitize the hosts
|
116
|
+
cleaned_hosts = SubcommandUtil.sanitize_options_for_save(@cli.combined_instance_and_options_hosts)
|
117
|
+
|
118
|
+
# Update each host provisioned with a flag indicating that it no longer needs
|
119
|
+
# provisioning
|
120
|
+
cleaned_hosts.each do |host, host_hash|
|
121
|
+
host_hash['provision'] = false
|
122
|
+
end
|
123
|
+
|
124
|
+
# should we only update the options here with the new host? Or update the settings
|
125
|
+
# with whatever new flags may have been provided with provision?
|
126
|
+
options_storage = YAML::Store.new(SubcommandUtil::SUBCOMMAND_OPTIONS)
|
127
|
+
options_storage.transaction do
|
128
|
+
@cli.logger.notify 'updating HOSTS key in subcommand_options'
|
129
|
+
options_storage['HOSTS'] = cleaned_hosts
|
130
|
+
options_storage['hosts_preserved_yaml_file'] = @cli.options[:hosts_preserved_yaml_file]
|
131
|
+
end
|
132
|
+
|
133
|
+
@cli.preserve_hosts_file
|
134
|
+
|
135
|
+
state.transaction do
|
136
|
+
state['provisioned'] = true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
desc 'exec FILE/BEAKER_SUITE', 'execute a directory, file, or beaker suite'
|
141
|
+
long_desc <<-LONG_DESC
|
142
|
+
Run a single file, directory, or beaker suite. If supplied a file or directory,
|
143
|
+
that resource will be run in the context of the `tests` suite; If supplied a beaker
|
144
|
+
suite, then just that suite will run. If no resource is supplied, then this command
|
145
|
+
executes the suites as they are defined in the configuration.
|
146
|
+
LONG_DESC
|
147
|
+
option :help, :type => :boolean, :hide => true
|
148
|
+
def exec(resource=nil)
|
149
|
+
if options[:help]
|
150
|
+
invoke :help, [], ["exec"]
|
151
|
+
return
|
152
|
+
end
|
153
|
+
|
154
|
+
@cli.parse_options
|
155
|
+
@cli.initialize_network_manager
|
88
156
|
|
89
|
-
|
90
|
-
|
157
|
+
if !resource
|
158
|
+
@cli.execute!
|
159
|
+
return
|
91
160
|
end
|
92
161
|
|
93
|
-
|
162
|
+
beaker_suites = [:pre_suite, :tests, :post_suite, :pre_cleanup]
|
94
163
|
|
95
|
-
if
|
96
|
-
|
97
|
-
|
164
|
+
if Pathname(resource).exist?
|
165
|
+
# If we determine the resource is a valid file resource, then we empty
|
166
|
+
# all the suites and run that file resource in the tests suite. In the
|
167
|
+
# future, when we have the ability to have custom suites, we should change
|
168
|
+
# this to run in a custom suite. You know, in the future.
|
169
|
+
beaker_suites.each do |suite|
|
170
|
+
@cli.options[suite] = []
|
171
|
+
end
|
172
|
+
if Pathname(resource).directory?
|
173
|
+
@cli.options[:tests] = Dir.glob("#{Pathname(resource).expand_path}/*.rb")
|
174
|
+
else
|
175
|
+
@cli.options[:tests] = [Pathname(resource).expand_path.to_s]
|
176
|
+
end
|
177
|
+
elsif resource.match(/pre-suite|tests|post-suite|pre-cleanup/)
|
178
|
+
# The regex match here is loose so that users can supply multiple suites,
|
179
|
+
# such as `beaker exec pre-suite,tests`.
|
180
|
+
beaker_suites.each do |suite|
|
181
|
+
@cli.options[suite] = [] unless resource.gsub(/-/, '_').match(suite.to_s)
|
182
|
+
end
|
98
183
|
else
|
99
|
-
|
184
|
+
raise ArgumentError, "Unable to parse #{resource} with beaker exec"
|
100
185
|
end
|
186
|
+
@cli.execute!
|
187
|
+
end
|
188
|
+
|
189
|
+
desc "destroy", "Destroys the provisioned VMs"
|
190
|
+
long_desc <<-LONG_DESC
|
191
|
+
Destroys the currently provisioned VMs
|
192
|
+
LONG_DESC
|
193
|
+
option :help, :type => :boolean, :hide => true
|
194
|
+
def destroy()
|
195
|
+
if options[:help]
|
196
|
+
invoke :help, [], ["destroy"]
|
197
|
+
return
|
198
|
+
end
|
199
|
+
|
200
|
+
state = YAML::Store.new(SubcommandUtil::SUBCOMMAND_STATE)
|
201
|
+
unless state.transaction { state['provisioned']}
|
202
|
+
SubcommandUtil.error_with('Please provision an environment')
|
203
|
+
end
|
204
|
+
|
205
|
+
@cli.parse_options
|
206
|
+
@cli.options[:provision] = false
|
207
|
+
@cli.initialize_network_manager
|
208
|
+
@cli.network_manager.cleanup
|
209
|
+
|
210
|
+
state.transaction {
|
211
|
+
state.delete('provisioned')
|
212
|
+
}
|
101
213
|
end
|
102
214
|
end
|
103
215
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'json'
|
1
2
|
require 'rake'
|
2
3
|
require 'stringio'
|
3
4
|
require 'yaml/store'
|
@@ -7,87 +8,27 @@ module Beaker
|
|
7
8
|
module Subcommands
|
8
9
|
# Methods used in execution of Subcommands
|
9
10
|
# - should we execute a subcommand?
|
10
|
-
# -
|
11
|
-
# - execute Beaker
|
12
|
-
# - update a rakefile to require beaker quickstart tasks
|
13
|
-
# - initialise a rake application
|
14
|
-
# - execute a rake task
|
15
|
-
# - execute the vagrant quickstart task
|
16
|
-
# - execute the vmpooler quickstart task
|
11
|
+
# - sanitize options for saving as json
|
17
12
|
# - exit with a specific message
|
18
|
-
# - execute the quick start task for the specified hypervisor
|
19
13
|
# - capture stdout and stderr
|
20
14
|
module SubcommandUtil
|
21
|
-
BEAKER_REQUIRE = "require 'beaker/tasks/quick_start'"
|
22
|
-
HYPERVISORS = ["vagrant", "vmpooler"]
|
23
15
|
CONFIG_DIR = ".beaker"
|
24
|
-
|
16
|
+
SUBCOMMAND_OPTIONS = Pathname("#{CONFIG_DIR}/subcommand_options.yaml")
|
17
|
+
SUBCOMMAND_STATE = Pathname("#{CONFIG_DIR}/.subcommand_state.yaml")
|
18
|
+
PERSISTED_HOSTS = Pathname("#{CONFIG_DIR}/.hosts.yaml")
|
19
|
+
PERSISTED_HYPERVISORS = Pathname("#{CONFIG_DIR}/.hypervisors.yaml")
|
25
20
|
|
26
|
-
# Check if the first argument to the beaker execution is a subcommand
|
27
|
-
# @return [Boolean] true if argv[0] is "help" or a method defined in the Subcommands class, false otherwise
|
28
21
|
def self.execute_subcommand?(arg0)
|
29
22
|
return false if arg0.nil?
|
30
23
|
(Beaker::Subcommand.instance_methods(false) << :help).include? arg0.to_sym
|
31
24
|
end
|
32
25
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Update ARGV and call Beaker
|
43
|
-
# @param [Array<String>] args the arguments determined by a specific subcommand
|
44
|
-
def self.execute_beaker(*args)
|
45
|
-
reset_argv(args)
|
46
|
-
Beaker::CLI.new.execute!
|
47
|
-
end
|
48
|
-
|
49
|
-
# Determines what Rakefile to use
|
50
|
-
# @return [String] the name of the rakefile to use
|
51
|
-
def self.determine_rake_file()
|
52
|
-
rake_app.find_rakefile_location() ? rake_app.find_rakefile_location()[0] : "Rakefile"
|
53
|
-
end
|
54
|
-
|
55
|
-
# Check for the presence of a Rakefile containing the require of the
|
56
|
-
# quick start tasks
|
57
|
-
def self.require_tasks()
|
58
|
-
rake_file = determine_rake_file()
|
59
|
-
FileUtils.touch(rake_file)
|
60
|
-
unless File.readlines(rake_file).grep(/#{BEAKER_REQUIRE}/).any?
|
61
|
-
File.open(rake_file, "a+") { |f| f.puts(BEAKER_REQUIRE) }
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# Initialises a rake application
|
66
|
-
# @return [Object] a rake application
|
67
|
-
def self.rake_app()
|
68
|
-
unless @rake_app
|
69
|
-
ARGV.clear
|
70
|
-
@rake_app = Rake.application
|
71
|
-
@rake_app.init
|
72
|
-
end
|
73
|
-
@rake_app
|
74
|
-
end
|
75
|
-
|
76
|
-
# Clear ARGV and execute a Rake task
|
77
|
-
# @param [String] task the rake task to execute
|
78
|
-
def self.execute_rake_task(task)
|
79
|
-
rake_app.load_rakefile()
|
80
|
-
with_captured_output { rake_app.invoke_task(task) }
|
81
|
-
end
|
82
|
-
|
83
|
-
# Execute the quick start task for vagrant
|
84
|
-
def self.init_vagrant()
|
85
|
-
execute_rake_task("beaker_quickstart:gen_hosts[vagrant]")
|
86
|
-
end
|
87
|
-
|
88
|
-
# Execute the quick start task for vmpooler
|
89
|
-
def self.init_vmpooler()
|
90
|
-
execute_rake_task("beaker_quickstart:gen_hosts[vmpooler]")
|
26
|
+
def self.sanitize_options_for_save(options)
|
27
|
+
# God help us, the YAML library won't stop adding tags to objects, so this
|
28
|
+
# hack is a way to force the options into the basic object types so that
|
29
|
+
# an eventual YAML.dump or .to_yaml call doesn't add tags.
|
30
|
+
# Relevant stackoverflow: http://stackoverflow.com/questions/18178098/how-do-i-have-ruby-yaml-dump-a-hash-subclass-as-a-simple-hash
|
31
|
+
JSON.parse(options.to_json)
|
91
32
|
end
|
92
33
|
|
93
34
|
# Print a message to the console and exit with specified exit code, defaults to 1
|
@@ -100,25 +41,6 @@ module Beaker
|
|
100
41
|
exit(exit_code)
|
101
42
|
end
|
102
43
|
|
103
|
-
# Call the quick start task for the specified hypervisor
|
104
|
-
# @param [String] hypervisor the hypervisor we want to query
|
105
|
-
def self.init_hypervisor(hypervisor)
|
106
|
-
case hypervisor
|
107
|
-
when "vagrant"
|
108
|
-
init_vagrant
|
109
|
-
when "vmpooler"
|
110
|
-
init_vmpooler
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# Verify that a valid hypervisor has been specified
|
115
|
-
# @param [String] hypervisor the hypervisor we want to validate
|
116
|
-
def self.verify_init_args(hypervisor)
|
117
|
-
unless HYPERVISORS.include?(hypervisor)
|
118
|
-
error_with("Invalid hypervisor. Currently supported hypervisors are: #{HYPERVISORS.join(', ')}")
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
44
|
# Execute a task but capture stdout and stderr to a buffer
|
123
45
|
def self.with_captured_output
|
124
46
|
begin
|
@@ -133,41 +55,6 @@ module Beaker
|
|
133
55
|
end
|
134
56
|
end
|
135
57
|
|
136
|
-
# Initialise the beaker config
|
137
|
-
def self.init_config()
|
138
|
-
FileUtils.mkdir_p CONFIG_DIR
|
139
|
-
@@store = YAML::Store.new("#{CONFIG_DIR}/config")
|
140
|
-
end
|
141
|
-
|
142
|
-
# Store values from a hash into the beaker config
|
143
|
-
# @param [Hash{Symbol=>String}] config values we want to store
|
144
|
-
def self.store_config(config)
|
145
|
-
@@store.transaction do
|
146
|
-
CONFIG_KEYS.each do |key|
|
147
|
-
@@store[key] = config[key] unless config[key].nil?
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Delete keys from the beaker configi
|
153
|
-
# @param [Array<Object>] keys the keys we want to delete from the config
|
154
|
-
def self.delete_config(keys)
|
155
|
-
@@store.transaction {
|
156
|
-
keys.each do |key|
|
157
|
-
@@store.delete(key)
|
158
|
-
end
|
159
|
-
}
|
160
|
-
end
|
161
|
-
|
162
|
-
# Reset args and provision nodes
|
163
|
-
# @param [String] hypervisor the hypervisor to use
|
164
|
-
# @param [Array<Object>] options the options to use when provisioning
|
165
|
-
def self.provision(hypervisor, options)
|
166
|
-
reset_argv(["--hosts", "#{CONFIG_DIR}/acceptance/config/default_#{hypervisor}_hosts.yaml", "--validate", options[:validate], "--configure", options[:configure]])
|
167
|
-
beaker = Beaker::CLI.new.parse_options
|
168
|
-
beaker.provision
|
169
|
-
beaker.preserve_hosts_file
|
170
|
-
end
|
171
58
|
end
|
172
59
|
end
|
173
60
|
end
|
data/lib/beaker/version.rb
CHANGED
data/spec/beaker/cli_spec.rb
CHANGED
@@ -50,6 +50,36 @@ module Beaker
|
|
50
50
|
Beaker::CLI.new.parse_options
|
51
51
|
}
|
52
52
|
|
53
|
+
context '#configured_options' do
|
54
|
+
it 'returns a list of options that were not presets' do
|
55
|
+
attribution = cli.instance_variable_get(:@attribution)
|
56
|
+
attribution.each do |attribute, setter|
|
57
|
+
if setter == 'preset'
|
58
|
+
expect(cli.configured_options[attribute]).to be_nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context '#combined_instance_and_options_hosts' do
|
65
|
+
let (:options_host) { {'HOSTS' => {'ubuntu' => {:options_attribute => 'options'}} }}
|
66
|
+
let (:instance_host ) {
|
67
|
+
[Beaker::Host.create('ubuntu', {:platform => 'host'}, {} )]
|
68
|
+
}
|
69
|
+
before do
|
70
|
+
cli.instance_variable_set(:@options, options_host)
|
71
|
+
cli.instance_variable_set(:@hosts, instance_host)
|
72
|
+
end
|
73
|
+
it 'combines the options and instance host objects' do
|
74
|
+
merged_host = cli.combined_instance_and_options_hosts
|
75
|
+
expect(merged_host).to have_key('ubuntu')
|
76
|
+
expect(merged_host['ubuntu']).to have_key(:options_attribute)
|
77
|
+
expect(merged_host['ubuntu']).to have_key(:platform)
|
78
|
+
expect(merged_host['ubuntu'][:options_attribute]).to eq('options')
|
79
|
+
expect(merged_host['ubuntu'][:platform]).to eq('host')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
53
83
|
context 'execute!' do
|
54
84
|
before :each do
|
55
85
|
stub_const("Beaker::Logger", double().as_null_object )
|
@@ -503,6 +533,7 @@ module Beaker
|
|
503
533
|
expect( answer.start_with?(command_correct) ).to be_truthy
|
504
534
|
end
|
505
535
|
end
|
536
|
+
|
506
537
|
end
|
507
538
|
end
|
508
539
|
end
|