Module: Simp::BeakerHelpers

Includes:
BeakerPuppet
Defined in:
lib/simp/beaker_helpers.rb,
lib/simp/beaker_helpers/ssg.rb,
lib/simp/beaker_helpers/inspec.rb,
lib/simp/beaker_helpers/version.rb,
lib/simp/beaker_helpers/windows.rb,
lib/simp/beaker_helpers/snapshot.rb,
lib/simp/beaker_helpers/constants.rb

Defined Under Namespace

Modules: Windows Classes: Inspec, SSG, Snapshot

Constant Summary collapse

VERSION =
'1.22.1'
DEFAULT_PUPPET_AGENT_VERSION =

This is the oldest puppet-agent version that the latest release of SIMP supports

This is done so that we know if some new thing that we’re using breaks the oldest system that we support

'~> 6.0'
SSG_REPO_URL =
ENV['BEAKER_ssg_repo'] || 'https://github.com/ComplianceAsCode/content.git'
ONLINE =
false

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.tmpnameObject

Stealing this from the Ruby 2.5 Dir::Tmpname workaround from Rails



16
17
18
19
# File 'lib/simp/beaker_helpers.rb', line 16

def self.tmpname
  t = Time.new.strftime("%Y%m%d")
  "simp-beaker-helpers-#{t}-#{$$}-#{rand(0x100000000).to_s(36)}.tmp"
end

Instance Method Details

#activate_interfaces(hosts) ⇒ Object

Activate all network interfaces on the target system

This is generally needed if the upstream vendor does not activate all interfaces by default (EL7 for example)

Can be passed any number of hosts either singly or as an Array



1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
# File 'lib/simp/beaker_helpers.rb', line 1085

def activate_interfaces(hosts)
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(hosts, :run_in_parallel => parallel) do |host|
    if host[:platform] =~ /windows/
      puts "  -- SKIPPING #{host} because it is windows"
      next
    end

    interfaces_fact = retry_on(host,'facter interfaces', verbose: true).stdout

    interfaces = interfaces_fact.strip.split(',')
    interfaces.delete_if { |x| x =~ /^lo/ }

    interfaces.each do |iface|
      if fact_on(host, "ipaddress_#{iface}").strip.empty?
        on(host, "ifup #{iface}", :accept_all_exit_codes => true)
      end
    end
  end
end

#clear_temp_hieradataObject

Clean up all temporary hiera data files.

Meant to be called from after(:all)



1298
1299
1300
1301
1302
1303
1304
1305
1306
# File 'lib/simp/beaker_helpers.rb', line 1298

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



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/simp/beaker_helpers.rb', line 372

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

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

  unless ENV['BEAKER_copy_fixtures'] == 'no'
    parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
    block_on(suts, :run_in_parallel => parallel) 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 = puppet_modulepath_on(sut).first

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

        Dir.chdir(mod_root) do
          # Have to do things the slow way on Windows
          if is_windows?(sut)
            Dir.glob('*') do |module_dir|
              if File.directory?(module_dir)
                copy_module_to( sut, {
                  :source             => module_dir,
                  :module_name        => module_dir,
                  :target_module_path => target_module_path
                })
              end
            end
          else
            begin
              tarfile = "#{Simp::BeakerHelpers.tmpname}.tar"

              excludes = PUPPET_MODULE_INSTALL_IGNORE.map do |x|
                x = "--exclude '*/#{x}'"
              end.join(' ')

              %x(tar -ch #{excludes} -f #{tarfile} *)

              if File.exist?(tarfile)
                copy_to(sut, tarfile, target_module_path, opts)
              else
                fail("Error: module tar file '#{tarfile}' could not be created at #{mod_root}")
              end

              on(sut, "cd #{target_module_path} && tar -xf #{File.basename(tarfile)}")
            ensure
              FileUtils.remove_entry(tarfile, true)
            end
          end
        end
      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_hiera_data_to(sut, path) ⇒ Object

A shim to stand in for the now deprecated copy_hiera_data_to function

Parameters:

  • sut (Host)

    One host to act upon

  • File (Path)

    containing hiera data



1221
1222
1223
# File 'lib/simp/beaker_helpers.rb', line 1221

def copy_hiera_data_to(sut, path)
  copy_to(sut, path, hiera_datadir(sut))
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/



1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
# File 'lib/simp/beaker_helpers.rb', line 1066

def copy_keydist_to( ca_sut = master, host_keydist_dir = nil  )
  if !host_keydist_dir
    modulepath = puppet_modulepath_on(ca_sut)

    host_keydist_dir = "#{modulepath.first}/pki/files/keydist"
  end
  on ca_sut, "rm -rf #{host_keydist_dir}/*"
  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
    # This is a copy of cacerts.pem since cacerts.pem is a
    # collection of the CA certificates in pupmod-simp-pki
    cacerts/simp_auto_ca.pem
    public/fdqn.pub
    private/fdqn.pem


1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
# File 'lib/simp/beaker_helpers.rb', line 1026

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')

    sut.mkdir_p("#{sut_pki_dir}/public")
    sut.mkdir_p("#{sut_pki_dir}/private")
    sut.mkdir_p("#{sut_pki_dir}/cacerts")
    copy_to(sut, "#{local_host_pki_tree}/#{fqdn}.pem", "#{sut_pki_dir}/private/")
    copy_to(sut, "#{local_host_pki_tree}/#{fqdn}.pub", "#{sut_pki_dir}/public/")

    copy_to(sut, local_cacert, "#{sut_pki_dir}/cacerts/simp_auto_ca.pem")

    # NOTE: to match pki::copy, 'cacert.pem' is copied to 'cacerts.pem'
    copy_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
      PATH=/opt/puppetlabs/puppet/bin:$PATH; \
      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

#copy_to(sut, src, dest, opts = {}) ⇒ Object

Figure out the best method to copy files to a host and use it

Will create the directories leading up to the target if they don’t exist



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
190
191
192
193
194
195
196
197
198
199
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
# File 'lib/simp/beaker_helpers.rb', line 153

def copy_to(sut, src, dest, opts={})
  sut.mkdir_p(File.dirname(dest))

  if sut[:hypervisor] == 'docker'
    exclude_list = []
    opts[:silent] ||= true

    if opts.has_key?(:ignore) && !opts[:ignore].empty?
      opts[:ignore].each do |value|
        exclude_list << "--exclude '#{value}'"
      end
    end

    # Work around for breaking changes in beaker-docker
    if sut.host_hash[:docker_container]
      container_id = sut.host_hash[:docker_container].id
    else
      container_id = sut.host_hash[:docker_container_id]
    end

    if ENV['BEAKER_docker_cmd']
      docker_cmd = ENV['BEAKER_docker_cmd']
    else
      docker_cmd = 'docker'

      if ::Docker.version['Components'].any?{|x| x['Name'] =~ /podman/i}
        docker_cmd = 'podman'

        if ENV['CONTAINER_HOST']
          docker_cmd = 'podman --remote'
        elsif ENV['DOCKER_HOST']
          docker_cmd = "podman --remote --url=#{ENV['DOCKER_HOST']}"
        end
      end
    end

    unless directory_exists_on(sut, dest)
      dest = File.dirname(dest)
      sut.mkdir_p(dest)
    end

    %x(tar #{exclude_list.join(' ')} -hcf - -C "#{File.dirname(src)}" "#{File.basename(src)}" | #{docker_cmd} exec -i "#{container_id}" tar -C "#{dest}" -xf -)

  elsif rsync_functional_on?(sut)
    # This makes rsync_to work like beaker and scp usually do
    exclude_hack = %(__-__' -L --exclude '__-__)

    # There appears to be a single copy of 'opts' that gets passed around
    # through all of the different hosts so we're going to make a local deep
    # copy so that we don't destroy the world accidentally.
    _opts = Marshal.load(Marshal.dump(opts))
    _opts[:ignore] ||= []
    _opts[:ignore] << exclude_hack

    if File.directory?(src)
      dest = File.join(dest, File.basename(src)) if File.directory?(src)
      sut.mkdir_p(dest)
    end

    # End rsync hackery

    begin
      rsync_to(sut, src, dest, _opts)
    rescue
      # Depending on what is getting tested, a new SSH session might not
      # work. In this case, we fall back to SSH.
      #
      # The rsync failure is quite fast so this doesn't affect performance as
      # much as shoving a bunch of data over the ssh session.
      scp_to(sut, src, dest, opts)
    end
  else
    scp_to(sut, src, dest, opts)
  end
end

#create_yum_resource(repo, metadata) ⇒ Object



545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
# File 'lib/simp/beaker_helpers.rb', line 545

def create_yum_resource( repo,  )
  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
  ]

    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}\n"
