beaker 2.7.1 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/HISTORY.md +121 -2
- data/lib/beaker/dsl.rb +2 -2
- data/lib/beaker/dsl/helpers.rb +13 -1429
- data/lib/beaker/dsl/helpers/facter_helpers.rb +48 -0
- data/lib/beaker/dsl/helpers/hiera_helpers.rb +71 -0
- data/lib/beaker/dsl/helpers/host_helpers.rb +506 -0
- data/lib/beaker/dsl/helpers/puppet_helpers.rb +698 -0
- data/lib/beaker/dsl/helpers/tk_helpers.rb +101 -0
- data/lib/beaker/dsl/helpers/web_helpers.rb +115 -0
- data/lib/beaker/dsl/install_utils.rb +8 -1570
- data/lib/beaker/dsl/install_utils/ezbake_utils.rb +256 -0
- data/lib/beaker/dsl/install_utils/module_utils.rb +237 -0
- data/lib/beaker/dsl/install_utils/pe_utils.rb +518 -0
- data/lib/beaker/dsl/install_utils/puppet_utils.rb +722 -0
- data/lib/beaker/dsl/outcomes.rb +0 -4
- data/lib/beaker/dsl/roles.rb +0 -3
- data/lib/beaker/dsl/structure.rb +127 -4
- data/lib/beaker/dsl/wrappers.rb +0 -4
- data/lib/beaker/host.rb +23 -0
- data/lib/beaker/host/unix/pkg.rb +4 -4
- data/lib/beaker/host_prebuilt_steps.rb +11 -5
- data/lib/beaker/hypervisor/vagrant.rb +1 -0
- data/lib/beaker/hypervisor/vmpooler.rb +38 -0
- data/lib/beaker/logger.rb +10 -4
- data/lib/beaker/network_manager.rb +5 -4
- data/lib/beaker/options/command_line_parser.rb +7 -0
- data/lib/beaker/shared.rb +2 -1
- data/lib/beaker/shared/semvar.rb +41 -0
- data/lib/beaker/test_suite.rb +20 -6
- data/lib/beaker/version.rb +1 -1
- data/spec/beaker/dsl/helpers/facter_helpers_spec.rb +59 -0
- data/spec/beaker/dsl/helpers/hiera_helpers_spec.rb +96 -0
- data/spec/beaker/dsl/helpers/host_helpers_spec.rb +413 -0
- data/spec/beaker/dsl/{helpers_spec.rb → helpers/puppet_helpers_spec.rb} +2 -611
- data/spec/beaker/dsl/helpers/tk_helpers_spec.rb +83 -0
- data/spec/beaker/dsl/helpers/web_helpers_spec.rb +60 -0
- data/spec/beaker/dsl/install_utils/module_utils_spec.rb +241 -0
- data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +475 -0
- data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +523 -0
- data/spec/beaker/dsl/structure_spec.rb +108 -0
- data/spec/beaker/host_prebuilt_steps_spec.rb +44 -0
- data/spec/beaker/host_spec.rb +41 -0
- data/spec/beaker/hypervisor/vagrant_spec.rb +2 -1
- data/spec/beaker/logger_spec.rb +9 -2
- data/spec/beaker/network_manager_spec.rb +7 -1
- data/spec/beaker/options/command_line_parser_spec.rb +3 -2
- data/spec/beaker/shared/semvar_spec.rb +36 -0
- data/spec/beaker/test_suite_spec.rb +48 -0
- data/spec/mocks.rb +10 -0
- metadata +23 -5
- data/lib/beaker/dsl/ezbake_utils.rb +0 -259
- data/spec/beaker/dsl/install_utils_spec.rb +0 -1242
@@ -0,0 +1,48 @@
|
|
1
|
+
module Beaker
|
2
|
+
module DSL
|
3
|
+
module Helpers
|
4
|
+
# Methods that help you interact with your facter installation, facter must be installed
|
5
|
+
# for these methods to execute correctly
|
6
|
+
#
|
7
|
+
module FacterHelpers
|
8
|
+
|
9
|
+
# @!macro common_opts
|
10
|
+
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
11
|
+
# @option opts [Boolean] :silent (false) Do not produce log output
|
12
|
+
# @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
|
13
|
+
# (or range) of integer exit codes that should be considered
|
14
|
+
# acceptable. An error will be thrown if the exit code does not
|
15
|
+
# match one of the values in this list.
|
16
|
+
# @option opts [Hash{String=>String}] :environment ({}) These will be
|
17
|
+
# treated as extra environment variables that should be set before
|
18
|
+
# running the command.
|
19
|
+
#
|
20
|
+
|
21
|
+
# Get a facter fact from a provided host
|
22
|
+
#
|
23
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
24
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
25
|
+
# @param [String] name The name of the fact to query for
|
26
|
+
# @!macro common_opts
|
27
|
+
#
|
28
|
+
# @return String The value of the fact 'name' on the provided host
|
29
|
+
# @raise [FailTest] Raises an exception if call to facter fails
|
30
|
+
def fact_on(host, name, opts = {})
|
31
|
+
result = on host, facter(name, opts)
|
32
|
+
if result.kind_of?(Array)
|
33
|
+
result.map { |res| res.stdout.chomp }
|
34
|
+
else
|
35
|
+
result.stdout.chomp
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get a facter fact from the default host
|
40
|
+
# @see #fact_on
|
41
|
+
def fact(name, opts = {})
|
42
|
+
fact_on(default, name, opts)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Beaker
|
2
|
+
module DSL
|
3
|
+
module Helpers
|
4
|
+
# Methods that help you interact with your hiera installation, hiera must be installed
|
5
|
+
# for these methods to execute correctly
|
6
|
+
module HieraHelpers
|
7
|
+
|
8
|
+
# @!macro common_opts
|
9
|
+
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
10
|
+
# @option opts [Boolean] :silent (false) Do not produce log output
|
11
|
+
# @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
|
12
|
+
# (or range) of integer exit codes that should be considered
|
13
|
+
# acceptable. An error will be thrown if the exit code does not
|
14
|
+
# match one of the values in this list.
|
15
|
+
# @option opts [Hash{String=>String}] :environment ({}) These will be
|
16
|
+
# treated as extra environment variables that should be set before
|
17
|
+
# running the command.
|
18
|
+
|
19
|
+
# Write hiera config file on one or more provided hosts
|
20
|
+
#
|
21
|
+
# @param[Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
22
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
23
|
+
# @param[Array] One or more hierarchy paths
|
24
|
+
def write_hiera_config_on(host, hierarchy)
|
25
|
+
|
26
|
+
block_on host do |host|
|
27
|
+
hiera_config=Hash.new
|
28
|
+
hiera_config[:backends] = 'yaml'
|
29
|
+
hiera_config[:yaml] = {}
|
30
|
+
hiera_config[:yaml][:datadir] = hiera_datadir(host)
|
31
|
+
hiera_config[:hierarchy] = hierarchy
|
32
|
+
hiera_config[:logger] = 'console'
|
33
|
+
create_remote_file host, host.puppet['hiera_config'], hiera_config.to_yaml
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Write hiera config file for the default host
|
38
|
+
# @see #write_hiera_config_on
|
39
|
+
def write_hiera_config(hierarchy)
|
40
|
+
write_hiera_config_on(default, hierarchy)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Copy hiera data files to one or more provided hosts
|
44
|
+
#
|
45
|
+
# @param[Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
46
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
47
|
+
# @param[String] Directory containing the hiera data files.
|
48
|
+
def copy_hiera_data_to(host, source)
|
49
|
+
scp_to host, File.expand_path(source), hiera_datadir(host)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Copy hiera data files to the default host
|
53
|
+
# @see #copy_hiera_data_to
|
54
|
+
def copy_hiera_data(source)
|
55
|
+
copy_hiera_data_to(default, source)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get file path to the hieradatadir for a given host.
|
59
|
+
# Handles whether or not a host is AIO-based & backwards compatibility
|
60
|
+
#
|
61
|
+
# @param[Host] host Host you want to use the hieradatadir from
|
62
|
+
#
|
63
|
+
# @return [String] Path to the hiera data directory
|
64
|
+
def hiera_datadir(host)
|
65
|
+
host[:type] =~ /aio/ ? File.join(host.puppet['codedir'], 'hieradata') : host[:hieradatadir]
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,506 @@
|
|
1
|
+
module Beaker
|
2
|
+
module DSL
|
3
|
+
module Helpers
|
4
|
+
# Methods that help you interact and manage the state of your Beaker SUTs, these
|
5
|
+
# methods do not require puppet to be installed to execute correctly
|
6
|
+
module HostHelpers
|
7
|
+
|
8
|
+
# @!macro common_opts
|
9
|
+
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
10
|
+
# @option opts [Boolean] :silent (false) Do not produce log output
|
11
|
+
# @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
|
12
|
+
# (or range) of integer exit codes that should be considered
|
13
|
+
# acceptable. An error will be thrown if the exit code does not
|
14
|
+
# match one of the values in this list.
|
15
|
+
# @option opts [Hash{String=>String}] :environment ({}) These will be
|
16
|
+
# treated as extra environment variables that should be set before
|
17
|
+
# running the command.
|
18
|
+
#
|
19
|
+
|
20
|
+
# The primary method for executing commands *on* some set of hosts.
|
21
|
+
#
|
22
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
23
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
24
|
+
# @param [String, Command] command The command to execute on *host*.
|
25
|
+
# @param [Proc] block Additional actions or assertions.
|
26
|
+
# @!macro common_opts
|
27
|
+
#
|
28
|
+
# @example Most basic usage
|
29
|
+
# on hosts, 'ls /tmp'
|
30
|
+
#
|
31
|
+
# @example Allowing additional exit codes to pass
|
32
|
+
# on agents, 'puppet agent -t', :acceptable_exit_codes => [0,2]
|
33
|
+
#
|
34
|
+
# @example Using the returned result for any kind of checking
|
35
|
+
# if on(host, 'ls -la ~').stdout =~ /\.bin/
|
36
|
+
# ...do some action...
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# @example Using TestCase helpers from within a test.
|
40
|
+
# agents.each do |agent|
|
41
|
+
# on agent, 'cat /etc/puppet/puppet.conf' do
|
42
|
+
# assert_match stdout, /server = #{master}/, 'WTF Mate'
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @example Using a role (defined in a String) to identify the host
|
47
|
+
# on "master", "echo hello"
|
48
|
+
#
|
49
|
+
# @example Using a role (defined in a Symbol) to identify the host
|
50
|
+
# on :dashboard, "echo hello"
|
51
|
+
# @return [Result] An object representing the outcome of *command*.
|
52
|
+
# @raise [FailTest] Raises an exception if *command* obviously fails.
|
53
|
+
def on(host, command, opts = {}, &block)
|
54
|
+
block_on host do | host |
|
55
|
+
cur_command = command
|
56
|
+
if command.is_a? Command
|
57
|
+
cur_command = command.cmd_line(host)
|
58
|
+
end
|
59
|
+
cmd_opts = {}
|
60
|
+
#add any additional environment variables to the command
|
61
|
+
if opts[:environment]
|
62
|
+
cmd_opts['ENV'] = opts[:environment]
|
63
|
+
end
|
64
|
+
@result = host.exec(Command.new(cur_command.to_s, [], cmd_opts), opts)
|
65
|
+
|
66
|
+
# Also, let additional checking be performed by the caller.
|
67
|
+
if block_given?
|
68
|
+
case block.arity
|
69
|
+
#block with arity of 0, just hand back yourself
|
70
|
+
when 0
|
71
|
+
yield self
|
72
|
+
#block with arity of 1 or greater, hand back the result object
|
73
|
+
else
|
74
|
+
yield @result
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@result
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# The method for executing commands on the default host
|
82
|
+
#
|
83
|
+
# @param [String, Command] command The command to execute on *host*.
|
84
|
+
# @param [Proc] block Additional actions or assertions.
|
85
|
+
# @!macro common_opts
|
86
|
+
#
|
87
|
+
# @example Most basic usage
|
88
|
+
# shell 'ls /tmp'
|
89
|
+
#
|
90
|
+
# @example Allowing additional exit codes to pass
|
91
|
+
# shell 'puppet agent -t', :acceptable_exit_codes => [0,2]
|
92
|
+
#
|
93
|
+
# @example Using the returned result for any kind of checking
|
94
|
+
# if shell('ls -la ~').stdout =~ /\.bin/
|
95
|
+
# ...do some action...
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# @example Using TestCase helpers from within a test.
|
99
|
+
# agents.each do |agent|
|
100
|
+
# shell('cat /etc/puppet/puppet.conf') do |result|
|
101
|
+
# assert_match result.stdout, /server = #{master}/, 'WTF Mate'
|
102
|
+
# end
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# @return [Result] An object representing the outcome of *command*.
|
106
|
+
# @raise [FailTest] Raises an exception if *command* obviously fails.
|
107
|
+
def shell(command, opts = {}, &block)
|
108
|
+
on(default, command, opts, &block)
|
109
|
+
end
|
110
|
+
|
111
|
+
# @deprecated
|
112
|
+
# An proxy for the last {Beaker::Result#stdout} returned by
|
113
|
+
# a method that makes remote calls. Use the {Beaker::Result}
|
114
|
+
# object returned by the method directly instead. For Usage see
|
115
|
+
# {Beaker::Result}.
|
116
|
+
def stdout
|
117
|
+
return nil if @result.nil?
|
118
|
+
@result.stdout
|
119
|
+
end
|
120
|
+
|
121
|
+
# @deprecated
|
122
|
+
# An proxy for the last {Beaker::Result#stderr} returned by
|
123
|
+
# a method that makes remote calls. Use the {Beaker::Result}
|
124
|
+
# object returned by the method directly instead. For Usage see
|
125
|
+
# {Beaker::Result}.
|
126
|
+
def stderr
|
127
|
+
return nil if @result.nil?
|
128
|
+
@result.stderr
|
129
|
+
end
|
130
|
+
|
131
|
+
# @deprecated
|
132
|
+
# An proxy for the last {Beaker::Result#exit_code} returned by
|
133
|
+
# a method that makes remote calls. Use the {Beaker::Result}
|
134
|
+
# object returned by the method directly instead. For Usage see
|
135
|
+
# {Beaker::Result}.
|
136
|
+
def exit_code
|
137
|
+
return nil if @result.nil?
|
138
|
+
@result.exit_code
|
139
|
+
end
|
140
|
+
|
141
|
+
# Move a file from a remote to a local path
|
142
|
+
# @note If using {Beaker::Host} for the hosts *scp* is not
|
143
|
+
# required on the system as it uses Ruby's net/scp library. The
|
144
|
+
# net-scp gem however is required (and specified in the gemspec).
|
145
|
+
#
|
146
|
+
# @param [Host, #do_scp_from] host One or more hosts (or some object
|
147
|
+
# that responds like
|
148
|
+
# {Beaker::Host#do_scp_from}.
|
149
|
+
# @param [String] from_path A remote path to a file.
|
150
|
+
# @param [String] to_path A local path to copy *from_path* to.
|
151
|
+
# @!macro common_opts
|
152
|
+
#
|
153
|
+
# @return [Result] Returns the result of the SCP operation
|
154
|
+
def scp_from host, from_path, to_path, opts = {}
|
155
|
+
block_on host do | host |
|
156
|
+
@result = host.do_scp_from(from_path, to_path, opts)
|
157
|
+
@result.log logger
|
158
|
+
@result
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Move a local file to a remote host using scp
|
163
|
+
# @note If using {Beaker::Host} for the hosts *scp* is not
|
164
|
+
# required on the system as it uses Ruby's net/scp library. The
|
165
|
+
# net-scp gem however is required (and specified in the gemspec.
|
166
|
+
# When using SCP with Windows it will now auto expand path when
|
167
|
+
# using `cygpath instead of failing or requiring full path
|
168
|
+
#
|
169
|
+
# @param [Host, #do_scp_to] host One or more hosts (or some object
|
170
|
+
# that responds like
|
171
|
+
# {Beaker::Host#do_scp_to}.
|
172
|
+
# @param [String] from_path A local path to a file.
|
173
|
+
# @param [String] to_path A remote path to copy *from_path* to.
|
174
|
+
# @!macro common_opts
|
175
|
+
#
|
176
|
+
# @return [Result] Returns the result of the SCP operation
|
177
|
+
def scp_to host, from_path, to_path, opts = {}
|
178
|
+
block_on host do | host |
|
179
|
+
if host['platform'] =~ /windows/ && to_path.match('`cygpath')
|
180
|
+
result = on host, "echo #{to_path}"
|
181
|
+
to_path = result.raw_output.chomp
|
182
|
+
end
|
183
|
+
@result = host.do_scp_to(from_path, to_path, opts)
|
184
|
+
@result.log logger
|
185
|
+
@result
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Move a local file or directory to a remote host using rsync
|
190
|
+
# @note rsync is required on the local host.
|
191
|
+
#
|
192
|
+
# @param [Host, #do_scp_to] host A host object that responds like
|
193
|
+
# {Beaker::Host}.
|
194
|
+
# @param [String] from_path A local path to a file or directory.
|
195
|
+
# @param [String] to_path A remote path to copy *from_path* to.
|
196
|
+
# @!macro common_opts
|
197
|
+
#
|
198
|
+
# @return [Result] Returns the result of the rsync operation
|
199
|
+
def rsync_to host, from_path, to_path, opts = {}
|
200
|
+
block_on host do | host |
|
201
|
+
if host['platform'] =~ /windows/ && to_path.match('`cygpath')
|
202
|
+
result = host.echo "#{to_path}"
|
203
|
+
to_path = result.raw_output.chomp
|
204
|
+
end
|
205
|
+
@result = host.do_rsync_to(from_path, to_path, opts)
|
206
|
+
@result
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Deploy packaging configurations generated by
|
211
|
+
# https://github.com/puppetlabs/packaging to a host.
|
212
|
+
#
|
213
|
+
# @note To ensure the repo configs are available for deployment,
|
214
|
+
# you should run `rake pl:jenkins:deb_repo_configs` and
|
215
|
+
# `rake pl:jenkins:rpm_repo_configs` on your project checkout
|
216
|
+
#
|
217
|
+
# @param [Host] host
|
218
|
+
# @param [String] path The path to the generated repository config
|
219
|
+
# files. ex: /myproject/pkg/repo_configs
|
220
|
+
# @param [String] name A human-readable name for the repository
|
221
|
+
# @param [String] version The version of the project, as used by the
|
222
|
+
# packaging tools. This can be determined with
|
223
|
+
# `rake pl:print_build_params` from the packaging
|
224
|
+
# repo.
|
225
|
+
def deploy_package_repo host, path, name, version
|
226
|
+
host.deploy_package_repo path, name, version
|
227
|
+
end
|
228
|
+
|
229
|
+
# Create a remote file out of a string
|
230
|
+
# @note This method uses Tempfile in Ruby's STDLIB as well as {#scp_to}.
|
231
|
+
#
|
232
|
+
# @param [Host, #do_scp_to] hosts One or more hosts (or some object
|
233
|
+
# that responds like
|
234
|
+
# {Beaker::Host#do_scp_from}.
|
235
|
+
# @param [String] file_path A remote path to place *file_content* at.
|
236
|
+
# @param [String] file_content The contents of the file to be placed.
|
237
|
+
# @!macro common_opts
|
238
|
+
# @option opts [String] :protocol Name of the underlying transfer method.
|
239
|
+
# Valid options are 'scp' or 'rsync'.
|
240
|
+
#
|
241
|
+
# @return [Result] Returns the result of the underlying SCP operation.
|
242
|
+
def create_remote_file(hosts, file_path, file_content, opts = {})
|
243
|
+
Tempfile.open 'beaker' do |tempfile|
|
244
|
+
File.open(tempfile.path, 'w') {|file| file.puts file_content }
|
245
|
+
|
246
|
+
opts[:protocol] ||= 'scp'
|
247
|
+
case opts[:protocol]
|
248
|
+
when 'scp'
|
249
|
+
scp_to hosts, tempfile.path, file_path, opts
|
250
|
+
when 'rsync'
|
251
|
+
rsync_to hosts, tempfile.path, file_path, opts
|
252
|
+
else
|
253
|
+
logger.debug "Unsupported transfer protocol, returning nil"
|
254
|
+
nil
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# Move a local script to a remote host and execute it
|
260
|
+
# @note this relies on {#on} and {#scp_to}
|
261
|
+
#
|
262
|
+
# @param [Host, #do_scp_to] host One or more hosts (or some object
|
263
|
+
# that responds like
|
264
|
+
# {Beaker::Host#do_scp_from}.
|
265
|
+
# @param [String] script A local path to find an executable script at.
|
266
|
+
# @!macro common_opts
|
267
|
+
# @param [Proc] block Additional tests to run after script has executed
|
268
|
+
#
|
269
|
+
# @return [Result] Returns the result of the underlying SCP operation.
|
270
|
+
def run_script_on(host, script, opts = {}, &block)
|
271
|
+
# this is unsafe as it uses the File::SEPARATOR will be set to that
|
272
|
+
# of the coordinator node. This works for us because we use cygwin
|
273
|
+
# which will properly convert the paths. Otherwise this would not
|
274
|
+
# work for running tests on a windows machine when the coordinator
|
275
|
+
# that the harness is running on is *nix. We should use
|
276
|
+
# {Beaker::Host#temp_path} instead. TODO
|
277
|
+
remote_path = File.join("", "tmp", File.basename(script))
|
278
|
+
|
279
|
+
scp_to host, script, remote_path
|
280
|
+
on host, remote_path, opts, &block
|
281
|
+
end
|
282
|
+
|
283
|
+
# Move a local script to default host and execute it
|
284
|
+
# @see #run_script_on
|
285
|
+
def run_script(script, opts = {}, &block)
|
286
|
+
run_script_on(default, script, opts, &block)
|
287
|
+
end
|
288
|
+
|
289
|
+
# Install a package on a host
|
290
|
+
#
|
291
|
+
# @param [Host] host A host object
|
292
|
+
# @param [String] package_name Name of the package to install
|
293
|
+
#
|
294
|
+
# @return [Result] An object representing the outcome of *install command*.
|
295
|
+
def install_package host, package_name, package_version = nil
|
296
|
+
host.install_package package_name, '', package_version
|
297
|
+
end
|
298
|
+
|
299
|
+
# Check to see if a package is installed on a remote host
|
300
|
+
#
|
301
|
+
# @param [Host] host A host object
|
302
|
+
# @param [String] package_name Name of the package to check for.
|
303
|
+
#
|
304
|
+
# @return [Boolean] true/false if the package is found
|
305
|
+
def check_for_package host, package_name
|
306
|
+
host.check_for_package package_name
|
307
|
+
end
|
308
|
+
|
309
|
+
# Upgrade a package on a host. The package must already be installed
|
310
|
+
#
|
311
|
+
# @param [Host] host A host object
|
312
|
+
# @param [String] package_name Name of the package to install
|
313
|
+
#
|
314
|
+
# @return [Result] An object representing the outcome of *upgrade command*.
|
315
|
+
def upgrade_package host, package_name
|
316
|
+
host.upgrade_package package_name
|
317
|
+
end
|
318
|
+
|
319
|
+
# Configure a host entry on the give host
|
320
|
+
# @example: will add a host entry for forge.puppetlabs.com
|
321
|
+
# add_system32_hosts_entry(host, { :ip => '23.251.154.122', :name => 'forge.puppetlabs.com' })
|
322
|
+
#
|
323
|
+
# @return nil
|
324
|
+
def add_system32_hosts_entry(host, opts = {})
|
325
|
+
if host['platform'] =~ /windows/
|
326
|
+
hosts_file = "C:\\Windows\\System32\\Drivers\\etc\\hosts"
|
327
|
+
host_entry = "#{opts['ip']}`t`t#{opts['name']}"
|
328
|
+
on host, powershell("\$text = \\\"#{host_entry}\\\"; Add-Content -path '#{hosts_file}' -value \$text")
|
329
|
+
else
|
330
|
+
raise "nothing to do for #{host.name} on #{host['platform']}"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# Back up the given file in the current_dir to the new_dir
|
335
|
+
#
|
336
|
+
# @!visibility private
|
337
|
+
#
|
338
|
+
# @param host [Beaker::Host] The target host
|
339
|
+
# @param current_dir [String] The directory containing the file to back up
|
340
|
+
# @param new_dir [String] The directory to copy the file to
|
341
|
+
# @param filename [String] The file to back up. Defaults to 'puppet.conf'
|
342
|
+
#
|
343
|
+
# @return [String, nil] The path to the file if the file exists, nil if it
|
344
|
+
# doesn't exist.
|
345
|
+
def backup_the_file host, current_dir, new_dir, filename = 'puppet.conf'
|
346
|
+
|
347
|
+
old_location = current_dir + '/' + filename
|
348
|
+
new_location = new_dir + '/' + filename + '.bak'
|
349
|
+
|
350
|
+
if host.file_exist? old_location
|
351
|
+
host.exec( Command.new( "cp #{old_location} #{new_location}" ) )
|
352
|
+
return new_location
|
353
|
+
else
|
354
|
+
logger.warn "Could not backup file '#{old_location}': no such file"
|
355
|
+
nil
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
#Run a curl command on the provided host(s)
|
360
|
+
#
|
361
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
362
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
363
|
+
# @param [String, Command] cmd The curl command to execute on *host*.
|
364
|
+
# @param [Proc] block Additional actions or assertions.
|
365
|
+
# @!macro common_opts
|
366
|
+
#
|
367
|
+
def curl_on(host, cmd, opts = {}, &block)
|
368
|
+
if options.is_pe? #check global options hash
|
369
|
+
on host, "curl --tlsv1 %s" % cmd, opts, &block
|
370
|
+
else
|
371
|
+
on host, "curl %s" % cmd, opts, &block
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
|
376
|
+
def curl_with_retries(desc, host, url, desired_exit_codes, max_retries = 60, retry_interval = 1)
|
377
|
+
opts = {
|
378
|
+
:desired_exit_codes => desired_exit_codes,
|
379
|
+
:max_retries => max_retries,
|
380
|
+
:retry_interval => retry_interval
|
381
|
+
}
|
382
|
+
retry_on(host, "curl -m 1 #{url}", opts)
|
383
|
+
end
|
384
|
+
|
385
|
+
# This command will execute repeatedly until success or it runs out with an error
|
386
|
+
#
|
387
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
388
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
389
|
+
# @param [String, Command] command The command to execute on *host*.
|
390
|
+
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
391
|
+
# @param [Proc] block Additional actions or assertions.
|
392
|
+
#
|
393
|
+
# @option opts [Array<Fixnum>, Fixnum] :desired_exit_codes (0) An array
|
394
|
+
# or integer exit code(s) that should be considered
|
395
|
+
# acceptable. An error will be thrown if the exit code never
|
396
|
+
# matches one of the values in this list.
|
397
|
+
# @option opts [Fixnum] :max_retries (60) number of times the
|
398
|
+
# command will be tried before failing
|
399
|
+
# @option opts [Float] :retry_interval (1) number of seconds
|
400
|
+
# that we'll wait between tries
|
401
|
+
# @option opts [Boolean] :verbose (false)
|
402
|
+
def retry_on(host, command, opts = {}, &block)
|
403
|
+
option_exit_codes = opts[:desired_exit_codes]
|
404
|
+
option_max_retries = opts[:max_retries].to_i
|
405
|
+
option_retry_interval = opts[:retry_interval].to_f
|
406
|
+
desired_exit_codes = option_exit_codes ? [option_exit_codes].flatten : [0]
|
407
|
+
desired_exit_codes = [0] if desired_exit_codes.empty?
|
408
|
+
max_retries = option_max_retries == 0 ? 60 : option_max_retries # nil & "" both return 0
|
409
|
+
retry_interval = option_retry_interval == 0 ? 1 : option_retry_interval
|
410
|
+
verbose = true.to_s == opts[:verbose]
|
411
|
+
|
412
|
+
log_prefix = host.log_prefix
|
413
|
+
logger.debug "\n#{log_prefix} #{Time.new.strftime('%H:%M:%S')}$ #{command}"
|
414
|
+
logger.debug " Trying command #{max_retries} times."
|
415
|
+
logger.debug ".", add_newline=false
|
416
|
+
|
417
|
+
result = on host, command, {:acceptable_exit_codes => (0...127), :silent => !verbose}, &block
|
418
|
+
num_retries = 0
|
419
|
+
until desired_exit_codes.include?(result.exit_code)
|
420
|
+
sleep retry_interval
|
421
|
+
result = on host, command, {:acceptable_exit_codes => (0...127), :silent => !verbose}, &block
|
422
|
+
num_retries += 1
|
423
|
+
logger.debug ".", add_newline=false
|
424
|
+
if (num_retries > max_retries)
|
425
|
+
logger.debug " Command \`#{command}\` failed."
|
426
|
+
fail("Command \`#{command}\` failed.")
|
427
|
+
end
|
428
|
+
end
|
429
|
+
logger.debug "\n#{log_prefix} #{Time.new.strftime('%H:%M:%S')}$ #{command} ostensibly successful."
|
430
|
+
result
|
431
|
+
end
|
432
|
+
|
433
|
+
# FIX: this should be moved into host/platform
|
434
|
+
# @visibility private
|
435
|
+
def run_cron_on(host, action, user, entry="", &block)
|
436
|
+
block_on host do | host |
|
437
|
+
platform = host['platform']
|
438
|
+
if platform.include?('solaris') || platform.include?('aix') then
|
439
|
+
case action
|
440
|
+
when :list then args = '-l'
|
441
|
+
when :remove then args = '-r'
|
442
|
+
when :add
|
443
|
+
on( host,
|
444
|
+
"echo '#{entry}' > /var/spool/cron/crontabs/#{user}",
|
445
|
+
&block )
|
446
|
+
end
|
447
|
+
|
448
|
+
else # default for GNU/Linux platforms
|
449
|
+
case action
|
450
|
+
when :list then args = '-l -u'
|
451
|
+
when :remove then args = '-r -u'
|
452
|
+
when :add
|
453
|
+
on( host,
|
454
|
+
"echo '#{entry}' > /tmp/#{user}.cron && " +
|
455
|
+
"crontab -u #{user} /tmp/#{user}.cron",
|
456
|
+
&block )
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
if args
|
461
|
+
case action
|
462
|
+
when :list, :remove then on(host, "crontab #{args} #{user}", &block)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
# Create a temp directory on remote host owned by specified user.
|
469
|
+
#
|
470
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
471
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
472
|
+
# @param [String] path_prefix A remote path prefix for the new temp
|
473
|
+
# directory.
|
474
|
+
# @param [String] user The name of user that should own the temp
|
475
|
+
# directory. If no username is specified defaults to the currently logged in user
|
476
|
+
# per host
|
477
|
+
#
|
478
|
+
# @return [String, Array<String>] Returns the name of the newly-created dir, or an array
|
479
|
+
# of names of newly-created dirs per-host
|
480
|
+
def create_tmpdir_on(host, path_prefix = '', user=nil)
|
481
|
+
|
482
|
+
block_on host do | host |
|
483
|
+
# use default user logged into this host
|
484
|
+
if not user
|
485
|
+
user = host['user']
|
486
|
+
end
|
487
|
+
|
488
|
+
if not on(host, "getent passwd #{user}").exit_code == 0
|
489
|
+
raise "User #{user} does not exist on #{host}."
|
490
|
+
end
|
491
|
+
|
492
|
+
if defined? host.tmpdir
|
493
|
+
dir = host.tmpdir(path_prefix)
|
494
|
+
on host, "chown #{user}:#{user} #{dir}"
|
495
|
+
dir
|
496
|
+
else
|
497
|
+
raise "Host platform not supported by `create_tmpdir_on`."
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|