simp-beaker-helpers 1.18.8
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 +7 -0
- data/.fixtures.yml +8 -0
- data/.gitignore +8 -0
- data/.gitlab-ci.yml +163 -0
- data/.rspec +4 -0
- data/.rubocop.yml +546 -0
- data/.travis.yml +36 -0
- data/CHANGELOG.md +231 -0
- data/Gemfile +51 -0
- data/LICENSE +27 -0
- data/README.md +543 -0
- data/Rakefile +151 -0
- data/files/pki/clean.sh +1 -0
- data/files/pki/make.sh +101 -0
- data/files/pki/template_ca.cnf +259 -0
- data/files/pki/template_host.cnf +263 -0
- data/files/puppet-agent-versions.yaml +46 -0
- data/lib/simp/beaker_helpers.rb +1231 -0
- data/lib/simp/beaker_helpers/constants.rb +25 -0
- data/lib/simp/beaker_helpers/inspec.rb +328 -0
- data/lib/simp/beaker_helpers/snapshot.rb +156 -0
- data/lib/simp/beaker_helpers/ssg.rb +383 -0
- data/lib/simp/beaker_helpers/version.rb +5 -0
- data/lib/simp/beaker_helpers/windows.rb +16 -0
- data/lib/simp/rake/beaker.rb +269 -0
- data/simp-beaker-helpers.gemspec +38 -0
- data/spec/acceptance/nodesets/default.yml +32 -0
- data/spec/acceptance/suites/default/check_puppet_version_spec.rb +23 -0
- data/spec/acceptance/suites/default/enable_fips_spec.rb +23 -0
- data/spec/acceptance/suites/default/fixture_modules_spec.rb +22 -0
- data/spec/acceptance/suites/default/install_simp_deps_repo_spec.rb +43 -0
- data/spec/acceptance/suites/default/nodesets +1 -0
- data/spec/acceptance/suites/default/pki_tests_spec.rb +55 -0
- data/spec/acceptance/suites/default/set_hieradata_on_spec.rb +33 -0
- data/spec/acceptance/suites/default/write_hieradata_to_spec.rb +33 -0
- data/spec/acceptance/suites/fips_from_fixtures/00_default_spec.rb +63 -0
- data/spec/acceptance/suites/fips_from_fixtures/metadata.yml +2 -0
- data/spec/acceptance/suites/fips_from_fixtures/nodesets +1 -0
- data/spec/acceptance/suites/offline/00_default_spec.rb +165 -0
- data/spec/acceptance/suites/offline/README +2 -0
- data/spec/acceptance/suites/offline/nodesets/default.yml +26 -0
- data/spec/acceptance/suites/puppet_collections/00_default_spec.rb +25 -0
- data/spec/acceptance/suites/puppet_collections/metadata.yml +2 -0
- data/spec/acceptance/suites/puppet_collections/nodesets/default.yml +30 -0
- data/spec/acceptance/suites/snapshot/00_snapshot_test_spec.rb +82 -0
- data/spec/acceptance/suites/snapshot/10_general_usage_spec.rb +56 -0
- data/spec/acceptance/suites/snapshot/nodesets +1 -0
- data/spec/acceptance/suites/windows/00_default_spec.rb +119 -0
- data/spec/acceptance/suites/windows/metadata.yml +2 -0
- data/spec/acceptance/suites/windows/nodesets/default.yml +33 -0
- data/spec/acceptance/suites/windows/nodesets/win2016.yml +35 -0
- data/spec/acceptance/suites/windows/nodesets/win2019.yml +34 -0
- data/spec/lib/simp/beaker_helpers_spec.rb +216 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/spec_helper_acceptance.rb +25 -0
- metadata +243 -0
@@ -0,0 +1,263 @@
|
|
1
|
+
#
|
2
|
+
# OpenSSL example configuration file.
|
3
|
+
# This is mostly being used for generation of certificate requests.
|
4
|
+
#
|
5
|
+
|
6
|
+
# This definition stops the following lines choking if HOME isn't
|
7
|
+
# defined.
|
8
|
+
HOME = .
|
9
|
+
RANDFILE = $ENV::HOME/.rnd
|
10
|
+
|
11
|
+
# Extra OBJECT IDENTIFIER info:
|
12
|
+
#oid_file = $ENV::HOME/.oid
|
13
|
+
oid_section = new_oids
|
14
|
+
|
15
|
+
# To use this configuration file with the "-extfile" option of the
|
16
|
+
# "openssl x509" utility, name here the section containing the
|
17
|
+
# X.509v3 extensions to use:
|
18
|
+
# extensions =
|
19
|
+
# (Alternatively, use a configuration file that has only
|
20
|
+
# X.509v3 extensions in its main [= default] section.)
|
21
|
+
|
22
|
+
[ new_oids ]
|
23
|
+
|
24
|
+
# We can add new OIDs in here for use by 'ca' and 'req'.
|
25
|
+
# Add a simple OID like this:
|
26
|
+
# testoid1=1.2.3.4
|
27
|
+
# Or use config file substitution like this:
|
28
|
+
# testoid2=${testoid1}.5.6
|
29
|
+
|
30
|
+
####################################################################
|
31
|
+
[ ca ]
|
32
|
+
default_ca = CA_default # The default ca section
|
33
|
+
|
34
|
+
####################################################################
|
35
|
+
[ CA_default ]
|
36
|
+
|
37
|
+
dir = ./demoCA # Where everything is kept
|
38
|
+
certs = $dir/certs # Where the issued certs are kept
|
39
|
+
crl_dir = $dir/crl # Where the issued crl are kept
|
40
|
+
database = $dir/index.txt # database index file.
|
41
|
+
new_certs_dir = $dir/newcerts # default place for new certs.
|
42
|
+
|
43
|
+
certificate = $dir/cacert.pem # The CA certificate
|
44
|
+
serial = $dir/serial # The current serial number
|
45
|
+
crl = $dir/crl.pem # The current CRL
|
46
|
+
private_key = $dir/private/cakey.pem# The private key
|
47
|
+
RANDFILE = $dir/private/.rand # private random number file
|
48
|
+
|
49
|
+
x509_extensions = usr_cert # The extentions to add to the cert
|
50
|
+
|
51
|
+
input_password = test
|
52
|
+
output_password = test
|
53
|
+
|
54
|
+
# Comment out the following two lines for the "traditional"
|
55
|
+
# (and highly broken) format.
|
56
|
+
name_opt = ca_default # Subject Name options
|
57
|
+
cert_opt = ca_default # Certificate field options
|
58
|
+
|
59
|
+
# Extension copying option: use with caution.
|
60
|
+
# copy_extensions = copy
|
61
|
+
|
62
|
+
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
|
63
|
+
# so this is commented out by default to leave a V1 CRL.
|
64
|
+
# crl_extensions = crl_ext
|
65
|
+
|
66
|
+
default_days = 365 # how long to certify for
|
67
|
+
default_crl_days= 30 # how long before next CRL
|
68
|
+
default_md = sha256 # which md to use.
|
69
|
+
preserve = no # keep passed DN ordering
|
70
|
+
|
71
|
+
# A few difference way of specifying how similar the request should look
|
72
|
+
# For type CA, the listed attributes must be the same, and the optional
|
73
|
+
# and supplied fields are just that :-)
|
74
|
+
policy = policy_anything
|
75
|
+
|
76
|
+
# For the CA policy
|
77
|
+
[ policy_match ]
|
78
|
+
countryName = match
|
79
|
+
stateOrProvinceName = match
|
80
|
+
organizationName = match
|
81
|
+
organizationalUnitName = optional
|
82
|
+
commonName = supplied
|
83
|
+
emailAddress = optional
|
84
|
+
|
85
|
+
# For the 'anything' policy
|
86
|
+
# At this point in time, you must list all acceptable 'object'
|
87
|
+
# types.
|
88
|
+
[ policy_anything ]
|
89
|
+
countryName = optional
|
90
|
+
stateOrProvinceName = optional
|
91
|
+
localityName = optional
|
92
|
+
organizationName = optional
|
93
|
+
organizationalUnitName = optional
|
94
|
+
commonName = supplied
|
95
|
+
emailAddress = optional
|
96
|
+
|
97
|
+
####################################################################
|
98
|
+
[ req ]
|
99
|
+
default_bits = 2048
|
100
|
+
default_keyfile = privkey.pem
|
101
|
+
distinguished_name = req_distinguished_name
|
102
|
+
attributes = req_attributes
|
103
|
+
x509_extensions = v3_ca # The extentions to add to the self signed cert
|
104
|
+
|
105
|
+
# Passwords for private keys if not present they will be prompted for
|
106
|
+
# input_password = secret
|
107
|
+
# output_password = secret
|
108
|
+
|
109
|
+
# This sets a mask for permitted string types. There are several options.
|
110
|
+
# default: PrintableString, T61String, BMPString.
|
111
|
+
# pkix : PrintableString, BMPString.
|
112
|
+
# utf8only: only UTF8Strings.
|
113
|
+
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
|
114
|
+
# MASK:XXXX a literal mask value.
|
115
|
+
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
|
116
|
+
# so use this option with caution!
|
117
|
+
string_mask = nombstr
|
118
|
+
|
119
|
+
# req_extensions = v3_req # The extensions to add to a certificate request
|
120
|
+
|
121
|
+
[ req_distinguished_name ]
|
122
|
+
countryName = Country Name (2 letter code)
|
123
|
+
countryName_default = ZZ
|
124
|
+
countryName_min = 2
|
125
|
+
countryName_max = 2
|
126
|
+
|
127
|
+
#stateOrProvinceName = State or Province Name (full name)
|
128
|
+
#stateOrProvinceName_default = Berkshire
|
129
|
+
|
130
|
+
#localityName = Locality Name (eg, city)
|
131
|
+
#localityName_default = Newbury
|
132
|
+
|
133
|
+
0.organizationName = Organization Name (eg, company)
|
134
|
+
0.organizationName_default = Fake Org
|
135
|
+
|
136
|
+
# we can do this but it is not needed normally :-)
|
137
|
+
#1.organizationName = Second Organization Name (eg, company)
|
138
|
+
#1.organizationName_default = World Wide Web Pty Ltd
|
139
|
+
|
140
|
+
organizationalUnitName = Organizational Unit Name (eg, section)
|
141
|
+
organizationalUnitName_default = Hosts
|
142
|
+
|
143
|
+
commonName = Common Name (eg, your name or your server\'s hostname)
|
144
|
+
commonName_max = 64
|
145
|
+
commonName_default = #HOSTNAME#
|
146
|
+
|
147
|
+
#emailAddress = Email Address
|
148
|
+
#emailAddress_max = 64
|
149
|
+
|
150
|
+
# SET-ex3 = SET extension number 3
|
151
|
+
|
152
|
+
[ req_attributes ]
|
153
|
+
#challengePassword = A challenge password
|
154
|
+
#challengePassword_min = 4
|
155
|
+
#challengePassword_max = 20
|
156
|
+
#challengePassword_default = password
|
157
|
+
|
158
|
+
unstructuredName = Fake Cert
|
159
|
+
|
160
|
+
[ usr_cert ]
|
161
|
+
|
162
|
+
# These extensions are added when 'ca' signs a request.
|
163
|
+
|
164
|
+
# This goes against PKIX guidelines but some CAs do it and some software
|
165
|
+
# requires this to avoid interpreting an end user certificate as a CA.
|
166
|
+
|
167
|
+
basicConstraints=CA:FALSE
|
168
|
+
|
169
|
+
# Here are some examples of the usage of nsCertType. If it is omitted
|
170
|
+
# the certificate can be used for anything *except* object signing.
|
171
|
+
|
172
|
+
# This is OK for an SSL server.
|
173
|
+
#nsCertType = server
|
174
|
+
|
175
|
+
# For an object signing certificate this would be used.
|
176
|
+
# nsCertType = objsign
|
177
|
+
|
178
|
+
# For normal client use this is typical
|
179
|
+
# nsCertType = client, email
|
180
|
+
# nsCertType = client
|
181
|
+
|
182
|
+
# and for everything including object signing:
|
183
|
+
nsCertType = server, client, email, objsign
|
184
|
+
|
185
|
+
# This is typical in keyUsage for a client certificate.
|
186
|
+
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
187
|
+
|
188
|
+
# This will be displayed in Netscape's comment listbox.
|
189
|
+
#nsComment = "Test Certificate"
|
190
|
+
|
191
|
+
# PKIX recommendations harmless if included in all certificates.
|
192
|
+
subjectKeyIdentifier=hash
|
193
|
+
authorityKeyIdentifier=keyid,issuer:always
|
194
|
+
|
195
|
+
# This stuff is for subjectAltName and issuerAltname.
|
196
|
+
# Import the email address.
|
197
|
+
# subjectAltName=email:copy
|
198
|
+
# An alternative to produce certificates that aren't
|
199
|
+
# deprecated according to PKIX.
|
200
|
+
# subjectAltName=email:move
|
201
|
+
# subjectAltName = #ALTNAMES#
|
202
|
+
|
203
|
+
# Copy subject details
|
204
|
+
# issuerAltName=issuer:copy
|
205
|
+
|
206
|
+
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
|
207
|
+
#nsBaseUrl
|
208
|
+
#nsRevocationUrl
|
209
|
+
#nsRenewalUrl
|
210
|
+
#nsCaPolicyUrl
|
211
|
+
#nsSslServerName
|
212
|
+
|
213
|
+
[ v3_req ]
|
214
|
+
|
215
|
+
# Extensions to add to a certificate request
|
216
|
+
|
217
|
+
basicConstraints = CA:FALSE
|
218
|
+
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
219
|
+
|
220
|
+
[ v3_ca ]
|
221
|
+
|
222
|
+
|
223
|
+
# Extensions for a typical CA
|
224
|
+
|
225
|
+
|
226
|
+
# PKIX recommendation.
|
227
|
+
|
228
|
+
subjectKeyIdentifier=hash
|
229
|
+
|
230
|
+
authorityKeyIdentifier=keyid:always,issuer:always
|
231
|
+
|
232
|
+
# This is what PKIX recommends but some broken software chokes on critical
|
233
|
+
# extensions.
|
234
|
+
#basicConstraints = critical,CA:true
|
235
|
+
# So we do this instead.
|
236
|
+
basicConstraints = CA:true
|
237
|
+
|
238
|
+
# Key usage: this is typical for a CA certificate. However since it will
|
239
|
+
# prevent it being used as an test self-signed certificate it is best
|
240
|
+
# left out by default.
|
241
|
+
# keyUsage = cRLSign, keyCertSign
|
242
|
+
|
243
|
+
# Some might want this also
|
244
|
+
# nsCertType = sslCA, emailCA
|
245
|
+
|
246
|
+
# Include email address in subject alt name: another PKIX recommendation
|
247
|
+
# subjectAltName=email:copy
|
248
|
+
# Copy issuer details
|
249
|
+
# issuerAltName=issuer:copy
|
250
|
+
|
251
|
+
# DER hex encoding of an extension: beware experts only!
|
252
|
+
# obj=DER:02:03
|
253
|
+
# Where 'obj' is a standard or added object
|
254
|
+
# You can even override a supported extension:
|
255
|
+
# basicConstraints= critical, DER:30:03:01:01:FF
|
256
|
+
|
257
|
+
[ crl_ext ]
|
258
|
+
|
259
|
+
# CRL extensions.
|
260
|
+
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
|
261
|
+
|
262
|
+
# issuerAltName=issuer:copy
|
263
|
+
authorityKeyIdentifier=keyid:always,issuer:always
|
@@ -0,0 +1,46 @@
|
|
1
|
+
---
|
2
|
+
# `puppet` agent to `puppet-agent` version mappings
|
3
|
+
#
|
4
|
+
# - https://docs.puppet.com/puppet/latest/about_agent.html
|
5
|
+
#
|
6
|
+
version_mappings:
|
7
|
+
'4.10.12': '1.10.14'
|
8
|
+
'4.10.11': '1.10.12'
|
9
|
+
'4.10.10': '1.10.10'
|
10
|
+
'4.10.9': '1.10.9'
|
11
|
+
'4.10.8': '1.10.8'
|
12
|
+
'4.10.7': '1.10.7'
|
13
|
+
'4.10.6': '1.10.6'
|
14
|
+
'4.10.5': '1.10.5'
|
15
|
+
'4.10.4': '1.10.4'
|
16
|
+
'4.10.3': '1.10.3'
|
17
|
+
'4.10.2': '1.10.2'
|
18
|
+
'4.10.1': '1.10.1'
|
19
|
+
'4.10.0': '1.10.0'
|
20
|
+
'4.9.4': '1.9.3'
|
21
|
+
'4.9.3': '1.9.2'
|
22
|
+
'4.9.2': '1.9.1'
|
23
|
+
'4.9.0': '1.9.0'
|
24
|
+
'4.8.2': '1.8.3'
|
25
|
+
'4.8.1': '1.8.2'
|
26
|
+
'4.8.0': '1.8.0'
|
27
|
+
'4.7.1': '1.7.2'
|
28
|
+
'4.7.0': '1.7.1'
|
29
|
+
'4.6.2': '1.6.2'
|
30
|
+
'4.6.1': '1.6.1'
|
31
|
+
'4.6.0': '1.6.0'
|
32
|
+
'4.5.2': '1.5.3'
|
33
|
+
'4.5.1': '1.5.1'
|
34
|
+
'4.5.0': '1.5.0'
|
35
|
+
'4.4.2': '1.4.2'
|
36
|
+
'4.4.1': '1.4.1'
|
37
|
+
'4.4.0': '1.4.0'
|
38
|
+
'4.3.2': '1.3.6'
|
39
|
+
'4.3.1': '1.3.2'
|
40
|
+
'4.3.0': '1.3.0'
|
41
|
+
'4.2.3': '1.2.7'
|
42
|
+
'4.2.2': '1.2.6'
|
43
|
+
'4.2.1': '1.2.2'
|
44
|
+
'4.2.0': '1.2.1'
|
45
|
+
'4.1.0': '1.1.1'
|
46
|
+
'4.0.0': '1.0.1'
|
@@ -0,0 +1,1231 @@
|
|
1
|
+
require 'beaker-puppet'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
module Simp; end
|
5
|
+
|
6
|
+
module Simp::BeakerHelpers
|
7
|
+
include BeakerPuppet
|
8
|
+
|
9
|
+
require 'simp/beaker_helpers/constants'
|
10
|
+
require 'simp/beaker_helpers/inspec'
|
11
|
+
require 'simp/beaker_helpers/snapshot'
|
12
|
+
require 'simp/beaker_helpers/ssg'
|
13
|
+
require 'simp/beaker_helpers/version'
|
14
|
+
|
15
|
+
# Stealing this from the Ruby 2.5 Dir::Tmpname workaround from Rails
|
16
|
+
def self.tmpname
|
17
|
+
t = Time.new.strftime("%Y%m%d")
|
18
|
+
"simp-beaker-helpers-#{t}-#{$$}-#{rand(0x100000000).to_s(36)}.tmp"
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_windows?(sut)
|
22
|
+
sut[:platform] =~ /windows/i
|
23
|
+
end
|
24
|
+
|
25
|
+
# We can't cache this because it may change during a run
|
26
|
+
def fips_enabled(sut)
|
27
|
+
return on( sut,
|
28
|
+
'cat /proc/sys/crypto/fips_enabled 2>/dev/null',
|
29
|
+
:accept_all_exit_codes => true
|
30
|
+
).output.strip == '1'
|
31
|
+
end
|
32
|
+
|
33
|
+
# Figure out the best method to copy files to a host and use it
|
34
|
+
#
|
35
|
+
# Will create the directories leading up to the target if they don't exist
|
36
|
+
def copy_to(sut, src, dest, opts={})
|
37
|
+
unless fips_enabled(sut) || @has_rsync
|
38
|
+
%x{which rsync 2>/dev/null}.strip
|
39
|
+
|
40
|
+
@has_rsync = !$?.nil? && $?.success?
|
41
|
+
end
|
42
|
+
|
43
|
+
sut.mkdir_p(File.dirname(dest))
|
44
|
+
|
45
|
+
if sut[:hypervisor] == 'docker'
|
46
|
+
exclude_list = []
|
47
|
+
if opts.has_key?(:ignore) && !opts[:ignore].empty?
|
48
|
+
opts[:ignore].each do |value|
|
49
|
+
exclude_list << "--exclude '#{value}'"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Work around for breaking changes in beaker-docker
|
54
|
+
if sut.host_hash[:docker_container]
|
55
|
+
container_id = sut.host_hash[:docker_container].id
|
56
|
+
else
|
57
|
+
container_id = sut.host_hash[:docker_container_id]
|
58
|
+
end
|
59
|
+
%x(tar #{exclude_list.join(' ')} -hcf - -C "#{File.dirname(src)}" "#{File.basename(src)}" | docker exec -i "#{container_id}" tar -C "#{dest}" -xf -)
|
60
|
+
elsif @has_rsync && sut.check_for_command('rsync')
|
61
|
+
# This makes rsync_to work like beaker and scp usually do
|
62
|
+
exclude_hack = %(__-__' -L --exclude '__-__)
|
63
|
+
|
64
|
+
# There appears to be a single copy of 'opts' that gets passed around
|
65
|
+
# through all of the different hosts so we're going to make a local deep
|
66
|
+
# copy so that we don't destroy the world accidentally.
|
67
|
+
_opts = Marshal.load(Marshal.dump(opts))
|
68
|
+
_opts[:ignore] ||= []
|
69
|
+
_opts[:ignore] << exclude_hack
|
70
|
+
|
71
|
+
if File.directory?(src)
|
72
|
+
dest = File.join(dest, File.basename(src)) if File.directory?(src)
|
73
|
+
sut.mkdir_p(dest)
|
74
|
+
end
|
75
|
+
|
76
|
+
# End rsync hackery
|
77
|
+
|
78
|
+
begin
|
79
|
+
rsync_to(sut, src, dest, _opts)
|
80
|
+
rescue
|
81
|
+
# Depending on what is getting tested, a new SSH session might not
|
82
|
+
# work. In this case, we fall back to SSH.
|
83
|
+
#
|
84
|
+
# The rsync failure is quite fast so this doesn't affect performance as
|
85
|
+
# much as shoving a bunch of data over the ssh session.
|
86
|
+
scp_to(sut, src, dest, opts)
|
87
|
+
end
|
88
|
+
else
|
89
|
+
scp_to(sut, src, dest, opts)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# use the `puppet fact` face to look up facts on an SUT
|
94
|
+
def pfact_on(sut, fact_name)
|
95
|
+
facts_json = on(sut,'puppet facts find xxx').output
|
96
|
+
facts = JSON.parse(facts_json).fetch( 'values' )
|
97
|
+
facts.fetch(fact_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the modulepath on the SUT, as an Array
|
101
|
+
def puppet_modulepath_on(sut, environment='production')
|
102
|
+
splitchar = ':'
|
103
|
+
splitchar = ';' if is_windows?(sut)
|
104
|
+
|
105
|
+
(
|
106
|
+
sut.puppet_configprint['modulepath'].split(splitchar) +
|
107
|
+
sut.puppet_configprint['basemodulepath'].split(splitchar)
|
108
|
+
).uniq
|
109
|
+
end
|
110
|
+
|
111
|
+
# Return the default environment path
|
112
|
+
def puppet_environment_path_on(sut, environment='production')
|
113
|
+
File.dirname(sut.puppet_configprint['manifest'])
|
114
|
+
end
|
115
|
+
|
116
|
+
# Return the path to the 'spec/fixtures' directory
|
117
|
+
def fixtures_path
|
118
|
+
return @fixtures_path if @fixtures_path
|
119
|
+
|
120
|
+
STDERR.puts ' ** fixtures_path' if ENV['BEAKER_helpers_verbose']
|
121
|
+
dir = RSpec.configuration.default_path
|
122
|
+
dir = File.join('.', 'spec') unless dir
|
123
|
+
|
124
|
+
dir = File.join(File.expand_path(dir), 'fixtures')
|
125
|
+
|
126
|
+
if File.directory?(dir)
|
127
|
+
@fixtures_path = dir
|
128
|
+
return @fixtures_path
|
129
|
+
else
|
130
|
+
raise("Could not find fixtures directory at '#{dir}'")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Locates .fixture.yml in or above this directory.
|
135
|
+
def fixtures_yml_path
|
136
|
+
return @fixtures_yml_path if @fixtures_yml_path
|
137
|
+
|
138
|
+
STDERR.puts ' ** fixtures_yml_path' if ENV['BEAKER_helpers_verbose']
|
139
|
+
|
140
|
+
if ENV['FIXTURES_YML']
|
141
|
+
fixtures_yml = ENV['FIXTURES_YML']
|
142
|
+
else
|
143
|
+
fixtures_yml = ''
|
144
|
+
dir = '.'
|
145
|
+
while( fixtures_yml.empty? && File.expand_path(dir) != '/' ) do
|
146
|
+
file = File.expand_path( '.fixtures.yml', dir )
|
147
|
+
STDERR.puts " ** fixtures_yml_path: #{file}" if ENV['BEAKER_helpers_verbose']
|
148
|
+
if File.exists? file
|
149
|
+
fixtures_yml = file
|
150
|
+
break
|
151
|
+
end
|
152
|
+
dir = "#{dir}/.."
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
raise 'ERROR: cannot locate .fixtures.yml!' if fixtures_yml.empty?
|
157
|
+
|
158
|
+
STDERR.puts " ** fixtures_yml_path:finished (file: '#{file}')" if ENV['BEAKER_helpers_verbose']
|
159
|
+
|
160
|
+
@fixtures_yml_path = fixtures_yml
|
161
|
+
|
162
|
+
return @fixtures_yml_path
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
# returns an Array of puppet modules declared in .fixtures.yml
|
167
|
+
def pupmods_in_fixtures_yml
|
168
|
+
return @pupmods_in_fixtures_yml if @pupmods_in_fixtures_yml
|
169
|
+
|
170
|
+
STDERR.puts ' ** pupmods_in_fixtures_yml' if ENV['BEAKER_helpers_verbose']
|
171
|
+
fixtures_yml = fixtures_yml_path
|
172
|
+
data = YAML.load_file( fixtures_yml )
|
173
|
+
repos = data.fetch('fixtures').fetch('repositories', {}).keys || []
|
174
|
+
symlinks = data.fetch('fixtures').fetch('symlinks', {}).keys || []
|
175
|
+
STDERR.puts ' ** pupmods_in_fixtures_yml: finished' if ENV['BEAKER_helpers_verbose']
|
176
|
+
|
177
|
+
@pupmods_in_fixtures_yml = (repos + symlinks)
|
178
|
+
|
179
|
+
return @pupmods_in_fixtures_yml
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
# Ensures that the fixture modules (under `spec/fixtures/modules`) exists.
|
184
|
+
# if any fixture modules are missing, run 'rake spec_prep' to populate the
|
185
|
+
# fixtures/modules
|
186
|
+
def ensure_fixture_modules
|
187
|
+
STDERR.puts " ** ensure_fixture_modules" if ENV['BEAKER_helpers_verbose']
|
188
|
+
unless ENV['BEAKER_spec_prep'] == 'no'
|
189
|
+
puts "== checking prepped modules from .fixtures.yml"
|
190
|
+
puts " -- (use BEAKER_spec_prep=no to disable)"
|
191
|
+
missing_modules = []
|
192
|
+
pupmods_in_fixtures_yml.each do |pupmod|
|
193
|
+
STDERR.puts " ** -- ensure_fixture_modules: '#{pupmod}'" if ENV['BEAKER_helpers_verbose']
|
194
|
+
mod_root = File.expand_path( "spec/fixtures/modules/#{pupmod}", File.dirname( fixtures_yml_path ))
|
195
|
+
missing_modules << pupmod unless File.directory? mod_root
|
196
|
+
end
|
197
|
+
puts " -- #{missing_modules.size} modules need to be prepped"
|
198
|
+
unless missing_modules.empty?
|
199
|
+
cmd = 'bundle exec rake spec_prep'
|
200
|
+
puts " -- running spec_prep: '#{cmd}'"
|
201
|
+
%x(#{cmd})
|
202
|
+
else
|
203
|
+
puts " == all fixture modules present"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
STDERR.puts " ** -- ensure_fixture_modules: finished" if ENV['BEAKER_helpers_verbose']
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
# Copy the local fixture modules (under `spec/fixtures/modules`) onto each SUT
|
211
|
+
def copy_fixture_modules_to( suts = hosts, opts = {})
|
212
|
+
ensure_fixture_modules
|
213
|
+
|
214
|
+
opts[:pluginsync] = opts.fetch(:pluginsync, true)
|
215
|
+
|
216
|
+
unless ENV['BEAKER_copy_fixtures'] == 'no'
|
217
|
+
parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
|
218
|
+
block_on(suts, :run_in_parallel => parallel) do |sut|
|
219
|
+
STDERR.puts " ** copy_fixture_modules_to: '#{sut}'" if ENV['BEAKER_helpers_verbose']
|
220
|
+
|
221
|
+
# Use spec_prep to provide modules (this supports isolated networks)
|
222
|
+
unless ENV['BEAKER_use_fixtures_dir_for_modules'] == 'no'
|
223
|
+
|
224
|
+
# NOTE: As a result of BKR-723, which does not look easy to fix, we
|
225
|
+
# cannot rely on `copy_module_to()` to choose a sane default for
|
226
|
+
# `target_module_path`. This workaround queries each SUT's
|
227
|
+
# `modulepath` and targets the first one.
|
228
|
+
target_module_path = puppet_modulepath_on(sut).first
|
229
|
+
|
230
|
+
mod_root = File.expand_path( "spec/fixtures/modules", File.dirname( fixtures_yml_path ))
|
231
|
+
|
232
|
+
Dir.chdir(mod_root) do
|
233
|
+
# Have to do things the slow way on Windows
|
234
|
+
if is_windows?(sut)
|
235
|
+
Dir.glob('*') do |module_dir|
|
236
|
+
if File.directory?(module_dir)
|
237
|
+
copy_module_to( sut, {
|
238
|
+
:source => module_dir,
|
239
|
+
:module_name => module_dir,
|
240
|
+
:target_module_path => target_module_path
|
241
|
+
})
|
242
|
+
end
|
243
|
+
end
|
244
|
+
else
|
245
|
+
begin
|
246
|
+
tarfile = "#{Simp::BeakerHelpers.tmpname}.tar"
|
247
|
+
|
248
|
+
excludes = PUPPET_MODULE_INSTALL_IGNORE.map do |x|
|
249
|
+
x = "--exclude '*/#{x}'"
|
250
|
+
end.join(' ')
|
251
|
+
|
252
|
+
%x(tar -ch #{excludes} -f #{tarfile} *)
|
253
|
+
|
254
|
+
if File.exist?(tarfile)
|
255
|
+
copy_to(sut, tarfile, target_module_path, opts)
|
256
|
+
else
|
257
|
+
fail("Error: module tar file '#{tarfile}' could not be created at #{mod_root}")
|
258
|
+
end
|
259
|
+
|
260
|
+
on(sut, "cd #{target_module_path} && tar -xf #{File.basename(tarfile)}")
|
261
|
+
ensure
|
262
|
+
FileUtils.remove_entry(tarfile, true)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
STDERR.puts ' ** copy_fixture_modules_to: finished' if ENV['BEAKER_helpers_verbose']
|
270
|
+
|
271
|
+
# sync custom facts from the new modules to each SUT's factpath
|
272
|
+
pluginsync_on(suts) if opts[:pluginsync]
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
# Configure and reboot SUTs into FIPS mode
|
277
|
+
def enable_fips_mode_on( suts = hosts )
|
278
|
+
puts '== configuring FIPS mode on SUTs'
|
279
|
+
puts ' -- (use BEAKER_fips=no to disable)'
|
280
|
+
parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
|
281
|
+
|
282
|
+
block_on(suts, :run_in_parallel => parallel) do |sut|
|
283
|
+
if is_windows?(sut)
|
284
|
+
puts " -- SKIPPING #{sut} because it is windows"
|
285
|
+
next
|
286
|
+
end
|
287
|
+
|
288
|
+
puts " -- enabling FIPS on '#{sut}'"
|
289
|
+
|
290
|
+
# We need to use FIPS compliant algorithms and keylengths as per the FIPS
|
291
|
+
# certification.
|
292
|
+
on(sut, 'puppet config set digest_algorithm sha256')
|
293
|
+
on(sut, 'puppet config set keylength 2048')
|
294
|
+
|
295
|
+
# We need to be able to get back into our system!
|
296
|
+
# Make these safe for all systems, even old ones.
|
297
|
+
# TODO Use simp-ssh Puppet module appropriately (i.e., in a fashion
|
298
|
+
# that doesn't break vagrant access and is appropriate for
|
299
|
+
# typical module tests.)
|
300
|
+
fips_ssh_ciphers = [ 'aes256-ctr','aes192-ctr','aes128-ctr']
|
301
|
+
on(sut, %(sed -i '/Ciphers /d' /etc/ssh/sshd_config))
|
302
|
+
on(sut, %(echo 'Ciphers #{fips_ssh_ciphers.join(',')}' >> /etc/ssh/sshd_config))
|
303
|
+
|
304
|
+
fips_enable_modulepath = ''
|
305
|
+
|
306
|
+
if pupmods_in_fixtures_yml.include?('fips')
|
307
|
+
copy_fixture_modules_to(sut)
|
308
|
+
else
|
309
|
+
# If we don't already have the simp-fips module installed
|
310
|
+
#
|
311
|
+
# Use the simp-fips Puppet module to set FIPS up properly:
|
312
|
+
# Download the appropriate version of the module and its dependencies from PuppetForge.
|
313
|
+
# TODO provide a R10k download option in which user provides a Puppetfile
|
314
|
+
# with simp-fips and its dependencies
|
315
|
+
on(sut, 'mkdir -p /root/.beaker_fips/modules')
|
316
|
+
|
317
|
+
fips_enable_modulepath = '--modulepath=/root/.beaker_fips/modules'
|
318
|
+
|
319
|
+
module_install_cmd = 'puppet module install simp-fips --target-dir=/root/.beaker_fips/modules'
|
320
|
+
|
321
|
+
if ENV['BEAKER_fips_module_version']
|
322
|
+
module_install_cmd += " --version #{ENV['BEAKER_fips_module_version']}"
|
323
|
+
end
|
324
|
+
|
325
|
+
on(sut, module_install_cmd)
|
326
|
+
end
|
327
|
+
|
328
|
+
# Enable FIPS and then reboot to finish.
|
329
|
+
on(sut, %(puppet apply --verbose #{fips_enable_modulepath} -e "class { 'fips': enabled => true }"))
|
330
|
+
|
331
|
+
# Work around Vagrant and cipher restrictions in EL8+
|
332
|
+
#
|
333
|
+
# Hopefully, Vagrant will update the used ciphers at some point but who
|
334
|
+
# knows when that will be
|
335
|
+
opensshserver_config = '/etc/crypto-policies/back-ends/opensshserver.config'
|
336
|
+
if file_exists_on(sut, opensshserver_config)
|
337
|
+
on(sut, "sed --follow-symlinks -i 's/PubkeyAcceptedKeyTypes=/PubkeyAcceptedKeyTypes=ssh-rsa,/' #{opensshserver_config}")
|
338
|
+
end
|
339
|
+
|
340
|
+
sut.reboot
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# Collect all 'yum_repos' entries from the host nodeset.
|
345
|
+
# The acceptable format is as follows:
|
346
|
+
# yum_repos:
|
347
|
+
# <repo_name>:
|
348
|
+
# url: <URL>
|
349
|
+
# gpgkeys:
|
350
|
+
# - <URL to GPGKEY1>
|
351
|
+
# - <URL to GPGKEY2>
|
352
|
+
def enable_yum_repos_on( suts = hosts )
|
353
|
+
parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
|
354
|
+
block_on(suts, :run_in_parallel => parallel) do |sut|
|
355
|
+
if sut['yum_repos']
|
356
|
+
sut['yum_repos'].each_pair do |repo, metadata|
|
357
|
+
repo_manifest = create_yum_resource( repo, metadata)
|
358
|
+
|
359
|
+
apply_manifest_on(sut, repo_manifest, :catch_failures => true)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def create_yum_resource( repo, metadata )
|
366
|
+
repo_attrs = [
|
367
|
+
:assumeyes,
|
368
|
+
:bandwidth,
|
369
|
+
:cost,
|
370
|
+
:deltarpm_metadata_percentage,
|
371
|
+
:deltarpm_percentage,
|
372
|
+
:descr,
|
373
|
+
:enabled,
|
374
|
+
:enablegroups,
|
375
|
+
:exclude,
|
376
|
+
:failovermethod,
|
377
|
+
:gpgcakey,
|
378
|
+
:gpgcheck,
|
379
|
+
:http_caching,
|
380
|
+
:include,
|
381
|
+
:includepkgs,
|
382
|
+
:keepalive,
|
383
|
+
:metadata_expire,
|
384
|
+
:metalink,
|
385
|
+
:mirrorlist,
|
386
|
+
:mirrorlist_expire,
|
387
|
+
:priority,
|
388
|
+
:protect,
|
389
|
+
:provider,
|
390
|
+
:proxy,
|
391
|
+
:proxy_password,
|
392
|
+
:proxy_username,
|
393
|
+
:repo_gpgcheck,
|
394
|
+
:retries,
|
395
|
+
:s3_enabled,
|
396
|
+
:skip_if_unavailable,
|
397
|
+
:sslcacert,
|
398
|
+
:sslclientcert,
|
399
|
+
:sslclientkey,
|
400
|
+
:sslverify,
|
401
|
+
:target,
|
402
|
+
:throttle,
|
403
|
+
:timeout
|
404
|
+
]
|
405
|
+
|
406
|
+
repo_manifest = %(yumrepo { #{repo}:)
|
407
|
+
|
408
|
+
repo_manifest_opts = []
|
409
|
+
|
410
|
+
# Legacy Support
|
411
|
+
urls = !metadata[:url].nil? ? metadata[:url] : metadata[:baseurl]
|
412
|
+
if urls
|
413
|
+
repo_manifest_opts << 'baseurl => ' + '"' + Array(urls).flatten.join('\n ').gsub('$','\$') + '"'
|
414
|
+
end
|
415
|
+
|
416
|
+
# Legacy Support
|
417
|
+
gpgkeys = !metadata[:gpgkeys].nil? ? metadata[:gpgkeys] : metadata[:gpgkey]
|
418
|
+
if gpgkeys
|
419
|
+
repo_manifest_opts << 'gpgkey => ' + '"' + Array(gpgkeys).flatten.join('\n ').gsub('$','\$') + '"'
|
420
|
+
end
|
421
|
+
|
422
|
+
repo_attrs.each do |attr|
|
423
|
+
if metadata[attr]
|
424
|
+
repo_manifest_opts << "#{attr} => '#{metadata[attr]}'"
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
repo_manifest = repo_manifest + %(\n#{repo_manifest_opts.join(",\n")}) + "\n}\n"
|
429
|
+
end
|
430
|
+
|
431
|
+
def linux_errata( sut )
|
432
|
+
# We need to be able to flip between server and client without issue
|
433
|
+
on sut, 'puppet resource group puppet gid=52'
|
434
|
+
on sut, 'puppet resource user puppet comment="Puppet" gid="52" uid="52" home="/var/lib/puppet" managehome=true'
|
435
|
+
|
436
|
+
# Make sure we have a domain on our host
|
437
|
+
current_domain = fact_on(sut, 'domain').strip
|
438
|
+
hostname = fact_on(sut, 'hostname').strip
|
439
|
+
|
440
|
+
if current_domain.empty?
|
441
|
+
new_fqdn = hostname + '.beaker.test'
|
442
|
+
|
443
|
+
on(sut, "sed -i 's/#{hostname}.*/#{new_fqdn} #{hostname}/' /etc/hosts")
|
444
|
+
on(sut, "echo '#{new_fqdn}' > /etc/hostname", :accept_all_exit_codes => true)
|
445
|
+
on(sut, "hostname #{new_fqdn}", :accept_all_exit_codes => true)
|
446
|
+
|
447
|
+
if sut.file_exist?('/etc/sysconfig/network')
|
448
|
+
on(sut, "sed -s '/HOSTNAME=/d' /etc/sysconfig/network")
|
449
|
+
on(sut, "echo 'HOSTNAME=#{new_fqdn}' >> /etc/sysconfig/network")
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
if fact_on(sut, 'domain').strip.empty?
|
454
|
+
fail("Error: hosts must have an FQDN, got domain='#{current_domain}'")
|
455
|
+
end
|
456
|
+
|
457
|
+
# This may not exist in docker so just skip the whole thing
|
458
|
+
if sut.file_exist?('/etc/ssh')
|
459
|
+
# SIMP uses a central ssh key location so we prep that spot in case we
|
460
|
+
# flip to the SIMP SSH module.
|
461
|
+
on(sut, 'mkdir -p /etc/ssh/local_keys')
|
462
|
+
on(sut, 'chown -R root:root /etc/ssh/local_keys')
|
463
|
+
on(sut, 'chmod 755 /etc/ssh/local_keys')
|
464
|
+
|
465
|
+
user_info = on(sut, 'getent passwd').stdout.lines
|
466
|
+
|
467
|
+
# Hash of user => home_dir
|
468
|
+
# Exclude silly directories
|
469
|
+
# * /
|
470
|
+
# * /dev/*
|
471
|
+
# * /s?bin
|
472
|
+
# * /proc
|
473
|
+
user_info = Hash[
|
474
|
+
user_info.map do |u|
|
475
|
+
u.strip!
|
476
|
+
u = u.split(':')
|
477
|
+
u[5] =~ %r{^(/|/dev/.*|/s?bin/?.*|/proc/?.*)$} ? [nil] : [u[0], u[5]]
|
478
|
+
end
|
479
|
+
]
|
480
|
+
|
481
|
+
user_info.keys.each do |user|
|
482
|
+
src_file = "#{user_info[user]}/.ssh/authorized_keys"
|
483
|
+
tgt_file = "/etc/ssh/local_keys/#{user}"
|
484
|
+
|
485
|
+
on(sut, %{if [ -f "#{src_file}" ]; then cp -a -f "#{src_file}" "#{tgt_file}" && chmod 644 "#{tgt_file}"; fi}, :silent => true)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# SIMP uses structured facts, therefore stringify_facts must be disabled
|
490
|
+
unless ENV['BEAKER_stringify_facts'] == 'yes'
|
491
|
+
on sut, 'puppet config set stringify_facts false'
|
492
|
+
end
|
493
|
+
|
494
|
+
# Occasionally we run across something similar to BKR-561, so to ensure we
|
495
|
+
# at least have the host defaults:
|
496
|
+
#
|
497
|
+
# :hieradatadir is used as a canary here; it isn't the only missing key
|
498
|
+
unless sut.host_hash.key? :hieradatadir
|
499
|
+
configure_type_defaults_on(sut)
|
500
|
+
end
|
501
|
+
|
502
|
+
if fact_on(sut, 'osfamily') == 'RedHat'
|
503
|
+
if fact_on(sut, 'operatingsystem') == 'RedHat'
|
504
|
+
RSpec.configure do |c|
|
505
|
+
c.before(:all) do
|
506
|
+
rhel_rhsm_subscribe(sut)
|
507
|
+
end
|
508
|
+
|
509
|
+
c.after(:all) do
|
510
|
+
rhel_rhsm_unsubscribe(sut)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
enable_yum_repos_on(sut)
|
516
|
+
|
517
|
+
# net-tools required for netstat utility being used by be_listening
|
518
|
+
if fact_on(sut, 'operatingsystemmajrelease') == '7'
|
519
|
+
pp = <<-EOS
|
520
|
+
package { 'net-tools': ensure => installed }
|
521
|
+
EOS
|
522
|
+
apply_manifest_on(sut, pp, :catch_failures => false)
|
523
|
+
end
|
524
|
+
|
525
|
+
# Clean up YUM prior to starting our test runs.
|
526
|
+
on(sut, 'yum clean all')
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
# Register a RHEL system with a development license
|
531
|
+
#
|
532
|
+
# Must set BEAKER_RHSM_USER and BEAKER_RHSM_PASS environment variables or pass them in as
|
533
|
+
# parameters
|
534
|
+
def rhel_rhsm_subscribe(sut, *opts)
|
535
|
+
require 'securerandom'
|
536
|
+
|
537
|
+
rhsm_opts = {
|
538
|
+
:username => ENV['BEAKER_RHSM_USER'],
|
539
|
+
:password => ENV['BEAKER_RHSM_PASS'],
|
540
|
+
:system_name => "#{sut}_beaker_#{Time.now.to_i}_#{SecureRandom.uuid}",
|
541
|
+
:repo_list => {
|
542
|
+
'7' => [
|
543
|
+
'rhel-7-server-extras-rpms',
|
544
|
+
'rhel-7-server-optional-rpms',
|
545
|
+
'rhel-7-server-rh-common-rpms',
|
546
|
+
'rhel-7-server-rpms',
|
547
|
+
'rhel-7-server-supplementary-rpms'
|
548
|
+
],
|
549
|
+
'8' => [
|
550
|
+
'rhel-8-for-x86_64-baseos-rpms',
|
551
|
+
'rhel-8-for-x86_64-supplementary-rpms'
|
552
|
+
]
|
553
|
+
}
|
554
|
+
}
|
555
|
+
|
556
|
+
if opts && opts.is_a?(Hash)
|
557
|
+
rhsm_opts.merge!(opts)
|
558
|
+
end
|
559
|
+
|
560
|
+
os = fact_on(sut, 'operatingsystem').strip
|
561
|
+
os_release = fact_on(sut, 'operatingsystemmajrelease').strip
|
562
|
+
|
563
|
+
if os == 'RedHat'
|
564
|
+
unless rhsm_opts[:username] && rhsm_opts[:password]
|
565
|
+
fail("You must set BEAKER_RHSM_USER and BEAKER_RHSM_PASS environment variables to register RHEL systems")
|
566
|
+
end
|
567
|
+
|
568
|
+
sub_status = on(sut, 'subscription-manager status', :accept_all_exit_codes => true)
|
569
|
+
unless sub_status.exit_code == 0
|
570
|
+
logger.info("Registering #{sut} via subscription-manager")
|
571
|
+
on(sut, %{subscription-manager register --auto-attach --name='#{rhsm_opts[:system_name]}' --username='#{rhsm_opts[:username]}' --password='#{rhsm_opts[:password]}'}, :silent => true)
|
572
|
+
end
|
573
|
+
|
574
|
+
if rhsm_opts[:repo_list][os_release]
|
575
|
+
rhel_repo_enable(sut, rhsm_opts[:repo_list][os_release])
|
576
|
+
else
|
577
|
+
logger.warn("simp-beaker-helpers:#{__method__} => Default repos for RHEL '#{os_release}' not found")
|
578
|
+
end
|
579
|
+
|
580
|
+
# Ensure that all users can access the entitlements since we don't know
|
581
|
+
# who we'll be running jobs as (often not root)
|
582
|
+
on(sut, 'chmod -R ugo+rX /etc/pki/entitlement', :accept_all_exit_codes => true)
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def sosreport(sut, dest='sosreports')
|
587
|
+
sut.install_package('sos')
|
588
|
+
on(sut, 'sosreport --batch')
|
589
|
+
|
590
|
+
files = on(sut, 'ls /var/tmp/sosreport* /tmp/sosreport* 2>/dev/null', :accept_all_exit_codes => true).output.lines.map(&:strip)
|
591
|
+
|
592
|
+
FileUtils.mkdir_p(dest)
|
593
|
+
|
594
|
+
files.each do |file|
|
595
|
+
scp_from(sut, file, File.absolute_path(dest))
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
def rhel_repo_enable(sut, repos)
|
600
|
+
Array(repos).each do |repo|
|
601
|
+
on(sut, %{subscription-manager repos --enable #{repo}})
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
def rhel_repo_disable(sut, repos)
|
606
|
+
Array(repos).each do |repo|
|
607
|
+
on(sut, %{subscription-manager repos --disable #{repo}}, :accept_all_exit_codes => true)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def rhel_rhsm_unsubscribe(sut)
|
612
|
+
on(sut, %{subscription-manager unregister}, :accept_all_exit_codes => true)
|
613
|
+
end
|
614
|
+
|
615
|
+
# Apply known OS fixes we need to run Beaker on each SUT
|
616
|
+
def fix_errata_on( suts = hosts )
|
617
|
+
parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
|
618
|
+
block_on(suts, :run_in_parallel => parallel) do |sut|
|
619
|
+
if is_windows?(sut)
|
620
|
+
# Load the Windows requirements
|
621
|
+
require 'simp/beaker_helpers/windows'
|
622
|
+
|
623
|
+
# Install the necessary windows certificate for testing
|
624
|
+
#
|
625
|
+
# https://petersouter.xyz/testing-windows-with-beaker-without-cygwin/
|
626
|
+
geotrust_global_ca = <<~EOM.freeze
|
627
|
+
-----BEGIN CERTIFICATE-----
|
628
|
+
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
|
629
|
+
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
|
630
|
+
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
|
631
|
+
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
|
632
|
+
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
|
633
|
+
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
|
634
|
+
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
|
635
|
+
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
|
636
|
+
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
|
637
|
+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
|
638
|
+
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
|
639
|
+
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
|
640
|
+
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
|
641
|
+
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
|
642
|
+
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
|
643
|
+
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
|
644
|
+
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
|
645
|
+
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
|
646
|
+
-----END CERTIFICATE-----
|
647
|
+
EOM
|
648
|
+
|
649
|
+
install_cert_on_windows(sut, 'geotrustglobal', geotrust_global_ca)
|
650
|
+
else
|
651
|
+
linux_errata(sut)
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
# Configure and reboot SUTs into FIPS mode
|
656
|
+
if ENV['BEAKER_fips'] == 'yes'
|
657
|
+
enable_fips_mode_on(suts)
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
# Generate a fake openssl CA + certs for each host on a given SUT
|
662
|
+
#
|
663
|
+
# The directory structure is the same as what FakeCA drops into keydist/
|
664
|
+
#
|
665
|
+
# NOTE: This generates everything within an SUT and copies it back out.
|
666
|
+
# This is because it is assumed the SUT will have the appropriate
|
667
|
+
# openssl in its environment, which may not be true of the host.
|
668
|
+
def run_fake_pki_ca_on( ca_sut = master, suts = hosts, local_dir = '' )
|
669
|
+
puts "== Fake PKI CA"
|
670
|
+
pki_dir = File.expand_path( "../../files/pki", File.dirname(__FILE__))
|
671
|
+
host_dir = '/root/pki'
|
672
|
+
|
673
|
+
ca_sut.mkdir_p(host_dir)
|
674
|
+
Dir[ File.join(pki_dir, '*') ].each{|f| copy_to( ca_sut, f, host_dir)}
|
675
|
+
|
676
|
+
# Collect network information from all SUTs
|
677
|
+
#
|
678
|
+
# We need this so that we don't insert any common IP addresses into certs
|
679
|
+
suts_network_info = {}
|
680
|
+
|
681
|
+
hosts.each do |host|
|
682
|
+
fqdn = fact_on(host, 'fqdn').strip
|
683
|
+
|
684
|
+
host_entry = { fqdn => [] }
|
685
|
+
|
686
|
+
# Ensure that all interfaces are active prior to collecting data
|
687
|
+
activate_interfaces(host) unless ENV['BEAKER_no_fix_interfaces']
|
688
|
+
|
689
|
+
# Gather the IP Addresses for the host to embed in the cert
|
690
|
+
interfaces = fact_on(host, 'interfaces').strip.split(',')
|
691
|
+
interfaces.each do |interface|
|
692
|
+
ipaddress = fact_on(host, "ipaddress_#{interface}")
|
693
|
+
|
694
|
+
next if ipaddress.nil? || ipaddress.empty? || ipaddress.start_with?('127.')
|
695
|
+
|
696
|
+
host_entry[fqdn] << ipaddress.strip
|
697
|
+
|
698
|
+
unless host_entry[fqdn].empty?
|
699
|
+
suts_network_info[fqdn] = host_entry[fqdn]
|
700
|
+
end
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
# Get all of the repeated SUT IP addresses:
|
705
|
+
# 1. Create a hash of elements that have a key that is the value and
|
706
|
+
# elements that are the same value
|
707
|
+
# 2. Grab all elements that have more than one value (therefore, were
|
708
|
+
# repeated)
|
709
|
+
# 3. Pull out an Array of all of the common element keys for future
|
710
|
+
# comparison
|
711
|
+
common_ip_addresses = suts_network_info
|
712
|
+
.values.flatten
|
713
|
+
.group_by{ |x| x }
|
714
|
+
.select{|k,v| v.size > 1}
|
715
|
+
.keys
|
716
|
+
|
717
|
+
# generate PKI certs for each SUT
|
718
|
+
Dir.mktmpdir do |dir|
|
719
|
+
pki_hosts_file = File.join(dir, 'pki.hosts')
|
720
|
+
|
721
|
+
File.open(pki_hosts_file, 'w') do |fh|
|
722
|
+
suts_network_info.each do |fqdn, ipaddresses|
|
723
|
+
fh.puts ([fqdn] + (ipaddresses - common_ip_addresses)) .join(',')
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
copy_to(ca_sut, pki_hosts_file, host_dir)
|
728
|
+
# generate certs
|
729
|
+
on(ca_sut, "cd #{host_dir}; cat #{host_dir}/pki.hosts | xargs bash make.sh")
|
730
|
+
end
|
731
|
+
|
732
|
+
# if a local_dir was provided, copy everything down to it
|
733
|
+
unless local_dir.empty?
|
734
|
+
FileUtils.mkdir_p local_dir
|
735
|
+
scp_from( ca_sut, host_dir, local_dir )
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
739
|
+
# Copy a single SUT's PKI certs (with cacerts) onto an SUT.
|
740
|
+
#
|
741
|
+
# This simulates the result of pki::copy
|
742
|
+
#
|
743
|
+
# The directory structure is:
|
744
|
+
#
|
745
|
+
# SUT_BASE_DIR/
|
746
|
+
# pki/
|
747
|
+
# cacerts/cacerts.pem
|
748
|
+
# # This is a copy of cacerts.pem since cacerts.pem is a
|
749
|
+
# # collection of the CA certificates in pupmod-simp-pki
|
750
|
+
# cacerts/simp_auto_ca.pem
|
751
|
+
# public/fdqn.pub
|
752
|
+
# private/fdqn.pem
|
753
|
+
def copy_pki_to(sut, local_pki_dir, sut_base_dir = '/etc/pki/simp-testing')
|
754
|
+
fqdn = fact_on(sut, 'fqdn')
|
755
|
+
sut_pki_dir = File.join( sut_base_dir, 'pki' )
|
756
|
+
local_host_pki_tree = File.join(local_pki_dir,'pki','keydist',fqdn)
|
757
|
+
local_cacert = File.join(local_pki_dir,'pki','demoCA','cacert.pem')
|
758
|
+
|
759
|
+
sut.mkdir_p("#{sut_pki_dir}/public")
|
760
|
+
sut.mkdir_p("#{sut_pki_dir}/private")
|
761
|
+
sut.mkdir_p("#{sut_pki_dir}/cacerts")
|
762
|
+
copy_to(sut, "#{local_host_pki_tree}/#{fqdn}.pem", "#{sut_pki_dir}/private/")
|
763
|
+
copy_to(sut, "#{local_host_pki_tree}/#{fqdn}.pub", "#{sut_pki_dir}/public/")
|
764
|
+
|
765
|
+
copy_to(sut, local_cacert, "#{sut_pki_dir}/cacerts/simp_auto_ca.pem")
|
766
|
+
|
767
|
+
# NOTE: to match pki::copy, 'cacert.pem' is copied to 'cacerts.pem'
|
768
|
+
copy_to(sut, local_cacert, "#{sut_pki_dir}/cacerts/cacerts.pem")
|
769
|
+
|
770
|
+
# Need to hash all of the CA certificates so that apps can use them
|
771
|
+
# properly! This must happen on the host itself since it needs to match
|
772
|
+
# the native hashing algorithms.
|
773
|
+
hash_cmd = <<-EOM.strip
|
774
|
+
cd #{sut_pki_dir}/cacerts; \
|
775
|
+
for x in *; do \
|
776
|
+
if [ ! -h "$x" ]; then \
|
777
|
+
`openssl x509 -in $x >/dev/null 2>&1`; \
|
778
|
+
if [ $? -eq 0 ]; then \
|
779
|
+
hash=`openssl x509 -in $x -hash | head -1`; \
|
780
|
+
ln -sf $x $hash.0; \
|
781
|
+
fi; \
|
782
|
+
fi; \
|
783
|
+
done
|
784
|
+
EOM
|
785
|
+
|
786
|
+
on(sut, hash_cmd)
|
787
|
+
end
|
788
|
+
|
789
|
+
# Copy a CA keydist/ directory of CA+host certs into an SUT
|
790
|
+
#
|
791
|
+
# This simulates the output of FakeCA's gencerts_nopass.sh to keydist/
|
792
|
+
def copy_keydist_to( ca_sut = master, host_keydist_dir = nil )
|
793
|
+
if !host_keydist_dir
|
794
|
+
modulepath = puppet_modulepath_on(ca_sut)
|
795
|
+
|
796
|
+
host_keydist_dir = "#{modulepath.first}/pki/files/keydist"
|
797
|
+
end
|
798
|
+
on ca_sut, "rm -rf #{host_keydist_dir}/*"
|
799
|
+
ca_sut.mkdir_p(host_keydist_dir)
|
800
|
+
on ca_sut, "cp -pR /root/pki/keydist/. #{host_keydist_dir}/"
|
801
|
+
on ca_sut, "chgrp -R puppet #{host_keydist_dir}"
|
802
|
+
end
|
803
|
+
|
804
|
+
|
805
|
+
# Activate all network interfaces on the target system
|
806
|
+
#
|
807
|
+
# This is generally needed if the upstream vendor does not activate all
|
808
|
+
# interfaces by default (EL7 for example)
|
809
|
+
#
|
810
|
+
# Can be passed any number of hosts either singly or as an Array
|
811
|
+
def activate_interfaces(hosts)
|
812
|
+
parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
|
813
|
+
block_on(hosts, :run_in_parallel => parallel) do |host|
|
814
|
+
if host[:platform] =~ /windows/
|
815
|
+
puts " -- SKIPPING #{host} because it is windows"
|
816
|
+
next
|
817
|
+
end
|
818
|
+
|
819
|
+
interfaces_fact = retry_on(host,'facter interfaces', verbose: true).stdout
|
820
|
+
|
821
|
+
interfaces = interfaces_fact.strip.split(',')
|
822
|
+
interfaces.delete_if { |x| x =~ /^lo/ }
|
823
|
+
|
824
|
+
interfaces.each do |iface|
|
825
|
+
if fact_on(host, "ipaddress_#{iface}").strip.empty?
|
826
|
+
on(host, "ifup #{iface}", :accept_all_exit_codes => true)
|
827
|
+
end
|
828
|
+
end
|
829
|
+
end
|
830
|
+
end
|
831
|
+
|
832
|
+
## Inline Hiera Helpers ##
|
833
|
+
## These will be integrated into core Beaker at some point ##
|
834
|
+
|
835
|
+
# Set things up for the inline hieradata functions 'set_hieradata_on'
|
836
|
+
# and 'clear_temp_hieradata'
|
837
|
+
#
|
838
|
+
#
|
839
|
+
require 'rspec'
|
840
|
+
RSpec.configure do |c|
|
841
|
+
c.before(:all) do
|
842
|
+
@temp_hieradata_dirs = @temp_hieradata_dirs || []
|
843
|
+
end
|
844
|
+
|
845
|
+
# We can't guarantee that the upstream vendor isn't disabling interfaces so
|
846
|
+
# we need to turn them on at each context run
|
847
|
+
c.before(:context) do
|
848
|
+
activate_interfaces(hosts) unless ENV['BEAKER_no_fix_interfaces']
|
849
|
+
end
|
850
|
+
|
851
|
+
c.after(:all) do
|
852
|
+
clear_temp_hieradata
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
# Return the contents of a file on the remote host
|
857
|
+
#
|
858
|
+
# @param sut [Host] the host upon which to operate
|
859
|
+
# @param path [String] the path to the target file
|
860
|
+
# @param trim [Boolean] remove leading and trailing whitespace
|
861
|
+
#
|
862
|
+
# @return [String, nil] the contents of the remote file
|
863
|
+
def file_content_on(sut, path, trim=true)
|
864
|
+
file_content = nil
|
865
|
+
|
866
|
+
if file_exists_on(sut, path)
|
867
|
+
Dir.mktempdir do |dir|
|
868
|
+
scp_from(host, path, dir)
|
869
|
+
|
870
|
+
file_content = File.read(File.basename(path))
|
871
|
+
end
|
872
|
+
end
|
873
|
+
|
874
|
+
return file_content
|
875
|
+
end
|
876
|
+
|
877
|
+
# Retrieve the default hiera.yaml path
|
878
|
+
#
|
879
|
+
# @param sut [Host] one host to act upon
|
880
|
+
#
|
881
|
+
# @returns [Hash] path to the default environment's hiera.yaml
|
882
|
+
def hiera_config_path_on(sut)
|
883
|
+
File.join(puppet_environment_path_on(sut), 'hiera.yaml')
|
884
|
+
end
|
885
|
+
|
886
|
+
# Retrieve the default environment hiera.yaml
|
887
|
+
#
|
888
|
+
# @param sut [Host] one host to act upon
|
889
|
+
#
|
890
|
+
# @returns [Hash] content of the default environment's hiera.yaml
|
891
|
+
def get_hiera_config_on(sut)
|
892
|
+
file_content_on(sut, hiera_config_path_on(sut))
|
893
|
+
end
|
894
|
+
|
895
|
+
# Updates the default environment hiera.yaml
|
896
|
+
#
|
897
|
+
# @param sut [Host] One host to act upon
|
898
|
+
# @param hiera_yaml [Hash, String] The data to place into hiera.yaml
|
899
|
+
#
|
900
|
+
# @returns [void]
|
901
|
+
def set_hiera_config_on(sut, hiera_yaml)
|
902
|
+
hiera_yaml = hiera_yaml.to_yaml if hiera_yaml.is_a?(Hash)
|
903
|
+
|
904
|
+
create_remote_file(sut, hiera_config_path_on(sut), hiera_yaml)
|
905
|
+
end
|
906
|
+
|
907
|
+
# Writes a YAML file in the Hiera :datadir of a Beaker::Host.
|
908
|
+
#
|
909
|
+
# @note This is useless unless Hiera is configured to use the data file.
|
910
|
+
# @see `#set_hiera_config_on`
|
911
|
+
#
|
912
|
+
# @param sut [Array<Host>, String, Symbol] One or more hosts to act upon.
|
913
|
+
#
|
914
|
+
# @param hieradata [Hash, String] The full hiera data structure to write to
|
915
|
+
# the system.
|
916
|
+
#
|
917
|
+
# @param terminus [String] DEPRECATED - This will be removed in a future
|
918
|
+
# release and currently has no effect.
|
919
|
+
#
|
920
|
+
# @return [Nil]
|
921
|
+
#
|
922
|
+
# @note This creates a tempdir on the host machine which should be removed
|
923
|
+
# using `#clear_temp_hieradata` in the `after(:all)` hook. It may also be
|
924
|
+
# retained for debugging purposes.
|
925
|
+
#
|
926
|
+
def write_hieradata_to(sut, hieradata, terminus = 'deprecated')
|
927
|
+
@temp_hieradata_dirs ||= []
|
928
|
+
data_dir = Dir.mktmpdir('hieradata')
|
929
|
+
@temp_hieradata_dirs << data_dir
|
930
|
+
|
931
|
+
fh = File.open(File.join(data_dir, 'common.yaml'), 'w')
|
932
|
+
if hieradata.is_a?(String)
|
933
|
+
fh.puts(hieradata)
|
934
|
+
else
|
935
|
+
fh.puts(hieradata.to_yaml)
|
936
|
+
end
|
937
|
+
fh.close
|
938
|
+
|
939
|
+
copy_hiera_data_to sut, File.join(data_dir, 'common.yaml')
|
940
|
+
end
|
941
|
+
|
942
|
+
# A shim to stand in for the now deprecated copy_hiera_data_to function
|
943
|
+
#
|
944
|
+
# @param sut [Host] One host to act upon
|
945
|
+
#
|
946
|
+
# @param [Path] File containing hiera data
|
947
|
+
def copy_hiera_data_to(sut, path)
|
948
|
+
copy_to(sut, path, hiera_datadir(sut))
|
949
|
+
end
|
950
|
+
|
951
|
+
# A shim to stand in for the now deprecated hiera_datadir function
|
952
|
+
#
|
953
|
+
# Note: This may not work if you've shoved data somewhere that is not the
|
954
|
+
# default and/or are manipulating the default hiera.yaml.
|
955
|
+
#
|
956
|
+
# @param sut [Host] One host to act upon
|
957
|
+
#
|
958
|
+
# @returns [String] Path to the Hieradata directory on the target system
|
959
|
+
def hiera_datadir(sut)
|
960
|
+
# This output lets us know where Hiera is configured to look on the system
|
961
|
+
puppet_lookup_info = on(sut, 'puppet lookup --explain test__simp__test').output.strip.lines
|
962
|
+
|
963
|
+
if sut.puppet_configprint['manifest'].nil? || sut.puppet_configprint['manifest'].empty?
|
964
|
+
fail("No output returned from `puppet config print manifest` on #{sut}")
|
965
|
+
end
|
966
|
+
|
967
|
+
puppet_env_path = puppet_environment_path_on(sut)
|
968
|
+
|
969
|
+
# We'll just take the first match since Hiera will find things there
|
970
|
+
puppet_lookup_info = puppet_lookup_info.grep(/Path "/).grep(Regexp.new(puppet_env_path))
|
971
|
+
|
972
|
+
# Grep always returns an Array
|
973
|
+
if puppet_lookup_info.empty?
|
974
|
+
fail("Could not determine hiera data directory under #{puppet_env_path} on #{sut}")
|
975
|
+
end
|
976
|
+
|
977
|
+
# Snag the actual path without the extra bits
|
978
|
+
puppet_lookup_info = puppet_lookup_info.first.strip.split('"').last
|
979
|
+
|
980
|
+
# Make the parent directories exist
|
981
|
+
sut.mkdir_p(File.dirname(puppet_lookup_info))
|
982
|
+
|
983
|
+
# We just want the data directory name
|
984
|
+
datadir_name = puppet_lookup_info.split(puppet_env_path).last
|
985
|
+
|
986
|
+
# Grab the file separator to add back later
|
987
|
+
file_sep = datadir_name[0]
|
988
|
+
|
989
|
+
# Snag the first entry (this is the data directory)
|
990
|
+
datadir_name = datadir_name.split(file_sep)[1]
|
991
|
+
|
992
|
+
# Constitute the full path to the data directory
|
993
|
+
datadir_path = puppet_env_path + file_sep + datadir_name
|
994
|
+
|
995
|
+
# Return the path to the data directory
|
996
|
+
return datadir_path
|
997
|
+
end
|
998
|
+
|
999
|
+
# Write the provided data structure to Hiera's :datadir and configure Hiera to
|
1000
|
+
# use that data exclusively.
|
1001
|
+
#
|
1002
|
+
# @note This is authoritative. It manages both Hiera data and configuration,
|
1003
|
+
# so it may not be used with other Hiera data sources.
|
1004
|
+
#
|
1005
|
+
# @param sut [Array<Host>, String, Symbol] One or more hosts to act upon.
|
1006
|
+
#
|
1007
|
+
# @param heradata [Hash, String] The full hiera data structure to write to
|
1008
|
+
# the system.
|
1009
|
+
#
|
1010
|
+
# @param terminus [String] DEPRECATED - Will be removed in a future release.
|
1011
|
+
# All hieradata is written to the first discovered path via 'puppet
|
1012
|
+
# lookup'
|
1013
|
+
#
|
1014
|
+
# @return [Nil]
|
1015
|
+
#
|
1016
|
+
def set_hieradata_on(sut, hieradata, terminus = 'deprecated')
|
1017
|
+
write_hieradata_to sut, hieradata
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
|
1021
|
+
# Clean up all temporary hiera data files.
|
1022
|
+
#
|
1023
|
+
# Meant to be called from after(:all)
|
1024
|
+
def clear_temp_hieradata
|
1025
|
+
if @temp_hieradata_dirs && !@temp_hieradata_dirs.empty?
|
1026
|
+
@temp_hieradata_dirs.each do |data_dir|
|
1027
|
+
if File.exists?(data_dir)
|
1028
|
+
FileUtils.rm_r(data_dir)
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
|
1035
|
+
# pluginsync custom facts for all modules
|
1036
|
+
def pluginsync_on( suts = hosts )
|
1037
|
+
puts "== pluginsync_on'" if ENV['BEAKER_helpers_verbose']
|
1038
|
+
pluginsync_manifest =<<-PLUGINSYNC_MANIFEST
|
1039
|
+
file { $::settings::libdir:
|
1040
|
+
ensure => directory,
|
1041
|
+
source => 'puppet:///plugins',
|
1042
|
+
recurse => true,
|
1043
|
+
purge => true,
|
1044
|
+
backup => false,
|
1045
|
+
noop => false
|
1046
|
+
}
|
1047
|
+
PLUGINSYNC_MANIFEST
|
1048
|
+
parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
|
1049
|
+
apply_manifest_on(hosts, pluginsync_manifest, :run_in_parallel => parallel)
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
|
1053
|
+
# Looks up latest `puppet-agent` version by the version of its `puppet` gem
|
1054
|
+
#
|
1055
|
+
# @param puppet_version [String] target Puppet gem version. Works with
|
1056
|
+
# Gemfile comparison syntax (e.g., '4.0', '= 4.2', '~> 4.3.1', '> 5.1, < 5.5')
|
1057
|
+
#
|
1058
|
+
# @return [String,Nil] the `puppet-agent` version or nil
|
1059
|
+
#
|
1060
|
+
def latest_puppet_agent_version_for( puppet_version )
|
1061
|
+
return nil if puppet_version.nil?
|
1062
|
+
|
1063
|
+
require 'rubygems/requirement'
|
1064
|
+
require 'rubygems/version'
|
1065
|
+
require 'yaml'
|
1066
|
+
|
1067
|
+
_puppet_version = puppet_version.strip.split(',')
|
1068
|
+
|
1069
|
+
|
1070
|
+
@agent_version_table ||= YAML.load_file(
|
1071
|
+
File.expand_path(
|
1072
|
+
'../../files/puppet-agent-versions.yaml',
|
1073
|
+
File.dirname(__FILE__)
|
1074
|
+
)).fetch('version_mappings')
|
1075
|
+
_pair = @agent_version_table.find do |k,v|
|
1076
|
+
Gem::Requirement.new(_puppet_version).satisfied_by?(Gem::Version.new(k))
|
1077
|
+
end
|
1078
|
+
result = _pair ? _pair.last : nil
|
1079
|
+
|
1080
|
+
# If we didn't get a match, go look for published rubygems
|
1081
|
+
unless result
|
1082
|
+
puppet_gems = nil
|
1083
|
+
|
1084
|
+
Bundler.with_clean_env do
|
1085
|
+
puppet_gems = %x(gem search -ra -e puppet).match(/\((.+)\)/)
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
if puppet_gems
|
1089
|
+
puppet_gems = puppet_gems[1].split(/,?\s+/).select{|x| x =~ /^\d/}
|
1090
|
+
|
1091
|
+
# If we don't have a full version string, we need to massage it for the
|
1092
|
+
# match.
|
1093
|
+
begin
|
1094
|
+
if _puppet_version.size == 1
|
1095
|
+
Gem::Version.new(_puppet_version[0])
|
1096
|
+
if _puppet_version[0].count('.') < 2
|
1097
|
+
_puppet_version = "~> #{_puppet_version[0]}"
|
1098
|
+
end
|
1099
|
+
end
|
1100
|
+
rescue ArgumentError
|
1101
|
+
# this means _puppet_version is not just a version, but a version
|
1102
|
+
# specifier such as "= 5.2.3", "<= 5.1", "> 4", "~> 4.10.7"
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
result = puppet_gems.find do |ver|
|
1106
|
+
Gem::Requirement.new(_puppet_version).satisfied_by?(Gem::Version.new(ver))
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
return result
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
# returns hash with :puppet_install_version, :puppet_collection,
|
1115
|
+
# and :puppet_install_type keys determined from environment variables,
|
1116
|
+
# host settings, and/or defaults
|
1117
|
+
#
|
1118
|
+
# NOTE: BEAKER_PUPPET_AGENT_VERSION or PUPPET_INSTALL_VERSION or
|
1119
|
+
# PUPPET_VERSION takes precedence over BEAKER_PUPPET_COLLECTION
|
1120
|
+
# or host.options['puppet_collection'], when both a puppet
|
1121
|
+
# install version and a puppet collection are specified. This is
|
1122
|
+
# because the puppet install version can specify more precise
|
1123
|
+
# version information than is available from a puppet collection.
|
1124
|
+
def get_puppet_install_info
|
1125
|
+
# The first match is internal Beaker and the second is legacy SIMP
|
1126
|
+
puppet_install_version = ENV['BEAKER_PUPPET_AGENT_VERSION'] || ENV['PUPPET_INSTALL_VERSION'] || ENV['PUPPET_VERSION']
|
1127
|
+
|
1128
|
+
if puppet_install_version and !puppet_install_version.strip.empty?
|
1129
|
+
puppet_agent_version = latest_puppet_agent_version_for(puppet_install_version.strip)
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
if puppet_agent_version.nil?
|
1133
|
+
if puppet_collection = (ENV['BEAKER_PUPPET_COLLECTION'] || host.options['puppet_collection'])
|
1134
|
+
if puppet_collection =~ /puppet(\d+)/
|
1135
|
+
puppet_install_version = "~> #{$1}"
|
1136
|
+
puppet_agent_version = latest_puppet_agent_version_for(puppet_install_version)
|
1137
|
+
else
|
1138
|
+
raise("Error: Puppet Collection '#{puppet_collection}' must match /puppet(\\d+)/")
|
1139
|
+
end
|
1140
|
+
else
|
1141
|
+
puppet_agent_version = latest_puppet_agent_version_for(DEFAULT_PUPPET_AGENT_VERSION)
|
1142
|
+
end
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
if puppet_collection.nil?
|
1146
|
+
base_version = puppet_agent_version.to_i
|
1147
|
+
puppet_collection = "puppet#{base_version}" if base_version >= 5
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
{
|
1151
|
+
:puppet_install_version => puppet_agent_version,
|
1152
|
+
:puppet_collection => puppet_collection,
|
1153
|
+
:puppet_install_type => ENV.fetch('PUPPET_INSTALL_TYPE', 'agent')
|
1154
|
+
}
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
|
1158
|
+
# Replacement for `install_puppet` in spec_helper_acceptance.rb
|
1159
|
+
def install_puppet
|
1160
|
+
install_info = get_puppet_install_info
|
1161
|
+
|
1162
|
+
# In case Beaker needs this info internally
|
1163
|
+
ENV['PUPPET_INSTALL_VERSION'] = install_info[:puppet_install_version]
|
1164
|
+
if install_info[:puppet_collection]
|
1165
|
+
ENV['BEAKER_PUPPET_COLLECTION'] = install_info[:puppet_collection]
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
require 'beaker/puppet_install_helper'
|
1169
|
+
|
1170
|
+
run_puppet_install_helper(install_info[:puppet_install_type], install_info[:puppet_install_version])
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
# Configure all SIMP repos on a host and enable all but those listed in the disable list
|
1174
|
+
#
|
1175
|
+
# @param sut Host on which to configure SIMP repos
|
1176
|
+
# @param disable List of SIMP repos to disable
|
1177
|
+
# @raise if disable contains an invalid repo name.
|
1178
|
+
#
|
1179
|
+
# Examples:
|
1180
|
+
# install_simp_repos( myhost ) # install all the repos an enable them.
|
1181
|
+
# install_simp_repos( myhost, ['simp']) # install the repos but disable the simp repo.
|
1182
|
+
#
|
1183
|
+
# Current set of valid SIMP repo names:
|
1184
|
+
# 'simp'
|
1185
|
+
# 'simp_deps'
|
1186
|
+
#
|
1187
|
+
def install_simp_repos(sut, disable = [] )
|
1188
|
+
repos = {
|
1189
|
+
'simp' => {
|
1190
|
+
:baseurl => 'https://packagecloud.io/simp-project/6_X/el/$releasever/$basearch',
|
1191
|
+
:gpgkey => ['https://raw.githubusercontent.com/NationalSecurityAgency/SIMP/master/GPGKEYS/RPM-GPG-KEY-SIMP',
|
1192
|
+
'https://download.simp-project.com/simp/GPGKEYS/RPM-GPG-KEY-SIMP-6'
|
1193
|
+
],
|
1194
|
+
:gpgcheck => 1,
|
1195
|
+
:sslverify => 1,
|
1196
|
+
:sslcacert => '/etc/pki/tls/certs/ca-bundle.crt',
|
1197
|
+
:metadata_expire => 300
|
1198
|
+
},
|
1199
|
+
'simp_deps' => {
|
1200
|
+
:baseurl => 'https://packagecloud.io/simp-project/6_X_Dependencies/el/$releasever/$basearch',
|
1201
|
+
:gpgkey => ['https://raw.githubusercontent.com/NationalSecurityAgency/SIMP/master/GPGKEYS/RPM-GPG-KEY-SIMP',
|
1202
|
+
'https://download.simp-project.com/simp/GPGKEYS/RPM-GPG-KEY-SIMP-6',
|
1203
|
+
'https://yum.puppet.com/RPM-GPG-KEY-puppetlabs',
|
1204
|
+
'https://yum.puppet.com/RPM-GPG-KEY-puppet',
|
1205
|
+
'https://apt.postgresql.org/pub/repos/yum/RPM-GPG-KEY-PGDG-96',
|
1206
|
+
'https://artifacts.elastic.co/GPG-KEY-elasticsearch',
|
1207
|
+
'https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana',
|
1208
|
+
'https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-$releasever'
|
1209
|
+
],
|
1210
|
+
:gpgcheck => 1,
|
1211
|
+
:sslverify => 1,
|
1212
|
+
:sslcacert => '/etc/pki/tls/certs/ca-bundle.crt',
|
1213
|
+
:metadata_expire => 300
|
1214
|
+
}
|
1215
|
+
}
|
1216
|
+
# Verify that the repos passed to disable are in the list of valid repos
|
1217
|
+
disable.each { |d|
|
1218
|
+
unless repos.has_key?(d)
|
1219
|
+
raise("ERROR: install_simp_repo - disable contains invalid SIMP repo '#{d}'.")
|
1220
|
+
end
|
1221
|
+
}
|
1222
|
+
repo_manifest = ''
|
1223
|
+
repos.each { | repo, metadata|
|
1224
|
+
metadata[:enabled] = disable.include?(repo) ? 0 : 1
|
1225
|
+
repo_manifest << create_yum_resource(repo, metadata)
|
1226
|
+
}
|
1227
|
+
apply_manifest_on(sut, repo_manifest, :catch_failures => true)
|
1228
|
+
end
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
|