end

#enable_epel_on(suts) ⇒ Object

Enable EPEL if appropriate to do so and the system is online

Can be disabled by setting BEAKER_enable_epel=no



614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
# File 'lib/simp/beaker_helpers.rb', line 614

def enable_epel_on(suts)
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    if ONLINE
      os_info = fact_on(sut, 'os')
      os_maj_rel = os_info['release']['major']

      # This is based on the official EPEL docs https://fedoraproject.org/wiki/EPEL
      case os_info['name']
      when 'RedHat','CentOS'
        install_latest_package_on(
          sut,
          'epel-release',
          "https://dl.fedoraproject.org/pub/epel/epel-release-latest-#{os_maj_rel}.noarch.rpm",
        )

        if os_info['name'] == 'RedHat'
          if os_maj_rel == '7'
            on sut, %{subscription-manager repos --enable "rhel-*-optional-rpms"}
            on sut, %{subscription-manager repos --enable "rhel-*-extras-rpms"}
            on sut, %{subscription-manager repos --enable "rhel-ha-for-rhel-*-server-rpms"}
          end

          if os_maj_rel == '8'
            on sut, %{subscription-manager repos --enable "codeready-builder-for-rhel-8-#{os_info['architecture']}-rpms"}
          end
        end

        if os_info['name'] == 'CentOS'
          if os_maj_rel == '8'
            # 8.0 fallback
            install_latest_package_on(sut, 'dnf-plugins-core')
            on sut, %{dnf config-manager --set-enabled powertools || dnf config-manager --set-enabled PowerTools}
          end
        end
      when 'OracleLinux'
        package_name = "oracle-epel-release-el#{os_maj_rel}"
        install_latest_package_on(sut,package_name)
      end

    end
  end
end

#enable_fips_mode_on(suts = hosts) ⇒ Object

Configure and reboot SUTs into FIPS mode



454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/simp/beaker_helpers.rb', line 454

