beaker 2.7.1 → 2.8.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 (53) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +121 -2
  3. data/lib/beaker/dsl.rb +2 -2
  4. data/lib/beaker/dsl/helpers.rb +13 -1429
  5. data/lib/beaker/dsl/helpers/facter_helpers.rb +48 -0
  6. data/lib/beaker/dsl/helpers/hiera_helpers.rb +71 -0
  7. data/lib/beaker/dsl/helpers/host_helpers.rb +506 -0
  8. data/lib/beaker/dsl/helpers/puppet_helpers.rb +698 -0
  9. data/lib/beaker/dsl/helpers/tk_helpers.rb +101 -0
  10. data/lib/beaker/dsl/helpers/web_helpers.rb +115 -0
  11. data/lib/beaker/dsl/install_utils.rb +8 -1570
  12. data/lib/beaker/dsl/install_utils/ezbake_utils.rb +256 -0
  13. data/lib/beaker/dsl/install_utils/module_utils.rb +237 -0
  14. data/lib/beaker/dsl/install_utils/pe_utils.rb +518 -0
  15. data/lib/beaker/dsl/install_utils/puppet_utils.rb +722 -0
  16. data/lib/beaker/dsl/outcomes.rb +0 -4
  17. data/lib/beaker/dsl/roles.rb +0 -3
  18. data/lib/beaker/dsl/structure.rb +127 -4
  19. data/lib/beaker/dsl/wrappers.rb +0 -4
  20. data/lib/beaker/host.rb +23 -0
  21. data/lib/beaker/host/unix/pkg.rb +4 -4
  22. data/lib/beaker/host_prebuilt_steps.rb +11 -5
  23. data/lib/beaker/hypervisor/vagrant.rb +1 -0
  24. data/lib/beaker/hypervisor/vmpooler.rb +38 -0
  25. data/lib/beaker/logger.rb +10 -4
  26. data/lib/beaker/network_manager.rb +5 -4
  27. data/lib/beaker/options/command_line_parser.rb +7 -0
  28. data/lib/beaker/shared.rb +2 -1
  29. data/lib/beaker/shared/semvar.rb +41 -0
  30. data/lib/beaker/test_suite.rb +20 -6
  31. data/lib/beaker/version.rb +1 -1
  32. data/spec/beaker/dsl/helpers/facter_helpers_spec.rb +59 -0
  33. data/spec/beaker/dsl/helpers/hiera_helpers_spec.rb +96 -0
  34. data/spec/beaker/dsl/helpers/host_helpers_spec.rb +413 -0
  35. data/spec/beaker/dsl/{helpers_spec.rb → helpers/puppet_helpers_spec.rb} +2 -611
  36. data/spec/beaker/dsl/helpers/tk_helpers_spec.rb +83 -0
  37. data/spec/beaker/dsl/helpers/web_helpers_spec.rb +60 -0
  38. data/spec/beaker/dsl/install_utils/module_utils_spec.rb +241 -0
  39. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +475 -0
  40. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +523 -0
  41. data/spec/beaker/dsl/structure_spec.rb +108 -0
  42. data/spec/beaker/host_prebuilt_steps_spec.rb +44 -0
  43. data/spec/beaker/host_spec.rb +41 -0
  44. data/spec/beaker/hypervisor/vagrant_spec.rb +2 -1
  45. data/spec/beaker/logger_spec.rb +9 -2
  46. data/spec/beaker/network_manager_spec.rb +7 -1
  47. data/spec/beaker/options/command_line_parser_spec.rb +3 -2
  48. data/spec/beaker/shared/semvar_spec.rb +36 -0
  49. data/spec/beaker/test_suite_spec.rb +48 -0
  50. data/spec/mocks.rb +10 -0
  51. metadata +23 -5
  52. data/lib/beaker/dsl/ezbake_utils.rb +0 -259
  53. data/spec/beaker/dsl/install_utils_spec.rb +0 -1242
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ODY4MmZhYzhjMzM5MGVlZmNkYzMwMmM0ZWVmMTIyYjdkMDgyNjZlYQ==
4
+ ZjYxOGQ2OTU1ODFmMGMyYzU5YTE3ZTg2ZTNhYjUzYzVkMTE4ZmZhNg==
5
5
  data.tar.gz: !binary |-
6
- ZDE3Nzk5ODczYWZhMWI5NmM5ZDJlZTZjNDkxZTM0YjhiMDY3MjE5YQ==
6
+ NGU4Y2UxNGI0YmMxZjRiZTRkNmY2ZjljNDlmZTY2YjUzZDg0ZDdkNw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- N2U4NDZjYjFhZjhiMDJmYWU2NzRlOWQzYmVmNmJhMjQxNmI0MWYyOWMyYzc1
10
- YTlmOGU4YjFhYjcwM2M5MDM3ZjUzZDc3ODVhZjZjYjQzZjY1N2I5OWI5Njg0
11
- MDJiYWM3NWJjMzIxNzQ5MmM3ODI0ZmZmMjBkMzY5YTMzNjE5NjE=
9
+ OTYwZTkxZDE1ZjIxZjdjNjcwZWM2OTg2MGM0YWIyZDUyMTEyY2U3OGExY2Jh
10
+ ZGNlNGMyYjA5YjJhOGIyZWE2MmI1NDlhMWM3NzI4YzZlNzkzNjg1MTY4ZWQ2
11
+ MWJmOTFiYjYzYTg1NTFkZDIxZTkxMmJlMzJkNmM3YTJkODE1YzQ=
12
12
  data.tar.gz: !binary |-
13
- NGVkZTc4ODQ0N2Q2OTliOTk0MWE0OWNhMDA3ZjI3YWYyOTY4ZTJkYTkyY2Ix
14
- ODIzODhjMTQ0ZDUwOTM3MmNjMGQzMjExOTAzYzgzNmYzN2I5NjExMzYxYTM2
15
- NDMyMTJiMDYxN2U2MjVhOTUyODFhMjJiMzRlOWMyMmFhNjEzY2U=
13
+ MWYzNWYxNzcwZDI2YTc1ZWFmOGE4M2ZjNjNmMGY5NjRiMDVhMmZhNzE1ODJl
14
+ NjAxN2U5ZmQ4Yjc1NDZkYTkwYzkzZDIxOTY0NDNjZjYyMjdlYTk2NTdjOGRh
15
+ NWI2YmU3YjgyODM0ZDQ3ZjNhMGQyZmMyMDBmZjkyZDU0OTNiNDA=
data/HISTORY.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # default - History
2
2
  ## Tags
