Module: Simp::BeakerHelpers

Defined in:
lib/simp/beaker_helpers.rb

Constant Summary collapse

VERSION =
'1.5.3'

Instance Method Summary collapse

Instance Method Details

#clear_temp_hieradataObject

Clean up all temporary hiera data files.

Meant to be called from after(:all)



475
476
477
478
479
480
481
482
483
# File 'lib/simp/beaker_helpers.rb', line 475

def clear_temp_hieradata
  if @temp_hieradata_dirs && !@temp_hieradata_dirs.empty?
    @temp_hieradata_dirs.each do |data_dir|
      if File.exists?(data_dir)
        FileUtils.rm_r(data_dir)
      end
    end
  end
end

#copy_fixture_modules_to(suts = hosts, opts = {}) ⇒ Object

Copy the local fixture modules (under ‘spec/fixtures/modules`) onto each SUT



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/simp/beaker_helpers.rb', line 74

def copy_fixture_modules_to( suts = hosts, opts = {})
  ensure_fixture_modules

  opts[:pluginsync] = opts.fetch(:pluginsync, true)

  unless ENV['BEAKER_copy_fixtures'] == 'no'
    Array(suts).each do |sut|
      STDERR.puts "  ** copy_fixture_modules_to: '#{sut}'" if ENV['BEAKER_helpers_verbose']

      # Use spec_prep to provide modules (this supports isolated networks)
      unless ENV['BEAKER_use_fixtures_dir_for_modules'] == 'no'

        # NOTE: As a result of BKR-723, which does not look easy to fix, we
        # cannot rely on `copy_module_to()` to choose a sane default for
        # `target_module_path`.  This workaround queries each SUT's
        # `modulepath` and targets the first one.
        target_module_path = on(
          sut, 'puppet config print modulepath --environment production'
        ).output.chomp.split(':').first

        mod_root = File.expand_path( "spec/fixtures/modules", File.dirname( fixtures_yml_path ))

        pupmods_in_fixtures_yml.each do |pupmod|
          STDERR.puts "  ** copy_fixture_modules_to: '#{sut}': '#{pupmod}'" if ENV['BEAKER_helpers_verbose']

          pupmod_root = File.join(mod_root, pupmod)

          if File.symlink?(pupmod_root)
            _opts = {
              :target_module_path => target_module_path,
            }.merge(opts)

            _opts = _opts.merge({
              :source => pupmod_root,
              :module_name => pupmod
            })

            copy_module_to(sut, _opts)
          end
        end

        _opts = {
          :ignore_list => PUPPET_MODULE_INSTALL_IGNORE
        }.merge(opts)

        _opts[:ignore] = build_ignore_list(_opts)

        scp_to(sut, mod_root, File.dirname(target_module_path), _opts)
      end
    end
  end
  STDERR.puts '  ** copy_fixture_modules_to: finished' if ENV['BEAKER_helpers_verbose']

  # sync custom facts from the new modules to each SUT's factpath
  pluginsync_on(suts) if opts[:pluginsync]
end

#copy_keydist_to(ca_sut = master, host_keydist_dir = nil) ⇒ Object

Copy a CA keydist/ directory of CA+host certs into an SUT

This simulates the output of FakeCA’s gencerts_nopass.sh to keydist/



393
394
395
396
397
398
399
400
401
402
# File 'lib/simp/beaker_helpers.rb', line 393

def copy_keydist_to( ca_sut = master, host_keydist_dir = nil  )
  if !host_keydist_dir
    modulepath = on(ca_sut, 'puppet config print modulepath --environment production' ).output.chomp.split(':')
    host_keydist_dir = "#{modulepath.first}/pki/files/keydist"
  end
  on ca_sut, "rm -rf #{host_keydist_dir}/*"
  on ca_sut, "mkdir -p #{host_keydist_dir}/"
  on ca_sut, "cp -pR /root/pki/keydist/. #{host_keydist_dir}/"
  on ca_sut, "chgrp -R puppet #{host_keydist_dir}"
end

#copy_pki_to(sut, local_pki_dir, sut_base_dir = '/etc/pki/simp-testing') ⇒ Object

Copy a single SUT’s PKI certs (with cacerts) onto an SUT.

This simulates the result of pki::copy

The directory structure is:

SUT_BASE_DIR/

pki/
    cacerts/cacerts.pem
    public/fdqn.pub
    private/fdqn.pem


358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/simp/beaker_helpers.rb', line 358

def copy_pki_to(sut, local_pki_dir, sut_base_dir = '/etc/pki/simp-testing')
    fqdn                = fact_on(sut, 'fqdn')
    sut_pki_dir         = File.join( sut_base_dir, 'pki' )
    local_host_pki_tree = File.join(local_pki_dir,'pki','keydist',fqdn)
    local_cacert = File.join(local_pki_dir,'pki','demoCA','cacert.pem')

    on sut, %Q(mkdir -p "#{sut_pki_dir}/public" "#{sut_pki_dir}/private" "#{sut_pki_dir}/cacerts")
    scp_to(sut, "#{local_host_pki_tree}/#{fqdn}.pem",   "#{sut_pki_dir}/private/")
    scp_to(sut, "#{local_host_pki_tree}/#{fqdn}.pub",   "#{sut_pki_dir}/public/")

    # NOTE: to match pki::copy, 'cacert.pem' is copied to 'cacerts.pem'
    scp_to(sut, local_cacert, "#{sut_pki_dir}/cacerts/cacerts.pem")

    # Need to hash all of the CA certificates so that apps can use them
    # properly! This must happen on the host itself since it needs to match
    # the native hashing algorithms.
    hash_cmd = <<-EOM.strip
cd #{sut_pki_dir}/cacerts; \
for x in *; do \
if [ ! -h "$x" ]; then \
  `openssl x509 -in $x >/dev/null 2>&1`; \
  if [ $? -eq 0 ]; then \
    hash=`openssl x509 -in $x -hash | head -1`; \
    ln -sf $x $hash.0; \
  fi; \
 fi; \
done
    EOM

    on(sut, hash_cmd)
end

#enable_fips_mode_on(suts = hosts) ⇒ Object

Configure and reboot SUTs into FIPS mode



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/simp/beaker_helpers.rb', line 133

def enable_fips_mode_on( suts = hosts )
  puts '== configuring FIPS mode on SUTs'
  puts '  -- (use BEAKER_fips=no to disable)'
  suts.each do |sut|
    puts "  -- enabling FIPS on '#{sut}'"

    # We need to use FIPS compliant algorithms and keylengths as per the FIPS
    # certification.
    on(sut, 'puppet config set digest_algorithm sha256')
    on(sut, 'puppet config set keylength 2048')

    # We need to be able to get back into our system!
    # Make these safe for all systems, even old ones.
    fips_ssh_ciphers = [ 'aes256-cbc','aes192-cbc','aes128-cbc']
    on(sut, %(sed -i '/Ciphers /d' /etc/ssh/sshd_config))
    on(sut, %(echo 'Ciphers #{fips_ssh_ciphers.join(',')}' >> /etc/ssh/sshd_config))

    if fact_on(sut, 'osfamily') == 'RedHat'
      pp = <<-EOS
      # This is necessary to prevent a kernel panic after rebooting into FIPS
      # (last checked: 20150928)
        package { ['kernel'] : ensure => 'latest' }

        package { ['grubby'] : ensure => 'latest' }
        ~>
        exec{ 'setup_fips':
          command     => '/bin/bash /root/setup_fips.sh',
          refreshonly => true,
        }

        file{ '/root/setup_fips.sh':
          ensure  => 'file',
          owner   => 'root',
          group   => 'root',
          mode    => '0700',
          content => "#!/bin/bash

# FIPS
if [ -e /sys/firmware/efi ]; then
BOOTDEV=`df /boot/efi | tail -1 | cut -f1 -d' '`
else
BOOTDEV=`df /boot | tail -1 | cut -f1 -d' '`
fi
# In case you need a working fallback
DEFAULT_KERNEL_INFO=`/sbin/grubby --default-kernel`
DEFAULT_INITRD=`/sbin/grubby --info=\\\${DEFAULT_KERNEL_INFO} | grep initrd | cut -f2 -d'='`
DEFAULT_KERNEL_TITLE=`/sbin/grubby --info=\\\${DEFAULT_KERNEL_INFO} | grep -m1 title | cut -f2 -d'='`
/sbin/grubby --copy-default --make-default --args=\\\"boot=\\\${BOOTDEV} fips=1\\\" --add-kernel=`/sbin/grubby --default-kernel` --initrd=\\\${DEFAULT_INITRD} --title=\\\"FIPS \\\${DEFAULT_KERNEL_TITLE}\\\"
",
          notify => Exec['setup_fips']
        }
      EOS
      apply_manifest_on(sut, pp, :catch_failures => false)
      on( sut, 'shutdown -r now', { :expect_connection_failure => true } )
    end
  end
end

#enable_yum_repos_on(suts = hosts) ⇒ Object

Collect all ‘yum_repos’ entries from the host nodeset. The acceptable format is as follows: yum_repos:

<repo_name>:
  url: <URL>
  gpgkeys:
    - <URL to GPGKEY1>
    - <URL to GPGKEY2>


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/simp/beaker_helpers.rb', line 200

def enable_yum_repos_on( suts = hosts )
  repo_attrs = [
    :assumeyes,
    :bandwidth,
    :cost,
    :deltarpm_metadata_percentage,
    :deltarpm_percentage,
    :descr,
    :enabled,
    :enablegroups,
    :exclude,
    :failovermethod,
    :gpgcakey,
    :gpgcheck,
    :http_caching,
    :include,
    :includepkgs,
    :keepalive,
    :metadata_expire,
    :metalink,
    :mirrorlist,
    :mirrorlist_expire,
    :priority,
    :protect,
    :provider,
    :proxy,
    :proxy_password,
    :proxy_username,
    :repo_gpgcheck,
    :retries,
    :s3_enabled,
    :skip_if_unavailable,
    :sslcacert,
    :sslclientcert,
    :sslclientkey,
    :sslverify,
    :target,
    :throttle,
    :timeout
  ]

  Array(suts).each do |sut|
    if sut['yum_repos']
      sut['yum_repos'].each_pair do |repo, |
        repo_manifest = %(yumrepo { #{repo}:)

        repo_manifest_opts = []

        # Legacy Support
        urls = ![:url].nil? ? [:url] : [:baseurl]
        if urls
          repo_manifest_opts << 'baseurl => ' + '"' + Array(urls).flatten.join('\n        ').gsub('$','\$') + '"'
        end

        # Legacy Support
        gpgkeys = ![:gpgkeys].nil? ? [:gpgkeys] : [:gpgkey]
        if gpgkeys
          repo_manifest_opts << 'gpgkey => ' + '"' + Array(gpgkeys).flatten.join('\n       ').gsub('$','\$') + '"'
        end

        repo_attrs.each do |attr|
          if [attr]
            repo_manifest_opts << "#{attr} => '#{[attr]}'"
          end
        end

        repo_manifest = repo_manifest + %(\n#{repo_manifest_opts.join(",\n")}) + "\n}"

        apply_manifest_on(sut, repo_manifest, :catch_failures => true)
      end
    end
  end
end

#ensure_fixture_modulesObject

Ensures that the fixture modules (under ‘spec/fixtures/modules`) exists. if any fixture modules are missing, run ’rake spec_prep’ to populate the fixtures/modules



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/simp/beaker_helpers.rb', line 49