def enable_fips_mode_on( suts = hosts )
  puts '== configuring FIPS mode on SUTs'
  puts '  -- (use BEAKER_fips=no to disable)'
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')

  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    next if sut[:hypervisor] == 'docker'

    if is_windows?(sut)
      puts "  -- SKIPPING #{sut} because it is windows"
      next
    end

    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.
    # TODO Use simp-ssh Puppet module appropriately (i.e., in a fashion
    #      that doesn't break vagrant access and is appropriate for
    #      typical module tests.)
    fips_ssh_ciphers = [ 'aes256-ctr','aes192-ctr','aes128-ctr']
    on(sut, %(sed -i '/Ciphers /d' /etc/ssh/sshd_config))
    on(sut, %(echo 'Ciphers #{fips_ssh_ciphers.join(',')}' >> /etc/ssh/sshd_config))

    fips_enable_modulepath = ''

    if pupmods_in_fixtures_yml.include?('fips')
      copy_fixture_modules_to(sut)
    else
      # If we don't already have the simp-fips module installed
      #
      # Use the simp-fips Puppet module to set FIPS up properly:
      # Download the appropriate version of the module and its dependencies from PuppetForge.
      # TODO provide a R10k download option in which user provides a Puppetfile
      # with simp-fips and its dependencies
      on(sut, 'mkdir -p /root/.beaker_fips/modules')

      fips_enable_modulepath = '--modulepath=/root/.beaker_fips/modules'

      modules_to_install = {
        'simp-fips' => ENV['BEAKER_fips_module_version'],
        'simp-crypto_policy' => nil
      }

      modules_to_install.each_pair do |to_install, version|
        module_install_cmd = "puppet module install #{to_install} --target-dir=/root/.beaker_fips/modules"
        module_install_cmd += " --version #{version}" if version
        on(sut, module_install_cmd)
      end
    end

    # Work around Vagrant and cipher restrictions in EL8+
    #
    # Hopefully, Vagrant will update the used ciphers at some point but who
    # knows when that will be
    munge_ssh_crypto_policies(sut)

    # Enable FIPS and then reboot to finish.
    on(sut, %(puppet apply --verbose #{fips_enable_modulepath} -e "class { 'fips': enabled => true }"))

    sut.reboot
  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>


532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/simp/beaker_helpers.rb', line 532

def enable_yum_repos_on( suts = hosts )
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    if sut['yum_repos']
      sut['yum_repos'].each_pair do |repo, |
        repo_manifest = create_yum_resource( repo, )

        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



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/simp/beaker_helpers.rb', line 347

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

#file_content_on(sut, path, trim = true) ⇒ String?

Return the contents of a file on the remote host

Parameters:

  • sut (Host)

    the host upon which to operate

  • path (String)

    the path to the target file

  • trim (Boolean) (defaults to: true)

    remove leading and trailing whitespace

Returns:

  • (String, nil)

    the contents of the remote file



1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
# File 'lib/simp/beaker_helpers.rb', line 1137

def file_content_on(sut, path, trim=true)
  file_content = nil

  if file_exists_on(sut, path)
    Dir.mktmpdir do |dir|
      scp_from(sut, path, dir)

      file_content = File.read(File.join(dir,File.basename(path)))
    end
  end

  return file_content
end

#fips_enabled(sut) ⇒ Object

We can’t cache this because it may change during a run



115
116
117
118
119
120
# File 'lib/simp/beaker_helpers.rb', line 115

def fips_enabled(sut)
  return on( sut,
            'cat /proc/sys/crypto/fips_enabled 2>/dev/null',
            :accept_all_exit_codes => true
           ).output.strip == '1'
end

#fix_errata_on(suts = hosts) ⇒ Object

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



885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
# File 'lib/simp/beaker_helpers.rb', line 885

def fix_errata_on( suts = hosts )
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    if is_windows?(sut)
      # Load the Windows requirements
      require 'simp/beaker_helpers/windows'

      # Install the necessary windows certificate for testing
      #
      # https://petersouter.xyz/testing-windows-with-beaker-without-cygwin/
      geotrust_global_ca = <<~EOM.freeze
      -----BEGIN CERTIFICATE-----
      MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
      MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
      YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
      EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
      R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
      9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
      fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
      iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
      1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
      bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
      MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
      ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
      uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
      Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
      tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
      PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
      hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
      5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
      -----END CERTIFICATE-----
      EOM

      install_cert_on_windows(sut, 'geotrustglobal', geotrust_global_ca)
    else
      linux_errata(sut)
    end
  end

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

#fixtures_pathObject

Return the path to the ‘spec/fixtures’ directory



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/simp/beaker_helpers.rb', line 278

def fixtures_path
  return @fixtures_path if @fixtures_path

  STDERR.puts '  ** fixtures_path' if ENV['BEAKER_helpers_verbose']
  dir = RSpec.configuration.default_path
  dir = File.join('.', 'spec') unless dir

  dir = File.join(File.expand_path(dir), 'fixtures')

  if File.directory?(dir)
    @fixtures_path = dir
    return @fixtures_path
  else
    raise("Could not find fixtures directory at '#{dir}'")
  end
end

#fixtures_yml_pathObject

Locates .fixture.yml in or above this directory.



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/simp/beaker_helpers.rb', line 296

def fixtures_yml_path
  return @fixtures_yml_path if @fixtures_yml_path

  STDERR.puts '  ** fixtures_yml_path' if ENV['BEAKER_helpers_verbose']

  if ENV['FIXTURES_YML']
    fixtures_yml = ENV['FIXTURES_YML']
  else
    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
  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_path = fixtures_yml

  return @fixtures_yml_path
end

#get_hiera_config_on(sut) ⇒ Object

Retrieve the default environment hiera.yaml

Parameters:

  • sut (Host)

    one host to act upon



1165
1166
1167
# File 'lib/simp/beaker_helpers.rb', line 1165

def get_hiera_config_on(sut)
  file_content_on(sut, hiera_config_path_on(sut))
end

#get_puppet_install_infoObject

returns hash with :puppet_install_version, :puppet_collection, and :puppet_install_type keys determined from environment variables, host settings, and/or defaults

NOTE: BEAKER_PUPPET_AGENT_VERSION or PUPPET_INSTALL_VERSION or

PUPPET_VERSION takes precedence over BEAKER_PUPPET_COLLECTION
or host.options['puppet_collection'], when both a puppet
install version and a puppet collection are specified. This is
because the puppet install version can specify more precise
version information than is available from a puppet collection.


1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
# File 'lib/simp/beaker_helpers.rb', line 1398

def get_puppet_install_info
  # The first match is internal Beaker and the second is legacy SIMP
  puppet_install_version = ENV['BEAKER_PUPPET_AGENT_VERSION'] || ENV['PUPPET_INSTALL_VERSION'] || ENV['PUPPET_VERSION']

  if puppet_install_version and !puppet_install_version.strip.empty?
    puppet_agent_version = latest_puppet_agent_version_for(puppet_install_version.strip)
  end

  if puppet_agent_version.nil?
    if puppet_collection = (ENV['BEAKER_PUPPET_COLLECTION'] || host.options['puppet_collection'])
      if puppet_collection =~ /puppet(\d+)/
        puppet_install_version = "~> #{$1}"
        puppet_agent_version = latest_puppet_agent_version_for(puppet_install_version)
      else
        raise("Error: Puppet Collection '#{puppet_collection}' must match /puppet(\\d+)/")
      end
    else
      puppet_agent_version = latest_puppet_agent_version_for(DEFAULT_PUPPET_AGENT_VERSION)
    end
  end

  if puppet_collection.nil?
    base_version = puppet_agent_version.to_i
    puppet_collection = "puppet#{base_version}" if base_version >= 5
  end

  {
    :puppet_install_version => puppet_agent_version,
    :puppet_collection      => puppet_collection,
    :puppet_install_type    => ENV.fetch('PUPPET_INSTALL_TYPE', 'agent')
  }
end

#has_crypto_policies(sut) ⇒ Object



436
437
438
# File 'lib/simp/beaker_helpers.rb', line 436

def has_crypto_policies(sut)
  file_exists_on(sut, '/etc/crypto-policies/config')
end

#hiera_config_path_on(sut) ⇒ Object

Retrieve the default hiera.yaml path

Parameters:

  • sut (Host)

    one host to act upon



1156
1157
1158
# File 'lib/simp/beaker_helpers.rb', line 1156

def hiera_config_path_on(sut)
  File.join(puppet_environment_path_on(sut), 'hiera.yaml')
end

#hiera_datadir(sut) ⇒ Object

A shim to stand in for the now deprecated hiera_datadir function

Note: This may not work if you’ve shoved data somewhere that is not the default and/or are manipulating the default hiera.yaml.

Parameters:

  • sut (Host)

    One host to act upon



1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
# File 'lib/simp/beaker_helpers.rb', line 1233

def hiera_datadir(sut)
  # This output lets us know where Hiera is configured to look on the system
  puppet_lookup_info = on(sut, 'puppet lookup --explain test__simp__test', :silent => true).output.strip.lines

  if sut.puppet_configprint['manifest'].nil? || sut.puppet_configprint['manifest'].empty?
    fail("No output returned from `puppet config print manifest` on #{sut}")
  end

  puppet_env_path = puppet_environment_path_on(sut)

  # We'll just take the first match since Hiera will find things there
  puppet_lookup_info = puppet_lookup_info.grep(/Path "/).grep(Regexp.new(puppet_env_path))

  # Grep always returns an Array
  if puppet_lookup_info.empty?
    fail("Could not determine hiera data directory under #{puppet_env_path} on #{sut}")
  end

  # Snag the actual path without the extra bits
  puppet_lookup_info = puppet_lookup_info.first.strip.split('"').last

  # Make the parent directories exist
  sut.mkdir_p(File.dirname(puppet_lookup_info))

  # We just want the data directory name
  datadir_name = puppet_lookup_info.split(puppet_env_path).last

  # Grab the file separator to add back later
  file_sep = datadir_name[0]

  # Snag the first entry (this is the data directory)
  datadir_name = datadir_name.split(file_sep)[1]

  # Constitute the full path to the data directory
  datadir_path = puppet_env_path + file_sep + datadir_name

  # Return the path to the data directory
  return datadir_path
end

#install_latest_package_on(suts, package_name, package_source = nil, opts = {}) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/simp/beaker_helpers.rb', line 88

def install_latest_package_on(suts, package_name, package_source=nil, opts={})
  default_opts = {
    max_retries: 3,
    retry_interval: 10
  }

  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    package_source = package_name unless package_source

    if sut.check_for_package(package_name)
      sut.upgrade_package(
        package_source,
        '',
        default_opts.merge(opts)
      )
    else
      install_package_unless_present_on(sut, package_name, package_source, opts)
    end
  end
end

#install_package_unless_present_on(suts, package_name, package_source = nil, opts = {}) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/simp/beaker_helpers.rb', line 67

def install_package_unless_present_on(suts, package_name, package_source=nil, opts={})
  default_opts = {
    max_retries: 3,
    retry_interval: 10
  }

  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    package_source = package_name unless package_source

    unless sut.check_for_package(package_name)
      sut.install_package(
        package_source,
        '',
        nil,
        default_opts.merge(opts)
      )
    end
  end
end

#install_puppetObject

Replacement for ‘install_puppet` in spec_helper_acceptance.rb



1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
# File 'lib/simp/beaker_helpers.rb', line 1433

def install_puppet
  install_info = get_puppet_install_info

  # In case  Beaker needs this info internally
  ENV['PUPPET_INSTALL_VERSION'] = install_info[:puppet_install_version]
  if install_info[:puppet_collection]
    ENV['BEAKER_PUPPET_COLLECTION'] = install_info[:puppet_collection]
  end

  require 'beaker/puppet_install_helper'

  run_puppet_install_helper(install_info[:puppet_install_type], install_info[:puppet_install_version])
end

#install_simp_repos(suts, disable = []) ⇒ Object

Configure all SIMP repos on a host and disable all repos in the disable Array

Examples:

install_simp_repos( myhost )           # install all the repos an enable them.
install_simp_repos( myhost, ['simp'])  # install the repos but disable the simp repo.

Valid repo names include any repository available on the system.

For backwards compatibility purposes, the following translations are automatically performed:

* 'simp'
  * 'simp-community-simp'

* 'simp_deps'
  * 'simp-community-epel'
  * 'simp-community-postgres'
  * 'simp-community-puppet'

Environment Variables:

* BEAKER_SIMP_install_repos
  * 'no' => disable the capability
* BEAKER_SIMP_disable_repos
  * Comma delimited list of active yum repo names to disable

Parameters:

  • sut (Beaker::Host)

    Host on which to configure SIMP repos

  • disable (Array[String]) (defaults to: [])

    List of repos to disable

Raises:

  • (StandardError)

    if disable contains an invalid repo name.



1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
# File 'lib/simp/beaker_helpers.rb', line 1476

def install_simp_repos(suts, disable = [])
  # NOTE: Do *NOT* use puppet in this method since it may not be available yet

  return if (ENV.fetch('SIMP_install_repos', 'yes') == 'no')

  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    install_package_unless_present_on(sut, 'yum-utils')

    install_package_unless_present_on(
      sut,
      'simp-release-community',
      "https://download.simp-project.com/simp-release-community.rpm",
    )

    to_disable = disable.dup
    to_disable += ENV.fetch('BEAKER_SIMP_disable_repos', '').split(',').map(&:strip)

    unless to_disable.empty?
      if to_disable.include?('simp')
        to_disable.delete('simp')
        to_disable << 'simp-community-simp'
      end

      if to_disable.include?('simp_deps')
        to_disable.delete('simp_deps')
        to_disable << 'simp-community-epel'
        to_disable << 'simp-community-postgres'
        to_disable << 'simp-community-puppet'
      end

      # NOTE: This --enablerepo enables the repos for listing and is inherited
      # from YUM. This does not actually "enable" the repos, that would require
      # the "--enable" option (from yum-config-manager) :-D.
      #
      # Note: Certain versions of EL8 do not dump by default and EL7 does not
      # have the '--dump' option.
      available_repos = on(sut, %{yum-config-manager --enablerepo="*" || yum-config-manager --enablerepo="*" --dump}).stdout.lines.grep(/\A\[(.+)\]\Z/){|x| $1}

      invalid_repos = (to_disable - available_repos)

      # Verify that the repos passed to disable are in the list of valid repos
      unless invalid_repos.empty?
        logger.warn(%{WARN: install_simp_repo - requested repos to disable do not exist on the target system '#{invalid_repos.join("', '")}'.})
      end

      (to_disable - invalid_repos).each do |repo|
        on(sut, %{yum-config-manager --disable "#{repo}"})
      end
    end
  end

  set_yum_opts_on(suts, {'simp*.skip_if_unavailable' => '1' })
end

#is_windows?(sut) ⇒ Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/simp/beaker_helpers.rb', line 110

def is_windows?(sut)
  sut[:platform] =~ /windows/i
end

#latest_puppet_agent_version_for(puppet_version) ⇒ String, Nil

Looks up latest ‘puppet-agent` version by the version of its `puppet` gem

Parameters:

  • puppet_version (String)

    target Puppet gem version. Works with Gemfile comparison syntax (e.g., ‘4.0’, ‘= 4.2’, ‘~> 4.3.1’, ‘> 5.1, < 5.5’)

Returns:

  • (String, Nil)

    the ‘puppet-agent` version or nil



1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
# File 'lib/simp/beaker_helpers.rb', line 1334

def latest_puppet_agent_version_for( puppet_version )
  return nil if puppet_version.nil?

  require 'rubygems/requirement'
  require 'rubygems/version'
  require 'yaml'

  _puppet_version = puppet_version.strip.split(',')


  @agent_version_table ||= YAML.load_file(
                             File.expand_path(
                               '../../files/puppet-agent-versions.yaml',
                               File.dirname(__FILE__)
                           )).fetch('version_mappings')
  _pair = @agent_version_table.find do |k,v|
    Gem::Requirement.new(_puppet_version).satisfied_by?(Gem::Version.new(k))
  end
  result = _pair ? _pair.last : nil

  # If we didn't get a match, go look for published rubygems
  unless result
    puppet_gems = nil

    Bundler.with_clean_env do
      puppet_gems = %x(gem search -ra -e puppet).match(/\((.+)\)/)
    end

    if puppet_gems
      puppet_gems = puppet_gems[1].split(/,?\s+/).select{|x| x =~ /^\d/}

      # If we don't have a full version string, we need to massage it for the
      # match.
      begin
        if _puppet_version.size == 1
          Gem::Version.new(_puppet_version[0])
          if _puppet_version[0].count('.') < 2
           _puppet_version = "~> #{_puppet_version[0]}"
          end
        end
      rescue ArgumentError
        # this means _puppet_version is not just a version, but a version
        # specifier such as "= 5.2.3", "<= 5.1", "> 4", "~> 4.10.7"
      end

      result = puppet_gems.find do |ver|
        Gem::Requirement.new(_puppet_version).satisfied_by?(Gem::Version.new(ver))
      end
    end
  end

  return result
end

#linux_errata(suts) ⇒ Object



667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
# File 'lib/simp/beaker_helpers.rb', line 667

def linux_errata( suts )
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    # We need to be able to flip between server and client without issue
    on sut, 'puppet resource group puppet gid=52'
    on sut, 'puppet resource user puppet comment="Puppet" gid="52" uid="52" home="/var/lib/puppet" managehome=true'

    os_info = fact_on(sut, 'os')

    # Make sure we have a domain on our host
    current_domain = fact_on(sut, 'domain').strip
    hostname = fact_on(sut, 'hostname').strip

    if current_domain.empty?
      new_fqdn = hostname + '.beaker.test'

      on(sut, "sed -i 's/#{hostname}.*/#{new_fqdn} #{hostname}/' /etc/hosts")
      on(sut, "echo '#{new_fqdn}' > /etc/hostname", :accept_all_exit_codes => true)
      on(sut, "hostname #{new_fqdn}", :accept_all_exit_codes => true)

      if sut.file_exist?('/etc/sysconfig/network')
        on(sut, "sed -s '/HOSTNAME=/d' /etc/sysconfig/network")
        on(sut, "echo 'HOSTNAME=#{new_fqdn}' >> /etc/sysconfig/network")
      end
    end

    if fact_on(sut, 'domain').strip.empty?
      fail("Error: hosts must have an FQDN, got domain='#{current_domain}'")
    end

    # This may not exist in docker so just skip the whole thing
    if sut.file_exist?('/etc/ssh')
      # SIMP uses a central ssh key location so we prep that spot in case we
      # flip to the SIMP SSH module.
      on(sut, 'mkdir -p /etc/ssh/local_keys')
      on(sut, 'chown -R root:root /etc/ssh/local_keys')
      on(sut, 'chmod 755 /etc/ssh/local_keys')

       = on(sut, 'getent passwd').stdout.lines

      # Hash of user => home_dir
      # Exclude silly directories
      #   * /
      #   * /dev/*
      #   * /s?bin
      #   * /proc
       = Hash[
        .map do |u|
          u.strip!
          u = u.split(':')
          u[5] =~ %r{^(/|/dev/.*|/s?bin/?.*|/proc/?.*)$} ? [nil] : [u[0], u[5]]
        end
      ]

      .keys.each do |user|
        src_file = "#{[user]}/.ssh/authorized_keys"
        tgt_file = "/etc/ssh/local_keys/#{user}"

        on(sut, %{if [ -f "#{src_file}" ]; then cp -a -f "#{src_file}" "#{tgt_file}" && chmod 644 "#{tgt_file}"; fi}, :silent => true)
      end
    end

    # 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 os_info['family'] == 'RedHat'
      # OS-specific items
      if os_info['name'] == 'RedHat'
        RSpec.configure do |c|
          c.before(:all) do
            rhel_rhsm_subscribe(sut)
          end

          c.after(:all) do
            rhel_rhsm_unsubscribe(sut)
          end
        end
      end

      if ['CentOS','RedHat','OracleLinux'].include?(os_info['name'])
        enable_yum_repos_on(sut)
        enable_epel_on(sut)

        # net-tools required for netstat utility being used by be_listening
        if os_info['release']['major'].to_i >= 7
          pp = <<-EOS
            package { 'net-tools': ensure => installed }
          EOS
          apply_manifest_on(sut, pp, :catch_failures => false)
        end

        unless sut[:hypervisor] == 'docker'
          if (os_info['name'] == 'CentOS') && (os_info['release']['major'].to_i >= 8)
            if os_info['release']['minor'].to_i == 3
              update_package_from_centos_stream(sut, 'kernel')
              sut.reboot
            end
          end
        end

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

#munge_ssh_crypto_policies(suts, key_types = ['ssh-rsa']) ⇒ Object



440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/simp/beaker_helpers.rb', line 440

def munge_ssh_crypto_policies(suts, key_types=['ssh-rsa'])
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    if has_crypto_policies(sut)
      install_latest_package_on(sut, 'crypto-policies', nil, :accept_all_exit_codes => true)

      # Since we may be doing this prior to having a box flip into FIPS mode, we
      # need to find and modify *all* of the affected policies
      on( sut, %{sed --follow-symlinks -i 's/\\(HostKeyAlgorithms\\|PubkeyAcceptedKeyTypes\\)\\(.\\)/\\1\\2#{key_types.join(',')},/g' $( grep -L ssh-rsa $( find /etc/crypto-policies /usr/share/crypto-policies -type f -a \\( -name '*.txt' -o -name '*.config' \\) -exec grep -l PubkeyAcceptedKeyTypes {} \\; ) ) })
    end
  end
end

#pfact_on(sut, fact_name) ⇒ Object

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



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
# File 'lib/simp/beaker_helpers.rb', line 230

def pfact_on(sut, fact_name)
  found_fact = nil
  # If puppet is not installed, there are no puppet facts to fetch
  if sut.which('puppet').empty?
    found_fact = fact_on(sut, fact_name)
  else
    facts_json = nil
    begin
      cmd_output = on(sut, 'facter -p --json', :silent => true)
      # Facter 4+
      raise('skip facter -p') if (cmd_output.stderr =~ /no longer supported/)

      facts = JSON.parse(cmd_output.stdout)
    rescue StandardError
      # If *anything* fails, we need to fall back to `puppet facts`

      facts_json = on(sut, 'puppet facts find garbage_xxx', :silent => true).stdout
      facts = JSON.parse(facts_json)['values']
    end

    found_fact = facts.dig(*(fact_name.split('.')))

    # If we did not find a fact, we should use the upstream function since
    # puppet may be installed via a gem or through some other means.
    found_fact = fact_on(sut, fact_name) if found_fact.nil?
  end

  # Ensure that Hashes return as Hash objects
  found_fact.is_a?(OpenStruct) ? found_fact.marshal_dump : found_fact
end

#pluginsync_on(suts = hosts) ⇒ Object

pluginsync custom facts for all modules



1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
# File 'lib/simp/beaker_helpers.rb', line 1310

def pluginsync_on( suts = hosts )
  puts "== pluginsync_on'" if ENV['BEAKER_helpers_verbose']
  pluginsync_manifest =<<-PLUGINSYNC_MANIFEST
  file { $::settings::libdir:
        ensure  => directory,
        source  => 'puppet:///plugins',
        recurse => true,
        purge   => true,
        backup  => false,
        noop    => false
      }
  PLUGINSYNC_MANIFEST
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  apply_manifest_on(hosts, pluginsync_manifest, :run_in_parallel => parallel)
end

#pupmods_in_fixtures_ymlObject

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



328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/simp/beaker_helpers.rb', line 328

def pupmods_in_fixtures_yml
  return @pupmods_in_fixtures_yml if @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']

  @pupmods_in_fixtures_yml = (repos + symlinks)

  return @pupmods_in_fixtures_yml
end

#puppet_environment_path_on(sut, environment = 'production') ⇒ Object

Return the default environment path



273
274
275
# File 'lib/simp/beaker_helpers.rb', line 273

def puppet_environment_path_on(sut, environment='production')
  File.dirname(sut.puppet_configprint['manifest'])
end

#puppet_modulepath_on(sut, environment = 'production') ⇒ Object

Returns the modulepath on the SUT, as an Array



262
263
264
265
266
267
268
269
270
# File 'lib/simp/beaker_helpers.rb', line 262

def puppet_modulepath_on(sut, environment='production')
  splitchar = ':'
  splitchar = ';' if is_windows?(sut)

  (
    sut.puppet_configprint['modulepath'].split(splitchar) +
    sut.puppet_configprint['basemodulepath'].split(splitchar)
  ).uniq
end

#rhel_repo_disable(suts, repos) ⇒ Object



868
869
870
871
872
873
874
875
# File 'lib/simp/beaker_helpers.rb', line 868

def rhel_repo_disable(suts, repos)
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    Array(repos).each do |repo|
      on(sut, %{subscription-manager repos --disable #{repo}}, :accept_all_exit_codes => true)
    end
  end
end

#rhel_repo_enable(suts, repos) ⇒ Object



859
860
861
862
863
864
865
866
# File 'lib/simp/beaker_helpers.rb', line 859

def rhel_repo_enable(suts, repos)
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    Array(repos).each do |repo|
      on(sut, %{subscription-manager repos --enable #{repo}})
    end
  end
end

#rhel_rhsm_subscribe(suts, *opts) ⇒ Object

Register a RHEL system with a development license

Must set BEAKER_RHSM_USER and BEAKER_RHSM_PASS environment variables or pass them in as parameters



788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
# File 'lib/simp/beaker_helpers.rb', line 788

def rhel_rhsm_subscribe(suts, *opts)
  require 'securerandom'

  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    rhsm_opts = {
      :username => ENV['BEAKER_RHSM_USER'],
      :password => ENV['BEAKER_RHSM_PASS'],
      :system_name => "#{sut}_beaker_#{Time.now.to_i}_#{SecureRandom.uuid}",
      :repo_list => {
        '7' => [
          'rhel-7-server-extras-rpms',
          'rhel-7-server-optional-rpms',
          'rhel-7-server-rh-common-rpms',
          'rhel-7-server-rpms',
          'rhel-7-server-supplementary-rpms'
        ],
        '8' => [
          'rhel-8-for-x86_64-baseos-rpms',
          'rhel-8-for-x86_64-supplementary-rpms'
        ]
      }
    }

    if opts && opts.is_a?(Hash)
      rhsm_opts.merge!(opts)
    end

    os = fact_on(sut, 'operatingsystem').strip
    os_release = fact_on(sut, 'operatingsystemmajrelease').strip

    if os == 'RedHat'
      unless rhsm_opts[:username] && rhsm_opts[:password]
        fail("You must set BEAKER_RHSM_USER and BEAKER_RHSM_PASS environment variables to register RHEL systems")
      end

      sub_status = on(sut, 'subscription-manager status', :accept_all_exit_codes => true)
      unless sub_status.exit_code == 0
        logger.info("Registering #{sut} via subscription-manager")
        on(sut, %{subscription-manager register --auto-attach --name='#{rhsm_opts[:system_name]}' --username='#{rhsm_opts[:username]}' --password='#{rhsm_opts[:password]}'}, :silent => true)
      end

      if rhsm_opts[:repo_list][os_release]
        rhel_repo_enable(sut, rhsm_opts[:repo_list][os_release])
      else
        logger.warn("simp-beaker-helpers:#{__method__} => Default repos for RHEL '#{os_release}' not found")
      end

      # Ensure that all users can access the entitlements since we don't know
      # who we'll be running jobs as (often not root)
      on(sut, 'chmod -R ugo+rX /etc/pki/entitlement', :accept_all_exit_codes => true)
    end
  end
end

#rhel_rhsm_unsubscribe(suts) ⇒ Object



877
878
879
880
881
882
# File 'lib/simp/beaker_helpers.rb', line 877

def rhel_rhsm_unsubscribe(suts)
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    on(sut, %{subscription-manager unregister}, :accept_all_exit_codes => true)
  end
end

#rsync_functional_on?(sut) ⇒ Boolean

Returns:

  • (Boolean)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/simp/beaker_helpers.rb', line 122

def rsync_functional_on?(sut)
  # We have to check if rsync *still* works otherwise
  return false if (@rsync_functional == false)

  require 'facter'
  unless Facter::Util::Resolution.which('rsync')
    @rsync_functional = false
    return @rsync_functional
  end

  require 'tempfile'

  testfile = Tempfile.new('rsync_check')
  testfile.puts('test')
  testfile.close

  begin
    rsync_to(sut, testfile.path, sut.system_temp_path)
  rescue Beaker::Host::CommandFailure
    @rsync_functional = false
    return false
  ensure
    testfile.unlink
  end

  return true
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.


937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
# File 'lib/simp/beaker_helpers.rb', line 937

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'

  ca_sut.mkdir_p(host_dir)
  Dir[ File.join(pki_dir, '*') ].each{|f| copy_to( ca_sut, f, host_dir)}

  # Collect network information from all SUTs
  #
  # We need this so that we don't insert any common IP addresses into certs
  suts_network_info = {}

  hosts.each do |host|
    fqdn = fact_on(host, 'fqdn').strip

    host_entry = { fqdn => [] }

    # Add the short name because containers can't change the hostname
    host_entry[fqdn] << host.name if (host[:hypervisor] == 'docker')

    # Ensure that all interfaces are active prior to collecting data
    activate_interfaces(host) unless ENV['BEAKER_no_fix_interfaces']

    # Gather the IP Addresses for the host to embed in the cert
    interfaces = fact_on(host, 'interfaces').strip.split(',')
    interfaces.each do |interface|
      ipaddress = fact_on(host, "ipaddress_#{interface}")

      next if ipaddress.nil? || ipaddress.empty? || ipaddress.start_with?('127.')

      host_entry[fqdn] << ipaddress.strip

      unless host_entry[fqdn].empty?
        suts_network_info[fqdn] = host_entry[fqdn].sort.uniq
      end
    end
  end

  # Get all of the repeated SUT IP addresses:
  #   1. Create a hash of elements that have a key that is the value and
  #      elements that are the same value
  #   2. Grab all elements that have more than one value (therefore, were
  #      repeated)
  #   3. Pull out an Array of all of the common element keys for future
  #      comparison
  common_ip_addresses = suts_network_info
    .values.flatten
    .group_by{ |x| x }
    .select{|k,v| v.size > 1}
    .keys

  # generate PKI certs for each SUT
  Dir.mktmpdir do |dir|
    pki_hosts_file = File.join(dir, 'pki.hosts')

    File.open(pki_hosts_file, 'w') do |fh|
      suts_network_info.each do |fqdn, ipaddresses|
        fh.puts ([fqdn] + (ipaddresses - common_ip_addresses)) .join(',')
      end
    end

    copy_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_hiera_config_on(sut, hiera_yaml) ⇒ Object

Updates the default environment hiera.yaml

Parameters:

  • sut (Host)

    One host to act upon

  • hiera_yaml (Hash, String)

    The data to place into hiera.yaml



1175
1176
1177
1178
1179
# File 'lib/simp/beaker_helpers.rb', line 1175

def set_hiera_config_on(sut, hiera_yaml)
  hiera_yaml = hiera_yaml.to_yaml if hiera_yaml.is_a?(Hash)

  create_remote_file(sut, hiera_config_path_on(sut), hiera_yaml)
end

#set_hieradata_on(sut, hieradata, terminus = 'deprecated') ⇒ Nil

Note:

This is authoritative. It manages both Hiera data and configuration, so it may not be used with other Hiera data sources.

Write the provided data structure to Hiera’s :datadir and configure Hiera to use that data exclusively.

Parameters:

  • sut (Array<Host>, String, Symbol)

    One or more hosts to act upon.

  • heradata (Hash, String)

    The full hiera data structure to write to the system.

  • terminus (String) (defaults to: 'deprecated')

    DEPRECATED - Will be removed in a future release. All hieradata is written to the first discovered path via ‘puppet lookup’

Returns:

  • (Nil)


1290
1291
1292
# File 'lib/simp/beaker_helpers.rb', line 1290

def set_hieradata_on(sut, hieradata, terminus = 'deprecated')
  write_hieradata_to sut, hieradata
end

#set_simp_repo_release(sut, simp_release_type = 'stable', simp_release = '6') ⇒ Object

Set the release and release type of the SIMP yum repos

Environment variables may be used to set either one

* BEAKER_SIMP_repo_release => The actual release (version number)
* BEAKER_SIMP_repo_release_type => The type of release (stable, unstable, rolling, etc...)


1536
1537
1538
1539
1540
1541
1542
1543
1544
# File 'lib/simp/beaker_helpers.rb', line 1536

def set_simp_repo_release(sut, simp_release_type='stable', simp_release='6')
  simp_release = ENV.fetch('BEAKER_SIMP_repo_release', simp_release)
  simp_release_type = ENV.fetch('BEAKER_SIMP_repo_release_type', simp_release_type)

  simp_release_type = 'releases' if (simp_release_type == 'stable')

  create_remote_file(sut, '/etc/yum/vars/simprelease', simp_release)
  create_remote_file(sut, '/etc/yum/vars/simpreleasetype', simp_release_type)
end

#set_yum_opt_on(suts, key, value) ⇒ Object

Sets a single YUM option in the form that yum-config-manager/dnf config-manager would expect.

If not prefaced with a repository, the option will be applied globally.

Has no effect if yum or dnf is not present.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/simp/beaker_helpers.rb', line 27

def set_yum_opt_on(suts, key, value)
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    repo,target = key.split('.')

    unless target
      key = "\\*.#{repo}"
    end

    command = nil
    if !sut.which('dnf').empty?
      install_package_unless_present_on(sut, 'dnf-plugins-core', :accept_all_exit_codes => true)
      command = 'dnf config-manager'
    elsif !sut.which('yum').empty?
      command = 'yum-config-manager'
    end

    on(sut, %{#{command} --save --setopt=#{key}=#{value}}, :silent => true) if command
  end
end

#set_yum_opts_on(suts, yum_opts = {}) ⇒ Object

Takes a hash of YUM options to set in the form that yum-config-manager/dnf config-manager would expect.

If not prefaced with a repository, the option will be applied globally.

Example:

{
  'skip_if_unavailable' => '1', # Applies globally
  'foo.installonly_limit' => '5' # Applies only to the 'foo' repo
}


58
59
60
61
62
63
64
65
# File 'lib/simp/beaker_helpers.rb', line 58

def set_yum_opts_on(suts, yum_opts={})
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    yum_opts.each_pair do |k,v|
      set_yum_opt_on(sut, k, v)
    end
  end
end

#sosreport(suts, dest = 'sosreports') ⇒ Object



843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
# File 'lib/simp/beaker_helpers.rb', line 843

def sosreport(suts, dest='sosreports')
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    install_latest_package_on(sut, 'sos')
    on(sut, 'sosreport --batch')

    files = on(sut, 'ls /var/tmp/sosreport* /tmp/sosreport* 2>/dev/null', :accept_all_exit_codes => true).output.lines.map(&:strip)

    FileUtils.mkdir_p(dest)

    files.each do |file|
      scp_from(sut, file, File.absolute_path(dest))
    end
  end
end

#update_package_from_centos_stream(suts, package_name) ⇒ Object



658
659
660
661
662
663
664
665
# File 'lib/simp/beaker_helpers.rb', line 658

def update_package_from_centos_stream(suts, package_name)
  parallel = (ENV['BEAKER_SIMP_parallel'] == 'yes')
  block_on(suts, :run_in_parallel => parallel) do |sut|
    sut.install_package('centos-release-stream') unless sut.check_for_package('centos-release-stream')
    install_latest_package_on(sut, package_name)
    sut.uninstall_package('centos-release-stream')
  end
end

#write_hieradata_to(sut, hieradata, terminus = 'deprecated') ⇒ Nil

Note:

This is useless unless Hiera is configured to use the data file. @see ‘#set_hiera_config_on`

Note:

This creates a tempdir on the host machine which should be removed using ‘#clear_temp_hieradata` in the `after(:all)` hook. It may also be retained for debugging purposes.

Writes a YAML file in the Hiera :datadir of a Beaker::Host.

Parameters:

  • sut (Array<Host>, String, Symbol)

    One or more hosts to act upon.

  • hieradata (Hash, String)

    The full hiera data structure to write to the system.

  • terminus (String) (defaults to: 'deprecated')

    DEPRECATED - This will be removed in a future release and currently has no effect.

Returns:

  • (Nil)


1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
# File 'lib/simp/beaker_helpers.rb', line 1200

def write_hieradata_to(sut, hieradata, terminus = 'deprecated')
  @temp_hieradata_dirs ||= []
  data_dir = Dir.mktmpdir('hieradata')
  @temp_hieradata_dirs << data_dir

  fh = File.open(File.join(data_dir, 'common.yaml'), 'w')
  if hieradata.is_a?(String)
    fh.puts(hieradata)
  else
    fh.puts(hieradata.to_yaml)
  end
  fh.close

  copy_hiera_data_to sut, File.join(data_dir, 'common.yaml')
end