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