beaker-puppet 0.1.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.
- checksums.yaml +15 -0
- data/.gitignore +25 -0
- data/.simplecov +9 -0
- data/Gemfile +25 -0
- data/HISTORY.md +8 -0
- data/LICENSE +202 -0
- data/README.md +55 -0
- data/Rakefile +299 -0
- data/acceptance/config/acceptance-options.rb +6 -0
- data/acceptance/config/gem/acceptance-options.rb +9 -0
- data/acceptance/config/git/acceptance-options.rb +9 -0
- data/acceptance/config/nodes/vagrant-ubuntu-1404.yml +8 -0
- data/acceptance/config/pkg/acceptance-options.rb +8 -0
- data/acceptance/lib/beaker/acceptance/install_utils.rb +58 -0
- data/acceptance/pre_suite/gem/install.rb +8 -0
- data/acceptance/pre_suite/git/install.rb +97 -0
- data/acceptance/pre_suite/pkg/install.rb +9 -0
- data/acceptance/tests/README.md +3 -0
- data/acceptance/tests/backwards_compatible.rb +19 -0
- data/acceptance/tests/install_smoke_test.rb +21 -0
- data/acceptance/tests/stub_host.rb +47 -0
- data/acceptance/tests/web_helpers_test.rb +54 -0
- data/acceptance/tests/with_puppet_running_on.rb +26 -0
- data/beaker-puppet.gemspec +38 -0
- data/bin/beaker-puppet +32 -0
- data/lib/beaker-puppet.rb +46 -0
- data/lib/beaker-puppet/helpers/facter_helpers.rb +57 -0
- data/lib/beaker-puppet/helpers/puppet_helpers.rb +865 -0
- data/lib/beaker-puppet/helpers/tk_helpers.rb +89 -0
- data/lib/beaker-puppet/install_utils/aio_defaults.rb +93 -0
- data/lib/beaker-puppet/install_utils/ezbake_utils.rb +256 -0
- data/lib/beaker-puppet/install_utils/foss_defaults.rb +211 -0
- data/lib/beaker-puppet/install_utils/foss_utils.rb +1309 -0
- data/lib/beaker-puppet/install_utils/module_utils.rb +244 -0
- data/lib/beaker-puppet/install_utils/puppet_utils.rb +157 -0
- data/lib/beaker-puppet/version.rb +3 -0
- data/lib/beaker-puppet/wrappers.rb +93 -0
- data/lib/beaker/dsl/helpers/facter_helpers.rb +1 -0
- data/lib/beaker/dsl/helpers/puppet_helpers.rb +1 -0
- data/lib/beaker/dsl/helpers/tk_helpers.rb +1 -0
- data/lib/beaker/dsl/install_utils/aio_defaults.rb +1 -0
- data/lib/beaker/dsl/install_utils/ezbake_utils.rb +1 -0
- data/lib/beaker/dsl/install_utils/foss_defaults.rb +1 -0
- data/lib/beaker/dsl/install_utils/foss_utils.rb +1 -0
- data/lib/beaker/dsl/install_utils/module_utils.rb +1 -0
- data/lib/beaker/dsl/install_utils/puppet_utils.rb +1 -0
- data/spec/beaker-puppet/helpers/facter_helpers_spec.rb +64 -0
- data/spec/beaker-puppet/helpers/puppet_helpers_spec.rb +1287 -0
- data/spec/beaker-puppet/helpers/tk_helpers_spec.rb +86 -0
- data/spec/helpers.rb +109 -0
- data/spec/spec_helper.rb +23 -0
- metadata +249 -0
data/bin/beaker-puppet
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems' unless defined?(Gem)
|
4
|
+
require 'beaker-puppet'
|
5
|
+
|
6
|
+
VERSION_STRING =
|
7
|
+
"
|
8
|
+
_ .--.
|
9
|
+
( ` )
|
10
|
+
beaker-puppet .-' `--,
|
11
|
+
_..----.. ( )`-.
|
12
|
+
.'_|` _|` _|( .__, )
|
13
|
+
/_| _| _| _( (_, .-'
|
14
|
+
;| _| _| _| '-'__,--'`--'
|
15
|
+
| _| _| _| _| |
|
16
|
+
_ || _| _| _| _| %s
|
17
|
+
_( `--.\\_| _| _| _|/
|
18
|
+
.-' )--,| _| _|.`
|
19
|
+
(__, (_ ) )_| _| /
|
20
|
+
`-.__.\\ _,--'\\|__|__/
|
21
|
+
;____;
|
22
|
+
\\YT/
|
23
|
+
||
|
24
|
+
|\"\"|
|
25
|
+
'=='
|
26
|
+
"
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
puts VERSION_STRING % [BeakerPuppet::VERSION]
|
31
|
+
|
32
|
+
exit 0
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'stringify-hash'
|
2
|
+
require 'in_parallel'
|
3
|
+
require 'beaker-puppet/version'
|
4
|
+
require 'beaker-puppet/wrappers'
|
5
|
+
|
6
|
+
[ 'aio', 'foss' ].each do |lib|
|
7
|
+
require "beaker-puppet/install_utils/#{lib}_defaults"
|
8
|
+
end
|
9
|
+
[ 'foss', 'puppet', 'ezbake', 'module' ].each do |lib|
|
10
|
+
require "beaker-puppet/install_utils/#{lib}_utils"
|
11
|
+
end
|
12
|
+
[ 'tk', 'facter', 'puppet' ].each do |lib|
|
13
|
+
require "beaker-puppet/helpers/#{lib}_helpers"
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
module BeakerPuppet
|
18
|
+
module InstallUtils
|
19
|
+
include Beaker::DSL::InstallUtils::FOSSDefaults
|
20
|
+
include Beaker::DSL::InstallUtils::AIODefaults
|
21
|
+
|
22
|
+
include Beaker::DSL::InstallUtils::PuppetUtils
|
23
|
+
include Beaker::DSL::InstallUtils::FOSSUtils
|
24
|
+
include Beaker::DSL::InstallUtils::EZBakeUtils
|
25
|
+
include Beaker::DSL::InstallUtils::ModuleUtils
|
26
|
+
end
|
27
|
+
|
28
|
+
module Helpers
|
29
|
+
include Beaker::DSL::Helpers::TKHelpers
|
30
|
+
include Beaker::DSL::Helpers::FacterHelpers
|
31
|
+
include Beaker::DSL::Helpers::PuppetHelpers
|
32
|
+
end
|
33
|
+
|
34
|
+
include Beaker::DSL::Wrappers
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# # Boilerplate DSL inclusion mechanism:
|
39
|
+
# # First we register our module with the Beaker DSL
|
40
|
+
# Beaker::DSL.register( Beaker::DSL::Puppet )
|
41
|
+
#
|
42
|
+
# # Modules added into a module which has previously been included are not
|
43
|
+
# # retroactively included in the including class.
|
44
|
+
# #
|
45
|
+
# # https://github.com/adrianomitre/retroactive_module_inclusion
|
46
|
+
# Beaker::TestCase.class_eval { include Beaker::DSL }
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Beaker
|
2
|
+
module DSL
|
3
|
+
module Helpers
|
4
|
+
# Methods that help you interact with your facter installation, facter must be installed
|
5
|
+
# for these methods to execute correctly
|
6
|
+
#
|
7
|
+
module FacterHelpers
|
8
|
+
|
9
|
+
# @!macro [new] common_opts
|
10
|
+
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
11
|
+
# @option opts [Boolean] :silent (false) Do not produce log output
|
12
|
+
# @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
|
13
|
+
# (or range) of integer exit codes that should be considered
|
14
|
+
# acceptable. An error will be thrown if the exit code does not
|
15
|
+
# match one of the values in this list.
|
16
|
+
# @option opts [Boolean] :accept_all_exit_codes (false) Consider all
|
17
|
+
# exit codes as passing.
|
18
|
+
# @option opts [Boolean] :dry_run (false) Do not actually execute any
|
19
|
+
# commands on the SUT
|
20
|
+
# @option opts [String] :stdin (nil) Input to be provided during command
|
21
|
+
# execution on the SUT.
|
22
|
+
# @option opts [Boolean] :pty (false) Execute this command in a pseudoterminal.
|
23
|
+
# @option opts [Boolean] :expect_connection_failure (false) Expect this command
|
24
|
+
# to result in a connection failure, reconnect and continue execution.
|
25
|
+
# @option opts [Hash{String=>String}] :environment ({}) These will be
|
26
|
+
# treated as extra environment variables that should be set before
|
27
|
+
# running the command.
|
28
|
+
#
|
29
|
+
|
30
|
+
# Get a facter fact from a provided host
|
31
|
+
#
|
32
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
33
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
34
|
+
# @param [String] name The name of the fact to query for
|
35
|
+
# @!macro common_opts
|
36
|
+
#
|
37
|
+
# @return String The value of the fact 'name' on the provided host
|
38
|
+
# @raise [FailTest] Raises an exception if call to facter fails
|
39
|
+
def fact_on(host, name, opts = {})
|
40
|
+
result = on host, facter(name, opts)
|
41
|
+
if result.kind_of?(Array)
|
42
|
+
result.map { |res| res.stdout.chomp }
|
43
|
+
else
|
44
|
+
result.stdout.chomp
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Get a facter fact from the default host
|
49
|
+
# @see #fact_on
|
50
|
+
def fact(name, opts = {})
|
51
|
+
fact_on(default, name, opts)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,865 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'inifile'
|
3
|
+
require 'resolv'
|
4
|
+
|
5
|
+
module Beaker
|
6
|
+
module DSL
|
7
|
+
module Helpers
|
8
|
+
# Methods that help you interact with your puppet installation, puppet must be installed
|
9
|
+
# for these methods to execute correctly
|
10
|
+
module PuppetHelpers
|
11
|
+
|
12
|
+
# Return the regular expression pattern for an IPv4 address
|
13
|
+
def ipv4_regex
|
14
|
+
return /(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return the IP address that given hostname returns when resolved on
|
18
|
+
# the given host.
|
19
|
+
#
|
20
|
+
# @ param [Host] host One object that acts like a Beaker::Host
|
21
|
+
# @ param [String] hostname The hostname to perform a DNS resolution on
|
22
|
+
#
|
23
|
+
# @return [String, nil] An IP address, or nil.
|
24
|
+
def resolve_hostname_on(host, hostname)
|
25
|
+
match = curl_on(host, "--verbose #{hostname}", :accept_all_exit_codes => true).stderr.match(ipv4_regex)
|
26
|
+
return match ? match[0] : nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# @!macro [new] common_opts
|
30
|
+
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
31
|
+
# @option opts [Boolean] :silent (false) Do not produce log output
|
32
|
+
# @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
|
33
|
+
# (or range) of integer exit codes that should be considered
|
34
|
+
# acceptable. An error will be thrown if the exit code does not
|
35
|
+
# match one of the values in this list.
|
36
|
+
# @option opts [Boolean] :accept_all_exit_codes (false) Consider all
|
37
|
+
# exit codes as passing.
|
38
|
+
# @option opts [Boolean] :dry_run (false) Do not actually execute any
|
39
|
+
# commands on the SUT
|
40
|
+
# @option opts [String] :stdin (nil) Input to be provided during command
|
41
|
+
# execution on the SUT.
|
42
|
+
# @option opts [Boolean] :pty (false) Execute this command in a pseudoterminal.
|
43
|
+
# @option opts [Boolean] :expect_connection_failure (false) Expect this command
|
44
|
+
# to result in a connection failure, reconnect and continue execution.
|
45
|
+
# @option opts [Hash{String=>String}] :environment ({}) These will be
|
46
|
+
# treated as extra environment variables that should be set before
|
47
|
+
# running the command.
|
48
|
+
#
|
49
|
+
|
50
|
+
# Return the name of the puppet user.
|
51
|
+
#
|
52
|
+
# @param [Host] host One object that acts like a Beaker::Host
|
53
|
+
#
|
54
|
+
# @note This method assumes puppet is installed on the host.
|
55
|
+
#
|
56
|
+
def puppet_user(host)
|
57
|
+
return host.puppet('master')['user']
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return the name of the puppet group.
|
61
|
+
#
|
62
|
+
# @param [Host] host One object that acts like a Beaker::Host
|
63
|
+
#
|
64
|
+
# @note This method assumes puppet is installed on the host.
|
65
|
+
#
|
66
|
+
def puppet_group(host)
|
67
|
+
return host.puppet('master')['group']
|
68
|
+
end
|
69
|
+
|
70
|
+
# Test Puppet running in a certain run mode with specific options.
|
71
|
+
# This ensures the following steps are performed:
|
72
|
+
# 1. The pre-test Puppet configuration is backed up
|
73
|
+
# 2. A new Puppet configuraton file is layed down
|
74
|
+
# 3. Puppet is started or restarted in the specified run mode
|
75
|
+
# 4. Ensure Puppet has started correctly
|
76
|
+
# 5. Further tests are yielded to
|
77
|
+
# 6. Revert Puppet to the pre-test state
|
78
|
+
# 7. Testing artifacts are saved in a folder named for the test
|
79
|
+
#
|
80
|
+
# @note Whether Puppet is started or restarted depends on what kind of
|
81
|
+
# server you're running. Passenger and puppetserver are restarted before.
|
82
|
+
# Webrick is started before and stopped after yielding, unless you're using
|
83
|
+
# service scripts, then it'll behave like passenger & puppetserver.
|
84
|
+
# Passenger and puppetserver (or webrick using service scripts)
|
85
|
+
# restart after yielding by default. You can stop this from happening
|
86
|
+
# by setting the :restart_when_done flag of the conf_opts argument.
|
87
|
+
#
|
88
|
+
# @param [Host] host One object that act like Host
|
89
|
+
#
|
90
|
+
# @param [Hash{Symbol=>String}] conf_opts Represents puppet settings.
|
91
|
+
# Sections of the puppet.conf may be
|
92
|
+
# specified, if no section is specified the
|
93
|
+
# a puppet.conf file will be written with the
|
94
|
+
# options put in a section named after [mode]
|
95
|
+
# @option conf_opts [String] :__commandline_args__ A special setting for
|
96
|
+
# command_line arguments such as --debug or
|
97
|
+
# --logdest, which cannot be set in
|
98
|
+
# puppet.conf. For example:
|
99
|
+
#
|
100
|
+
# :__commandline_args__ => '--logdest /tmp/a.log'
|
101
|
+
#
|
102
|
+
# These will only be applied when starting a FOSS
|
103
|
+
# master, as a pe master is just bounced.
|
104
|
+
# @option conf_opts [Hash] :__service_args__ A special setting of options
|
105
|
+
# for controlling how the puppet master service is
|
106
|
+
# handled. The only setting currently is
|
107
|
+
# :bypass_service_script, which if set true will
|
108
|
+
# force stopping and starting a webrick master
|
109
|
+
# using the start_puppet_from_source_* methods,
|
110
|
+
# even if it seems the host has passenger.
|
111
|
+
# This is needed in FOSS tests to initialize
|
112
|
+
# SSL.
|
113
|
+
# @option conf_opts [Boolean] :restart_when_done determines whether a restart
|
114
|
+
# should be run after the test has been yielded to.
|
115
|
+
# Will stop puppet if false. Default behavior
|
116
|
+
# is to restart, but you can override this on the
|
117
|
+
# host or with this option.
|
118
|
+
# (Note: only works for passenger & puppetserver
|
119
|
+
# masters (or webrick using the service scripts))
|
120
|
+
# @param [File] testdir The temporary directory which will hold backup
|
121
|
+
# configuration, and other test artifacts.
|
122
|
+
#
|
123
|
+
# @param [Block] block The point of this method, yields so
|
124
|
+
# tests may be ran. After the block is finished
|
125
|
+
# puppet will revert to a previous state.
|
126
|
+
#
|
127
|
+
# @example A simple use case to ensure a master is running
|
128
|
+
# with_puppet_running_on( master ) do
|
129
|
+
# ...tests that require a master...
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# @example Fully utilizing the possiblities of config options
|
133
|
+
# with_puppet_running_on( master,
|
134
|
+
# :main => {:logdest => '/var/blah'},
|
135
|
+
# :master => {:masterlog => '/elswhere'},
|
136
|
+
# :agent => {:server => 'localhost'} ) do
|
137
|
+
#
|
138
|
+
# ...tests to be ran...
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
def with_puppet_running_on host, conf_opts, testdir = host.tmpdir(File.basename(@path)), &block
|
142
|
+
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)
|
143
|
+
cmdline_args = conf_opts[:__commandline_args__]
|
144
|
+
service_args = conf_opts[:__service_args__] || {}
|
145
|
+
restart_when_done = true
|
146
|
+
restart_when_done = host[:restart_when_done] if host.has_key?(:restart_when_done)
|
147
|
+
restart_when_done = conf_opts.fetch(:restart_when_done, restart_when_done)
|
148
|
+
conf_opts = conf_opts.reject { |k,v| [:__commandline_args__, :__service_args__, :restart_when_done].include?(k) }
|
149
|
+
|
150
|
+
curl_retries = host['master-start-curl-retries'] || options['master-start-curl-retries']
|
151
|
+
logger.debug "Setting curl retries to #{curl_retries}"
|
152
|
+
|
153
|
+
if options[:is_puppetserver] || host[:is_puppetserver]
|
154
|
+
confdir = host.puppet('master')['confdir']
|
155
|
+
vardir = host.puppet('master')['vardir']
|
156
|
+
|
157
|
+
if cmdline_args
|
158
|
+
split_args = cmdline_args.split()
|
159
|
+
|
160
|
+
split_args.each do |arg|
|
161
|
+
case arg
|
162
|
+
when /--confdir=(.*)/
|
163
|
+
confdir = $1
|
164
|
+
when /--vardir=(.*)/
|
165
|
+
vardir = $1
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
puppetserver_opts = { "jruby-puppet" => {
|
171
|
+
"master-conf-dir" => confdir,
|
172
|
+
"master-var-dir" => vardir,
|
173
|
+
}}
|
174
|
+
|
175
|
+
puppetserver_conf = File.join("#{host['puppetserver-confdir']}", "puppetserver.conf")
|
176
|
+
modify_tk_config(host, puppetserver_conf, puppetserver_opts)
|
177
|
+
end
|
178
|
+
begin
|
179
|
+
backup_file = backup_the_file(host, host.puppet('master')['confdir'], testdir, 'puppet.conf')
|
180
|
+
lay_down_new_puppet_conf host, conf_opts, testdir
|
181
|
+
|
182
|
+
if host.use_service_scripts? && !service_args[:bypass_service_script]
|
183
|
+
bounce_service( host, host['puppetservice'], curl_retries )
|
184
|
+
else
|
185
|
+
puppet_master_started = start_puppet_from_source_on!( host, cmdline_args )
|
186
|
+
end
|
187
|
+
|
188
|
+
yield self if block_given?
|
189
|
+
|
190
|
+
# FIXME: these test-flow-control exceptions should be using throw
|
191
|
+
# they can be caught in test_case. current layout dows not allow it
|
192
|
+
rescue Beaker::DSL::Outcomes::PassTest => early_assertion
|
193
|
+
pass_test(early_assertion)
|
194
|
+
rescue Beaker::DSL::Outcomes::FailTest => early_assertion
|
195
|
+
fail_test(early_assertion)
|
196
|
+
rescue Beaker::DSL::Outcomes::PendingTest => early_assertion
|
197
|
+
pending_test(early_assertion)
|
198
|
+
rescue Beaker::DSL::Outcomes::SkipTest => early_assertion
|
199
|
+
skip_test(early_assertion)
|
200
|
+
rescue Beaker::DSL::Assertions, Minitest::Assertion => early_assertion
|
201
|
+
fail_test(early_assertion)
|
202
|
+
rescue Exception => early_exception
|
203
|
+
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")
|
204
|
+
raise(original_exception)
|
205
|
+
|
206
|
+
ensure
|
207
|
+
begin
|
208
|
+
|
209
|
+
if host.use_service_scripts? && !service_args[:bypass_service_script]
|
210
|
+
restore_puppet_conf_from_backup( host, backup_file )
|
211
|
+
if restart_when_done
|
212
|
+
bounce_service( host, host['puppetservice'], curl_retries )
|
213
|
+
else
|
214
|
+
host.exec puppet_resource('service', host['puppetservice'], 'ensure=stopped')
|
215
|
+
end
|
216
|
+
else
|
217
|
+
if puppet_master_started
|
218
|
+
stop_puppet_from_source_on( host )
|
219
|
+
else
|
220
|
+
dump_puppet_log(host)
|
221
|
+
end
|
222
|
+
restore_puppet_conf_from_backup( host, backup_file )
|
223
|
+
end
|
224
|
+
|
225
|
+
rescue Exception => teardown_exception
|
226
|
+
begin
|
227
|
+
if !host.is_pe?
|
228
|
+
dump_puppet_log(host)
|
229
|
+
end
|
230
|
+
rescue Exception => dumping_exception
|
231
|
+
logger.error("Raised during attempt to dump puppet logs: #{dumping_exception}")
|
232
|
+
end
|
233
|
+
|
234
|
+
if original_exception
|
235
|
+
logger.error("Raised during attempt to teardown with_puppet_running_on: #{teardown_exception}\n---\n")
|
236
|
+
raise original_exception
|
237
|
+
else
|
238
|
+
raise teardown_exception
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Test Puppet running in a certain run mode with specific options,
|
245
|
+
# on the default host
|
246
|
+
# @see #with_puppet_running_on
|
247
|
+
def with_puppet_running conf_opts, testdir = host.tmpdir(File.basename(@path)), &block
|
248
|
+
with_puppet_running_on(default, conf_opts, testdir, &block)
|
249
|
+
end
|
250
|
+
|
251
|
+
# @!visibility private
|
252
|
+
def restore_puppet_conf_from_backup( host, backup_file )
|
253
|
+
puppet_conf = host.puppet('master')['config']
|
254
|
+
|
255
|
+
if backup_file
|
256
|
+
host.exec( Command.new( "if [ -f '#{backup_file}' ]; then " +
|
257
|
+
"cat '#{backup_file}' > " +
|
258
|
+
"'#{puppet_conf}'; " +
|
259
|
+
"rm -f '#{backup_file}'; " +
|
260
|
+
"fi" ) )
|
261
|
+
else
|
262
|
+
host.exec( Command.new( "rm -f '#{puppet_conf}'" ))
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
# @!visibility private
|
268
|
+
def start_puppet_from_source_on! host, args = ''
|
269
|
+
host.exec( puppet( 'master', args ) )
|
270
|
+
|
271
|
+
logger.debug 'Waiting for the puppet master to start'
|
272
|
+
unless port_open_within?( host, 8140, 10 )
|
273
|
+
raise Beaker::DSL::FailTest, 'Puppet master did not start in a timely fashion'
|
274
|
+
end
|
275
|
+
logger.debug 'The puppet master has started'
|
276
|
+
return true
|
277
|
+
end
|
278
|
+
|
279
|
+
# @!visibility private
|
280
|
+
def stop_puppet_from_source_on( host )
|
281
|
+
pid = host.exec( Command.new('cat `puppet master --configprint pidfile`') ).stdout.chomp
|
282
|
+
host.exec( Command.new( "kill #{pid}" ) )
|
283
|
+
Timeout.timeout(10) do
|
284
|
+
while host.exec( Command.new( "kill -0 #{pid}"), :acceptable_exit_codes => [0,1] ).exit_code == 0 do
|
285
|
+
# until kill -0 finds no process and we know that puppet has finished cleaning up
|
286
|
+
sleep 1
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# @!visibility private
|
292
|
+
def dump_puppet_log(host)
|
293
|
+
syslogfile = case host['platform']
|
294
|
+
when /fedora|centos|el|redhat|scientific/ then '/var/log/messages'
|
295
|
+
when /ubuntu|debian|cumulus/ then '/var/log/syslog'
|
296
|
+
else return
|
297
|
+
end
|
298
|
+
|
299
|
+
logger.notify "\n*************************"
|
300
|
+
logger.notify "* Dumping master log *"
|
301
|
+
logger.notify "*************************"
|
302
|
+
host.exec( Command.new( "tail -n 100 #{syslogfile}" ), :acceptable_exit_codes => [0,1])
|
303
|
+
logger.notify "*************************\n"
|
304
|
+
end
|
305
|
+
|
306
|
+
# @!visibility private
|
307
|
+
def lay_down_new_puppet_conf( host, configuration_options, testdir )
|
308
|
+
puppetconf_main = host.puppet('master')['config']
|
309
|
+
puppetconf_filename = File.basename(puppetconf_main)
|
310
|
+
puppetconf_test = File.join(testdir, puppetconf_filename)
|
311
|
+
|
312
|
+
new_conf = puppet_conf_for( host, configuration_options )
|
313
|
+
create_remote_file host, puppetconf_test, new_conf.to_s
|
314
|
+
|
315
|
+
host.exec(
|
316
|
+
Command.new( "cat #{puppetconf_test} > #{puppetconf_main}" ),
|
317
|
+
:silent => true
|
318
|
+
)
|
319
|
+
host.exec( Command.new( "cat #{puppetconf_main}" ) )
|
320
|
+
end
|
321
|
+
|
322
|
+
# @!visibility private
|
323
|
+
def puppet_conf_for host, conf_opts
|
324
|
+
puppetconf = host.exec( Command.new( "cat #{host.puppet('master')['config']}" ) ).stdout
|
325
|
+
new_conf = IniFile.new(content: puppetconf).merge( conf_opts )
|
326
|
+
|
327
|
+
new_conf
|
328
|
+
end
|
329
|
+
|
330
|
+
# Restarts the named puppet service
|
331
|
+
#
|
332
|
+
# @param [Host] host Host the service runs on
|
333
|
+
# @param [String] service Name of the service to restart
|
334
|
+
# @param [Fixnum] curl_retries Number of seconds to wait for the restart to complete before failing
|
335
|
+
# @param [Fixnum] port Port to check status at
|
336
|
+
#
|
337
|
+
# @return [Result] Result of last status check
|
338
|
+
# @!visibility private
|
339
|
+
def bounce_service host, service, curl_retries = nil, port = nil
|
340
|
+
curl_retries = 120 if curl_retries.nil?
|
341
|
+
port = options[:puppetserver_port] if port.nil?
|
342
|
+
if host.graceful_restarts?
|
343
|
+
service = host.check_for_command('apache2ctl') ? 'apache2ctl' : 'apachectl'
|
344
|
+
apachectl_path = host.is_pe? ? "#{host['puppetsbindir']}/#{service}" : service
|
345
|
+
host.exec(Command.new("#{apachectl_path} graceful"))
|
346
|
+
else
|
347
|
+
result = host.exec(Command.new("service #{service} reload"),
|
348
|
+
:acceptable_exit_codes => [0,1,3])
|
349
|
+
if result.exit_code == 0
|
350
|
+
return result
|
351
|
+
else
|
352
|
+
host.exec puppet_resource('service', service, 'ensure=stopped')
|
353
|
+
host.exec puppet_resource('service', service, 'ensure=running')
|
354
|
+
end
|
355
|
+
end
|
356
|
+
curl_with_retries(" #{service} ", host, "https://localhost:#{port}", [35, 60], curl_retries)
|
357
|
+
end
|
358
|
+
|
359
|
+
# Runs 'puppet apply' on a remote host, piping manifest through stdin
|
360
|
+
#
|
361
|
+
# @param [Host] host The host that this command should be run on
|
362
|
+
#
|
363
|
+
# @param [String] manifest The puppet manifest to apply
|
364
|
+
#
|
365
|
+
# @!macro common_opts
|
366
|
+
# @option opts [Boolean] :parseonly (false) If this key is true, the
|
367
|
+
# "--parseonly" command line parameter will
|
368
|
+
# be passed to the 'puppet apply' command.
|
369
|
+
#
|
370
|
+
# @option opts [Boolean] :trace (false) If this key exists in the Hash,
|
371
|
+
# the "--trace" command line parameter will be
|
372
|
+
# passed to the 'puppet apply' command.
|
373
|
+
#
|
374
|
+
# @option opts [Array<Integer>] :acceptable_exit_codes ([0]) The list of exit
|
375
|
+
# codes that will NOT raise an error when found upon
|
376
|
+
# command completion. If provided, these values will
|
377
|
+
# be combined with those used in :catch_failures and
|
378
|
+
# :expect_failures to create the full list of
|
379
|
+
# passing exit codes.
|
380
|
+
#
|
381
|
+
# @option opts [Hash] :environment Additional environment variables to be
|
382
|
+
# passed to the 'puppet apply' command
|
383
|
+
#
|
384
|
+
# @option opts [Boolean] :catch_failures (false) By default `puppet
|
385
|
+
# --apply` will exit with 0, which does not count
|
386
|
+
# as a test failure, even if there were errors or
|
387
|
+
# changes when applying the manifest. This option
|
388
|
+
# enables detailed exit codes and causes a test
|
389
|
+
# failure if `puppet --apply` indicates there was
|
390
|
+
# a failure during its execution.
|
391
|
+
#
|
392
|
+
# @option opts [Boolean] :catch_changes (false) This option enables
|
393
|
+
# detailed exit codes and causes a test failure
|
394
|
+
# if `puppet --apply` indicates that there were
|
395
|
+
# changes or failures during its execution.
|
396
|
+
#
|
397
|
+
# @option opts [Boolean] :expect_changes (false) This option enables
|
398
|
+
# detailed exit codes and causes a test failure
|
399
|
+
# if `puppet --apply` indicates that there were
|
400
|
+
# no resource changes during its execution.
|
401
|
+
#
|
402
|
+
# @option opts [Boolean] :expect_failures (false) This option enables
|
403
|
+
# detailed exit codes and causes a test failure
|
404
|
+
# if `puppet --apply` indicates there were no
|
405
|
+
# failure during its execution.
|
406
|
+
#
|
407
|
+
# @option opts [Boolean] :future_parser (false) This option enables
|
408
|
+
# the future parser option that is available
|
409
|
+
# from Puppet verion 3.2
|
410
|
+
# By default it will use the 'current' parser.
|
411
|
+
#
|
412
|
+
# @option opts [Boolean] :noop (false) If this option exists, the
|
413
|
+
# the "--noop" command line parameter will be
|
414
|
+
# passed to the 'puppet apply' command.
|
415
|
+
#
|
416
|
+
# @option opts [String] :modulepath The search path for modules, as
|
417
|
+
# a list of directories separated by the system
|
418
|
+
# path separator character. (The POSIX path separator
|
419
|
+
# is ‘:’, and the Windows path separator is ‘;’.)
|
420
|
+
#
|
421
|
+
# @option opts [String] :debug (false) If this option exists,
|
422
|
+
# the "--debug" command line parameter
|
423
|
+
# will be passed to the 'puppet apply' command.
|
424
|
+
# @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
|
425
|
+
#
|
426
|
+
# @param [Block] block This method will yield to a block of code passed
|
427
|
+
# by the caller; this can be used for additional
|
428
|
+
# validation, etc.
|
429
|
+
#
|
430
|
+
# @return [Array<Result>, Result, nil] An array of results, a result object,
|
431
|
+
# or nil. Check {#run_block_on} for more details on this.
|
432
|
+
def apply_manifest_on(host, manifest, opts = {}, &block)
|
433
|
+
block_on host, opts do | host |
|
434
|
+
on_options = {}
|
435
|
+
on_options[:acceptable_exit_codes] = Array(opts[:acceptable_exit_codes])
|
436
|
+
|
437
|
+
puppet_apply_opts = {}
|
438
|
+
if opts[:debug]
|
439
|
+
puppet_apply_opts[:debug] = nil
|
440
|
+
else
|
441
|
+
puppet_apply_opts[:verbose] = nil
|
442
|
+
end
|
443
|
+
puppet_apply_opts[:parseonly] = nil if opts[:parseonly]
|
444
|
+
puppet_apply_opts[:trace] = nil if opts[:trace]
|
445
|
+
puppet_apply_opts[:parser] = 'future' if opts[:future_parser]
|
446
|
+
puppet_apply_opts[:modulepath] = opts[:modulepath] if opts[:modulepath]
|
447
|
+
puppet_apply_opts[:noop] = nil if opts[:noop]
|
448
|
+
|
449
|
+
# From puppet help:
|
450
|
+
# "... an exit code of '2' means there were changes, an exit code of
|
451
|
+
# '4' means there were failures during the transaction, and an exit
|
452
|
+
# code of '6' means there were both changes and failures."
|
453
|
+
if [opts[:catch_changes],opts[:catch_failures],opts[:expect_failures],opts[:expect_changes]].compact.length > 1
|
454
|
+
raise(ArgumentError,
|
455
|
+
'Cannot specify more than one of `catch_failures`, ' +
|
456
|
+
'`catch_changes`, `expect_failures`, or `expect_changes` ' +
|
457
|
+
'for a single manifest')
|
458
|
+
end
|
459
|
+
|
460
|
+
if opts[:catch_changes]
|
461
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
462
|
+
|
463
|
+
# We're after idempotency so allow exit code 0 only.
|
464
|
+
on_options[:acceptable_exit_codes] |= [0]
|
465
|
+
elsif opts[:catch_failures]
|
466
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
467
|
+
|
468
|
+
# We're after only complete success so allow exit codes 0 and 2 only.
|
469
|
+
on_options[:acceptable_exit_codes] |= [0, 2]
|
470
|
+
elsif opts[:expect_failures]
|
471
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
472
|
+
|
473
|
+
# We're after failures specifically so allow exit codes 1, 4, and 6 only.
|
474
|
+
on_options[:acceptable_exit_codes] |= [1, 4, 6]
|
475
|
+
elsif opts[:expect_changes]
|
476
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
477
|
+
|
478
|
+
# We're after changes specifically so allow exit code 2 only.
|
479
|
+
on_options[:acceptable_exit_codes] |= [2]
|
480
|
+
else
|
481
|
+
# Either use the provided acceptable_exit_codes or default to [0]
|
482
|
+
on_options[:acceptable_exit_codes] |= [0]
|
483
|
+
end
|
484
|
+
|
485
|
+
# Not really thrilled with this implementation, might want to improve it
|
486
|
+
# later. Basically, there is a magic trick in the constructor of
|
487
|
+
# PuppetCommand which allows you to pass in a Hash for the last value in
|
488
|
+
# the *args Array; if you do so, it will be treated specially. So, here
|
489
|
+
# we check to see if our caller passed us a hash of environment variables
|
490
|
+
# that they want to set for the puppet command. If so, we set the final
|
491
|
+
# value of *args to a new hash with just one entry (the value of which
|
492
|
+
# is our environment variables hash)
|
493
|
+
if opts.has_key?(:environment)
|
494
|
+
puppet_apply_opts['ENV'] = opts[:environment]
|
495
|
+
end
|
496
|
+
|
497
|
+
file_path = host.tmpfile('apply_manifest.pp')
|
498
|
+
create_remote_file(host, file_path, manifest + "\n")
|
499
|
+
|
500
|
+
if host[:default_apply_opts].respond_to? :merge
|
501
|
+
puppet_apply_opts = host[:default_apply_opts].merge( puppet_apply_opts )
|
502
|
+
end
|
503
|
+
|
504
|
+
on host, puppet('apply', file_path, puppet_apply_opts), on_options, &block
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
# Runs 'puppet apply' on default host, piping manifest through stdin
|
509
|
+
# @see #apply_manifest_on
|
510
|
+
def apply_manifest(manifest, opts = {}, &block)
|
511
|
+
apply_manifest_on(default, manifest, opts, &block)
|
512
|
+
end
|
513
|
+
|
514
|
+
# @deprecated
|
515
|
+
def run_agent_on(host, arg='--no-daemonize --verbose --onetime --test',
|
516
|
+
options={}, &block)
|
517
|
+
block_on host do | host |
|
518
|
+
on host, puppet_agent(arg), options, &block
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
# This method using the puppet resource 'host' will setup host aliases
|
523
|
+
# and register the remove of host aliases via Beaker::TestCase#teardown
|
524
|
+
#
|
525
|
+
# A teardown step is also added to make sure unstubbing of the host is
|
526
|
+
# removed always.
|
527
|
+
#
|
528
|
+
# @param [Host, Array<Host>, String, Symbol] machine One or more hosts to act upon,
|
529
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
530
|
+
# @param ip_spec [Hash{String=>String}] a hash containing the host to ip
|
531
|
+
# mappings
|
532
|
+
# @param alias_spec [Hash{String=>Array[String]] an hash containing the host to alias(es) mappings to apply
|
533
|
+
# @example Stub puppetlabs.com on the master to 127.0.0.1 with an alias example.com
|
534
|
+
# stub_hosts_on(master, {'puppetlabs.com' => '127.0.0.1'}, {'puppetlabs.com' => ['example.com']})
|
535
|
+
def stub_hosts_on(machine, ip_spec, alias_spec={})
|
536
|
+
block_on machine do | host |
|
537
|
+
ip_spec.each do |address, ip|
|
538
|
+
aliases = alias_spec[address] || []
|
539
|
+
manifest =<<-EOS.gsub /^\s+/, ""
|
540
|
+
host { '#{address}':
|
541
|
+
\tensure => present,
|
542
|
+
\tip => '#{ip}',
|
543
|
+
\thost_aliases => #{aliases},
|
544
|
+
}
|
545
|
+
EOS
|
546
|
+
logger.notify("Stubbing address #{address} to IP #{ip} on machine #{host}")
|
547
|
+
apply_manifest_on( host, manifest )
|
548
|
+
end
|
549
|
+
|
550
|
+
teardown do
|
551
|
+
ip_spec.each do |address, ip|
|
552
|
+
logger.notify("Unstubbing address #{address} to IP #{ip} on machine #{host}")
|
553
|
+
on( host, puppet('resource', 'host', address, 'ensure=absent') )
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
# This method accepts a block and using the puppet resource 'host' will
|
560
|
+
# setup host aliases before and after that block.
|
561
|
+
#
|
562
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
563
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
564
|
+
# @param ip_spec [Hash{String=>String}] a hash containing the host to ip
|
565
|
+
# mappings
|
566
|
+
# @param alias_spec [Hash{String=>Array[String]] an hash containing the host to alias(es) mappings to apply
|
567
|
+
# @example Stub forgeapi.puppetlabs.com on the master to 127.0.0.1 with an alias forgeapi.example.com
|
568
|
+
# with_host_stubbed_on(master, {'forgeapi.puppetlabs.com' => '127.0.0.1'}, {'forgeapi.puppetlabs.com' => ['forgeapi.example.com']}) do
|
569
|
+
# puppet( "module install puppetlabs-stdlib" )
|
570
|
+
# end
|
571
|
+
def with_host_stubbed_on(host, ip_spec, alias_spec={}, &block)
|
572
|
+
begin
|
573
|
+
block_on host do |host|
|
574
|
+
# this code is duplicated from the `stub_hosts_on` method. The
|
575
|
+
# `stub_hosts_on` method itself is not used here because this
|
576
|
+
# method is used by modules tests using `beaker-rspec`. Since
|
577
|
+
# the `stub_hosts_on` method contains a `teardown` step, it is
|
578
|
+
# incompatible with `beaker_rspec`.
|
579
|
+
ip_spec.each do |address, ip|
|
580
|
+
aliases = alias_spec[address] || []
|
581
|
+
manifest =<<-EOS.gsub /^\s+/, ""
|
582
|
+
host { '#{address}':
|
583
|
+
\tensure => present,
|
584
|
+
\tip => '#{ip}',
|
585
|
+
\thost_aliases => #{aliases},
|
586
|
+
}
|
587
|
+
EOS
|
588
|
+
logger.notify("Stubbing address #{address} to IP #{ip} on machine #{host}")
|
589
|
+
apply_manifest_on( host, manifest )
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
block.call
|
594
|
+
|
595
|
+
ensure
|
596
|
+
ip_spec.each do |address, ip|
|
597
|
+
logger.notify("Unstubbing address #{address} to IP #{ip} on machine #{host}")
|
598
|
+
on( host, puppet('resource', 'host', address, 'ensure=absent') )
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
# This method accepts a block and using the puppet resource 'host' will
|
604
|
+
# setup host aliases before and after that block on the default host
|
605
|
+
#
|
606
|
+
# @example Stub puppetlabs.com on the default host to 127.0.0.1
|
607
|
+
# stub_hosts('puppetlabs.com' => '127.0.0.1')
|
608
|
+
# @see #stub_hosts_on
|
609
|
+
def stub_hosts(ip_spec)
|
610
|
+
stub_hosts_on(default, ip_spec)
|
611
|
+
end
|
612
|
+
|
613
|
+
# This wraps the method `stub_hosts_on` and makes the stub specific to
|
614
|
+
# the forge alias.
|
615
|
+
#
|
616
|
+
# forge api v1 canonical source is forge.puppetlabs.com
|
617
|
+
# forge api v3 canonical source is forgeapi.puppetlabs.com
|
618
|
+
#
|
619
|
+
# @param machine [String] the host to perform the stub on
|
620
|
+
# @param forge_host [String] The URL to use as the forge alias, will default to using :forge_host in the
|
621
|
+
# global options hash
|
622
|
+
def stub_forge_on(machine, forge_host = nil)
|
623
|
+
#use global options hash
|
624
|
+
primary_forge_name = 'forge.puppetlabs.com'
|
625
|
+
forge_host ||= options[:forge_host]
|
626
|
+
forge_ip = resolve_hostname_on(machine, forge_host)
|
627
|
+
raise "Failed to resolve forge host '#{forge_host}'" unless forge_ip
|
628
|
+
@forge_ip ||= forge_ip
|
629
|
+
block_on machine do | host |
|
630
|
+
stub_hosts_on(host, {primary_forge_name => @forge_ip}, {primary_forge_name => ['forge.puppet.com','forgeapi.puppetlabs.com','forgeapi.puppet.com']})
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
# This wraps the method `with_host_stubbed_on` and makes the stub specific to
|
635
|
+
# the forge alias.
|
636
|
+
#
|
637
|
+
# forge api v1 canonical source is forge.puppetlabs.com
|
638
|
+
# forge api v3 canonical source is forgeapi.puppetlabs.com
|
639
|
+
#
|
640
|
+
# @param host [String] the host to perform the stub on
|
641
|
+
# @param forge_host [String] The URL to use as the forge alias, will default to using :forge_host in the
|
642
|
+
# global options hash
|
643
|
+
def with_forge_stubbed_on( host, forge_host = nil, &block )
|
644
|
+
#use global options hash
|
645
|
+
primary_forge_name = 'forge.puppetlabs.com'
|
646
|
+
forge_host ||= options[:forge_host]
|
647
|
+
forge_ip = resolve_hostname_on(host, forge_host)
|
648
|
+
raise "Failed to resolve forge host '#{forge_host}'" unless forge_ip
|
649
|
+
@forge_ip ||= forge_ip
|
650
|
+
with_host_stubbed_on( host, {primary_forge_name => @forge_ip}, {primary_forge_name => ['forge.puppet.com','forgeapi.puppetlabs.com','forgeapi.puppet.com']}, &block )
|
651
|
+
end
|
652
|
+
|
653
|
+
# This wraps `with_forge_stubbed_on` and provides it the default host
|
654
|
+
# @see with_forge_stubbed_on
|
655
|
+
def with_forge_stubbed( forge_host = nil, &block )
|
656
|
+
with_forge_stubbed_on( default, forge_host, &block )
|
657
|
+
end
|
658
|
+
|
659
|
+
# This wraps the method `stub_hosts` and makes the stub specific to
|
660
|
+
# the forge alias.
|
661
|
+
#
|
662
|
+
# @see #stub_forge_on
|
663
|
+
def stub_forge(forge_host = nil)
|
664
|
+
#use global options hash
|
665
|
+
forge_host ||= options[:forge_host]
|
666
|
+
stub_forge_on(default, forge_host)
|
667
|
+
end
|
668
|
+
|
669
|
+
# Waits until a successful curl check has happened against puppetdb
|
670
|
+
#
|
671
|
+
# @param [Host] host Host puppetdb is on
|
672
|
+
# @param [Fixnum] nonssl_port Port to make the HTTP status check over
|
673
|
+
# @param [Fixnum] ssl_port Port to make the HTTPS status check over
|
674
|
+
#
|
675
|
+
# @return [Result] Result of the last HTTPS status check
|
676
|
+
def sleep_until_puppetdb_started(host, nonssl_port = nil, ssl_port = nil)
|
677
|
+
nonssl_port = options[:puppetdb_port_nonssl] if nonssl_port.nil?
|
678
|
+
ssl_port = options[:puppetdb_port_ssl] if ssl_port.nil?
|
679
|
+
pe_ver = host['pe_ver'] || '0'
|
680
|
+
if version_is_less(pe_ver, '2016.1.0') then
|
681
|
+
# the status endpoint was introduced in puppetdb 4.0. The earliest
|
682
|
+
# PE release with the 4.x pdb version was 2016.1.0
|
683
|
+
endpoint = 'pdb/meta/v1/version'
|
684
|
+
expected_regex = '\"version\" \{0,\}: \{0,\}\"[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\"'
|
685
|
+
else
|
686
|
+
endpoint = 'status/v1/services/puppetdb-status'
|
687
|
+
expected_regex = '\"state\" \{0,\}: \{0,\}\"running\"'
|
688
|
+
end
|
689
|
+
retry_on(host,
|
690
|
+
"curl -m 1 http://localhost:#{nonssl_port}/#{endpoint} | grep '#{expected_regex}'",
|
691
|
+
{:max_retries => 120})
|
692
|
+
curl_with_retries("start puppetdb (ssl)",
|
693
|
+
host, "https://#{host.node_name}:#{ssl_port}", [35, 60])
|
694
|
+
end
|
695
|
+
|
696
|
+
# Waits until a successful curl check has happened against puppetserver
|
697
|
+
#
|
698
|
+
# @param [Host] host Host puppetserver is on
|
699
|
+
# @param [Fixnum] port Port to make the HTTPS status check over
|
700
|
+
#
|
701
|
+
# @return [Result] Result of the last HTTPS status check
|
702
|
+
def sleep_until_puppetserver_started(host, port = nil)
|
703
|
+
port = options[:puppetserver_port] if port.nil?
|
704
|
+
curl_with_retries("start puppetserver (ssl)",
|
705
|
+
host, "https://#{host.node_name}:#{port}", [35, 60])
|
706
|
+
end
|
707
|
+
|
708
|
+
# Waits until a successful curl check has happaned against node classifier
|
709
|
+
#
|
710
|
+
# @param [Host] host Host node classifier is on
|
711
|
+
# @param [Fixnum] port Port to make the HTTPS status check over
|
712
|
+
#
|
713
|
+
# @return [Result] Result of the last HTTPS status check
|
714
|
+
def sleep_until_nc_started(host, port = nil)
|
715
|
+
port = options[:nodeclassifier_port] if port.nil?
|
716
|
+
curl_with_retries("start nodeclassifier (ssl)",
|
717
|
+
host, "https://#{host.node_name}:#{port}", [35, 60])
|
718
|
+
end
|
719
|
+
|
720
|
+
#stops the puppet agent running on the host
|
721
|
+
# @param [Host, Array<Host>, String, Symbol] agent One or more hosts to act upon,
|
722
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
723
|
+
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
724
|
+
# @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
|
725
|
+
def stop_agent_on(agent, opts = {})
|
726
|
+
block_on agent, opts do | host |
|
727
|
+
vardir = host.puppet_configprint['vardir']
|
728
|
+
agent_running = true
|
729
|
+
while agent_running
|
730
|
+
agent_running = host.file_exist?("#{vardir}/state/agent_catalog_run.lock")
|
731
|
+
if agent_running
|
732
|
+
sleep 2
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
# In 4.0 this was changed to just be `puppet`
|
737
|
+
agent_service = 'puppet'
|
738
|
+
if !aio_version?(host)
|
739
|
+
# The agent service is `pe-puppet` everywhere EXCEPT certain linux distros on PE 2.8
|
740
|
+
# In all the case that it is different, this init script will exist. So we can assume
|
741
|
+
# that if the script doesn't exist, we should just use `pe-puppet`
|
742
|
+
agent_service = 'pe-puppet-agent'
|
743
|
+
agent_service = 'pe-puppet' unless host.file_exist?('/etc/init.d/pe-puppet-agent')
|
744
|
+
end
|
745
|
+
|
746
|
+
# Under a number of stupid circumstances, we can't stop the
|
747
|
+
# agent using puppet. This is usually because of issues with
|
748
|
+
# the init script or system on that particular configuration.
|
749
|
+
avoid_puppet_at_all_costs = false
|
750
|
+
avoid_puppet_at_all_costs ||= host['platform'] =~ /el-4/
|
751
|
+
avoid_puppet_at_all_costs ||= host['pe_ver'] && version_is_less(host['pe_ver'], '3.2') && host['platform'] =~ /sles/
|
752
|
+
|
753
|
+
if avoid_puppet_at_all_costs
|
754
|
+
# When upgrading, puppet is already stopped. On EL4, this causes an exit code of '1'
|
755
|
+
on host, "/etc/init.d/#{agent_service} stop", :acceptable_exit_codes => [0, 1]
|
756
|
+
else
|
757
|
+
on host, puppet_resource('service', agent_service, 'ensure=stopped')
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
#stops the puppet agent running on the default host
|
763
|
+
# @see #stop_agent_on
|
764
|
+
def stop_agent
|
765
|
+
stop_agent_on(default)
|
766
|
+
end
|
767
|
+
|
768
|
+
#wait for a given host to appear in the dashboard
|
769
|
+
# @deprecated this method should be removed in the next release since we don't believe the check is necessary.
|
770
|
+
def wait_for_host_in_dashboard(host)
|
771
|
+
|
772
|
+
hostname = host.node_name
|
773
|
+
hostcert = dashboard.puppet['hostcert']
|
774
|
+
key = dashboard.puppet['hostprivkey']
|
775
|
+
cacert = dashboard.puppet['localcacert']
|
776
|
+
retry_on(dashboard, "curl --cert #{hostcert} --key #{key} --cacert #{cacert}\
|
777
|
+
https://#{dashboard}:4433/classifier-api/v1/nodes | grep '\"name\":\"#{hostname}\"'")
|
778
|
+
end
|
779
|
+
|
780
|
+
# Ensure the host has requested a cert, then sign it
|
781
|
+
#
|
782
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts, or a role (String or Symbol)
|
783
|
+
# that identifies one or more hosts to validate certificate signing.
|
784
|
+
# No argument, or an empty array means no validation of success
|
785
|
+
# for specific hosts will be performed. This will always execute
|
786
|
+
# 'cert --sign --all --allow-dns-alt-names' even for a single host.
|
787
|
+
#
|
788
|
+
# @return nil
|
789
|
+
# @raise [FailTest] if process times out
|
790
|
+
def sign_certificate_for(host = [])
|
791
|
+
hostnames = []
|
792
|
+
hosts = host.is_a?(Array) ? host : [host]
|
793
|
+
hosts.each{ |current_host|
|
794
|
+
if [master, dashboard, database].include? current_host
|
795
|
+
|
796
|
+
on current_host, puppet( 'agent -t' ), :acceptable_exit_codes => [0,1,2]
|
797
|
+
on master, puppet( "cert --allow-dns-alt-names sign #{current_host}" ), :acceptable_exit_codes => [0,24]
|
798
|
+
|
799
|
+
else
|
800
|
+
hostnames << Regexp.escape( current_host.node_name )
|
801
|
+
end
|
802
|
+
}
|
803
|
+
if hostnames.size < 1
|
804
|
+
on master, puppet("cert --sign --all --allow-dns-alt-names"),
|
805
|
+
:acceptable_exit_codes => [0,24]
|
806
|
+
return
|
807
|
+
end
|
808
|
+
while hostnames.size > 0
|
809
|
+
last_sleep = 0
|
810
|
+
next_sleep = 1
|
811
|
+
(0..10).each do |i|
|
812
|
+
if i == 10
|
813
|
+
fail_test("Failed to sign cert for #{hostnames}")
|
814
|
+
hostnames.clear
|
815
|
+
end
|
816
|
+
on master, puppet("cert --sign --all --allow-dns-alt-names"), :acceptable_exit_codes => [0,24]
|
817
|
+
out = on(master, puppet("cert --list --all")).stdout
|
818
|
+
if hostnames.all? { |hostname| out =~ /\+ "?#{hostname}"?/ }
|
819
|
+
hostnames.clear
|
820
|
+
break
|
821
|
+
end
|
822
|
+
|
823
|
+
sleep next_sleep
|
824
|
+
(last_sleep, next_sleep) = next_sleep, last_sleep+next_sleep
|
825
|
+
end
|
826
|
+
end
|
827
|
+
host
|
828
|
+
end
|
829
|
+
|
830
|
+
#prompt the master to sign certs then check to confirm the cert for the default host is signed
|
831
|
+
#@see #sign_certificate_for
|
832
|
+
def sign_certificate
|
833
|
+
sign_certificate_for(default)
|
834
|
+
end
|
835
|
+
|
836
|
+
# Create a temp directory on remote host with a user. Default user
|
837
|
+
# is puppet master user.
|
838
|
+
#
|
839
|
+
# @param [Host] host A single remote host on which to create and adjust
|
840
|
+
# the ownership of a temp directory.
|
841
|
+
# @param [String] name A remote path prefix for the new temp
|
842
|
+
# directory. Default value is '/tmp/beaker'
|
843
|
+
# @param [String] user The name of user that should own the temp
|
844
|
+
# directory. If no username is specified, use `puppet master
|
845
|
+
# --configprint user` to obtain username from master. Raise RuntimeError
|
846
|
+
# if this puppet command returns a non-zero exit code.
|
847
|
+
#
|
848
|
+
# @return [String] Returns the name of the newly-created dir.
|
849
|
+
def create_tmpdir_for_user(host, name='/tmp/beaker', user=nil)
|
850
|
+
if not user
|
851
|
+
result = on host, puppet("master --configprint user")
|
852
|
+
if not result.exit_code == 0
|
853
|
+
raise "`puppet master --configprint` failed, check that puppet is installed on #{host} or explicitly pass in a user name."
|
854
|
+
end
|
855
|
+
user = result.stdout.strip
|
856
|
+
end
|
857
|
+
|
858
|
+
create_tmpdir_on(host, name, user)
|
859
|
+
|
860
|
+
end
|
861
|
+
|
862
|
+
end
|
863
|
+
end
|
864
|
+
end
|
865
|
+
end
|