3
- * [LATEST - 19 Mar, 2015 (2bf87b79)](#LATEST)
3
+ * [LATEST - 26 Mar, 2015 (f320c276)](#LATEST)
4
+ * [beaker2.7.1 - 19 Mar, 2015 (45b2bf10)](#beaker2.7.1)
4
5
  * [beaker2.7.0 - 19 Mar, 2015 (38b14ef8)](#beaker2.7.0)
5
6
  * [beaker2.6.0 - 12 Mar, 2015 (d4e731ab)](#beaker2.6.0)
6
7
  * [beaker2.5.1 - 4 Mar, 2015 (009c2c63)](#beaker2.5.1)
@@ -75,7 +76,125 @@
75
76
  * [pe1.2 - 6 Sep, 2011 (ba3dadd2)](#pe1.2)
76
77
 
77
78
  ## Details
78
- ### <a name = "LATEST">LATEST - 19 Mar, 2015 (2bf87b79)
79
+ ### <a name = "LATEST">LATEST - 26 Mar, 2015 (f320c276)
80
+
81
+ * (GEM) update beaker version to 2.8.0 (f320c276)
82
+
83
+ * Merge pull request #759 from sschneid/vmpooler_tagging (97052ab4)
84
+
85
+
86
+ ```
87
+ Merge pull request #759 from sschneid/vmpooler_tagging
88
+
89
+ (BKR-155) Add simple tagging to vmpooler hosts
90
+ ```
91
+ * Merge pull request #755 from anodelman/acceptance (0ac1460d)
92
+
93
+
94
+ ```
95
+ Merge pull request #755 from anodelman/acceptance
96
+
97
+ (BKR-77) breakup dsl/helpers and dsl/install_utils
98
+ ```
99
+ * Merge pull request #754 from anodelman/win-fix (f426a7cf)
100
+
101
+
102
+ ```
103
+ Merge pull request #754 from anodelman/win-fix
104
+
105
+ (BKR-151) periodic failure to restart ssh on windows
106
+ ```
107
+ * Merge pull request #734 from kevpl/bkr5_log_prefix (4aaedb45)
108
+
109
+
110
+ ```
111
+ Merge pull request #734 from kevpl/bkr5_log_prefix
112
+
113
+ (BKR-5) added a default log_prefix of the hostfile name, and the --log-p...
114
+ ```
115
+ * Wrap tagging attempt in a begin/rescue/end block (66f4629f)
116
+
117
+ * Merge pull request #745 from puppetlabs/fix_vagrant_insecure (77c14f7b)
118
+
119
+
120
+ ```
121
+ Merge pull request #745 from puppetlabs/fix_vagrant_insecure
122
+
123
+ (BKR-148) Stop vagrant from generating ssh key
124
+ ```
125
+ * Merge pull request #749 from kevpl/bkr79_deprecate_hostproperties (c7a6ef03)
126
+
127
+
128
+ ```
129
+ Merge pull request #749 from kevpl/bkr79_deprecate_hostproperties
130
+
131
+ (BKR-79) added deprecated warning to host defaults
132
+ ```
133
+ * Merge pull request #751 from petems/MAINT-fix-non-cygwin-root-enable (47a942ae)
134
+
135
+
136
+ ```
137
+ Merge pull request #751 from petems/MAINT-fix-non-cygwin-root-enable
138
+
139
+ (MAINT) Fix enable root login on non-cygwin
140
+ ```
141
+ * (BKR-155) Add simple tagging to vmpooler hosts (90d4e088)
142
+
143
+ * (BKR-151) periodic failure to restart ssh on windows (beec5281)
144
+
145
+
146
+ ```
147
+ (BKR-151) periodic failure to restart ssh on windows
148
+
149
+ - wrap ssh stop/start in a repeat loop
150
+ ```
151
+ * (BKR-77) breakup dsl/helpers and dsl/install_utils (73931bf3)
152
+
153
+
154
+ ```
155
+ (BKR-77) breakup dsl/helpers and dsl/install_utils
156
+
157
+ - dsl/helpers.rb and dsl/install_utils.rb have grown too large and no
158
+ longer make sense as discreet units
159
+ - divide install_utils into modules with methods associated with
160
+ different applications (pe, puppet, modules)
161
+ - divide helpers into modules with methods exercising different areas of
162
+ functionality (facter, hiera, host, puppet, web, trapperkeeper)
163
+ - update spec testing stucture to reflect new install_utils/helpers
164
+ stucture
165
+ ```
166
+ * (MAINT) Changes default to show warning (e17321ec)
167
+
168
+
169
+ ```
170
+ (MAINT) Changes default to show warning
171
+
172
+ This means that we won't try to run the sed command on Windows boxes if they have false for cygwin
173
+ ```
174
+ * (MAINT) Adds spec for `enable_root_login` method (1f666bef)
175
+
176
+ * (BKR-79) added deprecated warning to host defaults (f654a4f0)
177
+
178
+ * (BRK-148) Stop vagrant from generating ssh key (d7470ed9)
179
+
180
+
181
+ ```
182
+ (BRK-148) Stop vagrant from generating ssh key
183
+
184
+ Hashicorp added new behavior that generates a new SSH key
185
+ when generating a box, and will remove any existing "insecure"
186
+ key found on a box. See https://github.com/mitchellh/vagrant/pull/4707
187
+
188
+ This has the unfortunate side effect of breaking the automation
189
+ used in Beaker to setup SSH keys, since it relied on the default
190
+ "insecure" vagrant keys being used. Current workaround is to disable
191
+ the new Vagrant behavior with a configuration option.
192
+ ```
193
+ * (BKR-5) added a default log_prefix of the hostfile name, and the --log-prefix CLI option (6ce6cbd4)
194
+
195
+ ### <a name = "beaker2.7.1">beaker2.7.1 - 19 Mar, 2015 (45b2bf10)
196
+
197
+ * (HISTORY) update beaker history for gem release 2.7.1 (45b2bf10)
79
198
 
80
199
  * (GEM) update beaker version to 2.7.1 (2bf87b79)
81
200
 
@@ -1,5 +1,5 @@
1
1
  [ 'install_utils', 'roles', 'outcomes', 'assertions', 'patterns',
2
- 'structure', 'helpers', 'ezbake_utils', 'wrappers' ].each do |lib|
2
+ 'structure', 'helpers', 'wrappers' ].each do |lib|
3
3
  require "beaker/dsl/#{lib}"
4
4
  end
5
5
 
@@ -69,6 +69,7 @@ module Beaker
69
69
  # end
70
70
  # end
71
71
  #
72
+ #@api dsl
72
73
  module DSL
73
74
  include Beaker::DSL::Roles
74
75
  include Beaker::DSL::Outcomes
@@ -76,7 +77,6 @@ module Beaker
76
77
  include Beaker::DSL::Assertions
77
78
  include Beaker::DSL::Wrappers
78
79
  include Beaker::DSL::Helpers
79
- include Beaker::DSL::EZBakeUtils
80
80
  include Beaker::DSL::InstallUtils
81
81
  include Beaker::DSL::Patterns
82
82
  end
@@ -1,19 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
- require 'resolv'
3
- require 'inifile'
4
- require 'timeout'
5
- require 'beaker/dsl/outcomes'
6
- require 'beaker/options'
7
- require 'hocon'
8
- require 'hocon/config_error'
2
+ [ 'facter', 'hiera', 'host', 'puppet', 'tk', 'web' ].each do |lib|
3
+ require "beaker/dsl/helpers/#{lib}_helpers"
4
+ end
9
5
 
10
6
  module Beaker
11
7
  module DSL
12
- # This is the heart of the Puppet Acceptance DSL. Here you find a helper
13
- # to proxy commands to hosts, more commands to move files between hosts
14
- # and execute remote scripts, confine test cases to certain hosts and
15
- # prepare the state of a test case.
16
- #
8
+
9
+ # Contains methods to help you manage and configure your SUTs and configure and interact with puppet, facter
10
+ # and hiera.
11
+
17
12
  # To mix this is into a class you need the following:
18
13
  # * a method *hosts* that yields any hosts implementing
19
14
  # {Beaker::Host}'s interface to act upon.
@@ -25,1424 +20,13 @@ module Beaker
25
20
  # * the module {Beaker::DSL::Wrappers} the provides convenience methods for {Beaker::DSL::Command} creation
26
21
  #
27
22
  #
28
- # @api dsl
29
23
  module Helpers
30
-
31
- # @!macro common_opts
32
- # @param [Hash{Symbol=>String}] opts Options to alter execution.
33
- # @option opts [Boolean] :silent (false) Do not produce log output
34
- # @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
35
- # (or range) of integer exit codes that should be considered
36
- # acceptable. An error will be thrown if the exit code does not
37
- # match one of the values in this list.
38
- # @option opts [Hash{String=>String}] :environment ({}) These will be
39
- # treated as extra environment variables that should be set before
40
- # running the command.
41
- #
42
-
43
- # The primary method for executing commands *on* some set of 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, Command] command The command to execute on *host*.
48
- # @param [Proc] block Additional actions or assertions.
49
- # @!macro common_opts
50
- #
51
- # @example Most basic usage
52
- # on hosts, 'ls /tmp'
53
- #
54
- # @example Allowing additional exit codes to pass
55
- # on agents, 'puppet agent -t', :acceptable_exit_codes => [0,2]
56
- #
57
- # @example Using the returned result for any kind of checking
58
- # if on(host, 'ls -la ~').stdout =~ /\.bin/
59
- # ...do some action...
60
- # end
61
- #
62
- # @example Using TestCase helpers from within a test.
63
- # agents.each do |agent|
64
- # on agent, 'cat /etc/puppet/puppet.conf' do
65
- # assert_match stdout, /server = #{master}/, 'WTF Mate'
66
- # end
67
- # end
68
- #
69
- # @example Using a role (defined in a String) to identify the host
70
- # on "master", "echo hello"
71
- #
72
- # @example Using a role (defined in a Symbol) to identify the host
73
- # on :dashboard, "echo hello"
74
- #
75
- # @return [Result] An object representing the outcome of *command*.
76
- # @raise [FailTest] Raises an exception if *command* obviously fails.
77
- def on(host, command, opts = {}, &block)
78
- block_on host do | host |
79
- cur_command = command
80
- if command.is_a? Command
81
- cur_command = command.cmd_line(host)
82
- end
83
- cmd_opts = {}
84
- #add any additional environment variables to the command
85
- if opts[:environment]
86
- cmd_opts['ENV'] = opts[:environment]
87
- end
88
- @result = host.exec(Command.new(cur_command.to_s, [], cmd_opts), opts)
89
-
90
- # Also, let additional checking be performed by the caller.
91
- if block_given?
92
- case block.arity
93
- #block with arity of 0, just hand back yourself
94
- when 0
95
- yield self
96
- #block with arity of 1 or greater, hand back the result object
97
- else
98
- yield @result
99
- end
100
- end
101
- @result
102
- end
103
- end
104
-
105
- # The method for executing commands on the default host
106
- #
107
- # @param [String, Command] command The command to execute on *host*.
108
- # @param [Proc] block Additional actions or assertions.
109
- # @!macro common_opts
110
- #
111
- # @example Most basic usage
112
- # shell 'ls /tmp'
113
- #
114
- # @example Allowing additional exit codes to pass
115
- # shell 'puppet agent -t', :acceptable_exit_codes => [0,2]
116
- #
117
- # @example Using the returned result for any kind of checking
118
- # if shell('ls -la ~').stdout =~ /\.bin/
119
- # ...do some action...
120
- # end
121
- #
122
- # @example Using TestCase helpers from within a test.
123
- # agents.each do |agent|
124
- # shell('cat /etc/puppet/puppet.conf') do |result|
125
- # assert_match result.stdout, /server = #{master}/, 'WTF Mate'
126
- # end
127
- # end
128
- #
129
- # @return [Result] An object representing the outcome of *command*.
130
- # @raise [FailTest] Raises an exception if *command* obviously fails.
131
- def shell(command, opts = {}, &block)
132
- on(default, command, opts, &block)
133
- end
134
-
135
- # @deprecated
136
- # An proxy for the last {Beaker::Result#stdout} returned by
137
- # a method that makes remote calls. Use the {Beaker::Result}
138
- # object returned by the method directly instead. For Usage see
139
- # {Beaker::Result}.
140
- def stdout
141
- return nil if @result.nil?
142
- @result.stdout
143
- end
144
-
145
- # @deprecated
146
- # An proxy for the last {Beaker::Result#stderr} returned by
147
- # a method that makes remote calls. Use the {Beaker::Result}
148
- # object returned by the method directly instead. For Usage see
149
- # {Beaker::Result}.
150
- def stderr
151
- return nil if @result.nil?
152
- @result.stderr
153
- end
154
-
155
- # @deprecated
156
- # An proxy for the last {Beaker::Result#exit_code} returned by
157
- # a method that makes remote calls. Use the {Beaker::Result}
158
- # object returned by the method directly instead. For Usage see
159
- # {Beaker::Result}.
160
- def exit_code
161
- return nil if @result.nil?
162
- @result.exit_code
163
- end
164
-
165
- # Move a file from a remote to a local path
166
- # @note If using {Beaker::Host} for the hosts *scp* is not
167
- # required on the system as it uses Ruby's net/scp library. The
168
- # net-scp gem however is required (and specified in the gemspec).
169
- #
170
- # @param [Host, #do_scp_from] host One or more hosts (or some object
171
- # that responds like
172
- # {Beaker::Host#do_scp_from}.
173
- # @param [String] from_path A remote path to a file.
174
- # @param [String] to_path A local path to copy *from_path* to.
175
- # @!macro common_opts
176
- #
177
- # @return [Result] Returns the result of the SCP operation
178
- def scp_from host, from_path, to_path, opts = {}
179
- block_on host do | host |
180
- @result = host.do_scp_from(from_path, to_path, opts)
181
- @result.log logger
182
- @result
183
- end
184
- end
185
-
186
- # Move a local file to a remote host using scp
187
- # @note If using {Beaker::Host} for the hosts *scp* is not
188
- # required on the system as it uses Ruby's net/scp library. The
189
- # net-scp gem however is required (and specified in the gemspec.
190
- # When using SCP with Windows it will now auto expand path when
191
- # using `cygpath instead of failing or requiring full path
192
- #
193
- # @param [Host, #do_scp_to] host One or more hosts (or some object
194
- # that responds like
195
- # {Beaker::Host#do_scp_to}.
196
- # @param [String] from_path A local path to a file.
197
- # @param [String] to_path A remote path to copy *from_path* to.
198
- # @!macro common_opts
199
- #
200
- # @return [Result] Returns the result of the SCP operation
201
- def scp_to host, from_path, to_path, opts = {}
202
- block_on host do | host |
203
- if host['platform'] =~ /windows/ && to_path.match('`cygpath')
204
- result = on host, "echo #{to_path}"
205
- to_path = result.raw_output.chomp
206
- end
207
- @result = host.do_scp_to(from_path, to_path, opts)
208
- @result.log logger
209
- @result
210
- end
211
- end
212
-
213
- # Move a local file or directory to a remote host using rsync
214
- # @note rsync is required on the local host.
215
- #
216
- # @param [Host, #do_scp_to] host A host object that responds like
217
- # {Beaker::Host}.
218
- # @param [String] from_path A local path to a file or directory.
219
- # @param [String] to_path A remote path to copy *from_path* to.
220
- # @!macro common_opts
221
- #
222
- # @return [Result] Returns the result of the rsync operation
223
- def rsync_to host, from_path, to_path, opts = {}
224
- block_on host do | host |
225
- if host['platform'] =~ /windows/ && to_path.match('`cygpath')
226
- result = host.echo "#{to_path}"
227
- to_path = result.raw_output.chomp
228
- end
229
- @result = host.do_rsync_to(from_path, to_path, opts)
230
- @result
231
- end
232
- end
233
-
234
- # Deploy packaging configurations generated by
235
- # https://github.com/puppetlabs/packaging to a host.
236
- #
237
- # @note To ensure the repo configs are available for deployment,
238
- # you should run `rake pl:jenkins:deb_repo_configs` and
239
- # `rake pl:jenkins:rpm_repo_configs` on your project checkout
240
- #
241
- # @param [Host] host
242
- # @param [String] path The path to the generated repository config
243
- # files. ex: /myproject/pkg/repo_configs
244
- # @param [String] name A human-readable name for the repository
245
- # @param [String] version The version of the project, as used by the
246
- # packaging tools. This can be determined with
247
- # `rake pl:print_build_params` from the packaging
248
- # repo.
249
- def deploy_package_repo host, path, name, version
250
- host.deploy_package_repo path, name, version
251
- end
252
-
253
- # Create a remote file out of a string
254
- # @note This method uses Tempfile in Ruby's STDLIB as well as {#scp_to}.
255
- #
256
- # @param [Host, #do_scp_to] hosts One or more hosts (or some object
257
- # that responds like
258
- # {Beaker::Host#do_scp_from}.
259
- # @param [String] file_path A remote path to place *file_content* at.
260
- # @param [String] file_content The contents of the file to be placed.
261
- # @!macro common_opts
262
- # @option opts [String] :protocol Name of the underlying transfer method.
263
- # Valid options are 'scp' or 'rsync'.
264
- #
265
- # @return [Result] Returns the result of the underlying SCP operation.
266
- def create_remote_file(hosts, file_path, file_content, opts = {})
267
- Tempfile.open 'beaker' do |tempfile|
268
- File.open(tempfile.path, 'w') {|file| file.puts file_content }
269
-
270
- opts[:protocol] ||= 'scp'
271
- case opts[:protocol]
272
- when 'scp'
273
- scp_to hosts, tempfile.path, file_path, opts
274
- when 'rsync'
275
- rsync_to hosts, tempfile.path, file_path, opts
276
- else
277
- logger.debug "Unsupported transfer protocol, returning nil"
278
- nil
279
- end
280
- end
281
- end
282
-
283
- # Create a temp directory on remote host owned by specified user.
284
- #
285
- # @param [Host] host A single remote host on which to create and adjust
286
- # the ownership of a temp directory.
287
- # @param [String] name A remote path prefix for the new temp
288
- # directory. Default value is '/tmp/beaker'
289
- # @param [String] user The name of user that should own the temp
290
- # directory. If no username is specified, use `puppet master
291
- # --configprint user` to obtain username from master. Raise RuntimeError
292
- # if this puppet command returns a non-zero exit code.
293
- #
294
- # @return [String] Returns the name of the newly-created file.
295
- def create_tmpdir_for_user(host, name='/tmp/beaker', user=nil)
296
- if not user
297
- result = on host, puppet("master --configprint user")
298
- if not result.exit_code == 0
299
- raise "`puppet master --configprint` failed, check that puppet is installed on #{host} or explicitly pass in a user name."
300
- end
301
- user = result.stdout.strip
302
- end
303
-
304
- if not on(host, "getent passwd #{user}").exit_code == 0
305
- raise "User #{user} does not exist on #{host}."
306
- end
307
-
308
- if defined? host.tmpdir
309
- dir = host.tmpdir(name)
310
- on host, "chown #{user}:#{user} #{dir}"
311
- return dir
312
- else
313
- raise "Host platform not supported by `create_tmpdir_for_user`."
314
- end
315
- end
316
-
317
- # Move a local script to a remote host and execute it
318
- # @note this relies on {#on} and {#scp_to}
319
- #
320
- # @param [Host, #do_scp_to] host One or more hosts (or some object
321
- # that responds like
322
- # {Beaker::Host#do_scp_from}.
323
- # @param [String] script A local path to find an executable script at.
324
- # @!macro common_opts
325
- # @param [Proc] block Additional tests to run after script has executed
326
- #
327
- # @return [Result] Returns the result of the underlying SCP operation.
328
- def run_script_on(host, script, opts = {}, &block)
329
- # this is unsafe as it uses the File::SEPARATOR will be set to that
330
- # of the coordinator node. This works for us because we use cygwin
331
- # which will properly convert the paths. Otherwise this would not
332
- # work for running tests on a windows machine when the coordinator
333
- # that the harness is running on is *nix. We should use
334
- # {Beaker::Host#temp_path} instead. TODO
335
- remote_path = File.join("", "tmp", File.basename(script))
336
-
337
- scp_to host, script, remote_path
338
- on host, remote_path, opts, &block
339
- end
340
-
341
- # Move a local script to default host and execute it
342
- # @see #run_script_on
343
- def run_script(script, opts = {}, &block)
344
- run_script_on(default, script, opts, &block)
345
- end
346
-
347
- # Limit the hosts a test case is run against
348
- # @note This will modify the {Beaker::TestCase#hosts} member
349
- # in place unless an array of hosts is passed into it and
350
- # {Beaker::TestCase#logger} yielding an object that responds
351
- # like {Beaker::Logger#warn}, as well as
352
- # {Beaker::DSL::Outcomes#skip_test}, and optionally
353
- # {Beaker::TestCase#hosts}.
354
- #
355
- # @param [Symbol] type The type of confinement to do. Valid parameters
356
- # are *:to* to confine the hosts to only those that
357
- # match *criteria* or *:except* to confine the test
358
- # case to only those hosts that do not match
359
- # criteria.
360
- # @param [Hash{Symbol,String=>String,Regexp,Array<String,Regexp>}]
361
- # criteria Specify the criteria with which a host should be
362
- # considered for inclusion or exclusion. The key is any attribute
363
- # of the host that will be yielded by {Beaker::Host#[]}.
364
- # The value can be any string/regex or array of strings/regexp.
365
- # The values are compared using [Enumerable#any?] so that if one
366
- # value of an array matches the host is considered a match for that
367
- # criteria.
368
- # @param [Array<Host>] host_array This creatively named parameter is
369
- # an optional array of hosts to confine to. If not passed in, this
370
- # method will modify {Beaker::TestCase#hosts} in place.
371
- # @param [Proc] block Addition checks to determine suitability of hosts
372
- # for confinement. Each host that is still valid after checking
373
- # *criteria* is then passed in turn into this block. The block
374
- # should return true if the host matches this additional criteria.
375
- #
376
- # @example Basic usage to confine to debian OSes.
377
- # confine :to, :platform => 'debian'
378
- #
379
- # @example Confining to anything but Windows and Solaris
380
- # confine :except, :platform => ['windows', 'solaris']
381
- #
382
- # @example Using additional block to confine to Solaris global zone.
383
- # confine :to, :platform => 'solaris' do |solaris|
384
- # on( solaris, 'zonename' ) =~ /global/
385
- # end
386
- #
387
- # @return [Array<Host>] Returns an array of hosts that are still valid
388
- # targets for this tests case.
389
- # @raise [SkipTest] Raises skip test if there are no valid hosts for
390
- # this test case after confinement.
391
- def confine(type, criteria, host_array = nil, &block)
392
- hosts_to_modify = host_array || hosts
393
- case type
394
- when :except
395
- hosts_to_modify = hosts_to_modify - select_hosts(criteria, hosts_to_modify, &block)
396
- when :to
397
- hosts_to_modify = select_hosts(criteria, hosts_to_modify, &block)
398
- else
399
- raise "Unknown option #{type}"
400
- end
401
- if hosts_to_modify.empty?
402
- logger.warn "No suitable hosts with: #{criteria.inspect}"
403
- skip_test 'No suitable hosts found'
404
- end
405
- self.hosts = hosts_to_modify
406
- hosts_to_modify
407
- end
408
-
409
- # Ensures that host restrictions as specifid by type, criteria and
410
- # host_array are confined to activity within the passed block.
411
- # TestCase#hosts is reset after block has executed.
412
- #
413
- # @see #confine
414
- def confine_block(type, criteria, host_array = nil, &block)
415
- begin
416
- original_hosts = self.hosts.dup
417
- confine(type, criteria, host_array)
418
-
419
- yield
420
-
421
- ensure
422
- self.hosts = original_hosts
423
- end
424
- end
425
-
426
- #Return a set of hosts that meet the given criteria
427
- # @param [Hash{Symbol,String=>String,Regexp,Array<String,Regexp>}]
428
- # criteria Specify the criteria with which a host should be
429
- # considered for inclusion. The key is any attribute
430
- # of the host that will be yielded by {Beaker::Host#[]}.
431
- # The value can be any string/regex or array of strings/regexp.
432
- # The values are compared using [Enumerable#any?] so that if one
433
- # value of an array matches the host is considered a match for that
434
- # criteria.
435
- # @param [Array<Host>] host_array This creatively named parameter is
436
- # an optional array of hosts to confine to. If not passed in, this
437
- # method will modify {Beaker::TestCase#hosts} in place.
438
- # @param [Proc] block Addition checks to determine suitability of hosts
439
- # for selection. Each host that is still valid after checking
440
- # *criteria* is then passed in turn into this block. The block
441
- # should return true if the host matches this additional criteria.
442
- #
443
- # @return [Array<Host>] Returns an array of hosts that meet the provided criteria
444
- def select_hosts(criteria, host_array = nil, &block)
445
- hosts_to_select_from = host_array || hosts
446
- criteria.each_pair do |property, value|
447
- hosts_to_select_from = hosts_to_select_from.select do |host|
448
- inspect_host host, property, value
449
- end
450
- end
451
- if block_given?
452
- hosts_to_select_from = hosts_to_select_from.select do |host|
453
- yield host
454
- end
455
- end
456
- hosts_to_select_from
457
- end
458
-
459
- # Return the name of the puppet user.
460
- #
461
- # @param [Host] host One object that acts like a Beaker::Host
462
- #
463
- # @note This method assumes puppet is installed on the host.
464
- #
465
- def puppet_user(host)
466
- return host.puppet('master')['user']
467
- end
468
-
469
- # Return the name of the puppet group.
470
- #
471
- # @param [Host] host One object that acts like a Beaker::Host
472
- #
473
- # @note This method assumes puppet is installed on the host.
474
- #
475
- def puppet_group(host)
476
- return host.puppet('master')['group']
477
- end
478
-
479
- # @!visibility private
480
- def inspect_host(host, property, one_or_more_values)
481
- values = Array(one_or_more_values)
482
- return values.any? do |value|
483
- true_false = false
484
- case value
485
- when String
486
- true_false = host[property.to_s].include? value
487
- when Regexp
488
- true_false = host[property.to_s] =~ value
489
- end
490
- true_false
491
- end
492
- end
493
-
494
-
495
- # Test Puppet running in a certain run mode with specific options.
496
- # This ensures the following steps are performed:
497
- # 1. The pre-test Puppet configuration is backed up
498
- # 2. A new Puppet configuraton file is layed down
499
- # 3. Puppet is started or restarted in the specified run mode
500
- # 4. Ensure Puppet has started correctly
501
- # 5. Further tests are yielded to
502
- # 6. Revert Puppet to the pre-test state
503
- # 7. Testing artifacts are saved in a folder named for the test
504
- #
505
- # @param [Host] host One object that act like Host
506
- #
507
- # @param [Hash{Symbol=>String}] conf_opts Represents puppet settings.
508
- # Sections of the puppet.conf may be
509
- # specified, if no section is specified the
510
- # a puppet.conf file will be written with the
511
- # options put in a section named after [mode]
512
- # @option conf_opts [String] :__commandline_args__ A special setting for
513
- # command_line arguments such as --debug or
514
- # --logdest, which cannot be set in
515
- # puppet.conf. For example:
516
- #
517
- # :__commandline_args__ => '--logdest /tmp/a.log'
518
- #
519
- # These will only be applied when starting a FOSS
520
- # master, as a pe master is just bounced.
521
- # @option conf_opts [Hash] :__service_args__ A special setting of options
522
- # for controlling how the puppet master service is
523
- # handled. The only setting currently is
524
- # :bypass_service_script, which if set true will
525
- # force stopping and starting a webrick master
526
- # using the start_puppet_from_source_* methods,
527
- # even if it seems the host has passenger.
528
- # This is needed in FOSS tests to initialize
529
- # SSL.
530
- # @param [File] testdir The temporary directory which will hold backup
531
- # configuration, and other test artifacts.
532
- #
533
- # @param [Block] block The point of this method, yields so
534
- # tests may be ran. After the block is finished
535
- # puppet will revert to a previous state.
536
- #
537
- # @example A simple use case to ensure a master is running
538
- # with_puppet_running_on( master ) do
539
- # ...tests that require a master...
540
- # end
541
- #
542
- # @example Fully utilizing the possiblities of config options
543
- # with_puppet_running_on( master,
544
- # :main => {:logdest => '/var/blah'},
545
- # :master => {:masterlog => '/elswhere'},
546
- # :agent => {:server => 'localhost'} ) do
547
- #
548
- # ...tests to be ran...
549
- # end
550
- #
551
- # @api dsl
552
- def with_puppet_running_on host, conf_opts, testdir = host.tmpdir(File.basename(@path)), &block
553
- raise(ArgumentError, "with_puppet_running_on's conf_opts must be a Hash. You provided a #{conf_opts.class}: '#{conf_opts}'") if !conf_opts.kind_of?(Hash)
554
- cmdline_args = conf_opts[:__commandline_args__]
555
- service_args = conf_opts[:__service_args__] || {}
556
- conf_opts = conf_opts.reject { |k,v| [:__commandline_args__, :__service_args__].include?(k) }
557
-
558
- curl_retries = host['master-start-curl-retries'] || options['master-start-curl-retries']
559
- logger.debug "Setting curl retries to #{curl_retries}"
560
-
561
- if options[:is_puppetserver]
562
- confdir = host.puppet('master')['confdir']
563
- vardir = host.puppet('master')['vardir']
564
-
565
- if cmdline_args
566
- split_args = cmdline_args.split()
567
-
568
- split_args.each do |arg|
569
- case arg
570
- when /--confdir=(.*)/
571
- confdir = $1
572
- when /--vardir=(.*)/
573
- vardir = $1
574
- end
575
- end
576
- end
577
-
578
- puppetserver_opts = { "jruby-puppet" => {
579
- "master-conf-dir" => confdir,
580
- "master-var-dir" => vardir,
581
- }}
582
-
583
- puppetserver_conf = File.join("#{host['puppetserver-confdir']}", "puppetserver.conf")
584
- modify_tk_config(host, puppetserver_conf, puppetserver_opts)
585
- end
586
-
587
- begin
588
- backup_file = backup_the_file(host, host.puppet('master')['confdir'], testdir, 'puppet.conf')
589
- lay_down_new_puppet_conf host, conf_opts, testdir
590
-
591
- if host.use_service_scripts? && !service_args[:bypass_service_script]
592
- bounce_service( host, host['puppetservice'], curl_retries )
593
- else
594
- puppet_master_started = start_puppet_from_source_on!( host, cmdline_args )
595
- end
596
-
597
- yield self if block_given?
598
-
599
- rescue Beaker::DSL::Assertions, Minitest::Assertion => early_assertion
600
- fail_test(early_assertion)
601
- rescue Exception => early_exception
602
- original_exception = RuntimeError.new("PuppetAcceptance::DSL::Helpers.with_puppet_running_on failed (check backtrace for location) because: #{early_exception}\n#{early_exception.backtrace.join("\n")}\n")
603
- raise(original_exception)
604
-
605
- ensure
606
- begin
607
-
608
- if host.use_service_scripts? && !service_args[:bypass_service_script]
609
- restore_puppet_conf_from_backup( host, backup_file )
610
- bounce_service( host, host['puppetservice'], curl_retries )
611
- else
612
- if puppet_master_started
613
- stop_puppet_from_source_on( host )
614
- else
615
- dump_puppet_log(host)
616
- end
617
- restore_puppet_conf_from_backup( host, backup_file )
618
- end
619
-
620
- rescue Exception => teardown_exception
621
- begin
622
- if !host.is_pe?
623
- dump_puppet_log(host)
624
- end
625
- rescue Exception => dumping_exception
626
- logger.error("Raised during attempt to dump puppet logs: #{dumping_exception}")
627
- end
628
-
629
- if original_exception
630
- logger.error("Raised during attempt to teardown with_puppet_running_on: #{teardown_exception}\n---\n")
631
- raise original_exception
632
- else
633
- raise teardown_exception
634
- end
635
- end
636
- end
637
- end
638
-
639
- # Test Puppet running in a certain run mode with specific options,
640
- # on the default host
641
- # @api dsl
642
- # @see #with_puppet_running_on
643
- def with_puppet_running conf_opts, testdir = host.tmpdir(File.basename(@path)), &block
644
- with_puppet_running_on(default, conf_opts, testdir, &block)
645
- end
646
-
647
- # @!visibility private
648
- def restore_puppet_conf_from_backup( host, backup_file )
649
- puppet_conf = host.puppet('master')['config']
650
-
651
- if backup_file
652
- host.exec( Command.new( "if [ -f '#{backup_file}' ]; then " +
653
- "cat '#{backup_file}' > " +
654
- "'#{puppet_conf}'; " +
655
- "rm -f '#{backup_file}'; " +
656
- "fi" ) )
657
- else
658
- host.exec( Command.new( "rm -f '#{puppet_conf}'" ))
659
- end
660
-
661
- end
662
-
663
- # Back up the given file in the current_dir to the new_dir
664
- #
665
- # @!visibility private
666
- #
667
- # @param host [Beaker::Host] The target host
668
- # @param current_dir [String] The directory containing the file to back up
669
- # @param new_dir [String] The directory to copy the file to
670
- # @param filename [String] The file to back up. Defaults to 'puppet.conf'
671
- #
672
- # @return [String, nil] The path to the file if the file exists, nil if it
673
- # doesn't exist.
674
- def backup_the_file host, current_dir, new_dir, filename = 'puppet.conf'
675
-
676
- old_location = current_dir + '/' + filename
677
- new_location = new_dir + '/' + filename + '.bak'
678
-
679
- if host.file_exist? old_location
680
- host.exec( Command.new( "cp #{old_location} #{new_location}" ) )
681
- return new_location
682
- else
683
- logger.warn "Could not backup file '#{old_location}': no such file"
684
- nil
685
- end
686
- end
687
-
688
- # @!visibility private
689
- def start_puppet_from_source_on! host, args = ''
690
- host.exec( puppet( 'master', args ) )
691
-
692
- logger.debug 'Waiting for the puppet master to start'
693
- unless port_open_within?( host, 8140, 10 )
694
- raise Beaker::DSL::FailTest, 'Puppet master did not start in a timely fashion'
695
- end
696
- logger.debug 'The puppet master has started'
697
- return true
698
- end
699
-
700
- # @!visibility private
701
- def stop_puppet_from_source_on( host )
702
- pid = host.exec( Command.new('cat `puppet master --configprint pidfile`') ).stdout.chomp
703
- host.exec( Command.new( "kill #{pid}" ) )
704
- Timeout.timeout(10) do
705
- while host.exec( Command.new( "kill -0 #{pid}"), :acceptable_exit_codes => [0,1] ).exit_code == 0 do
706
- # until kill -0 finds no process and we know that puppet has finished cleaning up
707
- sleep 1
708
- end
709
- end
710
- end
711
-
712
- # @!visibility private
713
- def dump_puppet_log(host)
714
- syslogfile = case host['platform']
715
- when /fedora|centos|el|redhat|scientific/ then '/var/log/messages'
716
- when /ubuntu|debian|cumulus/ then '/var/log/syslog'
717
- else return
718
- end
719
-
720
- logger.notify "\n*************************"
721
- logger.notify "* Dumping master log *"
722
- logger.notify "*************************"
723
- host.exec( Command.new( "tail -n 100 #{syslogfile}" ), :acceptable_exit_codes => [0,1])
724
- logger.notify "*************************\n"
725
- end
726
-
727
- # @!visibility private
728
- def lay_down_new_puppet_conf( host, configuration_options, testdir )
729
- puppetconf_main = host.puppet('master')['config']
730
- puppetconf_filename = File.basename(puppetconf_main)
731
- puppetconf_test = File.join(testdir, puppetconf_filename)
732
-
733
- new_conf = puppet_conf_for( host, configuration_options )
734
- create_remote_file host, puppetconf_test, new_conf.to_s
735
-
736
- host.exec(
737
- Command.new( "cat #{puppetconf_test} > #{puppetconf_main}" ),
738
- :silent => true
739
- )
740
- host.exec( Command.new( "cat #{puppetconf_main}" ) )
741
- end
742
-
743
- # @!visibility private
744
- def puppet_conf_for host, conf_opts
745
- puppetconf = host.exec( Command.new( "cat #{host.puppet('master')['config']}" ) ).stdout
746
- new_conf = IniFile.new( puppetconf ).merge( conf_opts )
747
-
748
- new_conf
749
- end
750
-
751
- # Modify the given TrapperKeeper config file.
752
- #
753
- # @param [Host] host A host object
754
- # @param [OptionsHash] options_hash New hash which will be merged into
755
- # the given TrapperKeeper config.
756
- # @param [String] config_file_path Path to the TrapperKeeper config on
757
- # the given host which is to be
758
- # modified.
759
- # @param [Bool] replace If set true, instead of updating the existing
760
- # TrapperKeeper configuration, replace it entirely
761
- # with the contents of the given hash.
762
- #
763
- # @note TrapperKeeper config files can be HOCON, JSON, or Ini. We don't
764
- # particularly care which of these the file named by `config_file_path` on
765
- # the SUT actually is, just that the contents can be parsed into a map.
766
- #
767
- def modify_tk_config(host, config_file_path, options_hash, replace=false)
768
- if options_hash.empty?
769
- return nil
770
- end
771
-
772
- new_hash = Beaker::Options::OptionsHash.new
773
-
774
- if replace
775
- new_hash.merge!(options_hash)
776
- else
777
- if not host.file_exist?( config_file_path )
778
- raise "Error: #{config_file_path} does not exist on #{host}"
779
- end
780
- file_string = host.exec( Command.new( "cat #{config_file_path}" )).stdout
781
-
782
- begin
783
- tk_conf_hash = read_tk_config_string(file_string)
784
- rescue RuntimeError
785
- raise "Error reading trapperkeeper config: #{config_file_path} at host: #{host}"
786
- end
787
-
788
- new_hash.merge!(tk_conf_hash)
789
- new_hash.merge!(options_hash)
790
- end
791
-
792
- file_string = JSON.dump(new_hash)
793
- create_remote_file host, config_file_path, file_string
794
- end
795
-
796
- # The Trapperkeeper config service will accept HOCON (aka typesafe), JSON,
797
- # or Ini configuration files which means we need to safely handle the the
798
- # exceptions that might come from parsing the given string with the wrong
799
- # parser and fall back to the next valid parser in turn. We finally raise
800
- # a RuntimeException if none of the parsers succeed.
801
- #
802
- # @!visibility private
803
- def read_tk_config_string( string )
804
- begin
805
- return Hocon.parse(string)
806
- rescue Hocon::ConfigError
807
- nil
808
- end
809
-
810
- begin
811
- return JSON.parse(string)
812
- rescue JSON::JSONError
813
- nil
814
- end
815
-
816
- begin
817
- return IniFile.new(string)
818
- rescue IniFile::Error
819
- nil
820
- end
821
-
822
- raise "Failed to read TrapperKeeper config!"
823
- end
824
-
825
- # @!visibility private
826
- def bounce_service host, service, curl_retries = 120
827
- if host.graceful_restarts?
828
- apachectl_path = host.is_pe? ? "#{host['puppetsbindir']}/apache2ctl" : 'apache2ctl'
829
- host.exec(Command.new("#{apachectl_path} graceful"))
830
- else
831
- host.exec puppet_resource('service', service, 'ensure=stopped')
832
- host.exec puppet_resource('service', service, 'ensure=running')
833
- end
834
- curl_with_retries(" #{service} ", host, "https://localhost:8140", [35, 60], curl_retries)
835
- end
836
-
837
- # Blocks until the port is open on the host specified, returns false
838
- # on failure
839
- def port_open_within?( host, port = 8140, seconds = 120 )
840
- repeat_for( seconds ) do
841
- host.port_open?( port )
842
- end
843
- end
844
-
845
- # Runs 'puppet apply' on a remote host, piping manifest through stdin
846
- #
847
- # @param [Host] host The host that this command should be run on
848
- #
849
- # @param [String] manifest The puppet manifest to apply
850
- #
851
- # @!macro common_opts
852
- # @option opts [Boolean] :parseonly (false) If this key is true, the
853
- # "--parseonly" command line parameter will
854
- # be passed to the 'puppet apply' command.
855
- #
856
- # @option opts [Boolean] :trace (false) If this key exists in the Hash,
857
- # the "--trace" command line parameter will be
858
- # passed to the 'puppet apply' command.
859
- #
860
- # @option opts [Array<Integer>] :acceptable_exit_codes ([0]) The list of exit
861
- # codes that will NOT raise an error when found upon
862
- # command completion. If provided, these values will
863
- # be combined with those used in :catch_failures and
864
- # :expect_failures to create the full list of
865
- # passing exit codes.
866
- #
867
- # @option opts [Hash] :environment Additional environment variables to be
868
- # passed to the 'puppet apply' command
869
- #
870
- # @option opts [Boolean] :catch_failures (false) By default `puppet
871
- # --apply` will exit with 0, which does not count
872
- # as a test failure, even if there were errors or
873
- # changes when applying the manifest. This option
874
- # enables detailed exit codes and causes a test
875
- # failure if `puppet --apply` indicates there was
876
- # a failure during its execution.
877
- #
878
- # @option opts [Boolean] :catch_changes (false) This option enables
879
- # detailed exit codes and causes a test failure
880
- # if `puppet --apply` indicates that there were
881
- # changes or failures during its execution.
882
- #
883
- # @option opts [Boolean] :expect_changes (false) This option enables
884
- # detailed exit codes and causes a test failure
885
- # if `puppet --apply` indicates that there were
886
- # no resource changes during its execution.
887
- #
888
- # @option opts [Boolean] :expect_failures (false) This option enables
889
- # detailed exit codes and causes a test failure
890
- # if `puppet --apply` indicates there were no
891
- # failure during its execution.
892
- #
893
- # @option opts [Boolean] :future_parser (false) This option enables
894
- # the future parser option that is available
895
- # from Puppet verion 3.2
896
- # By default it will use the 'current' parser.
897
- #
898
- # @option opts [Boolean] :noop (false) If this option exists, the
899
- # the "--noop" command line parameter will be
900
- # passed to the 'puppet apply' command.
901
- #
902
- # @option opts [String] :modulepath The search path for modules, as
903
- # a list of directories separated by the system
904
- # path separator character. (The POSIX path separator
905
- # is ‘:’, and the Windows path separator is ‘;’.)
906
- #
907
- # @option opts [String] :debug (false) If this option exists,
908
- # the "--debug" command line parameter
909
- # will be passed to the 'puppet apply' command.
910
- #
911
- # @param [Block] block This method will yield to a block of code passed
912
- # by the caller; this can be used for additional
913
- # validation, etc.
914
- #
915
- def apply_manifest_on(host, manifest, opts = {}, &block)
916
- block_on host do | host |
917
- on_options = {}
918
- on_options[:acceptable_exit_codes] = Array(opts[:acceptable_exit_codes])
919
-
920
- puppet_apply_opts = {}
921
- if opts[:debug]
922
- puppet_apply_opts[:debug] = nil
923
- else
924
- puppet_apply_opts[:verbose] = nil
925
- end
926
- puppet_apply_opts[:parseonly] = nil if opts[:parseonly]
927
- puppet_apply_opts[:trace] = nil if opts[:trace]
928
- puppet_apply_opts[:parser] = 'future' if opts[:future_parser]
929
- puppet_apply_opts[:modulepath] = opts[:modulepath] if opts[:modulepath]
930
- puppet_apply_opts[:noop] = nil if opts[:noop]
931
-
932
- # From puppet help:
933
- # "... an exit code of '2' means there were changes, an exit code of
934
- # '4' means there were failures during the transaction, and an exit
935
- # code of '6' means there were both changes and failures."
936
- if [opts[:catch_changes],opts[:catch_failures],opts[:expect_failures],opts[:expect_changes]].compact.length > 1
937
- raise(ArgumentError,
938
- 'Cannot specify more than one of `catch_failures`, ' +
939
- '`catch_changes`, `expect_failures`, or `expect_changes` ' +
940
- 'for a single manifest')
941
- end
942
-
943
- if opts[:catch_changes]
944
- puppet_apply_opts['detailed-exitcodes'] = nil
945
-
946
- # We're after idempotency so allow exit code 0 only.
947
- on_options[:acceptable_exit_codes] |= [0]
948
- elsif opts[:catch_failures]
949
- puppet_apply_opts['detailed-exitcodes'] = nil
950
-
951
- # We're after only complete success so allow exit codes 0 and 2 only.
952
- on_options[:acceptable_exit_codes] |= [0, 2]
953
- elsif opts[:expect_failures]
954
- puppet_apply_opts['detailed-exitcodes'] = nil
955
-
956
- # We're after failures specifically so allow exit codes 1, 4, and 6 only.
957
- on_options[:acceptable_exit_codes] |= [1, 4, 6]
958
- elsif opts[:expect_changes]
959
- puppet_apply_opts['detailed-exitcodes'] = nil
960
-
961
- # We're after changes specifically so allow exit code 2 only.
962
- on_options[:acceptable_exit_codes] |= [2]
963
- else
964
- # Either use the provided acceptable_exit_codes or default to [0]
965
- on_options[:acceptable_exit_codes] |= [0]
966
- end
967
-
968
- # Not really thrilled with this implementation, might want to improve it
969
- # later. Basically, there is a magic trick in the constructor of
970
- # PuppetCommand which allows you to pass in a Hash for the last value in
971
- # the *args Array; if you do so, it will be treated specially. So, here
972
- # we check to see if our caller passed us a hash of environment variables
973
- # that they want to set for the puppet command. If so, we set the final
974
- # value of *args to a new hash with just one entry (the value of which
975
- # is our environment variables hash)
976
- if opts.has_key?(:environment)
977
- puppet_apply_opts['ENV'] = opts[:environment]
978
- end
979
-
980
- file_path = host.tmpfile('apply_manifest.pp')
981
- create_remote_file(host, file_path, manifest + "\n")
982
-
983
- if host[:default_apply_opts].respond_to? :merge
984
- puppet_apply_opts = host[:default_apply_opts].merge( puppet_apply_opts )
985
- end
986
-
987
- on host, puppet('apply', file_path, puppet_apply_opts), on_options, &block
988
- end
989
- end
990
-
991
- # Runs 'puppet apply' on default host, piping manifest through stdin
992
- # @see #apply_manifest_on
993
- def apply_manifest(manifest, opts = {}, &block)
994
- apply_manifest_on(default, manifest, opts, &block)
995
- end
996
-
997
- # @deprecated
998
- def run_agent_on(host, arg='--no-daemonize --verbose --onetime --test',
999
- options={}, &block)
1000
- block_on host do | host |
1001
- on host, puppet_agent(arg), options, &block
1002
- end
1003
- end
1004
-
1005
- # FIX: this should be moved into host/platform
1006
- # @visibility private
1007
- def run_cron_on(host, action, user, entry="", &block)
1008
- block_on host do | host |
1009
- platform = host['platform']
1010
- if platform.include?('solaris') || platform.include?('aix') then
1011
- case action
1012
- when :list then args = '-l'
1013
- when :remove then args = '-r'
1014
- when :add
1015
- on( host,
1016
- "echo '#{entry}' > /var/spool/cron/crontabs/#{user}",
1017
- &block )
1018
- end
1019
-
1020
- else # default for GNU/Linux platforms
1021
- case action
1022
- when :list then args = '-l -u'
1023
- when :remove then args = '-r -u'
1024
- when :add
1025
- on( host,
1026
- "echo '#{entry}' > /tmp/#{user}.cron && " +
1027
- "crontab -u #{user} /tmp/#{user}.cron",
1028
- &block )
1029
- end
1030
- end
1031
-
1032
- if args
1033
- case action
1034
- when :list, :remove then on(host, "crontab #{args} #{user}", &block)
1035
- end
1036
- end
1037
- end
1038
- end
1039
-
1040
- # This method using the puppet resource 'host' will setup host aliases
1041
- # and register the remove of host aliases via Beaker::TestCase#teardown
1042
- #
1043
- # A teardown step is also added to make sure unstubbing of the host is
1044
- # removed always.
1045
- #
1046
- # @param [Host, Array<Host>, String, Symbol] machine One or more hosts to act upon,
1047
- # or a role (String or Symbol) that identifies one or more hosts.
1048
- # @param ip_spec [Hash{String=>String}] a hash containing the host to ip
1049
- # mappings
1050
- # @example Stub puppetlabs.com on the master to 127.0.0.1
1051
- # stub_hosts_on(master, 'puppetlabs.com' => '127.0.0.1')
1052
- def stub_hosts_on(machine, ip_spec)
1053
- block_on machine do | host |
1054
- ip_spec.each do |address, ip|
1055
- logger.notify("Stubbing address #{address} to IP #{ip} on machine #{host}")
1056
- on( host, puppet('resource', 'host', address, 'ensure=present', "ip=#{ip}") )
1057
- end
1058
-
1059
- teardown do
1060
- ip_spec.each do |address, ip|
1061
- logger.notify("Unstubbing address #{address} to IP #{ip} on machine #{host}")
1062
- on( host, puppet('resource', 'host', address, 'ensure=absent') )
1063
- end
1064
- end
1065
- end
1066
- end
1067
-
1068
- # This method accepts a block and using the puppet resource 'host' will
1069
- # setup host aliases before and after that block.
1070
- #
1071
- # @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
1072
- # or a role (String or Symbol) that identifies one or more hosts.
1073
- # @param ip_spec [Hash{String=>String}] a hash containing the host to ip
1074
- # mappings
1075
- # @example Stub puppetlabs.com on the master to 127.0.0.1
1076
- # with_host_stubbed_on(master, 'forgeapi.puppetlabs.com' => '127.0.0.1') do
1077
- # puppet( "module install puppetlabs-stdlib" )
1078
- # end
1079
- def with_host_stubbed_on(host, ip_spec, &block)
1080
- begin
1081
- block_on host do |host|
1082
- ip_spec.each_pair do |address, ip|
1083
- logger.notify("Stubbing address #{address} to IP #{ip} on machine #{host}")
1084
- on( host, puppet('resource', 'host', address, 'ensure=present', "ip=#{ip}") )
1085
- end
1086
- end
1087
-
1088
- block.call
1089
-
1090
- ensure
1091
- ip_spec.each do |address, ip|
1092
- logger.notify("Unstubbing address #{address} to IP #{ip} on machine #{host}")
1093
- on( host, puppet('resource', 'host', address, 'ensure=absent') )
1094
- end
1095
- end
1096
- end
1097
-
1098
- # This method accepts a block and using the puppet resource 'host' will
1099
- # setup host aliases before and after that block on the default host
1100
- #
1101
- # @example Stub puppetlabs.com on the default host to 127.0.0.1
1102
- # stub_hosts('puppetlabs.com' => '127.0.0.1')
1103
- # @see #stub_hosts_on
1104
- def stub_hosts(ip_spec)
1105
- stub_hosts_on(default, ip_spec)
1106
- end
1107
-
1108
- # This wraps the method `stub_hosts_on` and makes the stub specific to
1109
- # the forge alias.
1110
- #
1111
- # forge api v1 canonical source is forge.puppetlabs.com
1112
- # forge api v3 canonical source is forgeapi.puppetlabs.com
1113
- #
1114
- # @param machine [String] the host to perform the stub on
1115
- # @param forge_host [String] The URL to use as the forge alias, will default to using :forge_host in the
1116
- # global options hash
1117
- def stub_forge_on(machine, forge_host = nil)
1118
- #use global options hash
1119
- forge_host ||= options[:forge_host]
1120
- @forge_ip ||= Resolv.getaddress(forge_host)
1121
- block_on machine do | host |
1122
- stub_hosts_on(host, 'forge.puppetlabs.com' => @forge_ip)
1123
- stub_hosts_on(host, 'forgeapi.puppetlabs.com' => @forge_ip)
1124
- end
1125
- end
1126
-
1127
- # This wraps the method `with_host_stubbed_on` and makes the stub specific to
1128
- # the forge alias.
1129
- #
1130
- # forge api v1 canonical source is forge.puppetlabs.com
1131
- # forge api v3 canonical source is forgeapi.puppetlabs.com
1132
- #
1133
- # @param host [String] the host to perform the stub on
1134
- # @param forge_host [String] The URL to use as the forge alias, will default to using :forge_host in the
1135
- # global options hash
1136
- def with_forge_stubbed_on( host, forge_host = nil, &block )
1137
- #use global options hash
1138
- forge_host ||= options[:forge_host]
1139
- @forge_ip ||= Resolv.getaddress(forge_host)
1140
- with_host_stubbed_on( host,
1141
- {'forge.puppetlabs.com' => @forge_ip,
1142
- 'forgeapi.puppetlabs.com' => @forge_ip},
1143
- &block )
1144
- end
1145
-
1146
- # This wraps `with_forge_stubbed_on` and provides it the default host
1147
- # @see with_forge_stubbed_on
1148
- def with_forge_stubbed( forge_host = nil, &block )
1149
- with_forge_stubbed_on( default, forge_host, &block )
1150
- end
1151
-
1152
- # This wraps the method `stub_hosts` and makes the stub specific to
1153
- # the forge alias.
1154
- #
1155
- # @see #stub_forge_on
1156
- def stub_forge(forge_host = nil)
1157
- #use global options hash
1158
- forge_host ||= options[:forge_host]
1159
- stub_forge_on(default, forge_host)
1160
- end
1161
-
1162
- def sleep_until_puppetdb_started(host)
1163
- curl_with_retries("start puppetdb", host, "http://localhost:8080", 0, 120)
1164
- curl_with_retries("start puppetdb (ssl)",
1165
- host, "https://#{host.node_name}:8081", [35, 60])
1166
- end
1167
-
1168
- def sleep_until_puppetserver_started(host)
1169
- curl_with_retries("start puppetserver (ssl)",
1170
- host, "https://#{host.node_name}:8140", [35, 60])
1171
- end
1172
-
1173
- def sleep_until_nc_started(host)
1174
- curl_with_retries("start nodeclassifier (ssl)",
1175
- host, "https://#{host.node_name}:4433", [35, 60])
1176
- end
1177
-
1178
- def curl_with_retries(desc, host, url, desired_exit_codes, max_retries = 60, retry_interval = 1)
1179
- opts = {
1180
- :desired_exit_codes => desired_exit_codes,
1181
- :max_retries => max_retries,
1182
- :retry_interval => retry_interval
1183
- }
1184
- retry_on(host, "curl -m 1 #{url}", opts)
1185
- end
1186
-
1187
- # This command will execute repeatedly until success or it runs out with an error
1188
- #
1189
- # @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
1190
- # or a role (String or Symbol) that identifies one or more hosts.
1191
- # @param [String, Command] command The command to execute on *host*.
1192
- # @param [Hash{Symbol=>String}] opts Options to alter execution.
1193
- # @param [Proc] block Additional actions or assertions.
1194
- #
1195
- # @option opts [Array<Fixnum>, Fixnum] :desired_exit_codes (0) An array
1196
- # or integer exit code(s) that should be considered
1197
- # acceptable. An error will be thrown if the exit code never
1198
- # matches one of the values in this list.
1199
- # @option opts [Fixnum] :max_retries (60) number of times the
1200
- # command will be tried before failing
1201
- # @option opts [Float] :retry_interval (1) number of seconds
1202
- # that we'll wait between tries
1203
- # @option opts [Boolean] :verbose (false)
1204
- def retry_on(host, command, opts = {}, &block)
1205
- option_exit_codes = opts[:desired_exit_codes]
1206
- option_max_retries = opts[:max_retries].to_i
1207
- option_retry_interval = opts[:retry_interval].to_f
1208
- desired_exit_codes = option_exit_codes ? [option_exit_codes].flatten : [0]
1209
- desired_exit_codes = [0] if desired_exit_codes.empty?
1210
- max_retries = option_max_retries == 0 ? 60 : option_max_retries # nil & "" both return 0
1211
- retry_interval = option_retry_interval == 0 ? 1 : option_retry_interval
1212
- verbose = true.to_s == opts[:verbose]
1213
-
1214
- log_prefix = host.log_prefix
1215
- logger.debug "\n#{log_prefix} #{Time.new.strftime('%H:%M:%S')}$ #{command}"
1216
- logger.debug " Trying command #{max_retries} times."
1217
- logger.debug ".", add_newline=false
1218
-
1219
- result = on host, command, {:acceptable_exit_codes => (0...127), :silent => !verbose}, &block
1220
- num_retries = 0
1221
- until desired_exit_codes.include?(result.exit_code)
1222
- sleep retry_interval
1223
- result = on host, command, {:acceptable_exit_codes => (0...127), :silent => !verbose}, &block
1224
- num_retries += 1
1225
- logger.debug ".", add_newline=false
1226
- if (num_retries > max_retries)
1227
- logger.debug " Command \`#{command}\` failed."
1228
- fail("Command \`#{command}\` failed.")
1229
- end
1230
- end
1231
- logger.debug "\n#{log_prefix} #{Time.new.strftime('%H:%M:%S')}$ #{command} ostensibly successful."
1232
- result
1233
- end
1234
-
1235
- #Is semver-ish version a less than semver-ish version b
1236
- #@param [String] a A version of the from '\d.\d.\d.*'
1237
- #@param [String] b A version of the form '\d.\d.\d.*'
1238
- #@return [Boolean] true if a is less than b, otherwise return false
1239
- #
1240
- #@note 3.0.0-160-gac44cfb is greater than 3.0.0, and 2.8.2
1241
- #@note -rc being less than final builds is not yet implemented.
1242
- def version_is_less a, b
1243
- a_nums = a.split('-')[0].split('.')
1244
- b_nums = b.split('-')[0].split('.')
1245
- (0...a_nums.length).each do |i|
1246
- if i < b_nums.length
1247
- if a_nums[i] < b_nums[i]
1248
- return true
1249
- elsif a_nums[i] > b_nums[i]
1250
- return false
1251
- end
1252
- else
1253
- return false
1254
- end
1255
- end
1256
- #checks all dots, they are equal so examine the rest
1257
- a_rest = a.split('-', 2)[1]
1258
- b_rest = b.split('-', 2)[1]
1259
- if a_rest and b_rest and a_rest < b_rest
1260
- return false
1261
- elsif a_rest and not b_rest
1262
- return false
1263
- elsif not a_rest and b_rest
1264
- return true
1265
- end
1266
- return false
1267
- end
1268
-
1269
- #stops the puppet agent running on the host
1270
- # @param [Host, Array<Host>, String, Symbol] agent One or more hosts to act upon,
1271
- # or a role (String or Symbol) that identifies one or more hosts.
1272
- def stop_agent_on(agent)
1273
- block_on agent do | host |
1274
- vardir = agent.puppet['vardir']
1275
- agent_running = true
1276
- while agent_running
1277
- agent_running = agent.file_exist?("#{vardir}/state/agent_catalog_run.lock")
1278
- if agent_running
1279
- sleep 2
1280
- end
1281
- end
1282
-
1283
- # The agent service is `pe-puppet` everywhere EXCEPT certain linux distros on PE 2.8
1284
- # In all the case that it is different, this init script will exist. So we can assume
1285
- # that if the script doesn't exist, we should just use `pe-puppet`
1286
- agent_service = 'pe-puppet-agent'
1287
- agent_service = 'pe-puppet' unless agent.file_exist?('/etc/init.d/pe-puppet-agent')
1288
-
1289
- # Under a number of stupid circumstances, we can't stop the
1290
- # agent using puppet. This is usually because of issues with
1291
- # the init script or system on that particular configuration.
1292
- avoid_puppet_at_all_costs = false
1293
- avoid_puppet_at_all_costs ||= agent['platform'] =~ /el-4/
1294
- avoid_puppet_at_all_costs ||= agent['pe_ver'] && version_is_less(agent['pe_ver'], '3.2') && agent['platform'] =~ /sles/
1295
-
1296
- if avoid_puppet_at_all_costs
1297
- # When upgrading, puppet is already stopped. On EL4, this causes an exit code of '1'
1298
- on agent, "/etc/init.d/#{agent_service} stop", :acceptable_exit_codes => [0, 1]
1299
- else
1300
- on agent, puppet_resource('service', agent_service, 'ensure=stopped')
1301
- end
1302
- end
1303
- end
1304
-
1305
- #stops the puppet agent running on the default host
1306
- # @see #stop_agent_on
1307
- def stop_agent
1308
- stop_agent_on(default)
1309
- end
1310
-
1311
-
1312
- #wait for a given host to appear in the dashboard
1313
- def wait_for_host_in_dashboard(host)
1314
- hostname = host.node_name
1315
- retry_on(dashboard, "! curl --tlsv1 -k -I https://#{dashboard}/nodes/#{hostname} | grep '404 Not Found'")
1316
- end
1317
-
1318
- # Ensure the host has requested a cert, then sign it
1319
- #
1320
- # @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
1321
- # or a role (String or Symbol) that identifies one or more hosts.
1322
- #
1323
- # @return nil
1324
- # @raise [FailTest] if process times out
1325
- def sign_certificate_for(host)
1326
- block_on host do | host |
1327
- if [master, dashboard, database].include? host
1328
-
1329
- on host, puppet( 'agent -t' ), :acceptable_exit_codes => [0,1,2]
1330
- on master, puppet( "cert --allow-dns-alt-names sign #{host}" ), :acceptable_exit_codes => [0,24]
1331
-
1332
- else
1333
-
1334
- hostname = Regexp.escape host.node_name
1335
-
1336
- last_sleep = 0
1337
- next_sleep = 1
1338
- (0..10).each do |i|
1339
- fail_test("Failed to sign cert for #{hostname}") if i == 10
1340
-
1341
- on master, puppet("cert --sign --all --allow-dns-alt-names"), :acceptable_exit_codes => [0,24]
1342
- break if on(master, puppet("cert --list --all")).stdout =~ /\+ "?#{hostname}"?/
1343
- sleep next_sleep
1344
- (last_sleep, next_sleep) = next_sleep, last_sleep+next_sleep
1345
- end
1346
-
1347
- end
1348
- end
1349
- end
1350
-
1351
- #prompt the master to sign certs then check to confirm the cert for the default host is signed
1352
- #@see #sign_certificate_for
1353
- def sign_certificate
1354
- sign_certificate_for(default)
1355
- end
1356
-
1357
- # Get a facter fact from a provided host
1358
- #
1359
- # @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
1360
- # or a role (String or Symbol) that identifies one or more hosts.
1361
- # @param [String] name The name of the fact to query for
1362
- # @!macro common_opts
1363
- #
1364
- # @return String The value of the fact 'name' on the provided host
1365
- # @raise [FailTest] Raises an exception if call to facter fails
1366
- def fact_on(host, name, opts = {})
1367
- result = on host, facter(name, opts)
1368
- if result.kind_of?(Array)
1369
- result.map { |res| res.stdout.chomp }
1370
- else
1371
- result.stdout.chomp
1372
- end
1373
- end
1374
-
1375
- # Get a facter fact from the default host
1376
- # @see #fact_on
1377
- def fact(name, opts = {})
1378
- fact_on(default, name, opts)
1379
- end
1380
-
1381
- #Run a curl command on the provided host(s)
1382
- #
1383
- # @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
1384
- # or a role (String or Symbol) that identifies one or more hosts.
1385
- # @param [String, Command] cmd The curl command to execute on *host*.
1386
- # @param [Proc] block Additional actions or assertions.
1387
- # @!macro common_opts
1388
- #
1389
- def curl_on(host, cmd, opts = {}, &block)
1390
- if options.is_pe? #check global options hash
1391
- on host, "curl --tlsv1 %s" % cmd, opts, &block
1392
- else
1393
- on host, "curl %s" % cmd, opts, &block
1394
- end
1395
- end
1396
-
1397
- # Write hiera config file on one or more provided hosts
1398
- #
1399
- # @param[Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
1400
- # or a role (String or Symbol) that identifies one or more hosts.
1401
- # @param[Array] One or more hierarchy paths
1402
- def write_hiera_config_on(host, hierarchy)
1403
-
1404
- block_on host do |host|
1405
- hiera_config=Hash.new
1406
- hiera_config[:backends] = 'yaml'
1407
- hiera_config[:yaml] = {}
1408
- hiera_config[:yaml][:datadir] = hiera_datadir(host)
1409
- hiera_config[:hierarchy] = hierarchy
1410
- hiera_config[:logger] = 'console'
1411
- create_remote_file host, host.puppet['hiera_config'], hiera_config.to_yaml
1412
- end
1413
- end
1414
-
1415
- # Write hiera config file for the default host
1416
- # @see #write_hiera_config_on
1417
- def write_hiera_config(hierarchy)
1418
- write_hiera_config_on(default, hierarchy)
1419
- end
1420
-
1421
- # Copy hiera data files to one or more provided hosts
1422
- #
1423
- # @param[Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
1424
- # or a role (String or Symbol) that identifies one or more hosts.
1425
- # @param[String] Directory containing the hiera data files.
1426
- def copy_hiera_data_to(host, source)
1427
- scp_to host, File.expand_path(source), hiera_datadir(host)
1428
- end
1429
-
1430
- # Copy hiera data files to the default host
1431
- # @see #copy_hiera_data_to
1432
- def copy_hiera_data(source)
1433
- copy_hiera_data_to(default, source)
1434
- end
1435
-
1436
- # Get file path to the hieradatadir for a given host.
1437
- # Handles whether or not a host is AIO-based & backwards compatibility
1438
- #
1439
- # @param[Host] host Host you want to use the hieradatadir from
1440
- #
1441
- # @return [String] Path to the hiera data directory
1442
- def hiera_datadir(host)
1443
- host[:type] =~ /aio/ ? File.join(host.puppet['codedir'], 'hieradata') : host[:hieradatadir]
1444
- end
1445
-
24
+ include Beaker::DSL::Helpers::FacterHelpers
25
+ include Beaker::DSL::Helpers::HieraHelpers
26
+ include Beaker::DSL::Helpers::HostHelpers
27
+ include Beaker::DSL::Helpers::PuppetHelpers
28
+ include Beaker::DSL::Helpers::TKHelpers
29
+ include Beaker::DSL::Helpers::WebHelpers
1446
30
  end
1447
31
  end
1448
32
  end