def ensure_fixture_modules
  STDERR.puts "  ** ensure_fixture_modules" if ENV['BEAKER_helpers_verbose']
  unless ENV['BEAKER_spec_prep'] == 'no'
    puts "== checking prepped modules from .fixtures.yml"
    puts "  -- (use BEAKER_spec_prep=no to disable)"
    missing_modules = []
    pupmods_in_fixtures_yml.each do |pupmod|
      STDERR.puts "  **  -- ensure_fixture_modules: '#{pupmod}'" if ENV['BEAKER_helpers_verbose']
      mod_root = File.expand_path( "spec/fixtures/modules/#{pupmod}", File.dirname( fixtures_yml_path ))
      missing_modules << pupmod unless File.directory? mod_root
    end
    puts "  -- #{missing_modules.size} modules need to be prepped"
    unless missing_modules.empty?
      cmd = 'bundle exec rake spec_prep'
      puts "  -- running spec_prep: '#{cmd}'"
      %x(#{cmd})
    else
      puts "  == all fixture modules present"
    end
  end
  STDERR.puts "  **  -- ensure_fixture_modules: finished" if ENV['BEAKER_helpers_verbose']
end

#fix_errata_on(suts = hosts) ⇒ Object

Apply known OS fixes we need to run Beaker on each SUT



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/simp/beaker_helpers.rb', line 275

def fix_errata_on( suts = hosts )

  suts.each do |sut|
    # SIMP uses structured facts, therefore stringify_facts must be disabled
    unless ENV['BEAKER_stringify_facts'] == 'yes'
      on sut, 'puppet config set stringify_facts false'
    end

    # Occasionally we run across something similar to BKR-561, so to ensure we
    # at least have the host defaults:
    #
    # :hieradatadir is used as a canary here; it isn't the only missing key
    unless sut.host_hash.key? :hieradatadir
      configure_type_defaults_on(sut)
    end

    if fact_on(sut, 'osfamily') == 'RedHat'
      enable_yum_repos_on(sut)

      # net-tools required for netstat utility being used by be_listening
      if fact_on(sut, 'operatingsystemmajrelease') == '7'
        pp = <<-EOS
          package { 'net-tools': ensure => installed }
        EOS
        apply_manifest_on(sut, pp, :catch_failures => false)
      end

      # Clean up YUM prior to starting our test runs.
      on(sut, 'yum clean all')
    end
  end

  # Configure and reboot SUTs into FIPS mode
  if ENV['BEAKER_fips'] == 'yes'
    enable_fips_mode_on(suts)
  end
end

#fixtures_yml_pathObject

Locates .fixture.yml in or above this directory.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/simp/beaker_helpers.rb', line 15

def fixtures_yml_path
  STDERR.puts '  ** fixtures_yml_path' if ENV['BEAKER_helpers_verbose']
  fixtures_yml = ''
  dir          = '.'
  while( fixtures_yml.empty? && File.expand_path(dir) != '/' ) do
    file = File.expand_path( '.fixtures.yml', dir )
    STDERR.puts "  ** fixtures_yml_path: #{file}" if ENV['BEAKER_helpers_verbose']
    if File.exists? file
      fixtures_yml = file
      break
    end
    dir = "#{dir}/.."
  end
  raise 'ERROR: cannot locate .fixtures.yml!' if fixtures_yml.empty?
  STDERR.puts "  ** fixtures_yml_path:finished (file: '#{file}')" if ENV['BEAKER_helpers_verbose']
  fixtures_yml
end

#pfact_on(sut, fact_name) ⇒ Object

use the ‘puppet fact` face to look up facts on an SUT



7
8
9
10
11
# File 'lib/simp/beaker_helpers.rb', line 7

def pfact_on(sut, fact_name)
  facts_json = on(sut,'puppet facts find xxx').output
  facts      = JSON.parse(facts_json).fetch( 'values' )
  facts.fetch(fact_name)
end

#pluginsync_on(suts = hosts) ⇒ Object

pluginsync custom facts for all modules



487
488
489
490
491
492
493
494
495
496
497
498
# File 'lib/simp/beaker_helpers.rb', line 487

def pluginsync_on( suts = hosts )
  puts "== pluginsync_on'" if ENV['BEAKER_helpers_verbose']
  suts.each do |sut|
    puts "  ** pluginsync_on: '#{sut}'" if ENV['BEAKER_helpers_verbose']
    fact_path = on(sut, %q(puppet config print factpath)).output.strip.split(':').first
    on(sut, %q(puppet config print modulepath)).output.strip.split(':').each do |mod_path|
      on(sut, %Q(mkdir -p #{fact_path}))
      next if on(sut, "ls #{mod_path}/*/lib/facter 2>/dev/null ", :accept_all_exit_codes => true).exit_code != 0
      on(sut, %Q(find #{mod_path}/*/lib/facter -type f -name '*.rb' -exec cp -a {} '#{fact_path}/' \\; ))
    end
  end
end

#pupmods_in_fixtures_ymlObject

returns an Array of puppet modules declared in .fixtures.yml



35
36
37
38
39
40
41
42
43
# File 'lib/simp/beaker_helpers.rb', line 35

def pupmods_in_fixtures_yml
  STDERR.puts '  ** pupmods_in_fixtures_yml' if ENV['BEAKER_helpers_verbose']
  fixtures_yml = fixtures_yml_path
  data         = YAML.load_file( fixtures_yml )
  repos        = data.fetch('fixtures').fetch('repositories', {}).keys || []
  symlinks     = data.fetch('fixtures').fetch('symlinks', {}).keys     || []
  STDERR.puts '  ** pupmods_in_fixtures_yml: finished' if ENV['BEAKER_helpers_verbose']
  (repos + symlinks)
end

#run_fake_pki_ca_on(ca_sut = master, suts = hosts, local_dir = '') ⇒ Object

Generate a fake openssl CA + certs for each host on a given SUT

The directory structure is the same as what FakeCA drops into keydist/

NOTE: This generates everything within an SUT and copies it back out.

This is because it is assumed the SUT will have the appropriate
openssl in its environment, which may not be true of the host.


321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/simp/beaker_helpers.rb', line 321

def run_fake_pki_ca_on( ca_sut = master, suts = hosts, local_dir = '' )
  puts "== Fake PKI CA"
  pki_dir  = File.expand_path( "../../files/pki", File.dirname(__FILE__))
  host_dir = '/root/pki'
  fqdns    = fact_on(hosts, 'fqdn')

  on ca_sut, %Q(mkdir -p "#{host_dir}")
  Dir[ File.join(pki_dir, '*') ].each{|f| scp_to( ca_sut, f, host_dir)}

  # generate PKI certs for each SUT
  Dir.mktmpdir do |dir|
    pki_hosts_file = File.join(dir, 'pki.hosts')
    File.open(pki_hosts_file, 'w'){|fh| fqdns.each{|fqdn| fh.puts fqdn}}
    scp_to(ca_sut, pki_hosts_file, host_dir)
    # generate certs
    on(ca_sut, "cd #{host_dir}; cat #{host_dir}/pki.hosts | xargs bash make.sh")
  end

  # if a local_dir was provided, copy everything down to it
  unless local_dir.empty?
    FileUtils.mkdir_p local_dir
    scp_from( ca_sut, host_dir, local_dir )
  end
end

#set_hieradata_on(sut, hieradata, data_file = 'default') ⇒ Object

Set the hiera data file on the provided host to the passed data structure

Note: This is authoritative, you cannot mix this with other hieradata copies

@param[sut, Array<Host>, String, Symbol] One or more hosts to act upon.

@param[heradata, Hash || String] The full hiera data structure to write to the system.

@param[data_file, String] The filename (not path) of the hiera data

YAML file to write to the system.

@param[hiera_config, Array<String>] The hiera config array to write

to the system. Must contain the
Data_file name as one element.


438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/simp/beaker_helpers.rb', line 438

def set_hieradata_on(sut, hieradata, data_file='default')
  # Keep a record of all temporary directories that are created
  #
  # Should be cleaned by calling `clear_temp_hiera data` in after(:all)
  #
  # Omit this call to be able to delve into the hiera data that is
  # being created
  @temp_hieradata_dirs = @temp_hieradata_dirs || []

  data_dir = Dir.mktmpdir('hieradata')
  @temp_hieradata_dirs << data_dir

  fh = File.open(File.join(data_dir,"#{data_file}.yaml"),'w')
  if hieradata.kind_of? String
    fh.puts(hieradata)
  else
    fh.puts(hieradata.to_yaml)
  end

  fh.close

  # If there is already a directory on the system, the SCP below will
  # add the local directory to the existing directory instead of
  # replacing the contents.
  apply_manifest_on(
    sut,
    "file { '#{hiera_datadir(sut)}': ensure => 'absent', force => true, recurse => true }"
  )

  copy_hiera_data_to(sut, data_dir)
  write_hiera_config_on(sut, Array(data_file))
end