beaker 2.7.1 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
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