require 'rake/clean' require 'pp' require 'yaml' require 'securerandom' require 'fileutils' require 'tempfile' require 'beaker-hostgenerator' require 'beaker/dsl/install_utils' extend Beaker::DSL::InstallUtils REPO_CONFIGS_DIR = 'repo-configs' CLEAN.include('*.tar', REPO_CONFIGS_DIR, 'tmp', '.beaker', 'log', 'junit') # Default test target if none specified # TODO There are some projects that do not need to test against a master. It # might be worth it to distinguish between these two scenarios so that we are # maximizing our available resources. As it, we are allocating a master for all # runs. Luckily, that master is also classified as an agent, so it's not a total # waste of resources. DEFAULT_MASTER_TEST_TARGET = 'redhat7-64m' DEFAULT_TEST_TARGETS = "#{DEFAULT_MASTER_TEST_TARGET}a-windows2012r2-64a" USAGE = <<-EOS Usage: bundle exec rake [arguments] where is one of: ci:test:git ci:test:aio ci:test:gem See `bundle exec rake -D ` for information about each target. Each rake task accepts the following environment variables: REQUIRED -------- SHA: The git SHA of either the puppet-agent package or component repo must be specified depending on the target. The `git` and `gem` use the component SHA, where as `aio` uses the puppet-agent package SHA. OPTIONAL -------- HOSTS: The hosts to run against. Must be specified as a path to a file or a beaker-hostgenerator string. Defaults to #{DEFAULT_TEST_TARGETS}. HOSTS=mynodes.yaml HOSTS=redhat7-64ma TESTS: A comma-delimited list of files/directories containing tests to run. Defaults to running all tests. TESTS=tests/cycle_detection.rb TESTS=tests/pluginsync,tests/language OPTIONS: Additional options to pass to beaker. Defaults to ''. OPTIONS='--dry-run --no-color' BEAKER_HOSTS: Same as HOSTS. TEST_TARGET: A beaker-hostgenerator string of agent-only hosts to run against. The MASTER_TEST_TARGET host will be added this list. This option is only intended to be used in CI. MASTER_TEST_TARGET: Override the default master test target. Should only be used with TEST_TARGET, and is only intended to be used in CI. Defaults to #{DEFAULT_MASTER_TEST_TARGET}. DEV_BUILDS_URL: Override the url where we look for development builds of packages to test. Defaults to https://builds.delivery.puppetlabs.net NIGHTLY_BUILDS_URL: Override the url where we look for nightly builds of packages to test. Defaults to https://nightlies.puppet.com RELEASE_STREAM The release stream for the puppet family you want to test. This defaults to 'puppet', which is the current latest release stream. Other options are currently published streams like 'puppet5' or 'puppet6'. This is currently only used when accessing repos on nightlies.puppet.com SERVER_VERSION: The version of puppetserver to test against. This defaults to 'latest' if unset. When it defaults to latest, it will attept to pull packages from NIGHTLY_BUILDS_URL from the RELEASE_STREAM repo. FORK: Used to build a github url. If unset, this defaults to 'puppetlabs'. This can be used to point acceptance to pull a repo to test from a personal fork. $project_FORK: Similar to FORK, but project specific. If you have only one project (i.e., hiera) that you wanted to test from a different fork then all the others, you could set HIERA_FORK=melissa, you would get back 'https://github.com/melissa/hiera.git'. SERVER: Used to build a github url. If unset, this defaults to 'github.com'. This can be used to pull a github repo from a unique server location. $project_SERVER: Similar to SERVER, but project specific. If you have only one project (i.e., hiera) that you want to pull from a different server then all the others, you could set HIERA_SERVER=179.0.0.1, and you would get back 'https://179.0.0.1/puppetlabs-hiera.git'. RUNTIME_BRANCH: Currently only used with git-based testing. This must correspond to a branch in the puppet-agent repo. We use it to determine the tag of the agent-runtime package that we want. We also use it to construct the agent-runtime archive name (ie agent-runtime-${branch}-${tag}) EOS namespace :ci do desc "Print usage information" task :help do puts USAGE exit 1 end task :check_env do if ENV['SHA'].nil? puts "Error: A SHA must be specified" puts "\n" puts USAGE exit 1 end if ENV['TESTS'].nil? ENV['TESTS'] ||= ENV['TEST'] ENV['TESTS'] ||= 'tests' end end task :gen_hosts, [:hypervisor] do |t, args| hosts = if ENV['HOSTS'] ENV['HOSTS'] elsif ENV['BEAKER_HOSTS'] ENV['BEAKER_HOSTS'] elsif env_config = ENV['CONFIG'] puts 'Warning: environment variable CONFIG deprecated. Please use HOSTS to match beaker options.' env_config else # By default we assume TEST_TARGET is an agent-only string if agent_target = ENV['TEST_TARGET'] master_target = ENV['MASTER_TEST_TARGET'] || DEFAULT_MASTER_TEST_TARGET "#{master_target}-#{agent_target}" else DEFAULT_TEST_TARGETS end end if File.exists?(hosts) ENV['HOSTS'] = hosts else hosts_file = "tmp/#{hosts}-#{SecureRandom.uuid}.yaml" cli_args = [ hosts, '--disable-default-role', '--osinfo-version', '1' ] if args[:hypervisor] cli_args += ['--hypervisor', args[:hypervisor]] end cli = BeakerHostGenerator::CLI.new(cli_args) FileUtils.mkdir_p('tmp') # -p ignores when dir already exists File.open(hosts_file, 'w') do |fh| fh.print(cli.execute) end ENV['HOSTS'] = hosts_file end end namespace :test do desc <<-EOS Run the acceptance tests using puppet-agent (AIO) packages, with or without retrying failed tests one time. $ SHA= bundle exec rake ci:test:aio or $ SHA= bundle exec rake ci:test:aio[RETRIES] SHA should be the full SHA for the puppet-agent package. RETRIES should be set to true to retry failed tests one time. It defaults to false. EOS task :aio, [:retries] => ['ci:check_env', 'ci:gen_hosts'] do |t, args| args.with_defaults(retries: false) if args[:retries] beaker_suite_retry(:aio) else beaker_suite(:aio) end end desc <<-EOS Run the acceptance tests against puppet gem on various platforms, performing a basic smoke test. $ SHA= bundle exec rake:ci:gem SHA should be the full SHA for the component. EOS task :gem => ['ci:check_env'] do beaker(:init, '--hosts', 'config/nodes/gem.yaml', '--options-file', 'config/gem/options.rb') beaker(:provision) begin beaker(:exec, 'pre-suite', '--preserve-state', '--pre-suite', pre_suites(:gem)) beaker(:exec, "#{File.dirname(__dir__)}/setup/gem/010_GemInstall.rb") ensure preserve_hosts = ENV['OPTIONS'].include?('--preserve-hosts=always') if ENV['OPTIONS'] beaker(:destroy) unless preserve_hosts end end desc <<-EOS Run the acceptance tests against a git checkout. $ SHA= bundle exec rake ci:test:git SHA: for git based testing specifically, this can be a sha, a branch, or a tag. FORK: to test against your fork, defaults to 'puppetlabs' SERVER: to git fetch from an alternate GIT server, defaults to 'github.com' RUNTIME_BRANCH: the branch of the agent-runtime package to grab, defaults to 'master'. This tells us which branch of puppet-agent to get the runtime tag from and helps us create the archive name when we go to curl it down. EOS task :git => ['ci:check_env', 'ci:gen_hosts'] do beaker_suite(:git) end end task :test_and_preserve_hosts => ['ci:check_env', 'ci:gen_hosts'] do puts "WARNING, the test_and_preserve_hosts task is deprecated, use ci:test:aio instead." Rake::Task['ci:test:aio'].execute end end task :default do sh('rake -T') end task :spec do sh('rspec lib') end def beaker(command, *argv) argv.concat(ENV['OPTIONS'].split(' ')) if ENV['OPTIONS'] sh('beaker', command.to_s, *argv) end def beaker_suite(type) beaker(:init, '--hosts', ENV['HOSTS'], '--options-file', "config/#{String(type)}/options.rb") beaker(:provision) begin beaker(:exec, 'pre-suite', '--preserve-state', '--pre-suite', pre_suites(type)) beaker(:exec, 'pre-suite', '--preserve-state') beaker(:exec, ENV['TESTS']) beaker(:exec, 'post-suite') ensure preserve_hosts = ENV['OPTIONS'].include?('--preserve-hosts=always') if ENV['OPTIONS'] beaker(:destroy) unless preserve_hosts end end def beaker_suite_retry(type) beaker(:init, '--hosts', ENV['HOSTS'], '--options-file', "config/#{String(type)}/options.rb") beaker(:provision) begin json_results_file = Tempfile.new beaker(:exec, 'pre-suite', '--preserve-state', '--pre-suite', pre_suites(type)) beaker(:exec, 'pre-suite', '--preserve-state') beaker(:exec, ENV['TESTS'], '--test-results-file', json_results_file.path) rescue RuntimeError tests_to_rerun = JSON.load(File.read(json_results_file.path)) if tests_to_rerun.length > 0 puts '*** Retrying the following:' puts tests_to_rerun.map { |spec| " #{spec}" } beaker(:exec, tests_to_rerun.map { |str| "#{str}" }.join(',') ) end ensure beaker(:exec, 'post-suite') preserve_hosts = ENV['OPTIONS'].include?('--preserve-hosts=always') if ENV['OPTIONS'] beaker(:destroy) unless preserve_hosts end end def pre_suites(type) beaker_root = Pathname.new(File.dirname(__dir__)).relative_path_from(Pathname.new(Dir.pwd)) presuites = case type when :aio [ "#{beaker_root}/setup/common/000-delete-puppet-when-none.rb", "#{beaker_root}/setup/aio/010_Install_Puppet_Agent.rb", "#{beaker_root}/setup/common/011_Install_Puppet_Server.rb", "#{beaker_root}/setup/common/012_Finalize_Installs.rb", "#{beaker_root}/setup/common/020_InstallCumulusModules.rb", "#{beaker_root}/setup/common/021_InstallAristaModuleMasters.rb", "#{beaker_root}/setup/common/022_InstallAristaModuleAgents.rb", "#{beaker_root}/setup/common/025_StopFirewall.rb", "#{beaker_root}/setup/common/030_StopSssd.rb", "#{beaker_root}/setup/common/040_ValidateSignCert.rb", "#{beaker_root}/setup/common/045_EnsureMasterStarted.rb", ] when :gem [ "#{beaker_root}/setup/common/000-delete-puppet-when-none.rb", "#{beaker_root}/setup/git/000_EnvSetup.rb", ] when :git [ "#{beaker_root}/setup/common/000-delete-puppet-when-none.rb", "#{beaker_root}/setup/git/000_EnvSetup.rb", "#{beaker_root}/setup/git/010_TestSetup.rb", "#{beaker_root}/setup/common/011_Install_Puppet_Server.rb", "#{beaker_root}/setup/git/020_PuppetUserAndGroup.rb", "#{beaker_root}/setup/git/070_InstallCACerts.rb", "#{beaker_root}/setup/common/025_StopFirewall.rb", "#{beaker_root}/setup/common/030_StopSssd.rb", "#{beaker_root}/setup/common/040_ValidateSignCert.rb", "#{beaker_root}/setup/common/045_EnsureMasterStarted.rb", ] end presuites.join(',') end