diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..93ca4a7f0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +--- +version: 2 +updates: + + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..efc5d431f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,69 @@ +--- +name: Test + +on: + pull_request: + push: + branches: + - 'develop' + - '*-stable' + +concurrency: + group: ${{ github.ref_name }} + cancel-in-progress: true + +jobs: + rubocop: + name: Rubocop + uses: theforeman/actions/.github/workflows/rubocop.yml@v0 + + test: + name: "Ruby ${{ matrix.ruby }} / Puppet ${{ matrix.puppet }}" + needs: rubocop + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - ruby: '2.7' + puppet: '7.0' + - ruby: '3.0' + puppet: '7.0' + - ruby: '3.2' + puppet: '8.0' + env: + PUPPET_VERSION: ${{ matrix.puppet }} + steps: + - uses: actions/checkout@v4 + - name: Install system dependencies + run: sudo apt-get install -y --no-install-recommends asciidoc + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Archive Gemfile.lock + uses: actions/upload-artifact@v4 + with: + name: Gemfile-ruby-${{ matrix.ruby }}-puppet-${{ matrix.puppet }}.lock + path: Gemfile.lock + - name: Run tests + run: bundle exec rake spec + - name: Test installer configuration + run: | + bundle exec rake install PREFIX=./local --trace + bundle exec rake installation_tests PREFIX=./local --trace + - name: Archive Puppetfile.lock + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: Puppetfile-ruby-${{ matrix.ruby }}-puppet-${{ matrix.puppet }}.lock + path: Puppetfile.lock + + # A dummy job that you can mark as a required check instead of each individual test + test-suite: + needs: test + runs-on: ubuntu-latest + name: Test suite + steps: + - run: echo Test suite completed diff --git a/.gitignore b/.gitignore index d57874185..bde55c650 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ config/foreman.migrations/.applied spec/fixtures/katello-certs-check/certs/*.csr spec/fixtures/katello-certs-check/ca.key spec/fixtures/katello-certs-check/certs/*.srl + +.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..b118b44c6 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,55 @@ +--- +stages: + - lint + - test + - installation-tests + +variables: + RUBY_VERSION: "2.7" + RUBY_IMAGE: "ruby:${RUBY_VERSION}" + PUPPET_VERSION: "7.0" + +.common: + image: ${RUBY_IMAGE} + cache: + key: $CI_COMMIT_REF_SLUG + paths: + - vendor/ruby + before_script: + - bundle config set --local path 'vendor/ruby' + - bundle install -j $(nproc) + +lint: + extends: .common + stage: lint + script: + - bundle exec rubocop --format junit --out rubocop.xml --display-only-fail-level-offenses --fail-level=A + artifacts: + when: always + paths: + - rubocop.xml + reports: + junit: rubocop.xml + +test: + extends: .common + stage: test + script: + - bundle exec rspec --format RspecJunitFormatter --out rspec.xml + artifacts: + when: always + paths: + - rspec.xml + reports: + junit: rspec.xml + +installation-tests: + extends: .common + stage: installation-tests + variables: + PREFIX: "${RUBY_VERSION}" + script: + - apt update && apt install -y asciidoc-base docbook-xsl --no-install-recommends + - bundle exec rake install --trace + - bundle exec rake installation_tests --trace +... diff --git a/.packit.yaml b/.packit.yaml index bb4a0b255..2cadfb25c 100644 --- a/.packit.yaml +++ b/.packit.yaml @@ -25,7 +25,6 @@ actions: - bundle config set --local without development:test - bundle install - bundle exec rake pkg:generate_source - - bash -c "ls -1t pkg/*.tar.bz2 | head -n 1" jobs: - job: copr_build diff --git a/.rubocop.yml b/.rubocop.yml index e2f91227b..9157fa5ad 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,11 +1,15 @@ +inherit_from: .rubocop_todo.yml + AllCops: - TargetRubyVersion: 2.5 + TargetRubyVersion: 2.7 + NewCops: enable Exclude: - - '_build/**/*' - - 'pkg/**/*' - - 'Gemfile' - - 'Puppetfile' - - 'Rakefile' + - "_build/**/*" + - "pkg/**/*" + - "Gemfile" + - "Puppetfile" + - "Rakefile" + - 'vendor/**/*' Bundler/OrderedGems: Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 000000000..f05775194 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,21 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2024-04-12 16:14:26 +0530 using RuboCop version 0.80.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# Cop supports --auto-correct. +Lint/RedundantCopDisableDirective: + Exclude: + - 'hooks/boot/05-environment.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: both, prefix, postfix +Style/NegatedIf: + Exclude: + - 'hooks/pre_validations/01-reset_data.rb' diff --git a/Gemfile b/Gemfile index 0fcb87ded..ce09ce515 100644 --- a/Gemfile +++ b/Gemfile @@ -12,13 +12,17 @@ gem 'facter', '>= 3.0', '!= 4.0.52' gem 'puppet-strings' gem 'rake' +gem 'racc' if RUBY_VERSION >= '3.3' + +gem 'semverse', groups: [:development, :test] + group :test do gem 'rspec' + gem 'rspec_junit_formatter' gem 'rubocop', '~> 0.80.0' end group :development do # Dependencies for rake pin_modules gem 'puppet_forge' - gem 'semverse' end diff --git a/Puppetfile b/Puppetfile index 371c12bb4..8fc3261e4 100644 --- a/Puppetfile +++ b/Puppetfile @@ -1,11 +1,10 @@ forge 'https://forgeapi.puppet.com/' # HTTP/2 and SSL support for settings in Hiera -# Our modules aren't yet compatible with 12 and the builds fail -mod 'puppetlabs/apache', '>= 8.3', '< 12' +mod 'puppetlabs/apache', '>= 8.3' -# Ensure Debian 11 support -mod 'puppetlabs/postgresql', '>= 7.4.0' +# SCRAM password support +mod 'puppetlabs/postgresql', '>= 10.1' # Dnfmodule support for Redis 6+ support mod 'puppet/redis', '>= 8.5.0' @@ -23,7 +22,6 @@ mod 'theforeman/tftp', :git => 'https://github.com/theforeman/pu # Katello dependencies mod 'katello/candlepin', :git => 'https://github.com/theforeman/puppet-candlepin' mod 'theforeman/pulpcore', :git => 'https://github.com/theforeman/puppet-pulpcore' -mod 'katello/qpid', :git => 'https://github.com/theforeman/puppet-qpid' # Top-level modules mod 'theforeman/foreman', :git => 'https://github.com/theforeman/puppet-foreman' diff --git a/Rakefile b/Rakefile index e6f1779ef..de7701996 100644 --- a/Rakefile +++ b/Rakefile @@ -15,56 +15,6 @@ rescue LoadError puts 'Rubocop not loaded' end -begin - require 'puppet_forge' - require 'semverse' - - class FakePuppetfile - def initialize - @new_content = [] - end - - def forge(url) - @new_content << ['forge', url, nil] - PuppetForge.host = url - end - - def mod(name, options = nil) - if options.is_a?(Hash) && !options.include?(:ref) - release = PuppetForge::Module.find(name.tr('/', '-')).current_release - @new_content << ['mod', name, "~> #{release.version}"] - else - @new_content << ['mod', name, options] - end - end - - def content - max_length = @new_content.select { |type, _value| type == 'mod' }.map { |_type, value| value.length }.max - - @new_content.each do |type, value, options| - if type == 'forge' - yield "forge '#{value}'" - yield "" - elsif type == 'mod' - if options.nil? - yield "mod '#{value}'" - elsif options.is_a?(String) - padding = ' ' * (max_length - value.length) - yield "mod '#{value}', #{padding}'#{options}'" - else - padding = ' ' * (max_length - value.length) - yield "mod '#{value}', #{padding}#{options.map { |k, v| ":#{k} => '#{v}'" }.join(', ')}" - end - end - end - end - end - - pin_task = true -rescue LoadError - pin_task = false -end - BUILD_KATELLO = !ENV.key?('EXCLUDE_KATELLO') BUILDDIR = File.expand_path(ENV['BUILDDIR'] || '_build') @@ -339,6 +289,14 @@ CLEAN.include [ PKGDIR, ] +task :installation_tests do + sh "bundle exec #{PREFIX}/sbin/foreman-installer --help --scenario foreman --trace" + sh "bundle exec #{PREFIX}/sbin/foreman-installer --help --scenario foreman-proxy-content --trace" + sh "bundle exec #{PREFIX}/sbin/foreman-installer --help --scenario katello --trace" + sh "bundle exec #{PREFIX}/sbin/foreman-proxy-certs-generate --help --trace" + sh "bundle exec #{PREFIX}/sbin/foreman-proxy-certs-generate --help | grep -q certs-update-server" +end + namespace :pkg do desc 'Generate package source tar.bz2' task :generate_source => [PKGDIR, "#{BUILDDIR}/modules"] do @@ -355,7 +313,11 @@ namespace :pkg do end end -if pin_task +begin + require_relative 'util/fake_puppet_file' +rescue LoadError + # Some dependency missing +else desc 'Pin all the modules in Puppetfile to released versions instead of git branches' task :pin_modules do filename = 'Puppetfile' diff --git a/VERSION b/VERSION index 9bbaed609..9f54573e3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.10.0-develop +3.12.0-develop diff --git a/bin/create-migration b/bin/create-migration index afb6d3e91..c5093d123 100755 --- a/bin/create-migration +++ b/bin/create-migration @@ -8,7 +8,7 @@ end migration_name = ARGV[0] directories = ARGV[1..-1] -content = (STDIN.tty? || STDIN.closed?) ? nil : STDIN.read +content = ($stdin.tty? || $stdin.closed?) ? nil : $stdin.read directories.each do |directory| unless File.directory?(directory) diff --git a/bin/foreman-proxy-certs-generate b/bin/foreman-proxy-certs-generate index d4d6e9f8a..50a3ea844 100755 --- a/bin/foreman-proxy-certs-generate +++ b/bin/foreman-proxy-certs-generate @@ -15,11 +15,7 @@ Kafo::KafoConfigure.hooking.register_pre(:init) do data = YAML.load_file(LAST_SCENARIO_PATH) if data && data[:answer_file] && File.file?(data[:answer_file]) scenario = YAML.load_file(data[:answer_file]) - organization = begin - scenario['foreman']['initial_organization'] - rescue StandardError - nil - end + organization = scenario&.dig('foreman', 'initial_organization') end end diff --git a/bin/katello-certs-check b/bin/katello-certs-check index d96391397..82f568563 100755 --- a/bin/katello-certs-check +++ b/bin/katello-certs-check @@ -164,7 +164,7 @@ function check-ca-bundle () { error 4 "The $CA_BUNDLE_FILE does not verify the $CERT_FILE" echo -e "${CHECK/OK/}\n" else - success + success fi } @@ -173,12 +173,24 @@ function check-ca-bundle-size () { CHECK=$(grep -c "^--*BEGIN" $CA_BUNDLE_FILE) printf $CHECK if [[ $CHECK -lt $CABUNDLE_MAX_ISSUERS ]]; then - success + success + else + CERRTISSUER=$(openssl x509 -noout -in $CERT_FILE -issuer 2>&1) + error 10 "The CA bundle counts $CHECK issuers. Please trim your CA bundle and include only the certs relevant to your cert file" + echo $CERTISSUER + echo + fi +} + +function check-ca-bundle-trust-rules () { + printf "Checking if CA bundle has trust rules: " + CHECK=$(grep 'BEGIN TRUSTED CERTIFICATE' $CA_BUNDLE_FILE| wc -l) + printf $CHECK + if [[ $CHECK -lt 1 ]]; then + success else - CERTISSUER=$(openssl x509 -noout -in $CERT_FILE -issuer 2>&1) - error 10 "The CA bundle counts $CHECK issuers. Please trim your CA bundle and include only the certs relevant to your cert file" - echo $CERTISSUER - echo + error 10 "The CA bundle contains $CHECK certificate(s) with trust rules. This may create problems for older systems to trust the bundle. Please, recreate the bundle using certificates without trust rules" + echo fi } @@ -251,6 +263,7 @@ check-passphrase check-priv-key check-ca-bundle check-ca-bundle-size +check-ca-bundle-trust-rules check-cert-san check-cert-usage-key-encipherment check-shortname diff --git a/checks/encoding.rb b/checks/encoding.rb new file mode 100755 index 000000000..785015da4 --- /dev/null +++ b/checks/encoding.rb @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby + +INVALID_ENCODING = 'The system encoding is not set to UTF-8.'.freeze + +def error_exit(message, code) + $stderr.puts message + exit code +end + +error_exit(INVALID_ENCODING, 1) if Encoding.locale_charmap != 'UTF-8' diff --git a/checks/hostname.rb b/checks/hostname.rb index f42cc97f1..cb1532b26 100755 --- a/checks/hostname.rb +++ b/checks/hostname.rb @@ -11,7 +11,7 @@ def error_exit(message, code) Make sure the above command is installed and executable in your system. ".freeze -ENV['PATH'] = ENV['PATH'].split(File::PATH_SEPARATOR).concat(['/opt/puppetlabs/bin']).join(File::PATH_SEPARATOR) +ENV['PATH'] = ENV['PATH'].split(File::PATH_SEPARATOR).push('/opt/puppetlabs/bin').join(File::PATH_SEPARATOR) system("which facter > /dev/null 2>&1") error_exit(MISSING, 3) if $CHILD_STATUS.exitstatus == 1 diff --git a/checks/lang.rb b/checks/lang.rb deleted file mode 100755 index e0b533e14..000000000 --- a/checks/lang.rb +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env ruby - -INVALID_LANG = 'The LANG environment variable should not be set to C'.freeze -INVALID_LC_ALL = 'The LC_ALL environment variable should not be set to C'.freeze - -def error_exit(message, code) - $stderr.puts message - exit code -end - -error_exit(INVALID_LANG, 1) if ENV["LANG"] == "C" -error_exit(INVALID_LC_ALL, 1) if ENV["LC_ALL"] == "C" diff --git a/checks/noexec b/checks/noexec new file mode 100755 index 000000000..63b68d07c --- /dev/null +++ b/checks/noexec @@ -0,0 +1,9 @@ +#!/bin/sh + +TARGET="/opt/puppetlabs" + +if findmnt --target "$TARGET" --noheadings --output options | grep -q noexec ; then + echo "$TARGET is on mount with the noexec option." >&2 + echo "Installation requires access to binaries which are stored under $TARGET and must therefore be mounted with exec." >&2 + exit 2 +fi diff --git a/config/foreman-answers.yaml b/config/foreman-answers.yaml index 815317824..26088a576 100644 --- a/config/foreman-answers.yaml +++ b/config/foreman-answers.yaml @@ -40,7 +40,6 @@ foreman::plugin::expire_hosts: false foreman::plugin::git_templates: false foreman::plugin::google: false foreman::plugin::hdm: false -foreman::plugin::hooks: false foreman::plugin::host_extra_validator: false foreman::plugin::kubevirt: false foreman::plugin::leapp: false @@ -56,7 +55,6 @@ foreman::plugin::remote_execution: false foreman::plugin::remote_execution::cockpit: false foreman::plugin::rescue: false foreman::plugin::salt: false -foreman::plugin::setup: false foreman::plugin::snapshot_management: false foreman::plugin::statistics: false foreman::plugin::tasks: false diff --git a/config/foreman-proxy-content.migrations/180111142132-foreman_proxy_autosignfile.rb b/config/foreman-proxy-content.migrations/180111142132-foreman_proxy_autosignfile.rb index a2ab711a8..19b472fde 100644 --- a/config/foreman-proxy-content.migrations/180111142132-foreman_proxy_autosignfile.rb +++ b/config/foreman-proxy-content.migrations/180111142132-foreman_proxy_autosignfile.rb @@ -2,6 +2,6 @@ answers['foreman_proxy']['use_autosignfile'] = true if answers['foreman_proxy'].key?('puppetdir') puppetdir = answers['foreman_proxy']['puppetdir'] - answers['foreman_proxy']['autosignfile'] = puppetdir + '/autosign.conf' + answers['foreman_proxy']['autosignfile'] = "#{puppetdir}/autosign.conf" end end diff --git a/config/foreman-proxy-content.migrations/190111180118-delete-removed-settings.rb b/config/foreman-proxy-content.migrations/190111180118-delete-removed-settings.rb index 116cb2b60..3a972ec95 100644 --- a/config/foreman-proxy-content.migrations/190111180118-delete-removed-settings.rb +++ b/config/foreman-proxy-content.migrations/190111180118-delete-removed-settings.rb @@ -69,6 +69,6 @@ end end -if (mod_answers = answers['foreman_proxy']) - mod_answers['dhcp_gateway'] = nil if mod_answers['dhcp_gateway'] == '192.168.100.1' +if (mod_answers = answers['foreman_proxy']) && (mod_answers['dhcp_gateway'] == '192.168.100.1') + mod_answers['dhcp_gateway'] = nil end diff --git a/config/foreman-proxy-content.migrations/210331121715-clear-puppetserver-nil-metrics.rb b/config/foreman-proxy-content.migrations/210331121715-clear-puppetserver-nil-metrics.rb index 0c23b2c7c..218f435cd 100644 --- a/config/foreman-proxy-content.migrations/210331121715-clear-puppetserver-nil-metrics.rb +++ b/config/foreman-proxy-content.migrations/210331121715-clear-puppetserver-nil-metrics.rb @@ -1,3 +1,3 @@ -if answers['puppet'].is_a?(Hash) - answers['puppet'].delete('server_puppetserver_metrics') if answers['puppet']['server_puppetserver_metrics'].nil? +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_puppetserver_metrics'].nil? + answers['puppet'].delete('server_puppetserver_metrics') end diff --git a/config/foreman-proxy-content.migrations/240322170637-reset-puppet-server-java-bin.rb b/config/foreman-proxy-content.migrations/240322170637-reset-puppet-server-java-bin.rb new file mode 100644 index 000000000..5f6018274 --- /dev/null +++ b/config/foreman-proxy-content.migrations/240322170637-reset-puppet-server-java-bin.rb @@ -0,0 +1,3 @@ +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_jvm_java_bin'] == '/usr/bin/java' + answers['puppet'].delete('server_jvm_java_bin') +end diff --git a/config/foreman-proxy-content.migrations/240328125552-reset-puppetserver-ciphers.rb b/config/foreman-proxy-content.migrations/240328125552-reset-puppetserver-ciphers.rb new file mode 100644 index 000000000..e95a07c3d --- /dev/null +++ b/config/foreman-proxy-content.migrations/240328125552-reset-puppetserver-ciphers.rb @@ -0,0 +1,5 @@ +# https://github.com/theforeman/puppet-puppet/commit/8cc4e3094d5bbd6d05d794e087816934e1697a87 +old_ciphers = ['TLS_RSA_WITH_AES_256_CBC_SHA256', 'TLS_RSA_WITH_AES_256_CBC_SHA', 'TLS_RSA_WITH_AES_128_CBC_SHA256', 'TLS_RSA_WITH_AES_128_CBC_SHA'] +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_cipher_suites'] == old_ciphers + answers['puppet'].delete('server_cipher_suites') +end diff --git a/config/foreman.hiera/common.yaml b/config/foreman.hiera/common.yaml index b8a3db668..b458706e3 100644 --- a/config/foreman.hiera/common.yaml +++ b/config/foreman.hiera/common.yaml @@ -18,3 +18,6 @@ dhcp::config_comment: | foreman::config::apache::proxy_params: retry: '0' timeout: '900' + +# Match default in PostgreSQL 14+ sooner to phase out MD5 +postgresql::globals::password_encryption: 'scram-sha-256' diff --git a/config/foreman.hiera/family/RedHat-8.yaml b/config/foreman.hiera/family/RedHat-8.yaml index 550d541ab..d030700b1 100644 --- a/config/foreman.hiera/family/RedHat-8.yaml +++ b/config/foreman.hiera/family/RedHat-8.yaml @@ -1,6 +1,6 @@ --- postgresql::globals::manage_dnf_module: true -postgresql::globals::version: "12" +postgresql::globals::version: "13" redis::dnf_module_stream: "6" redis::package_ensure: ">=6.0" diff --git a/config/foreman.migrations/20170530141108_foreman_proxy_autosignfile.rb b/config/foreman.migrations/20170530141108_foreman_proxy_autosignfile.rb index 0ec6ff542..ac0d81e23 100644 --- a/config/foreman.migrations/20170530141108_foreman_proxy_autosignfile.rb +++ b/config/foreman.migrations/20170530141108_foreman_proxy_autosignfile.rb @@ -2,6 +2,6 @@ answers['foreman_proxy']['use_autosignfile'] = true if answers['foreman_proxy'].has_key?('puppetdir') puppetdir = answers['foreman_proxy']['puppetdir'] - answers['foreman_proxy']['autosignfile'] = puppetdir + '/autosign.conf' + answers['foreman_proxy']['autosignfile'] = "#{puppetdir}/autosign.conf" end end diff --git a/config/foreman.migrations/20190111180118_delete_removed_settings.rb b/config/foreman.migrations/20190111180118_delete_removed_settings.rb index 116cb2b60..3a972ec95 100644 --- a/config/foreman.migrations/20190111180118_delete_removed_settings.rb +++ b/config/foreman.migrations/20190111180118_delete_removed_settings.rb @@ -69,6 +69,6 @@ end end -if (mod_answers = answers['foreman_proxy']) - mod_answers['dhcp_gateway'] = nil if mod_answers['dhcp_gateway'] == '192.168.100.1' +if (mod_answers = answers['foreman_proxy']) && (mod_answers['dhcp_gateway'] == '192.168.100.1') + mod_answers['dhcp_gateway'] = nil end diff --git a/config/foreman.migrations/20200909151007_manage_acls_on_debian.rb b/config/foreman.migrations/20200909151007_manage_acls_on_debian.rb index 54f68e155..f15c5509e 100644 --- a/config/foreman.migrations/20200909151007_manage_acls_on_debian.rb +++ b/config/foreman.migrations/20200909151007_manage_acls_on_debian.rb @@ -1,3 +1,3 @@ -if answers['foreman_proxy'].is_a?(Hash) && facts[:os][:family] == 'Debian' - answers['foreman_proxy']['dhcp_manage_acls'] ||= true if answers['foreman_proxy'].key?('dhcp_manage_acls') +if answers['foreman_proxy'].is_a?(Hash) && facts[:os][:family] == 'Debian' && answers['foreman_proxy'].key?('dhcp_manage_acls') + answers['foreman_proxy']['dhcp_manage_acls'] ||= true end diff --git a/config/foreman.migrations/20210331121715_clear_puppetserver-nil-metrics.rb b/config/foreman.migrations/20210331121715_clear_puppetserver-nil-metrics.rb index 0c23b2c7c..218f435cd 100644 --- a/config/foreman.migrations/20210331121715_clear_puppetserver-nil-metrics.rb +++ b/config/foreman.migrations/20210331121715_clear_puppetserver-nil-metrics.rb @@ -1,3 +1,3 @@ -if answers['puppet'].is_a?(Hash) - answers['puppet'].delete('server_puppetserver_metrics') if answers['puppet']['server_puppetserver_metrics'].nil? +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_puppetserver_metrics'].nil? + answers['puppet'].delete('server_puppetserver_metrics') end diff --git a/config/foreman.migrations/20210625142707_dynamic_puppet_in_foreman_groups.rb b/config/foreman.migrations/20210625142707_dynamic_puppet_in_foreman_groups.rb index b2f0ecdad..e9aabca9b 100644 --- a/config/foreman.migrations/20210625142707_dynamic_puppet_in_foreman_groups.rb +++ b/config/foreman.migrations/20210625142707_dynamic_puppet_in_foreman_groups.rb @@ -1,5 +1,3 @@ -if answers['foreman'].is_a?(Hash) - if answers['foreman']['user_groups'] && answers['foreman']['user_groups'].include?('puppet') - answers['foreman']['user_groups'].delete('puppet') - end +if answers['foreman'].is_a?(Hash) && (answers['foreman']['user_groups'] && answers['foreman']['user_groups'].include?('puppet')) + answers['foreman']['user_groups'].delete('puppet') end diff --git a/config/foreman.migrations/20210809133823_reset_puma_min_threads.rb b/config/foreman.migrations/20210809133823_reset_puma_min_threads.rb index be69d7f59..b6bd7af47 100644 --- a/config/foreman.migrations/20210809133823_reset_puma_min_threads.rb +++ b/config/foreman.migrations/20210809133823_reset_puma_min_threads.rb @@ -1,6 +1,4 @@ -if answers['foreman'].is_a?(Hash) - if answers['foreman']['foreman_service_puma_threads_min'] == 0 || - answers['foreman']['foreman_service_puma_threads_min'] == answers['foreman']['foreman_service_puma_threads_max'] - answers['foreman'].delete('foreman_service_puma_threads_min') - end +if answers['foreman'].is_a?(Hash) && (answers['foreman']['foreman_service_puma_threads_min'] == 0 || + answers['foreman']['foreman_service_puma_threads_min'] == answers['foreman']['foreman_service_puma_threads_max']) + answers['foreman'].delete('foreman_service_puma_threads_min') end diff --git a/config/foreman.migrations/20210819164113_reset_puma_workers_and_max_threads.rb b/config/foreman.migrations/20210819164113_reset_puma_workers_and_max_threads.rb index 124a81472..a6dff216b 100644 --- a/config/foreman.migrations/20210819164113_reset_puma_workers_and_max_threads.rb +++ b/config/foreman.migrations/20210819164113_reset_puma_workers_and_max_threads.rb @@ -1,12 +1,8 @@ # https://github.com/theforeman/puppet-foreman/commit/533c1f3be8069139d7375841019afeb7c1102830 -if answers['foreman'].is_a?(Hash) - if answers['foreman']['foreman_service_puma_workers'] == 2 - answers['foreman'].delete('foreman_service_puma_workers') - end +if answers['foreman'].is_a?(Hash) && (answers['foreman']['foreman_service_puma_workers'] == 2) + answers['foreman'].delete('foreman_service_puma_workers') end -if answers['foreman'].is_a?(Hash) - if answers['foreman']['foreman_service_puma_threads_max'] == 16 - answers['foreman'].delete('foreman_service_puma_threads_max') - end +if answers['foreman'].is_a?(Hash) && (answers['foreman']['foreman_service_puma_threads_max'] == 16) + answers['foreman'].delete('foreman_service_puma_threads_max') end diff --git a/config/foreman.migrations/20240322170637_reset_puppet_server_java_bin.rb b/config/foreman.migrations/20240322170637_reset_puppet_server_java_bin.rb new file mode 100644 index 000000000..5f6018274 --- /dev/null +++ b/config/foreman.migrations/20240322170637_reset_puppet_server_java_bin.rb @@ -0,0 +1,3 @@ +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_jvm_java_bin'] == '/usr/bin/java' + answers['puppet'].delete('server_jvm_java_bin') +end diff --git a/config/foreman.migrations/20240326121346_drop_setup_plugin.rb b/config/foreman.migrations/20240326121346_drop_setup_plugin.rb new file mode 100644 index 000000000..f1b6c7605 --- /dev/null +++ b/config/foreman.migrations/20240326121346_drop_setup_plugin.rb @@ -0,0 +1 @@ +answers.delete('foreman::plugin::setup') diff --git a/config/foreman.migrations/20240327093251_drop_hooks_plugin.rb b/config/foreman.migrations/20240327093251_drop_hooks_plugin.rb new file mode 100644 index 000000000..da33fe307 --- /dev/null +++ b/config/foreman.migrations/20240327093251_drop_hooks_plugin.rb @@ -0,0 +1 @@ +answers.delete('foreman::plugin::hooks') diff --git a/config/foreman.migrations/20240328125552_reset_puppetserver_ciphers.rb b/config/foreman.migrations/20240328125552_reset_puppetserver_ciphers.rb new file mode 100644 index 000000000..e95a07c3d --- /dev/null +++ b/config/foreman.migrations/20240328125552_reset_puppetserver_ciphers.rb @@ -0,0 +1,5 @@ +# https://github.com/theforeman/puppet-puppet/commit/8cc4e3094d5bbd6d05d794e087816934e1697a87 +old_ciphers = ['TLS_RSA_WITH_AES_256_CBC_SHA256', 'TLS_RSA_WITH_AES_256_CBC_SHA', 'TLS_RSA_WITH_AES_128_CBC_SHA256', 'TLS_RSA_WITH_AES_128_CBC_SHA'] +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_cipher_suites'] == old_ciphers + answers['puppet'].delete('server_cipher_suites') +end diff --git a/config/foreman.migrations/20240505103832_change_db_pool_size.rb b/config/foreman.migrations/20240505103832_change_db_pool_size.rb new file mode 100644 index 000000000..e77ef4ee8 --- /dev/null +++ b/config/foreman.migrations/20240505103832_change_db_pool_size.rb @@ -0,0 +1,3 @@ +if answers['foreman'].is_a?(Hash) && answers.dig('foreman', 'db_pool').to_i == 5 + answers['foreman'].delete('db_pool') +end diff --git a/config/katello-answers.yaml b/config/katello-answers.yaml index 6a45bc080..e210ef90c 100644 --- a/config/katello-answers.yaml +++ b/config/katello-answers.yaml @@ -55,7 +55,6 @@ foreman::plugin::expire_hosts: false foreman::plugin::git_templates: false foreman::plugin::google: false foreman::plugin::hdm: false -foreman::plugin::hooks: false foreman::plugin::kernel_care: false foreman::plugin::kubevirt: false foreman::plugin::leapp: false @@ -71,7 +70,6 @@ foreman::plugin::rescue: false foreman::plugin::rh_cloud: false foreman::plugin::salt: false foreman::plugin::scc_manager: false -foreman::plugin::setup: false foreman::plugin::snapshot_management: false foreman::plugin::statistics: false foreman::plugin::tasks: true diff --git a/config/katello.migrations/180111142132-foreman_proxy_autosignfile.rb b/config/katello.migrations/180111142132-foreman_proxy_autosignfile.rb index a2ab711a8..19b472fde 100644 --- a/config/katello.migrations/180111142132-foreman_proxy_autosignfile.rb +++ b/config/katello.migrations/180111142132-foreman_proxy_autosignfile.rb @@ -2,6 +2,6 @@ answers['foreman_proxy']['use_autosignfile'] = true if answers['foreman_proxy'].key?('puppetdir') puppetdir = answers['foreman_proxy']['puppetdir'] - answers['foreman_proxy']['autosignfile'] = puppetdir + '/autosign.conf' + answers['foreman_proxy']['autosignfile'] = "#{puppetdir}/autosign.conf" end end diff --git a/config/katello.migrations/190111180118-delete-removed-settings.rb b/config/katello.migrations/190111180118-delete-removed-settings.rb index 116cb2b60..3a972ec95 100644 --- a/config/katello.migrations/190111180118-delete-removed-settings.rb +++ b/config/katello.migrations/190111180118-delete-removed-settings.rb @@ -69,6 +69,6 @@ end end -if (mod_answers = answers['foreman_proxy']) - mod_answers['dhcp_gateway'] = nil if mod_answers['dhcp_gateway'] == '192.168.100.1' +if (mod_answers = answers['foreman_proxy']) && (mod_answers['dhcp_gateway'] == '192.168.100.1') + mod_answers['dhcp_gateway'] = nil end diff --git a/config/katello.migrations/20240327093251_drop_hooks_plugin.rb b/config/katello.migrations/20240327093251_drop_hooks_plugin.rb new file mode 100644 index 000000000..da33fe307 --- /dev/null +++ b/config/katello.migrations/20240327093251_drop_hooks_plugin.rb @@ -0,0 +1 @@ +answers.delete('foreman::plugin::hooks') diff --git a/config/katello.migrations/210331121715-clear-puppetserver-nil-metrics.rb b/config/katello.migrations/210331121715-clear-puppetserver-nil-metrics.rb index 0c23b2c7c..218f435cd 100644 --- a/config/katello.migrations/210331121715-clear-puppetserver-nil-metrics.rb +++ b/config/katello.migrations/210331121715-clear-puppetserver-nil-metrics.rb @@ -1,3 +1,3 @@ -if answers['puppet'].is_a?(Hash) - answers['puppet'].delete('server_puppetserver_metrics') if answers['puppet']['server_puppetserver_metrics'].nil? +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_puppetserver_metrics'].nil? + answers['puppet'].delete('server_puppetserver_metrics') end diff --git a/config/katello.migrations/210625142712-dynamic-puppet-in-foreman-groups.rb b/config/katello.migrations/210625142712-dynamic-puppet-in-foreman-groups.rb index b2f0ecdad..e9aabca9b 100644 --- a/config/katello.migrations/210625142712-dynamic-puppet-in-foreman-groups.rb +++ b/config/katello.migrations/210625142712-dynamic-puppet-in-foreman-groups.rb @@ -1,5 +1,3 @@ -if answers['foreman'].is_a?(Hash) - if answers['foreman']['user_groups'] && answers['foreman']['user_groups'].include?('puppet') - answers['foreman']['user_groups'].delete('puppet') - end +if answers['foreman'].is_a?(Hash) && (answers['foreman']['user_groups'] && answers['foreman']['user_groups'].include?('puppet')) + answers['foreman']['user_groups'].delete('puppet') end diff --git a/config/katello.migrations/210809133829-reset-puma-min-threads.rb b/config/katello.migrations/210809133829-reset-puma-min-threads.rb index be69d7f59..b6bd7af47 100644 --- a/config/katello.migrations/210809133829-reset-puma-min-threads.rb +++ b/config/katello.migrations/210809133829-reset-puma-min-threads.rb @@ -1,6 +1,4 @@ -if answers['foreman'].is_a?(Hash) - if answers['foreman']['foreman_service_puma_threads_min'] == 0 || - answers['foreman']['foreman_service_puma_threads_min'] == answers['foreman']['foreman_service_puma_threads_max'] - answers['foreman'].delete('foreman_service_puma_threads_min') - end +if answers['foreman'].is_a?(Hash) && (answers['foreman']['foreman_service_puma_threads_min'] == 0 || + answers['foreman']['foreman_service_puma_threads_min'] == answers['foreman']['foreman_service_puma_threads_max']) + answers['foreman'].delete('foreman_service_puma_threads_min') end diff --git a/config/katello.migrations/210819164117-reset-puma-workers-and-max-threads.rb b/config/katello.migrations/210819164117-reset-puma-workers-and-max-threads.rb index 124a81472..a6dff216b 100644 --- a/config/katello.migrations/210819164117-reset-puma-workers-and-max-threads.rb +++ b/config/katello.migrations/210819164117-reset-puma-workers-and-max-threads.rb @@ -1,12 +1,8 @@ # https://github.com/theforeman/puppet-foreman/commit/533c1f3be8069139d7375841019afeb7c1102830 -if answers['foreman'].is_a?(Hash) - if answers['foreman']['foreman_service_puma_workers'] == 2 - answers['foreman'].delete('foreman_service_puma_workers') - end +if answers['foreman'].is_a?(Hash) && (answers['foreman']['foreman_service_puma_workers'] == 2) + answers['foreman'].delete('foreman_service_puma_workers') end -if answers['foreman'].is_a?(Hash) - if answers['foreman']['foreman_service_puma_threads_max'] == 16 - answers['foreman'].delete('foreman_service_puma_threads_max') - end +if answers['foreman'].is_a?(Hash) && (answers['foreman']['foreman_service_puma_threads_max'] == 16) + answers['foreman'].delete('foreman_service_puma_threads_max') end diff --git a/config/katello.migrations/240322170637-reset-puppet-server-java-bin.rb b/config/katello.migrations/240322170637-reset-puppet-server-java-bin.rb new file mode 100644 index 000000000..5f6018274 --- /dev/null +++ b/config/katello.migrations/240322170637-reset-puppet-server-java-bin.rb @@ -0,0 +1,3 @@ +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_jvm_java_bin'] == '/usr/bin/java' + answers['puppet'].delete('server_jvm_java_bin') +end diff --git a/config/katello.migrations/240326121346-drop-setup-plugin.rb b/config/katello.migrations/240326121346-drop-setup-plugin.rb new file mode 100644 index 000000000..f1b6c7605 --- /dev/null +++ b/config/katello.migrations/240326121346-drop-setup-plugin.rb @@ -0,0 +1 @@ +answers.delete('foreman::plugin::setup') diff --git a/config/katello.migrations/240328125552-reset-puppetserver-ciphers.rb b/config/katello.migrations/240328125552-reset-puppetserver-ciphers.rb new file mode 100644 index 000000000..e95a07c3d --- /dev/null +++ b/config/katello.migrations/240328125552-reset-puppetserver-ciphers.rb @@ -0,0 +1,5 @@ +# https://github.com/theforeman/puppet-puppet/commit/8cc4e3094d5bbd6d05d794e087816934e1697a87 +old_ciphers = ['TLS_RSA_WITH_AES_256_CBC_SHA256', 'TLS_RSA_WITH_AES_256_CBC_SHA', 'TLS_RSA_WITH_AES_128_CBC_SHA256', 'TLS_RSA_WITH_AES_128_CBC_SHA'] +if answers['puppet'].is_a?(Hash) && answers['puppet']['server_cipher_suites'] == old_ciphers + answers['puppet'].delete('server_cipher_suites') +end diff --git a/config/katello.migrations/240506081834-change-db-pool-size.rb b/config/katello.migrations/240506081834-change-db-pool-size.rb new file mode 100644 index 000000000..e77ef4ee8 --- /dev/null +++ b/config/katello.migrations/240506081834-change-db-pool-size.rb @@ -0,0 +1,3 @@ +if answers['foreman'].is_a?(Hash) && answers.dig('foreman', 'db_pool').to_i == 5 + answers['foreman'].delete('db_pool') +end diff --git a/hooks/boot/01-kafo-hook-extensions.rb b/hooks/boot/01-kafo-hook-extensions.rb index d655a1324..32826ff47 100644 --- a/hooks/boot/01-kafo-hook-extensions.rb +++ b/hooks/boot/01-kafo-hook-extensions.rb @@ -115,6 +115,11 @@ def execute!(command, do_say = true, do_log = true) end end + def execute_as!(user, command, do_say = true, do_log = true) + runuser_command = "runuser -l #{user} -c '#{command}'" + execute!(runuser_command, do_say, do_log) + end + def execute(command, do_say, do_log) _stdout_stderr, status = execute_command(command, do_say, do_log) status @@ -139,6 +144,19 @@ def execute_command(command, do_say, do_log) def remote_host?(hostname) !['localhost', '127.0.0.1', `hostname`.strip].include?(hostname) end + + def el8? + facts[:os][:release][:major] == '8' && facts[:os][:family] == 'RedHat' + end + + def available_space(directory = nil) + directory = '/' if directory.nil? + mountpoints = facts[:mountpoints] + until (mountpoint = mountpoints[directory.to_sym]) + directory = File.dirname(directory) + end + mountpoint[:available_bytes] + end end Kafo::HookContext.send(:include, HookContextExtension) diff --git a/hooks/boot/04-services.rb b/hooks/boot/04-services.rb index 97f80e928..68d4bb50d 100644 --- a/hooks/boot/04-services.rb +++ b/hooks/boot/04-services.rb @@ -9,9 +9,6 @@ module ServicesHookContextExtension 'httpd.service', # Apache on Red Hat 'postgresql*', # Used by Foreman/Dynflow and Pulpcore 'pulpcore*', - 'qdrouterd.service', # Used by Katello for katello-agent - 'qpidd.service', # Used by Katello for katello-agent - 'smart_proxy_dynflow_core.service', # Used by Foreman Proxy 'tomcat.service', # Candlepin ].freeze diff --git a/hooks/boot/05-environment.rb b/hooks/boot/05-environment.rb index bf8d672e6..fd92a5bc5 100644 --- a/hooks/boot/05-environment.rb +++ b/hooks/boot/05-environment.rb @@ -1,5 +1,5 @@ %w[http_proxy https_proxy ssl_cert_file HTTP_PROXY HTTPS_PROXY SSL_CERT_FILE].each do |variable| - if ::ENV.delete(variable) + if ::ENV.delete(variable) # rubocop:disable Style/RedundantConstantBase logger.warn "Unsetting environment variable '#{variable}' for the duration of the install." end end diff --git a/hooks/boot/06-postgresql-upgrade-extensions.rb b/hooks/boot/06-postgresql-upgrade-extensions.rb new file mode 100644 index 000000000..fe76c8b15 --- /dev/null +++ b/hooks/boot/06-postgresql-upgrade-extensions.rb @@ -0,0 +1,66 @@ +module PostgresqlUpgradeHookContextExtension + def needs_postgresql_upgrade?(new_version) + File.read('/var/lib/pgsql/data/PG_VERSION').chomp.to_i < new_version.to_i + rescue Errno::ENOENT + false + end + + def os_needs_postgresql_upgrade? + el8? && needs_postgresql_upgrade?(13) + end + + def postgresql_upgrade(new_version) + logger.notice("Performing upgrade of PostgreSQL to #{new_version}") + + stop_services + + logger.notice("Upgrading PostgreSQL packages") + + execute!("dnf module switch-to postgresql:#{new_version} -y", false, true) + + server_packages = ['postgresql', 'postgresql-server', 'postgresql-upgrade'] + ['postgresql-contrib', 'postgresql-docs'].each do |extra_package| + if execute("rpm -q #{extra_package}", false, false) + server_packages << extra_package + end + end + + ensure_packages(server_packages, 'latest') + + logger.notice("Migrating PostgreSQL data") + + # puppetlabs-postgresql always sets data_directory in the config + # see https://github.com/puppetlabs/puppetlabs-postgresql/issues/1576 + # however, one can't use postgresql-setup --upgrade if that value is set + # see https://bugzilla.redhat.com/show_bug.cgi?id=1935301 + execute!("sed -i '/^data_directory/d' /var/lib/pgsql/data/postgresql.conf", false, true) + + execute_as!('postgres', 'postgresql-setup --upgrade', false, true) + + logger.notice("Analyzing the new PostgreSQL cluster") + + start_services(['postgresql']) + + execute_as!('postgres', 'vacuumdb --all --analyze-in-stages', false, true) + + logger.notice("Upgrade to PostgreSQL #{new_version} completed") + end + + def check_postgresql_storage + # Ensure there is at least 1x /var/lib/pgsql free disk space available on the main filesystem + current_postgres_dir = '/var/lib/pgsql/data' + new_postgres_dir = '/var/lib/pgsql' + + begin + postgres_size = `du --bytes --summarize #{current_postgres_dir}`.split[0].to_i + + if available_space(new_postgres_dir) < postgres_size + fail_and_exit "The PostgreSQL upgrade requires at least #{(postgres_size / 1024) / 1024} MB of storage to be available at #{new_postgres_dir}." + end + rescue StandardError + fail_and_exit 'Failed to verify available disk space' + end + end +end + +Kafo::HookContext.send(:include, PostgresqlUpgradeHookContextExtension) diff --git a/hooks/boot/09-version_locking.rb b/hooks/boot/09-version_locking.rb index 872588afb..1f46e30b8 100644 --- a/hooks/boot/09-version_locking.rb +++ b/hooks/boot/09-version_locking.rb @@ -7,8 +7,8 @@ def package_lock_feature? '--[no-]lock-package-versions', :flag, "Let installer lock versions of the installed packages to prevent\n" \ - "unexpected breakages during system updates. The choice is remembered\n" \ - "and used in next installer runs.", + "unexpected breakages during system updates. The choice is remembered\n" \ + "and used in next installer runs.", :default => nil ) end diff --git a/hooks/pre/10-reset_data.rb b/hooks/pre/10-reset_data.rb index e1924b56c..88a424aa0 100644 --- a/hooks/pre/10-reset_data.rb +++ b/hooks/pre/10-reset_data.rb @@ -61,7 +61,7 @@ def pg_command_base(config, command, args) end def pg_sql_statement(config, statement) - pg_command_base(config, 'psql', "-d #{config[:database]} -t -c \"" + statement + '"') + pg_command_base(config, 'psql', "-d #{config[:database]} -t -c \"#{statement}\"") end # WARNING: deletes all the data owned by the user. No warnings. No confirmations. diff --git a/hooks/pre/20-check-hammer-credentials.rb b/hooks/pre/20-check-hammer-credentials.rb index 421b0c3c4..9bd8858bc 100644 --- a/hooks/pre/20-check-hammer-credentials.rb +++ b/hooks/pre/20-check-hammer-credentials.rb @@ -12,19 +12,15 @@ def password_set?(path) if File.exist?(path) config = YAML.load_file(path) - return true if config[:foreman].is_a?(::Hash) && (config[:foreman][:username] != 'admin' || config[:foreman][:password]) + return true if config[:foreman].is_a?(Hash) && (config[:foreman][:username] != 'admin' || config[:foreman][:password]) end false end new_config_file = File.join(NEW_HAMMER_CONFIG_PATH, NEW_HAMMER_CONFIG_FILE) -unless File.exist?(new_config_file) - # if there is foreman password or non-admin user set in any of the legacy configs - # create empty hammer foreman config to prevent installer from creating new one - if POSSIBLE_RECENT_CONFIG_PATHS.any? { |path| password_set?(path) } - FileUtils.mkdir_p(NEW_HAMMER_CONFIG_PATH) - File.open(new_config_file, "w+") do |file| - file.write("---\n:foreman: {}\n") - end - end +# if there is foreman password or non-admin user set in any of the legacy configs +# create empty hammer foreman config to prevent installer from creating new one +if !File.exist?(new_config_file) && POSSIBLE_RECENT_CONFIG_PATHS.any? { |path| password_set?(path) } + FileUtils.mkdir_p(NEW_HAMMER_CONFIG_PATH) + File.write(new_config_file, "---\n:foreman: {}\n") end diff --git a/hooks/pre/30-el8_upgrade_postgresql.rb b/hooks/pre/30-el8_upgrade_postgresql.rb new file mode 100644 index 000000000..3ca5fa420 --- /dev/null +++ b/hooks/pre/30-el8_upgrade_postgresql.rb @@ -0,0 +1,3 @@ +if local_postgresql? && os_needs_postgresql_upgrade? + postgresql_upgrade(13) +end diff --git a/hooks/pre_commit/05-puppet_certs_exist.rb b/hooks/pre_commit/05-puppet_certs_exist.rb index bbc7edb2b..5a88f9fae 100644 --- a/hooks/pre_commit/05-puppet_certs_exist.rb +++ b/hooks/pre_commit/05-puppet_certs_exist.rb @@ -8,10 +8,10 @@ if puppet_ca_enabled && key_path && File.exist?(key_path) && !kafo.skip_checks_i_know_better? if !puppet_ca_cert.empty? && !File.exist?(puppet_ca_cert) - fail_and_exit("The file #{puppet_ca_cert} does not exist.\n #{client_message}\n" \ - " - if you use custom Puppet SSL directory (--foreman-proxy-ssldir) make sure the directory exists and contain the CA certificate.\n", 101) + fail_and_exit("The file #{puppet_ca_cert} does not exist.\n #{client_message}\n " \ + "- if you use custom Puppet SSL directory (--foreman-proxy-ssldir) make sure the directory exists and contain the CA certificate.\n", 101) end if server_crl_path.is_a?(String) && !server_crl_path.empty? && !File.exist?(server_crl_path) - fail_and_exit("The file #{server_crl_path} does not exist.\n #{client_message}\n - if you set custom revocation list (--foreman-server-ssl-crl) make sure the file exists.", 101) + fail_and_exit("The file #{server_crl_path} does not exist.\n#{client_message}\n - if you set a custom revocation list (--foreman-server-ssl-crl) make sure the file exists.", 101) end end diff --git a/hooks/pre_validations/01-reset_data.rb b/hooks/pre_validations/01-reset_data.rb index b29fdcd3b..abebcf7d2 100644 --- a/hooks/pre_validations/01-reset_data.rb +++ b/hooks/pre_validations/01-reset_data.rb @@ -1,6 +1,6 @@ if app_value(:reset_data) && !app_value(:noop) response = ask('Are you sure you want to continue? This will drop the databases, reset all configurations that you have made and bring all application data back to a fresh install. [y/n]') - if response.downcase != 'y' + if !response.casecmp('y').zero? $stderr.puts '** cancelled **' exit(1) end diff --git a/hooks/pre_validations/30-el8_upgrade_postgresql.rb b/hooks/pre_validations/30-el8_upgrade_postgresql.rb new file mode 100644 index 000000000..3250f10c4 --- /dev/null +++ b/hooks/pre_validations/30-el8_upgrade_postgresql.rb @@ -0,0 +1,3 @@ +if local_postgresql? && os_needs_postgresql_upgrade? + check_postgresql_storage +end diff --git a/spec/fixtures/katello-certs-check/certs/ca-bundle-with-trust-rules.crt b/spec/fixtures/katello-certs-check/certs/ca-bundle-with-trust-rules.crt new file mode 100644 index 000000000..b27403b25 --- /dev/null +++ b/spec/fixtures/katello-certs-check/certs/ca-bundle-with-trust-rules.crt @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfmgAwIBAgIUW99nJU3DgZdPE4KtDWgL6cTrEAgwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNVGhpcmRwYXJ0eSBDQTAeFw0yMDExMTgxNTQ0NTJaFw0z +MDExMTYxNTQ0NTJaMBgxFjAUBgNVBAMMDVRoaXJkcGFydHkgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDs4Haoc6xj4cTiGYzcqLBiX6fnDpmVPXaD +kE6s0iFYuqZcZgFIuVetgWn4ew7NvDgm1HwCA6EnKBY4zxMd+xic2vO8Pm9SqNWB +0bIdyKvHn1o3u9TMRcnHbp4MvlTTsd0Hr91n+J6Kv7TVUihhsWQH6YILqaKMaEa3 +78ssaLrTULdCHQ3vB0XyZGj3NLv5PYq6Yt92hG8M7vyVeBPdEECidD1csAOefk8T +EBRgXgl7dpBa16dZ9nNQzurxOGrRgFsW4wIQit3AU/Y3Zyz1f9hTcG+xHGHUzzsy +jaV345Mdito5DNaGqkh+7PUccb1JihuS6ePZl5J6GQSjktFwPL2jAgMBAAGjUzBR +MB0GA1UdDgQWBBRKB5Wwmh4CsZHFmnAczPb6wjMRajAfBgNVHSMEGDAWgBRKB5Ww +mh4CsZHFmnAczPb6wjMRajAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4IBAQCs7kfOgqFXpYqAYjxq+NlQeeWSyEMdU/5QbSjxS68U3wXZ2JR+N7ptmn8B +IaeIF8BMFkLKKCG0s486YnOGKBkmbE5xxAJYctzJSrAjCtkBqqCVMtqpomuXawJv +tB+HwKV3IW43lM8S3DJ5XbEWlZctqGb803ud6Mt2Rlyc6afPyFzt5DPrgvwkgKmX +cLgkvUP0W4dinOG3PqE5NbxqIMw+0kzyrbGYLO7Klwsqjfms++XXIdREzS/nt8+9 +c2uERABlpV58p/xyZjJMGGnU0YIhOfn6+LQ6gyqU3qKdj6/VtcQ6SnZC3GE3adqT +LpdSdMc5aL5hr2hZl/uVvrKLYDGz +-----END CERTIFICATE----- +-----BEGIN TRUSTED CERTIFICATE----- +MIIDHTCCAgWgAwIBAgIUK+x25LNYYMHS83aWDnAYviwxEYEwDQYJKoZIhvcNAQEL +BQAwHjEcMBoGA1UEAwwTVGVzdCBTZWxmLVNpZ25lZCBDQTAeFw0yMDExMTgwMjMw +NDNaFw0zMDExMTYwMjMwNDNaMB4xHDAaBgNVBAMME1Rlc3QgU2VsZi1TaWduZWQg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC92114uygw5KcqPCz1 +E/Cwd3Lo2ytyPD9FchWKPOxXpNisHMOr4zAfsxERXmgBLawHIkqc2Xae3TqHGGQa +ll3J3HukwghZQAyjcNG/Q2Q2QqfQW1tzxHRnz2EKBoRoyhmVXcnu+qBoEgkf5QI/ +Rk9HzLJINZPcZuMEkRgcf5q1h/F+PY2yCMwT5qjB6whn6zX6FP6G3//fRtkZw4cI +FPPjKJedbHlYEifRigmJfu+T5Q5xz19Og/1zDwfl7is5eBUV+KEoIE7UpmvR1UrM ++T6WYl3vxeM08y1QU6vR9GqummDMinfWLj0hV+dYwI9/1fHIjfPqgxPUa5AGw7ik +vyrvAgMBAAGjUzBRMB0GA1UdDgQWBBQz80R5aRb/egnEMKHQonUM3xgj6DAfBgNV +HSMEGDAWgBQz80R5aRb/egnEMKHQonUM3xgj6DAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQCdiBvQx6ExmteTzwkGCheKwUMvzCehuwvpoJRE/JXo +zz67414oyWXkSN8/9HE3nkH/xxunD/Ni+N9ppk7iicSpyOKfdDXiaS8qq1O1OXCx +CjoVuIFAPFWOEEhLdnb1v8YVWx2JwcbGvhCLNSoK1a6uwCmWixtoeQiKspBfwFcb +wfU9qNdXsezBljahE4Q2E4SR+XclA6iHdooX4ajnleamqeH0ephyCqvMAhzfJA5F +O1+SJRFbIjwfKxsEJS6Czrn+EU2eLtxk5g5+oO06ZYj4rVOfgc2Wc0+cisgP0fT/ +WVkAxgGS6L0jGvZSisEUBpoidJNddWnf9mzUT2kJ5DCOMAwwCgYIKwYBBQUHAwE= +-----END TRUSTED CERTIFICATE----- diff --git a/spec/fixtures/katello-certs-check/certs/ca-with-trust-rules.crt b/spec/fixtures/katello-certs-check/certs/ca-with-trust-rules.crt new file mode 100644 index 000000000..0824838c5 --- /dev/null +++ b/spec/fixtures/katello-certs-check/certs/ca-with-trust-rules.crt @@ -0,0 +1,19 @@ +-----BEGIN TRUSTED CERTIFICATE----- +MIIDHTCCAgWgAwIBAgIUK+x25LNYYMHS83aWDnAYviwxEYEwDQYJKoZIhvcNAQEL +BQAwHjEcMBoGA1UEAwwTVGVzdCBTZWxmLVNpZ25lZCBDQTAeFw0yMDExMTgwMjMw +NDNaFw0zMDExMTYwMjMwNDNaMB4xHDAaBgNVBAMME1Rlc3QgU2VsZi1TaWduZWQg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC92114uygw5KcqPCz1 +E/Cwd3Lo2ytyPD9FchWKPOxXpNisHMOr4zAfsxERXmgBLawHIkqc2Xae3TqHGGQa +ll3J3HukwghZQAyjcNG/Q2Q2QqfQW1tzxHRnz2EKBoRoyhmVXcnu+qBoEgkf5QI/ +Rk9HzLJINZPcZuMEkRgcf5q1h/F+PY2yCMwT5qjB6whn6zX6FP6G3//fRtkZw4cI +FPPjKJedbHlYEifRigmJfu+T5Q5xz19Og/1zDwfl7is5eBUV+KEoIE7UpmvR1UrM ++T6WYl3vxeM08y1QU6vR9GqummDMinfWLj0hV+dYwI9/1fHIjfPqgxPUa5AGw7ik +vyrvAgMBAAGjUzBRMB0GA1UdDgQWBBQz80R5aRb/egnEMKHQonUM3xgj6DAfBgNV +HSMEGDAWgBQz80R5aRb/egnEMKHQonUM3xgj6DAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQCdiBvQx6ExmteTzwkGCheKwUMvzCehuwvpoJRE/JXo +zz67414oyWXkSN8/9HE3nkH/xxunD/Ni+N9ppk7iicSpyOKfdDXiaS8qq1O1OXCx +CjoVuIFAPFWOEEhLdnb1v8YVWx2JwcbGvhCLNSoK1a6uwCmWixtoeQiKspBfwFcb +wfU9qNdXsezBljahE4Q2E4SR+XclA6iHdooX4ajnleamqeH0ephyCqvMAhzfJA5F +O1+SJRFbIjwfKxsEJS6Czrn+EU2eLtxk5g5+oO06ZYj4rVOfgc2Wc0+cisgP0fT/ +WVkAxgGS6L0jGvZSisEUBpoidJNddWnf9mzUT2kJ5DCOMAwwCgYIKwYBBQUHAwE= +-----END TRUSTED CERTIFICATE----- diff --git a/spec/fixtures/katello-certs-check/create_cert.sh b/spec/fixtures/katello-certs-check/create_cert.sh index 586adac67..fd3619398 100755 --- a/spec/fixtures/katello-certs-check/create_cert.sh +++ b/spec/fixtures/katello-certs-check/create_cert.sh @@ -28,6 +28,16 @@ else echo "CA certificate bundle exists. Skipping." fi +CA_BUNDLE=ca-bundle-with-trust-rules +CA_CERT_WITH_TRUST_RULES=ca-with-trust-rules +if [[ ! -f "$CERTS_DIR/$CA_BUNDLE.crt" ]]; then + echo "Generate CA bundle with trust rules" + openssl x509 -in $CERTS_DIR/$CA_CERT_NAME.crt -addtrust serverAuth -out $CERTS_DIR/$CA_CERT_WITH_TRUST_RULES.crt + cat $CERTS_DIR/$THIRDPARTY_CA_CERT_NAME.crt $CERTS_DIR/$CA_CERT_WITH_TRUST_RULES.crt > $CERTS_DIR/$CA_BUNDLE.crt +else + echo "CA certificate bundle with trust rules exists. Skipping." +fi + CERT_NAME=foreman.example.com if [[ ! -f "$CERTS_DIR/$CERT_NAME.key" || ! -f "$CERTS_DIR/$CERT_NAME.crt" ]]; then echo "Generate server certificate" diff --git a/spec/hook_context_extension_spec.rb b/spec/hook_context_extension_spec.rb index a0e782a55..9116430d2 100644 --- a/spec/hook_context_extension_spec.rb +++ b/spec/hook_context_extension_spec.rb @@ -142,5 +142,34 @@ end end end + + describe '.execute!' do + subject { context.execute!(command) } + let(:command) { 'uptime' } + + before do + allow(context).to receive(:execute_command).and_return([command, true]) + end + + it 'executes a command' do + expect(subject).to be_nil + expect(context).to have_received(:execute_command).with(command, true, true) + end + end + + describe '.execute_as!' do + subject { context.execute_as!(user, command) } + let(:command) { 'uptime' } + let(:user) { 'postgres' } + + before do + allow(context).to receive(:execute!) + end + + it 'executes a command' do + expect(subject).to be_nil + expect(context).to have_received(:execute!).with("runuser -l postgres -c 'uptime'", true, true) + end + end end end diff --git a/spec/katello_certs_check_spec.rb b/spec/katello_certs_check_spec.rb index 6a3ce804d..0eef3057d 100644 --- a/spec/katello_certs_check_spec.rb +++ b/spec/katello_certs_check_spec.rb @@ -78,4 +78,17 @@ def fixture(filename) expect(status.exitstatus).to eq 1 end end + + context 'with bundle containing trust rules' do + let(:key) { File.join(certs_directory, 'foreman.example.com.key') } + let(:cert) { File.join(certs_directory, 'foreman.example.com.crt') } + let(:ca) { File.join(certs_directory, 'ca-bundle-with-trust-rules.crt') } + + it 'fails on bundle validation' do + command_with_certs = "#{command} -b #{ca} -k #{key} -c #{cert}" + _stdout, stderr, status = Open3.capture3(command_with_certs) + expect(stderr).to include 'The CA bundle contains 1 certificate(s) with trust rules. This may create problems for older systems to trust the bundle. Please, recreate the bundle using certificates without trust rules' + expect(status.exitstatus).to eq 10 + end + end end diff --git a/spec/migrations/20240328125552_reset_puppetserver_ciphers_spec.rb b/spec/migrations/20240328125552_reset_puppetserver_ciphers_spec.rb new file mode 100644 index 000000000..c408ccf9c --- /dev/null +++ b/spec/migrations/20240328125552_reset_puppetserver_ciphers_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +migration '20231005004305_redis_default_cache' do + scenarios %w[foreman katello foreman-proxy-content] do + context 'current ciphers' do + let(:answers) do + { + 'puppet' => { + 'server_cipher_suites' => [ + 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256', + 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384', + 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256', + 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256', + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', + ], + }, + } + end + + it 'leaves ciphers untouched' do + expect(migrated_answers['puppet']['server_cipher_suites']).not_to be_nil + end + end + + context 'outdated ciphers' do + let(:answers) do + { + 'puppet' => { + 'server_cipher_suites' => [ + 'TLS_RSA_WITH_AES_256_CBC_SHA256', + 'TLS_RSA_WITH_AES_256_CBC_SHA', + 'TLS_RSA_WITH_AES_128_CBC_SHA256', + 'TLS_RSA_WITH_AES_128_CBC_SHA', + ], + }, + } + end + + it 'resets the answer' do + expect(migrated_answers['puppet']['server_cipher_suites']).to be_nil + end + end + end +end diff --git a/spec/postgresql_upgrade_hook_context_extension_spec.rb b/spec/postgresql_upgrade_hook_context_extension_spec.rb new file mode 100644 index 000000000..3d8089dfe --- /dev/null +++ b/spec/postgresql_upgrade_hook_context_extension_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' +require 'kafo/hook_context' + +require_relative '../hooks/boot/06-postgresql-upgrade-extensions' + +describe PostgresqlUpgradeHookContextExtension do + let(:kafo) { instance_double(Kafo::KafoConfigure) } + let(:logger) { instance_double(Kafo::Logger) } + let(:context) { Kafo::HookContext.new(kafo, logger) } + + describe '.needs_postgresql_upgrade?' do + subject { context.needs_postgresql_upgrade?(13) } + + it 'returns true for PostgreSQL 12' do + expect(File).to receive(:read).with('/var/lib/pgsql/data/PG_VERSION').and_return('12') + expect(subject).to be_truthy + end + + it 'returns false for PostgreSQL 13' do + expect(File).to receive(:read).with('/var/lib/pgsql/data/PG_VERSION').and_return('13') + expect(subject).to be_falsy + end + + it 'returns false when no PostgreSQL version file was found' do + expect(File).to receive(:read).with('/var/lib/pgsql/data/PG_VERSION').and_raise(Errno::ENOENT) + expect(subject).to be_falsy + end + end + + describe '.postgresql_upgrade' do + subject { context.postgresql_upgrade(13) } + + before do + allow(context).to receive(:logger).and_return(logger) + allow(context).to receive(:'execute!') + allow(context).to receive(:ensure_packages) + allow(context).to receive(:stop_services) + allow(context).to receive(:start_services) + allow(logger).to receive(:notice) + end + + it 'logs the upgrade' do + expect(subject).to be_nil + expect(logger).to have_received(:notice).with('Performing upgrade of PostgreSQL to 13') + expect(logger).to have_received(:notice).with('Upgrade to PostgreSQL 13 completed') + end + + it 'switches the dnf module' do + expect(subject).to be_nil + expect(context).to have_received(:'execute!').with('dnf module switch-to postgresql:13 -y', false, true) + end + + it 'removes data_directory from postgresql.conf' do + expect(subject).to be_nil + expect(context).to have_received(:'execute!').with("sed -i '/^data_directory/d' /var/lib/pgsql/data/postgresql.conf", false, true) + end + + it 'runs postgresql-setup --upgrade' do + expect(subject).to be_nil + expect(context).to have_received(:'execute!').with("runuser -l postgres -c 'postgresql-setup --upgrade'", false, true) + end + + it 'runs vacuumdb --all --analyze-in-stages' do + expect(subject).to be_nil + expect(context).to have_received(:'execute!').with("runuser -l postgres -c 'vacuumdb --all --analyze-in-stages'", false, true) + end + end +end diff --git a/spec/util/fake_puppet_file_spec.rb b/spec/util/fake_puppet_file_spec.rb new file mode 100644 index 000000000..13689402e --- /dev/null +++ b/spec/util/fake_puppet_file_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +require_relative '../../util/fake_puppet_file' + +describe FakePuppetfile do + let(:fake) { FakePuppetfile.new } + + specify 'without a version' do + expect(PuppetForge::Module).not_to receive(:find) + + fake.instance_eval { mod 'theforeman/motd' } + + expect { |b| fake.content(&b) }.to yield_with_args("mod 'theforeman/motd'") + end + + specify 'with a minimum' do + expect(PuppetForge::Module).not_to receive(:find) + + fake.instance_eval { mod 'theforeman/motd', '>= 1.2' } + + expect { |b| fake.content(&b) }.to yield_with_args("mod 'theforeman/motd', '>= 1.2'") + end + + specify 'with a minimum and a maximum' do + expect(PuppetForge::Module).not_to receive(:find) + + fake.instance_eval { mod 'theforeman/motd', '>= 1.2', '< 3' } + + expect { |b| fake.content(&b) }.to yield_with_args("mod 'theforeman/motd', '>= 1.2', '< 3'") + end + + specify 'with a git url' do + expect(PuppetForge::Module).to receive(:find) + .with('theforeman-motd') + .and_return(double(PuppetForge::V3::Module, current_release: double(PuppetForge::V3::Release, version: '1.2.3'))) + + fake.instance_eval { mod 'theforeman/motd', :git => 'https://github.com/theforeman/puppet-motd' } + + expect { |b| fake.content(&b) }.to yield_with_args("mod 'theforeman/motd', '~> 1.2.3'") + end +end diff --git a/util/fake_puppet_file.rb b/util/fake_puppet_file.rb new file mode 100644 index 000000000..acb141633 --- /dev/null +++ b/util/fake_puppet_file.rb @@ -0,0 +1,43 @@ +require 'puppet_forge' +require 'semverse' + +class FakePuppetfile + def initialize + @new_content = [] + end + + def forge(url) + @new_content << ['forge', url, nil] + PuppetForge.host = url + end + + def mod(name, *version, **options) + if options.any? && !options.include?(:ref) + release = PuppetForge::Module.find(name.tr('/', '-')).current_release + @new_content << ['mod', name, ["~> #{release.version}"]] + else + @new_content << ['mod', name, version] + end + end + + def content + max_length = @new_content.select { |type, _value| type == 'mod' }.map { |_type, value| value.length }.max + + @new_content.each do |type, value, options| + if type == 'forge' + yield "forge '#{value}'" + yield "" + elsif type == 'mod' + if options.empty? + yield "mod '#{value}'" + elsif options.is_a?(Array) + padding = ' ' * (max_length - value.length) + yield "mod '#{value}', #{padding}#{options.map { |version| "'#{version}'" }.join(', ')}" + else + padding = ' ' * (max_length - value.length) + yield "mod '#{value}', #{padding}#{options.map { |k, v| ":#{k} => '#{v}'" }.join(', ')}" + end + end + end + end +end