diff --git a/.fixtures.yml b/.fixtures.yml index b8538871e..0702e5146 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -15,6 +15,7 @@ fixtures: repositories: apache: repo: "https://github.com/mlibrary/puppetlabs-apache" + ref: "v7.1.1" forge_modules: rbenv: {"repo": "jdowning/rbenv", "ref": "3.1.0" } archive: {"repo": "puppet/archive", "ref": "7.1.0" } diff --git a/.gitattributes b/.gitattributes index 543dd6adb..9032a014a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,4 @@ *.erb eol=lf *.pp eol=lf *.sh eol=lf +*.epp eol=lf diff --git a/.gitignore b/.gitignore index b05c117ea..d8cdf58e4 100644 --- a/.gitignore +++ b/.gitignore @@ -14,13 +14,24 @@ /log/ /pkg/ /spec/fixtures/manifests/ -/spec/fixtures/modules/ +/spec/fixtures/modules/* /tmp/ /vendor/ +/.vendor/ /convert_report.txt /update_report.txt .DS_Store +.project +.envrc +/inventory.yaml +/spec/fixtures/litmus_inventory.yaml +.resource_types +.modules +.task_cache.json +.plan_cache.json +.rerun.json +bolt-debug.log +/.pdkignore /.tmp/ /Puppetfile.lock /modules/ -/spec/examples.txt diff --git a/.ruby-version b/.ruby-version index 1effb0034..8c50098d8 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7 +3.1 diff --git a/.sync.yml b/.sync.yml index 0034c7421..31442d5c4 100644 --- a/.sync.yml +++ b/.sync.yml @@ -7,14 +7,14 @@ - /.tmp/ - /Puppetfile.lock - /modules/ - - /spec/examples.txt Gemfile: required: ':development': - gem: faker - gem: librarian-puppet + version: '>= 5.0' - gem: pdk - version: 2.7.1 + version: '~> 3.3.0' spec/default_facts.yml: extra_facts: ec2_tag_role: nebula::role::aws diff --git a/.tool-versions b/.tool-versions deleted file mode 100644 index 59511e1d2..000000000 --- a/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -ruby 2.7.8 diff --git a/Dockerfile b/Dockerfile index 7bf98d581..e134acae7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ -FROM ruby:2.7 +FROM ruby:3.2 ARG UNAME=app ARG UID=1000 ARG GID=1000 ARG APP_HOME=/app -RUN gem install 'bundler:~>2.4.19' +RUN gem install 'bundler:~>2.5.11' RUN groupadd -g $GID -o $UNAME RUN useradd -m -d $APP_HOME -u $UID -g $GID -o -s /bin/bash $UNAME RUN mkdir -p /gems && chown $UID:$GID /gems diff --git a/Gemfile b/Gemfile index 8e555ce1d..7865e9dee 100644 --- a/Gemfile +++ b/Gemfile @@ -1,31 +1,81 @@ -source 'https://rubygems.org' +source ENV['GEM_SOURCE'] || 'https://rubygems.org' -minor_version = Gem::Version.new(RUBY_VERSION.dup).segments[0..1].join('.') +def location_for(place_or_version, fake_version = nil) + git_url_regex = %r{\A(?(https?|git)[:@][^#]*)(#(?.*))?} + file_url_regex = %r{\Afile:\/\/(?.*)} -gem 'rake', '>= 13.0.6' + if place_or_version && (git_url = place_or_version.match(git_url_regex)) + [fake_version, { git: git_url[:url], branch: git_url[:branch], require: false }].compact + elsif place_or_version && (file_url = place_or_version.match(file_url_regex)) + ['>= 0', { path: File.expand_path(file_url[:path]), require: false }] + else + [place_or_version, { require: false }] + end +end group :development do - gem "fast_gettext", require: false - gem "puppet-module-posix-default-r#{minor_version}", require: false, platforms: [:ruby] - gem "puppet-module-posix-dev-r#{minor_version}", require: false, platforms: [:ruby] - gem "rubocop-rake" - gem "faker" - gem "librarian-puppet" - gem "pdk", '2.7.1' + gem "json", '= 2.1.0', require: false if Gem::Requirement.create(['>= 2.5.0', '< 2.7.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.3.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.5.1', require: false if Gem::Requirement.create(['>= 3.0.0', '< 3.0.5']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.6.1', require: false if Gem::Requirement.create(['>= 3.1.0', '< 3.1.3']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.6.3', require: false if Gem::Requirement.create(['>= 3.2.0', '< 4.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "racc", '~> 1.4.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "deep_merge", '~> 1.2.2', require: false + gem "voxpupuli-puppet-lint-plugins", '~> 5.0', require: false + gem "facterdb", '~> 2.1', require: false + gem "metadata-json-lint", '~> 4.0', require: false + gem "rspec-puppet-facts", '~> 4.0', require: false + gem "dependency_checker", '~> 1.0.0', require: false + gem "parallel_tests", '= 3.12.1', require: false + gem "pry", '~> 0.10', require: false + gem "simplecov-console", '~> 0.9', require: false + gem "puppet-debugger", '~> 1.0', require: false + gem "rubocop", '~> 1.50.0', require: false + gem "rubocop-performance", '= 1.16.0', require: false + gem "rubocop-rspec", '= 2.19.0', require: false + gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw] + gem "rexml", '>= 3.0.0', '< 3.2.7', require: false + gem "faker", require: false + gem "librarian-puppet", '>= 5.0', require: false + gem "pdk", '~> 3.3.0', require: false +end +group :development, :release_prep do + gem "puppet-strings", '~> 4.0', require: false + gem "puppetlabs_spec_helper", '~> 7.0', require: false end +group :system_tests do + gem "puppet_litmus", '~> 1.0', require: false, platforms: [:ruby, :x64_mingw] + gem "CFPropertyList", '< 3.0.7', require: false, platforms: [:mswin, :mingw, :x64_mingw] + gem "serverspec", '~> 2.41', require: false +end + +puppet_version = ENV['PUPPET_GEM_VERSION'] +facter_version = ENV['FACTER_GEM_VERSION'] +hiera_version = ENV['HIERA_GEM_VERSION'] + +gems = {} -gem 'puppet', '~> 7.26' -gem 'puppet-strings' -gem 'semantic_puppet' -gem 'yard', '>= 0.9.36' +gems['puppet'] = location_for(puppet_version) + +# If facter or hiera versions have been specified via the environment +# variables + +gems['facter'] = location_for(facter_version) if facter_version +gems['hiera'] = location_for(hiera_version) if hiera_version + +gems.each do |gem_name, gem_params| + gem gem_name, *gem_params +end # Evaluate Gemfile.local and ~/.gemfile if they exist -[ +extra_gemfiles = [ "#{__FILE__}.local", File.join(Dir.home, '.gemfile'), -].each do |gemfile| +] + +extra_gemfiles.each do |gemfile| if File.file?(gemfile) && File.readable?(gemfile) eval(File.read(gemfile), binding) end end - +# vim: syntax=ruby diff --git a/Rakefile b/Rakefile index 64fa3a276..77590fe6f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,50 +1,9 @@ +# frozen_string_literal: true + +require 'bundler' +require 'puppet_litmus/rake_tasks' if Gem.loaded_specs.key? 'puppet_litmus' require 'puppetlabs_spec_helper/rake_tasks' require 'puppet-syntax/tasks/puppet-syntax' +require 'puppet-strings/tasks' if Gem.loaded_specs.key? 'puppet-strings' -# We sometimes refer to non-nebula puppet fileserver paths. -PuppetLint.configuration.send('disable_puppet_url_without_modules') - -desc "run librarian-puppet to confirm dependencies are resolvable" -task librarian: [:librarian_standalone, :librarian_clean] - -desc "don't clean after librarian" -task :librarian_standalone do |t| - system('librarian-puppet install --verbose') or abort -end - -desc "rm Puppetfile.lock" -task :librarian_clean do |t| - FileUtils.rm_f('Puppetfile.lock') -end - -desc "list outdated modules in .fixtures.yml" -task :outdated do |t| - require 'yaml' - require 'net/http' - require 'uri' - require 'json' - - fixtures = YAML.load_file('.fixtures.yml') - fixtures['fixtures']['forge_modules'].values.each do |mod| - repo = mod['repo'] - slug = repo.tr('/','-') - vers = mod['ref'] - uri = URI.parse "https://forgeapi.puppet.com/v3/modules/#{slug}" - - response = Net::HTTP.get_response(uri) - raise "failed to fetch #{mod['repo']}" unless response.code == '200' - - releases = JSON.parse(response.body)['releases'].map{|x| x['version']} - latest = releases.first - installed_index = releases.find_index(vers) - installed = releases[installed_index] - - if installed_index != 0 - newer = releases.slice(0,installed_index).join(', ') - puts "#{repo} (#{installed}) < #{newer}" - puts "https://forge.puppet.com/modules/#{repo}" - - puts - end - end -end +PuppetLint.configuration.send('disable_relative') diff --git a/lib/puppet/functions/public_ip.rb b/lib/puppet/functions/public_ip.rb index 76ba415d7..1f43beb56 100644 --- a/lib/puppet/functions/public_ip.rb +++ b/lib/puppet/functions/public_ip.rb @@ -13,7 +13,7 @@ def run if aws_node? ec2_metadata['public-ipv4'] else - facts['ipaddress'] + facts['networking']['ip'] end end diff --git a/metadata.json b/metadata.json index 4ebe960d1..8effbd299 100644 --- a/metadata.json +++ b/metadata.json @@ -20,7 +20,7 @@ {"name": "puppetlabs/concat", "version_requirement": ">= 9.0.2 < 10.0.0"}, {"name": "puppetlabs/cron_core", "version_requirement": ">= 1.3.0 < 2.0.0" }, {"name": "puppetlabs/docker", "version_requirement": ">= 10.0.1 < 11.0.0"}, - {"name": "puppetlabs/firewall", "version_requirement": ">= 8.1.1 < 9.0.0 "}, + {"name": "puppetlabs/firewall", "version_requirement": ">= 8.1.1 < 9.0.0" }, {"name": "puppetlabs/host_core", "version_requirement": ">= 1.3.0 < 2.0.0" }, {"name": "puppetlabs/inifile", "version_requirement": ">= 6.1.1 < 7.0.0" }, {"name": "puppetlabs/lvm", "version_requirement": ">= 2.3.0 < 3.0.0" }, @@ -46,10 +46,10 @@ "requirements": [ { "name": "puppet", - "version_requirement": ">= 7.0.0 < 8.0.0" + "version_requirement": ">= 7.0.0 < 9.0.0" } ], - "pdk-version": "1.3.2", - "template-url": "file:///opt/puppetlabs/pdk/share/cache/pdk-templates.git", - "template-ref": "1.3.2-0-g07678c8" + "pdk-version": "3.3.0", + "template-url": "https://github.com/puppetlabs/pdk-templates#3.3.0", + "template-ref": "tags/3.3.0-0-g5d17ec1" } diff --git a/rakelib/forge.rake b/rakelib/forge.rake new file mode 100644 index 000000000..c2f380cb8 --- /dev/null +++ b/rakelib/forge.rake @@ -0,0 +1,31 @@ +desc "list outdated modules in .fixtures.yml" +task :outdated do |t| + require 'yaml' + require 'net/http' + require 'uri' + require 'json' + + fixtures = YAML.load_file('.fixtures.yml') + fixtures['fixtures']['forge_modules'].values.each do |mod| + repo = mod['repo'] + slug = repo.tr('/','-') + vers = mod['ref'] + uri = URI.parse "https://forgeapi.puppet.com/v3/modules/#{slug}" + + response = Net::HTTP.get_response(uri) + raise "failed to fetch #{mod['repo']}" unless response.code == '200' + + releases = JSON.parse(response.body)['releases'].map{|x| x['version']} + latest = releases.first + installed_index = releases.find_index(vers) + installed = releases[installed_index] + + if installed_index != 0 + newer = releases.slice(0,installed_index).join(', ') + puts "#{repo} (#{installed}) < #{newer}" + puts "https://forge.puppet.com/modules/#{repo}" + + puts + end + end +end diff --git a/rakelib/librarian.rake b/rakelib/librarian.rake new file mode 100644 index 000000000..10a6d095b --- /dev/null +++ b/rakelib/librarian.rake @@ -0,0 +1,12 @@ +desc "run librarian-puppet to confirm dependencies are resolvable" +task librarian: [:librarian_standalone, :librarian_clean] + +desc "don't clean after librarian" +task :librarian_standalone do |t| + system('librarian-puppet install --verbose') or abort +end + +desc "rm Puppetfile.lock" +task :librarian_clean do |t| + FileUtils.rm_f('Puppetfile.lock') +end diff --git a/spec/classes/profile/base_spec.rb b/spec/classes/profile/base_spec.rb index 3c26d22d5..cae94fdf2 100644 --- a/spec/classes/profile/base_spec.rb +++ b/spec/classes/profile/base_spec.rb @@ -13,7 +13,7 @@ def contain_base_class(name) on_supported_os.each do |os, os_facts| context "on #{os}" do let(:facts) { os_facts } - let(:fqdn) { facts[:fqdn] } + let(:fqdn) { facts[:networking]['fqdn'] } it { is_expected.to contain_service('puppet').without_ensure } it { is_expected.to contain_service('puppet').with_enable(true) } diff --git a/spec/default_facts.yml b/spec/default_facts.yml index a0c936ec2..1dc85cdc0 100644 --- a/spec/default_facts.yml +++ b/spec/default_facts.yml @@ -2,38 +2,17 @@ # # Facts specified here will override the values provided by rspec-puppet-facts. --- -concat_basedir: "/tmp" -ipaddress: "172.16.254.254" is_pe: false -macaddress: "AA:AA:AA:AA:AA:AA" -dmi: {} -disks: {} +ec2_tag_role: nebula::role::aws +datacenter: mydatacenter installed_backports: [] -datacenter: "mydatacenter" -prometheus_errors_total: 0 -mountpoints: {} -disks: {} -root_home: "/root" letsencrypt_directory: - foo.example.com: '/etc/letsencrypt/live/foo.example.com' - example.invalid: '/etc/letsencrypt/live/example.invalid' - -# required by puppetlabs/postgres since 8.1.0 -service_provider: systemd - -# required by apt with update => { frequency => 'daily' } -apt_update_last_success: '1523250000' - -ec2_tag_role: nebula::role::aws - -vm_guests: ['invalid_existing_guest'] - -# The ruby tests by default are set to install 2.4.3 and 2.5.0, so 2.4.2 -# represents an installed but not desired version of ruby, while 2.5.0 -# represents an already-installed and still desired version of ruby. + foo.example.com: "/etc/letsencrypt/live/foo.example.com" + example.invalid: "/etc/letsencrypt/live/example.invalid" +prometheus_errors_total: 0 ruby_versions: - 2.4.2 - 2.5.0 +vm_guests: +- invalid_existing_guest -# This can be removed once the firewall module is updated. -ip6tables_version: 1.6.0 diff --git a/spec/functions/public_ip_spec.rb b/spec/functions/public_ip_spec.rb index 10e86574a..6d51aec8e 100644 --- a/spec/functions/public_ip_spec.rb +++ b/spec/functions/public_ip_spec.rb @@ -6,5 +6,17 @@ require 'spec_helper' describe 'public_ip' do - it { is_expected.to run.and_return('172.16.254.254') } + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) do + os_facts.merge( + ipaddress: 'INVALID_DO_NOT_USE', + hostname: 'INVALID_DO_NOT_USE', + fqdn: 'INVALID_DO_NOT_USE', + ) + end + + it { is_expected.to run.and_return(facts[:networking]['ip']) } + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1ab9763c4..87471ead7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,35 +2,74 @@ RSpec.configure do |c| c.mock_with :rspec - c.example_status_persistence_file_path = 'spec/examples.txt' end require 'puppetlabs_spec_helper/module_spec_helper' require 'rspec-puppet-facts' -include RspecPuppetFacts require 'spec_helper_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_local.rb')) +include RspecPuppetFacts + default_facts = { puppetversion: Puppet.version, facterversion: Facter.version, } -# The os.architecture fact was not being populated, though the rest of the os hash is. -# This will promote the os.architecture value to the top level if it is set, otherwise -# push the top level down, if it is set, otherwise set both to amd64. -# We do not anticipate a case where these should remain different values in a test context. -add_custom_fact :architecture, ->(_os, facts) { facts[:os][:architecture] ||= (facts[:architecture] || 'amd64') } +default_fact_files = [ + File.expand_path(File.join(File.dirname(__FILE__), 'default_facts.yml')), + File.expand_path(File.join(File.dirname(__FILE__), 'default_module_facts.yml')), +] + +default_fact_files.each do |f| + next unless File.exist?(f) && File.readable?(f) && File.size?(f) + + begin + require 'deep_merge' + default_facts.deep_merge!(YAML.safe_load(File.read(f), permitted_classes: [], permitted_symbols: [], aliases: true)) + rescue StandardError => e + RSpec.configuration.reporter.message "WARNING: Unable to load #{f}: #{e}" + end +end + +# read default_facts and merge them over what is provided by facterdb +default_facts.each do |fact, value| + add_custom_fact fact, value, merge_facts: true +end + +RSpec.configure do |c| + c.default_facts = default_facts + c.before :each do + # set to strictest setting for testing + # by default Puppet runs at warning level + Puppet.settings[:strict] = :warning + Puppet.settings[:strict_variables] = true + end + c.filter_run_excluding(bolt: true) unless ENV['GEM_BOLT'] + c.after(:suite) do + RSpec::Puppet::Coverage.report!(0) + end -default_facts_path = File.expand_path(File.join(File.dirname(__FILE__), 'default_facts.yml')) -default_module_facts_path = File.expand_path(File.join(File.dirname(__FILE__), 'default_module_facts.yml')) + # Filter backtrace noise + backtrace_exclusion_patterns = [ + %r{spec_helper}, + %r{gems}, + ] -if File.exist?(default_facts_path) && File.readable?(default_facts_path) - default_facts.merge!(YAML.safe_load(File.read(default_facts_path))) + if c.respond_to?(:backtrace_exclusion_patterns) + c.backtrace_exclusion_patterns = backtrace_exclusion_patterns + elsif c.respond_to?(:backtrace_clean_patterns) + c.backtrace_clean_patterns = backtrace_exclusion_patterns + end end -if File.exist?(default_module_facts_path) && File.readable?(default_module_facts_path) - default_facts.merge!(YAML.safe_load(File.read(default_module_facts_path))) +# Ensures that a module is defined +# @param module_name Name of the module +def ensure_module_defined(module_name) + module_name.split('::').reduce(Object) do |last_module, next_module| + last_module.const_set(next_module, Module.new) unless last_module.const_defined?(next_module, false) + last_module.const_get(next_module, false) + end end # 'spec_overrides' from sync.yml will appear below this line